Split state package into state-proto and state-native (#10069)

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
Co-authored-by: kasey <489222+kasey@users.noreply.github.com>
Co-authored-by: Dan Loewenherz <dloewenherz.adm@gmail.com>
Co-authored-by: prestonvanloon <preston@prysmaticlabs.com>
Co-authored-by: Fredrik Svantes <fredrik@ethereum.org>
Co-authored-by: Leo Lara <leolara@users.noreply.github.com>
This commit is contained in:
Radosław Kapka
2022-01-13 12:23:53 +01:00
committed by GitHub
parent 0ee85bcbab
commit 963acefe12
127 changed files with 12534 additions and 113 deletions

View File

@@ -31,7 +31,7 @@ import (
)
func TestListPoolAttestations(t *testing.T) {
state, err := util.NewBeaconState()
bs, err := util.NewBeaconState()
require.NoError(t, err)
att1 := &ethpbv1alpha1.Attestation{
AggregationBits: []byte{1, 10},
@@ -136,7 +136,7 @@ func TestListPoolAttestations(t *testing.T) {
Signature: bytesutil.PadTo([]byte("signature2"), 96),
}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: state},
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
AttestationsPool: attestations.NewPool(),
}
require.NoError(t, s.AttestationsPool.SaveAggregatedAttestations([]*ethpbv1alpha1.Attestation{att1, att2, att3}))
@@ -193,7 +193,7 @@ func TestListPoolAttestations(t *testing.T) {
}
func TestListPoolAttesterSlashings(t *testing.T) {
state, err := util.NewBeaconState()
bs, err := util.NewBeaconState()
require.NoError(t, err)
slashing1 := &ethpbv1alpha1.AttesterSlashing{
Attestation_1: &ethpbv1alpha1.IndexedAttestation{
@@ -269,7 +269,7 @@ func TestListPoolAttesterSlashings(t *testing.T) {
}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: state},
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
SlashingsPool: &slashings.PoolMock{PendingAttSlashings: []*ethpbv1alpha1.AttesterSlashing{slashing1, slashing2}},
}
@@ -281,7 +281,7 @@ func TestListPoolAttesterSlashings(t *testing.T) {
}
func TestListPoolProposerSlashings(t *testing.T) {
state, err := util.NewBeaconState()
bs, err := util.NewBeaconState()
require.NoError(t, err)
slashing1 := &ethpbv1alpha1.ProposerSlashing{
Header_1: &ethpbv1alpha1.SignedBeaconBlockHeader{
@@ -329,7 +329,7 @@ func TestListPoolProposerSlashings(t *testing.T) {
}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: state},
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
SlashingsPool: &slashings.PoolMock{PendingPropSlashings: []*ethpbv1alpha1.ProposerSlashing{slashing1, slashing2}},
}
@@ -341,7 +341,7 @@ func TestListPoolProposerSlashings(t *testing.T) {
}
func TestListPoolVoluntaryExits(t *testing.T) {
state, err := util.NewBeaconState()
bs, err := util.NewBeaconState()
require.NoError(t, err)
exit1 := &ethpbv1alpha1.SignedVoluntaryExit{
Exit: &ethpbv1alpha1.VoluntaryExit{
@@ -359,7 +359,7 @@ func TestListPoolVoluntaryExits(t *testing.T) {
}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: state},
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
VoluntaryExitsPool: &voluntaryexits.PoolMock{Exits: []*ethpbv1alpha1.SignedVoluntaryExit{exit1, exit2}},
}
@@ -378,7 +378,7 @@ func TestSubmitAttesterSlashing_Ok(t *testing.T) {
validator := &ethpbv1alpha1.Validator{
PublicKey: keys[0].PublicKey().Marshal(),
}
state, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
state.Validators = []*ethpbv1alpha1.Validator{validator}
return nil
})
@@ -422,7 +422,7 @@ func TestSubmitAttesterSlashing_Ok(t *testing.T) {
}
for _, att := range []*ethpbv1.IndexedAttestation{slashing.Attestation_1, slashing.Attestation_2} {
sb, err := signing.ComputeDomainAndSign(state, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0])
sb, err := signing.ComputeDomainAndSign(bs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0])
require.NoError(t, err)
sig, err := bls.SignatureFromBytes(sb)
require.NoError(t, err)
@@ -431,14 +431,14 @@ func TestSubmitAttesterSlashing_Ok(t *testing.T) {
broadcaster := &p2pMock.MockBroadcaster{}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: state},
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
SlashingsPool: &slashings.PoolMock{},
Broadcaster: broadcaster,
}
_, err = s.SubmitAttesterSlashing(ctx, slashing)
require.NoError(t, err)
pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, state, true)
pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true)
require.Equal(t, 1, len(pendingSlashings))
assert.DeepEqual(t, migration.V1AttSlashingToV1Alpha1(slashing), pendingSlashings[0])
assert.Equal(t, true, broadcaster.BroadcastCalled)
@@ -446,7 +446,7 @@ func TestSubmitAttesterSlashing_Ok(t *testing.T) {
func TestSubmitAttesterSlashing_InvalidSlashing(t *testing.T) {
ctx := context.Background()
state, err := util.NewBeaconState()
bs, err := util.NewBeaconState()
require.NoError(t, err)
attestation := &ethpbv1.IndexedAttestation{
@@ -474,7 +474,7 @@ func TestSubmitAttesterSlashing_InvalidSlashing(t *testing.T) {
broadcaster := &p2pMock.MockBroadcaster{}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: state},
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
SlashingsPool: &slashings.PoolMock{},
Broadcaster: broadcaster,
}
@@ -493,7 +493,7 @@ func TestSubmitProposerSlashing_Ok(t *testing.T) {
PublicKey: keys[0].PublicKey().Marshal(),
WithdrawableEpoch: eth2types.Epoch(1),
}
state, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
state.Validators = []*ethpbv1alpha1.Validator{validator}
return nil
})
@@ -524,7 +524,7 @@ func TestSubmitProposerSlashing_Ok(t *testing.T) {
for _, h := range []*ethpbv1.SignedBeaconBlockHeader{slashing.SignedHeader_1, slashing.SignedHeader_2} {
sb, err := signing.ComputeDomainAndSign(
state,
bs,
slots.ToEpoch(h.Message.Slot),
h.Message,
params.BeaconConfig().DomainBeaconProposer,
@@ -538,14 +538,14 @@ func TestSubmitProposerSlashing_Ok(t *testing.T) {
broadcaster := &p2pMock.MockBroadcaster{}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: state},
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
SlashingsPool: &slashings.PoolMock{},
Broadcaster: broadcaster,
}
_, err = s.SubmitProposerSlashing(ctx, slashing)
require.NoError(t, err)
pendingSlashings := s.SlashingsPool.PendingProposerSlashings(ctx, state, true)
pendingSlashings := s.SlashingsPool.PendingProposerSlashings(ctx, bs, true)
require.Equal(t, 1, len(pendingSlashings))
assert.DeepEqual(t, migration.V1ProposerSlashingToV1Alpha1(slashing), pendingSlashings[0])
assert.Equal(t, true, broadcaster.BroadcastCalled)
@@ -553,7 +553,7 @@ func TestSubmitProposerSlashing_Ok(t *testing.T) {
func TestSubmitProposerSlashing_InvalidSlashing(t *testing.T) {
ctx := context.Background()
state, err := util.NewBeaconState()
bs, err := util.NewBeaconState()
require.NoError(t, err)
header := &ethpbv1.SignedBeaconBlockHeader{
@@ -574,7 +574,7 @@ func TestSubmitProposerSlashing_InvalidSlashing(t *testing.T) {
broadcaster := &p2pMock.MockBroadcaster{}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: state},
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
SlashingsPool: &slashings.PoolMock{},
Broadcaster: broadcaster,
}
@@ -593,7 +593,7 @@ func TestSubmitVoluntaryExit_Ok(t *testing.T) {
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
PublicKey: keys[0].PublicKey().Marshal(),
}
state, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
state.Validators = []*ethpbv1alpha1.Validator{validator}
// Satisfy activity time required before exiting.
state.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().ShardCommitteePeriod))
@@ -609,7 +609,7 @@ func TestSubmitVoluntaryExit_Ok(t *testing.T) {
Signature: make([]byte, 96),
}
sb, err := signing.ComputeDomainAndSign(state, exit.Message.Epoch, exit.Message, params.BeaconConfig().DomainVoluntaryExit, keys[0])
sb, err := signing.ComputeDomainAndSign(bs, exit.Message.Epoch, exit.Message, params.BeaconConfig().DomainVoluntaryExit, keys[0])
require.NoError(t, err)
sig, err := bls.SignatureFromBytes(sb)
require.NoError(t, err)
@@ -617,14 +617,14 @@ func TestSubmitVoluntaryExit_Ok(t *testing.T) {
broadcaster := &p2pMock.MockBroadcaster{}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: state},
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
VoluntaryExitsPool: &voluntaryexits.PoolMock{},
Broadcaster: broadcaster,
}
_, err = s.SubmitVoluntaryExit(ctx, exit)
require.NoError(t, err)
pendingExits := s.VoluntaryExitsPool.PendingExits(state, state.Slot(), true)
pendingExits := s.VoluntaryExitsPool.PendingExits(bs, bs.Slot(), true)
require.Equal(t, 1, len(pendingExits))
assert.DeepEqual(t, migration.V1ExitToV1Alpha1(exit), pendingExits[0])
assert.Equal(t, true, broadcaster.BroadcastCalled)
@@ -639,7 +639,7 @@ func TestSubmitVoluntaryExit_InvalidValidatorIndex(t *testing.T) {
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
PublicKey: keys[0].PublicKey().Marshal(),
}
state, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
state.Validators = []*ethpbv1alpha1.Validator{validator}
return nil
})
@@ -655,7 +655,7 @@ func TestSubmitVoluntaryExit_InvalidValidatorIndex(t *testing.T) {
broadcaster := &p2pMock.MockBroadcaster{}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: state},
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
VoluntaryExitsPool: &voluntaryexits.PoolMock{},
Broadcaster: broadcaster,
}
@@ -674,7 +674,7 @@ func TestSubmitVoluntaryExit_InvalidExit(t *testing.T) {
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
PublicKey: keys[0].PublicKey().Marshal(),
}
state, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
state.Validators = []*ethpbv1alpha1.Validator{validator}
return nil
})
@@ -690,7 +690,7 @@ func TestSubmitVoluntaryExit_InvalidExit(t *testing.T) {
broadcaster := &p2pMock.MockBroadcaster{}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: state},
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
VoluntaryExitsPool: &voluntaryexits.PoolMock{},
Broadcaster: broadcaster,
}
@@ -716,7 +716,7 @@ func TestServer_SubmitAttestations_Ok(t *testing.T) {
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
},
}
state, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
state.Validators = validators
state.Slot = 1
state.PreviousJustifiedCheckpoint = &ethpbv1alpha1.Checkpoint{
@@ -764,7 +764,7 @@ func TestServer_SubmitAttestations_Ok(t *testing.T) {
for _, att := range []*ethpbv1.Attestation{att1, att2} {
sb, err := signing.ComputeDomainAndSign(
state,
bs,
slots.ToEpoch(att.Data.Slot),
att.Data,
params.BeaconConfig().DomainBeaconAttester,
@@ -777,7 +777,7 @@ func TestServer_SubmitAttestations_Ok(t *testing.T) {
}
broadcaster := &p2pMock.MockBroadcaster{}
chainService := &blockchainmock.ChainService{State: state}
chainService := &blockchainmock.ChainService{State: bs}
s := &Server{
HeadFetcher: chainService,
ChainInfoFetcher: chainService,
@@ -822,7 +822,7 @@ func TestServer_SubmitAttestations_ValidAttestationSubmitted(t *testing.T) {
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
},
}
state, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
state.Validators = validators
state.Slot = 1
state.PreviousJustifiedCheckpoint = &ethpbv1alpha1.Checkpoint{
@@ -871,7 +871,7 @@ func TestServer_SubmitAttestations_ValidAttestationSubmitted(t *testing.T) {
// Don't sign attInvalidSignature.
sb, err := signing.ComputeDomainAndSign(
state,
bs,
slots.ToEpoch(attValid.Data.Slot),
attValid.Data,
params.BeaconConfig().DomainBeaconAttester,
@@ -883,7 +883,7 @@ func TestServer_SubmitAttestations_ValidAttestationSubmitted(t *testing.T) {
attValid.Signature = sig.Marshal()
broadcaster := &p2pMock.MockBroadcaster{}
chainService := &blockchainmock.ChainService{State: state}
chainService := &blockchainmock.ChainService{State: bs}
s := &Server{
HeadFetcher: chainService,
ChainInfoFetcher: chainService,
@@ -922,7 +922,7 @@ func TestServer_SubmitAttestations_InvalidAttestationGRPCHeader(t *testing.T) {
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
},
}
state, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
state.Validators = validators
state.Slot = 1
state.PreviousJustifiedCheckpoint = &ethpbv1alpha1.Checkpoint{
@@ -954,7 +954,7 @@ func TestServer_SubmitAttestations_InvalidAttestationGRPCHeader(t *testing.T) {
Signature: nil,
}
chain := &blockchainmock.ChainService{State: state}
chain := &blockchainmock.ChainService{State: bs}
broadcaster := &p2pMock.MockBroadcaster{}
s := &Server{
ChainInfoFetcher: chain,

View File

@@ -59,7 +59,7 @@ func TestGetAttesterDuties(t *testing.T) {
require.NoError(t, bs.SetSlot(5))
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root")
roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
roots := make([][]byte, fieldparams.BlockRootsLength)
roots[0] = genesisRoot[:]
require.NoError(t, bs.SetBlockRoots(roots))
@@ -141,7 +141,7 @@ func TestGetAttesterDuties(t *testing.T) {
require.NoError(t, bs.SetSlot(5))
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root")
roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
roots := make([][]byte, fieldparams.BlockRootsLength)
roots[0] = genesisRoot[:]
require.NoError(t, bs.SetBlockRoots(roots))
@@ -236,7 +236,7 @@ func TestGetProposerDuties(t *testing.T) {
require.NoError(t, bs.SetSlot(5))
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root")
roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
roots := make([][]byte, fieldparams.BlockRootsLength)
roots[0] = genesisRoot[:]
require.NoError(t, bs.SetBlockRoots(roots))
@@ -285,7 +285,7 @@ func TestGetProposerDuties(t *testing.T) {
require.NoError(t, bs.SetSlot(5))
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root")
roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
roots := make([][]byte, fieldparams.BlockRootsLength)
roots[0] = genesisRoot[:]
require.NoError(t, bs.SetBlockRoots(roots))
@@ -1117,7 +1117,7 @@ func TestSubmitBeaconCommitteeSubscription(t *testing.T) {
require.NoError(t, bs.SetSlot(5))
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root")
roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
roots := make([][]byte, fieldparams.BlockRootsLength)
roots[0] = genesisRoot[:]
require.NoError(t, bs.SetBlockRoots(roots))
@@ -1258,7 +1258,7 @@ func TestSubmitSyncCommitteeSubscription(t *testing.T) {
require.NoError(t, err, "Could not set up genesis state")
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root")
roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
roots := make([][]byte, fieldparams.BlockRootsLength)
roots[0] = genesisRoot[:]
require.NoError(t, bs.SetBlockRoots(roots))

View File

@@ -29,14 +29,14 @@ func TestGetState(t *testing.T) {
state.Slot = headSlot
return nil
}
state, err := util.NewBeaconState(util.FillRootsNaturalOpt, fillSlot)
newBeaconState, err := util.NewBeaconState(util.FillRootsNaturalOpt, fillSlot)
require.NoError(t, err)
stateRoot, err := state.HashTreeRoot(ctx)
stateRoot, err := newBeaconState.HashTreeRoot(ctx)
require.NoError(t, err)
t.Run("head", func(t *testing.T) {
p := StateProvider{
ChainInfoFetcher: &chainMock.ChainService{State: state},
ChainInfoFetcher: &chainMock.ChainService{State: newBeaconState},
}
s, err := p.State(ctx, []byte("head"))
@@ -59,17 +59,17 @@ func TestGetState(t *testing.T) {
r, err := b.Block.HashTreeRoot()
require.NoError(t, err)
state, err := util.NewBeaconState(func(state *ethpb.BeaconState) error {
bs, err := util.NewBeaconState(func(state *ethpb.BeaconState) error {
state.BlockRoots[0] = r[:]
return nil
})
require.NoError(t, err)
stateRoot, err := state.HashTreeRoot(ctx)
newStateRoot, err := bs.HashTreeRoot(ctx)
require.NoError(t, err)
require.NoError(t, db.SaveStateSummary(ctx, &ethpb.StateSummary{Root: r[:]}))
require.NoError(t, db.SaveGenesisBlockRoot(ctx, r))
require.NoError(t, db.SaveState(ctx, state, r))
require.NoError(t, db.SaveState(ctx, bs, r))
p := StateProvider{
BeaconDB: db,
@@ -79,12 +79,12 @@ func TestGetState(t *testing.T) {
require.NoError(t, err)
sRoot, err := s.HashTreeRoot(ctx)
require.NoError(t, err)
assert.DeepEqual(t, stateRoot, sRoot)
assert.DeepEqual(t, newStateRoot, sRoot)
})
t.Run("finalized", func(t *testing.T) {
stateGen := stategen.NewMockService()
stateGen.StatesByRoot[stateRoot] = state
stateGen.StatesByRoot[stateRoot] = newBeaconState
p := StateProvider{
ChainInfoFetcher: &chainMock.ChainService{
@@ -104,7 +104,7 @@ func TestGetState(t *testing.T) {
t.Run("justified", func(t *testing.T) {
stateGen := stategen.NewMockService()
stateGen.StatesByRoot[stateRoot] = state
stateGen.StatesByRoot[stateRoot] = newBeaconState
p := StateProvider{
ChainInfoFetcher: &chainMock.ChainService{
@@ -126,10 +126,10 @@ func TestGetState(t *testing.T) {
stateId, err := hexutil.Decode("0x" + strings.Repeat("0", 63) + "1")
require.NoError(t, err)
stateGen := stategen.NewMockService()
stateGen.StatesByRoot[bytesutil.ToBytes32(stateId)] = state
stateGen.StatesByRoot[bytesutil.ToBytes32(stateId)] = newBeaconState
p := StateProvider{
ChainInfoFetcher: &chainMock.ChainService{State: state},
ChainInfoFetcher: &chainMock.ChainService{State: newBeaconState},
StateGenService: stateGen,
}
@@ -142,7 +142,7 @@ func TestGetState(t *testing.T) {
t.Run("hex_root_not_found", func(t *testing.T) {
p := StateProvider{
ChainInfoFetcher: &chainMock.ChainService{State: state},
ChainInfoFetcher: &chainMock.ChainService{State: newBeaconState},
}
stateId, err := hexutil.Decode("0x" + strings.Repeat("f", 64))
require.NoError(t, err)
@@ -152,7 +152,7 @@ func TestGetState(t *testing.T) {
t.Run("slot", func(t *testing.T) {
stateGen := stategen.NewMockService()
stateGen.StatesBySlot[headSlot] = state
stateGen.StatesBySlot[headSlot] = newBeaconState
p := StateProvider{
GenesisTimeFetcher: &chainMock.ChainService{Slot: &headSlot},
@@ -191,9 +191,9 @@ func TestGetStateRoot(t *testing.T) {
state.Slot = headSlot
return nil
}
state, err := util.NewBeaconState(util.FillRootsNaturalOpt, fillSlot)
newBeaconState, err := util.NewBeaconState(util.FillRootsNaturalOpt, fillSlot)
require.NoError(t, err)
stateRoot, err := state.HashTreeRoot(ctx)
stateRoot, err := newBeaconState.HashTreeRoot(ctx)
require.NoError(t, err)
t.Run("head", func(t *testing.T) {
@@ -201,7 +201,7 @@ func TestGetStateRoot(t *testing.T) {
b.Block.StateRoot = stateRoot[:]
p := StateProvider{
ChainInfoFetcher: &chainMock.ChainService{
State: state,
State: newBeaconState,
Block: wrapper.WrappedPhase0SignedBeaconBlock(b),
},
}
@@ -218,7 +218,7 @@ func TestGetStateRoot(t *testing.T) {
r, err := b.Block.HashTreeRoot()
require.NoError(t, err)
state, err := util.NewBeaconState(func(state *ethpb.BeaconState) error {
bs, err := util.NewBeaconState(func(state *ethpb.BeaconState) error {
state.BlockRoots[0] = r[:]
return nil
})
@@ -226,7 +226,7 @@ func TestGetStateRoot(t *testing.T) {
require.NoError(t, db.SaveStateSummary(ctx, &ethpb.StateSummary{Root: r[:]}))
require.NoError(t, db.SaveGenesisBlockRoot(ctx, r))
require.NoError(t, db.SaveState(ctx, state, r))
require.NoError(t, db.SaveState(ctx, bs, r))
p := StateProvider{
BeaconDB: db,
@@ -306,7 +306,7 @@ func TestGetStateRoot(t *testing.T) {
require.NoError(t, err)
p := StateProvider{
ChainInfoFetcher: &chainMock.ChainService{State: state},
ChainInfoFetcher: &chainMock.ChainService{State: newBeaconState},
}
s, err := p.StateRoot(ctx, stateId)
@@ -316,7 +316,7 @@ func TestGetStateRoot(t *testing.T) {
t.Run("hex_root_not_found", func(t *testing.T) {
p := StateProvider{
ChainInfoFetcher: &chainMock.ChainService{State: state},
ChainInfoFetcher: &chainMock.ChainService{State: newBeaconState},
}
stateId, err := hexutil.Decode("0x" + strings.Repeat("f", 64))
require.NoError(t, err)

View File

@@ -5,6 +5,7 @@ go_library(
srcs = [
"altair.go",
"phase0.go",
"prometheus.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state",
visibility = [
@@ -25,6 +26,8 @@ go_library(
deps = [
"//config/fieldparams:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
],

View File

@@ -159,6 +159,7 @@ type WriteOnlyBlockRoots interface {
// WriteOnlyStateRoots defines a struct which only has write access to state roots methods.
type WriteOnlyStateRoots interface {
SetStateRoots(val [][]byte) error
UpdateStateRootAtIndex(idx uint64, stateRoot [32]byte) error
}

View File

@@ -0,0 +1,13 @@
package state
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var (
StateCount = promauto.NewGauge(prometheus.GaugeOpts{
Name: "beacon_state_count",
Help: "Count the number of active beacon state objects.",
})
)

View File

@@ -0,0 +1,40 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"field_trie.go",
"field_trie_helpers.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/fieldtrie",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"field_trie_test.go",
"helpers_test.go",
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//config/params:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
],
)

View File

@@ -0,0 +1,200 @@
package fieldtrie
import (
"reflect"
"sync"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
)
// FieldTrie is the representation of the representative
// trie of the particular field.
type FieldTrie struct {
*sync.RWMutex
reference *stateutil.Reference
fieldLayers [][]*[32]byte
field types.FieldIndex
dataType types.DataType
length uint64
numOfElems int
}
// NewFieldTrie is the constructor for the field trie data structure. It creates the corresponding
// trie according to the given parameters. Depending on whether the field is a basic/composite array
// which is either fixed/variable length, it will appropriately determine the trie.
func NewFieldTrie(field types.FieldIndex, dataType types.DataType, elements interface{}, length uint64) (*FieldTrie, error) {
if elements == nil {
return &FieldTrie{
field: field,
dataType: dataType,
reference: stateutil.NewRef(1),
RWMutex: new(sync.RWMutex),
length: length,
numOfElems: 0,
}, nil
}
fieldRoots, err := fieldConverters(field, []uint64{}, elements, true)
if err != nil {
return nil, err
}
if err := validateElements(field, dataType, elements, length); err != nil {
return nil, err
}
switch dataType {
case types.BasicArray:
fl, err := stateutil.ReturnTrieLayer(fieldRoots, length)
if err != nil {
return nil, err
}
return &FieldTrie{
fieldLayers: fl,
field: field,
dataType: dataType,
reference: stateutil.NewRef(1),
RWMutex: new(sync.RWMutex),
length: length,
numOfElems: reflect.ValueOf(elements).Len(),
}, nil
case types.CompositeArray, types.CompressedArray:
return &FieldTrie{
fieldLayers: stateutil.ReturnTrieLayerVariable(fieldRoots, length),
field: field,
dataType: dataType,
reference: stateutil.NewRef(1),
RWMutex: new(sync.RWMutex),
length: length,
numOfElems: reflect.ValueOf(elements).Len(),
}, nil
default:
return nil, errors.Errorf("unrecognized data type in field map: %v", reflect.TypeOf(dataType).Name())
}
}
// RecomputeTrie rebuilds the affected branches in the trie according to the provided
// changed indices and elements. This recomputes the trie according to the particular
// field the trie is based on.
func (f *FieldTrie) RecomputeTrie(indices []uint64, elements interface{}) ([32]byte, error) {
f.Lock()
defer f.Unlock()
var fieldRoot [32]byte
if len(indices) == 0 {
return f.TrieRoot()
}
fieldRoots, err := fieldConverters(f.field, indices, elements, false)
if err != nil {
return [32]byte{}, err
}
if err := f.validateIndices(indices); err != nil {
return [32]byte{}, err
}
switch f.dataType {
case types.BasicArray:
fieldRoot, f.fieldLayers, err = stateutil.RecomputeFromLayer(fieldRoots, indices, f.fieldLayers)
if err != nil {
return [32]byte{}, err
}
f.numOfElems = reflect.ValueOf(elements).Len()
return fieldRoot, nil
case types.CompositeArray:
fieldRoot, f.fieldLayers, err = stateutil.RecomputeFromLayerVariable(fieldRoots, indices, f.fieldLayers)
if err != nil {
return [32]byte{}, err
}
f.numOfElems = reflect.ValueOf(elements).Len()
return stateutil.AddInMixin(fieldRoot, uint64(len(f.fieldLayers[0])))
case types.CompressedArray:
numOfElems, err := f.field.ElemsInChunk()
if err != nil {
return [32]byte{}, err
}
// We remove the duplicates here in order to prevent
// duplicated insertions into the trie.
newIndices := []uint64{}
indexExists := make(map[uint64]bool)
newRoots := make([][32]byte, 0, len(fieldRoots)/int(numOfElems))
for i, idx := range indices {
startIdx := idx / numOfElems
if indexExists[startIdx] {
continue
}
newIndices = append(newIndices, startIdx)
indexExists[startIdx] = true
newRoots = append(newRoots, fieldRoots[i])
}
fieldRoot, f.fieldLayers, err = stateutil.RecomputeFromLayerVariable(newRoots, newIndices, f.fieldLayers)
if err != nil {
return [32]byte{}, err
}
f.numOfElems = reflect.ValueOf(elements).Len()
return stateutil.AddInMixin(fieldRoot, uint64(f.numOfElems))
default:
return [32]byte{}, errors.Errorf("unrecognized data type in field map: %v", reflect.TypeOf(f.dataType).Name())
}
}
// CopyTrie copies the references to the elements the trie
// is built on.
func (f *FieldTrie) CopyTrie() *FieldTrie {
if f.fieldLayers == nil {
return &FieldTrie{
field: f.field,
dataType: f.dataType,
reference: stateutil.NewRef(1),
RWMutex: new(sync.RWMutex),
length: f.length,
numOfElems: f.numOfElems,
}
}
dstFieldTrie := make([][]*[32]byte, len(f.fieldLayers))
for i, layer := range f.fieldLayers {
dstFieldTrie[i] = make([]*[32]byte, len(layer))
copy(dstFieldTrie[i], layer)
}
return &FieldTrie{
fieldLayers: dstFieldTrie,
field: f.field,
dataType: f.dataType,
reference: stateutil.NewRef(1),
RWMutex: new(sync.RWMutex),
length: f.length,
numOfElems: f.numOfElems,
}
}
// TrieRoot returns the corresponding root of the trie.
func (f *FieldTrie) TrieRoot() ([32]byte, error) {
switch f.dataType {
case types.BasicArray:
return *f.fieldLayers[len(f.fieldLayers)-1][0], nil
case types.CompositeArray:
trieRoot := *f.fieldLayers[len(f.fieldLayers)-1][0]
return stateutil.AddInMixin(trieRoot, uint64(len(f.fieldLayers[0])))
case types.CompressedArray:
trieRoot := *f.fieldLayers[len(f.fieldLayers)-1][0]
return stateutil.AddInMixin(trieRoot, uint64(f.numOfElems))
default:
return [32]byte{}, errors.Errorf("unrecognized data type in field map: %v", reflect.TypeOf(f.dataType).Name())
}
}
// FieldReference returns the underlying field reference
// object for the trie.
func (f *FieldTrie) FieldReference() *stateutil.Reference {
return f.reference
}
// Empty checks whether the underlying field trie is
// empty or not.
func (f *FieldTrie) Empty() bool {
return f == nil || len(f.fieldLayers) == 0
}
// InsertFieldLayer manually inserts a field layer. This method
// bypasses the normal method of field computation, it is only
// meant to be used in tests.
func (f *FieldTrie) InsertFieldLayer(layer [][]*[32]byte) {
f.fieldLayers = layer
}

View File

@@ -0,0 +1,295 @@
package fieldtrie
import (
"encoding/binary"
"fmt"
"reflect"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/crypto/hash"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/encoding/ssz"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/runtime/version"
)
// ProofFromMerkleLayers creates a proof starting at the leaf index of the state Merkle layers.
func ProofFromMerkleLayers(layers [][][]byte, startingLeafIndex types.FieldIndex) [][]byte {
// The merkle tree structure looks as follows:
// [[r1, r2, r3, r4], [parent1, parent2], [root]]
proof := make([][]byte, 0)
currentIndex := startingLeafIndex
for i := 0; i < len(layers)-1; i++ {
neighborIdx := currentIndex ^ 1
neighbor := layers[i][neighborIdx]
proof = append(proof, neighbor)
currentIndex = currentIndex / 2
}
return proof
}
func (f *FieldTrie) validateIndices(idxs []uint64) error {
length := f.length
if f.dataType == types.CompressedArray {
comLength, err := f.field.ElemsInChunk()
if err != nil {
return err
}
length *= comLength
}
for _, idx := range idxs {
if idx >= length {
return errors.Errorf("invalid index for field %s: %d >= length %d", f.field.String(version.Phase0), idx, length)
}
}
return nil
}
func validateElements(field types.FieldIndex, dataType types.DataType, elements interface{}, length uint64) error {
if dataType == types.CompressedArray {
comLength, err := field.ElemsInChunk()
if err != nil {
return err
}
length *= comLength
}
val := reflect.ValueOf(elements)
if val.Len() > int(length) {
return errors.Errorf("elements length is larger than expected for field %s: %d > %d", field.String(version.Phase0), val.Len(), length)
}
return nil
}
// fieldConverters converts the corresponding field and the provided elements to the appropriate roots.
func fieldConverters(field types.FieldIndex, indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
switch field {
case types.BlockRoots, types.StateRoots, types.RandaoMixes:
val, ok := elements.([][]byte)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([][]byte{}).Name(), reflect.TypeOf(elements).Name())
}
return handleByteArrays(val, indices, convertAll)
case types.Eth1DataVotes:
val, ok := elements.([]*ethpb.Eth1Data)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]*ethpb.Eth1Data{}).Name(), reflect.TypeOf(elements).Name())
}
return handleEth1DataSlice(val, indices, convertAll)
case types.Validators:
val, ok := elements.([]*ethpb.Validator)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]*ethpb.Validator{}).Name(), reflect.TypeOf(elements).Name())
}
return handleValidatorSlice(val, indices, convertAll)
case types.PreviousEpochAttestations, types.CurrentEpochAttestations:
val, ok := elements.([]*ethpb.PendingAttestation)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]*ethpb.PendingAttestation{}).Name(), reflect.TypeOf(elements).Name())
}
return handlePendingAttestation(val, indices, convertAll)
case types.Balances:
val, ok := elements.([]uint64)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]uint64{}).Name(), reflect.TypeOf(elements).Name())
}
return handleBalanceSlice(val, indices, convertAll)
default:
return [][32]byte{}, errors.Errorf("got unsupported type of %v", reflect.TypeOf(elements).Name())
}
}
// handleByteArrays computes and returns byte arrays in a slice of root format.
func handleByteArrays(val [][]byte, indices []uint64, convertAll bool) ([][32]byte, error) {
length := len(indices)
if convertAll {
length = len(val)
}
roots := make([][32]byte, 0, length)
rootCreator := func(input []byte) {
newRoot := bytesutil.ToBytes32(input)
roots = append(roots, newRoot)
}
if convertAll {
for i := range val {
rootCreator(val[i])
}
return roots, nil
}
if len(val) > 0 {
for _, idx := range indices {
if idx > uint64(len(val))-1 {
return nil, fmt.Errorf("index %d greater than number of byte arrays %d", idx, len(val))
}
rootCreator(val[idx])
}
}
return roots, nil
}
// handleValidatorSlice returns the validator indices in a slice of root format.
func handleValidatorSlice(val []*ethpb.Validator, indices []uint64, convertAll bool) ([][32]byte, error) {
length := len(indices)
if convertAll {
length = len(val)
}
roots := make([][32]byte, 0, length)
hasher := hash.CustomSHA256Hasher()
rootCreator := func(input *ethpb.Validator) error {
newRoot, err := stateutil.ValidatorRootWithHasher(hasher, input)
if err != nil {
return err
}
roots = append(roots, newRoot)
return nil
}
if convertAll {
for i := range val {
err := rootCreator(val[i])
if err != nil {
return nil, err
}
}
return roots, nil
}
if len(val) > 0 {
for _, idx := range indices {
if idx > uint64(len(val))-1 {
return nil, fmt.Errorf("index %d greater than number of validators %d", idx, len(val))
}
err := rootCreator(val[idx])
if err != nil {
return nil, err
}
}
}
return roots, nil
}
// handleEth1DataSlice processes a list of eth1data and indices into the appropriate roots.
func handleEth1DataSlice(val []*ethpb.Eth1Data, indices []uint64, convertAll bool) ([][32]byte, error) {
length := len(indices)
if convertAll {
length = len(val)
}
roots := make([][32]byte, 0, length)
hasher := hash.CustomSHA256Hasher()
rootCreator := func(input *ethpb.Eth1Data) error {
newRoot, err := stateutil.Eth1DataRootWithHasher(hasher, input)
if err != nil {
return err
}
roots = append(roots, newRoot)
return nil
}
if convertAll {
for i := range val {
err := rootCreator(val[i])
if err != nil {
return nil, err
}
}
return roots, nil
}
if len(val) > 0 {
for _, idx := range indices {
if idx > uint64(len(val))-1 {
return nil, fmt.Errorf("index %d greater than number of items in eth1 data slice %d", idx, len(val))
}
err := rootCreator(val[idx])
if err != nil {
return nil, err
}
}
}
return roots, nil
}
func handlePendingAttestation(val []*ethpb.PendingAttestation, indices []uint64, convertAll bool) ([][32]byte, error) {
length := len(indices)
if convertAll {
length = len(val)
}
roots := make([][32]byte, 0, length)
hasher := hash.CustomSHA256Hasher()
rootCreator := func(input *ethpb.PendingAttestation) error {
newRoot, err := stateutil.PendingAttRootWithHasher(hasher, input)
if err != nil {
return err
}
roots = append(roots, newRoot)
return nil
}
if convertAll {
for i := range val {
err := rootCreator(val[i])
if err != nil {
return nil, err
}
}
return roots, nil
}
if len(val) > 0 {
for _, idx := range indices {
if idx > uint64(len(val))-1 {
return nil, fmt.Errorf("index %d greater than number of pending attestations %d", idx, len(val))
}
err := rootCreator(val[idx])
if err != nil {
return nil, err
}
}
}
return roots, nil
}
func handleBalanceSlice(val, indices []uint64, convertAll bool) ([][32]byte, error) {
if convertAll {
balancesMarshaling := make([][]byte, 0)
for _, b := range val {
balanceBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(balanceBuf, b)
balancesMarshaling = append(balancesMarshaling, balanceBuf)
}
balancesChunks, err := ssz.PackByChunk(balancesMarshaling)
if err != nil {
return [][32]byte{}, errors.Wrap(err, "could not pack balances into chunks")
}
return balancesChunks, nil
}
if len(val) > 0 {
numOfElems, err := types.Balances.ElemsInChunk()
if err != nil {
return nil, err
}
roots := [][32]byte{}
for _, idx := range indices {
// We split the indexes into their relevant groups. Balances
// are compressed according to 4 values -> 1 chunk.
startIdx := idx / numOfElems
startGroup := startIdx * numOfElems
chunk := [32]byte{}
sizeOfElem := len(chunk) / int(numOfElems)
for i, j := 0, startGroup; j < startGroup+numOfElems; i, j = i+sizeOfElem, j+1 {
wantedVal := uint64(0)
// We are adding chunks in sets of 4, if the set is at the edge of the array
// then you will need to zero out the rest of the chunk. Ex : 41 indexes,
// so 41 % 4 = 1 . There are 3 indexes, which do not exist yet but we
// have to add in as a root. These 3 indexes are then given a 'zero' value.
if int(j) < len(val) {
wantedVal = val[j]
}
binary.LittleEndian.PutUint64(chunk[i:i+sizeOfElem], wantedVal)
}
roots = append(roots, chunk)
}
return roots, nil
}
return [][32]byte{}, nil
}

View File

@@ -0,0 +1,79 @@
package fieldtrie_test
import (
"testing"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/fieldtrie"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
stateTypes "github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/params"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
)
func TestFieldTrie_NewTrie(t *testing.T) {
newState, _ := util.DeterministicGenesisState(t, 40)
// 5 represents the enum value of state roots
trie, err := fieldtrie.NewFieldTrie(5, stateTypes.BasicArray, newState.StateRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot))
require.NoError(t, err)
root, err := stateutil.RootsArrayHashTreeRoot(newState.StateRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot), "StateRoots")
require.NoError(t, err)
newRoot, err := trie.TrieRoot()
require.NoError(t, err)
assert.Equal(t, root, newRoot)
}
func TestFieldTrie_RecomputeTrie(t *testing.T) {
newState, _ := util.DeterministicGenesisState(t, 32)
// 10 represents the enum value of validators
trie, err := fieldtrie.NewFieldTrie(11, stateTypes.CompositeArray, newState.Validators(), params.BeaconConfig().ValidatorRegistryLimit)
require.NoError(t, err)
changedIdx := []uint64{2, 29}
val1, err := newState.ValidatorAtIndex(10)
require.NoError(t, err)
val2, err := newState.ValidatorAtIndex(11)
require.NoError(t, err)
val1.Slashed = true
val1.ExitEpoch = 20
val2.Slashed = true
val2.ExitEpoch = 40
changedVals := []*ethpb.Validator{val1, val2}
require.NoError(t, newState.UpdateValidatorAtIndex(types.ValidatorIndex(changedIdx[0]), changedVals[0]))
require.NoError(t, newState.UpdateValidatorAtIndex(types.ValidatorIndex(changedIdx[1]), changedVals[1]))
expectedRoot, err := stateutil.ValidatorRegistryRoot(newState.Validators())
require.NoError(t, err)
root, err := trie.RecomputeTrie(changedIdx, newState.Validators())
require.NoError(t, err)
assert.Equal(t, expectedRoot, root)
}
func TestFieldTrie_CopyTrieImmutable(t *testing.T) {
newState, _ := util.DeterministicGenesisState(t, 32)
// 12 represents the enum value of randao mixes.
trie, err := fieldtrie.NewFieldTrie(13, stateTypes.BasicArray, newState.RandaoMixes(), uint64(params.BeaconConfig().EpochsPerHistoricalVector))
require.NoError(t, err)
newTrie := trie.CopyTrie()
changedIdx := []uint64{2, 29}
changedVals := [][32]byte{{'A', 'B'}, {'C', 'D'}}
require.NoError(t, newState.UpdateRandaoMixesAtIndex(changedIdx[0], changedVals[0][:]))
require.NoError(t, newState.UpdateRandaoMixesAtIndex(changedIdx[1], changedVals[1][:]))
root, err := trie.RecomputeTrie(changedIdx, newState.RandaoMixes())
require.NoError(t, err)
newRoot, err := newTrie.TrieRoot()
require.NoError(t, err)
if root == newRoot {
t.Errorf("Wanted roots to be different, but they are the same: %#x", root)
}
}

View File

@@ -0,0 +1,85 @@
package fieldtrie
import (
"encoding/binary"
"sync"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/params"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
)
func Test_handlePendingAttestation_OutOfRange(t *testing.T) {
items := make([]*ethpb.PendingAttestation, 1)
indices := []uint64{3}
_, err := handlePendingAttestation(items, indices, false)
assert.ErrorContains(t, "index 3 greater than number of pending attestations 1", err)
}
func Test_handleEth1DataSlice_OutOfRange(t *testing.T) {
items := make([]*ethpb.Eth1Data, 1)
indices := []uint64{3}
_, err := handleEth1DataSlice(items, indices, false)
assert.ErrorContains(t, "index 3 greater than number of items in eth1 data slice 1", err)
}
func Test_handleValidatorSlice_OutOfRange(t *testing.T) {
vals := make([]*ethpb.Validator, 1)
indices := []uint64{3}
_, err := handleValidatorSlice(vals, indices, false)
assert.ErrorContains(t, "index 3 greater than number of validators 1", err)
}
func TestBalancesSlice_CorrectRoots_All(t *testing.T) {
balances := []uint64{5, 2929, 34, 1291, 354305}
roots, err := handleBalanceSlice(balances, []uint64{}, true)
assert.NoError(t, err)
root1 := [32]byte{}
binary.LittleEndian.PutUint64(root1[:8], balances[0])
binary.LittleEndian.PutUint64(root1[8:16], balances[1])
binary.LittleEndian.PutUint64(root1[16:24], balances[2])
binary.LittleEndian.PutUint64(root1[24:32], balances[3])
root2 := [32]byte{}
binary.LittleEndian.PutUint64(root2[:8], balances[4])
assert.DeepEqual(t, roots, [][32]byte{root1, root2})
}
func TestBalancesSlice_CorrectRoots_Some(t *testing.T) {
balances := []uint64{5, 2929, 34, 1291, 354305}
roots, err := handleBalanceSlice(balances, []uint64{2, 3}, false)
assert.NoError(t, err)
root1 := [32]byte{}
binary.LittleEndian.PutUint64(root1[:8], balances[0])
binary.LittleEndian.PutUint64(root1[8:16], balances[1])
binary.LittleEndian.PutUint64(root1[16:24], balances[2])
binary.LittleEndian.PutUint64(root1[24:32], balances[3])
// Returns root for each indice(even if duplicated)
assert.DeepEqual(t, roots, [][32]byte{root1, root1})
}
func TestValidateIndices_CompressedField(t *testing.T) {
fakeTrie := &FieldTrie{
RWMutex: new(sync.RWMutex),
reference: stateutil.NewRef(0),
fieldLayers: nil,
field: types.Balances,
dataType: types.CompressedArray,
length: params.BeaconConfig().ValidatorRegistryLimit / 4,
numOfElems: 0,
}
goodIdx := params.BeaconConfig().ValidatorRegistryLimit - 1
assert.NoError(t, fakeTrie.validateIndices([]uint64{goodIdx}))
badIdx := goodIdx + 1
assert.ErrorContains(t, "invalid index for field balances", fakeTrie.validateIndices([]uint64{badIdx}))
}

View File

@@ -0,0 +1,105 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"field_roots.go",
"getters_attestation.go",
"getters_block.go",
"getters_checkpoint.go",
"getters_eth1.go",
"getters_misc.go",
"getters_randao.go",
"getters_state.go",
"getters_validator.go",
"proofs.go",
"readonly_validator.go",
"setters_attestation.go",
"setters_block.go",
"setters_checkpoint.go",
"setters_eth1.go",
"setters_misc.go",
"setters_randao.go",
"setters_state.go",
"setters_validator.go",
"state_trie.go",
"types.go",
"unsupported_getters.go",
"unsupported_setters.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1",
visibility = [
"//beacon-chain:__subpackages__",
"//contracts/deposit:__subpackages__",
"//proto/migration:__subpackages__",
"//proto/prysm/v1alpha1:__subpackages__",
"//proto/testing:__subpackages__",
"//runtime/interop:__subpackages__",
"//slasher/rpc:__subpackages__",
"//testing/benchmark:__pkg__",
"//testing/fuzz:__pkg__",
"//testing/spectest:__subpackages__",
"//testing/util:__pkg__",
"//tools/benchmark-files-gen:__pkg__",
"//tools/pcli:__pkg__",
],
deps = [
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native/fieldtrie:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/slice:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)
# gazelle:exclude types_bench_test.go
go_test(
name = "go_default_test",
srcs = [
"getters_attestation_test.go",
"getters_block_test.go",
"getters_test.go",
"getters_validator_test.go",
"proofs_test.go",
"readonly_validator_test.go",
"references_test.go",
"setters_attestation_test.go",
"state_test.go",
"state_trie_test.go",
"types_test.go",
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/trie:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/interop:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)

View File

@@ -0,0 +1,40 @@
// Package v1 defines how the beacon chain state for Ethereum
// functions in the running beacon node, using an advanced,
// immutable implementation of the state data structure.
//
// BeaconState getters may be accessed from inside or outside the package. To
// avoid duplicating locks, we have internal and external versions of the
// getter The external function carries out the short-circuit conditions,
// obtains a read lock, then calls the internal function. The internal function
// carries out the short-circuit conditions and returns the required data
// without further locking, allowing it to be used by other package-level
// functions that already hold a lock. Hence the functions look something
// like this:
//
// func (b *BeaconState) Foo() uint64 {
// // Short-circuit conditions.
// if !b.hasInnerState() {
// return 0
// }
//
// // Read lock.
// b.lock.RLock()
// defer b.lock.RUnlock()
//
// // Internal getter.
// return b.foo()
// }
//
// func (b *BeaconState) foo() uint64 {
// // Short-circuit conditions.
// if !b.hasInnerState() {
// return 0
// }
//
// return b.state.foo
// }
//
// Although it is technically possible to remove the short-circuit conditions
// from the external function, that would require every read to obtain a lock
// even if the data was not present, leading to potential slowdowns.
package v1

View File

@@ -0,0 +1,18 @@
package v1
import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/config/features"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// computeFieldRoots returns the hash tree root computations of every field in
// the beacon state as a list of 32 byte roots.
func computeFieldRoots(ctx context.Context, state *ethpb.BeaconState) ([][]byte, error) {
if features.Get().EnableSSZCache {
return stateutil.CachedHasher.ComputeFieldRootsWithHasherPhase0(ctx, state)
}
return stateutil.NocachedHasher.ComputeFieldRootsWithHasherPhase0(ctx, state)
}

View File

@@ -0,0 +1,55 @@
package v1
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// PreviousEpochAttestations corresponding to blocks on the beacon chain.
func (b *BeaconState) PreviousEpochAttestations() ([]*ethpb.PendingAttestation, error) {
if !b.hasInnerState() {
return nil, nil
}
if b.state.PreviousEpochAttestations == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.previousEpochAttestations(), nil
}
// previousEpochAttestations corresponding to blocks on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) previousEpochAttestations() []*ethpb.PendingAttestation {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyPendingAttestationSlice(b.state.PreviousEpochAttestations)
}
// CurrentEpochAttestations corresponding to blocks on the beacon chain.
func (b *BeaconState) CurrentEpochAttestations() ([]*ethpb.PendingAttestation, error) {
if !b.hasInnerState() {
return nil, nil
}
if b.state.CurrentEpochAttestations == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.currentEpochAttestations(), nil
}
// currentEpochAttestations corresponding to blocks on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) currentEpochAttestations() []*ethpb.PendingAttestation {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyPendingAttestationSlice(b.state.CurrentEpochAttestations)
}

View File

@@ -0,0 +1,46 @@
package v1
import (
"testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_PreviousEpochAttestations(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
atts, err := s.PreviousEpochAttestations()
require.NoError(t, err)
require.DeepEqual(t, []*ethpb.PendingAttestation(nil), atts)
want := []*ethpb.PendingAttestation{{ProposerIndex: 100}}
s, err = InitializeFromProto(&ethpb.BeaconState{PreviousEpochAttestations: want})
require.NoError(t, err)
got, err := s.PreviousEpochAttestations()
require.NoError(t, err)
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0].ProposerIndex = 101
require.DeepNotEqual(t, want, got)
}
func TestBeaconState_CurrentEpochAttestations(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
atts, err := s.CurrentEpochAttestations()
require.NoError(t, err)
require.DeepEqual(t, []*ethpb.PendingAttestation(nil), atts)
want := []*ethpb.PendingAttestation{{ProposerIndex: 101}}
s, err = InitializeFromProto(&ethpb.BeaconState{CurrentEpochAttestations: want})
require.NoError(t, err)
got, err := s.CurrentEpochAttestations()
require.NoError(t, err)
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0].ProposerIndex = 102
require.DeepNotEqual(t, want, got)
}

View File

@@ -0,0 +1,99 @@
package v1
import (
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// LatestBlockHeader stored within the beacon state.
func (b *BeaconState) LatestBlockHeader() *ethpb.BeaconBlockHeader {
if !b.hasInnerState() {
return nil
}
if b.state.LatestBlockHeader == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.latestBlockHeader()
}
// latestBlockHeader stored within the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) latestBlockHeader() *ethpb.BeaconBlockHeader {
if !b.hasInnerState() {
return nil
}
if b.state.LatestBlockHeader == nil {
return nil
}
hdr := &ethpb.BeaconBlockHeader{
Slot: b.state.LatestBlockHeader.Slot,
ProposerIndex: b.state.LatestBlockHeader.ProposerIndex,
}
parentRoot := make([]byte, len(b.state.LatestBlockHeader.ParentRoot))
bodyRoot := make([]byte, len(b.state.LatestBlockHeader.BodyRoot))
stateRoot := make([]byte, len(b.state.LatestBlockHeader.StateRoot))
copy(parentRoot, b.state.LatestBlockHeader.ParentRoot)
copy(bodyRoot, b.state.LatestBlockHeader.BodyRoot)
copy(stateRoot, b.state.LatestBlockHeader.StateRoot)
hdr.ParentRoot = parentRoot
hdr.BodyRoot = bodyRoot
hdr.StateRoot = stateRoot
return hdr
}
// BlockRoots kept track of in the beacon state.
func (b *BeaconState) BlockRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.BlockRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.blockRoots()
}
// blockRoots kept track of in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) blockRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.BlockRoots)
}
// BlockRootAtIndex retrieves a specific block root based on an
// input index value.
func (b *BeaconState) BlockRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.BlockRoots == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.blockRootAtIndex(idx)
}
// blockRootAtIndex retrieves a specific block root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) blockRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
return bytesutil.SafeCopyRootAtIndex(b.state.BlockRoots, idx)
}

View File

@@ -0,0 +1,59 @@
package v1
import (
"testing"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_LatestBlockHeader(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
got := s.LatestBlockHeader()
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
want := &ethpb.BeaconBlockHeader{Slot: 100}
s, err = InitializeFromProto(&ethpb.BeaconState{LatestBlockHeader: want})
require.NoError(t, err)
got = s.LatestBlockHeader()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got.Slot = 101
require.DeepNotEqual(t, want, got)
}
func TestBeaconState_BlockRoots(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
got := s.BlockRoots()
require.DeepEqual(t, ([][]byte)(nil), got)
want := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconState{BlockRoots: want})
require.NoError(t, err)
got = s.BlockRoots()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0][0] = 'b'
require.DeepNotEqual(t, want, got)
}
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
got, err := s.BlockRootAtIndex(0)
require.NoError(t, err)
require.DeepEqual(t, ([]byte)(nil), got)
r := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconState{BlockRoots: r})
require.NoError(t, err)
got, err = s.BlockRootAtIndex(0)
require.NoError(t, err)
want := bytesutil.PadTo([]byte{'a'}, 32)
require.DeepSSZEqual(t, want, got)
}

View File

@@ -0,0 +1,160 @@
package v1
import (
"bytes"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/go-bitfield"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// JustificationBits marking which epochs have been justified in the beacon chain.
func (b *BeaconState) JustificationBits() bitfield.Bitvector4 {
if !b.hasInnerState() {
return nil
}
if b.state.JustificationBits == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.justificationBits()
}
// justificationBits marking which epochs have been justified in the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) justificationBits() bitfield.Bitvector4 {
if !b.hasInnerState() {
return nil
}
if b.state.JustificationBits == nil {
return nil
}
res := make([]byte, len(b.state.JustificationBits.Bytes()))
copy(res, b.state.JustificationBits.Bytes())
return res
}
// PreviousJustifiedCheckpoint denoting an epoch and block root.
func (b *BeaconState) PreviousJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
if b.state.PreviousJustifiedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.previousJustifiedCheckpoint()
}
// previousJustifiedCheckpoint denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) previousJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyCheckpoint(b.state.PreviousJustifiedCheckpoint)
}
// CurrentJustifiedCheckpoint denoting an epoch and block root.
func (b *BeaconState) CurrentJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
if b.state.CurrentJustifiedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.currentJustifiedCheckpoint()
}
// currentJustifiedCheckpoint denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) currentJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyCheckpoint(b.state.CurrentJustifiedCheckpoint)
}
// MatchCurrentJustifiedCheckpoint returns true if input justified checkpoint matches
// the current justified checkpoint in state.
func (b *BeaconState) MatchCurrentJustifiedCheckpoint(c *ethpb.Checkpoint) bool {
if !b.hasInnerState() {
return false
}
if b.state.CurrentJustifiedCheckpoint == nil {
return false
}
if c.Epoch != b.state.CurrentJustifiedCheckpoint.Epoch {
return false
}
return bytes.Equal(c.Root, b.state.CurrentJustifiedCheckpoint.Root)
}
// MatchPreviousJustifiedCheckpoint returns true if the input justified checkpoint matches
// the previous justified checkpoint in state.
func (b *BeaconState) MatchPreviousJustifiedCheckpoint(c *ethpb.Checkpoint) bool {
if !b.hasInnerState() {
return false
}
if b.state.PreviousJustifiedCheckpoint == nil {
return false
}
if c.Epoch != b.state.PreviousJustifiedCheckpoint.Epoch {
return false
}
return bytes.Equal(c.Root, b.state.PreviousJustifiedCheckpoint.Root)
}
// FinalizedCheckpoint denoting an epoch and block root.
func (b *BeaconState) FinalizedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
if b.state.FinalizedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.finalizedCheckpoint()
}
// finalizedCheckpoint denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) finalizedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyCheckpoint(b.state.FinalizedCheckpoint)
}
// FinalizedCheckpointEpoch returns the epoch value of the finalized checkpoint.
func (b *BeaconState) FinalizedCheckpointEpoch() types.Epoch {
if !b.hasInnerState() {
return 0
}
if b.state.FinalizedCheckpoint == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.state.FinalizedCheckpoint.Epoch
}

View File

@@ -0,0 +1,91 @@
package v1
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// Eth1Data corresponding to the proof-of-work chain information stored in the beacon state.
func (b *BeaconState) Eth1Data() *ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1Data == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1Data()
}
// eth1Data corresponding to the proof-of-work chain information stored in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) eth1Data() *ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1Data == nil {
return nil
}
return ethpb.CopyETH1Data(b.state.Eth1Data)
}
// Eth1DataVotes corresponds to votes from Ethereum on the canonical proof-of-work chain
// data retrieved from eth1.
func (b *BeaconState) Eth1DataVotes() []*ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1DataVotes == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1DataVotes()
}
// eth1DataVotes corresponds to votes from Ethereum on the canonical proof-of-work chain
// data retrieved from eth1.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) eth1DataVotes() []*ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1DataVotes == nil {
return nil
}
res := make([]*ethpb.Eth1Data, len(b.state.Eth1DataVotes))
for i := 0; i < len(res); i++ {
res[i] = ethpb.CopyETH1Data(b.state.Eth1DataVotes[i])
}
return res
}
// Eth1DepositIndex corresponds to the index of the deposit made to the
// validator deposit contract at the time of this state's eth1 data.
func (b *BeaconState) Eth1DepositIndex() uint64 {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1DepositIndex()
}
// eth1DepositIndex corresponds to the index of the deposit made to the
// validator deposit contract at the time of this state's eth1 data.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) eth1DepositIndex() uint64 {
if !b.hasInnerState() {
return 0
}
return b.state.Eth1DepositIndex
}

View File

@@ -0,0 +1,163 @@
package v1
import (
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/runtime/version"
)
// GenesisTime of the beacon state as a uint64.
func (b *BeaconState) GenesisTime() uint64 {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.genesisTime()
}
// genesisTime of the beacon state as a uint64.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) genesisTime() uint64 {
if !b.hasInnerState() {
return 0
}
return b.state.GenesisTime
}
// GenesisValidatorRoot of the beacon state.
func (b *BeaconState) GenesisValidatorRoot() []byte {
if !b.hasInnerState() {
return nil
}
if b.state.GenesisValidatorsRoot == nil {
return params.BeaconConfig().ZeroHash[:]
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.genesisValidatorRoot()
}
// genesisValidatorRoot of the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) genesisValidatorRoot() []byte {
if !b.hasInnerState() {
return nil
}
if b.state.GenesisValidatorsRoot == nil {
return params.BeaconConfig().ZeroHash[:]
}
root := make([]byte, 32)
copy(root, b.state.GenesisValidatorsRoot)
return root
}
// Version of the beacon state. This method
// is strictly meant to be used without a lock
// internally.
func (_ *BeaconState) Version() int {
return version.Phase0
}
// Slot of the current beacon chain state.
func (b *BeaconState) Slot() types.Slot {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.slot()
}
// slot of the current beacon chain state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) slot() types.Slot {
if !b.hasInnerState() {
return 0
}
return b.state.Slot
}
// Fork version of the beacon chain.
func (b *BeaconState) Fork() *ethpb.Fork {
if !b.hasInnerState() {
return nil
}
if b.state.Fork == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.fork()
}
// fork version of the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) fork() *ethpb.Fork {
if !b.hasInnerState() {
return nil
}
if b.state.Fork == nil {
return nil
}
prevVersion := make([]byte, len(b.state.Fork.PreviousVersion))
copy(prevVersion, b.state.Fork.PreviousVersion)
currVersion := make([]byte, len(b.state.Fork.CurrentVersion))
copy(currVersion, b.state.Fork.CurrentVersion)
return &ethpb.Fork{
PreviousVersion: prevVersion,
CurrentVersion: currVersion,
Epoch: b.state.Fork.Epoch,
}
}
// HistoricalRoots based on epochs stored in the beacon state.
func (b *BeaconState) HistoricalRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.HistoricalRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.historicalRoots()
}
// historicalRoots based on epochs stored in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) historicalRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.HistoricalRoots)
}
// balancesLength returns the length of the balances slice.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) balancesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.Balances == nil {
return 0
}
return len(b.state.Balances)
}

View File

@@ -0,0 +1,85 @@
package v1
import (
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
)
// RandaoMixes of block proposers on the beacon chain.
func (b *BeaconState) RandaoMixes() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.RandaoMixes == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.randaoMixes()
}
// randaoMixes of block proposers on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) randaoMixes() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.RandaoMixes)
}
// RandaoMixAtIndex retrieves a specific block root based on an
// input index value.
func (b *BeaconState) RandaoMixAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.RandaoMixes == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.randaoMixAtIndex(idx)
}
// randaoMixAtIndex retrieves a specific block root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) randaoMixAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
return bytesutil.SafeCopyRootAtIndex(b.state.RandaoMixes, idx)
}
// RandaoMixesLength returns the length of the randao mixes slice.
func (b *BeaconState) RandaoMixesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.RandaoMixes == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.randaoMixesLength()
}
// randaoMixesLength returns the length of the randao mixes slice.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) randaoMixesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.RandaoMixes == nil {
return 0
}
return len(b.state.RandaoMixes)
}

View File

@@ -0,0 +1,123 @@
package v1
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// InnerStateUnsafe returns the pointer value of the underlying
// beacon state proto object, bypassing immutability. Use with care.
func (b *BeaconState) InnerStateUnsafe() interface{} {
if b == nil {
return nil
}
return b.state
}
// CloneInnerState the beacon state into a protobuf for usage.
func (b *BeaconState) CloneInnerState() interface{} {
if b == nil || b.state == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return &ethpb.BeaconState{
GenesisTime: b.genesisTime(),
GenesisValidatorsRoot: b.genesisValidatorRoot(),
Slot: b.slot(),
Fork: b.fork(),
LatestBlockHeader: b.latestBlockHeader(),
BlockRoots: b.blockRoots(),
StateRoots: b.stateRoots(),
HistoricalRoots: b.historicalRoots(),
Eth1Data: b.eth1Data(),
Eth1DataVotes: b.eth1DataVotes(),
Eth1DepositIndex: b.eth1DepositIndex(),
Validators: b.validators(),
Balances: b.balances(),
RandaoMixes: b.randaoMixes(),
Slashings: b.slashings(),
PreviousEpochAttestations: b.previousEpochAttestations(),
CurrentEpochAttestations: b.currentEpochAttestations(),
JustificationBits: b.justificationBits(),
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint(),
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint(),
FinalizedCheckpoint: b.finalizedCheckpoint(),
}
}
// hasInnerState detects if the internal reference to the state data structure
// is populated correctly. Returns false if nil.
func (b *BeaconState) hasInnerState() bool {
return b != nil && b.state != nil
}
// StateRoots kept track of in the beacon state.
func (b *BeaconState) StateRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.StateRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.stateRoots()
}
// StateRoots kept track of in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) stateRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.StateRoots)
}
// StateRootAtIndex retrieves a specific state root based on an
// input index value.
func (b *BeaconState) StateRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.StateRoots == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.stateRootAtIndex(idx)
}
// stateRootAtIndex retrieves a specific state root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) stateRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
return bytesutil.SafeCopyRootAtIndex(b.state.StateRoots, idx)
}
// MarshalSSZ marshals the underlying beacon state to bytes.
func (b *BeaconState) MarshalSSZ() ([]byte, error) {
if !b.hasInnerState() {
return nil, errors.New("nil beacon state")
}
return b.state.MarshalSSZ()
}
// ProtobufBeaconState transforms an input into beacon state in the form of protobuf.
// Error is returned if the input is not type protobuf beacon state.
func ProtobufBeaconState(s interface{}) (*ethpb.BeaconState, error) {
pbState, ok := s.(*ethpb.BeaconState)
if !ok {
return nil, errors.New("input is not type ethpb.BeaconState")
}
return pbState, nil
}

View File

@@ -0,0 +1,216 @@
package v1
import (
"runtime/debug"
"sync"
"testing"
types "github.com/prysmaticlabs/eth2-types"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_SlotDataRace(t *testing.T) {
headState, err := InitializeFromProto(&ethpb.BeaconState{Slot: 1})
require.NoError(t, err)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
require.NoError(t, headState.SetSlot(0))
wg.Done()
}()
go func() {
headState.Slot()
wg.Done()
}()
wg.Wait()
}
func TestNilState_NoPanic(t *testing.T) {
var st *BeaconState
defer func() {
if r := recover(); r != nil {
t.Errorf("Method panicked when it was not supposed to: %v\n%v\n", r, string(debug.Stack()))
}
}()
// retrieve elements from nil state
_ = st.GenesisTime()
_ = st.GenesisValidatorRoot()
_ = st.GenesisValidatorRoot()
_ = st.Slot()
_ = st.Fork()
_ = st.LatestBlockHeader()
_ = st.BlockRoots()
_, err := st.BlockRootAtIndex(0)
_ = err
_ = st.StateRoots()
_ = st.HistoricalRoots()
_ = st.Eth1Data()
_ = st.Eth1DataVotes()
_ = st.Eth1DepositIndex()
_, err = st.ValidatorAtIndex(0)
_ = err
_, err = st.ValidatorAtIndexReadOnly(0)
_ = err
_, _ = st.ValidatorIndexByPubkey([fieldparams.BLSPubkeyLength]byte{})
_ = st.PubkeyAtIndex(0)
_ = st.NumValidators()
_ = st.Balances()
_, err = st.BalanceAtIndex(0)
_ = err
_ = st.BalancesLength()
_ = st.RandaoMixes()
_, err = st.RandaoMixAtIndex(0)
_ = err
_ = st.RandaoMixesLength()
_ = st.Slashings()
_, err = st.PreviousEpochAttestations()
require.NoError(t, err)
_, err = st.CurrentEpochAttestations()
require.NoError(t, err)
_ = st.JustificationBits()
_ = st.PreviousJustifiedCheckpoint()
_ = st.CurrentJustifiedCheckpoint()
_ = st.FinalizedCheckpoint()
}
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
c1 := &ethpb.Checkpoint{Epoch: 1}
c2 := &ethpb.Checkpoint{Epoch: 2}
beaconState, err := InitializeFromProto(&ethpb.BeaconState{CurrentJustifiedCheckpoint: c1})
require.NoError(t, err)
require.Equal(t, true, beaconState.MatchCurrentJustifiedCheckpoint(c1))
require.Equal(t, false, beaconState.MatchCurrentJustifiedCheckpoint(c2))
require.Equal(t, false, beaconState.MatchPreviousJustifiedCheckpoint(c1))
require.Equal(t, false, beaconState.MatchPreviousJustifiedCheckpoint(c2))
beaconState.state = nil
require.Equal(t, false, beaconState.MatchCurrentJustifiedCheckpoint(c1))
}
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
c1 := &ethpb.Checkpoint{Epoch: 1}
c2 := &ethpb.Checkpoint{Epoch: 2}
beaconState, err := InitializeFromProto(&ethpb.BeaconState{PreviousJustifiedCheckpoint: c1})
require.NoError(t, err)
require.NoError(t, err)
require.Equal(t, false, beaconState.MatchCurrentJustifiedCheckpoint(c1))
require.Equal(t, false, beaconState.MatchCurrentJustifiedCheckpoint(c2))
require.Equal(t, true, beaconState.MatchPreviousJustifiedCheckpoint(c1))
require.Equal(t, false, beaconState.MatchPreviousJustifiedCheckpoint(c2))
beaconState.state = nil
require.Equal(t, false, beaconState.MatchPreviousJustifiedCheckpoint(c1))
}
func TestBeaconState_MarshalSSZ_NilState(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
s.state = nil
_, err = s.MarshalSSZ()
require.ErrorContains(t, "nil beacon state", err)
}
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
keyCreator := func(input []byte) [fieldparams.BLSPubkeyLength]byte {
nKey := [fieldparams.BLSPubkeyLength]byte{}
copy(nKey[:1], input)
return nKey
}
tests := []struct {
name string
modifyFunc func(b *BeaconState, k [fieldparams.BLSPubkeyLength]byte)
exists bool
expectedIdx types.ValidatorIndex
largestIdxInSet types.ValidatorIndex
}{
{
name: "retrieve validator",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: true,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators from the start",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
},
exists: true,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: true,
expectedIdx: 2,
},
{
name: "retrieve validator with multiple validators from the start with shared state",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
_ = b.Copy()
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
},
exists: true,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators with shared state",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
n := b.Copy()
// Append to another state
assert.NoError(t, n.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: false,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators with shared state at boundary",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
n := b.Copy()
// Append to another state
assert.NoError(t, n.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: false,
expectedIdx: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
nKey := keyCreator([]byte{'A'})
tt.modifyFunc(s, nKey)
idx, ok := s.ValidatorIndexByPubkey(nKey)
assert.Equal(t, tt.exists, ok)
assert.Equal(t, tt.expectedIdx, idx)
})
}
}

View File

@@ -0,0 +1,298 @@
package v1
import (
"fmt"
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// ValidatorIndexOutOfRangeError represents an error scenario where a validator does not exist
// at a given index in the validator's array.
type ValidatorIndexOutOfRangeError struct {
message string
}
var (
// ErrNilValidatorsInState returns when accessing validators in the state while the state has a
// nil slice for the validators field.
ErrNilValidatorsInState = errors.New("state has nil validator slice")
)
// NewValidatorIndexOutOfRangeError creates a new error instance.
func NewValidatorIndexOutOfRangeError(index types.ValidatorIndex) ValidatorIndexOutOfRangeError {
return ValidatorIndexOutOfRangeError{
message: fmt.Sprintf("index %d out of range", index),
}
}
// Error returns the underlying error message.
func (e *ValidatorIndexOutOfRangeError) Error() string {
return e.message
}
// Validators participating in consensus on the beacon chain.
func (b *BeaconState) Validators() []*ethpb.Validator {
if !b.hasInnerState() {
return nil
}
if b.state.Validators == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.validators()
}
// validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) validators() []*ethpb.Validator {
if !b.hasInnerState() {
return nil
}
if b.state.Validators == nil {
return nil
}
res := make([]*ethpb.Validator, len(b.state.Validators))
for i := 0; i < len(res); i++ {
val := b.state.Validators[i]
if val == nil {
continue
}
res[i] = ethpb.CopyValidator(val)
}
return res
}
// references of validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState. This does not
// copy fully and instead just copies the reference.
func (b *BeaconState) validatorsReferences() []*ethpb.Validator {
if !b.hasInnerState() {
return nil
}
if b.state.Validators == nil {
return nil
}
res := make([]*ethpb.Validator, len(b.state.Validators))
for i := 0; i < len(res); i++ {
validator := b.state.Validators[i]
if validator == nil {
continue
}
// copy validator reference instead.
res[i] = validator
}
return res
}
// ValidatorAtIndex is the validator at the provided index.
func (b *BeaconState) ValidatorAtIndex(idx types.ValidatorIndex) (*ethpb.Validator, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.Validators == nil {
return &ethpb.Validator{}, nil
}
if uint64(len(b.state.Validators)) <= uint64(idx) {
e := NewValidatorIndexOutOfRangeError(idx)
return nil, &e
}
b.lock.RLock()
defer b.lock.RUnlock()
val := b.state.Validators[idx]
return ethpb.CopyValidator(val), nil
}
// ValidatorAtIndexReadOnly is the validator at the provided index. This method
// doesn't clone the validator.
func (b *BeaconState) ValidatorAtIndexReadOnly(idx types.ValidatorIndex) (state.ReadOnlyValidator, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.Validators == nil {
return nil, ErrNilValidatorsInState
}
if uint64(len(b.state.Validators)) <= uint64(idx) {
e := NewValidatorIndexOutOfRangeError(idx)
return nil, &e
}
b.lock.RLock()
defer b.lock.RUnlock()
return NewValidator(b.state.Validators[idx])
}
// ValidatorIndexByPubkey returns a given validator by its 48-byte public key.
func (b *BeaconState) ValidatorIndexByPubkey(key [fieldparams.BLSPubkeyLength]byte) (types.ValidatorIndex, bool) {
if b == nil || b.valMapHandler == nil || b.valMapHandler.IsNil() {
return 0, false
}
b.lock.RLock()
defer b.lock.RUnlock()
numOfVals := len(b.state.Validators)
idx, ok := b.valMapHandler.Get(key)
if ok && numOfVals <= int(idx) {
return types.ValidatorIndex(0), false
}
return idx, ok
}
// PubkeyAtIndex returns the pubkey at the given
// validator index.
func (b *BeaconState) PubkeyAtIndex(idx types.ValidatorIndex) [fieldparams.BLSPubkeyLength]byte {
if !b.hasInnerState() {
return [fieldparams.BLSPubkeyLength]byte{}
}
if uint64(idx) >= uint64(len(b.state.Validators)) {
return [fieldparams.BLSPubkeyLength]byte{}
}
b.lock.RLock()
defer b.lock.RUnlock()
if b.state.Validators[idx] == nil {
return [fieldparams.BLSPubkeyLength]byte{}
}
return bytesutil.ToBytes48(b.state.Validators[idx].PublicKey)
}
// NumValidators returns the size of the validator registry.
func (b *BeaconState) NumValidators() int {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return len(b.state.Validators)
}
// ReadFromEveryValidator reads values from every validator and applies it to the provided function.
// Warning: This method is potentially unsafe, as it exposes the actual validator registry.
func (b *BeaconState) ReadFromEveryValidator(f func(idx int, val state.ReadOnlyValidator) error) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if b.state.Validators == nil {
return errors.New("nil validators in state")
}
b.lock.RLock()
validators := b.state.Validators
b.lock.RUnlock()
for i, v := range validators {
v, err := NewValidator(v)
if err != nil {
return err
}
if err := f(i, v); err != nil {
return err
}
}
return nil
}
// Balances of validators participating in consensus on the beacon chain.
func (b *BeaconState) Balances() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Balances == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.balances()
}
// balances of validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) balances() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Balances == nil {
return nil
}
res := make([]uint64, len(b.state.Balances))
copy(res, b.state.Balances)
return res
}
// BalanceAtIndex of validator with the provided index.
func (b *BeaconState) BalanceAtIndex(idx types.ValidatorIndex) (uint64, error) {
if !b.hasInnerState() {
return 0, ErrNilInnerState
}
if b.state.Balances == nil {
return 0, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
if uint64(len(b.state.Balances)) <= uint64(idx) {
return 0, fmt.Errorf("index of %d does not exist", idx)
}
return b.state.Balances[idx], nil
}
// BalancesLength returns the length of the balances slice.
func (b *BeaconState) BalancesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.Balances == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.balancesLength()
}
// Slashings of validators on the beacon chain.
func (b *BeaconState) Slashings() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Slashings == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.slashings()
}
// slashings of validators on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) slashings() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Slashings == nil {
return nil
}
res := make([]uint64, len(b.state.Slashings))
copy(res, b.state.Slashings)
return res
}

View File

@@ -0,0 +1,20 @@
package v1_test
import (
"testing"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
st, err := v1.InitializeFromProtoUnsafe(&ethpb.BeaconState{
Validators: nil,
})
require.NoError(t, err)
_, err = st.ValidatorAtIndexReadOnly(0)
assert.Equal(t, v1.ErrNilValidatorsInState, err)
}

View File

@@ -0,0 +1,54 @@
package v1
import (
"context"
"encoding/binary"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/fieldtrie"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
)
const (
finalizedRootIndex = uint64(105) // Precomputed value.
)
// FinalizedRootGeneralizedIndex for the beacon state.
func FinalizedRootGeneralizedIndex() uint64 {
return finalizedRootIndex
}
// CurrentSyncCommitteeProof from the state's Merkle trie representation.
func (*BeaconState) CurrentSyncCommitteeProof(_ context.Context) ([][]byte, error) {
return nil, errors.New("CurrentSyncCommitteeProof() unsupported for v1 beacon state")
}
// NextSyncCommitteeProof from the state's Merkle trie representation.
func (*BeaconState) NextSyncCommitteeProof(_ context.Context) ([][]byte, error) {
return nil, errors.New("NextSyncCommitteeProof() unsupported for v1 beacon state")
}
// FinalizedRootProof crafts a Merkle proof for the finalized root
// contained within the finalized checkpoint of a beacon state.
func (b *BeaconState) FinalizedRootProof(ctx context.Context) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
if err := b.initializeMerkleLayers(ctx); err != nil {
return nil, err
}
if err := b.recomputeDirtyFields(ctx); err != nil {
return nil, err
}
cpt := b.state.FinalizedCheckpoint
// The epoch field of a finalized checkpoint is the neighbor
// index of the finalized root field in its Merkle tree representation
// of the checkpoint. This neighbor is the first element added to the proof.
epochBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(epochBuf, uint64(cpt.Epoch))
epochRoot := bytesutil.ToBytes32(epochBuf)
proof := make([][]byte, 0)
proof = append(proof, epochRoot[:])
branch := fieldtrie.ProofFromMerkleLayers(b.merkleLayers, finalizedCheckpoint)
proof = append(proof, branch...)
return proof, nil
}

View File

@@ -0,0 +1,64 @@
package v1_test
import (
"context"
"testing"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
"github.com/prysmaticlabs/prysm/container/trie"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
)
func TestBeaconStateMerkleProofs(t *testing.T) {
ctx := context.Background()
st, _ := util.DeterministicGenesisState(t, 256)
htr, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
t.Run("current sync committee", func(t *testing.T) {
_, err := st.CurrentSyncCommitteeProof(ctx)
require.ErrorContains(t, "unsupported", err)
})
t.Run("next sync committee", func(t *testing.T) {
_, err := st.NextSyncCommitteeProof(ctx)
require.ErrorContains(t, "unsupported", err)
})
t.Run("finalized root", func(t *testing.T) {
finalizedRoot := st.FinalizedCheckpoint().Root
proof, err := st.FinalizedRootProof(ctx)
require.NoError(t, err)
gIndex := v1.FinalizedRootGeneralizedIndex()
valid := trie.VerifyMerkleProof(htr[:], finalizedRoot, gIndex, proof)
require.Equal(t, true, valid)
})
t.Run("recomputes root on dirty fields", func(t *testing.T) {
currentRoot, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
cpt := st.FinalizedCheckpoint()
require.NoError(t, err)
// Edit the checkpoint.
cpt.Epoch = 100
require.NoError(t, st.SetFinalizedCheckpoint(cpt))
// Produce a proof for the finalized root.
proof, err := st.FinalizedRootProof(ctx)
require.NoError(t, err)
// We expect the previous step to have triggered
// a recomputation of dirty fields in the beacon state, resulting
// in a new hash tree root as the finalized checkpoint had previously
// changed and should have been marked as a dirty state field.
// The proof validity should be false for the old root, but true for the new.
finalizedRoot := st.FinalizedCheckpoint().Root
gIndex := v1.FinalizedRootGeneralizedIndex()
valid := trie.VerifyMerkleProof(currentRoot[:], finalizedRoot, gIndex, proof)
require.Equal(t, false, valid)
newRoot, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
valid = trie.VerifyMerkleProof(newRoot[:], finalizedRoot, gIndex, proof)
require.Equal(t, true, valid)
})
}

View File

@@ -0,0 +1,89 @@
package v1
import (
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
var (
// ErrNilWrappedValidator returns when caller attempts to wrap a nil pointer validator.
ErrNilWrappedValidator = errors.New("nil validator cannot be wrapped as readonly")
)
// readOnlyValidator returns a wrapper that only allows fields from a validator
// to be read, and prevents any modification of internal validator fields.
type readOnlyValidator struct {
validator *ethpb.Validator
}
var _ = state.ReadOnlyValidator(readOnlyValidator{})
// NewValidator initializes the read only wrapper for validator.
func NewValidator(v *ethpb.Validator) (state.ReadOnlyValidator, error) {
rov := readOnlyValidator{
validator: v,
}
if rov.IsNil() {
return nil, ErrNilWrappedValidator
}
return rov, nil
}
// EffectiveBalance returns the effective balance of the
// read only validator.
func (v readOnlyValidator) EffectiveBalance() uint64 {
return v.validator.EffectiveBalance
}
// ActivationEligibilityEpoch returns the activation eligibility epoch of the
// read only validator.
func (v readOnlyValidator) ActivationEligibilityEpoch() types.Epoch {
return v.validator.ActivationEligibilityEpoch
}
// ActivationEpoch returns the activation epoch of the
// read only validator.
func (v readOnlyValidator) ActivationEpoch() types.Epoch {
return v.validator.ActivationEpoch
}
// WithdrawableEpoch returns the withdrawable epoch of the
// read only validator.
func (v readOnlyValidator) WithdrawableEpoch() types.Epoch {
return v.validator.WithdrawableEpoch
}
// ExitEpoch returns the exit epoch of the
// read only validator.
func (v readOnlyValidator) ExitEpoch() types.Epoch {
return v.validator.ExitEpoch
}
// PublicKey returns the public key of the
// read only validator.
func (v readOnlyValidator) PublicKey() [fieldparams.BLSPubkeyLength]byte {
var pubkey [fieldparams.BLSPubkeyLength]byte
copy(pubkey[:], v.validator.PublicKey)
return pubkey
}
// WithdrawalCredentials returns the withdrawal credentials of the
// read only validator.
func (v readOnlyValidator) WithdrawalCredentials() []byte {
creds := make([]byte, len(v.validator.WithdrawalCredentials))
copy(creds, v.validator.WithdrawalCredentials)
return creds
}
// Slashed returns the read only validator is slashed.
func (v readOnlyValidator) Slashed() bool {
return v.validator.Slashed
}
// IsNil returns true if the validator is nil.
func (v readOnlyValidator) IsNil() bool {
return v.validator == nil
}

View File

@@ -0,0 +1,74 @@
package v1_test
import (
"testing"
types "github.com/prysmaticlabs/eth2-types"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestReadOnlyValidator_ReturnsErrorOnNil(t *testing.T) {
if _, err := v1.NewValidator(nil); err != v1.ErrNilWrappedValidator {
t.Errorf("Wrong error returned. Got %v, wanted %v", err, v1.ErrNilWrappedValidator)
}
}
func TestReadOnlyValidator_EffectiveBalance(t *testing.T) {
bal := uint64(234)
v, err := v1.NewValidator(&ethpb.Validator{EffectiveBalance: bal})
require.NoError(t, err)
assert.Equal(t, bal, v.EffectiveBalance())
}
func TestReadOnlyValidator_ActivationEligibilityEpoch(t *testing.T) {
epoch := types.Epoch(234)
v, err := v1.NewValidator(&ethpb.Validator{ActivationEligibilityEpoch: epoch})
require.NoError(t, err)
assert.Equal(t, epoch, v.ActivationEligibilityEpoch())
}
func TestReadOnlyValidator_ActivationEpoch(t *testing.T) {
epoch := types.Epoch(234)
v, err := v1.NewValidator(&ethpb.Validator{ActivationEpoch: epoch})
require.NoError(t, err)
assert.Equal(t, epoch, v.ActivationEpoch())
}
func TestReadOnlyValidator_WithdrawableEpoch(t *testing.T) {
epoch := types.Epoch(234)
v, err := v1.NewValidator(&ethpb.Validator{WithdrawableEpoch: epoch})
require.NoError(t, err)
assert.Equal(t, epoch, v.WithdrawableEpoch())
}
func TestReadOnlyValidator_ExitEpoch(t *testing.T) {
epoch := types.Epoch(234)
v, err := v1.NewValidator(&ethpb.Validator{ExitEpoch: epoch})
require.NoError(t, err)
assert.Equal(t, epoch, v.ExitEpoch())
}
func TestReadOnlyValidator_PublicKey(t *testing.T) {
key := [fieldparams.BLSPubkeyLength]byte{0xFA, 0xCC}
v, err := v1.NewValidator(&ethpb.Validator{PublicKey: key[:]})
require.NoError(t, err)
assert.Equal(t, key, v.PublicKey())
}
func TestReadOnlyValidator_WithdrawalCredentials(t *testing.T) {
creds := []byte{0xFA, 0xCC}
v, err := v1.NewValidator(&ethpb.Validator{WithdrawalCredentials: creds})
require.NoError(t, err)
assert.DeepEqual(t, creds, v.WithdrawalCredentials())
}
func TestReadOnlyValidator_Slashed(t *testing.T) {
slashed := true
v, err := v1.NewValidator(&ethpb.Validator{Slashed: slashed})
require.NoError(t, err)
assert.Equal(t, slashed, v.Slashed())
}

View File

@@ -0,0 +1,354 @@
package v1
import (
"reflect"
"runtime"
"runtime/debug"
"testing"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestStateReferenceSharing_Finalizer(t *testing.T) {
// This test showcases the logic on a the RandaoMixes field with the GC finalizer.
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconState{RandaoMixes: [][]byte{[]byte("foo")}})
require.NoError(t, err)
assert.Equal(t, uint(1), a.sharedFieldReferences[randaoMixes].Refs(), "Expected a single reference for RANDAO mixes")
func() {
// Create object in a different scope for GC
b := a.Copy()
assert.Equal(t, uint(2), a.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 references to RANDAO mixes")
_ = b
}()
runtime.GC() // Should run finalizer on object b
assert.Equal(t, uint(1), a.sharedFieldReferences[randaoMixes].Refs(), "Expected 1 shared reference to RANDAO mixes!")
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assert.Equal(t, uint(2), b.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes")
require.NoError(t, b.UpdateRandaoMixesAtIndex(0, []byte("bar")))
if b.sharedFieldReferences[randaoMixes].Refs() != 1 || a.sharedFieldReferences[randaoMixes].Refs() != 1 {
t.Error("Expected 1 shared reference to RANDAO mix for both a and b")
}
}
func TestStateReferenceCopy_NoUnexpectedRootsMutation(t *testing.T) {
root1, root2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconState{
BlockRoots: [][]byte{
root1[:],
},
StateRoots: [][]byte{
root1[:],
},
})
require.NoError(t, err)
assertRefCount(t, a, blockRoots, 1)
assertRefCount(t, a, stateRoots, 1)
// Copy, increases reference count.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, a, blockRoots, 2)
assertRefCount(t, a, stateRoots, 2)
assertRefCount(t, b, blockRoots, 2)
assertRefCount(t, b, stateRoots, 2)
assert.Equal(t, 1, len(b.state.GetBlockRoots()), "No block roots found")
assert.Equal(t, 1, len(b.state.GetStateRoots()), "No state roots found")
// Assert shared state.
blockRootsA := a.state.GetBlockRoots()
stateRootsA := a.state.GetStateRoots()
blockRootsB := b.state.GetBlockRoots()
stateRootsB := b.state.GetStateRoots()
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
t.Errorf("Unexpected number of block roots, want: %v", 1)
}
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
t.Errorf("Unexpected number of state roots, want: %v", 1)
}
assertValFound(t, blockRootsA, root1[:])
assertValFound(t, blockRootsB, root1[:])
assertValFound(t, stateRootsA, root1[:])
assertValFound(t, stateRootsB, root1[:])
// Mutator should only affect calling state: a.
require.NoError(t, a.UpdateBlockRootAtIndex(0, root2))
require.NoError(t, a.UpdateStateRootAtIndex(0, root2))
// Assert no shared state mutation occurred only on state a (copy on write).
assertValNotFound(t, a.state.GetBlockRoots(), root1[:])
assertValNotFound(t, a.state.GetStateRoots(), root1[:])
assertValFound(t, a.state.GetBlockRoots(), root2[:])
assertValFound(t, a.state.GetStateRoots(), root2[:])
assertValFound(t, b.state.GetBlockRoots(), root1[:])
assertValFound(t, b.state.GetStateRoots(), root1[:])
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
t.Errorf("Unexpected number of block roots, want: %v", 1)
}
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
t.Errorf("Unexpected number of state roots, want: %v", 1)
}
assert.DeepEqual(t, root2[:], a.state.GetBlockRoots()[0], "Expected mutation not found")
assert.DeepEqual(t, root2[:], a.state.GetStateRoots()[0], "Expected mutation not found")
assert.DeepEqual(t, root1[:], blockRootsB[0], "Unexpected mutation found")
assert.DeepEqual(t, root1[:], stateRootsB[0], "Unexpected mutation found")
// Copy on write happened, reference counters are reset.
assertRefCount(t, a, blockRoots, 1)
assertRefCount(t, a, stateRoots, 1)
assertRefCount(t, b, blockRoots, 1)
assertRefCount(t, b, stateRoots, 1)
}
func TestStateReferenceCopy_NoUnexpectedRandaoMutation(t *testing.T) {
val1, val2 := []byte("foo"), []byte("bar")
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconState{
RandaoMixes: [][]byte{
val1,
},
})
require.NoError(t, err)
assertRefCount(t, a, randaoMixes, 1)
// Copy, increases reference count.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, a, randaoMixes, 2)
assertRefCount(t, b, randaoMixes, 2)
assert.Equal(t, 1, len(b.state.GetRandaoMixes()), "No randao mixes found")
// Assert shared state.
mixesA := a.state.GetRandaoMixes()
mixesB := b.state.GetRandaoMixes()
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
t.Errorf("Unexpected number of mix values, want: %v", 1)
}
assertValFound(t, mixesA, val1)
assertValFound(t, mixesB, val1)
// Mutator should only affect calling state: a.
require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2))
// Assert no shared state mutation occurred only on state a (copy on write).
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
t.Errorf("Unexpected number of mix values, want: %v", 1)
}
assertValFound(t, a.state.GetRandaoMixes(), val2)
assertValNotFound(t, a.state.GetRandaoMixes(), val1)
assertValFound(t, b.state.GetRandaoMixes(), val1)
assertValNotFound(t, b.state.GetRandaoMixes(), val2)
assertValFound(t, mixesB, val1)
assertValNotFound(t, mixesB, val2)
assert.DeepEqual(t, val2, a.state.GetRandaoMixes()[0], "Expected mutation not found")
assert.DeepEqual(t, val1, mixesB[0], "Unexpected mutation found")
// Copy on write happened, reference counters are reset.
assertRefCount(t, a, randaoMixes, 1)
assertRefCount(t, b, randaoMixes, 1)
}
func TestStateReferenceCopy_NoUnexpectedAttestationsMutation(t *testing.T) {
assertAttFound := func(vals []*ethpb.PendingAttestation, val uint64) {
for i := range vals {
if reflect.DeepEqual(vals[i].AggregationBits, bitfield.NewBitlist(val)) {
return
}
}
t.Log(string(debug.Stack()))
t.Fatalf("Expected attestation not found (%v), want: %v", vals, val)
}
assertAttNotFound := func(vals []*ethpb.PendingAttestation, val uint64) {
for i := range vals {
if reflect.DeepEqual(vals[i].AggregationBits, bitfield.NewBitlist(val)) {
t.Log(string(debug.Stack()))
t.Fatalf("Unexpected attestation found (%v): %v", vals, val)
return
}
}
}
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconState{})
require.NoError(t, err)
assertRefCount(t, a, previousEpochAttestations, 1)
assertRefCount(t, a, currentEpochAttestations, 1)
// Update initial state.
atts := []*ethpb.PendingAttestation{
{AggregationBits: bitfield.NewBitlist(1)},
{AggregationBits: bitfield.NewBitlist(2)},
}
a.setPreviousEpochAttestations(atts[:1])
a.setCurrentEpochAttestations(atts[:1])
curAtt, err := a.CurrentEpochAttestations()
require.NoError(t, err)
assert.Equal(t, 1, len(curAtt), "Unexpected number of attestations")
preAtt, err := a.PreviousEpochAttestations()
require.NoError(t, err)
assert.Equal(t, 1, len(preAtt), "Unexpected number of attestations")
// Copy, increases reference count.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, a, previousEpochAttestations, 2)
assertRefCount(t, a, currentEpochAttestations, 2)
assertRefCount(t, b, previousEpochAttestations, 2)
assertRefCount(t, b, currentEpochAttestations, 2)
assert.Equal(t, 1, len(b.state.GetPreviousEpochAttestations()), "Unexpected number of attestations")
assert.Equal(t, 1, len(b.state.GetCurrentEpochAttestations()), "Unexpected number of attestations")
// Assert shared state.
curAttsA := a.state.GetCurrentEpochAttestations()
prevAttsA := a.state.GetPreviousEpochAttestations()
curAttsB := b.state.GetCurrentEpochAttestations()
prevAttsB := b.state.GetPreviousEpochAttestations()
if len(curAttsA) != len(curAttsB) || len(curAttsA) < 1 {
t.Errorf("Unexpected number of attestations, want: %v", 1)
}
if len(prevAttsA) != len(prevAttsB) || len(prevAttsA) < 1 {
t.Errorf("Unexpected number of attestations, want: %v", 1)
}
assertAttFound(curAttsA, 1)
assertAttFound(prevAttsA, 1)
assertAttFound(curAttsB, 1)
assertAttFound(prevAttsB, 1)
// Extends state a attestations.
require.NoError(t, a.AppendCurrentEpochAttestations(atts[1]))
require.NoError(t, a.AppendPreviousEpochAttestations(atts[1]))
curAtt, err = a.CurrentEpochAttestations()
require.NoError(t, err)
assert.Equal(t, 2, len(curAtt), "Unexpected number of attestations")
preAtt, err = a.PreviousEpochAttestations()
require.NoError(t, err)
assert.Equal(t, 2, len(preAtt), "Unexpected number of attestations")
assertAttFound(a.state.GetCurrentEpochAttestations(), 1)
assertAttFound(a.state.GetPreviousEpochAttestations(), 1)
assertAttFound(a.state.GetCurrentEpochAttestations(), 2)
assertAttFound(a.state.GetPreviousEpochAttestations(), 2)
assertAttFound(b.state.GetCurrentEpochAttestations(), 1)
assertAttFound(b.state.GetPreviousEpochAttestations(), 1)
assertAttNotFound(b.state.GetCurrentEpochAttestations(), 2)
assertAttNotFound(b.state.GetPreviousEpochAttestations(), 2)
// Mutator should only affect calling state: a.
applyToEveryAttestation := func(state *ethpb.BeaconState) {
// One MUST copy on write.
atts = make([]*ethpb.PendingAttestation, len(state.CurrentEpochAttestations))
copy(atts, state.CurrentEpochAttestations)
state.CurrentEpochAttestations = atts
for i := range state.GetCurrentEpochAttestations() {
att := ethpb.CopyPendingAttestation(state.CurrentEpochAttestations[i])
att.AggregationBits = bitfield.NewBitlist(3)
state.CurrentEpochAttestations[i] = att
}
atts = make([]*ethpb.PendingAttestation, len(state.PreviousEpochAttestations))
copy(atts, state.PreviousEpochAttestations)
state.PreviousEpochAttestations = atts
for i := range state.GetPreviousEpochAttestations() {
att := ethpb.CopyPendingAttestation(state.PreviousEpochAttestations[i])
att.AggregationBits = bitfield.NewBitlist(3)
state.PreviousEpochAttestations[i] = att
}
}
applyToEveryAttestation(a.state)
// Assert no shared state mutation occurred only on state a (copy on write).
assertAttFound(a.state.GetCurrentEpochAttestations(), 3)
assertAttFound(a.state.GetPreviousEpochAttestations(), 3)
assertAttNotFound(a.state.GetCurrentEpochAttestations(), 1)
assertAttNotFound(a.state.GetPreviousEpochAttestations(), 1)
assertAttNotFound(a.state.GetCurrentEpochAttestations(), 2)
assertAttNotFound(a.state.GetPreviousEpochAttestations(), 2)
// State b must be unaffected.
assertAttNotFound(b.state.GetCurrentEpochAttestations(), 3)
assertAttNotFound(b.state.GetPreviousEpochAttestations(), 3)
assertAttFound(b.state.GetCurrentEpochAttestations(), 1)
assertAttFound(b.state.GetPreviousEpochAttestations(), 1)
assertAttNotFound(b.state.GetCurrentEpochAttestations(), 2)
assertAttNotFound(b.state.GetPreviousEpochAttestations(), 2)
// Copy on write happened, reference counters are reset.
assertRefCount(t, a, currentEpochAttestations, 1)
assertRefCount(t, b, currentEpochAttestations, 1)
assertRefCount(t, a, previousEpochAttestations, 1)
assertRefCount(t, b, previousEpochAttestations, 1)
}
func TestValidatorReferences_RemainsConsistent(t *testing.T) {
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconState{
Validators: []*ethpb.Validator{
{PublicKey: []byte{'A'}},
{PublicKey: []byte{'B'}},
{PublicKey: []byte{'C'}},
{PublicKey: []byte{'D'}},
{PublicKey: []byte{'E'}},
},
})
require.NoError(t, err)
// Create a second state.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
// Update First Validator.
assert.NoError(t, a.UpdateValidatorAtIndex(0, &ethpb.Validator{PublicKey: []byte{'Z'}}))
assert.DeepNotEqual(t, a.state.Validators[0], b.state.Validators[0], "validators are equal when they are supposed to be different")
// Modify all validators from copied state.
assert.NoError(t, b.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
return true, &ethpb.Validator{PublicKey: []byte{'V'}}, nil
}))
// Ensure reference is properly accounted for.
assert.NoError(t, a.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
assert.NotEqual(t, bytesutil.ToBytes48([]byte{'V'}), val.PublicKey())
return nil
}))
}
// assertRefCount checks whether reference count for a given state
// at a given index is equal to expected amount.
func assertRefCount(t *testing.T, b *BeaconState, idx types.FieldIndex, want uint) {
if cnt := b.sharedFieldReferences[idx].Refs(); cnt != want {
t.Errorf("Unexpected count of references for index %d, want: %v, got: %v", idx, want, cnt)
}
}
// assertValFound checks whether item with a given value exists in list.
func assertValFound(t *testing.T, vals [][]byte, val []byte) {
for i := range vals {
if reflect.DeepEqual(vals[i], val) {
return
}
}
t.Log(string(debug.Stack()))
t.Fatalf("Expected value not found (%v), want: %v", vals, val)
}
// assertValNotFound checks whether item with a given value doesn't exist in list.
func assertValNotFound(t *testing.T, vals [][]byte, val []byte) {
for i := range vals {
if reflect.DeepEqual(vals[i], val) {
t.Log(string(debug.Stack()))
t.Errorf("Unexpected value found (%v),: %v", vals, val)
return
}
}
}

View File

@@ -0,0 +1,98 @@
package v1
import (
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// RotateAttestations sets the previous epoch attestations to the current epoch attestations and
// then clears the current epoch attestations.
func (b *BeaconState) RotateAttestations() error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.setPreviousEpochAttestations(b.currentEpochAttestations())
b.setCurrentEpochAttestations([]*ethpb.PendingAttestation{})
return nil
}
func (b *BeaconState) setPreviousEpochAttestations(val []*ethpb.PendingAttestation) {
b.sharedFieldReferences[previousEpochAttestations].MinusRef()
b.sharedFieldReferences[previousEpochAttestations] = stateutil.NewRef(1)
b.state.PreviousEpochAttestations = val
b.markFieldAsDirty(previousEpochAttestations)
b.rebuildTrie[previousEpochAttestations] = true
}
func (b *BeaconState) setCurrentEpochAttestations(val []*ethpb.PendingAttestation) {
b.sharedFieldReferences[currentEpochAttestations].MinusRef()
b.sharedFieldReferences[currentEpochAttestations] = stateutil.NewRef(1)
b.state.CurrentEpochAttestations = val
b.markFieldAsDirty(currentEpochAttestations)
b.rebuildTrie[currentEpochAttestations] = true
}
// AppendCurrentEpochAttestations for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendCurrentEpochAttestations(val *ethpb.PendingAttestation) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
atts := b.state.CurrentEpochAttestations
max := uint64(fieldparams.CurrentEpochAttestationsLength)
if uint64(len(atts)) >= max {
return fmt.Errorf("current pending attestation exceeds max length %d", max)
}
if b.sharedFieldReferences[currentEpochAttestations].Refs() > 1 {
// Copy elements in underlying array by reference.
atts = make([]*ethpb.PendingAttestation, len(b.state.CurrentEpochAttestations))
copy(atts, b.state.CurrentEpochAttestations)
b.sharedFieldReferences[currentEpochAttestations].MinusRef()
b.sharedFieldReferences[currentEpochAttestations] = stateutil.NewRef(1)
}
b.state.CurrentEpochAttestations = append(atts, val)
b.markFieldAsDirty(currentEpochAttestations)
b.addDirtyIndices(currentEpochAttestations, []uint64{uint64(len(b.state.CurrentEpochAttestations) - 1)})
return nil
}
// AppendPreviousEpochAttestations for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendPreviousEpochAttestations(val *ethpb.PendingAttestation) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
atts := b.state.PreviousEpochAttestations
max := uint64(fieldparams.PreviousEpochAttestationsLength)
if uint64(len(atts)) >= max {
return fmt.Errorf("previous pending attestation exceeds max length %d", max)
}
if b.sharedFieldReferences[previousEpochAttestations].Refs() > 1 {
atts = make([]*ethpb.PendingAttestation, len(b.state.PreviousEpochAttestations))
copy(atts, b.state.PreviousEpochAttestations)
b.sharedFieldReferences[previousEpochAttestations].MinusRef()
b.sharedFieldReferences[previousEpochAttestations] = stateutil.NewRef(1)
}
b.state.PreviousEpochAttestations = append(atts, val)
b.markFieldAsDirty(previousEpochAttestations)
b.addDirtyIndices(previousEpochAttestations, []uint64{uint64(len(b.state.PreviousEpochAttestations) - 1)})
return nil
}

View File

@@ -0,0 +1,72 @@
package v1
import (
"context"
"testing"
types "github.com/prysmaticlabs/eth2-types"
stateTypes "github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/params"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_RotateAttestations(t *testing.T) {
st, err := InitializeFromProto(&ethpb.BeaconState{
Slot: 1,
CurrentEpochAttestations: []*ethpb.PendingAttestation{{Data: &ethpb.AttestationData{Slot: 456}}},
PreviousEpochAttestations: []*ethpb.PendingAttestation{{Data: &ethpb.AttestationData{Slot: 123}}},
})
require.NoError(t, err)
require.NoError(t, st.RotateAttestations())
require.Equal(t, 0, len(st.currentEpochAttestations()))
require.Equal(t, types.Slot(456), st.previousEpochAttestations()[0].Data.Slot)
}
func TestAppendBeyondIndicesLimit(t *testing.T) {
zeroHash := params.BeaconConfig().ZeroHash
mockblockRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(mockblockRoots); i++ {
mockblockRoots[i] = zeroHash[:]
}
mockstateRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(mockstateRoots); i++ {
mockstateRoots[i] = zeroHash[:]
}
mockrandaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(mockrandaoMixes); i++ {
mockrandaoMixes[i] = zeroHash[:]
}
st, err := InitializeFromProto(&ethpb.BeaconState{
Slot: 1,
CurrentEpochAttestations: []*ethpb.PendingAttestation{{Data: &ethpb.AttestationData{Slot: 456}}},
PreviousEpochAttestations: []*ethpb.PendingAttestation{{Data: &ethpb.AttestationData{Slot: 123}}},
Validators: []*ethpb.Validator{},
Eth1Data: &ethpb.Eth1Data{},
BlockRoots: mockblockRoots,
StateRoots: mockstateRoots,
RandaoMixes: mockrandaoMixes,
})
require.NoError(t, err)
_, err = st.HashTreeRoot(context.Background())
require.NoError(t, err)
for i := stateTypes.FieldIndex(0); i < stateTypes.FieldIndex(params.BeaconConfig().BeaconStateFieldCount); i++ {
st.dirtyFields[i] = true
}
_, err = st.HashTreeRoot(context.Background())
require.NoError(t, err)
for i := 0; i < 10; i++ {
assert.NoError(t, st.AppendValidator(&ethpb.Validator{}))
}
assert.Equal(t, false, st.rebuildTrie[validators])
assert.NotEqual(t, len(st.dirtyIndices[validators]), 0)
for i := 0; i < indicesLimit; i++ {
assert.NoError(t, st.AppendValidator(&ethpb.Validator{}))
}
assert.Equal(t, true, st.rebuildTrie[validators])
assert.Equal(t, len(st.dirtyIndices[validators]), 0)
}

View File

@@ -0,0 +1,68 @@
package v1
import (
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetLatestBlockHeader in the beacon state.
func (b *BeaconState) SetLatestBlockHeader(val *ethpb.BeaconBlockHeader) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.LatestBlockHeader = ethpb.CopyBeaconBlockHeader(val)
b.markFieldAsDirty(latestBlockHeader)
return nil
}
// SetBlockRoots for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetBlockRoots(val [][]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[blockRoots].MinusRef()
b.sharedFieldReferences[blockRoots] = stateutil.NewRef(1)
b.state.BlockRoots = val
b.markFieldAsDirty(blockRoots)
b.rebuildTrie[blockRoots] = true
return nil
}
// UpdateBlockRootAtIndex for the beacon state. Updates the block root
// at a specific index to a new value.
func (b *BeaconState) UpdateBlockRootAtIndex(idx uint64, blockRoot [32]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if uint64(len(b.state.BlockRoots)) <= idx {
return fmt.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
r := b.state.BlockRoots
if ref := b.sharedFieldReferences[blockRoots]; ref.Refs() > 1 {
// Copy elements in underlying array by reference.
r = make([][]byte, len(b.state.BlockRoots))
copy(r, b.state.BlockRoots)
ref.MinusRef()
b.sharedFieldReferences[blockRoots] = stateutil.NewRef(1)
}
r[idx] = blockRoot[:]
b.state.BlockRoots = r
b.markFieldAsDirty(blockRoots)
b.addDirtyIndices(blockRoots, []uint64{idx})
return nil
}

View File

@@ -0,0 +1,58 @@
package v1
import (
"github.com/prysmaticlabs/go-bitfield"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetJustificationBits for the beacon state.
func (b *BeaconState) SetJustificationBits(val bitfield.Bitvector4) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.JustificationBits = val
b.markFieldAsDirty(justificationBits)
return nil
}
// SetPreviousJustifiedCheckpoint for the beacon state.
func (b *BeaconState) SetPreviousJustifiedCheckpoint(val *ethpb.Checkpoint) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.PreviousJustifiedCheckpoint = val
b.markFieldAsDirty(previousJustifiedCheckpoint)
return nil
}
// SetCurrentJustifiedCheckpoint for the beacon state.
func (b *BeaconState) SetCurrentJustifiedCheckpoint(val *ethpb.Checkpoint) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.CurrentJustifiedCheckpoint = val
b.markFieldAsDirty(currentJustifiedCheckpoint)
return nil
}
// SetFinalizedCheckpoint for the beacon state.
func (b *BeaconState) SetFinalizedCheckpoint(val *ethpb.Checkpoint) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.FinalizedCheckpoint = val
b.markFieldAsDirty(finalizedCheckpoint)
return nil
}

View File

@@ -0,0 +1,74 @@
package v1
import (
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetEth1Data for the beacon state.
func (b *BeaconState) SetEth1Data(val *ethpb.Eth1Data) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.Eth1Data = val
b.markFieldAsDirty(eth1Data)
return nil
}
// SetEth1DataVotes for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetEth1DataVotes(val []*ethpb.Eth1Data) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[eth1DataVotes].MinusRef()
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
b.state.Eth1DataVotes = val
b.markFieldAsDirty(eth1DataVotes)
b.rebuildTrie[eth1DataVotes] = true
return nil
}
// SetEth1DepositIndex for the beacon state.
func (b *BeaconState) SetEth1DepositIndex(val uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.Eth1DepositIndex = val
b.markFieldAsDirty(eth1DepositIndex)
return nil
}
// AppendEth1DataVotes for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendEth1DataVotes(val *ethpb.Eth1Data) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
votes := b.state.Eth1DataVotes
if b.sharedFieldReferences[eth1DataVotes].Refs() > 1 {
// Copy elements in underlying array by reference.
votes = make([]*ethpb.Eth1Data, len(b.state.Eth1DataVotes))
copy(votes, b.state.Eth1DataVotes)
b.sharedFieldReferences[eth1DataVotes].MinusRef()
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
}
b.state.Eth1DataVotes = append(votes, val)
b.markFieldAsDirty(eth1DataVotes)
b.addDirtyIndices(eth1DataVotes, []uint64{uint64(len(b.state.Eth1DataVotes) - 1)})
return nil
}

View File

@@ -0,0 +1,187 @@
package v1
import (
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
stateTypes "github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/features"
"github.com/prysmaticlabs/prysm/crypto/hash"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"google.golang.org/protobuf/proto"
)
// For our setters, we have a field reference counter through
// which we can track shared field references. This helps when
// performing state copies, as we simply copy the reference to the
// field. When we do need to modify these fields, we
// perform a full copy of the field. This is true of most of our
// fields except for the following below.
// 1) BlockRoots
// 2) StateRoots
// 3) Eth1DataVotes
// 4) RandaoMixes
// 5) HistoricalRoots
// 6) CurrentEpochAttestations
// 7) PreviousEpochAttestations
// 8) Validators
//
// The fields referred to above are instead copied by reference, where
// we simply copy the reference to the underlying object instead of the
// whole object. This is possible due to how we have structured our state
// as we copy the value on read, so as to ensure the underlying object is
// not mutated while it is being accessed during a state read.
const (
// This specifies the limit till which we process all dirty indices for a certain field.
// If we have more dirty indices than the threshold, then we rebuild the whole trie. This
// comes due to the fact that O(alogn) > O(n) beyond a certain value of a.
indicesLimit = 8000
)
// SetGenesisTime for the beacon state.
func (b *BeaconState) SetGenesisTime(val uint64) error {
b.lock.Lock()
defer b.lock.Unlock()
b.state.GenesisTime = val
b.markFieldAsDirty(genesisTime)
return nil
}
// SetGenesisValidatorRoot for the beacon state.
func (b *BeaconState) SetGenesisValidatorRoot(val []byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.state.GenesisValidatorsRoot = val
b.markFieldAsDirty(genesisValidatorRoot)
return nil
}
// SetSlot for the beacon state.
func (b *BeaconState) SetSlot(val types.Slot) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.Slot = val
b.markFieldAsDirty(slot)
return nil
}
// SetFork version for the beacon chain.
func (b *BeaconState) SetFork(val *ethpb.Fork) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
fk, ok := proto.Clone(val).(*ethpb.Fork)
if !ok {
return errors.New("proto.Clone did not return a fork proto")
}
b.state.Fork = fk
b.markFieldAsDirty(fork)
return nil
}
// SetHistoricalRoots for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetHistoricalRoots(val [][]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[historicalRoots].MinusRef()
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
b.state.HistoricalRoots = val
b.markFieldAsDirty(historicalRoots)
return nil
}
// AppendHistoricalRoots for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendHistoricalRoots(root [32]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
roots := b.state.HistoricalRoots
if b.sharedFieldReferences[historicalRoots].Refs() > 1 {
roots = make([][]byte, len(b.state.HistoricalRoots))
copy(roots, b.state.HistoricalRoots)
b.sharedFieldReferences[historicalRoots].MinusRef()
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
}
b.state.HistoricalRoots = append(roots, root[:])
b.markFieldAsDirty(historicalRoots)
return nil
}
// Recomputes the branch up the index in the Merkle trie representation
// of the beacon state. This method performs slice reads and the caller MUST
// hold the lock before calling this method.
func (b *BeaconState) recomputeRoot(idx int) {
hashFunc := hash.CustomSHA256Hasher()
layers := b.merkleLayers
// The merkle tree structure looks as follows:
// [[r1, r2, r3, r4], [parent1, parent2], [root]]
// Using information about the index which changed, idx, we recompute
// only its branch up the tree.
currentIndex := idx
root := b.merkleLayers[0][idx]
for i := 0; i < len(layers)-1; i++ {
isLeft := currentIndex%2 == 0
neighborIdx := currentIndex ^ 1
neighbor := make([]byte, 32)
if layers[i] != nil && len(layers[i]) != 0 && neighborIdx < len(layers[i]) {
neighbor = layers[i][neighborIdx]
}
if isLeft {
parentHash := hashFunc(append(root, neighbor...))
root = parentHash[:]
} else {
parentHash := hashFunc(append(neighbor, root...))
root = parentHash[:]
}
parentIdx := currentIndex / 2
// Update the cached layers at the parent index.
layers[i+1][parentIdx] = root
currentIndex = parentIdx
}
b.merkleLayers = layers
}
func (b *BeaconState) markFieldAsDirty(field stateTypes.FieldIndex) {
b.dirtyFields[field] = true
}
// addDirtyIndices adds the relevant dirty field indices, so that they
// can be recomputed.
func (b *BeaconState) addDirtyIndices(index stateTypes.FieldIndex, indices []uint64) {
if b.rebuildTrie[index] {
return
}
// Exit early if balance trie computation isn't enabled.
if !features.Get().EnableBalanceTrieComputation && index == balances {
return
}
totalIndicesLen := len(b.dirtyIndices[index]) + len(indices)
if totalIndicesLen > indicesLimit {
b.rebuildTrie[index] = true
b.dirtyIndices[index] = []uint64{}
} else {
b.dirtyIndices[index] = append(b.dirtyIndices[index], indices...)
}
}

View File

@@ -0,0 +1,53 @@
package v1
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
)
// SetRandaoMixes for the beacon state. Updates the entire
// randao mixes to a new value by overwriting the previous one.
func (b *BeaconState) SetRandaoMixes(val [][]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[randaoMixes].MinusRef()
b.sharedFieldReferences[randaoMixes] = stateutil.NewRef(1)
b.state.RandaoMixes = val
b.markFieldAsDirty(randaoMixes)
b.rebuildTrie[randaoMixes] = true
return nil
}
// UpdateRandaoMixesAtIndex for the beacon state. Updates the randao mixes
// at a specific index to a new value.
func (b *BeaconState) UpdateRandaoMixesAtIndex(idx uint64, val []byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if uint64(len(b.state.RandaoMixes)) <= idx {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
mixes := b.state.RandaoMixes
if refs := b.sharedFieldReferences[randaoMixes].Refs(); refs > 1 {
// Copy elements in underlying array by reference.
mixes = make([][]byte, len(b.state.RandaoMixes))
copy(mixes, b.state.RandaoMixes)
b.sharedFieldReferences[randaoMixes].MinusRef()
b.sharedFieldReferences[randaoMixes] = stateutil.NewRef(1)
}
mixes[idx] = val
b.state.RandaoMixes = mixes
b.markFieldAsDirty(randaoMixes)
b.addDirtyIndices(randaoMixes, []uint64{idx})
return nil
}

View File

@@ -0,0 +1,59 @@
package v1
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
)
// SetStateRoots for the beacon state. Updates the state roots
// to a new value by overwriting the previous value.
func (b *BeaconState) SetStateRoots(val [][]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[stateRoots].MinusRef()
b.sharedFieldReferences[stateRoots] = stateutil.NewRef(1)
b.state.StateRoots = val
b.markFieldAsDirty(stateRoots)
b.rebuildTrie[stateRoots] = true
return nil
}
// UpdateStateRootAtIndex for the beacon state. Updates the state root
// at a specific index to a new value.
func (b *BeaconState) UpdateStateRootAtIndex(idx uint64, stateRoot [32]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.RLock()
if uint64(len(b.state.StateRoots)) <= idx {
b.lock.RUnlock()
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.RUnlock()
b.lock.Lock()
defer b.lock.Unlock()
// Check if we hold the only reference to the shared state roots slice.
r := b.state.StateRoots
if ref := b.sharedFieldReferences[stateRoots]; ref.Refs() > 1 {
// Copy elements in underlying array by reference.
r = make([][]byte, len(b.state.StateRoots))
copy(r, b.state.StateRoots)
ref.MinusRef()
b.sharedFieldReferences[stateRoots] = stateutil.NewRef(1)
}
r[idx] = stateRoot[:]
b.state.StateRoots = r
b.markFieldAsDirty(stateRoots)
b.addDirtyIndices(stateRoots, []uint64{idx})
return nil
}

View File

@@ -0,0 +1,228 @@
package v1
import (
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetValidators for the beacon state. Updates the entire
// to a new value by overwriting the previous one.
func (b *BeaconState) SetValidators(val []*ethpb.Validator) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.Validators = val
b.sharedFieldReferences[validators].MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
b.markFieldAsDirty(validators)
b.rebuildTrie[validators] = true
b.valMapHandler = stateutil.NewValMapHandler(b.state.Validators)
return nil
}
// ApplyToEveryValidator applies the provided callback function to each validator in the
// validator registry.
func (b *BeaconState) ApplyToEveryValidator(f func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error)) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
v := b.state.Validators
if ref := b.sharedFieldReferences[validators]; ref.Refs() > 1 {
v = b.validatorsReferences()
ref.MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
}
b.lock.Unlock()
var changedVals []uint64
for i, val := range v {
changed, newVal, err := f(i, val)
if err != nil {
return err
}
if changed {
changedVals = append(changedVals, uint64(i))
v[i] = newVal
}
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.Validators = v
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, changedVals)
return nil
}
// UpdateValidatorAtIndex for the beacon state. Updates the validator
// at a specific index to a new value.
func (b *BeaconState) UpdateValidatorAtIndex(idx types.ValidatorIndex, val *ethpb.Validator) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if uint64(len(b.state.Validators)) <= uint64(idx) {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
v := b.state.Validators
if ref := b.sharedFieldReferences[validators]; ref.Refs() > 1 {
v = b.validatorsReferences()
ref.MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
}
v[idx] = val
b.state.Validators = v
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, []uint64{uint64(idx)})
return nil
}
// SetBalances for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetBalances(val []uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
b.state.Balances = val
b.markFieldAsDirty(balances)
b.rebuildTrie[balances] = true
return nil
}
// UpdateBalancesAtIndex for the beacon state. This method updates the balance
// at a specific index to a new value.
func (b *BeaconState) UpdateBalancesAtIndex(idx types.ValidatorIndex, val uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if uint64(len(b.state.Balances)) <= uint64(idx) {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
bals := b.state.Balances
if b.sharedFieldReferences[balances].Refs() > 1 {
bals = b.balances()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
}
bals[idx] = val
b.state.Balances = bals
b.markFieldAsDirty(balances)
b.addDirtyIndices(balances, []uint64{uint64(idx)})
return nil
}
// SetSlashings for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetSlashings(val []uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[slashings].MinusRef()
b.sharedFieldReferences[slashings] = stateutil.NewRef(1)
b.state.Slashings = val
b.markFieldAsDirty(slashings)
return nil
}
// UpdateSlashingsAtIndex for the beacon state. Updates the slashings
// at a specific index to a new value.
func (b *BeaconState) UpdateSlashingsAtIndex(idx, val uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if uint64(len(b.state.Slashings)) <= idx {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
s := b.state.Slashings
if b.sharedFieldReferences[slashings].Refs() > 1 {
s = b.slashings()
b.sharedFieldReferences[slashings].MinusRef()
b.sharedFieldReferences[slashings] = stateutil.NewRef(1)
}
s[idx] = val
b.state.Slashings = s
b.markFieldAsDirty(slashings)
return nil
}
// AppendValidator for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendValidator(val *ethpb.Validator) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
vals := b.state.Validators
if b.sharedFieldReferences[validators].Refs() > 1 {
vals = b.validatorsReferences()
b.sharedFieldReferences[validators].MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
}
// append validator to slice
b.state.Validators = append(vals, val)
valIdx := types.ValidatorIndex(len(b.state.Validators) - 1)
b.valMapHandler.Set(bytesutil.ToBytes48(val.PublicKey), valIdx)
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, []uint64{uint64(valIdx)})
return nil
}
// AppendBalance for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendBalance(bal uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
bals := b.state.Balances
if b.sharedFieldReferences[balances].Refs() > 1 {
bals = b.balances()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
}
b.state.Balances = append(bals, bal)
balIdx := len(b.state.Balances) - 1
b.markFieldAsDirty(balances)
b.addDirtyIndices(balances, []uint64{uint64(balIdx)})
return nil
}

View File

@@ -0,0 +1,193 @@
package v1
import (
"context"
"strconv"
"sync"
"testing"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
)
func TestValidatorMap_DistinctCopy(t *testing.T) {
count := uint64(100)
vals := make([]*ethpb.Validator, 0, count)
for i := uint64(1); i < count; i++ {
someRoot := [32]byte{}
someKey := [fieldparams.BLSPubkeyLength]byte{}
copy(someRoot[:], strconv.Itoa(int(i)))
copy(someKey[:], strconv.Itoa(int(i)))
vals = append(vals, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
}
handler := stateutil.NewValMapHandler(vals)
newHandler := handler.Copy()
wantedPubkey := strconv.Itoa(22)
handler.Set(bytesutil.ToBytes48([]byte(wantedPubkey)), 27)
val1, _ := handler.Get(bytesutil.ToBytes48([]byte(wantedPubkey)))
val2, _ := newHandler.Get(bytesutil.ToBytes48([]byte(wantedPubkey)))
assert.NotEqual(t, val1, val2, "Values are supposed to be unequal due to copy")
}
func TestBeaconState_NoDeadlock(t *testing.T) {
count := uint64(100)
vals := make([]*ethpb.Validator, 0, count)
for i := uint64(1); i < count; i++ {
someRoot := [32]byte{}
someKey := [fieldparams.BLSPubkeyLength]byte{}
copy(someRoot[:], strconv.Itoa(int(i)))
copy(someKey[:], strconv.Itoa(int(i)))
vals = append(vals, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
}
st, err := InitializeFromProtoUnsafe(&ethpb.BeaconState{
Validators: vals,
})
assert.NoError(t, err)
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
// Continuously lock and unlock the state
// by acquiring the lock.
for i := 0; i < 1000; i++ {
for _, f := range st.stateFieldLeaves {
f.Lock()
if f.Empty() {
f.InsertFieldLayer(make([][]*[32]byte, 10))
}
f.Unlock()
f.FieldReference().AddRef()
}
}
wg.Done()
}()
// Constantly read from the offending portion
// of the code to ensure there is no possible
// recursive read locking.
for i := 0; i < 1000; i++ {
go func() {
_ = st.FieldReferencesCount()
}()
}
// Test will not terminate in the event of a deadlock.
wg.Wait()
}
func TestStateTrie_IsNil(t *testing.T) {
var emptyState *BeaconState
assert.Equal(t, true, emptyState.IsNil())
emptyProto := &BeaconState{state: nil}
assert.Equal(t, true, emptyProto.IsNil())
nonNilState := &BeaconState{state: &ethpb.BeaconState{}}
assert.Equal(t, false, nonNilState.IsNil())
}
func TestBeaconState_AppendBalanceWithTrie(t *testing.T) {
count := uint64(100)
vals := make([]*ethpb.Validator, 0, count)
bals := make([]uint64, 0, count)
for i := uint64(1); i < count; i++ {
someRoot := [32]byte{}
someKey := [fieldparams.BLSPubkeyLength]byte{}
copy(someRoot[:], strconv.Itoa(int(i)))
copy(someKey[:], strconv.Itoa(int(i)))
vals = append(vals, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
bals = append(bals, params.BeaconConfig().MaxEffectiveBalance)
}
zeroHash := params.BeaconConfig().ZeroHash
mockblockRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(mockblockRoots); i++ {
mockblockRoots[i] = zeroHash[:]
}
mockstateRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(mockstateRoots); i++ {
mockstateRoots[i] = zeroHash[:]
}
mockrandaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(mockrandaoMixes); i++ {
mockrandaoMixes[i] = zeroHash[:]
}
st, err := InitializeFromProto(&ethpb.BeaconState{
Slot: 1,
GenesisValidatorsRoot: make([]byte, 32),
Fork: &ethpb.Fork{
PreviousVersion: make([]byte, 4),
CurrentVersion: make([]byte, 4),
Epoch: 0,
},
LatestBlockHeader: &ethpb.BeaconBlockHeader{
ParentRoot: make([]byte, fieldparams.RootLength),
StateRoot: make([]byte, fieldparams.RootLength),
BodyRoot: make([]byte, fieldparams.RootLength),
},
Validators: vals,
Balances: bals,
Eth1Data: &ethpb.Eth1Data{
DepositRoot: make([]byte, 32),
BlockHash: make([]byte, 32),
},
BlockRoots: mockblockRoots,
StateRoots: mockstateRoots,
RandaoMixes: mockrandaoMixes,
JustificationBits: bitfield.NewBitvector4(),
PreviousJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
FinalizedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
})
assert.NoError(t, err)
_, err = st.HashTreeRoot(context.Background())
assert.NoError(t, err)
for i := 0; i < 100; i++ {
if i%2 == 0 {
assert.NoError(t, st.UpdateBalancesAtIndex(types.ValidatorIndex(i), 1000))
}
if i%3 == 0 {
assert.NoError(t, st.AppendBalance(1000))
}
}
_, err = st.HashTreeRoot(context.Background())
assert.NoError(t, err)
newRt := bytesutil.ToBytes32(st.merkleLayers[0][balances])
wantedRt, err := stateutil.Uint64ListRootWithRegistryLimit(st.state.Balances)
assert.NoError(t, err)
assert.Equal(t, wantedRt, newRt, "state roots are unequal")
}

View File

@@ -0,0 +1,434 @@
package v1
import (
"context"
"runtime"
"sort"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/fieldtrie"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/features"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/container/slice"
"github.com/prysmaticlabs/prysm/crypto/hash"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/encoding/ssz"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"go.opencensus.io/trace"
"google.golang.org/protobuf/proto"
)
// InitializeFromProto the beacon state from a protobuf representation.
func InitializeFromProto(st *ethpb.BeaconState) (*BeaconState, error) {
return InitializeFromProtoUnsafe(proto.Clone(st).(*ethpb.BeaconState))
}
// InitializeFromProtoUnsafe directly uses the beacon state protobuf pointer
// and sets it as the inner state of the BeaconState type.
func InitializeFromProtoUnsafe(st *ethpb.BeaconState) (*BeaconState, error) {
if st == nil {
return nil, errors.New("received nil state")
}
fieldCount := params.BeaconConfig().BeaconStateFieldCount
b := &BeaconState{
state: st,
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, 10),
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
valMapHandler: stateutil.NewValMapHandler(st.Validators),
}
var err error
for i := 0; i < fieldCount; i++ {
b.dirtyFields[types.FieldIndex(i)] = true
b.rebuildTrie[types.FieldIndex(i)] = true
b.dirtyIndices[types.FieldIndex(i)] = []uint64{}
b.stateFieldLeaves[types.FieldIndex(i)], err = fieldtrie.NewFieldTrie(types.FieldIndex(i), types.BasicArray, nil, 0)
if err != nil {
return nil, err
}
}
// Initialize field reference tracking for shared data.
b.sharedFieldReferences[randaoMixes] = stateutil.NewRef(1)
b.sharedFieldReferences[stateRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[blockRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[previousEpochAttestations] = stateutil.NewRef(1)
b.sharedFieldReferences[currentEpochAttestations] = stateutil.NewRef(1)
b.sharedFieldReferences[slashings] = stateutil.NewRef(1)
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
state.StateCount.Inc()
return b, nil
}
// Copy returns a deep copy of the beacon state.
func (b *BeaconState) Copy() state.BeaconState {
if !b.hasInnerState() {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
fieldCount := params.BeaconConfig().BeaconStateFieldCount
dst := &BeaconState{
state: &ethpb.BeaconState{
// Primitive types, safe to copy.
GenesisTime: b.state.GenesisTime,
Slot: b.state.Slot,
Eth1DepositIndex: b.state.Eth1DepositIndex,
// Large arrays, infrequently changed, constant size.
RandaoMixes: b.state.RandaoMixes,
StateRoots: b.state.StateRoots,
BlockRoots: b.state.BlockRoots,
PreviousEpochAttestations: b.state.PreviousEpochAttestations,
CurrentEpochAttestations: b.state.CurrentEpochAttestations,
Slashings: b.state.Slashings,
Eth1DataVotes: b.state.Eth1DataVotes,
// Large arrays, increases over time.
Validators: b.state.Validators,
Balances: b.state.Balances,
HistoricalRoots: b.state.HistoricalRoots,
// Everything else, too small to be concerned about, constant size.
Fork: b.fork(),
LatestBlockHeader: b.latestBlockHeader(),
Eth1Data: b.eth1Data(),
JustificationBits: b.justificationBits(),
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint(),
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint(),
FinalizedCheckpoint: b.finalizedCheckpoint(),
GenesisValidatorsRoot: b.genesisValidatorRoot(),
},
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, 10),
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
// Share the reference to validator index map.
valMapHandler: b.valMapHandler,
}
for field, ref := range b.sharedFieldReferences {
ref.AddRef()
dst.sharedFieldReferences[field] = ref
}
// Increment ref for validator map
b.valMapHandler.AddRef()
for i := range b.dirtyFields {
dst.dirtyFields[i] = true
}
for i := range b.dirtyIndices {
indices := make([]uint64, len(b.dirtyIndices[i]))
copy(indices, b.dirtyIndices[i])
dst.dirtyIndices[i] = indices
}
for i := range b.rebuildTrie {
dst.rebuildTrie[i] = true
}
for fldIdx, fieldTrie := range b.stateFieldLeaves {
dst.stateFieldLeaves[fldIdx] = fieldTrie
if fieldTrie.FieldReference() != nil {
fieldTrie.Lock()
fieldTrie.FieldReference().AddRef()
fieldTrie.Unlock()
}
}
if b.merkleLayers != nil {
dst.merkleLayers = make([][][]byte, len(b.merkleLayers))
for i, layer := range b.merkleLayers {
dst.merkleLayers[i] = make([][]byte, len(layer))
for j, content := range layer {
dst.merkleLayers[i][j] = make([]byte, len(content))
copy(dst.merkleLayers[i][j], content)
}
}
}
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(dst, func(b *BeaconState) {
for field, v := range b.sharedFieldReferences {
v.MinusRef()
if b.stateFieldLeaves[field].FieldReference() != nil {
b.stateFieldLeaves[field].FieldReference().MinusRef()
}
}
for i := 0; i < fieldCount; i++ {
field := types.FieldIndex(i)
delete(b.stateFieldLeaves, field)
delete(b.dirtyIndices, field)
delete(b.dirtyFields, field)
delete(b.sharedFieldReferences, field)
delete(b.stateFieldLeaves, field)
}
state.StateCount.Sub(1)
})
return dst
}
// HashTreeRoot of the beacon state retrieves the Merkle root of the trie
// representation of the beacon state based on the Ethereum Simple Serialize specification.
func (b *BeaconState) HashTreeRoot(ctx context.Context) ([32]byte, error) {
ctx, span := trace.StartSpan(ctx, "beaconState.HashTreeRoot")
defer span.End()
b.lock.Lock()
defer b.lock.Unlock()
if err := b.initializeMerkleLayers(ctx); err != nil {
return [32]byte{}, err
}
if err := b.recomputeDirtyFields(ctx); err != nil {
return [32]byte{}, err
}
return bytesutil.ToBytes32(b.merkleLayers[len(b.merkleLayers)-1][0]), nil
}
// Initializes the Merkle layers for the beacon state if they are empty.
// WARNING: Caller must acquire the mutex before using.
func (b *BeaconState) initializeMerkleLayers(ctx context.Context) error {
if len(b.merkleLayers) > 0 {
return nil
}
fieldRoots, err := computeFieldRoots(ctx, b.state)
if err != nil {
return err
}
layers := stateutil.Merkleize(fieldRoots)
b.merkleLayers = layers
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateFieldCount)
return nil
}
// Recomputes the Merkle layers for the dirty fields in the state.
// WARNING: Caller must acquire the mutex before using.
func (b *BeaconState) recomputeDirtyFields(ctx context.Context) error {
for field := range b.dirtyFields {
root, err := b.rootSelector(ctx, field)
if err != nil {
return err
}
b.merkleLayers[0][field] = root[:]
b.recomputeRoot(int(field))
delete(b.dirtyFields, field)
}
return nil
}
// FieldReferencesCount returns the reference count held by each field. This
// also includes the field trie held by each field.
func (b *BeaconState) FieldReferencesCount() map[string]uint64 {
refMap := make(map[string]uint64)
b.lock.RLock()
defer b.lock.RUnlock()
for i, f := range b.sharedFieldReferences {
refMap[i.String(b.Version())] = uint64(f.Refs())
}
for i, f := range b.stateFieldLeaves {
numOfRefs := uint64(f.FieldReference().Refs())
f.RLock()
if !f.Empty() {
refMap[i.String(b.Version())+"_trie"] = numOfRefs
}
f.RUnlock()
}
return refMap
}
// IsNil checks if the state and the underlying proto
// object are nil.
func (b *BeaconState) IsNil() bool {
return b == nil || b.state == nil
}
func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex) ([32]byte, error) {
_, span := trace.StartSpan(ctx, "beaconState.rootSelector")
defer span.End()
span.AddAttributes(trace.StringAttribute("field", field.String(b.Version())))
hasher := hash.CustomSHA256Hasher()
switch field {
case genesisTime:
return ssz.Uint64Root(b.state.GenesisTime), nil
case genesisValidatorRoot:
return bytesutil.ToBytes32(b.state.GenesisValidatorsRoot), nil
case slot:
return ssz.Uint64Root(uint64(b.state.Slot)), nil
case eth1DepositIndex:
return ssz.Uint64Root(b.state.Eth1DepositIndex), nil
case fork:
return ssz.ForkRoot(b.state.Fork)
case latestBlockHeader:
return stateutil.BlockHeaderRoot(b.state.LatestBlockHeader)
case blockRoots:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.state.BlockRoots, fieldparams.BlockRootsLength)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(blockRoots, b.state.BlockRoots)
case stateRoots:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.state.StateRoots, fieldparams.StateRootsLength)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(stateRoots, b.state.StateRoots)
case historicalRoots:
return ssz.ByteArrayRootWithLimit(b.state.HistoricalRoots, fieldparams.HistoricalRootsLength)
case eth1Data:
return stateutil.Eth1Root(hasher, b.state.Eth1Data)
case eth1DataVotes:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(
field,
b.state.Eth1DataVotes,
fieldparams.Eth1DataVotesLength,
)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(field, b.state.Eth1DataVotes)
case validators:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.state.Validators, fieldparams.ValidatorRegistryLimit)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, validators)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(validators, b.state.Validators)
case balances:
if features.Get().EnableBalanceTrieComputation {
if b.rebuildTrie[field] {
maxBalCap := uint64(fieldparams.ValidatorRegistryLimit)
elemSize := uint64(8)
balLimit := (maxBalCap*elemSize + 31) / 32
err := b.resetFieldTrie(field, b.state.Balances, balLimit)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(balances, b.state.Balances)
}
return stateutil.Uint64ListRootWithRegistryLimit(b.state.Balances)
case randaoMixes:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.state.RandaoMixes, fieldparams.RandaoMixesLength)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(randaoMixes, b.state.RandaoMixes)
case slashings:
return ssz.SlashingsRoot(b.state.Slashings)
case previousEpochAttestations:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(
field,
b.state.PreviousEpochAttestations,
fieldparams.PreviousEpochAttestationsLength,
)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(field, b.state.PreviousEpochAttestations)
case currentEpochAttestations:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(
field,
b.state.CurrentEpochAttestations,
fieldparams.CurrentEpochAttestationsLength,
)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(field, b.state.CurrentEpochAttestations)
case justificationBits:
return bytesutil.ToBytes32(b.state.JustificationBits), nil
case previousJustifiedCheckpoint:
return ssz.CheckpointRoot(hasher, b.state.PreviousJustifiedCheckpoint)
case currentJustifiedCheckpoint:
return ssz.CheckpointRoot(hasher, b.state.CurrentJustifiedCheckpoint)
case finalizedCheckpoint:
return ssz.CheckpointRoot(hasher, b.state.FinalizedCheckpoint)
}
return [32]byte{}, errors.New("invalid field index provided")
}
func (b *BeaconState) recomputeFieldTrie(index types.FieldIndex, elements interface{}) ([32]byte, error) {
fTrie := b.stateFieldLeaves[index]
// We can't lock the trie directly because the trie's variable gets reassigned,
// and therefore we would call Unlock() on a different object.
fTrieMutex := fTrie.RWMutex
if fTrie.FieldReference().Refs() > 1 {
fTrieMutex.Lock()
fTrie.FieldReference().MinusRef()
newTrie := fTrie.CopyTrie()
b.stateFieldLeaves[index] = newTrie
fTrie = newTrie
fTrieMutex.Unlock()
}
// remove duplicate indexes
b.dirtyIndices[index] = slice.SetUint64(b.dirtyIndices[index])
// sort indexes again
sort.Slice(b.dirtyIndices[index], func(i int, j int) bool {
return b.dirtyIndices[index][i] < b.dirtyIndices[index][j]
})
root, err := fTrie.RecomputeTrie(b.dirtyIndices[index], elements)
if err != nil {
return [32]byte{}, err
}
b.dirtyIndices[index] = []uint64{}
return root, nil
}
func (b *BeaconState) resetFieldTrie(index types.FieldIndex, elements interface{}, length uint64) error {
fTrie, err := fieldtrie.NewFieldTrie(index, fieldMap[index], elements, length)
if err != nil {
return err
}
b.stateFieldLeaves[index] = fTrie
b.dirtyIndices[index] = []uint64{}
return nil
}

View File

@@ -0,0 +1,260 @@
package v1_test
import (
"bytes"
"context"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
"github.com/prysmaticlabs/prysm/config/features"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
)
func TestMain(m *testing.M) {
resetCfg := features.InitWithReset(&features.Flags{EnableBalanceTrieComputation: true})
defer resetCfg()
m.Run()
}
func TestInitializeFromProto(t *testing.T) {
testState, _ := util.DeterministicGenesisState(t, 64)
pbState, err := v1.ProtobufBeaconState(testState.InnerStateUnsafe())
require.NoError(t, err)
type test struct {
name string
state *ethpb.BeaconState
error string
}
initTests := []test{
{
name: "nil state",
state: nil,
error: "received nil state",
},
{
name: "nil validators",
state: &ethpb.BeaconState{
Slot: 4,
Validators: nil,
},
},
{
name: "empty state",
state: &ethpb.BeaconState{},
},
{
name: "full state",
state: pbState,
},
}
for _, tt := range initTests {
t.Run(tt.name, func(t *testing.T) {
_, err := v1.InitializeFromProto(tt.state)
if tt.error != "" {
assert.ErrorContains(t, tt.error, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestInitializeFromProtoUnsafe(t *testing.T) {
testState, _ := util.DeterministicGenesisState(t, 64)
pbState, err := v1.ProtobufBeaconState(testState.InnerStateUnsafe())
require.NoError(t, err)
type test struct {
name string
state *ethpb.BeaconState
error string
}
initTests := []test{
{
name: "nil state",
state: nil,
error: "received nil state",
},
{
name: "nil validators",
state: &ethpb.BeaconState{
Slot: 4,
Validators: nil,
},
},
{
name: "empty state",
state: &ethpb.BeaconState{},
},
{
name: "full state",
state: pbState,
},
}
for _, tt := range initTests {
t.Run(tt.name, func(t *testing.T) {
_, err := v1.InitializeFromProtoUnsafe(tt.state)
if tt.error != "" {
assert.ErrorContains(t, tt.error, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestBeaconState_HashTreeRoot(t *testing.T) {
testState, _ := util.DeterministicGenesisState(t, 64)
type test struct {
name string
stateModify func(beaconState state.BeaconState) (state.BeaconState, error)
error string
}
initTests := []test{
{
name: "unchanged state",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
return beaconState, nil
},
error: "",
},
{
name: "different slot",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
if err := beaconState.SetSlot(5); err != nil {
return nil, err
}
return beaconState, nil
},
error: "",
},
{
name: "different validator balance",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
val, err := beaconState.ValidatorAtIndex(5)
if err != nil {
return nil, err
}
val.EffectiveBalance = params.BeaconConfig().MaxEffectiveBalance - params.BeaconConfig().EffectiveBalanceIncrement
if err := beaconState.UpdateValidatorAtIndex(5, val); err != nil {
return nil, err
}
return beaconState, nil
},
error: "",
},
}
var err error
var oldHTR []byte
for _, tt := range initTests {
t.Run(tt.name, func(t *testing.T) {
testState, err = tt.stateModify(testState)
assert.NoError(t, err)
root, err := testState.HashTreeRoot(context.Background())
if err == nil && tt.error != "" {
t.Errorf("Expected error, expected %v, recevied %v", tt.error, err)
}
pbState, err := v1.ProtobufBeaconState(testState.InnerStateUnsafe())
require.NoError(t, err)
genericHTR, err := pbState.HashTreeRoot()
if err == nil && tt.error != "" {
t.Errorf("Expected error, expected %v, recevied %v", tt.error, err)
}
assert.DeepNotEqual(t, []byte{}, root[:], "Received empty hash tree root")
assert.DeepEqual(t, genericHTR[:], root[:], "Expected hash tree root to match generic")
if len(oldHTR) != 0 && bytes.Equal(root[:], oldHTR) {
t.Errorf("Expected HTR to change, received %#x == old %#x", root, oldHTR)
}
oldHTR = root[:]
})
}
}
func TestBeaconState_HashTreeRoot_FieldTrie(t *testing.T) {
testState, _ := util.DeterministicGenesisState(t, 64)
type test struct {
name string
stateModify func(state.BeaconState) (state.BeaconState, error)
error string
}
initTests := []test{
{
name: "unchanged state",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
return beaconState, nil
},
error: "",
},
{
name: "different slot",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
if err := beaconState.SetSlot(5); err != nil {
return nil, err
}
return beaconState, nil
},
error: "",
},
{
name: "different validator balance",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
val, err := beaconState.ValidatorAtIndex(5)
if err != nil {
return nil, err
}
val.EffectiveBalance = params.BeaconConfig().MaxEffectiveBalance - params.BeaconConfig().EffectiveBalanceIncrement
if err := beaconState.UpdateValidatorAtIndex(5, val); err != nil {
return nil, err
}
return beaconState, nil
},
error: "",
},
}
var err error
var oldHTR []byte
for _, tt := range initTests {
t.Run(tt.name, func(t *testing.T) {
testState, err = tt.stateModify(testState)
assert.NoError(t, err)
root, err := testState.HashTreeRoot(context.Background())
if err == nil && tt.error != "" {
t.Errorf("Expected error, expected %v, recevied %v", tt.error, err)
}
pbState, err := v1.ProtobufBeaconState(testState.InnerStateUnsafe())
require.NoError(t, err)
genericHTR, err := pbState.HashTreeRoot()
if err == nil && tt.error != "" {
t.Errorf("Expected error, expected %v, recevied %v", tt.error, err)
}
assert.DeepNotEqual(t, []byte{}, root[:], "Received empty hash tree root")
assert.DeepEqual(t, genericHTR[:], root[:], "Expected hash tree root to match generic")
if len(oldHTR) != 0 && bytes.Equal(root[:], oldHTR) {
t.Errorf("Expected HTR to change, received %#x == old %#x", root, oldHTR)
}
oldHTR = root[:]
})
}
}
func TestBeaconState_AppendValidator_DoesntMutateCopy(t *testing.T) {
st0, err := util.NewBeaconState()
require.NoError(t, err)
st1 := st0.Copy()
originalCount := st1.NumValidators()
val := &ethpb.Validator{Slashed: true}
assert.NoError(t, st0.AppendValidator(val))
assert.Equal(t, originalCount, st1.NumValidators(), "st1 NumValidators mutated")
_, ok := st1.ValidatorIndexByPubkey(bytesutil.ToBytes48(val.PublicKey))
assert.Equal(t, false, ok, "Expected no validator index to be present in st1 for the newly inserted pubkey")
}

View File

@@ -0,0 +1,78 @@
package v1
import (
"sync"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/fieldtrie"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/params"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// Ensure type BeaconState below implements BeaconState interface.
var _ state.BeaconState = (*BeaconState)(nil)
func init() {
fieldMap = make(map[types.FieldIndex]types.DataType, params.BeaconConfig().BeaconStateFieldCount)
// Initialize the fixed sized arrays.
fieldMap[types.BlockRoots] = types.BasicArray
fieldMap[types.StateRoots] = types.BasicArray
fieldMap[types.RandaoMixes] = types.BasicArray
// Initialize the composite arrays.
fieldMap[types.Eth1DataVotes] = types.CompositeArray
fieldMap[types.Validators] = types.CompositeArray
fieldMap[types.PreviousEpochAttestations] = types.CompositeArray
fieldMap[types.CurrentEpochAttestations] = types.CompositeArray
fieldMap[types.Balances] = types.CompressedArray
}
// fieldMap keeps track of each field
// to its corresponding data type.
var fieldMap map[types.FieldIndex]types.DataType
// ErrNilInnerState returns when the inner state is nil and no copy set or get
// operations can be performed on state.
var ErrNilInnerState = errors.New("nil inner state")
// BeaconState defines a struct containing utilities for the Ethereum Beacon Chain state, defining
// getters and setters for its respective values and helpful functions such as HashTreeRoot().
type BeaconState struct {
state *ethpb.BeaconState
lock sync.RWMutex
dirtyFields map[types.FieldIndex]bool
dirtyIndices map[types.FieldIndex][]uint64
stateFieldLeaves map[types.FieldIndex]*fieldtrie.FieldTrie
rebuildTrie map[types.FieldIndex]bool
valMapHandler *stateutil.ValidatorMapHandler
merkleLayers [][][]byte
sharedFieldReferences map[types.FieldIndex]*stateutil.Reference
}
// Field Aliases for values from the types package.
const (
genesisTime = types.GenesisTime
genesisValidatorRoot = types.GenesisValidatorRoot
slot = types.Slot
fork = types.Fork
latestBlockHeader = types.LatestBlockHeader
blockRoots = types.BlockRoots
stateRoots = types.StateRoots
historicalRoots = types.HistoricalRoots
eth1Data = types.Eth1Data
eth1DataVotes = types.Eth1DataVotes
eth1DepositIndex = types.Eth1DepositIndex
validators = types.Validators
balances = types.Balances
randaoMixes = types.RandaoMixes
slashings = types.Slashings
previousEpochAttestations = types.PreviousEpochAttestations
currentEpochAttestations = types.CurrentEpochAttestations
justificationBits = types.JustificationBits
previousJustifiedCheckpoint = types.PreviousJustifiedCheckpoint
currentJustifiedCheckpoint = types.CurrentJustifiedCheckpoint
finalizedCheckpoint = types.FinalizedCheckpoint
)

View File

@@ -0,0 +1,232 @@
package v1_test
import (
"context"
"reflect"
"strconv"
"testing"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/runtime/interop"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
log "github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto"
)
func TestBeaconState_ProtoBeaconStateCompatibility(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
ctx := context.Background()
genesis := setupGenesisState(t, 64)
customState, err := v1.InitializeFromProto(genesis)
require.NoError(t, err)
cloned, ok := proto.Clone(genesis).(*ethpb.BeaconState)
assert.Equal(t, true, ok, "Object is not of type *ethpb.BeaconState")
custom := customState.CloneInnerState()
assert.DeepSSZEqual(t, cloned, custom)
r1, err := customState.HashTreeRoot(ctx)
require.NoError(t, err)
beaconState, err := v1.InitializeFromProto(genesis)
require.NoError(t, err)
r2, err := beaconState.HashTreeRoot(context.Background())
require.NoError(t, err)
assert.Equal(t, r1, r2, "Mismatched roots")
// We then write to the the state and compare hash tree roots again.
balances := genesis.Balances
balances[0] = 3823
require.NoError(t, customState.SetBalances(balances))
r1, err = customState.HashTreeRoot(ctx)
require.NoError(t, err)
genesis.Balances = balances
beaconState, err = v1.InitializeFromProto(genesis)
require.NoError(t, err)
r2, err = beaconState.HashTreeRoot(context.Background())
require.NoError(t, err)
assert.Equal(t, r1, r2, "Mismatched roots")
}
func setupGenesisState(tb testing.TB, count uint64) *ethpb.BeaconState {
genesisState, _, err := interop.GenerateGenesisState(context.Background(), 0, count)
require.NoError(tb, err, "Could not generate genesis beacon state")
for i := uint64(1); i < count; i++ {
someRoot := [32]byte{}
someKey := [fieldparams.BLSPubkeyLength]byte{}
copy(someRoot[:], strconv.Itoa(int(i)))
copy(someKey[:], strconv.Itoa(int(i)))
genesisState.Validators = append(genesisState.Validators, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
genesisState.Balances = append(genesisState.Balances, params.BeaconConfig().MaxEffectiveBalance)
}
return genesisState
}
func BenchmarkCloneValidators_Proto(b *testing.B) {
b.StopTimer()
validators := make([]*ethpb.Validator, 16384)
somePubKey := [fieldparams.BLSPubkeyLength]byte{1, 2, 3}
someRoot := [32]byte{3, 4, 5}
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
PublicKey: somePubKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
ActivationEpoch: 3,
ExitEpoch: 4,
WithdrawableEpoch: 5,
}
}
b.StartTimer()
for i := 0; i < b.N; i++ {
cloneValidatorsWithProto(validators)
}
}
func BenchmarkCloneValidators_Manual(b *testing.B) {
b.StopTimer()
validators := make([]*ethpb.Validator, 16384)
somePubKey := [fieldparams.BLSPubkeyLength]byte{1, 2, 3}
someRoot := [32]byte{3, 4, 5}
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
PublicKey: somePubKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
ActivationEpoch: 3,
ExitEpoch: 4,
WithdrawableEpoch: 5,
}
}
b.StartTimer()
for i := 0; i < b.N; i++ {
cloneValidatorsManually(validators)
}
}
func BenchmarkStateClone_Proto(b *testing.B) {
b.StopTimer()
params.SetupTestConfigCleanup(b)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
genesis := setupGenesisState(b, 64)
b.StartTimer()
for i := 0; i < b.N; i++ {
_, ok := proto.Clone(genesis).(*ethpb.BeaconState)
assert.Equal(b, true, ok, "Entity is not of type *ethpb.BeaconState")
}
}
func BenchmarkStateClone_Manual(b *testing.B) {
b.StopTimer()
params.SetupTestConfigCleanup(b)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
genesis := setupGenesisState(b, 64)
st, err := v1.InitializeFromProto(genesis)
require.NoError(b, err)
b.StartTimer()
for i := 0; i < b.N; i++ {
_ = st.CloneInnerState()
}
}
func cloneValidatorsWithProto(vals []*ethpb.Validator) []*ethpb.Validator {
var ok bool
res := make([]*ethpb.Validator, len(vals))
for i := 0; i < len(res); i++ {
res[i], ok = proto.Clone(vals[i]).(*ethpb.Validator)
if !ok {
log.Debug("Entity is not of type *ethpb.Validator")
}
}
return res
}
func cloneValidatorsManually(vals []*ethpb.Validator) []*ethpb.Validator {
res := make([]*ethpb.Validator, len(vals))
for i := 0; i < len(res); i++ {
val := vals[i]
res[i] = &ethpb.Validator{
PublicKey: val.PublicKey,
WithdrawalCredentials: val.WithdrawalCredentials,
EffectiveBalance: val.EffectiveBalance,
Slashed: val.Slashed,
ActivationEligibilityEpoch: val.ActivationEligibilityEpoch,
ActivationEpoch: val.ActivationEpoch,
ExitEpoch: val.ExitEpoch,
WithdrawableEpoch: val.WithdrawableEpoch,
}
}
return res
}
func TestBeaconState_ImmutabilityWithSharedResources(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
genesis := setupGenesisState(t, 64)
a, err := v1.InitializeFromProto(genesis)
require.NoError(t, err)
b := a.Copy()
// Randao mixes
require.DeepEqual(t, a.RandaoMixes(), b.RandaoMixes(), "Test precondition failed, fields are not equal")
require.NoError(t, a.UpdateRandaoMixesAtIndex(1, []byte("foo")))
if reflect.DeepEqual(a.RandaoMixes(), b.RandaoMixes()) {
t.Error("Expect a.RandaoMixes() to be different from b.RandaoMixes()")
}
// Validators
require.DeepEqual(t, a.Validators(), b.Validators(), "Test precondition failed, fields are not equal")
require.NoError(t, a.UpdateValidatorAtIndex(1, &ethpb.Validator{Slashed: true}))
if reflect.DeepEqual(a.Validators(), b.Validators()) {
t.Error("Expect a.Validators() to be different from b.Validators()")
}
// State Roots
require.DeepEqual(t, a.StateRoots(), b.StateRoots(), "Test precondition failed, fields are not equal")
require.NoError(t, a.UpdateStateRootAtIndex(1, bytesutil.ToBytes32([]byte("foo"))))
if reflect.DeepEqual(a.StateRoots(), b.StateRoots()) {
t.Fatal("Expected a.StateRoots() to be different from b.StateRoots()")
}
// Block Roots
require.DeepEqual(t, a.BlockRoots(), b.BlockRoots(), "Test precondition failed, fields are not equal")
require.NoError(t, a.UpdateBlockRootAtIndex(1, bytesutil.ToBytes32([]byte("foo"))))
if reflect.DeepEqual(a.BlockRoots(), b.BlockRoots()) {
t.Fatal("Expected a.BlockRoots() to be different from b.BlockRoots()")
}
}
func TestForkManualCopy_OK(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
genesis := setupGenesisState(t, 64)
a, err := v1.InitializeFromProto(genesis)
require.NoError(t, err)
wantedFork := &ethpb.Fork{
PreviousVersion: []byte{'a', 'b', 'c'},
CurrentVersion: []byte{'d', 'e', 'f'},
Epoch: 0,
}
require.NoError(t, a.SetFork(wantedFork))
pbState, err := v1.ProtobufBeaconState(a.InnerStateUnsafe())
require.NoError(t, err)
require.DeepEqual(t, pbState.Fork, wantedFork)
}

View File

@@ -0,0 +1,36 @@
package v1
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// CurrentEpochParticipation is not supported for phase 0 beacon state.
func (*BeaconState) CurrentEpochParticipation() ([]byte, error) {
return nil, errors.New("CurrentEpochParticipation is not supported for phase 0 beacon state")
}
// PreviousEpochParticipation is not supported for phase 0 beacon state.
func (*BeaconState) PreviousEpochParticipation() ([]byte, error) {
return nil, errors.New("PreviousEpochParticipation is not supported for phase 0 beacon state")
}
// InactivityScores is not supported for phase 0 beacon state.
func (*BeaconState) InactivityScores() ([]uint64, error) {
return nil, errors.New("InactivityScores is not supported for phase 0 beacon state")
}
// CurrentSyncCommittee is not supported for phase 0 beacon state.
func (*BeaconState) CurrentSyncCommittee() (*ethpb.SyncCommittee, error) {
return nil, errors.New("CurrentSyncCommittee is not supported for phase 0 beacon state")
}
// NextSyncCommittee is not supported for phase 0 beacon state.
func (*BeaconState) NextSyncCommittee() (*ethpb.SyncCommittee, error) {
return nil, errors.New("NextSyncCommittee is not supported for phase 0 beacon state")
}
// LatestExecutionPayloadHeader is not supported for phase 0 beacon state.
func (*BeaconState) LatestExecutionPayloadHeader() (*ethpb.ExecutionPayloadHeader, error) {
return nil, errors.New("LatestExecutionPayloadHeader is not supported for phase 0 beacon state")
}

View File

@@ -0,0 +1,51 @@
package v1
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// AppendCurrentParticipationBits is not supported for phase 0 beacon state.
func (*BeaconState) AppendCurrentParticipationBits(_ byte) error {
return errors.New("AppendCurrentParticipationBits is not supported for phase 0 beacon state")
}
// AppendPreviousParticipationBits is not supported for phase 0 beacon state.
func (*BeaconState) AppendPreviousParticipationBits(_ byte) error {
return errors.New("AppendPreviousParticipationBits is not supported for phase 0 beacon state")
}
// AppendInactivityScore is not supported for phase 0 beacon state.
func (*BeaconState) AppendInactivityScore(_ uint64) error {
return errors.New("AppendInactivityScore is not supported for phase 0 beacon state")
}
// SetCurrentSyncCommittee is not supported for phase 0 beacon state.
func (*BeaconState) SetCurrentSyncCommittee(_ *ethpb.SyncCommittee) error {
return errors.New("SetCurrentSyncCommittee is not supported for phase 0 beacon state")
}
// SetNextSyncCommittee is not supported for phase 0 beacon state.
func (*BeaconState) SetNextSyncCommittee(_ *ethpb.SyncCommittee) error {
return errors.New("SetNextSyncCommittee is not supported for phase 0 beacon state")
}
// SetPreviousParticipationBits is not supported for phase 0 beacon state.
func (*BeaconState) SetPreviousParticipationBits(_ []byte) error {
return errors.New("SetPreviousParticipationBits is not supported for phase 0 beacon state")
}
// SetCurrentParticipationBits is not supported for phase 0 beacon state.
func (*BeaconState) SetCurrentParticipationBits(_ []byte) error {
return errors.New("SetCurrentParticipationBits is not supported for phase 0 beacon state")
}
// SetInactivityScores is not supported for phase 0 beacon state.
func (*BeaconState) SetInactivityScores(_ []uint64) error {
return errors.New("SetInactivityScores is not supported for phase 0 beacon state")
}
// SetLatestExecutionPayloadHeader is not supported for phase 0 beacon state.
func (*BeaconState) SetLatestExecutionPayloadHeader(val *ethpb.ExecutionPayloadHeader) error {
return errors.New("SetLatestExecutionPayloadHeader is not supported for phase 0 beacon state")
}

View File

@@ -0,0 +1,91 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"deprecated_getters.go",
"deprecated_setters.go",
"field_roots.go",
"getters_block.go",
"getters_checkpoint.go",
"getters_eth1.go",
"getters_misc.go",
"getters_participation.go",
"getters_randao.go",
"getters_state.go",
"getters_sync_committee.go",
"getters_validator.go",
"proofs.go",
"setters_block.go",
"setters_checkpoint.go",
"setters_eth1.go",
"setters_misc.go",
"setters_participation.go",
"setters_randao.go",
"setters_state.go",
"setters_sync_committee.go",
"setters_validator.go",
"state_trie.go",
"types.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v2",
visibility = [
"//beacon-chain:__subpackages__",
"//proto/migration:__subpackages__",
"//testing/spectest:__subpackages__",
"//testing/util:__pkg__",
],
deps = [
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native/fieldtrie:go_default_library",
"//beacon-chain/state/state-native/v1:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/slice:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"deprecated_getters_test.go",
"deprecated_setters_test.go",
"getters_block_test.go",
"getters_test.go",
"getters_validator_test.go",
"proofs_test.go",
"setters_test.go",
"state_trie_test.go",
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/state/state-native/v1:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/trie:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
],
)

View File

@@ -0,0 +1,21 @@
package v2
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// PreviousEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) PreviousEpochAttestations() ([]*ethpb.PendingAttestation, error) {
return nil, errors.New("PreviousEpochAttestations is not supported for hard fork 1 beacon state")
}
// CurrentEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) CurrentEpochAttestations() ([]*ethpb.PendingAttestation, error) {
return nil, errors.New("CurrentEpochAttestations is not supported for hard fork 1 beacon state")
}
// LatestExecutionPayloadHeader is not supported for hard fork 1 beacon state.
func (*BeaconState) LatestExecutionPayloadHeader() (*ethpb.ExecutionPayloadHeader, error) {
return nil, errors.New("LatestExecutionPayloadHeader is not supported for hard fork 1 beacon state")
}

View File

@@ -0,0 +1,19 @@
package v2
import (
"testing"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_CurrentEpochAttestations(t *testing.T) {
s := &BeaconState{}
_, err := s.CurrentEpochAttestations()
require.ErrorContains(t, "CurrentEpochAttestations is not supported for hard fork 1 beacon state", err)
}
func TestBeaconState_PreviousEpochAttestations(t *testing.T) {
s := &BeaconState{}
_, err := s.PreviousEpochAttestations()
require.ErrorContains(t, "PreviousEpochAttestations is not supported for hard fork 1 beacon state", err)
}

View File

@@ -0,0 +1,36 @@
package v2
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetPreviousEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) SetPreviousEpochAttestations(_ []*ethpb.PendingAttestation) error {
return errors.New("SetPreviousEpochAttestations is not supported for hard fork 1 beacon state")
}
// SetCurrentEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) SetCurrentEpochAttestations(_ []*ethpb.PendingAttestation) error {
return errors.New("SetCurrentEpochAttestations is not supported for hard fork 1 beacon state")
}
// AppendCurrentEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) AppendCurrentEpochAttestations(_ *ethpb.PendingAttestation) error {
return errors.New("AppendCurrentEpochAttestations is not supported for hard fork 1 beacon state")
}
// AppendPreviousEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) AppendPreviousEpochAttestations(_ *ethpb.PendingAttestation) error {
return errors.New("AppendPreviousEpochAttestations is not supported for hard fork 1 beacon state")
}
// RotateAttestations is not supported for HF1 beacon state.
func (*BeaconState) RotateAttestations() error {
return errors.New("RotateAttestations is not supported for hard fork 1 beacon state")
}
// SetLatestExecutionPayloadHeader is not supported for hard fork 1 beacon state.
func (*BeaconState) SetLatestExecutionPayloadHeader(_ *ethpb.ExecutionPayloadHeader) error {
return errors.New("SetLatestExecutionPayloadHeader is not supported for hard fork 1 beacon state")
}

View File

@@ -0,0 +1,27 @@
package v2
import (
"testing"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_AppendCurrentEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "AppendCurrentEpochAttestations is not supported for hard fork 1 beacon state", s.AppendCurrentEpochAttestations(nil))
}
func TestBeaconState_AppendPreviousEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "AppendPreviousEpochAttestations is not supported for hard fork 1 beacon state", s.AppendPreviousEpochAttestations(nil))
}
func TestBeaconState_SetCurrentEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "SetCurrentEpochAttestations is not supported for hard fork 1 beacon state", s.SetCurrentEpochAttestations(nil))
}
func TestBeaconState_SetPreviousEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "SetPreviousEpochAttestations is not supported for hard fork 1 beacon state", s.SetPreviousEpochAttestations(nil))
}

View File

@@ -0,0 +1,18 @@
package v2
import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/config/features"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// computeFieldRoots returns the hash tree root computations of every field in
// the beacon state as a list of 32 byte roots.
func computeFieldRoots(ctx context.Context, state *ethpb.BeaconStateAltair) ([][]byte, error) {
if features.Get().EnableSSZCache {
return stateutil.CachedHasher.ComputeFieldRootsWithHasherAltair(ctx, state)
}
return stateutil.NocachedHasher.ComputeFieldRootsWithHasherAltair(ctx, state)
}

View File

@@ -0,0 +1,99 @@
package v2
import (
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// LatestBlockHeader stored within the beacon state.
func (b *BeaconState) LatestBlockHeader() *ethpb.BeaconBlockHeader {
if !b.hasInnerState() {
return nil
}
if b.state.LatestBlockHeader == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.latestBlockHeader()
}
// latestBlockHeader stored within the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) latestBlockHeader() *ethpb.BeaconBlockHeader {
if !b.hasInnerState() {
return nil
}
if b.state.LatestBlockHeader == nil {
return nil
}
hdr := &ethpb.BeaconBlockHeader{
Slot: b.state.LatestBlockHeader.Slot,
ProposerIndex: b.state.LatestBlockHeader.ProposerIndex,
}
parentRoot := make([]byte, len(b.state.LatestBlockHeader.ParentRoot))
bodyRoot := make([]byte, len(b.state.LatestBlockHeader.BodyRoot))
stateRoot := make([]byte, len(b.state.LatestBlockHeader.StateRoot))
copy(parentRoot, b.state.LatestBlockHeader.ParentRoot)
copy(bodyRoot, b.state.LatestBlockHeader.BodyRoot)
copy(stateRoot, b.state.LatestBlockHeader.StateRoot)
hdr.ParentRoot = parentRoot
hdr.BodyRoot = bodyRoot
hdr.StateRoot = stateRoot
return hdr
}
// BlockRoots kept track of in the beacon state.
func (b *BeaconState) BlockRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.BlockRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.blockRoots()
}
// blockRoots kept track of in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) blockRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.BlockRoots)
}
// BlockRootAtIndex retrieves a specific block root based on an
// input index value.
func (b *BeaconState) BlockRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.BlockRoots == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.blockRootAtIndex(idx)
}
// blockRootAtIndex retrieves a specific block root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) blockRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
return bytesutil.SafeCopyRootAtIndex(b.state.BlockRoots, idx)
}

View File

@@ -0,0 +1,60 @@
package v2
import (
"testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_LatestBlockHeader(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
got := s.LatestBlockHeader()
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
want := &ethpb.BeaconBlockHeader{Slot: 100}
s, err = InitializeFromProto(&ethpb.BeaconStateAltair{LatestBlockHeader: want})
require.NoError(t, err)
got = s.LatestBlockHeader()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got.Slot = 101
require.DeepNotEqual(t, want, got)
}
func TestBeaconState_BlockRoots(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
got := s.BlockRoots()
require.DeepEqual(t, ([][]byte)(nil), got)
want := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: want})
require.NoError(t, err)
got = s.BlockRoots()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0][0] = 'b'
require.DeepNotEqual(t, want, got)
}
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
got, err := s.BlockRootAtIndex(0)
require.NoError(t, err)
require.DeepEqual(t, ([]byte)(nil), got)
r := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: r})
require.NoError(t, err)
got, err = s.BlockRootAtIndex(0)
require.NoError(t, err)
want := bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
require.DeepSSZEqual(t, want, got)
}

View File

@@ -0,0 +1,160 @@
package v2
import (
"bytes"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/go-bitfield"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// JustificationBits marking which epochs have been justified in the beacon chain.
func (b *BeaconState) JustificationBits() bitfield.Bitvector4 {
if !b.hasInnerState() {
return nil
}
if b.state.JustificationBits == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.justificationBits()
}
// justificationBits marking which epochs have been justified in the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) justificationBits() bitfield.Bitvector4 {
if !b.hasInnerState() {
return nil
}
if b.state.JustificationBits == nil {
return nil
}
res := make([]byte, len(b.state.JustificationBits.Bytes()))
copy(res, b.state.JustificationBits.Bytes())
return res
}
// PreviousJustifiedCheckpoint denoting an epoch and block root.
func (b *BeaconState) PreviousJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
if b.state.PreviousJustifiedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.previousJustifiedCheckpoint()
}
// previousJustifiedCheckpoint denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) previousJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyCheckpoint(b.state.PreviousJustifiedCheckpoint)
}
// CurrentJustifiedCheckpoint denoting an epoch and block root.
func (b *BeaconState) CurrentJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
if b.state.CurrentJustifiedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.currentJustifiedCheckpoint()
}
// currentJustifiedCheckpoint denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) currentJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyCheckpoint(b.state.CurrentJustifiedCheckpoint)
}
// MatchCurrentJustifiedCheckpoint returns true if input justified checkpoint matches
// the current justified checkpoint in state.
func (b *BeaconState) MatchCurrentJustifiedCheckpoint(c *ethpb.Checkpoint) bool {
if !b.hasInnerState() {
return false
}
if b.state.CurrentJustifiedCheckpoint == nil {
return false
}
if c.Epoch != b.state.CurrentJustifiedCheckpoint.Epoch {
return false
}
return bytes.Equal(c.Root, b.state.CurrentJustifiedCheckpoint.Root)
}
// MatchPreviousJustifiedCheckpoint returns true if the input justified checkpoint matches
// the previous justified checkpoint in state.
func (b *BeaconState) MatchPreviousJustifiedCheckpoint(c *ethpb.Checkpoint) bool {
if !b.hasInnerState() {
return false
}
if b.state.PreviousJustifiedCheckpoint == nil {
return false
}
if c.Epoch != b.state.PreviousJustifiedCheckpoint.Epoch {
return false
}
return bytes.Equal(c.Root, b.state.PreviousJustifiedCheckpoint.Root)
}
// FinalizedCheckpoint denoting an epoch and block root.
func (b *BeaconState) FinalizedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
if b.state.FinalizedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.finalizedCheckpoint()
}
// finalizedCheckpoint denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) finalizedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyCheckpoint(b.state.FinalizedCheckpoint)
}
// FinalizedCheckpointEpoch returns the epoch value of the finalized checkpoint.
func (b *BeaconState) FinalizedCheckpointEpoch() types.Epoch {
if !b.hasInnerState() {
return 0
}
if b.state.FinalizedCheckpoint == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.state.FinalizedCheckpoint.Epoch
}

View File

@@ -0,0 +1,91 @@
package v2
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// Eth1Data corresponding to the proof-of-work chain information stored in the beacon state.
func (b *BeaconState) Eth1Data() *ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1Data == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1Data()
}
// eth1Data corresponding to the proof-of-work chain information stored in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) eth1Data() *ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1Data == nil {
return nil
}
return ethpb.CopyETH1Data(b.state.Eth1Data)
}
// Eth1DataVotes corresponds to votes from Ethereum on the canonical proof-of-work chain
// data retrieved from eth1.
func (b *BeaconState) Eth1DataVotes() []*ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1DataVotes == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1DataVotes()
}
// eth1DataVotes corresponds to votes from Ethereum on the canonical proof-of-work chain
// data retrieved from eth1.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) eth1DataVotes() []*ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1DataVotes == nil {
return nil
}
res := make([]*ethpb.Eth1Data, len(b.state.Eth1DataVotes))
for i := 0; i < len(res); i++ {
res[i] = ethpb.CopyETH1Data(b.state.Eth1DataVotes[i])
}
return res
}
// Eth1DepositIndex corresponds to the index of the deposit made to the
// validator deposit contract at the time of this state's eth1 data.
func (b *BeaconState) Eth1DepositIndex() uint64 {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1DepositIndex()
}
// eth1DepositIndex corresponds to the index of the deposit made to the
// validator deposit contract at the time of this state's eth1 data.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) eth1DepositIndex() uint64 {
if !b.hasInnerState() {
return 0
}
return b.state.Eth1DepositIndex
}

View File

@@ -0,0 +1,212 @@
package v2
import (
"time"
types "github.com/prysmaticlabs/eth2-types"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/runtime/version"
)
// GenesisTime of the beacon state as a uint64.
func (b *BeaconState) GenesisTime() uint64 {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.genesisTime()
}
// genesisTime of the beacon state as a uint64.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) genesisTime() uint64 {
if !b.hasInnerState() {
return 0
}
return b.state.GenesisTime
}
// GenesisValidatorRoot of the beacon state.
func (b *BeaconState) GenesisValidatorRoot() []byte {
if !b.hasInnerState() {
return nil
}
if b.state.GenesisValidatorsRoot == nil {
return params.BeaconConfig().ZeroHash[:]
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.genesisValidatorRoot()
}
// genesisValidatorRoot of the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) genesisValidatorRoot() []byte {
if !b.hasInnerState() {
return nil
}
if b.state.GenesisValidatorsRoot == nil {
return params.BeaconConfig().ZeroHash[:]
}
root := make([]byte, fieldparams.RootLength)
copy(root, b.state.GenesisValidatorsRoot)
return root
}
// GenesisUnixTime returns the genesis time as time.Time.
func (b *BeaconState) GenesisUnixTime() time.Time {
if !b.hasInnerState() {
return time.Unix(0, 0)
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.genesisUnixTime()
}
// genesisUnixTime returns the genesis time as time.Time.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) genesisUnixTime() time.Time {
if !b.hasInnerState() {
return time.Unix(0, 0)
}
return time.Unix(int64(b.state.GenesisTime), 0)
}
// ParentRoot is a convenience method to access state.LatestBlockRoot.ParentRoot.
func (b *BeaconState) ParentRoot() [32]byte {
if !b.hasInnerState() {
return [32]byte{}
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.parentRoot()
}
// parentRoot is a convenience method to access state.LatestBlockRoot.ParentRoot.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) parentRoot() [32]byte {
if !b.hasInnerState() {
return [32]byte{}
}
parentRoot := [32]byte{}
copy(parentRoot[:], b.state.LatestBlockHeader.ParentRoot)
return parentRoot
}
// Version of the beacon state. This method
// is strictly meant to be used without a lock
// internally.
func (_ *BeaconState) Version() int {
return version.Altair
}
// Slot of the current beacon chain state.
func (b *BeaconState) Slot() types.Slot {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.slot()
}
// slot of the current beacon chain state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) slot() types.Slot {
if !b.hasInnerState() {
return 0
}
return b.state.Slot
}
// Fork version of the beacon chain.
func (b *BeaconState) Fork() *ethpb.Fork {
if !b.hasInnerState() {
return nil
}
if b.state.Fork == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.fork()
}
// fork version of the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) fork() *ethpb.Fork {
if !b.hasInnerState() {
return nil
}
if b.state.Fork == nil {
return nil
}
prevVersion := make([]byte, len(b.state.Fork.PreviousVersion))
copy(prevVersion, b.state.Fork.PreviousVersion)
currVersion := make([]byte, len(b.state.Fork.CurrentVersion))
copy(currVersion, b.state.Fork.CurrentVersion)
return &ethpb.Fork{
PreviousVersion: prevVersion,
CurrentVersion: currVersion,
Epoch: b.state.Fork.Epoch,
}
}
// HistoricalRoots based on epochs stored in the beacon state.
func (b *BeaconState) HistoricalRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.HistoricalRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.historicalRoots()
}
// historicalRoots based on epochs stored in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) historicalRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.HistoricalRoots)
}
// balancesLength returns the length of the balances slice.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) balancesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.Balances == nil {
return 0
}
return len(b.state.Balances)
}

View File

@@ -0,0 +1,53 @@
package v2
// CurrentEpochParticipation corresponding to participation bits on the beacon chain.
func (b *BeaconState) CurrentEpochParticipation() ([]byte, error) {
if !b.hasInnerState() {
return nil, nil
}
if b.state.CurrentEpochParticipation == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.currentEpochParticipation(), nil
}
// PreviousEpochParticipation corresponding to participation bits on the beacon chain.
func (b *BeaconState) PreviousEpochParticipation() ([]byte, error) {
if !b.hasInnerState() {
return nil, nil
}
if b.state.PreviousEpochParticipation == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.previousEpochParticipation(), nil
}
// currentEpochParticipation corresponding to participation bits on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) currentEpochParticipation() []byte {
if !b.hasInnerState() {
return nil
}
tmp := make([]byte, len(b.state.CurrentEpochParticipation))
copy(tmp, b.state.CurrentEpochParticipation)
return tmp
}
// previousEpochParticipation corresponding to participation bits on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) previousEpochParticipation() []byte {
if !b.hasInnerState() {
return nil
}
tmp := make([]byte, len(b.state.PreviousEpochParticipation))
copy(tmp, b.state.PreviousEpochParticipation)
return tmp
}

View File

@@ -0,0 +1,85 @@
package v2
import (
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
)
// RandaoMixes of block proposers on the beacon chain.
func (b *BeaconState) RandaoMixes() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.RandaoMixes == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.randaoMixes()
}
// randaoMixes of block proposers on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) randaoMixes() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.RandaoMixes)
}
// RandaoMixAtIndex retrieves a specific block root based on an
// input index value.
func (b *BeaconState) RandaoMixAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.RandaoMixes == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.randaoMixAtIndex(idx)
}
// randaoMixAtIndex retrieves a specific block root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) randaoMixAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
return bytesutil.SafeCopyRootAtIndex(b.state.RandaoMixes, idx)
}
// RandaoMixesLength returns the length of the randao mixes slice.
func (b *BeaconState) RandaoMixesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.RandaoMixes == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.randaoMixesLength()
}
// randaoMixesLength returns the length of the randao mixes slice.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) randaoMixesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.RandaoMixes == nil {
return 0
}
return len(b.state.RandaoMixes)
}

View File

@@ -0,0 +1,126 @@
package v2
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// InnerStateUnsafe returns the pointer value of the underlying
// beacon state proto object, bypassing immutability. Use with care.
func (b *BeaconState) InnerStateUnsafe() interface{} {
if b == nil {
return nil
}
return b.state
}
// CloneInnerState the beacon state into a protobuf for usage.
func (b *BeaconState) CloneInnerState() interface{} {
if b == nil || b.state == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return &ethpb.BeaconStateAltair{
GenesisTime: b.genesisTime(),
GenesisValidatorsRoot: b.genesisValidatorRoot(),
Slot: b.slot(),
Fork: b.fork(),
LatestBlockHeader: b.latestBlockHeader(),
BlockRoots: b.blockRoots(),
StateRoots: b.stateRoots(),
HistoricalRoots: b.historicalRoots(),
Eth1Data: b.eth1Data(),
Eth1DataVotes: b.eth1DataVotes(),
Eth1DepositIndex: b.eth1DepositIndex(),
Validators: b.validators(),
Balances: b.balances(),
RandaoMixes: b.randaoMixes(),
Slashings: b.slashings(),
CurrentEpochParticipation: b.currentEpochParticipation(),
PreviousEpochParticipation: b.previousEpochParticipation(),
JustificationBits: b.justificationBits(),
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint(),
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint(),
FinalizedCheckpoint: b.finalizedCheckpoint(),
InactivityScores: b.inactivityScores(),
CurrentSyncCommittee: b.currentSyncCommittee(),
NextSyncCommittee: b.nextSyncCommittee(),
}
}
// hasInnerState detects if the internal reference to the state data structure
// is populated correctly. Returns false if nil.
func (b *BeaconState) hasInnerState() bool {
return b != nil && b.state != nil
}
// StateRoots kept track of in the beacon state.
func (b *BeaconState) StateRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.StateRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.stateRoots()
}
// StateRoots kept track of in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) stateRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.StateRoots)
}
// StateRootAtIndex retrieves a specific state root based on an
// input index value.
func (b *BeaconState) StateRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.StateRoots == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.stateRootAtIndex(idx)
}
// stateRootAtIndex retrieves a specific state root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) stateRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
return bytesutil.SafeCopyRootAtIndex(b.state.StateRoots, idx)
}
// MarshalSSZ marshals the underlying beacon state to bytes.
func (b *BeaconState) MarshalSSZ() ([]byte, error) {
if !b.hasInnerState() {
return nil, errors.New("nil beacon state")
}
return b.state.MarshalSSZ()
}
// ProtobufBeaconState transforms an input into beacon state hard fork 1 in the form of protobuf.
// Error is returned if the input is not type protobuf beacon state.
func ProtobufBeaconState(s interface{}) (*ethpb.BeaconStateAltair, error) {
pbState, ok := s.(*ethpb.BeaconStateAltair)
if !ok {
return nil, errors.New("input is not type pb.BeaconStateAltair")
}
return pbState, nil
}

View File

@@ -0,0 +1,69 @@
package v2
import (
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// currentSyncCommittee of the current sync committee in beacon chain state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) currentSyncCommittee() *ethpb.SyncCommittee {
if !b.hasInnerState() {
return nil
}
return CopySyncCommittee(b.state.CurrentSyncCommittee)
}
// nextSyncCommittee of the next sync committee in beacon chain state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) nextSyncCommittee() *ethpb.SyncCommittee {
if !b.hasInnerState() {
return nil
}
return CopySyncCommittee(b.state.NextSyncCommittee)
}
// CurrentSyncCommittee of the current sync committee in beacon chain state.
func (b *BeaconState) CurrentSyncCommittee() (*ethpb.SyncCommittee, error) {
if !b.hasInnerState() {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
if b.state.CurrentSyncCommittee == nil {
return nil, nil
}
return b.currentSyncCommittee(), nil
}
// NextSyncCommittee of the next sync committee in beacon chain state.
func (b *BeaconState) NextSyncCommittee() (*ethpb.SyncCommittee, error) {
if !b.hasInnerState() {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
if b.state.NextSyncCommittee == nil {
return nil, nil
}
return b.nextSyncCommittee(), nil
}
// CopySyncCommittee copies the provided sync committee object.
func CopySyncCommittee(data *ethpb.SyncCommittee) *ethpb.SyncCommittee {
if data == nil {
return nil
}
return &ethpb.SyncCommittee{
Pubkeys: bytesutil.SafeCopy2dBytes(data.Pubkeys),
AggregatePubkey: bytesutil.SafeCopyBytes(data.AggregatePubkey),
}
}

View File

@@ -0,0 +1,193 @@
package v2
import (
"runtime/debug"
"sync"
"testing"
types "github.com/prysmaticlabs/eth2-types"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_SlotDataRace(t *testing.T) {
headState, err := InitializeFromProto(&ethpb.BeaconStateAltair{Slot: 1})
require.NoError(t, err)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
require.NoError(t, headState.SetSlot(0))
wg.Done()
}()
go func() {
headState.Slot()
wg.Done()
}()
wg.Wait()
}
func TestNilState_NoPanic(t *testing.T) {
var st *BeaconState
defer func() {
if r := recover(); r != nil {
t.Errorf("Method panicked when it was not supposed to: %v\n%v\n", r, string(debug.Stack()))
}
}()
// retrieve elements from nil state
_ = st.GenesisTime()
_ = st.GenesisValidatorRoot()
_ = st.GenesisUnixTime()
_ = st.GenesisValidatorRoot()
_ = st.Slot()
_ = st.Fork()
_ = st.LatestBlockHeader()
_ = st.ParentRoot()
_ = st.BlockRoots()
_, err := st.BlockRootAtIndex(0)
_ = err
_ = st.StateRoots()
_ = st.HistoricalRoots()
_ = st.Eth1Data()
_ = st.Eth1DataVotes()
_ = st.Eth1DepositIndex()
_, err = st.ValidatorAtIndex(0)
_ = err
_, err = st.ValidatorAtIndexReadOnly(0)
_ = err
_, _ = st.ValidatorIndexByPubkey([fieldparams.BLSPubkeyLength]byte{})
_ = st.PubkeyAtIndex(0)
_ = st.NumValidators()
_ = st.Balances()
_, err = st.BalanceAtIndex(0)
_ = err
_ = st.BalancesLength()
_ = st.RandaoMixes()
_, err = st.RandaoMixAtIndex(0)
_ = err
_ = st.RandaoMixesLength()
_ = st.Slashings()
_, err = st.CurrentEpochParticipation()
_ = err
_, err = st.PreviousEpochParticipation()
_ = err
_ = st.JustificationBits()
_ = st.PreviousJustifiedCheckpoint()
_ = st.CurrentJustifiedCheckpoint()
_ = st.FinalizedCheckpoint()
_, err = st.CurrentEpochParticipation()
_ = err
_, err = st.PreviousEpochParticipation()
_ = err
_, err = st.InactivityScores()
_ = err
_, err = st.CurrentSyncCommittee()
_ = err
_, err = st.NextSyncCommittee()
_ = err
}
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
keyCreator := func(input []byte) [fieldparams.BLSPubkeyLength]byte {
nKey := [fieldparams.BLSPubkeyLength]byte{}
copy(nKey[:1], input)
return nKey
}
tests := []struct {
name string
modifyFunc func(b *BeaconState, k [fieldparams.BLSPubkeyLength]byte)
exists bool
expectedIdx types.ValidatorIndex
largestIdxInSet types.ValidatorIndex
}{
{
name: "retrieve validator",
modifyFunc: func(b *BeaconState, key [48]byte) {
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: true,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators from the start",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
},
exists: true,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: true,
expectedIdx: 2,
},
{
name: "retrieve validator with multiple validators from the start with shared state",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
_ = b.Copy()
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
},
exists: true,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators with shared state",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
n := b.Copy()
// Append to another state
assert.NoError(t, n.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: false,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators with shared state at boundary",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
n := b.Copy()
// Append to another state
assert.NoError(t, n.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: false,
expectedIdx: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
nKey := keyCreator([]byte{'A'})
tt.modifyFunc(s, nKey)
idx, ok := s.ValidatorIndexByPubkey(nKey)
assert.Equal(t, tt.exists, ok)
assert.Equal(t, tt.expectedIdx, idx)
})
}
}

View File

@@ -0,0 +1,329 @@
package v2
import (
"fmt"
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// ValidatorIndexOutOfRangeError represents an error scenario where a validator does not exist
// at a given index in the validator's array.
type ValidatorIndexOutOfRangeError struct {
message string
}
var (
// ErrNilValidatorsInState returns when accessing validators in the state while the state has a
// nil slice for the validators field.
ErrNilValidatorsInState = errors.New("state has nil validator slice")
)
// NewValidatorIndexOutOfRangeError creates a new error instance.
func NewValidatorIndexOutOfRangeError(index types.ValidatorIndex) ValidatorIndexOutOfRangeError {
return ValidatorIndexOutOfRangeError{
message: fmt.Sprintf("index %d out of range", index),
}
}
// Error returns the underlying error message.
func (e *ValidatorIndexOutOfRangeError) Error() string {
return e.message
}
// Validators participating in consensus on the beacon chain.
func (b *BeaconState) Validators() []*ethpb.Validator {
if !b.hasInnerState() {
return nil
}
if b.state.Validators == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.validators()
}
// validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) validators() []*ethpb.Validator {
if !b.hasInnerState() {
return nil
}
if b.state.Validators == nil {
return nil
}
res := make([]*ethpb.Validator, len(b.state.Validators))
for i := 0; i < len(res); i++ {
val := b.state.Validators[i]
if val == nil {
continue
}
res[i] = ethpb.CopyValidator(val)
}
return res
}
// references of validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState. This does not
// copy fully and instead just copies the reference.
func (b *BeaconState) validatorsReferences() []*ethpb.Validator {
if !b.hasInnerState() {
return nil
}
if b.state.Validators == nil {
return nil
}
res := make([]*ethpb.Validator, len(b.state.Validators))
for i := 0; i < len(res); i++ {
validator := b.state.Validators[i]
if validator == nil {
continue
}
// copy validator reference instead.
res[i] = validator
}
return res
}
// ValidatorAtIndex is the validator at the provided index.
func (b *BeaconState) ValidatorAtIndex(idx types.ValidatorIndex) (*ethpb.Validator, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.Validators == nil {
return &ethpb.Validator{}, nil
}
if uint64(len(b.state.Validators)) <= uint64(idx) {
e := NewValidatorIndexOutOfRangeError(idx)
return nil, &e
}
b.lock.RLock()
defer b.lock.RUnlock()
val := b.state.Validators[idx]
return ethpb.CopyValidator(val), nil
}
// ValidatorAtIndexReadOnly is the validator at the provided index. This method
// doesn't clone the validator.
func (b *BeaconState) ValidatorAtIndexReadOnly(idx types.ValidatorIndex) (state.ReadOnlyValidator, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.Validators == nil {
return nil, ErrNilValidatorsInState
}
if uint64(len(b.state.Validators)) <= uint64(idx) {
e := NewValidatorIndexOutOfRangeError(idx)
return nil, &e
}
b.lock.RLock()
defer b.lock.RUnlock()
return v1.NewValidator(b.state.Validators[idx])
}
// ValidatorIndexByPubkey returns a given validator by its 48-byte public key.
func (b *BeaconState) ValidatorIndexByPubkey(key [fieldparams.BLSPubkeyLength]byte) (types.ValidatorIndex, bool) {
if b == nil || b.valMapHandler == nil || b.valMapHandler.IsNil() {
return 0, false
}
b.lock.RLock()
defer b.lock.RUnlock()
numOfVals := len(b.state.Validators)
idx, ok := b.valMapHandler.Get(key)
if ok && numOfVals <= int(idx) {
return types.ValidatorIndex(0), false
}
return idx, ok
}
// PubkeyAtIndex returns the pubkey at the given
// validator index.
func (b *BeaconState) PubkeyAtIndex(idx types.ValidatorIndex) [fieldparams.BLSPubkeyLength]byte {
if !b.hasInnerState() {
return [fieldparams.BLSPubkeyLength]byte{}
}
if uint64(idx) >= uint64(len(b.state.Validators)) {
return [fieldparams.BLSPubkeyLength]byte{}
}
b.lock.RLock()
defer b.lock.RUnlock()
if b.state.Validators[idx] == nil {
return [fieldparams.BLSPubkeyLength]byte{}
}
return bytesutil.ToBytes48(b.state.Validators[idx].PublicKey)
}
// NumValidators returns the size of the validator registry.
func (b *BeaconState) NumValidators() int {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return len(b.state.Validators)
}
// ReadFromEveryValidator reads values from every validator and applies it to the provided function.
// Warning: This method is potentially unsafe, as it exposes the actual validator registry.
func (b *BeaconState) ReadFromEveryValidator(f func(idx int, val state.ReadOnlyValidator) error) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if b.state.Validators == nil {
return errors.New("nil validators in state")
}
b.lock.RLock()
validators := b.state.Validators
b.lock.RUnlock()
for i, v := range validators {
v, err := v1.NewValidator(v)
if err != nil {
return err
}
if err := f(i, v); err != nil {
return err
}
}
return nil
}
// Balances of validators participating in consensus on the beacon chain.
func (b *BeaconState) Balances() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Balances == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.balances()
}
// balances of validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) balances() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Balances == nil {
return nil
}
res := make([]uint64, len(b.state.Balances))
copy(res, b.state.Balances)
return res
}
// BalanceAtIndex of validator with the provided index.
func (b *BeaconState) BalanceAtIndex(idx types.ValidatorIndex) (uint64, error) {
if !b.hasInnerState() {
return 0, ErrNilInnerState
}
if b.state.Balances == nil {
return 0, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
if uint64(len(b.state.Balances)) <= uint64(idx) {
return 0, fmt.Errorf("index of %d does not exist", idx)
}
return b.state.Balances[idx], nil
}
// BalancesLength returns the length of the balances slice.
func (b *BeaconState) BalancesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.Balances == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.balancesLength()
}
// Slashings of validators on the beacon chain.
func (b *BeaconState) Slashings() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Slashings == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.slashings()
}
// slashings of validators on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) slashings() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Slashings == nil {
return nil
}
res := make([]uint64, len(b.state.Slashings))
copy(res, b.state.Slashings)
return res
}
// inactivityScores of validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) inactivityScores() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.InactivityScores == nil {
return nil
}
res := make([]uint64, len(b.state.InactivityScores))
copy(res, b.state.InactivityScores)
return res
}
// InactivityScores of validators participating in consensus on the beacon chain.
func (b *BeaconState) InactivityScores() ([]uint64, error) {
if !b.hasInnerState() {
return nil, nil
}
if b.state.InactivityScores == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.inactivityScores(), nil
}

View File

@@ -0,0 +1,20 @@
package v2_test
import (
"testing"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
st, err := v1.InitializeFromProtoUnsafe(&ethpb.BeaconState{
Validators: nil,
})
require.NoError(t, err)
_, err = st.ValidatorAtIndexReadOnly(0)
assert.Equal(t, v1.ErrNilValidatorsInState, err)
}

View File

@@ -0,0 +1,83 @@
package v2
import (
"context"
"encoding/binary"
"github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/fieldtrie"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
)
const (
finalizedRootIndex = uint64(105) // Precomputed value.
)
// FinalizedRootGeneralizedIndex for the beacon state.
func FinalizedRootGeneralizedIndex() uint64 {
return finalizedRootIndex
}
// CurrentSyncCommitteeGeneralizedIndex for the beacon state.
func CurrentSyncCommitteeGeneralizedIndex() uint64 {
return uint64(currentSyncCommittee)
}
// NextSyncCommitteeGeneralizedIndex for the beacon state.
func NextSyncCommitteeGeneralizedIndex() uint64 {
return uint64(nextSyncCommittee)
}
// CurrentSyncCommitteeProof from the state's Merkle trie representation.
func (b *BeaconState) CurrentSyncCommitteeProof(ctx context.Context) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
// In case the Merkle layers of the trie are not populated, we need
// to perform some initialization.
if err := b.initializeMerkleLayers(ctx); err != nil {
return nil, err
}
// Our beacon state uses a "dirty" fields pattern which requires us to
// recompute branches of the Merkle layers that are marked as dirty.
if err := b.recomputeDirtyFields(ctx); err != nil {
return nil, err
}
return fieldtrie.ProofFromMerkleLayers(b.merkleLayers, currentSyncCommittee), nil
}
// NextSyncCommitteeProof from the state's Merkle trie representation.
func (b *BeaconState) NextSyncCommitteeProof(ctx context.Context) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
if err := b.initializeMerkleLayers(ctx); err != nil {
return nil, err
}
if err := b.recomputeDirtyFields(ctx); err != nil {
return nil, err
}
return fieldtrie.ProofFromMerkleLayers(b.merkleLayers, nextSyncCommittee), nil
}
// FinalizedRootProof crafts a Merkle proof for the finalized root
// contained within the finalized checkpoint of a beacon state.
func (b *BeaconState) FinalizedRootProof(ctx context.Context) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
if err := b.initializeMerkleLayers(ctx); err != nil {
return nil, err
}
if err := b.recomputeDirtyFields(ctx); err != nil {
return nil, err
}
cpt := b.state.FinalizedCheckpoint
// The epoch field of a finalized checkpoint is the neighbor
// index of the finalized root field in its Merkle tree representation
// of the checkpoint. This neighbor is the first element added to the proof.
epochBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(epochBuf, uint64(cpt.Epoch))
epochRoot := bytesutil.ToBytes32(epochBuf)
proof := make([][]byte, 0)
proof = append(proof, epochRoot[:])
branch := fieldtrie.ProofFromMerkleLayers(b.merkleLayers, finalizedCheckpoint)
proof = append(proof, branch...)
return proof, nil
}

View File

@@ -0,0 +1,105 @@
package v2_test
import (
"context"
"testing"
v2 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v2"
"github.com/prysmaticlabs/prysm/container/trie"
"github.com/prysmaticlabs/prysm/crypto/bls"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
)
func TestBeaconStateMerkleProofs(t *testing.T) {
ctx := context.Background()
st, _ := util.DeterministicGenesisStateAltair(t, 256)
htr, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
t.Run("current sync committee", func(t *testing.T) {
sc, err := st.CurrentSyncCommittee()
require.NoError(t, err)
// Verify the Merkle proof.
scRoot, err := sc.HashTreeRoot()
require.NoError(t, err)
proof, err := st.CurrentSyncCommitteeProof(ctx)
require.NoError(t, err)
valid := trie.VerifyMerkleProof(htr[:], scRoot[:], v2.CurrentSyncCommitteeGeneralizedIndex(), proof)
require.Equal(t, true, valid)
})
t.Run("next sync committee", func(t *testing.T) {
nextSC, err := st.NextSyncCommittee()
require.NoError(t, err)
proof, err := st.NextSyncCommitteeProof(ctx)
require.NoError(t, err)
// Verify the Merkle proof.
nextSCRoot, err := nextSC.HashTreeRoot()
require.NoError(t, err)
valid := trie.VerifyMerkleProof(htr[:], nextSCRoot[:], v2.NextSyncCommitteeGeneralizedIndex(), proof)
require.Equal(t, true, valid)
// Edit the sync committee.
privKey, err := bls.RandKey()
require.NoError(t, err)
nextSC.AggregatePubkey = privKey.PublicKey().Marshal()
require.NoError(t, st.SetNextSyncCommittee(nextSC))
// Verifying the old Merkle proof for the new value should fail.
nextSCRoot, err = nextSC.HashTreeRoot()
require.NoError(t, err)
valid = trie.VerifyMerkleProof(htr[:], nextSCRoot[:], v2.NextSyncCommitteeGeneralizedIndex(), proof)
require.Equal(t, false, valid)
// Generating a new, valid proof should pass.
proof, err = st.NextSyncCommitteeProof(ctx)
require.NoError(t, err)
htr, err = st.HashTreeRoot(ctx)
require.NoError(t, err)
valid = trie.VerifyMerkleProof(htr[:], nextSCRoot[:], v2.NextSyncCommitteeGeneralizedIndex(), proof)
require.Equal(t, true, valid)
})
t.Run("finalized root", func(t *testing.T) {
finalizedRoot := st.FinalizedCheckpoint().Root
// Verify the Merkle proof.
htr, err = st.HashTreeRoot(ctx)
require.NoError(t, err)
proof, err := st.FinalizedRootProof(ctx)
require.NoError(t, err)
gIndex := v2.FinalizedRootGeneralizedIndex()
valid := trie.VerifyMerkleProof(htr[:], finalizedRoot, gIndex, proof)
require.Equal(t, true, valid)
})
t.Run("recomputes root on dirty fields", func(t *testing.T) {
currentRoot, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
cpt := st.FinalizedCheckpoint()
require.NoError(t, err)
// Edit the checkpoint.
cpt.Epoch = 100
require.NoError(t, st.SetFinalizedCheckpoint(cpt))
// Produce a proof for the finalized root.
proof, err := st.FinalizedRootProof(ctx)
require.NoError(t, err)
// We expect the previous step to have triggered
// a recomputation of dirty fields in the beacon state, resulting
// in a new hash tree root as the finalized checkpoint had previously
// changed and should have been marked as a dirty state field.
// The proof validity should be false for the old root, but true for the new.
finalizedRoot := st.FinalizedCheckpoint().Root
gIndex := v2.FinalizedRootGeneralizedIndex()
valid := trie.VerifyMerkleProof(currentRoot[:], finalizedRoot, gIndex, proof)
require.Equal(t, false, valid)
newRoot, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
valid = trie.VerifyMerkleProof(newRoot[:], finalizedRoot, gIndex, proof)
require.Equal(t, true, valid)
})
}

View File

@@ -0,0 +1,68 @@
package v2
import (
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetLatestBlockHeader in the beacon state.
func (b *BeaconState) SetLatestBlockHeader(val *ethpb.BeaconBlockHeader) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.LatestBlockHeader = ethpb.CopyBeaconBlockHeader(val)
b.markFieldAsDirty(latestBlockHeader)
return nil
}
// SetBlockRoots for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetBlockRoots(val [][]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[blockRoots].MinusRef()
b.sharedFieldReferences[blockRoots] = stateutil.NewRef(1)
b.state.BlockRoots = val
b.markFieldAsDirty(blockRoots)
b.rebuildTrie[blockRoots] = true
return nil
}
// UpdateBlockRootAtIndex for the beacon state. Updates the block root
// at a specific index to a new value.
func (b *BeaconState) UpdateBlockRootAtIndex(idx uint64, blockRoot [32]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if uint64(len(b.state.BlockRoots)) <= idx {
return fmt.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
r := b.state.BlockRoots
if ref := b.sharedFieldReferences[blockRoots]; ref.Refs() > 1 {
// Copy elements in underlying array by reference.
r = make([][]byte, len(b.state.BlockRoots))
copy(r, b.state.BlockRoots)
ref.MinusRef()
b.sharedFieldReferences[blockRoots] = stateutil.NewRef(1)
}
r[idx] = blockRoot[:]
b.state.BlockRoots = r
b.markFieldAsDirty(blockRoots)
b.addDirtyIndices(blockRoots, []uint64{idx})
return nil
}

View File

@@ -0,0 +1,58 @@
package v2
import (
"github.com/prysmaticlabs/go-bitfield"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetJustificationBits for the beacon state.
func (b *BeaconState) SetJustificationBits(val bitfield.Bitvector4) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.JustificationBits = val
b.markFieldAsDirty(justificationBits)
return nil
}
// SetPreviousJustifiedCheckpoint for the beacon state.
func (b *BeaconState) SetPreviousJustifiedCheckpoint(val *ethpb.Checkpoint) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.PreviousJustifiedCheckpoint = val
b.markFieldAsDirty(previousJustifiedCheckpoint)
return nil
}
// SetCurrentJustifiedCheckpoint for the beacon state.
func (b *BeaconState) SetCurrentJustifiedCheckpoint(val *ethpb.Checkpoint) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.CurrentJustifiedCheckpoint = val
b.markFieldAsDirty(currentJustifiedCheckpoint)
return nil
}
// SetFinalizedCheckpoint for the beacon state.
func (b *BeaconState) SetFinalizedCheckpoint(val *ethpb.Checkpoint) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.FinalizedCheckpoint = val
b.markFieldAsDirty(finalizedCheckpoint)
return nil
}

View File

@@ -0,0 +1,74 @@
package v2
import (
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetEth1Data for the beacon state.
func (b *BeaconState) SetEth1Data(val *ethpb.Eth1Data) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.Eth1Data = val
b.markFieldAsDirty(eth1Data)
return nil
}
// SetEth1DataVotes for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetEth1DataVotes(val []*ethpb.Eth1Data) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[eth1DataVotes].MinusRef()
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
b.state.Eth1DataVotes = val
b.markFieldAsDirty(eth1DataVotes)
b.rebuildTrie[eth1DataVotes] = true
return nil
}
// SetEth1DepositIndex for the beacon state.
func (b *BeaconState) SetEth1DepositIndex(val uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.Eth1DepositIndex = val
b.markFieldAsDirty(eth1DepositIndex)
return nil
}
// AppendEth1DataVotes for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendEth1DataVotes(val *ethpb.Eth1Data) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
votes := b.state.Eth1DataVotes
if b.sharedFieldReferences[eth1DataVotes].Refs() > 1 {
// Copy elements in underlying array by reference.
votes = make([]*ethpb.Eth1Data, len(b.state.Eth1DataVotes))
copy(votes, b.state.Eth1DataVotes)
b.sharedFieldReferences[eth1DataVotes].MinusRef()
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
}
b.state.Eth1DataVotes = append(votes, val)
b.markFieldAsDirty(eth1DataVotes)
b.addDirtyIndices(eth1DataVotes, []uint64{uint64(len(b.state.Eth1DataVotes) - 1)})
return nil
}

View File

@@ -0,0 +1,186 @@
package v2
import (
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
stateTypes "github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/features"
"github.com/prysmaticlabs/prysm/crypto/hash"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"google.golang.org/protobuf/proto"
)
// For our setters, we have a field reference counter through
// which we can track shared field references. This helps when
// performing state copies, as we simply copy the reference to the
// field. When we do need to do need to modify these fields, we
// perform a full copy of the field. This is true of most of our
// fields except for the following below.
// 1) BlockRoots
// 2) StateRoots
// 3) Eth1DataVotes
// 4) RandaoMixes
// 5) HistoricalRoots
// 6) CurrentParticipationBits
// 7) PreviousParticipationBits
//
// The fields referred to above are instead copied by reference, where
// we simply copy the reference to the underlying object instead of the
// whole object. This is possible due to how we have structured our state
// as we copy the value on read, so as to ensure the underlying object is
// not mutated while it is being accessed during a state read.
const (
// This specifies the limit till which we process all dirty indices for a certain field.
// If we have more dirty indices than the threshold, then we rebuild the whole trie. This
// comes due to the fact that O(alogn) > O(n) beyond a certain value of a.
indicesLimit = 8000
)
// SetGenesisTime for the beacon state.
func (b *BeaconState) SetGenesisTime(val uint64) error {
b.lock.Lock()
defer b.lock.Unlock()
b.state.GenesisTime = val
b.markFieldAsDirty(genesisTime)
return nil
}
// SetGenesisValidatorRoot for the beacon state.
func (b *BeaconState) SetGenesisValidatorRoot(val []byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.state.GenesisValidatorsRoot = val
b.markFieldAsDirty(genesisValidatorRoot)
return nil
}
// SetSlot for the beacon state.
func (b *BeaconState) SetSlot(val types.Slot) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.Slot = val
b.markFieldAsDirty(slot)
return nil
}
// SetFork version for the beacon chain.
func (b *BeaconState) SetFork(val *ethpb.Fork) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
fk, ok := proto.Clone(val).(*ethpb.Fork)
if !ok {
return errors.New("proto.Clone did not return a fork proto")
}
b.state.Fork = fk
b.markFieldAsDirty(fork)
return nil
}
// SetHistoricalRoots for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetHistoricalRoots(val [][]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[historicalRoots].MinusRef()
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
b.state.HistoricalRoots = val
b.markFieldAsDirty(historicalRoots)
return nil
}
// AppendHistoricalRoots for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendHistoricalRoots(root [32]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
roots := b.state.HistoricalRoots
if b.sharedFieldReferences[historicalRoots].Refs() > 1 {
roots = make([][]byte, len(b.state.HistoricalRoots))
copy(roots, b.state.HistoricalRoots)
b.sharedFieldReferences[historicalRoots].MinusRef()
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
}
b.state.HistoricalRoots = append(roots, root[:])
b.markFieldAsDirty(historicalRoots)
return nil
}
// Recomputes the branch up the index in the Merkle trie representation
// of the beacon state. This method performs slice reads and the caller MUST
// hold the lock before calling this method.
func (b *BeaconState) recomputeRoot(idx int) {
hashFunc := hash.CustomSHA256Hasher()
layers := b.merkleLayers
// The merkle tree structure looks as follows:
// [[r1, r2, r3, r4], [parent1, parent2], [root]]
// Using information about the index which changed, idx, we recompute
// only its branch up the tree.
currentIndex := idx
root := b.merkleLayers[0][idx]
for i := 0; i < len(layers)-1; i++ {
isLeft := currentIndex%2 == 0
neighborIdx := currentIndex ^ 1
neighbor := make([]byte, 32)
if layers[i] != nil && len(layers[i]) != 0 && neighborIdx < len(layers[i]) {
neighbor = layers[i][neighborIdx]
}
if isLeft {
parentHash := hashFunc(append(root, neighbor...))
root = parentHash[:]
} else {
parentHash := hashFunc(append(neighbor, root...))
root = parentHash[:]
}
parentIdx := currentIndex / 2
// Update the cached layers at the parent index.
layers[i+1][parentIdx] = root
currentIndex = parentIdx
}
b.merkleLayers = layers
}
func (b *BeaconState) markFieldAsDirty(field stateTypes.FieldIndex) {
b.dirtyFields[field] = true
}
// addDirtyIndices adds the relevant dirty field indices, so that they
// can be recomputed.
func (b *BeaconState) addDirtyIndices(index stateTypes.FieldIndex, indices []uint64) {
if b.rebuildTrie[index] {
return
}
// Exit early if balance trie computation isn't enabled.
if !features.Get().EnableBalanceTrieComputation && index == balances {
return
}
totalIndicesLen := len(b.dirtyIndices[index]) + len(indices)
if totalIndicesLen > indicesLimit {
b.rebuildTrie[index] = true
b.dirtyIndices[index] = []uint64{}
} else {
b.dirtyIndices[index] = append(b.dirtyIndices[index], indices...)
}
}

View File

@@ -0,0 +1,89 @@
package v2
import (
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
)
// SetPreviousParticipationBits for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetPreviousParticipationBits(val []byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[previousEpochParticipationBits].MinusRef()
b.sharedFieldReferences[previousEpochParticipationBits] = stateutil.NewRef(1)
b.state.PreviousEpochParticipation = val
b.markFieldAsDirty(previousEpochParticipationBits)
b.rebuildTrie[previousEpochParticipationBits] = true
return nil
}
// SetCurrentParticipationBits for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetCurrentParticipationBits(val []byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[currentEpochParticipationBits].MinusRef()
b.sharedFieldReferences[currentEpochParticipationBits] = stateutil.NewRef(1)
b.state.CurrentEpochParticipation = val
b.markFieldAsDirty(currentEpochParticipationBits)
b.rebuildTrie[currentEpochParticipationBits] = true
return nil
}
// AppendCurrentParticipationBits for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendCurrentParticipationBits(val byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
participation := b.state.CurrentEpochParticipation
if b.sharedFieldReferences[currentEpochParticipationBits].Refs() > 1 {
// Copy elements in underlying array by reference.
participation = make([]byte, len(b.state.CurrentEpochParticipation))
copy(participation, b.state.CurrentEpochParticipation)
b.sharedFieldReferences[currentEpochParticipationBits].MinusRef()
b.sharedFieldReferences[currentEpochParticipationBits] = stateutil.NewRef(1)
}
b.state.CurrentEpochParticipation = append(participation, val)
b.markFieldAsDirty(currentEpochParticipationBits)
b.addDirtyIndices(currentEpochParticipationBits, []uint64{uint64(len(b.state.CurrentEpochParticipation) - 1)})
return nil
}
// AppendPreviousParticipationBits for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendPreviousParticipationBits(val byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
bits := b.state.PreviousEpochParticipation
if b.sharedFieldReferences[previousEpochParticipationBits].Refs() > 1 {
bits = make([]byte, len(b.state.PreviousEpochParticipation))
copy(bits, b.state.PreviousEpochParticipation)
b.sharedFieldReferences[previousEpochParticipationBits].MinusRef()
b.sharedFieldReferences[previousEpochParticipationBits] = stateutil.NewRef(1)
}
b.state.PreviousEpochParticipation = append(bits, val)
b.markFieldAsDirty(previousEpochParticipationBits)
b.addDirtyIndices(previousEpochParticipationBits, []uint64{uint64(len(b.state.PreviousEpochParticipation) - 1)})
return nil
}

View File

@@ -0,0 +1,53 @@
package v2
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
)
// SetRandaoMixes for the beacon state. Updates the entire
// randao mixes to a new value by overwriting the previous one.
func (b *BeaconState) SetRandaoMixes(val [][]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[randaoMixes].MinusRef()
b.sharedFieldReferences[randaoMixes] = stateutil.NewRef(1)
b.state.RandaoMixes = val
b.markFieldAsDirty(randaoMixes)
b.rebuildTrie[randaoMixes] = true
return nil
}
// UpdateRandaoMixesAtIndex for the beacon state. Updates the randao mixes
// at a specific index to a new value.
func (b *BeaconState) UpdateRandaoMixesAtIndex(idx uint64, val []byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if uint64(len(b.state.RandaoMixes)) <= idx {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
mixes := b.state.RandaoMixes
if refs := b.sharedFieldReferences[randaoMixes].Refs(); refs > 1 {
// Copy elements in underlying array by reference.
mixes = make([][]byte, len(b.state.RandaoMixes))
copy(mixes, b.state.RandaoMixes)
b.sharedFieldReferences[randaoMixes].MinusRef()
b.sharedFieldReferences[randaoMixes] = stateutil.NewRef(1)
}
mixes[idx] = val
b.state.RandaoMixes = mixes
b.markFieldAsDirty(randaoMixes)
b.addDirtyIndices(randaoMixes, []uint64{idx})
return nil
}

View File

@@ -0,0 +1,59 @@
package v2
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
)
// SetStateRoots for the beacon state. Updates the state roots
// to a new value by overwriting the previous value.
func (b *BeaconState) SetStateRoots(val [][]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[stateRoots].MinusRef()
b.sharedFieldReferences[stateRoots] = stateutil.NewRef(1)
b.state.StateRoots = val
b.markFieldAsDirty(stateRoots)
b.rebuildTrie[stateRoots] = true
return nil
}
// UpdateStateRootAtIndex for the beacon state. Updates the state root
// at a specific index to a new value.
func (b *BeaconState) UpdateStateRootAtIndex(idx uint64, stateRoot [32]byte) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.RLock()
if uint64(len(b.state.StateRoots)) <= idx {
b.lock.RUnlock()
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.RUnlock()
b.lock.Lock()
defer b.lock.Unlock()
// Check if we hold the only reference to the shared state roots slice.
r := b.state.StateRoots
if ref := b.sharedFieldReferences[stateRoots]; ref.Refs() > 1 {
// Copy elements in underlying array by reference.
r = make([][]byte, len(b.state.StateRoots))
copy(r, b.state.StateRoots)
ref.MinusRef()
b.sharedFieldReferences[stateRoots] = stateutil.NewRef(1)
}
r[idx] = stateRoot[:]
b.state.StateRoots = r
b.markFieldAsDirty(stateRoots)
b.addDirtyIndices(stateRoots, []uint64{idx})
return nil
}

View File

@@ -0,0 +1,31 @@
package v2
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetCurrentSyncCommittee for the beacon state.
func (b *BeaconState) SetCurrentSyncCommittee(val *ethpb.SyncCommittee) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.CurrentSyncCommittee = val
b.markFieldAsDirty(currentSyncCommittee)
return nil
}
// SetNextSyncCommittee for the beacon state.
func (b *BeaconState) SetNextSyncCommittee(val *ethpb.SyncCommittee) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.NextSyncCommittee = val
b.markFieldAsDirty(nextSyncCommittee)
return nil
}

View File

@@ -0,0 +1,161 @@
package v2
import (
"context"
"strconv"
"testing"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
stateTypes "github.com/prysmaticlabs/prysm/beacon-chain/state/types"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestAppendBeyondIndicesLimit(t *testing.T) {
zeroHash := params.BeaconConfig().ZeroHash
mockblockRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(mockblockRoots); i++ {
mockblockRoots[i] = zeroHash[:]
}
mockstateRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(mockstateRoots); i++ {
mockstateRoots[i] = zeroHash[:]
}
mockrandaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(mockrandaoMixes); i++ {
mockrandaoMixes[i] = zeroHash[:]
}
st, err := InitializeFromProto(&ethpb.BeaconStateAltair{
Slot: 1,
CurrentEpochParticipation: []byte{},
PreviousEpochParticipation: []byte{},
Validators: []*ethpb.Validator{},
Eth1Data: &ethpb.Eth1Data{},
BlockRoots: mockblockRoots,
StateRoots: mockstateRoots,
RandaoMixes: mockrandaoMixes,
})
require.NoError(t, err)
_, err = st.HashTreeRoot(context.Background())
require.NoError(t, err)
for i := stateTypes.FieldIndex(0); i < stateTypes.FieldIndex(params.BeaconConfig().BeaconStateAltairFieldCount); i++ {
st.dirtyFields[i] = true
}
_, err = st.HashTreeRoot(context.Background())
require.NoError(t, err)
for i := 0; i < 10; i++ {
assert.NoError(t, st.AppendValidator(&ethpb.Validator{}))
}
assert.Equal(t, false, st.rebuildTrie[validators])
assert.NotEqual(t, len(st.dirtyIndices[validators]), 0)
for i := 0; i < indicesLimit; i++ {
assert.NoError(t, st.AppendValidator(&ethpb.Validator{}))
}
assert.Equal(t, true, st.rebuildTrie[validators])
assert.Equal(t, len(st.dirtyIndices[validators]), 0)
}
func TestBeaconState_AppendBalanceWithTrie(t *testing.T) {
count := uint64(100)
vals := make([]*ethpb.Validator, 0, count)
bals := make([]uint64, 0, count)
for i := uint64(1); i < count; i++ {
someRoot := [32]byte{}
someKey := [fieldparams.BLSPubkeyLength]byte{}
copy(someRoot[:], strconv.Itoa(int(i)))
copy(someKey[:], strconv.Itoa(int(i)))
vals = append(vals, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
bals = append(bals, params.BeaconConfig().MaxEffectiveBalance)
}
zeroHash := params.BeaconConfig().ZeroHash
mockblockRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(mockblockRoots); i++ {
mockblockRoots[i] = zeroHash[:]
}
mockstateRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(mockstateRoots); i++ {
mockstateRoots[i] = zeroHash[:]
}
mockrandaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(mockrandaoMixes); i++ {
mockrandaoMixes[i] = zeroHash[:]
}
var pubKeys [][]byte
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSize; i++ {
pubKeys = append(pubKeys, bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength))
}
st, err := InitializeFromProto(&ethpb.BeaconStateAltair{
Slot: 1,
GenesisValidatorsRoot: make([]byte, 32),
Fork: &ethpb.Fork{
PreviousVersion: make([]byte, 4),
CurrentVersion: make([]byte, 4),
Epoch: 0,
},
LatestBlockHeader: &ethpb.BeaconBlockHeader{
ParentRoot: make([]byte, fieldparams.RootLength),
StateRoot: make([]byte, fieldparams.RootLength),
BodyRoot: make([]byte, fieldparams.RootLength),
},
CurrentEpochParticipation: []byte{},
PreviousEpochParticipation: []byte{},
Validators: vals,
Balances: bals,
Eth1Data: &ethpb.Eth1Data{
DepositRoot: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, 32),
},
BlockRoots: mockblockRoots,
StateRoots: mockstateRoots,
RandaoMixes: mockrandaoMixes,
JustificationBits: bitfield.NewBitvector4(),
PreviousJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
FinalizedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
CurrentSyncCommittee: &ethpb.SyncCommittee{
Pubkeys: pubKeys,
AggregatePubkey: make([]byte, 48),
},
NextSyncCommittee: &ethpb.SyncCommittee{
Pubkeys: pubKeys,
AggregatePubkey: make([]byte, 48),
},
})
assert.NoError(t, err)
_, err = st.HashTreeRoot(context.Background())
assert.NoError(t, err)
for i := 0; i < 100; i++ {
if i%2 == 0 {
assert.NoError(t, st.UpdateBalancesAtIndex(types.ValidatorIndex(i), 1000))
}
if i%3 == 0 {
assert.NoError(t, st.AppendBalance(1000))
}
}
_, err = st.HashTreeRoot(context.Background())
assert.NoError(t, err)
newRt := bytesutil.ToBytes32(st.merkleLayers[0][balances])
wantedRt, err := stateutil.Uint64ListRootWithRegistryLimit(st.state.Balances)
assert.NoError(t, err)
assert.Equal(t, wantedRt, newRt, "state roots are unequal")
}

View File

@@ -0,0 +1,265 @@
package v2
import (
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetValidators for the beacon state. Updates the entire
// to a new value by overwriting the previous one.
func (b *BeaconState) SetValidators(val []*ethpb.Validator) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.Validators = val
b.sharedFieldReferences[validators].MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
b.markFieldAsDirty(validators)
b.rebuildTrie[validators] = true
b.valMapHandler = stateutil.NewValMapHandler(b.state.Validators)
return nil
}
// ApplyToEveryValidator applies the provided callback function to each validator in the
// validator registry.
func (b *BeaconState) ApplyToEveryValidator(f func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error)) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
v := b.state.Validators
if ref := b.sharedFieldReferences[validators]; ref.Refs() > 1 {
v = b.validatorsReferences()
ref.MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
}
b.lock.Unlock()
var changedVals []uint64
for i, val := range v {
changed, newVal, err := f(i, val)
if err != nil {
return err
}
if changed {
changedVals = append(changedVals, uint64(i))
v[i] = newVal
}
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.Validators = v
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, changedVals)
return nil
}
// UpdateValidatorAtIndex for the beacon state. Updates the validator
// at a specific index to a new value.
func (b *BeaconState) UpdateValidatorAtIndex(idx types.ValidatorIndex, val *ethpb.Validator) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if uint64(len(b.state.Validators)) <= uint64(idx) {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
v := b.state.Validators
if ref := b.sharedFieldReferences[validators]; ref.Refs() > 1 {
v = b.validatorsReferences()
ref.MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
}
v[idx] = val
b.state.Validators = v
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, []uint64{uint64(idx)})
return nil
}
// SetBalances for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetBalances(val []uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
b.state.Balances = val
b.rebuildTrie[balances] = true
b.markFieldAsDirty(balances)
return nil
}
// UpdateBalancesAtIndex for the beacon state. This method updates the balance
// at a specific index to a new value.
func (b *BeaconState) UpdateBalancesAtIndex(idx types.ValidatorIndex, val uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if uint64(len(b.state.Balances)) <= uint64(idx) {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
bals := b.state.Balances
if b.sharedFieldReferences[balances].Refs() > 1 {
bals = b.balances()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
}
bals[idx] = val
b.state.Balances = bals
b.markFieldAsDirty(balances)
b.addDirtyIndices(balances, []uint64{uint64(idx)})
return nil
}
// SetSlashings for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetSlashings(val []uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[slashings].MinusRef()
b.sharedFieldReferences[slashings] = stateutil.NewRef(1)
b.state.Slashings = val
b.markFieldAsDirty(slashings)
return nil
}
// UpdateSlashingsAtIndex for the beacon state. Updates the slashings
// at a specific index to a new value.
func (b *BeaconState) UpdateSlashingsAtIndex(idx, val uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if uint64(len(b.state.Slashings)) <= idx {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
s := b.state.Slashings
if b.sharedFieldReferences[slashings].Refs() > 1 {
s = b.slashings()
b.sharedFieldReferences[slashings].MinusRef()
b.sharedFieldReferences[slashings] = stateutil.NewRef(1)
}
s[idx] = val
b.state.Slashings = s
b.markFieldAsDirty(slashings)
return nil
}
// AppendValidator for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendValidator(val *ethpb.Validator) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
vals := b.state.Validators
if b.sharedFieldReferences[validators].Refs() > 1 {
vals = b.validatorsReferences()
b.sharedFieldReferences[validators].MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
}
// append validator to slice
b.state.Validators = append(vals, val)
valIdx := types.ValidatorIndex(len(b.state.Validators) - 1)
b.valMapHandler.Set(bytesutil.ToBytes48(val.PublicKey), valIdx)
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, []uint64{uint64(valIdx)})
return nil
}
// AppendBalance for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendBalance(bal uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
bals := b.state.Balances
if b.sharedFieldReferences[balances].Refs() > 1 {
bals = b.balances()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
}
b.state.Balances = append(bals, bal)
balIdx := len(b.state.Balances) - 1
b.markFieldAsDirty(balances)
b.addDirtyIndices(balances, []uint64{uint64(balIdx)})
return nil
}
// AppendInactivityScore for the beacon state.
func (b *BeaconState) AppendInactivityScore(s uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
scores := b.state.InactivityScores
if b.sharedFieldReferences[inactivityScores].Refs() > 1 {
scores = b.inactivityScores()
b.sharedFieldReferences[inactivityScores].MinusRef()
b.sharedFieldReferences[inactivityScores] = stateutil.NewRef(1)
}
b.state.InactivityScores = append(scores, s)
b.markFieldAsDirty(inactivityScores)
return nil
}
// SetInactivityScores for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetInactivityScores(val []uint64) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[inactivityScores].MinusRef()
b.sharedFieldReferences[inactivityScores] = stateutil.NewRef(1)
b.state.InactivityScores = val
b.markFieldAsDirty(inactivityScores)
return nil
}

View File

@@ -0,0 +1,444 @@
package v2
import (
"context"
"io"
"io/ioutil"
"runtime"
"sort"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/fieldtrie"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/features"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/container/slice"
"github.com/prysmaticlabs/prysm/crypto/hash"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/encoding/ssz"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"go.opencensus.io/trace"
"google.golang.org/protobuf/proto"
)
// InitializeFromProto the beacon state from a protobuf representation.
func InitializeFromProto(st *ethpb.BeaconStateAltair) (*BeaconState, error) {
return InitializeFromProtoUnsafe(proto.Clone(st).(*ethpb.BeaconStateAltair))
}
// InitializeFromSSZReader can be used when the source for a serialized BeaconState object
// is an io.Reader. This allows client code to remain agnostic about whether the data comes
// from the network or a file without needing to read the entire state into mem as a large byte slice.
func InitializeFromSSZReader(r io.Reader) (*BeaconState, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
return InitializeFromSSZBytes(b)
}
// InitializeFromSSZBytes is a convenience method to obtain a BeaconState by unmarshaling
// a slice of bytes containing the ssz-serialized representation of the state.
func InitializeFromSSZBytes(marshaled []byte) (*BeaconState, error) {
st := &ethpb.BeaconStateAltair{}
if err := st.UnmarshalSSZ(marshaled); err != nil {
return nil, err
}
return InitializeFromProtoUnsafe(st)
}
// InitializeFromProtoUnsafe directly uses the beacon state protobuf pointer
// and sets it as the inner state of the BeaconState type.
func InitializeFromProtoUnsafe(st *ethpb.BeaconStateAltair) (*BeaconState, error) {
if st == nil {
return nil, errors.New("received nil state")
}
fieldCount := params.BeaconConfig().BeaconStateAltairFieldCount
b := &BeaconState{
state: st,
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, 11),
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
valMapHandler: stateutil.NewValMapHandler(st.Validators),
}
var err error
for i := 0; i < fieldCount; i++ {
b.dirtyFields[types.FieldIndex(i)] = true
b.rebuildTrie[types.FieldIndex(i)] = true
b.dirtyIndices[types.FieldIndex(i)] = []uint64{}
b.stateFieldLeaves[types.FieldIndex(i)], err = fieldtrie.NewFieldTrie(types.FieldIndex(i), types.BasicArray, nil, 0)
if err != nil {
return nil, err
}
}
// Initialize field reference tracking for shared data.
b.sharedFieldReferences[randaoMixes] = stateutil.NewRef(1)
b.sharedFieldReferences[stateRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[blockRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[previousEpochParticipationBits] = stateutil.NewRef(1) // New in Altair.
b.sharedFieldReferences[currentEpochParticipationBits] = stateutil.NewRef(1) // New in Altair.
b.sharedFieldReferences[slashings] = stateutil.NewRef(1)
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
b.sharedFieldReferences[inactivityScores] = stateutil.NewRef(1) // New in Altair.
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
state.StateCount.Inc()
return b, nil
}
// Copy returns a deep copy of the beacon state.
func (b *BeaconState) Copy() state.BeaconState {
if !b.hasInnerState() {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
fieldCount := params.BeaconConfig().BeaconStateAltairFieldCount
dst := &BeaconState{
state: &ethpb.BeaconStateAltair{
// Primitive types, safe to copy.
GenesisTime: b.state.GenesisTime,
Slot: b.state.Slot,
Eth1DepositIndex: b.state.Eth1DepositIndex,
// Large arrays, infrequently changed, constant size.
RandaoMixes: b.state.RandaoMixes,
StateRoots: b.state.StateRoots,
BlockRoots: b.state.BlockRoots,
Slashings: b.state.Slashings,
Eth1DataVotes: b.state.Eth1DataVotes,
// Large arrays, increases over time.
Validators: b.state.Validators,
Balances: b.state.Balances,
HistoricalRoots: b.state.HistoricalRoots,
PreviousEpochParticipation: b.state.PreviousEpochParticipation,
CurrentEpochParticipation: b.state.CurrentEpochParticipation,
InactivityScores: b.state.InactivityScores,
// Everything else, too small to be concerned about, constant size.
Fork: b.fork(),
LatestBlockHeader: b.latestBlockHeader(),
Eth1Data: b.eth1Data(),
JustificationBits: b.justificationBits(),
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint(),
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint(),
FinalizedCheckpoint: b.finalizedCheckpoint(),
GenesisValidatorsRoot: b.genesisValidatorRoot(),
CurrentSyncCommittee: b.currentSyncCommittee(),
NextSyncCommittee: b.nextSyncCommittee(),
},
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, 11),
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
// Share the reference to validator index map.
valMapHandler: b.valMapHandler,
}
for field, ref := range b.sharedFieldReferences {
ref.AddRef()
dst.sharedFieldReferences[field] = ref
}
// Increment ref for validator map
b.valMapHandler.AddRef()
for i := range b.dirtyFields {
dst.dirtyFields[i] = true
}
for i := range b.dirtyIndices {
indices := make([]uint64, len(b.dirtyIndices[i]))
copy(indices, b.dirtyIndices[i])
dst.dirtyIndices[i] = indices
}
for i := range b.rebuildTrie {
dst.rebuildTrie[i] = true
}
for fldIdx, fieldTrie := range b.stateFieldLeaves {
dst.stateFieldLeaves[fldIdx] = fieldTrie
if fieldTrie.FieldReference() != nil {
fieldTrie.Lock()
fieldTrie.FieldReference().AddRef()
fieldTrie.Unlock()
}
}
if b.merkleLayers != nil {
dst.merkleLayers = make([][][]byte, len(b.merkleLayers))
for i, layer := range b.merkleLayers {
dst.merkleLayers[i] = make([][]byte, len(layer))
for j, content := range layer {
dst.merkleLayers[i][j] = make([]byte, len(content))
copy(dst.merkleLayers[i][j], content)
}
}
}
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(dst, func(b *BeaconState) {
for field, v := range b.sharedFieldReferences {
v.MinusRef()
if b.stateFieldLeaves[field].FieldReference() != nil {
b.stateFieldLeaves[field].FieldReference().MinusRef()
}
}
for i := 0; i < fieldCount; i++ {
field := types.FieldIndex(i)
delete(b.stateFieldLeaves, field)
delete(b.dirtyIndices, field)
delete(b.dirtyFields, field)
delete(b.sharedFieldReferences, field)
delete(b.stateFieldLeaves, field)
}
state.StateCount.Sub(1)
})
return dst
}
// HashTreeRoot of the beacon state retrieves the Merkle root of the trie
// representation of the beacon state based on the eth2 Simple Serialize specification.
func (b *BeaconState) HashTreeRoot(ctx context.Context) ([32]byte, error) {
_, span := trace.StartSpan(ctx, "beaconStateAltair.HashTreeRoot")
defer span.End()
b.lock.Lock()
defer b.lock.Unlock()
if err := b.initializeMerkleLayers(ctx); err != nil {
return [32]byte{}, err
}
if err := b.recomputeDirtyFields(ctx); err != nil {
return [32]byte{}, err
}
return bytesutil.ToBytes32(b.merkleLayers[len(b.merkleLayers)-1][0]), nil
}
// Initializes the Merkle layers for the beacon state if they are empty.
// WARNING: Caller must acquire the mutex before using.
func (b *BeaconState) initializeMerkleLayers(ctx context.Context) error {
if len(b.merkleLayers) > 0 {
return nil
}
fieldRoots, err := computeFieldRoots(ctx, b.state)
if err != nil {
return err
}
layers := stateutil.Merkleize(fieldRoots)
b.merkleLayers = layers
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateAltairFieldCount)
return nil
}
// Recomputes the Merkle layers for the dirty fields in the state.
// WARNING: Caller must acquire the mutex before using.
func (b *BeaconState) recomputeDirtyFields(ctx context.Context) error {
for field := range b.dirtyFields {
root, err := b.rootSelector(ctx, field)
if err != nil {
return err
}
b.merkleLayers[0][field] = root[:]
b.recomputeRoot(int(field))
delete(b.dirtyFields, field)
}
return nil
}
// FieldReferencesCount returns the reference count held by each field. This
// also includes the field trie held by each field.
func (b *BeaconState) FieldReferencesCount() map[string]uint64 {
refMap := make(map[string]uint64)
b.lock.RLock()
defer b.lock.RUnlock()
for i, f := range b.sharedFieldReferences {
refMap[i.String(b.Version())] = uint64(f.Refs())
}
for i, f := range b.stateFieldLeaves {
numOfRefs := uint64(f.FieldReference().Refs())
f.RLock()
if !f.Empty() {
refMap[i.String(b.Version())+"_trie"] = numOfRefs
}
f.RUnlock()
}
return refMap
}
// IsNil checks if the state and the underlying proto
// object are nil.
func (b *BeaconState) IsNil() bool {
return b == nil || b.state == nil
}
func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex) ([32]byte, error) {
_, span := trace.StartSpan(ctx, "beaconState.rootSelector")
defer span.End()
span.AddAttributes(trace.StringAttribute("field", field.String(b.Version())))
hasher := hash.CustomSHA256Hasher()
switch field {
case genesisTime:
return ssz.Uint64Root(b.state.GenesisTime), nil
case genesisValidatorRoot:
return bytesutil.ToBytes32(b.state.GenesisValidatorsRoot), nil
case slot:
return ssz.Uint64Root(uint64(b.state.Slot)), nil
case eth1DepositIndex:
return ssz.Uint64Root(b.state.Eth1DepositIndex), nil
case fork:
return ssz.ForkRoot(b.state.Fork)
case latestBlockHeader:
return stateutil.BlockHeaderRoot(b.state.LatestBlockHeader)
case blockRoots:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.state.BlockRoots, fieldparams.BlockRootsLength)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(blockRoots, b.state.BlockRoots)
case stateRoots:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.state.StateRoots, fieldparams.StateRootsLength)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(stateRoots, b.state.StateRoots)
case historicalRoots:
return ssz.ByteArrayRootWithLimit(b.state.HistoricalRoots, fieldparams.HistoricalRootsLength)
case eth1Data:
return stateutil.Eth1Root(hasher, b.state.Eth1Data)
case eth1DataVotes:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(
field,
b.state.Eth1DataVotes,
fieldparams.Eth1DataVotesLength,
)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(field, b.state.Eth1DataVotes)
case validators:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.state.Validators, fieldparams.ValidatorRegistryLimit)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, validators)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(validators, b.state.Validators)
case balances:
if features.Get().EnableBalanceTrieComputation {
if b.rebuildTrie[field] {
maxBalCap := uint64(fieldparams.ValidatorRegistryLimit)
elemSize := uint64(8)
balLimit := (maxBalCap*elemSize + 31) / 32
err := b.resetFieldTrie(field, b.state.Balances, balLimit)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(balances, b.state.Balances)
}
return stateutil.Uint64ListRootWithRegistryLimit(b.state.Balances)
case randaoMixes:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.state.RandaoMixes, fieldparams.RandaoMixesLength)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(randaoMixes, b.state.RandaoMixes)
case slashings:
return ssz.SlashingsRoot(b.state.Slashings)
case previousEpochParticipationBits:
return stateutil.ParticipationBitsRoot(b.state.PreviousEpochParticipation)
case currentEpochParticipationBits:
return stateutil.ParticipationBitsRoot(b.state.CurrentEpochParticipation)
case justificationBits:
return bytesutil.ToBytes32(b.state.JustificationBits), nil
case previousJustifiedCheckpoint:
return ssz.CheckpointRoot(hasher, b.state.PreviousJustifiedCheckpoint)
case currentJustifiedCheckpoint:
return ssz.CheckpointRoot(hasher, b.state.CurrentJustifiedCheckpoint)
case finalizedCheckpoint:
return ssz.CheckpointRoot(hasher, b.state.FinalizedCheckpoint)
case inactivityScores:
return stateutil.Uint64ListRootWithRegistryLimit(b.state.InactivityScores)
case currentSyncCommittee:
return stateutil.SyncCommitteeRoot(b.state.CurrentSyncCommittee)
case nextSyncCommittee:
return stateutil.SyncCommitteeRoot(b.state.NextSyncCommittee)
}
return [32]byte{}, errors.New("invalid field index provided")
}
func (b *BeaconState) recomputeFieldTrie(index types.FieldIndex, elements interface{}) ([32]byte, error) {
fTrie := b.stateFieldLeaves[index]
// We can't lock the trie directly because the trie's variable gets reassigned,
// and therefore we would call Unlock() on a different object.
fTrieMutex := fTrie.RWMutex
if fTrie.FieldReference().Refs() > 1 {
fTrieMutex.Lock()
fTrie.FieldReference().MinusRef()
newTrie := fTrie.CopyTrie()
b.stateFieldLeaves[index] = newTrie
fTrie = newTrie
fTrieMutex.Unlock()
}
// remove duplicate indexes
b.dirtyIndices[index] = slice.SetUint64(b.dirtyIndices[index])
// sort indexes again
sort.Slice(b.dirtyIndices[index], func(i int, j int) bool {
return b.dirtyIndices[index][i] < b.dirtyIndices[index][j]
})
root, err := fTrie.RecomputeTrie(b.dirtyIndices[index], elements)
if err != nil {
return [32]byte{}, err
}
b.dirtyIndices[index] = []uint64{}
return root, nil
}
func (b *BeaconState) resetFieldTrie(index types.FieldIndex, elements interface{}, length uint64) error {
fTrie, err := fieldtrie.NewFieldTrie(index, fieldMap[index], elements, length)
if err != nil {
return err
}
b.stateFieldLeaves[index] = fTrie
b.dirtyIndices[index] = []uint64{}
return nil
}

View File

@@ -0,0 +1,169 @@
package v2
import (
"strconv"
"sync"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/config/features"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestMain(m *testing.M) {
resetCfg := features.InitWithReset(&features.Flags{EnableBalanceTrieComputation: true})
defer resetCfg()
m.Run()
}
func TestValidatorMap_DistinctCopy(t *testing.T) {
count := uint64(100)
vals := make([]*ethpb.Validator, 0, count)
for i := uint64(1); i < count; i++ {
someRoot := [32]byte{}
someKey := [fieldparams.BLSPubkeyLength]byte{}
copy(someRoot[:], strconv.Itoa(int(i)))
copy(someKey[:], strconv.Itoa(int(i)))
vals = append(vals, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
}
handler := stateutil.NewValMapHandler(vals)
newHandler := handler.Copy()
wantedPubkey := strconv.Itoa(22)
handler.Set(bytesutil.ToBytes48([]byte(wantedPubkey)), 27)
val1, _ := handler.Get(bytesutil.ToBytes48([]byte(wantedPubkey)))
val2, _ := newHandler.Get(bytesutil.ToBytes48([]byte(wantedPubkey)))
assert.NotEqual(t, val1, val2, "Values are supposed to be unequal due to copy")
}
func TestInitializeFromProto(t *testing.T) {
type test struct {
name string
state *ethpb.BeaconStateAltair
error string
}
initTests := []test{
{
name: "nil state",
state: nil,
error: "received nil state",
},
{
name: "nil validators",
state: &ethpb.BeaconStateAltair{
Slot: 4,
Validators: nil,
},
},
{
name: "empty state",
state: &ethpb.BeaconStateAltair{},
},
// TODO: Add full state. Blocked by testutil migration.
}
for _, tt := range initTests {
t.Run(tt.name, func(t *testing.T) {
_, err := InitializeFromProto(tt.state)
if tt.error != "" {
require.ErrorContains(t, tt.error, err)
} else {
require.NoError(t, err)
}
})
}
}
func TestBeaconState_NoDeadlock(t *testing.T) {
count := uint64(100)
vals := make([]*ethpb.Validator, 0, count)
for i := uint64(1); i < count; i++ {
someRoot := [32]byte{}
someKey := [fieldparams.BLSPubkeyLength]byte{}
copy(someRoot[:], strconv.Itoa(int(i)))
copy(someKey[:], strconv.Itoa(int(i)))
vals = append(vals, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
}
st, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{
Validators: vals,
})
assert.NoError(t, err)
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
// Continuously lock and unlock the state
// by acquiring the lock.
for i := 0; i < 1000; i++ {
for _, f := range st.stateFieldLeaves {
f.Lock()
if f.Empty() {
f.InsertFieldLayer(make([][]*[32]byte, 10))
}
f.Unlock()
f.FieldReference().AddRef()
}
}
wg.Done()
}()
// Constantly read from the offending portion
// of the code to ensure there is no possible
// recursive read locking.
for i := 0; i < 1000; i++ {
go func() {
_ = st.FieldReferencesCount()
}()
}
// Test will not terminate in the event of a deadlock.
wg.Wait()
}
func TestInitializeFromProtoUnsafe(t *testing.T) {
type test struct {
name string
state *ethpb.BeaconStateAltair
error string
}
initTests := []test{
{
name: "nil state",
state: nil,
error: "received nil state",
},
{
name: "nil validators",
state: &ethpb.BeaconStateAltair{
Slot: 4,
Validators: nil,
},
},
{
name: "empty state",
state: &ethpb.BeaconStateAltair{},
},
// TODO: Add full state. Blocked by testutil migration.
}
_ = initTests
}

View File

@@ -0,0 +1,78 @@
package v2
import (
"sync"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/fieldtrie"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/params"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
func init() {
fieldMap = make(map[types.FieldIndex]types.DataType, params.BeaconConfig().BeaconStateFieldCount)
// Initialize the fixed sized arrays.
fieldMap[types.BlockRoots] = types.BasicArray
fieldMap[types.StateRoots] = types.BasicArray
fieldMap[types.RandaoMixes] = types.BasicArray
// Initialize the composite arrays.
fieldMap[types.Eth1DataVotes] = types.CompositeArray
fieldMap[types.Validators] = types.CompositeArray
// Initialize Compressed Arrays
fieldMap[types.Balances] = types.CompressedArray
}
// fieldMap keeps track of each field
// to its corresponding data type.
var fieldMap map[types.FieldIndex]types.DataType
// ErrNilInnerState returns when the inner state is nil and no copy set or get
// operations can be performed on state.
var ErrNilInnerState = errors.New("nil inner state")
// BeaconState defines a struct containing utilities for the eth2 chain state, defining
// getters and setters for its respective values and helpful functions such as HashTreeRoot().
type BeaconState struct {
state *ethpb.BeaconStateAltair
lock sync.RWMutex
dirtyFields map[types.FieldIndex]bool
dirtyIndices map[types.FieldIndex][]uint64
stateFieldLeaves map[types.FieldIndex]*fieldtrie.FieldTrie
rebuildTrie map[types.FieldIndex]bool
valMapHandler *stateutil.ValidatorMapHandler
merkleLayers [][][]byte
sharedFieldReferences map[types.FieldIndex]*stateutil.Reference
}
// Field Aliases for values from the types package.
const (
genesisTime = types.GenesisTime
genesisValidatorRoot = types.GenesisValidatorRoot
slot = types.Slot
fork = types.Fork
latestBlockHeader = types.LatestBlockHeader
blockRoots = types.BlockRoots
stateRoots = types.StateRoots
historicalRoots = types.HistoricalRoots
eth1Data = types.Eth1Data
eth1DataVotes = types.Eth1DataVotes
eth1DepositIndex = types.Eth1DepositIndex
validators = types.Validators
balances = types.Balances
randaoMixes = types.RandaoMixes
slashings = types.Slashings
previousEpochParticipationBits = types.PreviousEpochParticipationBits
currentEpochParticipationBits = types.CurrentEpochParticipationBits
justificationBits = types.JustificationBits
previousJustifiedCheckpoint = types.PreviousJustifiedCheckpoint
currentJustifiedCheckpoint = types.CurrentJustifiedCheckpoint
finalizedCheckpoint = types.FinalizedCheckpoint
inactivityScores = types.InactivityScores
currentSyncCommittee = types.CurrentSyncCommittee
nextSyncCommittee = types.NextSyncCommittee
)

View File

@@ -0,0 +1,92 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"deprecated_getters.go",
"deprecated_setters.go",
"field_roots.go",
"getters_block.go",
"getters_checkpoint.go",
"getters_eth1.go",
"getters_misc.go",
"getters_participation.go",
"getters_payload_header.go",
"getters_randao.go",
"getters_state.go",
"getters_sync_committee.go",
"getters_validator.go",
"proofs.go",
"setters_block.go",
"setters_checkpoint.go",
"setters_eth1.go",
"setters_misc.go",
"setters_participation.go",
"setters_payload_header.go",
"setters_randao.go",
"setters_state.go",
"setters_sync_committee.go",
"setters_validator.go",
"state_trie.go",
"types.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v3",
visibility = [
"//beacon-chain:__subpackages__",
"//testing/spectest:__subpackages__",
"//testing/util:__pkg__",
],
deps = [
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native/fieldtrie:go_default_library",
"//beacon-chain/state/state-native/v1:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/slice:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"deprecated_getters_test.go",
"deprecated_setters_test.go",
"getters_block_test.go",
"getters_test.go",
"getters_validator_test.go",
"proofs_test.go",
"setters_test.go",
"state_trie_test.go",
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/state/state-native/v1:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/trie:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
],
)

View File

@@ -0,0 +1,16 @@
package v3
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// PreviousEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) PreviousEpochAttestations() ([]*ethpb.PendingAttestation, error) {
return nil, errors.New("PreviousEpochAttestations is not supported for version Bellatrix beacon state")
}
// CurrentEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) CurrentEpochAttestations() ([]*ethpb.PendingAttestation, error) {
return nil, errors.New("CurrentEpochAttestations is not supported for version Bellatrix beacon state")
}

View File

@@ -0,0 +1,19 @@
package v3
import (
"testing"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_CurrentEpochAttestations(t *testing.T) {
s := &BeaconState{}
_, err := s.CurrentEpochAttestations()
require.ErrorContains(t, "CurrentEpochAttestations is not supported for version Bellatrix beacon state", err)
}
func TestBeaconState_PreviousEpochAttestations(t *testing.T) {
s := &BeaconState{}
_, err := s.PreviousEpochAttestations()
require.ErrorContains(t, "PreviousEpochAttestations is not supported for version Bellatrix beacon state", err)
}

View File

@@ -0,0 +1,31 @@
package v3
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetPreviousEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) SetPreviousEpochAttestations(_ []*ethpb.PendingAttestation) error {
return errors.New("SetPreviousEpochAttestations is not supported for version Bellatrix beacon state")
}
// SetCurrentEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) SetCurrentEpochAttestations(_ []*ethpb.PendingAttestation) error {
return errors.New("SetCurrentEpochAttestations is not supported for version Bellatrix beacon state")
}
// AppendCurrentEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) AppendCurrentEpochAttestations(_ *ethpb.PendingAttestation) error {
return errors.New("AppendCurrentEpochAttestations is not supported for version Bellatrix beacon state")
}
// AppendPreviousEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) AppendPreviousEpochAttestations(_ *ethpb.PendingAttestation) error {
return errors.New("AppendPreviousEpochAttestations is not supported for version Bellatrix beacon state")
}
// RotateAttestations is not supported for HF1 beacon state.
func (*BeaconState) RotateAttestations() error {
return errors.New("RotateAttestations is not supported for version Bellatrix beacon state")
}

View File

@@ -0,0 +1,27 @@
package v3
import (
"testing"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_AppendCurrentEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "AppendCurrentEpochAttestations is not supported for version Bellatrix beacon state", s.AppendCurrentEpochAttestations(nil))
}
func TestBeaconState_AppendPreviousEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "AppendPreviousEpochAttestations is not supported for version Bellatrix beacon state", s.AppendPreviousEpochAttestations(nil))
}
func TestBeaconState_SetCurrentEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "SetCurrentEpochAttestations is not supported for version Bellatrix beacon state", s.SetCurrentEpochAttestations(nil))
}
func TestBeaconState_SetPreviousEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "SetPreviousEpochAttestations is not supported for version Bellatrix beacon state", s.SetPreviousEpochAttestations(nil))
}

View File

@@ -0,0 +1,19 @@
package v3
import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/config/features"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// computeFieldRoots returns the hash tree root computations of every field in
// the beacon state as a list of 32 byte roots.
//nolint:deadcode
func computeFieldRoots(ctx context.Context, state *ethpb.BeaconStateBellatrix) ([][]byte, error) {
if features.Get().EnableSSZCache {
return stateutil.CachedHasher.ComputeFieldRootsWithHasherBellatrix(ctx, state)
}
return stateutil.NocachedHasher.ComputeFieldRootsWithHasherBellatrix(ctx, state)
}

View File

@@ -0,0 +1,99 @@
package v3
import (
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// LatestBlockHeader stored within the beacon state.
func (b *BeaconState) LatestBlockHeader() *ethpb.BeaconBlockHeader {
if !b.hasInnerState() {
return nil
}
if b.state.LatestBlockHeader == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.latestBlockHeader()
}
// latestBlockHeader stored within the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) latestBlockHeader() *ethpb.BeaconBlockHeader {
if !b.hasInnerState() {
return nil
}
if b.state.LatestBlockHeader == nil {
return nil
}
hdr := &ethpb.BeaconBlockHeader{
Slot: b.state.LatestBlockHeader.Slot,
ProposerIndex: b.state.LatestBlockHeader.ProposerIndex,
}
parentRoot := make([]byte, len(b.state.LatestBlockHeader.ParentRoot))
bodyRoot := make([]byte, len(b.state.LatestBlockHeader.BodyRoot))
stateRoot := make([]byte, len(b.state.LatestBlockHeader.StateRoot))
copy(parentRoot, b.state.LatestBlockHeader.ParentRoot)
copy(bodyRoot, b.state.LatestBlockHeader.BodyRoot)
copy(stateRoot, b.state.LatestBlockHeader.StateRoot)
hdr.ParentRoot = parentRoot
hdr.BodyRoot = bodyRoot
hdr.StateRoot = stateRoot
return hdr
}
// BlockRoots kept track of in the beacon state.
func (b *BeaconState) BlockRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.BlockRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.blockRoots()
}
// blockRoots kept track of in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) blockRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.BlockRoots)
}
// BlockRootAtIndex retrieves a specific block root based on an
// input index value.
func (b *BeaconState) BlockRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.BlockRoots == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.blockRootAtIndex(idx)
}
// blockRootAtIndex retrieves a specific block root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) blockRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
return bytesutil.SafeCopyRootAtIndex(b.state.BlockRoots, idx)
}

View File

@@ -0,0 +1,59 @@
package v3
import (
"testing"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_LatestBlockHeader(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
got := s.LatestBlockHeader()
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
want := &ethpb.BeaconBlockHeader{Slot: 100}
s, err = InitializeFromProto(&ethpb.BeaconStateBellatrix{LatestBlockHeader: want})
require.NoError(t, err)
got = s.LatestBlockHeader()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got.Slot = 101
require.DeepNotEqual(t, want, got)
}
func TestBeaconState_BlockRoots(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
got := s.BlockRoots()
require.DeepEqual(t, ([][]byte)(nil), got)
want := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconStateBellatrix{BlockRoots: want})
require.NoError(t, err)
got = s.BlockRoots()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0][0] = 'b'
require.DeepNotEqual(t, want, got)
}
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
got, err := s.BlockRootAtIndex(0)
require.NoError(t, err)
require.DeepEqual(t, ([]byte)(nil), got)
r := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconStateBellatrix{BlockRoots: r})
require.NoError(t, err)
got, err = s.BlockRootAtIndex(0)
require.NoError(t, err)
want := bytesutil.PadTo([]byte{'a'}, 32)
require.DeepSSZEqual(t, want, got)
}

View File

@@ -0,0 +1,160 @@
package v3
import (
"bytes"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/go-bitfield"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// JustificationBits marking which epochs have been justified in the beacon chain.
func (b *BeaconState) JustificationBits() bitfield.Bitvector4 {
if !b.hasInnerState() {
return nil
}
if b.state.JustificationBits == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.justificationBits()
}
// justificationBits marking which epochs have been justified in the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) justificationBits() bitfield.Bitvector4 {
if !b.hasInnerState() {
return nil
}
if b.state.JustificationBits == nil {
return nil
}
res := make([]byte, len(b.state.JustificationBits.Bytes()))
copy(res, b.state.JustificationBits.Bytes())
return res
}
// PreviousJustifiedCheckpoint denoting an epoch and block root.
func (b *BeaconState) PreviousJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
if b.state.PreviousJustifiedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.previousJustifiedCheckpoint()
}
// previousJustifiedCheckpoint denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) previousJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyCheckpoint(b.state.PreviousJustifiedCheckpoint)
}
// CurrentJustifiedCheckpoint denoting an epoch and block root.
func (b *BeaconState) CurrentJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
if b.state.CurrentJustifiedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.currentJustifiedCheckpoint()
}
// currentJustifiedCheckpoint denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) currentJustifiedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyCheckpoint(b.state.CurrentJustifiedCheckpoint)
}
// MatchCurrentJustifiedCheckpoint returns true if input justified checkpoint matches
// the current justified checkpoint in state.
func (b *BeaconState) MatchCurrentJustifiedCheckpoint(c *ethpb.Checkpoint) bool {
if !b.hasInnerState() {
return false
}
if b.state.CurrentJustifiedCheckpoint == nil {
return false
}
if c.Epoch != b.state.CurrentJustifiedCheckpoint.Epoch {
return false
}
return bytes.Equal(c.Root, b.state.CurrentJustifiedCheckpoint.Root)
}
// MatchPreviousJustifiedCheckpoint returns true if the input justified checkpoint matches
// the previous justified checkpoint in state.
func (b *BeaconState) MatchPreviousJustifiedCheckpoint(c *ethpb.Checkpoint) bool {
if !b.hasInnerState() {
return false
}
if b.state.PreviousJustifiedCheckpoint == nil {
return false
}
if c.Epoch != b.state.PreviousJustifiedCheckpoint.Epoch {
return false
}
return bytes.Equal(c.Root, b.state.PreviousJustifiedCheckpoint.Root)
}
// FinalizedCheckpoint denoting an epoch and block root.
func (b *BeaconState) FinalizedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
if b.state.FinalizedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.finalizedCheckpoint()
}
// finalizedCheckpoint denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) finalizedCheckpoint() *ethpb.Checkpoint {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyCheckpoint(b.state.FinalizedCheckpoint)
}
// FinalizedCheckpointEpoch returns the epoch value of the finalized checkpoint.
func (b *BeaconState) FinalizedCheckpointEpoch() types.Epoch {
if !b.hasInnerState() {
return 0
}
if b.state.FinalizedCheckpoint == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.state.FinalizedCheckpoint.Epoch
}

View File

@@ -0,0 +1,91 @@
package v3
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// Eth1Data corresponding to the proof-of-work chain information stored in the beacon state.
func (b *BeaconState) Eth1Data() *ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1Data == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1Data()
}
// eth1Data corresponding to the proof-of-work chain information stored in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) eth1Data() *ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1Data == nil {
return nil
}
return ethpb.CopyETH1Data(b.state.Eth1Data)
}
// Eth1DataVotes corresponds to votes from Ethereum on the canonical proof-of-work chain
// data retrieved from eth1.
func (b *BeaconState) Eth1DataVotes() []*ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1DataVotes == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1DataVotes()
}
// eth1DataVotes corresponds to votes from Ethereum on the canonical proof-of-work chain
// data retrieved from eth1.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) eth1DataVotes() []*ethpb.Eth1Data {
if !b.hasInnerState() {
return nil
}
if b.state.Eth1DataVotes == nil {
return nil
}
res := make([]*ethpb.Eth1Data, len(b.state.Eth1DataVotes))
for i := 0; i < len(res); i++ {
res[i] = ethpb.CopyETH1Data(b.state.Eth1DataVotes[i])
}
return res
}
// Eth1DepositIndex corresponds to the index of the deposit made to the
// validator deposit contract at the time of this state's eth1 data.
func (b *BeaconState) Eth1DepositIndex() uint64 {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1DepositIndex()
}
// eth1DepositIndex corresponds to the index of the deposit made to the
// validator deposit contract at the time of this state's eth1 data.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) eth1DepositIndex() uint64 {
if !b.hasInnerState() {
return 0
}
return b.state.Eth1DepositIndex
}

View File

@@ -0,0 +1,211 @@
package v3
import (
"time"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/runtime/version"
)
// GenesisTime of the beacon state as a uint64.
func (b *BeaconState) GenesisTime() uint64 {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.genesisTime()
}
// genesisTime of the beacon state as a uint64.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) genesisTime() uint64 {
if !b.hasInnerState() {
return 0
}
return b.state.GenesisTime
}
// GenesisValidatorRoot of the beacon state.
func (b *BeaconState) GenesisValidatorRoot() []byte {
if !b.hasInnerState() {
return nil
}
if b.state.GenesisValidatorsRoot == nil {
return params.BeaconConfig().ZeroHash[:]
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.genesisValidatorRoot()
}
// genesisValidatorRoot of the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) genesisValidatorRoot() []byte {
if !b.hasInnerState() {
return nil
}
if b.state.GenesisValidatorsRoot == nil {
return params.BeaconConfig().ZeroHash[:]
}
root := make([]byte, 32)
copy(root, b.state.GenesisValidatorsRoot)
return root
}
// GenesisUnixTime returns the genesis time as time.Time.
func (b *BeaconState) GenesisUnixTime() time.Time {
if !b.hasInnerState() {
return time.Unix(0, 0)
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.genesisUnixTime()
}
// genesisUnixTime returns the genesis time as time.Time.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) genesisUnixTime() time.Time {
if !b.hasInnerState() {
return time.Unix(0, 0)
}
return time.Unix(int64(b.state.GenesisTime), 0)
}
// ParentRoot is a convenience method to access state.LatestBlockRoot.ParentRoot.
func (b *BeaconState) ParentRoot() [32]byte {
if !b.hasInnerState() {
return [32]byte{}
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.parentRoot()
}
// parentRoot is a convenience method to access state.LatestBlockRoot.ParentRoot.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) parentRoot() [32]byte {
if !b.hasInnerState() {
return [32]byte{}
}
parentRoot := [32]byte{}
copy(parentRoot[:], b.state.LatestBlockHeader.ParentRoot)
return parentRoot
}
// Version of the beacon state. This method
// is strictly meant to be used without a lock
// internally.
func (_ *BeaconState) Version() int {
return version.Bellatrix
}
// Slot of the current beacon chain state.
func (b *BeaconState) Slot() types.Slot {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.slot()
}
// slot of the current beacon chain state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) slot() types.Slot {
if !b.hasInnerState() {
return 0
}
return b.state.Slot
}
// Fork version of the beacon chain.
func (b *BeaconState) Fork() *ethpb.Fork {
if !b.hasInnerState() {
return nil
}
if b.state.Fork == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.fork()
}
// fork version of the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) fork() *ethpb.Fork {
if !b.hasInnerState() {
return nil
}
if b.state.Fork == nil {
return nil
}
prevVersion := make([]byte, len(b.state.Fork.PreviousVersion))
copy(prevVersion, b.state.Fork.PreviousVersion)
currVersion := make([]byte, len(b.state.Fork.CurrentVersion))
copy(currVersion, b.state.Fork.CurrentVersion)
return &ethpb.Fork{
PreviousVersion: prevVersion,
CurrentVersion: currVersion,
Epoch: b.state.Fork.Epoch,
}
}
// HistoricalRoots based on epochs stored in the beacon state.
func (b *BeaconState) HistoricalRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.HistoricalRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.historicalRoots()
}
// historicalRoots based on epochs stored in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) historicalRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.HistoricalRoots)
}
// balancesLength returns the length of the balances slice.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) balancesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.Balances == nil {
return 0
}
return len(b.state.Balances)
}

View File

@@ -0,0 +1,53 @@
package v3
// CurrentEpochParticipation corresponding to participation bits on the beacon chain.
func (b *BeaconState) CurrentEpochParticipation() ([]byte, error) {
if !b.hasInnerState() {
return nil, nil
}
if b.state.CurrentEpochParticipation == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.currentEpochParticipation(), nil
}
// PreviousEpochParticipation corresponding to participation bits on the beacon chain.
func (b *BeaconState) PreviousEpochParticipation() ([]byte, error) {
if !b.hasInnerState() {
return nil, nil
}
if b.state.PreviousEpochParticipation == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.previousEpochParticipation(), nil
}
// currentEpochParticipation corresponding to participation bits on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) currentEpochParticipation() []byte {
if !b.hasInnerState() {
return nil
}
tmp := make([]byte, len(b.state.CurrentEpochParticipation))
copy(tmp, b.state.CurrentEpochParticipation)
return tmp
}
// previousEpochParticipation corresponding to participation bits on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) previousEpochParticipation() []byte {
if !b.hasInnerState() {
return nil
}
tmp := make([]byte, len(b.state.PreviousEpochParticipation))
copy(tmp, b.state.PreviousEpochParticipation)
return tmp
}

View File

@@ -0,0 +1,30 @@
package v3
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// LatestExecutionPayloadHeader of the beacon state.
func (b *BeaconState) LatestExecutionPayloadHeader() (*ethpb.ExecutionPayloadHeader, error) {
if !b.hasInnerState() {
return nil, nil
}
if b.state.LatestExecutionPayloadHeader == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.latestExecutionPayloadHeader(), nil
}
// latestExecutionPayloadHeader of the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) latestExecutionPayloadHeader() *ethpb.ExecutionPayloadHeader {
if !b.hasInnerState() {
return nil
}
return ethpb.CopyExecutionPayloadHeader(b.state.LatestExecutionPayloadHeader)
}

View File

@@ -0,0 +1,85 @@
package v3
import (
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
)
// RandaoMixes of block proposers on the beacon chain.
func (b *BeaconState) RandaoMixes() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.RandaoMixes == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.randaoMixes()
}
// randaoMixes of block proposers on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) randaoMixes() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.RandaoMixes)
}
// RandaoMixAtIndex retrieves a specific block root based on an
// input index value.
func (b *BeaconState) RandaoMixAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.RandaoMixes == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.randaoMixAtIndex(idx)
}
// randaoMixAtIndex retrieves a specific block root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) randaoMixAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
return bytesutil.SafeCopyRootAtIndex(b.state.RandaoMixes, idx)
}
// RandaoMixesLength returns the length of the randao mixes slice.
func (b *BeaconState) RandaoMixesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.RandaoMixes == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.randaoMixesLength()
}
// randaoMixesLength returns the length of the randao mixes slice.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) randaoMixesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.RandaoMixes == nil {
return 0
}
return len(b.state.RandaoMixes)
}

View File

@@ -0,0 +1,127 @@
package v3
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// InnerStateUnsafe returns the pointer value of the underlying
// beacon state proto object, bypassing immutability. Use with care.
func (b *BeaconState) InnerStateUnsafe() interface{} {
if b == nil {
return nil
}
return b.state
}
// CloneInnerState the beacon state into a protobuf for usage.
func (b *BeaconState) CloneInnerState() interface{} {
if b == nil || b.state == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return &ethpb.BeaconStateBellatrix{
GenesisTime: b.genesisTime(),
GenesisValidatorsRoot: b.genesisValidatorRoot(),
Slot: b.slot(),
Fork: b.fork(),
LatestBlockHeader: b.latestBlockHeader(),
BlockRoots: b.blockRoots(),
StateRoots: b.stateRoots(),
HistoricalRoots: b.historicalRoots(),
Eth1Data: b.eth1Data(),
Eth1DataVotes: b.eth1DataVotes(),
Eth1DepositIndex: b.eth1DepositIndex(),
Validators: b.validators(),
Balances: b.balances(),
RandaoMixes: b.randaoMixes(),
Slashings: b.slashings(),
CurrentEpochParticipation: b.currentEpochParticipation(),
PreviousEpochParticipation: b.previousEpochParticipation(),
JustificationBits: b.justificationBits(),
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint(),
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint(),
FinalizedCheckpoint: b.finalizedCheckpoint(),
InactivityScores: b.inactivityScores(),
CurrentSyncCommittee: b.currentSyncCommittee(),
NextSyncCommittee: b.nextSyncCommittee(),
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeader(),
}
}
// hasInnerState detects if the internal reference to the state data structure
// is populated correctly. Returns false if nil.
func (b *BeaconState) hasInnerState() bool {
return b != nil && b.state != nil
}
// StateRoots kept track of in the beacon state.
func (b *BeaconState) StateRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
if b.state.StateRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.stateRoots()
}
// StateRoots kept track of in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) stateRoots() [][]byte {
if !b.hasInnerState() {
return nil
}
return bytesutil.SafeCopy2dBytes(b.state.StateRoots)
}
// StateRootAtIndex retrieves a specific state root based on an
// input index value.
func (b *BeaconState) StateRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.StateRoots == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.stateRootAtIndex(idx)
}
// stateRootAtIndex retrieves a specific state root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) stateRootAtIndex(idx uint64) ([]byte, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
return bytesutil.SafeCopyRootAtIndex(b.state.StateRoots, idx)
}
// MarshalSSZ marshals the underlying beacon state to bytes.
func (b *BeaconState) MarshalSSZ() ([]byte, error) {
if !b.hasInnerState() {
return nil, errors.New("nil beacon state")
}
return b.state.MarshalSSZ()
}
// ProtobufBeaconState transforms an input into beacon state Merge in the form of protobuf.
// Error is returned if the input is not type protobuf beacon state.
func ProtobufBeaconState(s interface{}) (*ethpb.BeaconStateBellatrix, error) {
pbState, ok := s.(*ethpb.BeaconStateBellatrix)
if !ok {
return nil, errors.New("input is not type pb.BeaconStateBellatrix")
}
return pbState, nil
}

View File

@@ -0,0 +1,69 @@
package v3
import (
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// currentSyncCommittee of the current sync committee in beacon chain state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) currentSyncCommittee() *ethpb.SyncCommittee {
if !b.hasInnerState() {
return nil
}
return CopySyncCommittee(b.state.CurrentSyncCommittee)
}
// nextSyncCommittee of the next sync committee in beacon chain state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) nextSyncCommittee() *ethpb.SyncCommittee {
if !b.hasInnerState() {
return nil
}
return CopySyncCommittee(b.state.NextSyncCommittee)
}
// CurrentSyncCommittee of the current sync committee in beacon chain state.
func (b *BeaconState) CurrentSyncCommittee() (*ethpb.SyncCommittee, error) {
if !b.hasInnerState() {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
if b.state.CurrentSyncCommittee == nil {
return nil, nil
}
return b.currentSyncCommittee(), nil
}
// NextSyncCommittee of the next sync committee in beacon chain state.
func (b *BeaconState) NextSyncCommittee() (*ethpb.SyncCommittee, error) {
if !b.hasInnerState() {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
if b.state.NextSyncCommittee == nil {
return nil, nil
}
return b.nextSyncCommittee(), nil
}
// CopySyncCommittee copies the provided sync committee object.
func CopySyncCommittee(data *ethpb.SyncCommittee) *ethpb.SyncCommittee {
if data == nil {
return nil
}
return &ethpb.SyncCommittee{
Pubkeys: bytesutil.SafeCopy2dBytes(data.Pubkeys),
AggregatePubkey: bytesutil.SafeCopyBytes(data.AggregatePubkey),
}
}

View File

@@ -0,0 +1,193 @@
package v3
import (
"runtime/debug"
"sync"
"testing"
types "github.com/prysmaticlabs/eth2-types"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_SlotDataRace(t *testing.T) {
headState, err := InitializeFromProto(&ethpb.BeaconStateBellatrix{Slot: 1})
require.NoError(t, err)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
require.NoError(t, headState.SetSlot(0))
wg.Done()
}()
go func() {
headState.Slot()
wg.Done()
}()
wg.Wait()
}
func TestNilState_NoPanic(t *testing.T) {
var st *BeaconState
defer func() {
if r := recover(); r != nil {
t.Errorf("Method panicked when it was not supposed to: %v\n%v\n", r, string(debug.Stack()))
}
}()
// retrieve elements from nil state
_ = st.GenesisTime()
_ = st.GenesisValidatorRoot()
_ = st.GenesisUnixTime()
_ = st.GenesisValidatorRoot()
_ = st.Slot()
_ = st.Fork()
_ = st.LatestBlockHeader()
_ = st.ParentRoot()
_ = st.BlockRoots()
_, err := st.BlockRootAtIndex(0)
_ = err
_ = st.StateRoots()
_ = st.HistoricalRoots()
_ = st.Eth1Data()
_ = st.Eth1DataVotes()
_ = st.Eth1DepositIndex()
_, err = st.ValidatorAtIndex(0)
_ = err
_, err = st.ValidatorAtIndexReadOnly(0)
_ = err
_, _ = st.ValidatorIndexByPubkey([fieldparams.BLSPubkeyLength]byte{})
_ = st.PubkeyAtIndex(0)
_ = st.NumValidators()
_ = st.Balances()
_, err = st.BalanceAtIndex(0)
_ = err
_ = st.BalancesLength()
_ = st.RandaoMixes()
_, err = st.RandaoMixAtIndex(0)
_ = err
_ = st.RandaoMixesLength()
_ = st.Slashings()
_, err = st.CurrentEpochParticipation()
_ = err
_, err = st.PreviousEpochParticipation()
_ = err
_ = st.JustificationBits()
_ = st.PreviousJustifiedCheckpoint()
_ = st.CurrentJustifiedCheckpoint()
_ = st.FinalizedCheckpoint()
_, err = st.CurrentEpochParticipation()
_ = err
_, err = st.PreviousEpochParticipation()
_ = err
_, err = st.InactivityScores()
_ = err
_, err = st.CurrentSyncCommittee()
_ = err
_, err = st.NextSyncCommittee()
_ = err
}
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
keyCreator := func(input []byte) [fieldparams.BLSPubkeyLength]byte {
nKey := [fieldparams.BLSPubkeyLength]byte{}
copy(nKey[:1], input)
return nKey
}
tests := []struct {
name string
modifyFunc func(b *BeaconState, k [fieldparams.BLSPubkeyLength]byte)
exists bool
expectedIdx types.ValidatorIndex
largestIdxInSet types.ValidatorIndex
}{
{
name: "retrieve validator",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: true,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators from the start",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
},
exists: true,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: true,
expectedIdx: 2,
},
{
name: "retrieve validator with multiple validators from the start with shared state",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
_ = b.Copy()
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
},
exists: true,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators with shared state",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
key2 := keyCreator([]byte{'D'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key2[:]}))
n := b.Copy()
// Append to another state
assert.NoError(t, n.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: false,
expectedIdx: 0,
},
{
name: "retrieve validator with multiple validators with shared state at boundary",
modifyFunc: func(b *BeaconState, key [fieldparams.BLSPubkeyLength]byte) {
key1 := keyCreator([]byte{'C'})
assert.NoError(t, b.AppendValidator(&ethpb.Validator{PublicKey: key1[:]}))
n := b.Copy()
// Append to another state
assert.NoError(t, n.AppendValidator(&ethpb.Validator{PublicKey: key[:]}))
},
exists: false,
expectedIdx: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
nKey := keyCreator([]byte{'A'})
tt.modifyFunc(s, nKey)
idx, ok := s.ValidatorIndexByPubkey(nKey)
assert.Equal(t, tt.exists, ok)
assert.Equal(t, tt.expectedIdx, idx)
})
}
}

View File

@@ -0,0 +1,329 @@
package v3
import (
"fmt"
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// ValidatorIndexOutOfRangeError represents an error scenario where a validator does not exist
// at a given index in the validator's array.
type ValidatorIndexOutOfRangeError struct {
message string
}
var (
// ErrNilValidatorsInState returns when accessing validators in the state while the state has a
// nil slice for the validators field.
ErrNilValidatorsInState = errors.New("state has nil validator slice")
)
// NewValidatorIndexOutOfRangeError creates a new error instance.
func NewValidatorIndexOutOfRangeError(index types.ValidatorIndex) ValidatorIndexOutOfRangeError {
return ValidatorIndexOutOfRangeError{
message: fmt.Sprintf("index %d out of range", index),
}
}
// Error returns the underlying error message.
func (e *ValidatorIndexOutOfRangeError) Error() string {
return e.message
}
// Validators participating in consensus on the beacon chain.
func (b *BeaconState) Validators() []*ethpb.Validator {
if !b.hasInnerState() {
return nil
}
if b.state.Validators == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.validators()
}
// validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) validators() []*ethpb.Validator {
if !b.hasInnerState() {
return nil
}
if b.state.Validators == nil {
return nil
}
res := make([]*ethpb.Validator, len(b.state.Validators))
for i := 0; i < len(res); i++ {
val := b.state.Validators[i]
if val == nil {
continue
}
res[i] = ethpb.CopyValidator(val)
}
return res
}
// references of validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState. This does not
// copy fully and instead just copies the reference.
func (b *BeaconState) validatorsReferences() []*ethpb.Validator {
if !b.hasInnerState() {
return nil
}
if b.state.Validators == nil {
return nil
}
res := make([]*ethpb.Validator, len(b.state.Validators))
for i := 0; i < len(res); i++ {
validator := b.state.Validators[i]
if validator == nil {
continue
}
// copy validator reference instead.
res[i] = validator
}
return res
}
// ValidatorAtIndex is the validator at the provided index.
func (b *BeaconState) ValidatorAtIndex(idx types.ValidatorIndex) (*ethpb.Validator, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.Validators == nil {
return &ethpb.Validator{}, nil
}
if uint64(len(b.state.Validators)) <= uint64(idx) {
e := NewValidatorIndexOutOfRangeError(idx)
return nil, &e
}
b.lock.RLock()
defer b.lock.RUnlock()
val := b.state.Validators[idx]
return ethpb.CopyValidator(val), nil
}
// ValidatorAtIndexReadOnly is the validator at the provided index. This method
// doesn't clone the validator.
func (b *BeaconState) ValidatorAtIndexReadOnly(idx types.ValidatorIndex) (state.ReadOnlyValidator, error) {
if !b.hasInnerState() {
return nil, ErrNilInnerState
}
if b.state.Validators == nil {
return nil, ErrNilValidatorsInState
}
if uint64(len(b.state.Validators)) <= uint64(idx) {
e := NewValidatorIndexOutOfRangeError(idx)
return nil, &e
}
b.lock.RLock()
defer b.lock.RUnlock()
return v1.NewValidator(b.state.Validators[idx])
}
// ValidatorIndexByPubkey returns a given validator by its 48-byte public key.
func (b *BeaconState) ValidatorIndexByPubkey(key [fieldparams.BLSPubkeyLength]byte) (types.ValidatorIndex, bool) {
if b == nil || b.valMapHandler == nil || b.valMapHandler.IsNil() {
return 0, false
}
b.lock.RLock()
defer b.lock.RUnlock()
numOfVals := len(b.state.Validators)
idx, ok := b.valMapHandler.Get(key)
if ok && numOfVals <= int(idx) {
return types.ValidatorIndex(0), false
}
return idx, ok
}
// PubkeyAtIndex returns the pubkey at the given
// validator index.
func (b *BeaconState) PubkeyAtIndex(idx types.ValidatorIndex) [fieldparams.BLSPubkeyLength]byte {
if !b.hasInnerState() {
return [fieldparams.BLSPubkeyLength]byte{}
}
if uint64(idx) >= uint64(len(b.state.Validators)) {
return [fieldparams.BLSPubkeyLength]byte{}
}
b.lock.RLock()
defer b.lock.RUnlock()
if b.state.Validators[idx] == nil {
return [fieldparams.BLSPubkeyLength]byte{}
}
return bytesutil.ToBytes48(b.state.Validators[idx].PublicKey)
}
// NumValidators returns the size of the validator registry.
func (b *BeaconState) NumValidators() int {
if !b.hasInnerState() {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return len(b.state.Validators)
}
// ReadFromEveryValidator reads values from every validator and applies it to the provided function.
// Warning: This method is potentially unsafe, as it exposes the actual validator registry.
func (b *BeaconState) ReadFromEveryValidator(f func(idx int, val state.ReadOnlyValidator) error) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
if b.state.Validators == nil {
return errors.New("nil validators in state")
}
b.lock.RLock()
validators := b.state.Validators
b.lock.RUnlock()
for i, v := range validators {
v, err := v1.NewValidator(v)
if err != nil {
return err
}
if err := f(i, v); err != nil {
return err
}
}
return nil
}
// Balances of validators participating in consensus on the beacon chain.
func (b *BeaconState) Balances() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Balances == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.balances()
}
// balances of validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) balances() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Balances == nil {
return nil
}
res := make([]uint64, len(b.state.Balances))
copy(res, b.state.Balances)
return res
}
// BalanceAtIndex of validator with the provided index.
func (b *BeaconState) BalanceAtIndex(idx types.ValidatorIndex) (uint64, error) {
if !b.hasInnerState() {
return 0, ErrNilInnerState
}
if b.state.Balances == nil {
return 0, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
if uint64(len(b.state.Balances)) <= uint64(idx) {
return 0, fmt.Errorf("index of %d does not exist", idx)
}
return b.state.Balances[idx], nil
}
// BalancesLength returns the length of the balances slice.
func (b *BeaconState) BalancesLength() int {
if !b.hasInnerState() {
return 0
}
if b.state.Balances == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.balancesLength()
}
// Slashings of validators on the beacon chain.
func (b *BeaconState) Slashings() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Slashings == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.slashings()
}
// slashings of validators on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) slashings() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.Slashings == nil {
return nil
}
res := make([]uint64, len(b.state.Slashings))
copy(res, b.state.Slashings)
return res
}
// inactivityScores of validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) inactivityScores() []uint64 {
if !b.hasInnerState() {
return nil
}
if b.state.InactivityScores == nil {
return nil
}
res := make([]uint64, len(b.state.InactivityScores))
copy(res, b.state.InactivityScores)
return res
}
// InactivityScores of validators participating in consensus on the beacon chain.
func (b *BeaconState) InactivityScores() ([]uint64, error) {
if !b.hasInnerState() {
return nil, nil
}
if b.state.InactivityScores == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.inactivityScores(), nil
}

View File

@@ -0,0 +1,20 @@
package v3_test
import (
"testing"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
st, err := v1.InitializeFromProtoUnsafe(&ethpb.BeaconState{
Validators: nil,
})
require.NoError(t, err)
_, err = st.ValidatorAtIndexReadOnly(0)
assert.Equal(t, v1.ErrNilValidatorsInState, err)
}

View File

@@ -0,0 +1,83 @@
package v3
import (
"context"
"encoding/binary"
"github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/fieldtrie"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
)
const (
finalizedRootIndex = uint64(105) // Precomputed value.
)
// FinalizedRootGeneralizedIndex for the beacon state.
func FinalizedRootGeneralizedIndex() uint64 {
return finalizedRootIndex
}
// CurrentSyncCommitteeGeneralizedIndex for the beacon state.
func CurrentSyncCommitteeGeneralizedIndex() uint64 {
return uint64(currentSyncCommittee)
}
// NextSyncCommitteeGeneralizedIndex for the beacon state.
func NextSyncCommitteeGeneralizedIndex() uint64 {
return uint64(nextSyncCommittee)
}
// CurrentSyncCommitteeProof from the state's Merkle trie representation.
func (b *BeaconState) CurrentSyncCommitteeProof(ctx context.Context) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
// In case the Merkle layers of the trie are not populated, we need
// to perform some initialization.
if err := b.initializeMerkleLayers(ctx); err != nil {
return nil, err
}
// Our beacon state uses a "dirty" fields pattern which requires us to
// recompute branches of the Merkle layers that are marked as dirty.
if err := b.recomputeDirtyFields(ctx); err != nil {
return nil, err
}
return fieldtrie.ProofFromMerkleLayers(b.merkleLayers, currentSyncCommittee), nil
}
// NextSyncCommitteeProof from the state's Merkle trie representation.
func (b *BeaconState) NextSyncCommitteeProof(ctx context.Context) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
if err := b.initializeMerkleLayers(ctx); err != nil {
return nil, err
}
if err := b.recomputeDirtyFields(ctx); err != nil {
return nil, err
}
return fieldtrie.ProofFromMerkleLayers(b.merkleLayers, nextSyncCommittee), nil
}
// FinalizedRootProof crafts a Merkle proof for the finalized root
// contained within the finalized checkpoint of a beacon state.
func (b *BeaconState) FinalizedRootProof(ctx context.Context) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
if err := b.initializeMerkleLayers(ctx); err != nil {
return nil, err
}
if err := b.recomputeDirtyFields(ctx); err != nil {
return nil, err
}
cpt := b.state.FinalizedCheckpoint
// The epoch field of a finalized checkpoint is the neighbor
// index of the finalized root field in its Merkle tree representation
// of the checkpoint. This neighbor is the first element added to the proof.
epochBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(epochBuf, uint64(cpt.Epoch))
epochRoot := bytesutil.ToBytes32(epochBuf)
proof := make([][]byte, 0)
proof = append(proof, epochRoot[:])
branch := fieldtrie.ProofFromMerkleLayers(b.merkleLayers, finalizedCheckpoint)
proof = append(proof, branch...)
return proof, nil
}

Some files were not shown because too many files have changed in this diff Show More