diff --git a/beacon-chain/core/blocks/block_operations.go b/beacon-chain/core/blocks/block_operations.go index 4c3bf97d9..216d5ce50 100644 --- a/beacon-chain/core/blocks/block_operations.go +++ b/beacon-chain/core/blocks/block_operations.go @@ -13,6 +13,37 @@ import ( "github.com/prysmaticlabs/prysm/shared/slices" ) +// ProcessPOWReceiptRoots processes the proof-of-work chain's receipts +// contained in a beacon block and appends them as candidate receipt roots +// in the beacon state. +// +// Official spec definition for processing pow receipt roots: +// If block.candidate_pow_receipt_root is x.candidate_pow_receipt_root +// for some x in state.candidate_pow_receipt_roots, set x.vote_count += 1. +// Otherwise, append to state.candidate_pow_receipt_roots a +// new CandidatePoWReceiptRootRecord( +// candidate_pow_receipt_root=block.candidate_pow_receipt_root, +// vote_count=1 +// ) +func ProcessPOWReceiptRoots( + beaconState *types.BeaconState, + block *pb.BeaconBlock, +) []*pb.CandidatePoWReceiptRootRecord { + var newCandidateReceiptRoots []*pb.CandidatePoWReceiptRootRecord + currentCandidateReceiptRoots := beaconState.CandidatePowReceiptRoots() + for idx, root := range currentCandidateReceiptRoots { + if bytes.Equal(block.GetCandidatePowReceiptRootHash32(), root.GetCandidatePowReceiptRootHash32()) { + currentCandidateReceiptRoots[idx].Votes++ + } else { + newCandidateReceiptRoots = append(newCandidateReceiptRoots, &pb.CandidatePoWReceiptRootRecord{ + CandidatePowReceiptRootHash32: block.GetCandidatePowReceiptRootHash32(), + Votes: 1, + }) + } + } + return append(currentCandidateReceiptRoots, newCandidateReceiptRoots...) +} + // ProcessProposerSlashings is one of the operations performed // on each processed beacon block to penalize proposers based on // slashing conditions if any slashable events occurred. diff --git a/beacon-chain/core/blocks/block_operations_test.go b/beacon-chain/core/blocks/block_operations_test.go index 0f2b1e70d..b701d5a9c 100644 --- a/beacon-chain/core/blocks/block_operations_test.go +++ b/beacon-chain/core/blocks/block_operations_test.go @@ -1,6 +1,7 @@ package blocks import ( + "bytes" "fmt" "reflect" "strings" @@ -11,6 +12,55 @@ import ( "github.com/prysmaticlabs/prysm/shared/params" ) +func TestProcessPOWReceiptRoots_SameRootHash(t *testing.T) { + beaconState := types.NewBeaconState(&pb.BeaconState{ + CandidatePowReceiptRoots: []*pb.CandidatePoWReceiptRootRecord{ + { + CandidatePowReceiptRootHash32: []byte{1}, + Votes: 5, + }, + }, + }) + block := &pb.BeaconBlock{ + CandidatePowReceiptRootHash32: []byte{1}, + } + newReceiptRoots := ProcessPOWReceiptRoots(beaconState, block) + if newReceiptRoots[0].Votes != 6 { + t.Errorf("expected votes to increase from 5 to 6, received %v", newReceiptRoots[0].Votes) + } +} + +func TestProcessPOWReceiptRoots_NewCandidateRecord(t *testing.T) { + beaconState := types.NewBeaconState(&pb.BeaconState{ + CandidatePowReceiptRoots: []*pb.CandidatePoWReceiptRootRecord{ + { + CandidatePowReceiptRootHash32: []byte{0}, + Votes: 5, + }, + }, + }) + block := &pb.BeaconBlock{ + CandidatePowReceiptRootHash32: []byte{1}, + } + newReceiptRoots := ProcessPOWReceiptRoots(beaconState, block) + if len(newReceiptRoots) == 1 { + t.Error("expected new receipt roots to have length > 1") + } + if newReceiptRoots[1].Votes != 1 { + t.Errorf( + "expected new receipt roots to have a new element with votes = 1, received votes = %d", + newReceiptRoots[1].Votes, + ) + } + if !bytes.Equal(newReceiptRoots[1].CandidatePowReceiptRootHash32, []byte{1}) { + t.Errorf( + "expected new receipt roots to have a new element with root = %#x, received root = %#x", + []byte{1}, + newReceiptRoots[1].CandidatePowReceiptRootHash32, + ) + } +} + func TestProcessProposerSlashings_ThresholdReached(t *testing.T) { slashings := make([]*pb.ProposerSlashing, params.BeaconConfig().MaxProposerSlashings+1) registry := []*pb.ValidatorRecord{}