From 1598ae86054f6d8f58157c060b7ba821ce317d40 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Fri, 24 Aug 2018 09:07:23 -0700 Subject: [PATCH] Part 3 of Aligning Beacon Chain with Latest 2.1 - Processing Attestations (#423) --- beacon-chain/blockchain/BUILD.bazel | 1 + beacon-chain/blockchain/core.go | 122 ++++++++++-- beacon-chain/blockchain/core_test.go | 234 ++++++++++++------------ beacon-chain/blockchain/service.go | 7 + beacon-chain/blockchain/service_test.go | 95 ++++++++++ beacon-chain/casper/BUILD.bazel | 1 + beacon-chain/casper/incentives.go | 7 +- beacon-chain/casper/validator.go | 1 + beacon-chain/casper/validator_test.go | 10 +- beacon-chain/node/node.go | 1 + beacon-chain/types/block.go | 5 + beacon-chain/utils/checkbit.go | 20 +- beacon-chain/utils/checkbit_test.go | 29 ++- validator/beacon/service.go | 4 +- validator/beacon/service_test.go | 8 +- validator/node/node.go | 2 +- 16 files changed, 367 insertions(+), 180 deletions(-) diff --git a/beacon-chain/blockchain/BUILD.bazel b/beacon-chain/blockchain/BUILD.bazel index 3929993cd3..a86a077869 100644 --- a/beacon-chain/blockchain/BUILD.bazel +++ b/beacon-chain/blockchain/BUILD.bazel @@ -20,6 +20,7 @@ go_library( "@com_github_ethereum_go_ethereum//event:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", + "@org_golang_x_crypto//blake2b:go_default_library", ], ) diff --git a/beacon-chain/blockchain/core.go b/beacon-chain/blockchain/core.go index fd5d3c22ad..324c18787b 100644 --- a/beacon-chain/blockchain/core.go +++ b/beacon-chain/blockchain/core.go @@ -2,6 +2,8 @@ package blockchain import ( "context" + "encoding/binary" + "errors" "fmt" "sync" "time" @@ -15,6 +17,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/utils" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/sirupsen/logrus" + "golang.org/x/crypto/blake2b" ) var canonicalHeadKey = "latest-canonical-head" @@ -194,7 +197,7 @@ func (b *BeaconChain) PersistCrystallizedState() error { } // IsCycleTransition checks if a new cycle has been reached. At that point, -// a new state transition will occur. +// a new crystallized state transition will occur. func (b *BeaconChain) IsCycleTransition(slotNumber uint64) bool { return slotNumber >= b.CrystallizedState().LastStateRecalc()+params.CycleLength } @@ -215,22 +218,6 @@ func (b *BeaconChain) CanProcessBlock(fetcher types.POWBlockFetcher, block *type if !canProcess { return false, fmt.Errorf("time stamp verification for beacon block %v failed", block.SlotNumber()) } - - canProcess, err = b.verifyBlockActiveHash(block) - if err != nil { - return false, fmt.Errorf("unable to process block: %v", err) - } - if !canProcess { - return false, fmt.Errorf("active state verification for beacon block %v failed", block.SlotNumber()) - } - - canProcess, err = b.verifyBlockCrystallizedHash(block) - if err != nil { - return false, fmt.Errorf("unable to process block: %v", err) - } - if !canProcess { - return false, fmt.Errorf("crystallized verification for beacon block %v failed", block.SlotNumber()) - } return canProcess, nil } @@ -327,6 +314,107 @@ func (b *BeaconChain) saveBlock(block *types.Block) error { return b.db.Put(hash[:], encodedState) } +// processAttestations processes the attestations of an incoming block. +func (b *BeaconChain) processAttestations(block *types.Block) error { + // Validate attestation's slot number has is within range of incoming block number. + slotNumber := int(block.SlotNumber()) + for _, attestation := range block.Attestations() { + if int(attestation.Slot) > slotNumber { + return fmt.Errorf("attestation slot number can't be higher than block slot number. Found: %v, Needed lower than: %v", + attestation.Slot, + slotNumber) + } + if int(attestation.Slot) < slotNumber-params.CycleLength { + return fmt.Errorf("attestation slot number can't be lower than block slot number by one CycleLength. Found: %v, Needed greater than: %v", + attestation.Slot, + slotNumber-params.CycleLength) + } + + // Get all the block hashes up to cycle length. + parentHashes := b.getSignedParentHashes(block, attestation) + attesterIndices, err := b.getAttesterIndices(attestation) + if err != nil { + return err + } + + // Verify attester bitfields matches crystallized state's prev computed bitfield. + if err := b.validateAttesterBitfields(attestation, attesterIndices); err != nil { + return err + } + + // TODO: Generate validators aggregated pub key. + + // Hash parentHashes + shardID + slotNumber + shardBlockHash into a message to use to + // to verify with aggregated public key and aggregated attestation signature. + msg := make([]byte, binary.MaxVarintLen64) + var signedHashesStr []byte + for _, parentHash := range parentHashes { + signedHashesStr = append(signedHashesStr, parentHash.Bytes()...) + signedHashesStr = append(signedHashesStr, byte(' ')) + } + binary.PutUvarint(msg, attestation.Slot%params.CycleLength) + msg = append(msg, signedHashesStr...) + binary.PutUvarint(msg, attestation.ShardId) + msg = append(msg, attestation.ShardBlockHash...) + + msgHash := blake2b.Sum512(msg) + + log.Debugf("Attestation message for shard: %v, slot %v, block hash %v is: %v", + attestation.ShardId, attestation.Slot, attestation.ShardBlockHash, msgHash) + + // TODO: Verify msgHash against aggregated pub key and aggregated signature. + } + return nil +} + +// getSignedParentHashes returns all the parent hashes stored in active state up to last ycle length. +func (b *BeaconChain) getSignedParentHashes(block *types.Block, attestation *pb.AttestationRecord) []*common.Hash { + var signedParentHashes []*common.Hash + start := block.SlotNumber() - attestation.Slot + end := block.SlotNumber() - attestation.Slot - uint64(len(attestation.ObliqueParentHashes)) + params.CycleLength + for _, hashes := range b.ActiveState().RecentBlockHashes()[start:end] { + signedParentHashes = append(signedParentHashes, &hashes) + } + for _, obliqueParentHashes := range attestation.ObliqueParentHashes { + hashes := common.BytesToHash(obliqueParentHashes) + signedParentHashes = append(signedParentHashes, &hashes) + } + return signedParentHashes +} + +// getAttesterIndices returns the attester committee of based from attestation's shard ID and slot number. +func (b *BeaconChain) getAttesterIndices(attestation *pb.AttestationRecord) ([]uint32, error) { + lastStateRecalc := b.CrystallizedState().LastStateRecalc() + shardCommitteeArray := b.CrystallizedState().IndicesForHeights() + shardCommittee := shardCommitteeArray[attestation.Slot-lastStateRecalc+params.CycleLength].ArrayShardAndCommittee + for i := 0; i < len(shardCommittee); i++ { + if attestation.ShardId == shardCommittee[i].ShardId { + return shardCommittee[i].Committee, nil + } + } + return nil, fmt.Errorf("unable to find attestation based on slot: %v, shardID: %v", attestation.Slot, attestation.ShardId) +} + +// validateAttesterBitfields validates the attester bitfields are equal between attestation and crystallized state's calculation. +func (b *BeaconChain) validateAttesterBitfields(attestation *pb.AttestationRecord, attesterIndices []uint32) error { + // Validate attester bit field has the correct length. + if utils.BitLength(len(attesterIndices)) != len(attestation.AttesterBitfield) { + return fmt.Errorf("attestation has incorrect bitfield length. Found %v, expected %v", + len(attestation.AttesterBitfield), utils.BitLength(len(attesterIndices))) + } + + // Valid attestation can not have non-zero trailing bits. + lastBit := len(attesterIndices) + if lastBit%8 != 0 { + for i := 0; i < 8-lastBit%8; i++ { + if utils.CheckBit(attestation.AttesterBitfield, lastBit+i) { + return errors.New("attestation has non-zero trailing bits") + } + } + } + return nil +} + // saveCanonical puts the passed block into the beacon chain db // and also saves a "latest-head" key mapping to the block in the db. func (b *BeaconChain) saveCanonical(block *types.Block) error { diff --git a/beacon-chain/blockchain/core_test.go b/beacon-chain/blockchain/core_test.go index b92935c7a3..a64f0ebdf9 100644 --- a/beacon-chain/blockchain/core_test.go +++ b/beacon-chain/blockchain/core_test.go @@ -286,49 +286,20 @@ func TestCanProcessBlock(t *testing.T) { canProcess, err := beaconChain.CanProcessBlock(&mockFetcher{}, block, true) if err != nil { - t.Fatalf("CanProcessBlocks failed: %v", err) + t.Fatalf("canProcessBlocks failed: %v", err) } if !canProcess { t.Error("Should be able to process block, could not") } - // Negative scenario #1, invalid active hash - block = NewBlock(t, &pb.BeaconBlock{ - SlotNumber: 2, - ActiveStateHash: []byte{'A'}, - CrystallizedStateHash: crystallizedHash[:], - ParentHash: parentHash[:], - }) - canProcess, err = beaconChain.CanProcessBlock(&mockFetcher{}, block, true) - if err == nil { - t.Fatalf("CanProcessBlocks failed: %v", err) - } - if canProcess { - t.Error("Should not be able to process block with invalid active hash") - } - - // Negative scenario #2, invalid crystallized hash - block = NewBlock(t, &pb.BeaconBlock{ - SlotNumber: 2, - ActiveStateHash: activeHash[:], - CrystallizedStateHash: []byte{'A'}, - ParentHash: parentHash[:], - }) - canProcess, err = beaconChain.CanProcessBlock(&mockFetcher{}, block, true) - if err == nil { - t.Fatalf("CanProcessBlocks failed: %v", err) - } - if canProcess { - t.Error("Should not be able to process block with invalid crystallied hash") - } - - // Negative scenario #3, invalid timestamp + // Negative scenario, invalid timestamp block = NewBlock(t, &pb.BeaconBlock{ SlotNumber: 1000000, ActiveStateHash: activeHash[:], CrystallizedStateHash: crystallizedHash[:], ParentHash: parentHash[:], }) + canProcess, err = beaconChain.CanProcessBlock(&mockFetcher{}, block, true) if err == nil { t.Fatalf("CanProcessBlocks failed: %v", err) @@ -338,63 +309,6 @@ func TestCanProcessBlock(t *testing.T) { } } -func TestProcessBlockWithBadHashes(t *testing.T) { - beaconChain, db := startInMemoryBeaconChain(t) - defer db.Close() - - // Test negative scenario where active state hash is different than node's compute. - parentBlock := NewBlock(t, nil) - parentHash, err := parentBlock.Hash() - if err != nil { - t.Fatalf("Failed to compute parent block's hash: %v", err) - } - if err = db.DB().Put(parentHash[:], []byte{}); err != nil { - t.Fatalf("Failed to put parent block on db: %v", err) - } - - // Initialize state. - active := types.NewActiveState(&pb.ActiveState{RecentBlockHashes: [][]byte{{'A'}}}) - activeStateHash, err := active.Hash() - if err != nil { - t.Fatalf("Cannot hash active state: %v", err) - } - - crystallized := types.NewCrystallizedState(&pb.CrystallizedState{LastStateRecalc: 10000}) - crystallizedStateHash, err := crystallized.Hash() - if err != nil { - t.Fatalf("Cannot hash crystallized state: %v", err) - } - - block := NewBlock(t, &pb.BeaconBlock{ - SlotNumber: 1, - ActiveStateHash: activeStateHash[:], - CrystallizedStateHash: crystallizedStateHash[:], - ParentHash: parentHash[:], - }) - - // Test negative scenario where active state hash is different than node's compute. - beaconChain.state.ActiveState = types.NewActiveState(&pb.ActiveState{RecentBlockHashes: [][]byte{{'B'}}}) - - canProcess, err := beaconChain.CanProcessBlock(&mockFetcher{}, block, true) - if err == nil { - t.Fatalf("CanProcessBlocks failed: %v", err) - } - if canProcess { - t.Error("CanProcessBlocks should have returned false with diff state hashes") - } - - // Test negative scenario where crystallized state hash is different than node's compute. - beaconChain.state.CrystallizedState = types.NewCrystallizedState(&pb.CrystallizedState{LastStateRecalc: 9999}) - - canProcess, err = beaconChain.CanProcessBlock(&mockFetcher{}, block, true) - if err == nil { - t.Fatalf("CanProcessBlocks failed: %v", err) - } - if canProcess { - t.Error("CanProcessBlocks should have returned false with diff state hashes") - } -} - func TestIsSlotTransition(t *testing.T) { beaconChain, db := startInMemoryBeaconChain(t) defer db.Close() @@ -459,37 +373,7 @@ func TestCanProcessBlockObserver(t *testing.T) { t.Error("Should be able to process block, could not") } - // Negative scenario #1, invalid crystallized state hash - block = NewBlock(t, &pb.BeaconBlock{ - SlotNumber: 2, - ActiveStateHash: activeHash[:], - CrystallizedStateHash: []byte{'A'}, - ParentHash: parentHash[:], - }) - canProcess, err = beaconChain.CanProcessBlock(nil, block, false) - if err == nil { - t.Fatalf("CanProcessBlocks failed: %v", err) - } - if canProcess { - t.Error("Should not be able to process block with invalid crystallized hash") - } - - // Negative scenario #2, invalid active sate hash - block = NewBlock(t, &pb.BeaconBlock{ - SlotNumber: 2, - ActiveStateHash: []byte{'A'}, - CrystallizedStateHash: crystallizedHash[:], - ParentHash: parentHash[:], - }) - canProcess, err = beaconChain.CanProcessBlock(nil, block, false) - if err == nil { - t.Fatalf("CanProcessBlocks failed: %v", err) - } - if canProcess { - t.Error("Should not be able to process block with invalid active hash") - } - - // Negative scenario #3, invalid timestamp + // Negative scenario, invalid timestamp block = NewBlock(t, &pb.BeaconBlock{ SlotNumber: 1000000, ActiveStateHash: activeHash[:], @@ -564,6 +448,116 @@ func TestComputeActiveState(t *testing.T) { } } +func TestCanProcessAttestations(t *testing.T) { + bc, db := startInMemoryBeaconChain(t) + defer db.Close() + + // Process attestation on this block should fail because AttestationRecord's slot # > than block's slot #. + block := NewBlock(t, &pb.BeaconBlock{ + SlotNumber: 1, + Attestations: []*pb.AttestationRecord{ + {Slot: 2, ShardId: 0}, + }, + }) + if err := bc.processAttestations(block); err == nil { + t.Error("Process attestation should have failed because attestation slot # > block #") + } + + // Process attestation on this should fail because AttestationRecord's slot # > than block's slot #. + block = NewBlock(t, &pb.BeaconBlock{ + SlotNumber: 2 + params.CycleLength, + Attestations: []*pb.AttestationRecord{ + {Slot: 1, ShardId: 0}, + }, + }) + if err := bc.processAttestations(block); err == nil { + t.Error("Process attestation should have failed because attestation slot # < block # + cycle length") + } + + block = NewBlock(t, &pb.BeaconBlock{ + SlotNumber: 0, + Attestations: []*pb.AttestationRecord{ + {Slot: 0, ShardId: 0, ObliqueParentHashes: [][]byte{{'A'}, {'B'}, {'C'}}}, + }, + }) + var recentBlockHashes [][]byte + for i := 0; i < params.CycleLength; i++ { + recentBlockHashes = append(recentBlockHashes, []byte{'X'}) + } + active := types.NewActiveState(&pb.ActiveState{RecentBlockHashes: recentBlockHashes}) + if err := bc.SetActiveState(active); err != nil { + t.Fatalf("unable to mutate active state: %v", err) + } + + // Process attestation on this crystallized state should fail because only committee is in shard 1. + crystallized := types.NewCrystallizedState(&pb.CrystallizedState{ + LastStateRecalc: 64, + IndicesForHeights: []*pb.ShardAndCommitteeArray{ + { + ArrayShardAndCommittee: []*pb.ShardAndCommittee{ + {ShardId: 1, Committee: []uint32{0, 1, 2, 3, 4, 5}}, + }, + }, + }, + }) + if err := bc.SetCrystallizedState(crystallized); err != nil { + t.Fatalf("unable to mutate crystallized state: %v", err) + } + if err := bc.processAttestations(block); err == nil { + t.Error("Process attestation should have failed, there's no committee in shard 0") + } + + // Process attestation should work now, there's a committee in shard 0. + crystallized = types.NewCrystallizedState(&pb.CrystallizedState{ + LastStateRecalc: 64, + IndicesForHeights: []*pb.ShardAndCommitteeArray{ + { + ArrayShardAndCommittee: []*pb.ShardAndCommittee{ + {ShardId: 0, Committee: []uint32{0, 1, 2, 3, 4, 5}}, + }, + }, + }, + }) + if err := bc.SetCrystallizedState(crystallized); err != nil { + t.Fatalf("unable to mutate crystallized state: %v", err) + } + + // Process attestation should fail because attester bit field has incorrect length. + block = NewBlock(t, &pb.BeaconBlock{ + SlotNumber: 0, + Attestations: []*pb.AttestationRecord{ + {Slot: 0, ShardId: 0, AttesterBitfield: []byte{'A', 'B', 'C'}}, + }, + }) + if err := bc.processAttestations(block); err == nil { + t.Error("Process attestation should have failed, incorrect attester bit field length") + } + + // Set attester bitfield to the right length. + block = NewBlock(t, &pb.BeaconBlock{ + SlotNumber: 0, + Attestations: []*pb.AttestationRecord{ + {Slot: 0, ShardId: 0, AttesterBitfield: []byte{'a'}}, + }, + }) + // Process attestation should fail because the non-zero leading bits for votes. + // a is 01100001 + if err := bc.processAttestations(block); err == nil { + t.Error("Process attestation should have failed, incorrect attester bit field length") + } + + // Process attestation should work with correct bitfield bits. + block = NewBlock(t, &pb.BeaconBlock{ + SlotNumber: 0, + Attestations: []*pb.AttestationRecord{ + {Slot: 0, ShardId: 0, AttesterBitfield: []byte{'0'}}, + }, + }) + if err := bc.processAttestations(block); err != nil { + t.Error(err) + } +} + // NewBlock is a helper method to create blocks with valid defaults. // For a generic block, use NewBlock(t, nil). func NewBlock(t *testing.T, b *pb.BeaconBlock) *types.Block { diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index 8cda2b1c04..3f39a28931 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -295,6 +295,13 @@ func (c *ChainService) blockProcessing(done <-chan struct{}) { log.Debugf("Incoming block failed validity conditions: %v", err) } + // Process attestation as a validator. + if err := c.chain.processAttestations(block); err != nil { + // We might receive a lot of blocks that fail attestation processing, + // so we create a debug level log instead of an error log. + log.Debugf("could not process attestation: %v", err) + } + // If we cannot process this block, we keep listening. if !canProcess { continue diff --git a/beacon-chain/blockchain/service_test.go b/beacon-chain/blockchain/service_test.go index d2d539c9fb..c04a98485d 100644 --- a/beacon-chain/blockchain/service_test.go +++ b/beacon-chain/blockchain/service_test.go @@ -419,3 +419,98 @@ func TestUpdateHead(t *testing.T) { chainService.lastSlot = 100 chainService.updateHead(101) } + +func TestProcessingBlockWithAttestations(t *testing.T) { + ctx := context.Background() + config := &database.DBConfig{DataDir: "", Name: "", InMemory: true} + db, err := database.NewDB(config) + if err != nil { + t.Fatalf("could not setup beaconDB: %v", err) + + } + endpoint := "ws://127.0.0.1" + client := &mockClient{} + web3Service, err := powchain.NewWeb3Service(ctx, &powchain.Web3ServiceConfig{Endpoint: endpoint, Pubkey: "", VrcAddr: common.Address{}}, client, client, client) + if err != nil { + t.Fatalf("unable to set up web3 service: %v", err) + } + beaconChain, err := NewBeaconChain(db.DB()) + if err != nil { + t.Fatalf("could not register blockchain service: %v", err) + } + + var validators []*pb.ValidatorRecord + for i := 0; i < 40; i++ { + validator := &pb.ValidatorRecord{Balance: 32, StartDynasty: 1, EndDynasty: 10} + validators = append(validators, validator) + } + + crystallized := types.NewCrystallizedState(&pb.CrystallizedState{ + LastStateRecalc: 64, + Validators: validators, + CurrentDynasty: 5, + IndicesForHeights: []*pb.ShardAndCommitteeArray{ + { + ArrayShardAndCommittee: []*pb.ShardAndCommittee{ + {ShardId: 0, Committee: []uint32{0, 1, 2, 3, 4, 5}}, + }, + }, + }, + }) + crystallizedStateHash, err := crystallized.Hash() + if err != nil { + t.Fatalf("Cannot hash crystallized state: %v", err) + } + if err := beaconChain.SetCrystallizedState(crystallized); err != nil { + t.Fatalf("unable to mutate crystallized state: %v", err) + } + + var recentBlockHashes [][]byte + for i := 0; i < params.CycleLength+1; i++ { + recentBlockHashes = append(recentBlockHashes, []byte{'X'}) + } + active := types.NewActiveState(&pb.ActiveState{RecentBlockHashes: recentBlockHashes}) + if err := beaconChain.SetActiveState(active); err != nil { + t.Fatalf("unable to mutate active state: %v", err) + } + cfg := &Config{ + BeaconBlockBuf: 0, + BeaconDB: db.DB(), + Chain: beaconChain, + Web3Service: web3Service, + } + + chainService, _ := NewChainService(ctx, cfg) + + exitRoutine := make(chan bool) + go func() { + chainService.blockProcessing(chainService.ctx.Done()) + <-exitRoutine + }() + + parentBlock := NewBlock(t, nil) + if err := chainService.SaveBlock(parentBlock); err != nil { + t.Fatal(err) + } + parentHash, _ := parentBlock.Hash() + + activeStateHash, err := active.Hash() + if err != nil { + t.Fatalf("Cannot hash active state: %v", err) + } + + block := NewBlock(t, &pb.BeaconBlock{ + SlotNumber: 1, + ActiveStateHash: activeStateHash[:], + CrystallizedStateHash: crystallizedStateHash[:], + ParentHash: parentHash[:], + PowChainRef: []byte("a"), + Attestations: []*pb.AttestationRecord{ + {Slot: 0, ShardId: 0, AttesterBitfield: []byte{'0'}}, + }, + }) + + chainService.incomingBlockChan <- block + chainService.cancel() + exitRoutine <- true +} diff --git a/beacon-chain/casper/BUILD.bazel b/beacon-chain/casper/BUILD.bazel index e23200e0a5..adb131c627 100644 --- a/beacon-chain/casper/BUILD.bazel +++ b/beacon-chain/casper/BUILD.bazel @@ -27,6 +27,7 @@ go_test( "validator_test.go", ], embed = [":go_default_library"], + race = "off", # TODO(#434): fix issues with tests failing with race on. deps = [ "//beacon-chain/params:go_default_library", "//beacon-chain/types:go_default_library", diff --git a/beacon-chain/casper/incentives.go b/beacon-chain/casper/incentives.go index e3a8a3b730..3942c674e4 100644 --- a/beacon-chain/casper/incentives.go +++ b/beacon-chain/casper/incentives.go @@ -1,8 +1,6 @@ package casper import ( - "fmt" - "github.com/prysmaticlabs/prysm/beacon-chain/params" "github.com/prysmaticlabs/prysm/beacon-chain/types" "github.com/prysmaticlabs/prysm/beacon-chain/utils" @@ -31,10 +29,7 @@ func CalculateRewards(active *types.ActiveState, crystallized *types.Crystallize log.Debug("Applying rewards and penalties for the validators from last cycle") for i, attesterIndex := range activeValidators { - voted, err := utils.CheckBit(latestPendingAtt.AttesterBitfield, attesterIndex) - if err != nil { - return fmt.Errorf("exiting calculate rewards FFG due to %v", err) - } + voted := utils.CheckBit(latestPendingAtt.AttesterBitfield, attesterIndex) if voted { validators[i].Balance += params.AttesterReward } else { diff --git a/beacon-chain/casper/validator.go b/beacon-chain/casper/validator.go index fa1df10ef4..1225ce2e0d 100644 --- a/beacon-chain/casper/validator.go +++ b/beacon-chain/casper/validator.go @@ -89,6 +89,7 @@ func SampleAttestersAndProposers(seed common.Hash, crystallized *types.Crystalli if crystallized.ValidatorsLength() < params.MinCommiteeSize { attesterCount = crystallized.ValidatorsLength() } + fmt.Println() indices, err := utils.ShuffleIndices(seed, ActiveValidatorIndices(crystallized)) if err != nil { return nil, -1, err diff --git a/beacon-chain/casper/validator_test.go b/beacon-chain/casper/validator_test.go index 912cc50f59..0c74bb0f18 100644 --- a/beacon-chain/casper/validator_test.go +++ b/beacon-chain/casper/validator_test.go @@ -81,10 +81,7 @@ func TestHasVoted(t *testing.T) { active.NewPendingAttestation(pendingAttestation) for i := 0; i < len(active.LatestPendingAttestation().AttesterBitfield); i++ { - voted, err := utils.CheckBit(active.LatestPendingAttestation().AttesterBitfield, i) - if err != nil { - t.Errorf("checking bitfield for vote failed: %v", err) - } + voted := utils.CheckBit(active.LatestPendingAttestation().AttesterBitfield, i) if !voted { t.Error("validator voted but received didn't vote") } @@ -97,10 +94,7 @@ func TestHasVoted(t *testing.T) { active.NewPendingAttestation(pendingAttestation) for i := 0; i < len(active.LatestPendingAttestation().AttesterBitfield); i++ { - voted, err := utils.CheckBit(active.LatestPendingAttestation().AttesterBitfield, i) - if err != nil { - t.Errorf("checking bitfield for vote failed: %v", err) - } + voted := utils.CheckBit(active.LatestPendingAttestation().AttesterBitfield, i) if i%2 == 0 && voted { t.Error("validator didn't vote but received voted") } diff --git a/beacon-chain/node/node.go b/beacon-chain/node/node.go index 53a1e49ede..2010e0a905 100644 --- a/beacon-chain/node/node.go +++ b/beacon-chain/node/node.go @@ -285,5 +285,6 @@ func (b *BeaconNode) registerRPCService(ctx *cli.Context) error { KeyFlag: key, SubscriptionBuf: 100, }, chainService) + return b.services.RegisterService(rpcService) } diff --git a/beacon-chain/types/block.go b/beacon-chain/types/block.go index 0bd6c15df8..a388bd248a 100644 --- a/beacon-chain/types/block.go +++ b/beacon-chain/types/block.go @@ -119,6 +119,11 @@ func (b *Block) AttestationCount() int { return len(b.data.Attestations) } +// Attestations returns an array of attestations in the block. +func (b *Block) Attestations() []*pb.AttestationRecord { + return b.data.Attestations +} + // Timestamp returns the Go type time.Time from the protobuf type contained in the block. func (b *Block) Timestamp() (time.Time, error) { return ptypes.Timestamp(b.data.Timestamp) diff --git a/beacon-chain/utils/checkbit.go b/beacon-chain/utils/checkbit.go index ca3ea8d487..7d9a4ce2a8 100644 --- a/beacon-chain/utils/checkbit.go +++ b/beacon-chain/utils/checkbit.go @@ -1,11 +1,7 @@ package utils -import ( - "errors" -) - // CheckBit checks if a bit in a bit field is one. -func CheckBit(bitfield []byte, index int) (bool, error) { +func CheckBit(bitfield []byte, index int) bool { chunkLocation := (index + 1) / 8 indexLocation := (index + 1) % 8 if indexLocation == 0 { @@ -14,15 +10,8 @@ func CheckBit(bitfield []byte, index int) (bool, error) { chunkLocation++ } - if len(bitfield) < chunkLocation { - return false, errors.New("attester index does not exist") - } - field := bitfield[chunkLocation-1] >> (8 - uint(indexLocation)) - if field%2 != 0 { - return true, nil - } - return false, nil + return field%2 != 0 } // BitSetCount counts the number of 1s in a byte using the following algo: @@ -32,3 +21,8 @@ func BitSetCount(v byte) byte { v = (v & 0x33) + ((v >> 2) & 0x33) return (v + (v >> 4)) & 0xF } + +// BitLength returns the length of the bitfield for a given number of attesters in bytes. +func BitLength(b int) int { + return (b + 7) / 8 +} diff --git a/beacon-chain/utils/checkbit_test.go b/beacon-chain/utils/checkbit_test.go index 988b3f063b..b96e5d3691 100644 --- a/beacon-chain/utils/checkbit_test.go +++ b/beacon-chain/utils/checkbit_test.go @@ -15,20 +15,13 @@ func TestCheckBit(t *testing.T) { {a: []byte{146}, b: 4, c: false}, //10010010 {a: []byte{179}, b: 7, c: true}, //10110011 {a: []byte{49}, b: 6, c: false}, //00110001 - } for _, tt := range tests { - set, err := CheckBit(tt.a, tt.b) - if err != nil { - t.Fatalf("Call check bit failed: %v", err) - } + set := CheckBit(tt.a, tt.b) if set != tt.c { t.Errorf("Test check bit set failed with %v and location %v", tt.a, tt.b) } } - if _, err := CheckBit([]byte{1}, 8); err == nil { - t.Errorf("Call check bit should have failed with invalid index") - } } func TestBitSetCount(t *testing.T) { @@ -44,7 +37,25 @@ func TestBitSetCount(t *testing.T) { } for _, tt := range tests { if int(BitSetCount(tt.a)) != tt.b { - t.Errorf("Expected %v, Got %v", tt.b, int(BitSetCount(tt.a))) + t.Errorf("BitSetCount(%d) = %v, want = %d", tt.a, int(BitSetCount(tt.a)), tt.b) + } + } +} + +func TestByteLength(t *testing.T) { + tests := []struct { + a int + b int + }{ + {a: 200, b: 25}, //11001000 + {a: 34324, b: 4291}, //10010100 + {a: 146, b: 19}, //10010010 + {a: 179, b: 23}, //10110011 + {a: 49, b: 7}, //00110001 + } + for _, tt := range tests { + if BitLength(tt.a) != tt.b { + t.Errorf("BitLength(%d) = %d, want = %d", tt.a, BitLength(tt.a), tt.b) } } } diff --git a/validator/beacon/service.go b/validator/beacon/service.go index 7724dae575..a1c5359a4a 100644 --- a/validator/beacon/service.go +++ b/validator/beacon/service.go @@ -38,8 +38,8 @@ func DefaultConfig() *Config { return &Config{AttesterChanBuf: 5, ProposerChanBuf: 5} } -// NewBeaconClient instantiates a service that interacts with a beacon node. -func NewBeaconClient(ctx context.Context, cfg *Config, rpcClient types.RPCClient) *Service { +// NewBeaconValidator instantiates a service that interacts with a beacon node. +func NewBeaconValidator(ctx context.Context, cfg *Config, rpcClient types.RPCClient) *Service { ctx, cancel := context.WithCancel(ctx) return &Service{ ctx: ctx, diff --git a/validator/beacon/service_test.go b/validator/beacon/service_test.go index a49d48a1c1..ffcaba1280 100644 --- a/validator/beacon/service_test.go +++ b/validator/beacon/service_test.go @@ -49,7 +49,7 @@ func (fc *mockClient) BeaconServiceClient() pb.BeaconServiceClient { func TestChannelGetters(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - b := NewBeaconClient(context.Background(), &Config{AttesterChanBuf: 1, ProposerChanBuf: 1}, &mockClient{ctrl}) + b := NewBeaconValidator(context.Background(), &Config{AttesterChanBuf: 1, ProposerChanBuf: 1}, &mockClient{ctrl}) b.proposerChan <- false proposerVal := <-b.ProposerAssignment() if proposerVal { @@ -66,7 +66,7 @@ func TestLifecycle(t *testing.T) { hook := logTest.NewGlobal() ctrl := gomock.NewController(t) defer ctrl.Finish() - b := NewBeaconClient(context.Background(), &Config{AttesterChanBuf: 0, ProposerChanBuf: 0}, &mockClient{ctrl}) + b := NewBeaconValidator(context.Background(), &Config{AttesterChanBuf: 0, ProposerChanBuf: 0}, &mockClient{ctrl}) // Testing default config values. cfg := DefaultConfig() @@ -88,7 +88,7 @@ func TestFetchBeaconBlocks(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - b := NewBeaconClient(context.Background(), &Config{AttesterChanBuf: 1, ProposerChanBuf: 1}, &mockClient{ctrl}) + b := NewBeaconValidator(context.Background(), &Config{AttesterChanBuf: 1, ProposerChanBuf: 1}, &mockClient{ctrl}) // Create mock for the stream returned by LatestBeaconBlock. stream := internal.NewMockBeaconService_LatestBeaconBlockClient(ctrl) @@ -163,7 +163,7 @@ func TestFetchCrystallizedState(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - b := NewBeaconClient(context.Background(), &Config{AttesterChanBuf: 0, ProposerChanBuf: 0}, &mockClient{ctrl}) + b := NewBeaconValidator(context.Background(), &Config{AttesterChanBuf: 0, ProposerChanBuf: 0}, &mockClient{ctrl}) // Creating a faulty stream will trigger error. stream := internal.NewMockBeaconService_LatestCrystallizedStateClient(ctrl) diff --git a/validator/node/node.go b/validator/node/node.go index e4a4e76492..cbba5d6a3a 100644 --- a/validator/node/node.go +++ b/validator/node/node.go @@ -168,7 +168,7 @@ func (s *ShardEthereum) registerBeaconService() error { if err := s.services.FetchService(&rpcService); err != nil { return err } - b := beacon.NewBeaconClient(context.TODO(), beacon.DefaultConfig(), rpcService) + b := beacon.NewBeaconValidator(context.TODO(), beacon.DefaultConfig(), rpcService) return s.services.RegisterService(b) }