Files
prysm/beacon-chain/core/blocks/block_operations_test.go
Ivan Martinez 5e939378d0 Update to spec v0.8.3 (#3355)
* Ignore latest messages in fork choice prior to latest justified

* Make sure Compact Committee Roots isn't changed by process_final_updates

* WIP add attestation bitfields length to match committee length

* Begin work on updating spec tests to 0.8.2

* WIP set up for new spec test structure

* Fix slashings

* Get mainnet tests mostly passing for attestations and attester slashings

* Fix process attestation test

* Undo change

* Complete spec tests for all operations
Still need sanity block tests

* Fix BLS sigs

* Reduce amount of reused code in core/blocks/spectests/

* Fix tests

* Update block sanity tests to 0.8.2

* Update epoch spec tests to 0.8.2

* Clean up all tests and fix shuffling/epoch tests

* WIP update bls tests to 0.8.2

* WIP update bls tests to 0.8.3

* Finish BLS spectest update to 0.8.3

* Fix shuffling spec tests

* Fix more tests

* Update proto ssz spec tests to 0.8.3

* Attempt to fix PrevEpochFFGDataMismatches test

* Goimports

* Fix documentation

* fix test

* Use custom general spec tests

* Reduce code footprint

* Remove unneeded minimal skip

* Fix for comments

* Fix for comments

* Fix test

* Small fixes

* Cleanup block spec tests a bit

* Undo change

* fix validator

* Fix validator tests

* Run gazelle

* Fix error output for epoch spec tests
2019-09-08 12:41:52 -07:00

2135 lines
61 KiB
Go

package blocks_test
import (
"bytes"
"crypto/rand"
"encoding/binary"
"fmt"
"io/ioutil"
"reflect"
"strings"
"testing"
"github.com/gogo/protobuf/proto"
blsintern "github.com/phoreproject/bls"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state/stateutils"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/shared/trieutil"
"github.com/sirupsen/logrus"
"gopkg.in/d4l3k/messagediff.v1"
)
func init() {
logrus.SetOutput(ioutil.Discard) // Ignore "validator activated" logs
}
func TestProcessBlockHeader_WrongProposerSig(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
deposits, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{})
if err != nil {
t.Error(err)
}
beaconState.LatestBlockHeader = &ethpb.BeaconBlockHeader{Slot: 9}
lbhsr, err := ssz.SigningRoot(beaconState.LatestBlockHeader)
if err != nil {
t.Error(err)
}
proposerIdx, err := helpers.BeaconProposerIndex(beaconState)
if err != nil {
t.Error(err)
}
block := &ethpb.BeaconBlock{
Slot: 0,
Body: &ethpb.BeaconBlockBody{
RandaoReveal: []byte{'A', 'B', 'C'},
},
ParentRoot: lbhsr[:],
}
signingRoot, err := ssz.SigningRoot(block)
if err != nil {
t.Fatalf("Failed to get signing root of block: %v", err)
}
dt := helpers.Domain(beaconState, 0, params.BeaconConfig().DomainBeaconProposer)
blockSig := privKeys[proposerIdx+1].Sign(signingRoot[:], dt)
block.Signature = blockSig.Marshal()[:]
_, err = blocks.ProcessBlockHeader(beaconState, block)
want := "signature did not verify"
if !strings.Contains(err.Error(), want) {
t.Errorf("Expected %v, received %v", want, err)
}
}
func TestProcessBlockHeader_DifferentSlots(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
Slashed: true,
}
}
state := &pb.BeaconState{
Validators: validators,
Slot: 0,
LatestBlockHeader: &ethpb.BeaconBlockHeader{Slot: 9},
Fork: &pb.Fork{
PreviousVersion: []byte{0, 0, 0, 0},
CurrentVersion: []byte{0, 0, 0, 0},
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
lbhsr, err := ssz.HashTreeRoot(state.LatestBlockHeader)
if err != nil {
t.Error(err)
}
currentEpoch := helpers.CurrentEpoch(state)
dt := helpers.Domain(state, currentEpoch, params.BeaconConfig().DomainBeaconProposer)
priv, err := bls.RandKey(rand.Reader)
if err != nil {
t.Errorf("failed to generate private key got: %v", err)
}
blockSig := priv.Sign([]byte("hello"), dt)
validators[5896].PublicKey = priv.PublicKey().Marshal()
block := &ethpb.BeaconBlock{
Slot: 1,
Body: &ethpb.BeaconBlockBody{
RandaoReveal: []byte{'A', 'B', 'C'},
},
ParentRoot: lbhsr[:],
Signature: blockSig.Marshal(),
}
_, err = blocks.ProcessBlockHeader(state, block)
want := "is different then block slot"
if !strings.Contains(err.Error(), want) {
t.Errorf("Expected %v, received %v", want, err)
}
}
func TestProcessBlockHeader_PreviousBlockRootNotSignedRoot(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
Slashed: true,
}
}
state := &pb.BeaconState{
Validators: validators,
Slot: 0,
LatestBlockHeader: &ethpb.BeaconBlockHeader{Slot: 9},
Fork: &pb.Fork{
PreviousVersion: []byte{0, 0, 0, 0},
CurrentVersion: []byte{0, 0, 0, 0},
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
currentEpoch := helpers.CurrentEpoch(state)
dt := helpers.Domain(state, currentEpoch, params.BeaconConfig().DomainBeaconProposer)
priv, err := bls.RandKey(rand.Reader)
if err != nil {
t.Errorf("failed to generate private key got: %v", err)
}
blockSig := priv.Sign([]byte("hello"), dt)
validators[5896].PublicKey = priv.PublicKey().Marshal()
block := &ethpb.BeaconBlock{
Slot: 0,
Body: &ethpb.BeaconBlockBody{
RandaoReveal: []byte{'A', 'B', 'C'},
},
ParentRoot: []byte{'A'},
Signature: blockSig.Marshal(),
}
_, err = blocks.ProcessBlockHeader(state, block)
want := "does not match"
if !strings.Contains(err.Error(), want) {
t.Errorf("Expected %v, received %v", want, err)
}
}
func TestProcessBlockHeader_SlashedProposer(t *testing.T) {
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
Slashed: true,
}
}
state := &pb.BeaconState{
Validators: validators,
Slot: 0,
LatestBlockHeader: &ethpb.BeaconBlockHeader{Slot: 9},
Fork: &pb.Fork{
PreviousVersion: []byte{0, 0, 0, 0},
CurrentVersion: []byte{0, 0, 0, 0},
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
parentRoot, err := ssz.SigningRoot(state.LatestBlockHeader)
if err != nil {
t.Error(err)
}
currentEpoch := helpers.CurrentEpoch(state)
dt := helpers.Domain(state, currentEpoch, params.BeaconConfig().DomainBeaconProposer)
priv, err := bls.RandKey(rand.Reader)
if err != nil {
t.Errorf("failed to generate private key got: %v", err)
}
blockSig := priv.Sign([]byte("hello"), dt)
validators[12683].PublicKey = priv.PublicKey().Marshal()
block := &ethpb.BeaconBlock{
Slot: 0,
Body: &ethpb.BeaconBlockBody{
RandaoReveal: []byte{'A', 'B', 'C'},
},
ParentRoot: parentRoot[:],
Signature: blockSig.Marshal(),
}
_, err = blocks.ProcessBlockHeader(state, block)
want := "was previously slashed"
if !strings.Contains(err.Error(), want) {
t.Errorf("Expected %v, received %v", want, err)
}
}
func TestProcessBlockHeader_OK(t *testing.T) {
helpers.ClearAllCaches()
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Fatalf("SlotsPerEpoch should be 64 for these tests to pass")
}
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
Slashed: true,
}
}
state := &pb.BeaconState{
Validators: validators,
Slot: 0,
LatestBlockHeader: &ethpb.BeaconBlockHeader{Slot: 9},
Fork: &pb.Fork{
PreviousVersion: []byte{0, 0, 0, 0},
CurrentVersion: []byte{0, 0, 0, 0},
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
latestBlockSignedRoot, err := ssz.SigningRoot(state.LatestBlockHeader)
if err != nil {
t.Error(err)
}
currentEpoch := helpers.CurrentEpoch(state)
dt := helpers.Domain(state, currentEpoch, params.BeaconConfig().DomainBeaconProposer)
priv, err := bls.RandKey(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate private key got: %v", err)
}
block := &ethpb.BeaconBlock{
Slot: 0,
Body: &ethpb.BeaconBlockBody{
RandaoReveal: []byte{'A', 'B', 'C'},
},
ParentRoot: latestBlockSignedRoot[:],
}
signingRoot, err := ssz.SigningRoot(block)
if err != nil {
t.Fatalf("Failed to get signing root of block: %v", err)
}
blockSig := priv.Sign(signingRoot[:], dt)
block.Signature = blockSig.Marshal()[:]
bodyRoot, err := ssz.HashTreeRoot(block.Body)
if err != nil {
t.Fatalf("Failed to hash block bytes got: %v", err)
}
proposerIdx, err := helpers.BeaconProposerIndex(state)
if err != nil {
t.Fatal(err)
}
validators[proposerIdx].Slashed = false
validators[proposerIdx].PublicKey = priv.PublicKey().Marshal()
newState, err := blocks.ProcessBlockHeader(state, block)
if err != nil {
t.Fatalf("Failed to process block header got: %v", err)
}
var zeroHash [32]byte
var zeroSig [96]byte
nsh := newState.LatestBlockHeader
expected := &ethpb.BeaconBlockHeader{
Slot: block.Slot,
ParentRoot: latestBlockSignedRoot[:],
BodyRoot: bodyRoot[:],
StateRoot: zeroHash[:],
Signature: zeroSig[:],
}
if !proto.Equal(nsh, expected) {
t.Errorf("Expected %v, received %vk9k", expected, nsh)
}
}
func TestProcessRandao_IncorrectProposerFailsVerification(t *testing.T) {
helpers.ClearAllCaches()
deposits, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
// We fetch the proposer's index as that is whom the RANDAO will be verified against.
proposerIdx, err := helpers.BeaconProposerIndex(beaconState)
if err != nil {
t.Fatal(err)
}
epoch := uint64(0)
buf := make([]byte, 32)
binary.LittleEndian.PutUint64(buf, epoch)
domain := helpers.Domain(beaconState, epoch, params.BeaconConfig().DomainRandao)
// We make the previous validator's index sign the message instead of the proposer.
epochSignature := privKeys[proposerIdx-1].Sign(buf, domain)
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
RandaoReveal: epochSignature.Marshal(),
},
}
want := "block randao: signature did not verify"
if _, err := blocks.ProcessRandao(
beaconState,
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %v, received %v", want, err)
}
}
func TestProcessRandao_SignatureVerifiesAndUpdatesLatestStateMixes(t *testing.T) {
deposits, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
epoch := helpers.CurrentEpoch(beaconState)
epochSignature, err := testutil.CreateRandaoReveal(beaconState, epoch, privKeys)
if err != nil {
t.Fatal(err)
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
RandaoReveal: epochSignature,
},
}
newState, err := blocks.ProcessRandao(
beaconState,
block.Body,
)
if err != nil {
t.Errorf("Unexpected error processing block randao: %v", err)
}
currentEpoch := helpers.CurrentEpoch(beaconState)
mix := newState.RandaoMixes[currentEpoch%params.BeaconConfig().EpochsPerHistoricalVector]
if bytes.Equal(mix, params.BeaconConfig().ZeroHash[:]) {
t.Errorf(
"Expected empty signature to be overwritten by randao reveal, received %v",
params.BeaconConfig().EmptySignature,
)
}
}
func TestProcessEth1Data_SetsCorrectly(t *testing.T) {
beaconState := &pb.BeaconState{
Eth1DataVotes: []*ethpb.Eth1Data{},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Eth1Data: &ethpb.Eth1Data{
DepositRoot: []byte{2},
BlockHash: []byte{3},
},
},
}
var err error
for i := uint64(0); i < params.BeaconConfig().SlotsPerEth1VotingPeriod; i++ {
beaconState, err = blocks.ProcessEth1DataInBlock(beaconState, block)
if err != nil {
t.Fatal(err)
}
}
newETH1DataVotes := beaconState.Eth1DataVotes
if len(newETH1DataVotes) <= 1 {
t.Error("Expected new ETH1 data votes to have length > 1")
}
if !proto.Equal(beaconState.Eth1Data, block.Body.Eth1Data) {
t.Errorf(
"Expected latest eth1 data to have been set to %v, received %v",
block.Body.Eth1Data,
beaconState.Eth1Data,
)
}
}
func TestProcessProposerSlashings_UnmatchedHeaderEpochs(t *testing.T) {
registry := make([]*ethpb.Validator, 2)
currentSlot := uint64(0)
slashings := []*ethpb.ProposerSlashing{
{
ProposerIndex: 1,
Header_1: &ethpb.BeaconBlockHeader{
Slot: params.BeaconConfig().SlotsPerEpoch + 1,
},
Header_2: &ethpb.BeaconBlockHeader{
Slot: 0,
},
},
}
beaconState := &pb.BeaconState{
Validators: registry,
Slot: currentSlot,
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
ProposerSlashings: slashings,
},
}
want := "mismatched header epochs"
if _, err := blocks.ProcessProposerSlashings(
beaconState,
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessProposerSlashings_SameHeaders(t *testing.T) {
registry := make([]*ethpb.Validator, 2)
currentSlot := uint64(0)
slashings := []*ethpb.ProposerSlashing{
{
ProposerIndex: 1,
Header_1: &ethpb.BeaconBlockHeader{
Slot: 0,
},
Header_2: &ethpb.BeaconBlockHeader{
Slot: 0,
},
},
}
beaconState := &pb.BeaconState{
Validators: registry,
Slot: currentSlot,
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
ProposerSlashings: slashings,
},
}
want := "expected slashing headers to differ"
if _, err := blocks.ProcessProposerSlashings(
beaconState,
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessProposerSlashings_ValidatorNotSlashable(t *testing.T) {
registry := []*ethpb.Validator{
{
PublicKey: []byte("key"),
Slashed: true,
ActivationEpoch: 0,
WithdrawableEpoch: 0,
},
}
currentSlot := uint64(0)
slashings := []*ethpb.ProposerSlashing{
{
ProposerIndex: 0,
Header_1: &ethpb.BeaconBlockHeader{
Slot: 0,
Signature: []byte("A"),
},
Header_2: &ethpb.BeaconBlockHeader{
Slot: 0,
Signature: []byte("B"),
},
},
}
beaconState := &pb.BeaconState{
Validators: registry,
Slot: currentSlot,
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
ProposerSlashings: slashings,
},
}
want := fmt.Sprintf(
"validator with key %#x is not slashable",
beaconState.Validators[0].PublicKey,
)
if _, err := blocks.ProcessProposerSlashings(
beaconState,
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessProposerSlashings_AppliesCorrectStatus(t *testing.T) {
// We test the case when data is correct and verify the validator
// registry has been updated.
helpers.ClearShuffledValidatorCache()
validators := make([]*ethpb.Validator, 100)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
ActivationEpoch: 0,
}
}
validatorBalances := make([]uint64, len(validators))
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxEffectiveBalance
}
currentSlot := uint64(0)
beaconState := &pb.BeaconState{
Validators: validators,
Slot: currentSlot,
Balances: validatorBalances,
Fork: &pb.Fork{
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
Epoch: 0,
},
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
domain := helpers.Domain(
beaconState,
helpers.CurrentEpoch(beaconState),
params.BeaconConfig().DomainBeaconProposer,
)
privKey, err := bls.RandKey(rand.Reader)
if err != nil {
t.Errorf("Could not generate random private key: %v", err)
}
header1 := &ethpb.BeaconBlockHeader{
Slot: 0,
StateRoot: []byte("A"),
}
signingRoot, err := ssz.SigningRoot(header1)
if err != nil {
t.Errorf("Could not get signing root of beacon block header: %v", err)
}
header1.Signature = privKey.Sign(signingRoot[:], domain).Marshal()[:]
header2 := &ethpb.BeaconBlockHeader{
Slot: 0,
StateRoot: []byte("B"),
}
signingRoot, err = ssz.SigningRoot(header2)
if err != nil {
t.Errorf("Could not get signing root of beacon block header: %v", err)
}
header2.Signature = privKey.Sign(signingRoot[:], domain).Marshal()[:]
slashings := []*ethpb.ProposerSlashing{
{
ProposerIndex: 1,
Header_1: header1,
Header_2: header2,
},
}
beaconState.Validators[1].PublicKey = privKey.PublicKey().Marshal()[:]
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
ProposerSlashings: slashings,
},
}
newState, err := blocks.ProcessProposerSlashings(beaconState, block.Body)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
newStateVals := newState.Validators
if newStateVals[1].ExitEpoch != validators[1].ExitEpoch {
t.Errorf("Proposer with index 1 did not correctly exit,"+"wanted slot:%d, got:%d",
newStateVals[1].ExitEpoch, validators[1].ExitEpoch)
}
}
func TestSlashableAttestationData_CanSlash(t *testing.T) {
att1 := &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 1},
Source: &ethpb.Checkpoint{Root: []byte{'A'}},
}
att2 := &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 1},
Source: &ethpb.Checkpoint{Root: []byte{'B'}},
}
if !blocks.IsSlashableAttestationData(att1, att2) {
t.Error("atts should have been slashable")
}
att1.Target.Epoch = 4
att1.Source.Epoch = 2
att2.Source.Epoch = 3
if !blocks.IsSlashableAttestationData(att1, att2) {
t.Error("atts should have been slashable")
}
}
func TestProcessAttesterSlashings_DataNotSlashable(t *testing.T) {
slashings := []*ethpb.AttesterSlashing{
{
Attestation_1: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
},
Attestation_2: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 1},
Crosslink: &ethpb.Crosslink{
Shard: 3,
},
},
},
},
}
registry := []*ethpb.Validator{}
currentSlot := uint64(0)
beaconState := &pb.BeaconState{
Validators: registry,
Slot: currentSlot,
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
AttesterSlashings: slashings,
},
}
want := fmt.Sprint("attestations are not slashable")
if _, err := blocks.ProcessAttesterSlashings(
beaconState,
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T) {
slashings := []*ethpb.AttesterSlashing{
{
Attestation_1: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: []uint64{0, 1, 2},
CustodyBit_1Indices: []uint64{0, 1, 2},
},
Attestation_2: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: []uint64{0, 1, 2},
CustodyBit_1Indices: []uint64{0, 1, 2},
},
},
}
registry := []*ethpb.Validator{}
currentSlot := uint64(0)
beaconState := &pb.BeaconState{
Validators: registry,
Slot: currentSlot,
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
AttesterSlashings: slashings,
},
}
want := fmt.Sprint("expected no bit 1 indices")
if _, err := blocks.ProcessAttesterSlashings(
beaconState,
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
slashings = []*ethpb.AttesterSlashing{
{
Attestation_1: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+1),
},
Attestation_2: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+1),
},
},
}
block.Body.AttesterSlashings = slashings
want = fmt.Sprint("over max number of allowed indices")
if _, err := blocks.ProcessAttesterSlashings(
beaconState,
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
deposits, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, 0, &ethpb.Eth1Data{})
for _, vv := range beaconState.Validators {
vv.WithdrawableEpoch = 1 * params.BeaconConfig().SlotsPerEpoch
}
att1 := &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: []uint64{0, 1},
}
dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{
Data: att1.Data,
CustodyBit: false,
}
hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit)
if err != nil {
t.Error(err)
}
domain := helpers.Domain(beaconState, 0, params.BeaconConfig().DomainAttestation)
sig0 := privKeys[0].Sign(hashTreeRoot[:], domain)
sig1 := privKeys[1].Sign(hashTreeRoot[:], domain)
aggregateSig := bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
att1.Signature = aggregateSig.Marshal()[:]
att2 := &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 4,
},
},
CustodyBit_0Indices: []uint64{0, 1},
}
dataAndCustodyBit = &pb.AttestationDataAndCustodyBit{
Data: att2.Data,
CustodyBit: false,
}
hashTreeRoot, err = ssz.HashTreeRoot(dataAndCustodyBit)
if err != nil {
t.Error(err)
}
sig0 = privKeys[0].Sign(hashTreeRoot[:], domain)
sig1 = privKeys[1].Sign(hashTreeRoot[:], domain)
aggregateSig = bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
att2.Signature = aggregateSig.Marshal()[:]
slashings := []*ethpb.AttesterSlashing{
{
Attestation_1: att1,
Attestation_2: att2,
},
}
currentSlot := 2 * params.BeaconConfig().SlotsPerEpoch
beaconState.Slot = currentSlot
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
AttesterSlashings: slashings,
},
}
newState, err := blocks.ProcessAttesterSlashings(beaconState, block.Body)
if err != nil {
t.Fatal(err)
}
newRegistry := newState.Validators
// Given the intersection of slashable indices is [1], only validator
// at index 1 should be slashed and exited. We confirm this below.
if newRegistry[1].ExitEpoch != beaconState.Validators[1].ExitEpoch {
t.Errorf(
`
Expected validator at index 1's exit epoch to match
%d, received %d instead
`,
beaconState.Validators[1].ExitEpoch,
newRegistry[1].ExitEpoch,
)
}
}
func TestProcessAttestations_InclusionDelayFailure(t *testing.T) {
attestations := []*ethpb.Attestation{
{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
},
},
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Attestations: attestations,
},
}
deposits, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
attestationSlot, err := helpers.AttestationDataSlot(beaconState, attestations[0].Data)
if err != nil {
t.Fatal(err)
}
want := fmt.Sprintf(
"attestation slot %d + inclusion delay %d > state slot %d",
attestationSlot,
params.BeaconConfig().MinAttestationInclusionDelay,
beaconState.Slot,
)
if _, err := blocks.ProcessAttestations(beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessAttestations_NeitherCurrentNorPrevEpoch(t *testing.T) {
helpers.ClearActiveIndicesCache()
helpers.ClearActiveCountCache()
helpers.ClearStartShardCache()
att := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Attestations: []*ethpb.Attestation{att},
},
}
deposits, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
helpers.ClearAllCaches()
beaconState.Slot += params.BeaconConfig().SlotsPerEpoch*4 + params.BeaconConfig().MinAttestationInclusionDelay
beaconState.PreviousCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
},
}
beaconState.PreviousJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.PreviousEpochAttestations = []*pb.PendingAttestation{}
want := fmt.Sprintf(
"expected target epoch (%d) to be the previous epoch (%d) or the current epoch (%d)",
att.Data.Target.Epoch,
helpers.PrevEpoch(beaconState),
helpers.CurrentEpoch(beaconState),
)
if _, err := blocks.ProcessAttestations(beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessAttestations_CurrentEpochFFGDataMismatches(t *testing.T) {
helpers.ClearAllCaches()
aggBits := bitfield.NewBitlist(1)
custodyBits := bitfield.NewBitlist(1)
attestations := []*ethpb.Attestation{
{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 0},
Source: &ethpb.Checkpoint{Epoch: 1},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
AggregationBits: aggBits,
CustodyBits: custodyBits,
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Attestations: attestations,
},
}
deposits, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
want := fmt.Sprintf(
"expected source epoch %d, received %d",
helpers.CurrentEpoch(beaconState),
attestations[0].Data.Source.Epoch,
)
if _, err := blocks.ProcessAttestations(beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
block.Body.Attestations[0].Data.Source.Epoch = helpers.CurrentEpoch(beaconState)
block.Body.Attestations[0].Data.Source.Root = []byte{}
want = fmt.Sprintf(
"expected source root %#x, received %#x",
beaconState.CurrentJustifiedCheckpoint.Root,
attestations[0].Data.Source.Root,
)
if _, err := blocks.ProcessAttestations(beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessAttestations_PrevEpochFFGDataMismatches(t *testing.T) {
helpers.ClearAllCaches()
deposits, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
aggBits := bitfield.NewBitlist(1)
aggBits.SetBitAt(0, true)
custodyBits := bitfield.NewBitlist(1)
attestations := []*ethpb.Attestation{
{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 1},
Crosslink: &ethpb.Crosslink{
Shard: 0,
},
},
AggregationBits: aggBits,
CustodyBits: custodyBits,
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Attestations: attestations,
},
}
helpers.ClearAllCaches()
beaconState.Slot += params.BeaconConfig().SlotsPerEpoch + params.BeaconConfig().MinAttestationInclusionDelay
beaconState.PreviousCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
},
}
beaconState.PreviousJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.PreviousEpochAttestations = []*pb.PendingAttestation{}
want := fmt.Sprintf(
"expected source epoch %d, received %d",
helpers.PrevEpoch(beaconState),
attestations[0].Data.Source.Epoch,
)
if _, err := blocks.ProcessAttestations(beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
helpers.ClearAllCaches()
block.Body.Attestations[0].Data.Source.Epoch = helpers.PrevEpoch(beaconState)
block.Body.Attestations[0].Data.Target.Epoch = helpers.CurrentEpoch(beaconState)
block.Body.Attestations[0].Data.Source.Root = []byte{}
want = fmt.Sprintf(
"expected source root %#x, received %#x",
beaconState.CurrentJustifiedCheckpoint.Root,
attestations[0].Data.Source.Root,
)
if _, err := blocks.ProcessAttestations(beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessAttestations_CrosslinkMismatches(t *testing.T) {
helpers.ClearAllCaches()
aggBits := bitfield.NewBitlist(1)
aggBits.SetBitAt(0, true)
custodyBits := bitfield.NewBitlist(1)
attestations := []*ethpb.Attestation{
{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
},
},
AggregationBits: aggBits,
CustodyBits: custodyBits,
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Attestations: attestations,
},
}
deposits, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
StartEpoch: 0,
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
want := "mismatched parent crosslink root"
if _, err := blocks.ProcessAttestations(beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
block.Body.Attestations[0].Data.Crosslink.StartEpoch = 0
if _, err := blocks.ProcessAttestations(beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
block.Body.Attestations[0].Data.Crosslink.ParentRoot = encoded[:]
block.Body.Attestations[0].Data.Crosslink.DataRoot = encoded[:]
want = fmt.Sprintf("expected data root %#x == ZERO_HASH", encoded)
if _, err := blocks.ProcessAttestations(beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessAttestations_InvalidAggregationBitsLength(t *testing.T) {
helpers.ClearAllCaches()
deposits, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
aggBits := bitfield.NewBitlist(2)
custodyBits := bitfield.NewBitlist(2)
att := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
AggregationBits: aggBits,
CustodyBits: custodyBits,
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Attestations: []*ethpb.Attestation{att},
},
}
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
StartEpoch: 0,
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
block.Body.Attestations[0].Data.Crosslink.ParentRoot = encoded[:]
block.Body.Attestations[0].Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
expected := "failed to verify aggregation bitfield: wanted participants bitfield length 1, got: 2"
_, err = blocks.ProcessAttestations(beaconState, block.Body)
if !strings.Contains(err.Error(), expected) {
t.Errorf("Expected error checking aggregation and custody bit length, received: %v", err)
}
}
func TestProcessAttestations_OK(t *testing.T) {
helpers.ClearAllCaches()
deposits, privKeys := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
aggBits := bitfield.NewBitlist(1)
aggBits.SetBitAt(0, true)
custodyBits := bitfield.NewBitlist(1)
att := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
AggregationBits: aggBits,
CustodyBits: custodyBits,
}
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
StartEpoch: 0,
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
att.Data.Crosslink.ParentRoot = encoded[:]
att.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
attestingIndices, err := helpers.AttestingIndices(beaconState, att.Data, att.AggregationBits)
if err != nil {
t.Error(err)
}
dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{
Data: att.Data,
CustodyBit: false,
}
hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit)
if err != nil {
t.Error(err)
}
domain := helpers.Domain(beaconState, 0, params.BeaconConfig().DomainAttestation)
sigs := make([]*bls.Signature, len(attestingIndices))
for i, indice := range attestingIndices {
sig := privKeys[indice].Sign(hashTreeRoot[:], domain)
sigs[i] = sig
}
att.Signature = bls.AggregateSignatures(sigs).Marshal()[:]
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Attestations: []*ethpb.Attestation{att},
},
}
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
if _, err := blocks.ProcessAttestations(beaconState, block.Body); err != nil {
t.Errorf("Unexpected error: %v", err)
}
}
func TestProcessAttestationsNoVerify_OK(t *testing.T) {
// Attestation with an empty signature
helpers.ClearAllCaches()
deposits, _ := testutil.SetupInitialDeposits(t, 100)
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}
aggBits := bitfield.NewBitlist(1)
aggBits.SetBitAt(1, true)
custodyBits := bitfield.NewBitlist(1)
att := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 0,
StartEpoch: 0,
},
},
AggregationBits: aggBits,
CustodyBits: custodyBits,
}
zeroSig := [96]byte{}
att.Signature = zeroSig[:]
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
{
Shard: 0,
StartEpoch: 0,
},
}
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
if err != nil {
t.Fatal(err)
}
att.Data.Crosslink.ParentRoot = encoded[:]
att.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
if _, err := blocks.ProcessAttestationNoVerify(beaconState, att); err != nil {
t.Errorf("Unexpected error: %v", err)
}
}
func TestConvertToIndexed_OK(t *testing.T) {
helpers.ClearAllCaches()
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
validators := make([]*ethpb.Validator, 2*params.BeaconConfig().SlotsPerEpoch)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
state := &pb.BeaconState{
Slot: 5,
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
tests := []struct {
aggregationBitfield bitfield.Bitlist
custodyBitfield bitfield.Bitlist
wantedCustodyBit0Indices []uint64
wantedCustodyBit1Indices []uint64
}{
{
aggregationBitfield: bitfield.Bitlist{0x07},
custodyBitfield: bitfield.Bitlist{0x05},
wantedCustodyBit0Indices: []uint64{71},
wantedCustodyBit1Indices: []uint64{127},
},
{
aggregationBitfield: bitfield.Bitlist{0x07},
custodyBitfield: bitfield.Bitlist{0x06},
wantedCustodyBit0Indices: []uint64{127},
wantedCustodyBit1Indices: []uint64{71},
},
{
aggregationBitfield: bitfield.Bitlist{0x07},
custodyBitfield: bitfield.Bitlist{0x07},
wantedCustodyBit0Indices: []uint64{},
wantedCustodyBit1Indices: []uint64{71, 127},
},
}
attestation := &ethpb.Attestation{
Signature: []byte("signed"),
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 0},
Crosslink: &ethpb.Crosslink{
Shard: 3,
},
},
}
for _, tt := range tests {
helpers.ClearAllCaches()
attestation.AggregationBits = tt.aggregationBitfield
attestation.CustodyBits = tt.custodyBitfield
wanted := &ethpb.IndexedAttestation{
CustodyBit_0Indices: tt.wantedCustodyBit0Indices,
CustodyBit_1Indices: tt.wantedCustodyBit1Indices,
Data: attestation.Data,
Signature: attestation.Signature,
}
ia, err := blocks.ConvertToIndexed(state, attestation)
if err != nil {
t.Errorf("failed to convert attestation to indexed attestation: %v", err)
}
if !reflect.DeepEqual(wanted, ia) {
diff, _ := messagediff.PrettyDiff(ia, wanted)
t.Log(diff)
t.Error("convert attestation to indexed attestation didn't result as wanted")
}
}
}
func TestVerifyIndexedAttestation_OK(t *testing.T) {
helpers.ClearAllCaches()
if params.BeaconConfig().SlotsPerEpoch != 64 {
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
}
numOfValidators := 2 * params.BeaconConfig().SlotsPerEpoch
validators := make([]*ethpb.Validator, numOfValidators)
_, keys := testutil.SetupInitialDeposits(t, numOfValidators)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
PublicKey: keys[i].PublicKey().Marshal(),
}
}
state := &pb.BeaconState{
Slot: 5,
Validators: validators,
Fork: &pb.Fork{
Epoch: 0,
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
tests := []struct {
attestation *ethpb.IndexedAttestation
}{
{attestation: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{
Epoch: 2,
},
},
CustodyBit_0Indices: []uint64{1},
}},
{attestation: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{
Epoch: 1,
},
},
CustodyBit_0Indices: []uint64{47, 99},
}},
{attestation: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{
Epoch: 4,
},
},
CustodyBit_0Indices: []uint64{21, 72},
}},
{attestation: &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{
Epoch: 7,
},
},
CustodyBit_0Indices: []uint64{100, 121},
}},
}
for _, tt := range tests {
helpers.ClearAllCaches()
attDataAndCustodyBit := &pb.AttestationDataAndCustodyBit{
Data: tt.attestation.Data,
CustodyBit: false,
}
domain := helpers.Domain(state, tt.attestation.Data.Target.Epoch, params.BeaconConfig().DomainAttestation)
root, err := ssz.HashTreeRoot(attDataAndCustodyBit)
if err != nil {
t.Errorf("Could not find the ssz root: %v", err)
continue
}
var sig []*bls.Signature
for _, idx := range tt.attestation.CustodyBit_0Indices {
validatorSig := keys[idx].Sign(root[:], domain)
sig = append(sig, validatorSig)
}
aggSig := bls.AggregateSignatures(sig)
marshalledSig := aggSig.Marshal()
tt.attestation.Signature = marshalledSig
err = blocks.VerifyIndexedAttestation(state, tt.attestation)
if err != nil {
t.Errorf("failed to verify indexed attestation: %v", err)
}
}
}
func TestValidateIndexedAttestation_AboveMaxLength(t *testing.T) {
indexedAtt1 := &ethpb.IndexedAttestation{
CustodyBit_0Indices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+5),
CustodyBit_1Indices: []uint64{},
}
for i := uint64(0); i < params.BeaconConfig().MaxValidatorsPerCommittee+5; i++ {
indexedAtt1.CustodyBit_0Indices[i] = i
}
want := "over max number of allowed indices"
if err := blocks.VerifyIndexedAttestation(&pb.BeaconState{}, indexedAtt1); !strings.Contains(err.Error(), want) {
t.Errorf("Expected verification to fail return false, received: %v", err)
}
}
func TestProcessDeposits_MerkleBranchFailsVerification(t *testing.T) {
deposit := &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: []byte{1, 2, 3},
Signature: make([]byte, 96),
},
}
leaf, err := ssz.HashTreeRoot(deposit.Data)
if err != nil {
t.Fatal(err)
}
// We then create a merkle branch for the test.
depositTrie, err := trieutil.GenerateTrieFromItems([][]byte{leaf[:]}, int(params.BeaconConfig().DepositContractTreeDepth))
if err != nil {
t.Fatalf("Could not generate trie: %v", err)
}
proof, err := depositTrie.MerkleProof(0)
if err != nil {
t.Fatalf("Could not generate proof: %v", err)
}
deposit.Proof = proof
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Deposits: []*ethpb.Deposit{deposit},
},
}
beaconState := &pb.BeaconState{
Eth1Data: &ethpb.Eth1Data{
DepositRoot: []byte{0},
BlockHash: []byte{1},
},
}
want := "deposit root did not verify"
if _, err := blocks.ProcessDeposits(beaconState, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected error: %s, received %v", want, err)
}
}
func TestProcessDeposits_AddsNewValidatorDeposit(t *testing.T) {
dep, _ := testutil.SetupInitialDeposits(t, 1)
eth1Data := testutil.GenerateEth1Data(t, dep)
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Deposits: []*ethpb.Deposit{dep[0]},
},
}
registry := []*ethpb.Validator{
{
PublicKey: []byte{1},
WithdrawalCredentials: []byte{1, 2, 3},
},
}
balances := []uint64{0}
beaconState := &pb.BeaconState{
Validators: registry,
Balances: balances,
Eth1Data: eth1Data,
Fork: &pb.Fork{
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
},
}
newState, err := blocks.ProcessDeposits(
beaconState,
block.Body,
)
if err != nil {
t.Fatalf("Expected block deposits to process correctly, received: %v", err)
}
if newState.Balances[1] != dep[0].Data.Amount {
t.Errorf(
"Expected state validator balances index 0 to equal %d, received %d",
dep[0].Data.Amount,
newState.Balances[1],
)
}
}
func TestProcessDeposits_RepeatedDeposit_IncreasesValidatorBalance(t *testing.T) {
sk, err := bls.RandKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
deposit := &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: sk.PublicKey().Marshal(),
Amount: 1000,
},
}
sr, err := ssz.SigningRoot(deposit.Data)
if err != nil {
t.Fatal(err)
}
sig := sk.Sign(sr[:], 3)
deposit.Data.Signature = sig.Marshal()
leaf, err := ssz.HashTreeRoot(deposit.Data)
if err != nil {
t.Fatal(err)
}
// We then create a merkle branch for the test.
depositTrie, err := trieutil.GenerateTrieFromItems([][]byte{leaf[:]}, int(params.BeaconConfig().DepositContractTreeDepth))
if err != nil {
t.Fatalf("Could not generate trie: %v", err)
}
proof, err := depositTrie.MerkleProof(0)
if err != nil {
t.Fatalf("Could not generate proof: %v", err)
}
deposit.Proof = proof
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Deposits: []*ethpb.Deposit{deposit},
},
}
registry := []*ethpb.Validator{
{
PublicKey: []byte{1, 2, 3},
},
{
PublicKey: sk.PublicKey().Marshal(),
WithdrawalCredentials: []byte{1},
},
}
balances := []uint64{0, 50}
root := depositTrie.Root()
beaconState := &pb.BeaconState{
Validators: registry,
Balances: balances,
Eth1Data: &ethpb.Eth1Data{
DepositRoot: root[:],
BlockHash: root[:],
},
}
newState, err := blocks.ProcessDeposits(beaconState, block.Body)
if err != nil {
t.Fatalf("Process deposit failed: %v", err)
}
if newState.Balances[1] != 1000+50 {
t.Errorf("Expected balance at index 1 to be 1050, received %d", newState.Balances[1])
}
}
func TestProcessDeposit_AddsNewValidatorDeposit(t *testing.T) {
//Similar to TestProcessDeposits_AddsNewValidatorDeposit except that this test directly calls ProcessDeposit
dep, _ := testutil.SetupInitialDeposits(t, 1)
eth1Data := testutil.GenerateEth1Data(t, dep)
registry := []*ethpb.Validator{
{
PublicKey: []byte{1},
WithdrawalCredentials: []byte{1, 2, 3},
},
}
balances := []uint64{0}
beaconState := &pb.BeaconState{
Validators: registry,
Balances: balances,
Eth1Data: eth1Data,
Fork: &pb.Fork{
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
},
}
newState, err := blocks.ProcessDeposit(
beaconState,
dep[0],
stateutils.ValidatorIndexMap(beaconState),
)
if err != nil {
t.Fatalf("Process deposit failed: %v", err)
}
if len(newState.Validators) != 2 {
t.Errorf("Expected validator list to have length 2, received: %v", len(newState.Validators))
}
if len(newState.Balances) != 2 {
t.Fatalf("Expected validator balances list to have length 2, received: %v", len(newState.Balances))
}
if newState.Balances[1] != dep[0].Data.Amount {
t.Errorf(
"Expected state validator balances index 1 to equal %d, received %d",
dep[0].Data.Amount,
newState.Balances[1],
)
}
}
func TestProcessDeposit_SkipsInvalidDeposit(t *testing.T) {
// Same test settings as in TestProcessDeposit_AddsNewValidatorDeposit, except that we use an invalid signature
dep, _ := testutil.SetupInitialDeposits(t, 1)
dep[0].Data.Signature = make([]byte, 96)
eth1Data := testutil.GenerateEth1Data(t, dep)
testutil.ResetCache() // Can't have an invalid signature in the cache.
registry := []*ethpb.Validator{
{
PublicKey: []byte{1},
WithdrawalCredentials: []byte{1, 2, 3},
},
}
balances := []uint64{0}
beaconState := &pb.BeaconState{
Validators: registry,
Balances: balances,
Eth1Data: eth1Data,
Fork: &pb.Fork{
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
},
}
newState, err := blocks.ProcessDeposit(
beaconState,
dep[0],
stateutils.ValidatorIndexMap(beaconState),
)
if err != nil {
t.Fatalf("Expected invalid block deposit to be ignored without error, received: %v", err)
}
if newState.Eth1DepositIndex != 1 {
t.Errorf(
"Expected Eth1DepositIndex to be increased by 1 after processing an invalid deposit, received change: %v",
newState.Eth1DepositIndex,
)
}
if len(newState.Validators) != 1 {
t.Errorf("Expected validator list to have length 1, received: %v", len(newState.Validators))
}
if len(newState.Balances) != 1 {
t.Errorf("Expected validator balances list to have length 1, received: %v", len(newState.Balances))
}
if newState.Balances[0] != 0 {
t.Errorf("Expected validator balance at index 0 to stay 0, received: %v", newState.Balances[0])
}
}
func TestProcessDeposit_SkipsDepositWithUncompressedSignature(t *testing.T) {
// Same test settings as in TestProcessDeposit_AddsNewValidatorDeposit, except that we use an uncompressed signature
dep, _ := testutil.SetupInitialDeposits(t, 1)
a, _ := blsintern.DecompressG2(bytesutil.ToBytes96(dep[0].Data.Signature))
uncompressedSignature := a.SerializeBytes()
dep[0].Data.Signature = uncompressedSignature[:]
eth1Data := testutil.GenerateEth1Data(t, dep)
testutil.ResetCache() // Can't have an uncompressed signature in the cache.
registry := []*ethpb.Validator{
{
PublicKey: []byte{1},
WithdrawalCredentials: []byte{1, 2, 3},
},
}
balances := []uint64{0}
beaconState := &pb.BeaconState{
Validators: registry,
Balances: balances,
Eth1Data: eth1Data,
Fork: &pb.Fork{
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
},
}
newState, err := blocks.ProcessDeposit(
beaconState,
dep[0],
stateutils.ValidatorIndexMap(beaconState),
)
if err != nil {
t.Fatalf("Expected invalid block deposit to be ignored without error, received: %v", err)
}
if newState.Eth1DepositIndex != 1 {
t.Errorf(
"Expected Eth1DepositIndex to be increased by 1 after processing an invalid deposit, received change: %v",
newState.Eth1DepositIndex,
)
}
if len(newState.Validators) != 1 {
t.Errorf("Expected validator list to have length 1, received: %v", len(newState.Validators))
}
if len(newState.Balances) != 1 {
t.Errorf("Expected validator balances list to have length 1, received: %v", len(newState.Balances))
}
if newState.Balances[0] != 0 {
t.Errorf("Expected validator balance at index 0 to stay 0, received: %v", newState.Balances[0])
}
}
func TestProcessVoluntaryExits_ValidatorNotActive(t *testing.T) {
exits := []*ethpb.VoluntaryExit{
{
ValidatorIndex: 0,
},
}
registry := []*ethpb.Validator{
{
ExitEpoch: 0,
},
}
state := &pb.BeaconState{
Validators: registry,
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
VoluntaryExits: exits,
},
}
want := "non-active validator cannot exit"
if _, err := blocks.ProcessVoluntaryExits(
state,
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessVoluntaryExits_InvalidExitEpoch(t *testing.T) {
exits := []*ethpb.VoluntaryExit{
{
Epoch: 10,
},
}
registry := []*ethpb.Validator{
{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
},
}
state := &pb.BeaconState{
Validators: registry,
Slot: 0,
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
VoluntaryExits: exits,
},
}
want := "expected current epoch >= exit epoch"
if _, err := blocks.ProcessVoluntaryExits(
state,
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessVoluntaryExits_NotActiveLongEnoughToExit(t *testing.T) {
exits := []*ethpb.VoluntaryExit{
{
ValidatorIndex: 0,
Epoch: 0,
},
}
registry := []*ethpb.Validator{
{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
},
}
state := &pb.BeaconState{
Validators: registry,
Slot: 10,
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
VoluntaryExits: exits,
},
}
want := "validator has not been active long enough to exit"
if _, err := blocks.ProcessVoluntaryExits(
state,
block.Body,
); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessVoluntaryExits_AppliesCorrectStatus(t *testing.T) {
exits := []*ethpb.VoluntaryExit{
{
ValidatorIndex: 0,
Epoch: 0,
},
}
registry := []*ethpb.Validator{
{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
ActivationEpoch: 0,
},
}
state := &pb.BeaconState{
Validators: registry,
Fork: &pb.Fork{
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
},
Slot: params.BeaconConfig().SlotsPerEpoch * 5,
}
state.Slot = state.Slot + (params.BeaconConfig().PersistentCommitteePeriod * params.BeaconConfig().SlotsPerEpoch)
priv, err := bls.RandKey(rand.Reader)
if err != nil {
t.Error(err)
}
state.Validators[0].PublicKey = priv.PublicKey().Marshal()[:]
signingRoot, err := ssz.SigningRoot(exits[0])
if err != nil {
t.Error(err)
}
domain := helpers.Domain(state, helpers.CurrentEpoch(state), params.BeaconConfig().DomainVoluntaryExit)
sig := priv.Sign(signingRoot[:], domain)
exits[0].Signature = sig.Marshal()
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
VoluntaryExits: exits,
},
}
newState, err := blocks.ProcessVoluntaryExits(state, block.Body)
if err != nil {
t.Fatalf("Could not process exits: %v", err)
}
newRegistry := newState.Validators
if newRegistry[0].ExitEpoch != helpers.DelayedActivationExitEpoch(state.Slot/params.BeaconConfig().SlotsPerEpoch) {
t.Errorf("Expected validator exit epoch to be %d, got %d",
helpers.DelayedActivationExitEpoch(state.Slot/params.BeaconConfig().SlotsPerEpoch), newRegistry[0].ExitEpoch)
}
}
func TestProcessBeaconTransfers_NotEnoughSenderIndexBalance(t *testing.T) {
registry := []*ethpb.Validator{
{
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
},
}
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
state := &pb.BeaconState{
Validators: registry,
Balances: balances,
}
transfers := []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MaxEffectiveBalance,
Amount: params.BeaconConfig().MaxEffectiveBalance,
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Transfers: transfers,
},
}
want := fmt.Sprintf(
"expected sender balance %d >= %d",
balances[0],
transfers[0].Fee+transfers[0].Amount,
)
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessBeaconTransfers_FailsVerification(t *testing.T) {
testConfig := params.BeaconConfig()
testConfig.MaxTransfers = 1
params.OverrideBeaconConfig(testConfig)
registry := []*ethpb.Validator{
{
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
},
{
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
},
}
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
state := &pb.BeaconState{
Slot: 0,
Validators: registry,
Balances: balances,
}
transfers := []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MaxEffectiveBalance + 1,
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Transfers: transfers,
},
}
want := fmt.Sprintf(
"expected sender balance %d >= %d",
balances[0],
transfers[0].Fee,
)
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
block.Body.Transfers = []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MinDepositAmount,
Slot: state.Slot + 1,
},
}
want = fmt.Sprintf(
"expected beacon state slot %d == transfer slot %d",
state.Slot,
block.Body.Transfers[0].Slot,
)
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
state.Validators[0].WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
state.Validators[0].ActivationEligibilityEpoch = 0
state.Balances[0] = params.BeaconConfig().MinDepositAmount + params.BeaconConfig().MaxEffectiveBalance
block.Body.Transfers = []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MinDepositAmount,
Amount: params.BeaconConfig().MaxEffectiveBalance,
Slot: state.Slot,
},
}
want = "over max transfer"
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
state.Validators[0].WithdrawableEpoch = 0
state.Validators[0].ActivationEligibilityEpoch = params.BeaconConfig().FarFutureEpoch
buf := []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
pubKey := []byte("B")
hashed := hashutil.Hash(pubKey)
buf = append(buf, hashed[:]...)
state.Validators[0].WithdrawalCredentials = buf
block.Body.Transfers = []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MinDepositAmount,
Amount: params.BeaconConfig().MinDepositAmount,
Slot: state.Slot,
SenderWithdrawalPublicKey: []byte("A"),
},
}
want = "invalid public key"
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestProcessBeaconTransfers_OK(t *testing.T) {
helpers.ClearShuffledValidatorCache()
testConfig := params.BeaconConfig()
testConfig.MaxTransfers = 1
params.OverrideBeaconConfig(testConfig)
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount/32)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ActivationEpoch: 0,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
Slashed: false,
WithdrawableEpoch: 0,
}
}
validatorBalances := make([]uint64, len(validators))
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxEffectiveBalance
}
state := &pb.BeaconState{
Validators: validators,
Slot: 0,
Balances: validatorBalances,
Fork: &pb.Fork{
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}
transfer := &ethpb.Transfer{
SenderIndex: 0,
RecipientIndex: 1,
Fee: params.BeaconConfig().MinDepositAmount,
Amount: params.BeaconConfig().MinDepositAmount,
Slot: state.Slot,
}
priv, err := bls.RandKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
pubKey := priv.PublicKey().Marshal()[:]
transfer.SenderWithdrawalPublicKey = pubKey
state.Validators[transfer.SenderIndex].PublicKey = pubKey
signingRoot, err := ssz.SigningRoot(transfer)
if err != nil {
t.Fatalf("Failed to get signing root of block: %v", err)
}
epoch := helpers.CurrentEpoch(state)
dt := helpers.Domain(state, epoch, params.BeaconConfig().DomainTransfer)
transferSig := priv.Sign(signingRoot[:], dt)
transfer.Signature = transferSig.Marshal()[:]
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Transfers: []*ethpb.Transfer{transfer},
},
}
buf := []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
hashed := hashutil.Hash(pubKey)
buf = append(buf, hashed[:][1:]...)
state.Validators[0].WithdrawalCredentials = buf
state.Validators[0].ActivationEligibilityEpoch = params.BeaconConfig().FarFutureEpoch
newState, err := blocks.ProcessTransfers(state, block.Body)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
expectedRecipientIndex := params.BeaconConfig().MaxEffectiveBalance + block.Body.Transfers[0].Amount
if newState.Balances[1] != expectedRecipientIndex {
t.Errorf("Expected recipient balance %d, received %d", newState.Balances[1], expectedRecipientIndex)
}
expectedSenderIndex := params.BeaconConfig().MaxEffectiveBalance - block.Body.Transfers[0].Amount - block.Body.Transfers[0].Fee
if newState.Balances[0] != expectedSenderIndex {
t.Errorf("Expected sender balance %d, received %d", newState.Balances[0], expectedSenderIndex)
}
}