mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 22:07:59 -05:00
Compare commits
24 Commits
inStateUpg
...
v3.2.1-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cf1ca8b3c | ||
|
|
4dec2ebcf6 | ||
|
|
5e89dac9f0 | ||
|
|
2579242669 | ||
|
|
529d48d0c5 | ||
|
|
b8bbeae740 | ||
|
|
59cb9f0dd7 | ||
|
|
b10e59a144 | ||
|
|
c070283bf1 | ||
|
|
3f67818a65 | ||
|
|
847835534d | ||
|
|
c2194c57b7 | ||
|
|
bd83018e76 | ||
|
|
39114e6817 | ||
|
|
67a9701e06 | ||
|
|
5dc7741b30 | ||
|
|
53a135adbd | ||
|
|
950d0c0282 | ||
|
|
e8c25effb0 | ||
|
|
cc454bb42c | ||
|
|
9529c73ff1 | ||
|
|
eca129d8ff | ||
|
|
53a4939da4 | ||
|
|
efc1e06c6b |
26
WORKSPACE
26
WORKSPACE
@@ -124,28 +124,28 @@ load(
|
||||
|
||||
container_pull(
|
||||
name = "cc_image_base",
|
||||
digest = "sha256:2c4bb6b7236db0a55ec54ba8845e4031f5db2be957ac61867872bf42e56c4deb",
|
||||
digest = "sha256:41036fc7ed8df0f6addc18484cef0c94a85867508967789f947e11ffd5ff0cc8",
|
||||
registry = "gcr.io",
|
||||
repository = "distroless/cc",
|
||||
)
|
||||
|
||||
container_pull(
|
||||
name = "cc_debug_image_base",
|
||||
digest = "sha256:3680c61e81f68fc00bfb5e1ec65e8e678aaafa7c5f056bc2681c29527ebbb30c",
|
||||
digest = "sha256:6865ad48467c89c3c3524d4c426f52ad12d9ab7dec31fad31fae69da40eb6445",
|
||||
registry = "gcr.io",
|
||||
repository = "distroless/cc",
|
||||
)
|
||||
|
||||
container_pull(
|
||||
name = "go_image_base",
|
||||
digest = "sha256:ba7a315f86771332e76fa9c3d423ecfdbb8265879c6f1c264d6fff7d4fa460a4",
|
||||
digest = "sha256:b9b124f955961599e72630654107a0cf04e08e6fa777fa250b8f840728abd770",
|
||||
registry = "gcr.io",
|
||||
repository = "distroless/base",
|
||||
)
|
||||
|
||||
container_pull(
|
||||
name = "go_debug_image_base",
|
||||
digest = "sha256:efd8711717d9e9b5d0dbb20ea10876dab0609c923bc05321b912f9239090ca80",
|
||||
digest = "sha256:65668d2b78d25df3d8ccf5a778d017fcaba513b52078c700083eaeef212b9979",
|
||||
registry = "gcr.io",
|
||||
repository = "distroless/base",
|
||||
)
|
||||
@@ -188,7 +188,9 @@ filegroup(
|
||||
url = "https://github.com/eth-clients/slashing-protection-interchange-tests/archive/b8413ca42dc92308019d0d4db52c87e9e125c4e9.tar.gz",
|
||||
)
|
||||
|
||||
consensus_spec_version = "v1.3.0-rc.1"
|
||||
consensus_spec_version = "v1.3.0-rc.2"
|
||||
|
||||
consensus_spec_test_version = "v1.3.0-rc.2-hotfix"
|
||||
|
||||
bls_test_version = "v0.1.1"
|
||||
|
||||
@@ -204,8 +206,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "3d6fadb64674eb64a84fae6c2efa9949231ea91e7cb74ada9214097323e86569",
|
||||
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_version,
|
||||
sha256 = "5c90f42ff30857def5ae0952904c5cad6dbc16310a86d3a0914f37b557920779",
|
||||
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_test_version,
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -220,8 +222,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "54ffbcab1e77316a280e6f5a64c6ed62351e8f5678e6fa49340e49b9b5575e8e",
|
||||
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_version,
|
||||
sha256 = "8fbec5f1fa30fb1194b42230bf75f05690a72cf3c56af5b5ac8aafb94c60fc19",
|
||||
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_test_version,
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -236,8 +238,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "bb06d30ca533dc97d45f2367916ba9ff1b5af52f08a9d8c33bb7b1a61254094e",
|
||||
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_version,
|
||||
sha256 = "cb22875dbb67b12f577d12a5aa8074de4c8bd31d74fff45cc110be515c1df847",
|
||||
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_test_version,
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -251,7 +253,7 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "9d22246c00ec3907ef8dc3ddccdfe6f7153ce46df73deee0a0176fe7e4aa1126",
|
||||
sha256 = "c728947ccf8b628691c76cc92f2f59c02f1c7882a463f26365d32c098bb946b8",
|
||||
strip_prefix = "consensus-specs-" + consensus_spec_version[1:],
|
||||
url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version,
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ go_library(
|
||||
"chain_info.go",
|
||||
"error.go",
|
||||
"execution_engine.go",
|
||||
"forkchoice_update_execution.go",
|
||||
"head.go",
|
||||
"head_sync_committee_info.go",
|
||||
"init_sync_process_block.go",
|
||||
@@ -104,6 +105,7 @@ go_test(
|
||||
"chain_info_test.go",
|
||||
"checktags_test.go",
|
||||
"execution_engine_test.go",
|
||||
"forkchoice_update_execution_test.go",
|
||||
"head_sync_committee_info_test.go",
|
||||
"head_test.go",
|
||||
"init_test.go",
|
||||
|
||||
@@ -54,6 +54,7 @@ type HeadFetcher interface {
|
||||
HeadRoot(ctx context.Context) ([]byte, error)
|
||||
HeadBlock(ctx context.Context) (interfaces.SignedBeaconBlock, error)
|
||||
HeadState(ctx context.Context) (state.BeaconState, error)
|
||||
HeadStateReadOnly(ctx context.Context) (state.ReadOnlyBeaconState, error)
|
||||
HeadValidatorsIndices(ctx context.Context, epoch primitives.Epoch) ([]primitives.ValidatorIndex, error)
|
||||
HeadGenesisValidatorsRoot() [32]byte
|
||||
HeadETH1Data() *ethpb.Eth1Data
|
||||
@@ -187,6 +188,28 @@ func (s *Service) HeadState(ctx context.Context) (state.BeaconState, error) {
|
||||
return s.cfg.StateGen.StateByRoot(ctx, s.headRoot())
|
||||
}
|
||||
|
||||
// HeadStateReadOnly returns the read only head state of the chain.
|
||||
// If the head is nil from service struct, it will attempt to get the
|
||||
// head state from DB. Any callers of this method MUST only use the
|
||||
// state instance to read fields from the state. Any type assertions back
|
||||
// to the concrete type and subsequent use of it could lead to corruption
|
||||
// of the state.
|
||||
func (s *Service) HeadStateReadOnly(ctx context.Context) (state.ReadOnlyBeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.HeadStateReadOnly")
|
||||
defer span.End()
|
||||
s.headLock.RLock()
|
||||
defer s.headLock.RUnlock()
|
||||
|
||||
ok := s.hasHeadState()
|
||||
span.AddAttributes(trace.BoolAttribute("cache_hit", ok))
|
||||
|
||||
if ok {
|
||||
return s.headStateReadOnly(ctx), nil
|
||||
}
|
||||
|
||||
return s.cfg.StateGen.StateByRoot(ctx, s.headRoot())
|
||||
}
|
||||
|
||||
// HeadValidatorsIndices returns a list of active validator indices from the head view of a given epoch.
|
||||
func (s *Service) HeadValidatorsIndices(ctx context.Context, epoch primitives.Epoch) ([]primitives.ValidatorIndex, error) {
|
||||
s.headLock.RLock()
|
||||
|
||||
71
beacon-chain/blockchain/forkchoice_update_execution.go
Normal file
71
beacon-chain/blockchain/forkchoice_update_execution.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
|
||||
)
|
||||
|
||||
func (s *Service) isNewProposer() bool {
|
||||
_, _, ok := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(s.CurrentSlot()+1, [32]byte{} /* root */)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (s *Service) isNewHead(r [32]byte) bool {
|
||||
s.headLock.RLock()
|
||||
defer s.headLock.RUnlock()
|
||||
|
||||
currentHeadRoot := s.originBlockRoot
|
||||
if s.head != nil {
|
||||
currentHeadRoot = s.headRoot()
|
||||
}
|
||||
|
||||
return r != currentHeadRoot || r == [32]byte{}
|
||||
}
|
||||
|
||||
func (s *Service) getStateAndBlock(ctx context.Context, r [32]byte) (state.BeaconState, interfaces.SignedBeaconBlock, error) {
|
||||
if !s.hasBlockInInitSyncOrDB(ctx, r) {
|
||||
return nil, nil, errors.New("block does not exist")
|
||||
}
|
||||
newHeadBlock, err := s.getBlock(ctx, r)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
headState, err := s.cfg.StateGen.StateByRoot(ctx, r)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return headState, newHeadBlock, nil
|
||||
}
|
||||
|
||||
func (s *Service) forkchoiceUpdateWithExecution(ctx context.Context, newHeadRoot [32]byte) error {
|
||||
isNewHead := s.isNewHead(newHeadRoot)
|
||||
if !isNewHead && !s.isNewProposer() {
|
||||
return nil
|
||||
}
|
||||
|
||||
headState, headBlock, err := s.getStateAndBlock(ctx, newHeadRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get forkchoice update argument")
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = s.notifyForkchoiceUpdate(ctx, ¬ifyForkchoiceUpdateArg{
|
||||
headState: headState,
|
||||
headRoot: newHeadRoot,
|
||||
headBlock: headBlock.Block(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isNewHead {
|
||||
if err := s.saveHead(ctx, newHeadRoot, headBlock, headState); err != nil {
|
||||
log.WithError(err).Error("could not save head")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
190
beacon-chain/blockchain/forkchoice_update_execution_test.go
Normal file
190
beacon-chain/blockchain/forkchoice_update_execution_test.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/cache"
|
||||
testDB "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
|
||||
mockExecution "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/util"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func TestService_isNewProposer(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := setupBeaconChain(t, beaconDB)
|
||||
require.Equal(t, false, service.isNewProposer())
|
||||
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(service.CurrentSlot()+1, 0, [8]byte{}, [32]byte{} /* root */)
|
||||
require.Equal(t, true, service.isNewProposer())
|
||||
}
|
||||
|
||||
func TestService_isNewHead(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := setupBeaconChain(t, beaconDB)
|
||||
require.Equal(t, true, service.isNewHead([32]byte{}))
|
||||
|
||||
service.head = &head{root: [32]byte{1}}
|
||||
require.Equal(t, true, service.isNewHead([32]byte{2}))
|
||||
require.Equal(t, false, service.isNewHead([32]byte{1}))
|
||||
|
||||
// Nil head should use origin root
|
||||
service.head = nil
|
||||
service.originBlockRoot = [32]byte{3}
|
||||
require.Equal(t, true, service.isNewHead([32]byte{2}))
|
||||
require.Equal(t, false, service.isNewHead([32]byte{3}))
|
||||
}
|
||||
|
||||
func TestService_getHeadStateAndBlock(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := setupBeaconChain(t, beaconDB)
|
||||
_, _, err := service.getStateAndBlock(context.Background(), [32]byte{})
|
||||
require.ErrorContains(t, "block does not exist", err)
|
||||
|
||||
blk, err := blocks.NewSignedBeaconBlock(util.HydrateSignedBeaconBlock(ðpb.SignedBeaconBlock{Signature: []byte{1}}))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveBlock(context.Background(), blk))
|
||||
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
r, err := blk.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(context.Background(), st, r))
|
||||
|
||||
gotState, err := service.cfg.BeaconDB.State(context.Background(), r)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, st.ToProto(), gotState.ToProto())
|
||||
|
||||
gotBlk, err := service.cfg.BeaconDB.Block(context.Background(), r)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, blk, gotBlk)
|
||||
}
|
||||
|
||||
func TestService_forkchoiceUpdateWithExecution_exceptionalCases(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctx := context.Background()
|
||||
opts := testServiceOptsWithDB(t)
|
||||
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
service.cfg.ProposerSlotIndexCache = cache.NewProposerPayloadIDsCache()
|
||||
require.NoError(t, service.forkchoiceUpdateWithExecution(ctx, service.headRoot()))
|
||||
hookErr := "could not notify forkchoice update"
|
||||
invalidStateErr := "could not get state summary: could not find block in DB"
|
||||
require.LogsDoNotContain(t, hook, invalidStateErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
gb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.saveInitSyncBlock(ctx, [32]byte{'a'}, gb))
|
||||
require.NoError(t, service.forkchoiceUpdateWithExecution(ctx, [32]byte{'a'}))
|
||||
require.LogsContain(t, hook, invalidStateErr)
|
||||
|
||||
hook.Reset()
|
||||
service.head = &head{
|
||||
root: [32]byte{'a'},
|
||||
block: nil, /* should not panic if notify head uses correct head */
|
||||
}
|
||||
|
||||
// Block in Cache
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = 2
|
||||
wsb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
r1, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.saveInitSyncBlock(ctx, r1, wsb))
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
service.head = &head{
|
||||
root: r1,
|
||||
block: wsb,
|
||||
state: st,
|
||||
}
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(2, 1, [8]byte{1}, [32]byte{2})
|
||||
require.NoError(t, service.forkchoiceUpdateWithExecution(ctx, r1))
|
||||
require.LogsDoNotContain(t, hook, invalidStateErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
|
||||
// Block in DB
|
||||
b = util.NewBeaconBlock()
|
||||
b.Block.Slot = 3
|
||||
util.SaveBlock(t, ctx, service.cfg.BeaconDB, b)
|
||||
r1, err = b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
st, _ = util.DeterministicGenesisState(t, 1)
|
||||
service.head = &head{
|
||||
root: r1,
|
||||
block: wsb,
|
||||
state: st,
|
||||
}
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(2, 1, [8]byte{1}, [32]byte{2})
|
||||
require.NoError(t, service.forkchoiceUpdateWithExecution(ctx, r1))
|
||||
require.LogsDoNotContain(t, hook, invalidStateErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
vId, payloadID, has := service.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(2, [32]byte{2})
|
||||
require.Equal(t, true, has)
|
||||
require.Equal(t, primitives.ValidatorIndex(1), vId)
|
||||
require.Equal(t, [8]byte{1}, payloadID)
|
||||
|
||||
// Test zero headRoot returns immediately.
|
||||
headRoot := service.headRoot()
|
||||
require.NoError(t, service.forkchoiceUpdateWithExecution(ctx, [32]byte{}))
|
||||
require.Equal(t, service.headRoot(), headRoot)
|
||||
}
|
||||
|
||||
func TestService_forkchoiceUpdateWithExecution_SameHeadRootNewProposer(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
altairBlk := util.SaveBlock(t, ctx, beaconDB, util.NewBeaconBlockAltair())
|
||||
altairBlkRoot, err := altairBlk.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
bellatrixBlk := util.SaveBlock(t, ctx, beaconDB, util.NewBeaconBlockBellatrix())
|
||||
bellatrixBlkRoot, err := bellatrixBlk.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
fcs := doublylinkedtree.New()
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB, fcs)),
|
||||
WithForkChoiceStore(fcs),
|
||||
WithProposerIdsCache(cache.NewProposerPayloadIDsCache()),
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
st, _ := util.DeterministicGenesisState(t, 10)
|
||||
service.head = &head{
|
||||
state: st,
|
||||
}
|
||||
|
||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 1, altairBlkRoot, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 2, bellatrixBlkRoot, altairBlkRoot, params.BeaconConfig().ZeroHash, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
service.cfg.ExecutionEngineCaller = &mockExecution.EngineClient{}
|
||||
require.NoError(t, beaconDB.SaveState(ctx, st, bellatrixBlkRoot))
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, bellatrixBlkRoot))
|
||||
sb, err := blocks.NewSignedBeaconBlock(util.HydrateSignedBeaconBlockBellatrix(ðpb.SignedBeaconBlockBellatrix{}))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, sb))
|
||||
r, err := sb.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set head to be the same but proposing next slot
|
||||
service.head.root = r
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(service.CurrentSlot()+1, 0, [8]byte{}, [32]byte{} /* root */)
|
||||
require.NoError(t, service.forkchoiceUpdateWithExecution(ctx, r))
|
||||
|
||||
}
|
||||
@@ -64,17 +64,10 @@ func (s *Service) saveHead(ctx context.Context, newHeadRoot [32]byte, headBlock
|
||||
defer span.End()
|
||||
|
||||
// Do nothing if head hasn't changed.
|
||||
var oldHeadRoot [32]byte
|
||||
s.headLock.RLock()
|
||||
if s.head == nil {
|
||||
oldHeadRoot = s.originBlockRoot
|
||||
} else {
|
||||
oldHeadRoot = s.head.root
|
||||
}
|
||||
s.headLock.RUnlock()
|
||||
if newHeadRoot == oldHeadRoot {
|
||||
if !s.isNewHead(newHeadRoot) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := blocks.BeaconBlockIsNil(headBlock); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -101,6 +94,11 @@ func (s *Service) saveHead(ctx context.Context, newHeadRoot [32]byte, headBlock
|
||||
newStateRoot := headBlock.Block().StateRoot()
|
||||
|
||||
// A chain re-org occurred, so we fire an event notifying the rest of the services.
|
||||
r, err := s.HeadRoot(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get old head root")
|
||||
}
|
||||
oldHeadRoot := bytesutil.ToBytes32(r)
|
||||
if headBlock.Block().ParentRoot() != oldHeadRoot {
|
||||
commonRoot, forkSlot, err := s.ForkChoicer().CommonAncestor(ctx, oldHeadRoot, newHeadRoot)
|
||||
if err != nil {
|
||||
@@ -276,6 +274,16 @@ func (s *Service) headState(ctx context.Context) state.BeaconState {
|
||||
return s.head.state.Copy()
|
||||
}
|
||||
|
||||
// This returns a read only version of the head state.
|
||||
// It does not perform a copy of the head state.
|
||||
// This is a lock free version.
|
||||
func (s *Service) headStateReadOnly(ctx context.Context) state.ReadOnlyBeaconState {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.headStateReadOnly")
|
||||
defer span.End()
|
||||
|
||||
return s.head.state
|
||||
}
|
||||
|
||||
// This returns the genesis validators root of the head state.
|
||||
// This is a lock free version.
|
||||
func (s *Service) headGenesisValidatorsRoot() [32]byte {
|
||||
|
||||
@@ -232,6 +232,48 @@ func Test_notifyNewHeadEvent(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRetrieveHead_ReadOnly(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := setupBeaconChain(t, beaconDB)
|
||||
|
||||
oldBlock := util.SaveBlock(t, context.Background(), service.cfg.BeaconDB, util.NewBeaconBlock())
|
||||
oldRoot, err := oldBlock.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
service.head = &head{
|
||||
root: oldRoot,
|
||||
block: oldBlock,
|
||||
}
|
||||
|
||||
newHeadSignedBlock := util.NewBeaconBlock()
|
||||
newHeadSignedBlock.Block.Slot = 1
|
||||
newHeadBlock := newHeadSignedBlock.Block
|
||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||
|
||||
wsb := util.SaveBlock(t, context.Background(), service.cfg.BeaconDB, newHeadSignedBlock)
|
||||
newRoot, err := newHeadBlock.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, wsb.Block().Slot()-1, wsb.Block().ParentRoot(), service.cfg.ForkChoiceStore.CachedHeadRoot(), [32]byte{}, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, wsb.Block().Slot(), newRoot, wsb.Block().ParentRoot(), [32]byte{}, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||
headState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, headState.SetSlot(1))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(context.Background(), ðpb.StateSummary{Slot: 1, Root: newRoot[:]}))
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(context.Background(), headState, newRoot))
|
||||
require.NoError(t, service.saveHead(context.Background(), newRoot, wsb, headState))
|
||||
|
||||
rOnlyState, err := service.HeadStateReadOnly(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, rOnlyState, service.head.state, "Head is not the same object")
|
||||
}
|
||||
|
||||
func TestSaveOrphanedAtts(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1/attestation"
|
||||
"github.com/prysmaticlabs/prysm/v3/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
@@ -196,9 +197,25 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Could not update head")
|
||||
}
|
||||
if blockRoot != headRoot {
|
||||
receivedWeight, err := s.ForkChoicer().Weight(blockRoot)
|
||||
if err != nil {
|
||||
log.WithField("root", fmt.Sprintf("%#x", blockRoot)).Warn("could not determine node weight")
|
||||
}
|
||||
headWeight, err := s.ForkChoicer().Weight(headRoot)
|
||||
if err != nil {
|
||||
log.WithField("root", fmt.Sprintf("%#x", headRoot)).Warn("could not determine node weight")
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"receivedRoot": fmt.Sprintf("%#x", blockRoot),
|
||||
"receivedWeight": receivedWeight,
|
||||
"headRoot": fmt.Sprintf("%#x", headRoot),
|
||||
"headWeight": headWeight,
|
||||
}).Debug("Head block is not the received block")
|
||||
}
|
||||
newBlockHeadElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
||||
|
||||
if err := s.notifyEngineIfChangedHead(ctx, headRoot); err != nil {
|
||||
if err := s.forkchoiceUpdateWithExecution(ctx, headRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -477,6 +494,7 @@ func (s *Service) handleEpochBoundary(ctx context.Context, postState state.Beaco
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.handleEpochBoundary")
|
||||
defer span.End()
|
||||
|
||||
var err error
|
||||
if postState.Slot()+1 == s.nextEpochBoundarySlot {
|
||||
copied := postState.Copy()
|
||||
copied, err := transition.ProcessSlots(ctx, copied, copied.Slot()+1)
|
||||
@@ -491,27 +509,27 @@ func (s *Service) handleEpochBoundary(ctx context.Context, postState state.Beaco
|
||||
return err
|
||||
}
|
||||
} else if postState.Slot() >= s.nextEpochBoundarySlot {
|
||||
s.headLock.RLock()
|
||||
st := s.head.state
|
||||
s.headLock.RUnlock()
|
||||
if err := reportEpochMetrics(ctx, postState, st); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var err error
|
||||
s.nextEpochBoundarySlot, err = slots.EpochStart(coreTime.NextEpoch(postState))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update caches at epoch boundary slot.
|
||||
// The following updates have short cut to return nil cheaply if fulfilled during boundary slot - 1.
|
||||
// The following updates have shortcut to return nil cheaply if fulfilled during boundary slot - 1.
|
||||
if err := helpers.UpdateCommitteeCache(ctx, postState, coreTime.CurrentEpoch(postState)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := helpers.UpdateProposerIndicesInCache(ctx, postState); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
headSt, err := s.HeadState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := reportEpochMetrics(ctx, postState, headSt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -163,51 +163,12 @@ func (s *Service) UpdateHead(ctx context.Context) error {
|
||||
}).Debug("Head changed due to attestations")
|
||||
}
|
||||
s.headLock.RUnlock()
|
||||
if err := s.notifyEngineIfChangedHead(ctx, newHeadRoot); err != nil {
|
||||
if err := s.forkchoiceUpdateWithExecution(ctx, newHeadRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// This calls notify Forkchoice Update in the event that the head has changed
|
||||
func (s *Service) notifyEngineIfChangedHead(ctx context.Context, newHeadRoot [32]byte) error {
|
||||
s.headLock.RLock()
|
||||
if newHeadRoot == [32]byte{} || s.headRoot() == newHeadRoot {
|
||||
s.headLock.RUnlock()
|
||||
return nil
|
||||
}
|
||||
s.headLock.RUnlock()
|
||||
|
||||
if !s.hasBlockInInitSyncOrDB(ctx, newHeadRoot) {
|
||||
log.Debug("New head does not exist in DB. Do nothing")
|
||||
return nil // We don't have the block, don't notify the engine and update head.
|
||||
}
|
||||
|
||||
newHeadBlock, err := s.getBlock(ctx, newHeadRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get new head block")
|
||||
return nil
|
||||
}
|
||||
headState, err := s.cfg.StateGen.StateByRoot(ctx, newHeadRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get state from db")
|
||||
return nil
|
||||
}
|
||||
arg := ¬ifyForkchoiceUpdateArg{
|
||||
headState: headState,
|
||||
headRoot: newHeadRoot,
|
||||
headBlock: newHeadBlock.Block(),
|
||||
}
|
||||
_, err = s.notifyForkchoiceUpdate(s.ctx, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.saveHead(ctx, newHeadRoot, newHeadBlock, headState); err != nil {
|
||||
log.WithError(err).Error("could not save head")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// This processes fork choice attestations from the pool to account for validator votes and fork choice.
|
||||
func (s *Service) processAttestations(ctx context.Context) {
|
||||
atts := s.cfg.AttPool.ForkchoiceAttestations()
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/transition"
|
||||
testDB "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
|
||||
@@ -124,77 +123,6 @@ func TestProcessAttestations_Ok(t *testing.T) {
|
||||
require.LogsDoNotContain(t, hook, "Could not process attestation for fork choice")
|
||||
}
|
||||
|
||||
func TestNotifyEngineIfChangedHead(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctx := context.Background()
|
||||
opts := testServiceOptsWithDB(t)
|
||||
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
service.cfg.ProposerSlotIndexCache = cache.NewProposerPayloadIDsCache()
|
||||
require.NoError(t, service.notifyEngineIfChangedHead(ctx, service.headRoot()))
|
||||
hookErr := "could not notify forkchoice update"
|
||||
invalidStateErr := "Could not get state from db"
|
||||
require.LogsDoNotContain(t, hook, invalidStateErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
gb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.saveInitSyncBlock(ctx, [32]byte{'a'}, gb))
|
||||
require.NoError(t, service.notifyEngineIfChangedHead(ctx, [32]byte{'a'}))
|
||||
require.LogsContain(t, hook, invalidStateErr)
|
||||
|
||||
hook.Reset()
|
||||
service.head = &head{
|
||||
root: [32]byte{'a'},
|
||||
block: nil, /* should not panic if notify head uses correct head */
|
||||
}
|
||||
|
||||
// Block in Cache
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = 2
|
||||
wsb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
r1, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.saveInitSyncBlock(ctx, r1, wsb))
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
service.head = &head{
|
||||
root: r1,
|
||||
block: wsb,
|
||||
state: st,
|
||||
}
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(2, 1, [8]byte{1}, [32]byte{2})
|
||||
require.NoError(t, service.notifyEngineIfChangedHead(ctx, r1))
|
||||
require.LogsDoNotContain(t, hook, invalidStateErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
|
||||
// Block in DB
|
||||
b = util.NewBeaconBlock()
|
||||
b.Block.Slot = 3
|
||||
util.SaveBlock(t, ctx, service.cfg.BeaconDB, b)
|
||||
r1, err = b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
st, _ = util.DeterministicGenesisState(t, 1)
|
||||
service.head = &head{
|
||||
root: r1,
|
||||
block: wsb,
|
||||
state: st,
|
||||
}
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(2, 1, [8]byte{1}, [32]byte{2})
|
||||
require.NoError(t, service.notifyEngineIfChangedHead(ctx, r1))
|
||||
require.LogsDoNotContain(t, hook, invalidStateErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
vId, payloadID, has := service.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(2, [32]byte{2})
|
||||
require.Equal(t, true, has)
|
||||
require.Equal(t, primitives.ValidatorIndex(1), vId)
|
||||
require.Equal(t, [8]byte{1}, payloadID)
|
||||
|
||||
// Test zero headRoot returns immediately.
|
||||
headRoot := service.headRoot()
|
||||
require.NoError(t, service.notifyEngineIfChangedHead(ctx, [32]byte{}))
|
||||
require.Equal(t, service.headRoot(), headRoot)
|
||||
}
|
||||
|
||||
func TestService_ProcessAttestationsAndUpdateHead(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
opts := testServiceOptsWithDB(t)
|
||||
|
||||
@@ -95,7 +95,7 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) {
|
||||
boundaryRoots: [][32]byte{},
|
||||
checkpointStateCache: cache.NewCheckpointStateCache(),
|
||||
initSyncBlocks: make(map[[32]byte]interfaces.SignedBeaconBlock),
|
||||
cfg: &config{},
|
||||
cfg: &config{ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache()},
|
||||
}
|
||||
for _, opt := range opts {
|
||||
if err := opt(srv); err != nil {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/prysmaticlabs/prysm/v3/async/event"
|
||||
mock "github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/cache/depositcache"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed"
|
||||
@@ -135,6 +136,7 @@ func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service {
|
||||
WithForkChoiceStore(fc),
|
||||
WithAttestationService(attService),
|
||||
WithStateGen(stateGen),
|
||||
WithProposerIdsCache(cache.NewProposerPayloadIDsCache()),
|
||||
}
|
||||
|
||||
chainService, err := NewService(ctx, opts...)
|
||||
|
||||
@@ -284,6 +284,11 @@ func (s *ChainService) HeadState(context.Context) (state.BeaconState, error) {
|
||||
return s.State, nil
|
||||
}
|
||||
|
||||
// HeadStateReadOnly mocks HeadStateReadOnly method in chain service.
|
||||
func (s *ChainService) HeadStateReadOnly(context.Context) (state.ReadOnlyBeaconState, error) {
|
||||
return s.State, nil
|
||||
}
|
||||
|
||||
// CurrentFork mocks HeadState method in chain service.
|
||||
func (s *ChainService) CurrentFork() *ethpb.Fork {
|
||||
return s.Fork
|
||||
|
||||
@@ -16,8 +16,10 @@ import (
|
||||
type MockBuilderService struct {
|
||||
HasConfigured bool
|
||||
Payload *v1.ExecutionPayload
|
||||
PayloadCapella *v1.ExecutionPayloadCapella
|
||||
ErrSubmitBlindedBlock error
|
||||
Bid *ethpb.SignedBuilderBid
|
||||
BidCapella *ethpb.SignedBuilderBidCapella
|
||||
ErrGetHeader error
|
||||
ErrRegisterValidator error
|
||||
}
|
||||
@@ -29,18 +31,32 @@ func (s *MockBuilderService) Configured() bool {
|
||||
|
||||
// SubmitBlindedBlock for mocking.
|
||||
func (s *MockBuilderService) SubmitBlindedBlock(_ context.Context, _ interfaces.SignedBeaconBlock) (interfaces.ExecutionData, error) {
|
||||
w, err := blocks.WrappedExecutionPayload(s.Payload)
|
||||
if s.Payload != nil {
|
||||
w, err := blocks.WrappedExecutionPayload(s.Payload)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not wrap payload")
|
||||
}
|
||||
return w, s.ErrSubmitBlindedBlock
|
||||
}
|
||||
w, err := blocks.WrappedExecutionPayloadCapella(s.PayloadCapella)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not wrap payload")
|
||||
return nil, errors.Wrap(err, "could not wrap capella payload")
|
||||
}
|
||||
return w, s.ErrSubmitBlindedBlock
|
||||
}
|
||||
|
||||
// GetHeader for mocking.
|
||||
func (s *MockBuilderService) GetHeader(context.Context, primitives.Slot, [32]byte, [48]byte) (builder.SignedBid, error) {
|
||||
w, err := builder.WrappedSignedBuilderBid(s.Bid)
|
||||
if s.Bid != nil {
|
||||
w, err := builder.WrappedSignedBuilderBid(s.Bid)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not wrap bid")
|
||||
}
|
||||
return w, s.ErrGetHeader
|
||||
}
|
||||
w, err := builder.WrappedSignedBuilderBidCapella(s.BidCapella)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not wrap bid")
|
||||
return nil, errors.Wrap(err, "could not wrap capella bid")
|
||||
}
|
||||
return w, s.ErrGetHeader
|
||||
}
|
||||
|
||||
15
beacon-chain/cache/depositsnapshot/BUILD.bazel
vendored
15
beacon-chain/cache/depositsnapshot/BUILD.bazel
vendored
@@ -2,7 +2,20 @@ load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["merkle_tree.go"],
|
||||
srcs = [
|
||||
"deposit_tree.go",
|
||||
"deposit_tree_snapshot.go",
|
||||
"merkle_tree.go",
|
||||
"zerohashes.gen.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/beacon-chain/cache/depositsnapshot",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//container/slice:go_default_library",
|
||||
"//crypto/hash:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//math:go_default_library",
|
||||
"//proto/eth/v1:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
157
beacon-chain/cache/depositsnapshot/deposit_tree.go
vendored
Normal file
157
beacon-chain/cache/depositsnapshot/deposit_tree.go
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
// Package depositsnapshot implements the EIP-4881 standard for minimal sparse Merkle tree.
|
||||
// The format proposed by the EIP allows for the pruning of deposits that are no longer needed to participate fully in consensus.
|
||||
// Full EIP-4881 specification can be found here: https://eips.ethereum.org/EIPS/eip-4881
|
||||
package depositsnapshot
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/math"
|
||||
eth "github.com/prysmaticlabs/prysm/v3/proto/eth/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrEmptyExecutionBlock occurs when the execution block is nil.
|
||||
ErrEmptyExecutionBlock = errors.New("empty execution block")
|
||||
// ErrInvalidSnapshotRoot occurs when the snapshot root does not match the calculated root.
|
||||
ErrInvalidSnapshotRoot = errors.New("snapshot root is invalid")
|
||||
// ErrInvalidMixInLength occurs when the value for mix in length is 0.
|
||||
ErrInvalidMixInLength = errors.New("mixInLength should be greater than 0")
|
||||
// ErrInvalidIndex occurs when the index is less than the number of finalized deposits.
|
||||
ErrInvalidIndex = errors.New("index should be greater than finalizedDeposits - 1")
|
||||
// ErrNoDeposits occurs when the number of deposits is 0.
|
||||
ErrNoDeposits = errors.New("number of deposits should be greater than 0")
|
||||
// ErrNoFinalizedDeposits occurs when the number of finalized deposits is 0.
|
||||
ErrNoFinalizedDeposits = errors.New("number of finalized deposits should be greater than 0")
|
||||
// ErrTooManyDeposits occurs when the number of deposits exceeds the capacity of the tree.
|
||||
ErrTooManyDeposits = errors.New("number of deposits should not be greater than the capacity of the tree")
|
||||
)
|
||||
|
||||
// DepositTree is the Merkle tree representation of deposits.
|
||||
type DepositTree struct {
|
||||
tree MerkleTreeNode
|
||||
mixInLength uint64 // number of deposits in the tree, reference implementation calls this mix_in_length.
|
||||
finalizedExecutionBlock executionBlock
|
||||
}
|
||||
|
||||
type executionBlock struct {
|
||||
Hash [32]byte
|
||||
Depth uint64
|
||||
}
|
||||
|
||||
// New creates an empty deposit tree.
|
||||
//
|
||||
//nolint:unused
|
||||
func New() *DepositTree {
|
||||
var leaves [][32]byte
|
||||
merkle := create(leaves, DepositContractDepth)
|
||||
return &DepositTree{
|
||||
tree: merkle,
|
||||
mixInLength: 0,
|
||||
finalizedExecutionBlock: executionBlock{},
|
||||
}
|
||||
}
|
||||
|
||||
// getSnapshot returns a deposit tree snapshot.
|
||||
//
|
||||
//nolint:unused
|
||||
func (d *DepositTree) getSnapshot() (DepositTreeSnapshot, error) {
|
||||
if d.finalizedExecutionBlock == (executionBlock{}) {
|
||||
return DepositTreeSnapshot{}, ErrEmptyExecutionBlock
|
||||
}
|
||||
var finalized [][32]byte
|
||||
depositCount, _ := d.tree.GetFinalized(finalized)
|
||||
return fromTreeParts(finalized, depositCount, d.finalizedExecutionBlock)
|
||||
}
|
||||
|
||||
// fromSnapshot returns a deposit tree from a deposit tree snapshot.
|
||||
//
|
||||
//nolint:unused
|
||||
func fromSnapshot(snapshot DepositTreeSnapshot) (DepositTree, error) {
|
||||
root, err := snapshot.CalculateRoot()
|
||||
if err != nil {
|
||||
return DepositTree{}, err
|
||||
}
|
||||
if snapshot.depositRoot != root {
|
||||
return DepositTree{}, ErrInvalidSnapshotRoot
|
||||
}
|
||||
if snapshot.depositCount >= math.PowerOf2(uint64(DepositContractDepth)) {
|
||||
return DepositTree{}, ErrTooManyDeposits
|
||||
}
|
||||
tree, err := fromSnapshotParts(snapshot.finalized, snapshot.depositCount, DepositContractDepth)
|
||||
if err != nil {
|
||||
return DepositTree{}, err
|
||||
}
|
||||
if snapshot.depositCount == 0 {
|
||||
return DepositTree{}, ErrNoDeposits
|
||||
}
|
||||
return DepositTree{
|
||||
tree: tree,
|
||||
mixInLength: snapshot.depositCount,
|
||||
finalizedExecutionBlock: snapshot.executionBlock,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// finalize marks a deposit as finalized.
|
||||
//
|
||||
//nolint:unused
|
||||
func (d *DepositTree) finalize(eth1data *eth.Eth1Data, executionBlockHeight uint64) error {
|
||||
var blockHash [32]byte
|
||||
copy(blockHash[:], eth1data.BlockHash)
|
||||
d.finalizedExecutionBlock = executionBlock{
|
||||
Hash: blockHash,
|
||||
Depth: executionBlockHeight,
|
||||
}
|
||||
_, err := d.tree.Finalize(eth1data.DepositCount, DepositContractDepth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getProof returns the Deposit tree proof.
|
||||
//
|
||||
//nolint:unused
|
||||
func (d *DepositTree) getProof(index uint64) ([32]byte, [][32]byte, error) {
|
||||
if d.mixInLength <= 0 {
|
||||
return [32]byte{}, nil, ErrInvalidMixInLength
|
||||
}
|
||||
finalizedDeposits, _ := d.tree.GetFinalized([][32]byte{})
|
||||
if finalizedDeposits == 0 {
|
||||
return [32]byte{}, nil, ErrNoFinalizedDeposits
|
||||
}
|
||||
if finalizedDeposits != 0 {
|
||||
finalizedDeposits = finalizedDeposits - 1
|
||||
}
|
||||
if index <= finalizedDeposits {
|
||||
return [32]byte{}, nil, ErrInvalidIndex
|
||||
}
|
||||
leaf, proof := generateProof(d.tree, index, DepositContractDepth)
|
||||
var mixInLength [32]byte
|
||||
copy(mixInLength[:], bytesutil.Uint64ToBytesLittleEndian32(d.mixInLength))
|
||||
proof = append(proof, mixInLength)
|
||||
return leaf, proof, nil
|
||||
}
|
||||
|
||||
// getRoot returns the root of the deposit tree.
|
||||
//
|
||||
//nolint:unused
|
||||
func (d *DepositTree) getRoot() [32]byte {
|
||||
root := d.tree.GetRoot()
|
||||
return sha256.Sum256(append(root[:], bytesutil.Uint64ToBytesLittleEndian32(d.mixInLength)...))
|
||||
}
|
||||
|
||||
// pushLeaf adds a new leaf to the tree.
|
||||
//
|
||||
//nolint:unused
|
||||
func (d *DepositTree) pushLeaf(leaf [32]byte) error {
|
||||
var err error
|
||||
d.tree, err = d.tree.PushLeaf(leaf, DepositContractDepth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.mixInLength++
|
||||
return nil
|
||||
}
|
||||
62
beacon-chain/cache/depositsnapshot/deposit_tree_snapshot.go
vendored
Normal file
62
beacon-chain/cache/depositsnapshot/deposit_tree_snapshot.go
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
package depositsnapshot
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrZeroIndex occurs when the value of index is 0.
|
||||
ErrZeroIndex = errors.New("index should be greater than 0")
|
||||
)
|
||||
|
||||
// DepositTreeSnapshot represents the data used to create a
|
||||
// deposit tree given a snapshot.
|
||||
//
|
||||
//nolint:unused
|
||||
type DepositTreeSnapshot struct {
|
||||
finalized [][32]byte
|
||||
depositRoot [32]byte
|
||||
depositCount uint64
|
||||
executionBlock executionBlock
|
||||
}
|
||||
|
||||
// CalculateRoot returns the root of a deposit tree snapshot.
|
||||
func (ds *DepositTreeSnapshot) CalculateRoot() ([32]byte, error) {
|
||||
size := ds.depositCount
|
||||
index := len(ds.finalized)
|
||||
root := Zerohashes[0]
|
||||
for i := 0; i < DepositContractDepth; i++ {
|
||||
if (size & 1) == 1 {
|
||||
if index == 0 {
|
||||
return [32]byte{}, ErrZeroIndex
|
||||
}
|
||||
index--
|
||||
root = sha256.Sum256(append(ds.finalized[index][:], root[:]...))
|
||||
} else {
|
||||
root = sha256.Sum256(append(root[:], Zerohashes[i][:]...))
|
||||
}
|
||||
size >>= 1
|
||||
}
|
||||
return sha256.Sum256(append(root[:], bytesutil.Uint64ToBytesLittleEndian(ds.depositCount)...)), nil
|
||||
}
|
||||
|
||||
// fromTreeParts constructs the deposit tree from pre-existing data.
|
||||
//
|
||||
//nolint:unused
|
||||
func fromTreeParts(finalised [][32]byte, depositCount uint64, executionBlock executionBlock) (DepositTreeSnapshot, error) {
|
||||
snapshot := DepositTreeSnapshot{
|
||||
finalized: finalised,
|
||||
depositRoot: Zerohashes[0],
|
||||
depositCount: depositCount,
|
||||
executionBlock: executionBlock,
|
||||
}
|
||||
root, err := snapshot.CalculateRoot()
|
||||
if err != nil {
|
||||
return snapshot, ErrInvalidSnapshotRoot
|
||||
}
|
||||
snapshot.depositRoot = root
|
||||
return snapshot, nil
|
||||
}
|
||||
317
beacon-chain/cache/depositsnapshot/merkle_tree.go
vendored
317
beacon-chain/cache/depositsnapshot/merkle_tree.go
vendored
@@ -1,5 +1,28 @@
|
||||
package depositsnapshot
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/container/slice"
|
||||
"github.com/prysmaticlabs/prysm/v3/crypto/hash"
|
||||
"github.com/prysmaticlabs/prysm/v3/math"
|
||||
)
|
||||
|
||||
const (
|
||||
// DepositContractDepth is the maximum tree depth as defined by EIP-4881.
|
||||
DepositContractDepth = 32
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrFinalizedNodeCannotPushLeaf may occur when attempting to push a leaf to a finalized node. When a node is finalized, it cannot be modified or changed.
|
||||
ErrFinalizedNodeCannotPushLeaf = errors.New("can't push a leaf to a finalized node")
|
||||
// ErrLeafNodeCannotPushLeaf may occur when attempting to push a leaf to a leaf node.
|
||||
ErrLeafNodeCannotPushLeaf = errors.New("can't push a leaf to a leaf node")
|
||||
// ErrZeroLevel occurs when the value of level is 0.
|
||||
ErrZeroLevel = errors.New("level should be greater than 0")
|
||||
// ErrZeroDepth occurs when the value of depth is 0.
|
||||
ErrZeroDepth = errors.New("depth should be greater than 0")
|
||||
)
|
||||
|
||||
// MerkleTreeNode is the interface for a Merkle tree.
|
||||
type MerkleTreeNode interface {
|
||||
// GetRoot returns the root of the Merkle tree.
|
||||
@@ -7,9 +30,295 @@ type MerkleTreeNode interface {
|
||||
// IsFull returns whether there is space left for deposits.
|
||||
IsFull() bool
|
||||
// Finalize marks deposits of the Merkle tree as finalized.
|
||||
Finalize(deposits uint, depth uint) MerkleTreeNode
|
||||
// GetFinalized returns a list of hashes of all the finalized nodes and the number of deposits.
|
||||
GetFinalized(result [][32]byte) ([][32]byte, uint)
|
||||
Finalize(depositsToFinalize uint64, depth uint64) (MerkleTreeNode, error)
|
||||
// GetFinalized returns the number of deposits and a list of hashes of all the finalized nodes.
|
||||
GetFinalized(result [][32]byte) (uint64, [][32]byte)
|
||||
// PushLeaf adds a new leaf node at the next available Zero node.
|
||||
PushLeaf(leaf [32]byte, deposits uint, depth uint) MerkleTreeNode
|
||||
PushLeaf(leaf [32]byte, depth uint64) (MerkleTreeNode, error)
|
||||
|
||||
// Right represents the right child of a node.
|
||||
Right() MerkleTreeNode
|
||||
// Left represents the left child of a node.
|
||||
Left() MerkleTreeNode
|
||||
}
|
||||
|
||||
// create builds a new merkle tree
|
||||
func create(leaves [][32]byte, depth uint64) MerkleTreeNode {
|
||||
length := uint64(len(leaves))
|
||||
if length == 0 {
|
||||
return &ZeroNode{depth: depth}
|
||||
}
|
||||
if depth == 0 {
|
||||
return &LeafNode{hash: leaves[0]}
|
||||
}
|
||||
split := math.Min(math.PowerOf2(depth-1), length)
|
||||
left := create(leaves[0:split], depth-1)
|
||||
right := create(leaves[split:], depth-1)
|
||||
return &InnerNode{left: left, right: right}
|
||||
}
|
||||
|
||||
// fromSnapshotParts creates a new Merkle tree from a list of finalized leaves, number of deposits and specified depth.
|
||||
//
|
||||
//nolint:unused
|
||||
func fromSnapshotParts(finalized [][32]byte, deposits uint64, level uint64) (_ MerkleTreeNode, err error) {
|
||||
if len(finalized) < 1 || deposits == 0 {
|
||||
return &ZeroNode{
|
||||
depth: level,
|
||||
}, nil
|
||||
}
|
||||
if deposits == math.PowerOf2(level) {
|
||||
return &FinalizedNode{
|
||||
depositCount: deposits,
|
||||
hash: finalized[0],
|
||||
}, nil
|
||||
}
|
||||
if level == 0 {
|
||||
return &ZeroNode{}, ErrZeroLevel
|
||||
}
|
||||
node := InnerNode{}
|
||||
if leftSubtree := math.PowerOf2(level - 1); deposits <= leftSubtree {
|
||||
node.left, err = fromSnapshotParts(finalized, deposits, level-1)
|
||||
if err != nil {
|
||||
return &ZeroNode{}, err
|
||||
}
|
||||
node.right = &ZeroNode{depth: level - 1}
|
||||
} else {
|
||||
node.left = &FinalizedNode{
|
||||
depositCount: leftSubtree,
|
||||
hash: finalized[0],
|
||||
}
|
||||
node.right, err = fromSnapshotParts(finalized[1:], deposits-leftSubtree, level-1)
|
||||
if err != nil {
|
||||
return &ZeroNode{}, err
|
||||
}
|
||||
}
|
||||
return &node, nil
|
||||
}
|
||||
|
||||
// generateProof returns a merkle proof and root
|
||||
//
|
||||
//nolint:unused
|
||||
func generateProof(tree MerkleTreeNode, index uint64, depth uint64) ([32]byte, [][32]byte) {
|
||||
var proof [][32]byte
|
||||
node := tree
|
||||
for depth > 0 {
|
||||
ithBit := (index >> (depth - 1)) & 0x1
|
||||
if ithBit == 1 {
|
||||
proof = append(proof, node.Left().GetRoot())
|
||||
node = node.Right()
|
||||
} else {
|
||||
proof = append(proof, node.Right().GetRoot())
|
||||
node = node.Left()
|
||||
}
|
||||
depth--
|
||||
}
|
||||
proof = slice.Reverse(proof)
|
||||
return node.GetRoot(), proof
|
||||
}
|
||||
|
||||
// FinalizedNode represents a finalized node and satisfies the MerkleTreeNode interface.
|
||||
type FinalizedNode struct {
|
||||
depositCount uint64
|
||||
hash [32]byte
|
||||
}
|
||||
|
||||
// GetRoot returns the root of the Merkle tree.
|
||||
func (f *FinalizedNode) GetRoot() [32]byte {
|
||||
return f.hash
|
||||
}
|
||||
|
||||
// IsFull returns whether there is space left for deposits.
|
||||
// A FinalizedNode will always return true as by definition it
|
||||
// is full and deposits can't be added to it.
|
||||
func (_ *FinalizedNode) IsFull() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Finalize marks deposits of the Merkle tree as finalized.
|
||||
func (f *FinalizedNode) Finalize(depositsToFinalize uint64, depth uint64) (MerkleTreeNode, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// GetFinalized returns a list of hashes of all the finalized nodes and the number of deposits.
|
||||
func (f *FinalizedNode) GetFinalized(result [][32]byte) (uint64, [][32]byte) {
|
||||
return f.depositCount, append(result, f.hash)
|
||||
}
|
||||
|
||||
// PushLeaf adds a new leaf node at the next available zero node.
|
||||
func (_ *FinalizedNode) PushLeaf(_ [32]byte, _ uint64) (MerkleTreeNode, error) {
|
||||
return nil, ErrFinalizedNodeCannotPushLeaf
|
||||
}
|
||||
|
||||
// Right returns nil as a finalized node can't have any children.
|
||||
func (_ *FinalizedNode) Right() MerkleTreeNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Left returns nil as a finalized node can't have any children.
|
||||
func (_ *FinalizedNode) Left() MerkleTreeNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LeafNode represents a leaf node holding a deposit and satisfies the MerkleTreeNode interface.
|
||||
type LeafNode struct {
|
||||
hash [32]byte
|
||||
}
|
||||
|
||||
// GetRoot returns the root of the Merkle tree.
|
||||
func (l *LeafNode) GetRoot() [32]byte {
|
||||
return l.hash
|
||||
}
|
||||
|
||||
// IsFull returns whether there is space left for deposits.
|
||||
// A LeafNode will always return true as it is the last node
|
||||
// in the tree and therefore can't have any deposits added to it.
|
||||
func (_ *LeafNode) IsFull() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Finalize marks deposits of the Merkle tree as finalized.
|
||||
func (l *LeafNode) Finalize(depositsToFinalize uint64, depth uint64) (MerkleTreeNode, error) {
|
||||
return &FinalizedNode{1, l.hash}, nil
|
||||
}
|
||||
|
||||
// GetFinalized returns a list of hashes of all the finalized nodes and the number of deposits.
|
||||
func (_ *LeafNode) GetFinalized(result [][32]byte) (uint64, [][32]byte) {
|
||||
return 0, result
|
||||
}
|
||||
|
||||
// PushLeaf adds a new leaf node at the next available zero node.
|
||||
func (_ *LeafNode) PushLeaf(_ [32]byte, _ uint64) (MerkleTreeNode, error) {
|
||||
return nil, ErrLeafNodeCannotPushLeaf
|
||||
}
|
||||
|
||||
// Right returns nil as a leaf node is the last node and can't have any children.
|
||||
func (_ *LeafNode) Right() MerkleTreeNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Left returns nil as a leaf node is the last node and can't have any children.
|
||||
func (_ *LeafNode) Left() MerkleTreeNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
// InnerNode represents an inner node with two children and satisfies the MerkleTreeNode interface.
|
||||
type InnerNode struct {
|
||||
left, right MerkleTreeNode
|
||||
}
|
||||
|
||||
// GetRoot returns the root of the Merkle tree.
|
||||
func (n *InnerNode) GetRoot() [32]byte {
|
||||
left := n.left.GetRoot()
|
||||
right := n.right.GetRoot()
|
||||
return hash.Hash(append(left[:], right[:]...))
|
||||
}
|
||||
|
||||
// IsFull returns whether there is space left for deposits.
|
||||
func (n *InnerNode) IsFull() bool {
|
||||
return n.right.IsFull()
|
||||
}
|
||||
|
||||
// Finalize marks deposits of the Merkle tree as finalized.
|
||||
func (n *InnerNode) Finalize(depositsToFinalize uint64, depth uint64) (_ MerkleTreeNode, err error) {
|
||||
deposits := math.PowerOf2(depth)
|
||||
if deposits <= depositsToFinalize {
|
||||
return &FinalizedNode{deposits, n.GetRoot()}, nil
|
||||
}
|
||||
if depth == 0 {
|
||||
return &ZeroNode{}, ErrZeroDepth
|
||||
}
|
||||
n.left, err = n.left.Finalize(depositsToFinalize, depth-1)
|
||||
if err != nil {
|
||||
return &ZeroNode{}, err
|
||||
}
|
||||
if depositsToFinalize > deposits/2 {
|
||||
remaining := depositsToFinalize - deposits/2
|
||||
n.right, err = n.right.Finalize(remaining, depth-1)
|
||||
if err != nil {
|
||||
return &ZeroNode{}, err
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// GetFinalized returns a list of hashes of all the finalized nodes and the number of deposits.
|
||||
func (n *InnerNode) GetFinalized(result [][32]byte) (uint64, [][32]byte) {
|
||||
leftDeposits, result := n.left.GetFinalized(result)
|
||||
rightDeposits, result := n.right.GetFinalized(result)
|
||||
return leftDeposits + rightDeposits, result
|
||||
}
|
||||
|
||||
// PushLeaf adds a new leaf node at the next available zero node.
|
||||
func (n *InnerNode) PushLeaf(leaf [32]byte, depth uint64) (MerkleTreeNode, error) {
|
||||
if !n.left.IsFull() {
|
||||
left, err := n.left.PushLeaf(leaf, depth-1)
|
||||
if err == nil {
|
||||
n.left = left
|
||||
} else {
|
||||
return n, err
|
||||
}
|
||||
} else {
|
||||
right, err := n.right.PushLeaf(leaf, depth-1)
|
||||
if err == nil {
|
||||
n.right = right
|
||||
} else {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Right returns the child node on the right.
|
||||
func (n *InnerNode) Right() MerkleTreeNode {
|
||||
return n.right
|
||||
}
|
||||
|
||||
// Left returns the child node on the left.
|
||||
func (n *InnerNode) Left() MerkleTreeNode {
|
||||
return n.left
|
||||
}
|
||||
|
||||
// ZeroNode represents an empty node without a deposit and satisfies the MerkleTreeNode interface.
|
||||
type ZeroNode struct {
|
||||
depth uint64
|
||||
}
|
||||
|
||||
// GetRoot returns the root of the Merkle tree.
|
||||
func (z *ZeroNode) GetRoot() [32]byte {
|
||||
if z.depth == DepositContractDepth {
|
||||
return hash.Hash(append(Zerohashes[z.depth-1][:], Zerohashes[z.depth-1][:]...))
|
||||
}
|
||||
return Zerohashes[z.depth]
|
||||
}
|
||||
|
||||
// IsFull returns wh ether there is space left for deposits.
|
||||
// A ZeroNode will always return false as a ZeroNode is an empty node
|
||||
// that gets replaced by a deposit.
|
||||
func (_ *ZeroNode) IsFull() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Finalize marks deposits of the Merkle tree as finalized.
|
||||
func (_ *ZeroNode) Finalize(depositsToFinalize uint64, depth uint64) (MerkleTreeNode, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetFinalized returns a list of hashes of all the finalized nodes and the number of deposits.
|
||||
func (_ *ZeroNode) GetFinalized(result [][32]byte) (uint64, [][32]byte) {
|
||||
return 0, result
|
||||
}
|
||||
|
||||
// PushLeaf adds a new leaf node at the next available zero node.
|
||||
func (_ *ZeroNode) PushLeaf(leaf [32]byte, depth uint64) (MerkleTreeNode, error) {
|
||||
return create([][32]byte{leaf}, depth), nil
|
||||
}
|
||||
|
||||
// Right returns nil as a zero node can't have any children.
|
||||
func (_ *ZeroNode) Right() MerkleTreeNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Left returns nil as a zero node can't have any children.
|
||||
func (_ *ZeroNode) Left() MerkleTreeNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
69
beacon-chain/cache/depositsnapshot/zerohashes.gen.go
vendored
Normal file
69
beacon-chain/cache/depositsnapshot/zerohashes.gen.go
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// Code generated by gen_zerohashes. DO NOT EDIT.
|
||||
package depositsnapshot
|
||||
|
||||
var Zerohashes = [][32]byte{
|
||||
// 0000000000000000000000000000000000000000000000000000000000000000
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
// f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b
|
||||
{245, 165, 253, 66, 209, 106, 32, 48, 39, 152, 239, 110, 211, 9, 151, 155, 67, 0, 61, 35, 32, 217, 240, 232, 234, 152, 49, 169, 39, 89, 251, 75},
|
||||
// db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71
|
||||
{219, 86, 17, 78, 0, 253, 212, 193, 248, 92, 137, 43, 243, 90, 201, 168, 146, 137, 170, 236, 177, 235, 208, 169, 108, 222, 96, 106, 116, 139, 93, 113},
|
||||
// c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c
|
||||
{199, 128, 9, 253, 240, 127, 197, 106, 17, 241, 34, 55, 6, 88, 163, 83, 170, 165, 66, 237, 99, 228, 76, 75, 193, 95, 244, 205, 16, 90, 179, 60},
|
||||
// 536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c
|
||||
{83, 109, 152, 131, 127, 45, 209, 101, 165, 93, 94, 234, 233, 20, 133, 149, 68, 114, 213, 111, 36, 109, 242, 86, 191, 60, 174, 25, 53, 42, 18, 60},
|
||||
// 9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30
|
||||
{158, 253, 224, 82, 170, 21, 66, 159, 174, 5, 186, 212, 208, 177, 215, 198, 77, 166, 77, 3, 215, 161, 133, 74, 88, 140, 44, 184, 67, 12, 13, 48},
|
||||
// d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1
|
||||
{216, 141, 223, 238, 212, 0, 168, 117, 85, 150, 178, 25, 66, 193, 73, 126, 17, 76, 48, 46, 97, 24, 41, 15, 145, 230, 119, 41, 118, 4, 31, 161},
|
||||
// 87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c
|
||||
{135, 235, 13, 219, 165, 126, 53, 246, 210, 134, 103, 56, 2, 164, 175, 89, 117, 226, 37, 6, 199, 207, 76, 100, 187, 107, 229, 238, 17, 82, 127, 44},
|
||||
// 26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193
|
||||
{38, 132, 100, 118, 253, 95, 197, 74, 93, 67, 56, 81, 103, 201, 81, 68, 242, 100, 63, 83, 60, 200, 91, 185, 209, 107, 120, 47, 141, 125, 177, 147},
|
||||
// 506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1
|
||||
{80, 109, 134, 88, 45, 37, 36, 5, 184, 64, 1, 135, 146, 202, 210, 191, 18, 89, 241, 239, 90, 165, 248, 135, 225, 60, 178, 240, 9, 79, 81, 225},
|
||||
// ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b
|
||||
{255, 255, 10, 215, 230, 89, 119, 47, 149, 52, 193, 149, 200, 21, 239, 196, 1, 78, 241, 225, 218, 237, 68, 4, 192, 99, 133, 209, 17, 146, 233, 43},
|
||||
// 6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220
|
||||
{108, 240, 65, 39, 219, 5, 68, 28, 216, 51, 16, 122, 82, 190, 133, 40, 104, 137, 14, 67, 23, 230, 160, 42, 180, 118, 131, 170, 117, 150, 66, 32},
|
||||
// b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f
|
||||
{183, 208, 95, 135, 95, 20, 0, 39, 239, 81, 24, 162, 36, 123, 187, 132, 206, 143, 47, 15, 17, 35, 98, 48, 133, 218, 247, 150, 12, 50, 159, 95},
|
||||
// df6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e
|
||||
{223, 106, 245, 245, 187, 219, 107, 233, 239, 138, 166, 24, 228, 191, 128, 115, 150, 8, 103, 23, 30, 41, 103, 111, 139, 40, 77, 234, 106, 8, 168, 94},
|
||||
// b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784
|
||||
{181, 141, 144, 15, 94, 24, 46, 60, 80, 239, 116, 150, 158, 161, 108, 119, 38, 197, 73, 117, 124, 194, 53, 35, 195, 105, 88, 125, 167, 41, 55, 132},
|
||||
// d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb
|
||||
{212, 154, 117, 2, 255, 207, 176, 52, 11, 29, 120, 133, 104, 133, 0, 202, 48, 129, 97, 167, 249, 107, 98, 223, 157, 8, 59, 113, 252, 200, 242, 187},
|
||||
// 8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb
|
||||
{143, 230, 177, 104, 146, 86, 192, 211, 133, 244, 47, 91, 190, 32, 39, 162, 44, 25, 150, 225, 16, 186, 151, 193, 113, 211, 229, 148, 141, 233, 43, 235},
|
||||
// 8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab
|
||||
{141, 13, 99, 195, 158, 186, 222, 133, 9, 224, 174, 60, 156, 56, 118, 251, 95, 161, 18, 190, 24, 249, 5, 236, 172, 254, 203, 146, 5, 118, 3, 171},
|
||||
// 95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4
|
||||
{149, 238, 200, 178, 229, 65, 202, 212, 233, 29, 227, 131, 133, 242, 224, 70, 97, 159, 84, 73, 108, 35, 130, 203, 108, 172, 213, 185, 140, 38, 245, 164},
|
||||
// f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f
|
||||
{248, 147, 233, 8, 145, 119, 117, 182, 43, 255, 35, 41, 77, 187, 227, 161, 205, 142, 108, 193, 195, 91, 72, 1, 136, 123, 100, 106, 111, 129, 241, 127},
|
||||
// cddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa
|
||||
{205, 219, 167, 181, 146, 227, 19, 51, 147, 193, 97, 148, 250, 199, 67, 26, 191, 47, 84, 133, 237, 113, 29, 178, 130, 24, 60, 129, 158, 8, 235, 170},
|
||||
// 8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c
|
||||
{138, 141, 127, 227, 175, 140, 170, 8, 90, 118, 57, 168, 50, 0, 20, 87, 223, 185, 18, 138, 128, 97, 20, 42, 208, 51, 86, 41, 255, 35, 255, 156},
|
||||
// feb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167
|
||||
{254, 179, 195, 55, 215, 165, 26, 111, 191, 0, 185, 227, 76, 82, 225, 201, 25, 92, 150, 155, 212, 231, 160, 191, 213, 29, 92, 91, 237, 156, 17, 103},
|
||||
// e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7
|
||||
{231, 31, 10, 168, 60, 195, 46, 223, 190, 250, 159, 77, 62, 1, 116, 202, 133, 24, 46, 236, 159, 58, 9, 246, 166, 192, 223, 99, 119, 165, 16, 215},
|
||||
// 31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0
|
||||
{49, 32, 111, 168, 10, 80, 187, 106, 190, 41, 8, 80, 88, 241, 98, 18, 33, 42, 96, 238, 200, 240, 73, 254, 203, 146, 216, 200, 224, 168, 75, 192},
|
||||
// 21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544
|
||||
{33, 53, 43, 254, 203, 237, 221, 233, 147, 131, 159, 97, 76, 61, 172, 10, 62, 227, 117, 67, 249, 180, 18, 177, 97, 153, 220, 21, 142, 35, 181, 68},
|
||||
// 619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765
|
||||
{97, 158, 49, 39, 36, 187, 109, 124, 49, 83, 237, 157, 231, 145, 215, 100, 163, 102, 179, 137, 175, 19, 197, 139, 248, 168, 217, 4, 129, 164, 103, 101},
|
||||
// 7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4
|
||||
{124, 221, 41, 134, 38, 130, 80, 98, 141, 12, 16, 227, 133, 197, 140, 97, 145, 230, 251, 224, 81, 145, 188, 192, 79, 19, 63, 44, 234, 114, 193, 196},
|
||||
// 848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1
|
||||
{132, 137, 48, 189, 123, 168, 202, 197, 70, 97, 7, 33, 19, 251, 39, 136, 105, 224, 123, 184, 88, 127, 145, 57, 41, 51, 55, 77, 1, 123, 203, 225},
|
||||
// 8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636
|
||||
{136, 105, 255, 44, 34, 178, 140, 193, 5, 16, 217, 133, 50, 146, 128, 51, 40, 190, 79, 176, 232, 4, 149, 232, 187, 141, 39, 31, 91, 136, 150, 54},
|
||||
// b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c
|
||||
{181, 254, 40, 231, 159, 27, 133, 15, 134, 88, 36, 108, 233, 182, 161, 231, 180, 159, 192, 109, 183, 20, 62, 143, 224, 180, 242, 176, 197, 82, 58, 92},
|
||||
// 985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7
|
||||
{152, 94, 146, 159, 112, 175, 40, 208, 189, 209, 169, 10, 128, 143, 151, 127, 89, 124, 124, 119, 140, 72, 158, 152, 211, 189, 137, 16, 211, 26, 192, 247},
|
||||
}
|
||||
@@ -201,7 +201,7 @@ func BLSChangesSignatureBatch(
|
||||
// It validates the signature with the Capella fork version if the passed state
|
||||
// is from a previous fork.
|
||||
func VerifyBLSChangeSignature(
|
||||
st state.BeaconState,
|
||||
st state.ReadOnlyBeaconState,
|
||||
change *ethpbv2.SignedBLSToExecutionChange,
|
||||
) error {
|
||||
c := params.BeaconConfig()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package signing
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
fssz "github.com/prysmaticlabs/fastssz"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
@@ -17,6 +19,11 @@ const ForkVersionByteLength = 4
|
||||
// DomainByteLength length of domain byte array.
|
||||
const DomainByteLength = 4
|
||||
|
||||
// digestMap maps the fork version and genesis validator root to the
|
||||
// resultant fork digest.
|
||||
var digestMapLock sync.RWMutex
|
||||
var digestMap = make(map[string][32]byte)
|
||||
|
||||
// ErrSigFailedToVerify returns when a signature of a block object(ie attestation, slashing, exit... etc)
|
||||
// failed to verify.
|
||||
var ErrSigFailedToVerify = errors.New("signature did not verify")
|
||||
@@ -240,6 +247,12 @@ func domain(domainType [DomainByteLength]byte, forkDataRoot []byte) []byte {
|
||||
// genesis_validators_root=genesis_validators_root,
|
||||
// ))
|
||||
func computeForkDataRoot(version, root []byte) ([32]byte, error) {
|
||||
digestMapLock.RLock()
|
||||
if val, ok := digestMap[string(version)+string(root)]; ok {
|
||||
digestMapLock.RUnlock()
|
||||
return val, nil
|
||||
}
|
||||
digestMapLock.RUnlock()
|
||||
r, err := (ðpb.ForkData{
|
||||
CurrentVersion: version,
|
||||
GenesisValidatorsRoot: root,
|
||||
@@ -247,6 +260,12 @@ func computeForkDataRoot(version, root []byte) ([32]byte, error) {
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
// Cache result of digest computation
|
||||
// as this is a hot path and doesn't need
|
||||
// to be constantly computed.
|
||||
digestMapLock.Lock()
|
||||
digestMap[string(version)+string(root)] = r
|
||||
digestMapLock.Unlock()
|
||||
return r, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,25 @@ func TestFuzzverifySigningRoot_10000(_ *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDigestMap(t *testing.T) {
|
||||
testVersion := []byte{'A', 'B', 'C', 'D'}
|
||||
testValRoot := [32]byte{'t', 'e', 's', 't', 'r', 'o', 'o', 't'}
|
||||
digest, err := signing.ComputeForkDigest(testVersion, testValRoot[:])
|
||||
assert.NoError(t, err)
|
||||
|
||||
cachedDigest, err := signing.ComputeForkDigest(testVersion, testValRoot[:])
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, digest, cachedDigest)
|
||||
testVersion[3] = 'E'
|
||||
cachedDigest, err = signing.ComputeForkDigest(testVersion, testValRoot[:])
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, digest, cachedDigest)
|
||||
testValRoot[5] = 'z'
|
||||
cachedDigest2, err := signing.ComputeForkDigest(testVersion, testValRoot[:])
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, digest, cachedDigest2)
|
||||
assert.NotEqual(t, cachedDigest, cachedDigest2)
|
||||
}
|
||||
func TestBlockSignatureBatch_NoSigVerification(t *testing.T) {
|
||||
tests := []struct {
|
||||
pubkey []byte
|
||||
|
||||
@@ -204,6 +204,7 @@ func (s *Store) pruneFinalizedNodeByRootMap(ctx context.Context, node, finalized
|
||||
|
||||
node.children = nil
|
||||
delete(s.nodeByRoot, node.root)
|
||||
delete(s.nodeByPayload, node.payloadHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -224,10 +224,10 @@ func TestStore_Prune_ReturnEarly(t *testing.T) {
|
||||
func TestStore_Prune_NoDanglingBranch(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, [32]byte{'1'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, [32]byte{'2'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
@@ -235,6 +235,7 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
|
||||
s.finalizedCheckpoint.Root = indexToHash(1)
|
||||
require.NoError(t, s.prune(context.Background()))
|
||||
require.Equal(t, len(s.nodeByRoot), 1)
|
||||
require.Equal(t, len(s.nodeByPayload), 1)
|
||||
}
|
||||
|
||||
// This test starts with the following branching diagram
|
||||
@@ -304,10 +305,10 @@ func TestStore_tips(t *testing.T) {
|
||||
func TestStore_PruneMapsNodes(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, [32]byte{'1'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, [32]byte{'2'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
@@ -315,6 +316,7 @@ func TestStore_PruneMapsNodes(t *testing.T) {
|
||||
s.finalizedCheckpoint.Root = indexToHash(1)
|
||||
require.NoError(t, s.prune(context.Background()))
|
||||
require.Equal(t, len(s.nodeByRoot), 1)
|
||||
require.Equal(t, len(s.nodeByPayload), 1)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,11 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// We recycle the BLS changes pool to avoid the backing map growing without
|
||||
// bound. The cycling operation is expensive because it copies all elements, so
|
||||
// we only do it when the map is smaller than this upper bound.
|
||||
const blsChangesPoolThreshold = 2000
|
||||
|
||||
// PoolManager maintains pending and seen BLS-to-execution-change objects.
|
||||
// This pool is used by proposers to insert BLS-to-execution-change objects into new blocks.
|
||||
type PoolManager interface {
|
||||
@@ -39,6 +44,15 @@ func NewPool() *Pool {
|
||||
}
|
||||
}
|
||||
|
||||
// Copies the internal map and returns a new one.
|
||||
func (p *Pool) cycleMap() {
|
||||
newMap := make(map[primitives.ValidatorIndex]*doublylinkedlist.Node[*ethpb.SignedBLSToExecutionChange])
|
||||
for k, v := range p.m {
|
||||
newMap[k] = v
|
||||
}
|
||||
p.m = newMap
|
||||
}
|
||||
|
||||
// PendingBLSToExecChanges returns all objects from the pool.
|
||||
func (p *Pool) PendingBLSToExecChanges() ([]*ethpb.SignedBLSToExecutionChange, error) {
|
||||
p.lock.RLock()
|
||||
@@ -150,6 +164,9 @@ func (p *Pool) MarkIncluded(change *ethpb.SignedBLSToExecutionChange) {
|
||||
|
||||
delete(p.m, change.Message.ValidatorIndex)
|
||||
p.pending.Remove(node)
|
||||
if p.numPending() == blsChangesPoolThreshold {
|
||||
p.cycleMap()
|
||||
}
|
||||
}
|
||||
|
||||
// ValidatorExists checks if the bls to execution change object exists
|
||||
@@ -162,3 +179,8 @@ func (p *Pool) ValidatorExists(idx primitives.ValidatorIndex) bool {
|
||||
|
||||
return node != nil
|
||||
}
|
||||
|
||||
// numPending returns the number of pending bls to execution changes in the pool
|
||||
func (p *Pool) numPending() int {
|
||||
return p.pending.Len()
|
||||
}
|
||||
|
||||
@@ -407,3 +407,29 @@ func TestValidatorExists(t *testing.T) {
|
||||
assert.Equal(t, false, pool.ValidatorExists(30))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPoolCycleMap(t *testing.T) {
|
||||
pool := NewPool()
|
||||
firstChange := ð.SignedBLSToExecutionChange{
|
||||
Message: ð.BLSToExecutionChange{
|
||||
ValidatorIndex: primitives.ValidatorIndex(0),
|
||||
}}
|
||||
pool.InsertBLSToExecChange(firstChange)
|
||||
secondChange := ð.SignedBLSToExecutionChange{
|
||||
Message: ð.BLSToExecutionChange{
|
||||
ValidatorIndex: primitives.ValidatorIndex(10),
|
||||
}}
|
||||
pool.InsertBLSToExecChange(secondChange)
|
||||
thirdChange := ð.SignedBLSToExecutionChange{
|
||||
Message: ð.BLSToExecutionChange{
|
||||
ValidatorIndex: primitives.ValidatorIndex(30),
|
||||
}}
|
||||
pool.InsertBLSToExecChange(thirdChange)
|
||||
|
||||
pool.cycleMap()
|
||||
require.Equal(t, true, pool.ValidatorExists(0))
|
||||
require.Equal(t, true, pool.ValidatorExists(10))
|
||||
require.Equal(t, true, pool.ValidatorExists(30))
|
||||
require.Equal(t, false, pool.ValidatorExists(20))
|
||||
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const broadcastBLSChangesRateLimit = 128
|
||||
|
||||
// ErrMessageNotMapped occurs on a Broadcast attempt when a message has not been defined in the
|
||||
// GossipTypeMapping.
|
||||
var ErrMessageNotMapped = errors.New("message type is not mapped to a PubSub topic")
|
||||
@@ -54,36 +52,6 @@ func (s *Service) Broadcast(ctx context.Context, msg proto.Message) error {
|
||||
return s.broadcastObject(ctx, castMsg, fmt.Sprintf(topic, forkDigest))
|
||||
}
|
||||
|
||||
// BroadcastBLSChanges spins up a go routine that rate limits and broadcasts BLS
|
||||
// to execution changes at prescribed intervals.
|
||||
func (s *Service) BroadcastBLSChanges(ctx context.Context, changes []*ethpb.SignedBLSToExecutionChange) {
|
||||
go s.broadcastBLSChanges(ctx, changes)
|
||||
}
|
||||
|
||||
func (s *Service) broadcastBLSChanges(ctx context.Context, changes []*ethpb.SignedBLSToExecutionChange) {
|
||||
ticker := time.NewTicker(500 * time.Millisecond)
|
||||
for {
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
limit := broadcastBLSChangesRateLimit
|
||||
if len(changes) < broadcastBLSChangesRateLimit {
|
||||
limit = len(changes)
|
||||
}
|
||||
for _, ch := range changes[:limit] {
|
||||
if err := s.Broadcast(ctx, ch); err != nil {
|
||||
log.WithError(err).Error("could not broadcast BLS to execution changes.")
|
||||
}
|
||||
}
|
||||
changes = changes[limit:]
|
||||
if len(changes) == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BroadcastAttestation broadcasts an attestation to the p2p network, the message is assumed to be
|
||||
// broadcasted to the current fork.
|
||||
func (s *Service) BroadcastAttestation(ctx context.Context, subnet uint64, att *ethpb.Attestation) error {
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/util"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -440,56 +439,3 @@ func TestService_BroadcastSyncCommittee(t *testing.T) {
|
||||
t.Error("Failed to receive pubsub within 1s")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBroadcastBLSChanges(t *testing.T) {
|
||||
logHook := logTest.NewGlobal()
|
||||
p1 := p2ptest.NewTestP2P(t)
|
||||
p2 := p2ptest.NewTestP2P(t)
|
||||
p1.Connect(p2)
|
||||
if len(p1.BHost.Network().Peers()) == 0 {
|
||||
t.Fatal("No peers")
|
||||
}
|
||||
|
||||
p := &Service{
|
||||
host: p1.BHost,
|
||||
pubsub: p1.PubSub(),
|
||||
joinedTopics: map[string]*pubsub.Topic{},
|
||||
cfg: &Config{},
|
||||
ctx: context.Background(),
|
||||
genesisTime: time.Now(),
|
||||
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
|
||||
subnetsLock: make(map[uint64]*sync.RWMutex),
|
||||
subnetsLockLock: sync.Mutex{},
|
||||
peers: peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
}),
|
||||
}
|
||||
|
||||
message := ðpb.SignedBLSToExecutionChange{
|
||||
Message: ðpb.BLSToExecutionChange{
|
||||
ValidatorIndex: 1,
|
||||
FromBlsPubkey: bytesutil.PadTo([]byte("pubkey1"), 48),
|
||||
ToExecutionAddress: bytesutil.PadTo([]byte("address1"), 20),
|
||||
},
|
||||
Signature: bytesutil.PadTo([]byte("signature1"), 96),
|
||||
}
|
||||
message2 := ðpb.SignedBLSToExecutionChange{
|
||||
Message: ðpb.BLSToExecutionChange{
|
||||
ValidatorIndex: 1,
|
||||
FromBlsPubkey: bytesutil.PadTo([]byte("pubkey1"), 48),
|
||||
ToExecutionAddress: bytesutil.PadTo([]byte("address1"), 20),
|
||||
},
|
||||
Signature: bytesutil.PadTo([]byte("signature1"), 96),
|
||||
}
|
||||
messages := make([]*ethpb.SignedBLSToExecutionChange, 200)
|
||||
for i := 0; i < 128; i++ {
|
||||
messages[i] = message
|
||||
}
|
||||
for i := 128; i < 200; i++ {
|
||||
messages[i] = message2
|
||||
}
|
||||
|
||||
p.broadcastBLSChanges(context.Background(), messages)
|
||||
require.LogsDoNotContain(t, logHook, "could not")
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ type Broadcaster interface {
|
||||
Broadcast(context.Context, proto.Message) error
|
||||
BroadcastAttestation(ctx context.Context, subnet uint64, att *ethpb.Attestation) error
|
||||
BroadcastSyncCommitteeMessage(ctx context.Context, subnet uint64, sMsg *ethpb.SyncCommitteeMessage) error
|
||||
BroadcastBLSChanges(context.Context, []*ethpb.SignedBLSToExecutionChange)
|
||||
}
|
||||
|
||||
// SetStreamHandler configures p2p to handle streams of a certain topic ID.
|
||||
|
||||
@@ -143,10 +143,6 @@ func (_ *FakeP2P) BroadcastSyncCommitteeMessage(_ context.Context, _ uint64, _ *
|
||||
return nil
|
||||
}
|
||||
|
||||
// BroadcastBLSChanges mocks a broadcast BLS change ocurred
|
||||
func (_ *FakeP2P) BroadcastBLSChanges(_ context.Context, _ []*ethpb.SignedBLSToExecutionChange) {
|
||||
}
|
||||
|
||||
// InterceptPeerDial -- fake.
|
||||
func (_ *FakeP2P) InterceptPeerDial(peer.ID) (allow bool) {
|
||||
return true
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -34,13 +33,3 @@ func (m *MockBroadcaster) BroadcastSyncCommitteeMessage(_ context.Context, _ uin
|
||||
m.BroadcastCalled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// BroadcastBLSChanges mocks a broadcast BLS change ocurred
|
||||
func (m *MockBroadcaster) BroadcastBLSChanges(ctx context.Context, changes []*ethpb.SignedBLSToExecutionChange) {
|
||||
for _, change := range changes {
|
||||
err := m.Broadcast(ctx, change)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not broadcast Signed BLS change")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,14 +176,6 @@ func (p *TestP2P) BroadcastSyncCommitteeMessage(_ context.Context, _ uint64, _ *
|
||||
return nil
|
||||
}
|
||||
|
||||
// BroadcastBLSChanges mocks a broadcast BLS change ocurred
|
||||
func (p *TestP2P) BroadcastBLSChanges(_ context.Context, changes []*ethpb.SignedBLSToExecutionChange) {
|
||||
if len(changes) > 0 {
|
||||
p.BroadcastCalled = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SetStreamHandler for RPC.
|
||||
func (p *TestP2P) SetStreamHandler(topic string, handler network.StreamHandler) {
|
||||
p.BHost.SetStreamHandler(protocol.ID(topic), handler)
|
||||
|
||||
@@ -38,6 +38,7 @@ type FeeRecipientsRequestJSON struct {
|
||||
type StateRootResponseJson struct {
|
||||
Data *StateRootResponse_StateRootJson `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type StateRootResponse_StateRootJson struct {
|
||||
@@ -47,11 +48,13 @@ type StateRootResponse_StateRootJson struct {
|
||||
type StateForkResponseJson struct {
|
||||
Data *ForkJson `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type StateFinalityCheckpointResponseJson struct {
|
||||
Data *StateFinalityCheckpointResponse_StateFinalityCheckpointJson `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type StateFinalityCheckpointResponse_StateFinalityCheckpointJson struct {
|
||||
@@ -63,26 +66,31 @@ type StateFinalityCheckpointResponse_StateFinalityCheckpointJson struct {
|
||||
type StateValidatorsResponseJson struct {
|
||||
Data []*ValidatorContainerJson `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type StateValidatorResponseJson struct {
|
||||
Data *ValidatorContainerJson `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type ValidatorBalancesResponseJson struct {
|
||||
Data []*ValidatorBalanceJson `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type StateCommitteesResponseJson struct {
|
||||
Data []*CommitteeJson `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type SyncCommitteesResponseJson struct {
|
||||
Data *SyncCommitteeValidatorsJson `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type RandaoResponseJson struct {
|
||||
@@ -90,6 +98,7 @@ type RandaoResponseJson struct {
|
||||
Randao string `json:"randao" hex:"true"`
|
||||
} `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type BlockHeadersResponseJson struct {
|
||||
@@ -201,6 +210,7 @@ type BeaconStateV2ResponseJson struct {
|
||||
Version string `json:"version" enum:"true"`
|
||||
Data *BeaconStateContainerV2Json `json:"data"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
}
|
||||
|
||||
type ForkChoiceHeadsResponseJson struct {
|
||||
|
||||
@@ -2,6 +2,7 @@ package beacon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/api/grpc"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
@@ -24,6 +25,8 @@ import (
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
const broadcastBLSChangesRateLimit = 128
|
||||
|
||||
// ListPoolAttestations retrieves attestations known by the node but
|
||||
// not necessarily incorporated into any block. Allows filtering by committee index or slot.
|
||||
func (bs *Server) ListPoolAttestations(ctx context.Context, req *ethpbv1.AttestationsPoolRequest) (*ethpbv1.AttestationsPoolResponse, error) {
|
||||
@@ -312,7 +315,7 @@ func (bs *Server) SubmitVoluntaryExit(ctx context.Context, req *ethpbv1.SignedVo
|
||||
func (bs *Server) SubmitSignedBLSToExecutionChanges(ctx context.Context, req *ethpbv2.SubmitBLSToExecutionChangesRequest) (*emptypb.Empty, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon.SubmitSignedBLSToExecutionChanges")
|
||||
defer span.End()
|
||||
st, err := bs.ChainInfoFetcher.HeadState(ctx)
|
||||
st, err := bs.ChainInfoFetcher.HeadStateReadOnly(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not get head state: %v", err)
|
||||
}
|
||||
@@ -347,7 +350,7 @@ func (bs *Server) SubmitSignedBLSToExecutionChanges(ctx context.Context, req *et
|
||||
toBroadcast = append(toBroadcast, alphaChange)
|
||||
}
|
||||
}
|
||||
bs.Broadcaster.BroadcastBLSChanges(ctx, toBroadcast)
|
||||
go bs.broadcastBLSChanges(ctx, toBroadcast)
|
||||
if len(failures) > 0 {
|
||||
failuresContainer := &helpers.IndexedVerificationFailure{Failures: failures}
|
||||
err := grpc.AppendCustomErrorHeader(ctx, failuresContainer)
|
||||
@@ -363,6 +366,54 @@ func (bs *Server) SubmitSignedBLSToExecutionChanges(ctx context.Context, req *et
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
// broadcastBLSBatch broadcasts the first `broadcastBLSChangesRateLimit` messages from the slice pointed to by ptr.
|
||||
// It validates the messages again because they could have been invalidated by being included in blocks since the last validation.
|
||||
// It removes the messages from the slice and modifies it in place.
|
||||
func (bs *Server) broadcastBLSBatch(ctx context.Context, ptr *[]*ethpbalpha.SignedBLSToExecutionChange) {
|
||||
limit := broadcastBLSChangesRateLimit
|
||||
if len(*ptr) < broadcastBLSChangesRateLimit {
|
||||
limit = len(*ptr)
|
||||
}
|
||||
st, err := bs.ChainInfoFetcher.HeadStateReadOnly(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not get head state")
|
||||
return
|
||||
}
|
||||
for _, ch := range (*ptr)[:limit] {
|
||||
if ch != nil {
|
||||
_, err := blocks.ValidateBLSToExecutionChange(st, ch)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not validate BLS to execution change")
|
||||
continue
|
||||
}
|
||||
if err := bs.Broadcaster.Broadcast(ctx, ch); err != nil {
|
||||
log.WithError(err).Error("could not broadcast BLS to execution changes.")
|
||||
}
|
||||
}
|
||||
}
|
||||
*ptr = (*ptr)[limit:]
|
||||
}
|
||||
|
||||
func (bs *Server) broadcastBLSChanges(ctx context.Context, changes []*ethpbalpha.SignedBLSToExecutionChange) {
|
||||
bs.broadcastBLSBatch(ctx, &changes)
|
||||
if len(changes) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(500 * time.Millisecond)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
bs.broadcastBLSBatch(ctx, &changes)
|
||||
if len(changes) == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ListBLSToExecutionChanges retrieves BLS to execution changes known by the node but not necessarily incorporated into any block
|
||||
func (bs *Server) ListBLSToExecutionChanges(ctx context.Context, _ *emptypb.Empty) (*ethpbv2.BLSToExecutionChangesPoolResponse, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon.ListBLSToExecutionChanges")
|
||||
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
grpcutil "github.com/prysmaticlabs/prysm/v3/api/grpc"
|
||||
blockchainmock "github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/signing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/time"
|
||||
prysmtime "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/operations/attestations"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/operations/blstoexec"
|
||||
@@ -1250,7 +1251,7 @@ func TestSubmitSignedBLSToExecutionChanges_Ok(t *testing.T) {
|
||||
|
||||
signedChanges := make([]*ethpbv2.SignedBLSToExecutionChange, numValidators)
|
||||
for i, message := range blsChanges {
|
||||
signature, err := signing.ComputeDomainAndSign(st, time.CurrentEpoch(st), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
|
||||
signature, err := signing.ComputeDomainAndSign(st, prysmtime.CurrentEpoch(st), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
|
||||
require.NoError(t, err)
|
||||
|
||||
signed := ðpbv2.SignedBLSToExecutionChange{
|
||||
@@ -1275,6 +1276,7 @@ func TestSubmitSignedBLSToExecutionChanges_Ok(t *testing.T) {
|
||||
Changes: signedChanges,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
time.Sleep(100 * time.Millisecond) // Delay to let the routine start
|
||||
assert.Equal(t, true, broadcaster.BroadcastCalled)
|
||||
assert.Equal(t, numValidators, len(broadcaster.BroadcastMessages))
|
||||
|
||||
@@ -1356,7 +1358,7 @@ func TestSubmitSignedBLSToExecutionChanges_Bellatrix(t *testing.T) {
|
||||
|
||||
signedChanges := make([]*ethpbv2.SignedBLSToExecutionChange, numValidators)
|
||||
for i, message := range blsChanges {
|
||||
signature, err := signing.ComputeDomainAndSign(stc, time.CurrentEpoch(stc), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
|
||||
signature, err := signing.ComputeDomainAndSign(stc, prysmtime.CurrentEpoch(stc), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
|
||||
require.NoError(t, err)
|
||||
|
||||
signed := ðpbv2.SignedBLSToExecutionChange{
|
||||
@@ -1450,7 +1452,7 @@ func TestSubmitSignedBLSToExecutionChanges_Failures(t *testing.T) {
|
||||
|
||||
signedChanges := make([]*ethpbv2.SignedBLSToExecutionChange, numValidators)
|
||||
for i, message := range blsChanges {
|
||||
signature, err := signing.ComputeDomainAndSign(st, time.CurrentEpoch(st), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
|
||||
signature, err := signing.ComputeDomainAndSign(st, prysmtime.CurrentEpoch(st), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
|
||||
require.NoError(t, err)
|
||||
|
||||
signed := ðpbv2.SignedBLSToExecutionChange{
|
||||
@@ -1475,6 +1477,7 @@ func TestSubmitSignedBLSToExecutionChanges_Failures(t *testing.T) {
|
||||
_, err = s.SubmitSignedBLSToExecutionChanges(ctx, ðpbv2.SubmitBLSToExecutionChangesRequest{
|
||||
Changes: signedChanges,
|
||||
})
|
||||
time.Sleep(10 * time.Millisecond) // Delay to allow the routine to start
|
||||
require.ErrorContains(t, "One or more BLSToExecutionChange failed validation", err)
|
||||
assert.Equal(t, true, broadcaster.BroadcastCalled)
|
||||
assert.Equal(t, numValidators, len(broadcaster.BroadcastMessages)+1)
|
||||
|
||||
@@ -75,12 +75,18 @@ func (bs *Server) GetStateRoot(ctx context.Context, req *ethpb.StateRequest) (*e
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
|
||||
}
|
||||
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
|
||||
}
|
||||
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
|
||||
|
||||
return ðpb.StateRootResponse{
|
||||
Data: ðpb.StateRootResponse_StateRoot{
|
||||
Root: stateRoot,
|
||||
},
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
Finalized: isFinalized,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -98,6 +104,11 @@ func (bs *Server) GetStateFork(ctx context.Context, req *ethpb.StateRequest) (*e
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
|
||||
}
|
||||
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
|
||||
}
|
||||
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
|
||||
|
||||
return ðpb.StateForkResponse{
|
||||
Data: ðpb.Fork{
|
||||
@@ -106,6 +117,7 @@ func (bs *Server) GetStateFork(ctx context.Context, req *ethpb.StateRequest) (*e
|
||||
Epoch: fork.Epoch,
|
||||
},
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
Finalized: isFinalized,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -123,6 +135,11 @@ func (bs *Server) GetFinalityCheckpoints(ctx context.Context, req *ethpb.StateRe
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
|
||||
}
|
||||
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
|
||||
}
|
||||
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
|
||||
|
||||
return ðpb.StateFinalityCheckpointResponse{
|
||||
Data: ðpb.StateFinalityCheckpointResponse_StateFinalityCheckpoint{
|
||||
@@ -131,6 +148,7 @@ func (bs *Server) GetFinalityCheckpoints(ctx context.Context, req *ethpb.StateRe
|
||||
Finalized: checkpoint(st.FinalizedCheckpoint()),
|
||||
},
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
Finalized: isFinalized,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -173,9 +191,16 @@ func (bs *Server) GetRandao(ctx context.Context, req *eth2.RandaoRequest) (*eth2
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
|
||||
}
|
||||
|
||||
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
|
||||
}
|
||||
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
|
||||
|
||||
return ð2.RandaoResponse{
|
||||
Data: ð2.RandaoResponse_Randao{Randao: randao},
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
Finalized: isFinalized,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ func TestGetStateRoot(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -116,6 +117,7 @@ func TestGetStateRoot(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetStateRoot(context.Background(), ð.StateRequest{
|
||||
@@ -125,6 +127,40 @@ func TestGetStateRoot(t *testing.T) {
|
||||
assert.NotNil(t, resp)
|
||||
assert.DeepEqual(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
|
||||
t.Run("finalized", func(t *testing.T) {
|
||||
parentRoot := [32]byte{'a'}
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
root, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, db, blk)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
|
||||
|
||||
headerRoot, err := fakeState.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
chainService := &chainMock.ChainService{
|
||||
FinalizedRoots: map[[32]byte]bool{
|
||||
headerRoot: true,
|
||||
},
|
||||
}
|
||||
server := &Server{
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconStateRoot: stateRoot[:],
|
||||
BeaconState: fakeState,
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetStateRoot(context.Background(), ð.StateRequest{
|
||||
StateId: make([]byte, 0),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.DeepEqual(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetStateFork(t *testing.T) {
|
||||
@@ -148,6 +184,7 @@ func TestGetStateFork(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -177,6 +214,7 @@ func TestGetStateFork(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetStateFork(context.Background(), ð.StateRequest{
|
||||
@@ -186,6 +224,39 @@ func TestGetStateFork(t *testing.T) {
|
||||
assert.NotNil(t, resp)
|
||||
assert.DeepEqual(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
|
||||
t.Run("finalized", func(t *testing.T) {
|
||||
parentRoot := [32]byte{'a'}
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
root, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, db, blk)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
|
||||
|
||||
headerRoot, err := fakeState.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
chainService := &chainMock.ChainService{
|
||||
FinalizedRoots: map[[32]byte]bool{
|
||||
headerRoot: true,
|
||||
},
|
||||
}
|
||||
server := &Server{
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconState: fakeState,
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetStateFork(context.Background(), ð.StateRequest{
|
||||
StateId: make([]byte, 0),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.DeepEqual(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetFinalityCheckpoints(t *testing.T) {
|
||||
@@ -216,6 +287,7 @@ func TestGetFinalityCheckpoints(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -247,6 +319,7 @@ func TestGetFinalityCheckpoints(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetFinalityCheckpoints(context.Background(), ð.StateRequest{
|
||||
@@ -256,6 +329,39 @@ func TestGetFinalityCheckpoints(t *testing.T) {
|
||||
assert.NotNil(t, resp)
|
||||
assert.DeepEqual(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
|
||||
t.Run("finalized", func(t *testing.T) {
|
||||
parentRoot := [32]byte{'a'}
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
root, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, db, blk)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
|
||||
|
||||
headerRoot, err := fakeState.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
chainService := &chainMock.ChainService{
|
||||
FinalizedRoots: map[[32]byte]bool{
|
||||
headerRoot: true,
|
||||
},
|
||||
}
|
||||
server := &Server{
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconState: fakeState,
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetFinalityCheckpoints(context.Background(), ð.StateRequest{
|
||||
StateId: make([]byte, 0),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.DeepEqual(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetRandao(t *testing.T) {
|
||||
@@ -287,6 +393,7 @@ func TestGetRandao(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -339,6 +446,7 @@ func TestGetRandao(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetRandao(context.Background(), ð2.RandaoRequest{
|
||||
@@ -348,4 +456,36 @@ func TestGetRandao(t *testing.T) {
|
||||
assert.NotNil(t, resp)
|
||||
assert.DeepEqual(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
t.Run("finalized", func(t *testing.T) {
|
||||
parentRoot := [32]byte{'a'}
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
root, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, db, blk)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
|
||||
|
||||
headerRoot, err := headSt.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
chainService := &chainMock.ChainService{
|
||||
FinalizedRoots: map[[32]byte]bool{
|
||||
headerRoot: true,
|
||||
},
|
||||
}
|
||||
server := &Server{
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconState: st,
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetRandao(context.Background(), ð2.RandaoRequest{
|
||||
StateId: make([]byte, 0),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.DeepEqual(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -96,12 +96,19 @@ func (bs *Server) ListSyncCommittees(ctx context.Context, req *ethpbv2.StateSync
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
|
||||
}
|
||||
|
||||
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
|
||||
}
|
||||
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
|
||||
|
||||
return ðpbv2.StateSyncCommitteesResponse{
|
||||
Data: ðpbv2.SyncCommitteeValidators{
|
||||
Validators: committeeIndices,
|
||||
ValidatorAggregates: subcommittees,
|
||||
},
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
Finalized: isFinalized,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -171,6 +171,7 @@ func TestListSyncCommittees(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
req := ðpbv2.StateSyncCommitteesRequest{StateId: stRoot[:]}
|
||||
@@ -214,12 +215,46 @@ func TestListSyncCommittees(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := s.ListSyncCommittees(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
|
||||
t.Run("finalized", func(t *testing.T) {
|
||||
parentRoot := [32]byte{'a'}
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
root, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, db, blk)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
|
||||
|
||||
headerRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
chainService := &mock.ChainService{
|
||||
FinalizedRoots: map[[32]byte]bool{
|
||||
headerRoot: true,
|
||||
},
|
||||
}
|
||||
s := &Server{
|
||||
GenesisTimeFetcher: &testutil.MockGenesisTimeFetcher{
|
||||
Genesis: time.Now(),
|
||||
},
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconState: st,
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := s.ListSyncCommittees(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
type futureSyncMockFetcher struct {
|
||||
@@ -272,6 +307,7 @@ func TestListSyncCommitteesFuture(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
req := ðpbv2.StateSyncCommitteesRequest{}
|
||||
|
||||
@@ -62,7 +62,13 @@ func (bs *Server) GetValidator(ctx context.Context, req *ethpb.StateValidatorReq
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
|
||||
}
|
||||
|
||||
return ðpb.StateValidatorResponse{Data: valContainer[0], ExecutionOptimistic: isOptimistic}, nil
|
||||
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
|
||||
}
|
||||
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
|
||||
|
||||
return ðpb.StateValidatorResponse{Data: valContainer[0], ExecutionOptimistic: isOptimistic, Finalized: isFinalized}, nil
|
||||
}
|
||||
|
||||
// ListValidators returns filterable list of validators with their balance, status and index.
|
||||
@@ -85,9 +91,15 @@ func (bs *Server) ListValidators(ctx context.Context, req *ethpb.StateValidators
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
|
||||
}
|
||||
|
||||
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
|
||||
}
|
||||
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
|
||||
|
||||
// Exit early if no matching validators we found or we don't want to further filter validators by status.
|
||||
if len(valContainers) == 0 || len(req.Status) == 0 {
|
||||
return ðpb.StateValidatorsResponse{Data: valContainers, ExecutionOptimistic: isOptimistic}, nil
|
||||
return ðpb.StateValidatorsResponse{Data: valContainers, ExecutionOptimistic: isOptimistic, Finalized: isFinalized}, nil
|
||||
}
|
||||
|
||||
filterStatus := make(map[ethpb.ValidatorStatus]bool, len(req.Status))
|
||||
@@ -118,7 +130,7 @@ func (bs *Server) ListValidators(ctx context.Context, req *ethpb.StateValidators
|
||||
}
|
||||
}
|
||||
|
||||
return ðpb.StateValidatorsResponse{Data: filteredVals, ExecutionOptimistic: isOptimistic}, nil
|
||||
return ðpb.StateValidatorsResponse{Data: filteredVals, ExecutionOptimistic: isOptimistic, Finalized: isFinalized}, nil
|
||||
}
|
||||
|
||||
// ListValidatorBalances returns a filterable list of validator balances.
|
||||
@@ -148,7 +160,13 @@ func (bs *Server) ListValidatorBalances(ctx context.Context, req *ethpb.Validato
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
|
||||
}
|
||||
|
||||
return ðpb.ValidatorBalancesResponse{Data: valBalances, ExecutionOptimistic: isOptimistic}, nil
|
||||
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
|
||||
}
|
||||
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
|
||||
|
||||
return ðpb.ValidatorBalancesResponse{Data: valBalances, ExecutionOptimistic: isOptimistic, Finalized: isFinalized}, nil
|
||||
}
|
||||
|
||||
// ListCommittees retrieves the committees for the given state at the given epoch.
|
||||
@@ -207,7 +225,13 @@ func (bs *Server) ListCommittees(ctx context.Context, req *ethpb.StateCommittees
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
|
||||
}
|
||||
|
||||
return ðpb.StateCommitteesResponse{Data: committees, ExecutionOptimistic: isOptimistic}, nil
|
||||
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
|
||||
}
|
||||
isFinalized := bs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
|
||||
|
||||
return ðpb.StateCommitteesResponse{Data: committees, ExecutionOptimistic: isOptimistic, Finalized: isFinalized}, nil
|
||||
}
|
||||
|
||||
// This function returns the validator object based on the passed in ID. The validator ID could be its public key,
|
||||
|
||||
@@ -39,6 +39,7 @@ func TestGetValidator(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -58,6 +59,7 @@ func TestGetValidator(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -101,6 +103,7 @@ func TestGetValidator(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := s.GetValidator(ctx, ðpb.StateValidatorRequest{
|
||||
@@ -110,6 +113,39 @@ func TestGetValidator(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
|
||||
t.Run("finalized", func(t *testing.T) {
|
||||
parentRoot := [32]byte{'a'}
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
root, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, db, blk)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
|
||||
|
||||
headerRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
chainService := &chainMock.ChainService{
|
||||
FinalizedRoots: map[[32]byte]bool{
|
||||
headerRoot: true,
|
||||
},
|
||||
}
|
||||
s := Server{
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconState: st,
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := s.GetValidator(ctx, ðpb.StateValidatorRequest{
|
||||
StateId: []byte("head"),
|
||||
ValidatorId: []byte("15"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListValidators(t *testing.T) {
|
||||
@@ -126,6 +162,7 @@ func TestListValidators(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -147,6 +184,7 @@ func TestListValidators(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -171,6 +209,7 @@ func TestListValidators(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
idNums := []primitives.ValidatorIndex{20, 66, 90, 100}
|
||||
@@ -199,6 +238,7 @@ func TestListValidators(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -229,6 +269,7 @@ func TestListValidators(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -251,6 +292,7 @@ func TestListValidators(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -280,6 +322,7 @@ func TestListValidators(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{
|
||||
@@ -288,6 +331,38 @@ func TestListValidators(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
|
||||
t.Run("finalized", func(t *testing.T) {
|
||||
parentRoot := [32]byte{'a'}
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
root, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, db, blk)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
|
||||
|
||||
headerRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
chainService := &chainMock.ChainService{
|
||||
FinalizedRoots: map[[32]byte]bool{
|
||||
headerRoot: true,
|
||||
},
|
||||
}
|
||||
s := Server{
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconState: st,
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{
|
||||
StateId: []byte("head"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListValidators_Status(t *testing.T) {
|
||||
@@ -370,6 +445,7 @@ func TestListValidators_Status(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -407,6 +483,7 @@ func TestListValidators_Status(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -443,6 +520,7 @@ func TestListValidators_Status(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -478,6 +556,7 @@ func TestListValidators_Status(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -513,6 +592,7 @@ func TestListValidators_Status(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -563,6 +643,7 @@ func TestListValidatorBalances(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -587,6 +668,7 @@ func TestListValidatorBalances(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
idNums := []primitives.ValidatorIndex{20, 66, 90, 100}
|
||||
@@ -614,6 +696,7 @@ func TestListValidatorBalances(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -648,6 +731,7 @@ func TestListValidatorBalances(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -659,6 +743,41 @@ func TestListValidatorBalances(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
|
||||
t.Run("finalized", func(t *testing.T) {
|
||||
parentRoot := [32]byte{'a'}
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
root, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, db, blk)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
|
||||
|
||||
headerRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
chainService := &chainMock.ChainService{
|
||||
FinalizedRoots: map[[32]byte]bool{
|
||||
headerRoot: true,
|
||||
},
|
||||
}
|
||||
s := Server{
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconState: st,
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
ids := [][]byte{[]byte("15"), []byte("26"), []byte("400")}
|
||||
resp, err := s.ListValidatorBalances(ctx, ðpb.ValidatorBalancesRequest{
|
||||
StateId: []byte("head"),
|
||||
Id: ids,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListCommittees(t *testing.T) {
|
||||
@@ -677,6 +796,7 @@ func TestListCommittees(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -699,6 +819,7 @@ func TestListCommittees(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
epoch := primitives.Epoch(10)
|
||||
@@ -720,6 +841,7 @@ func TestListCommittees(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -747,6 +869,7 @@ func TestListCommittees(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -774,6 +897,7 @@ func TestListCommittees(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -809,6 +933,7 @@ func TestListCommittees(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
@@ -818,4 +943,37 @@ func TestListCommittees(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
|
||||
t.Run("finalized", func(t *testing.T) {
|
||||
parentRoot := [32]byte{'a'}
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
root, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, db, blk)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
|
||||
|
||||
headerRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
chainService := &chainMock.ChainService{
|
||||
FinalizedRoots: map[[32]byte]bool{
|
||||
headerRoot: true,
|
||||
},
|
||||
}
|
||||
s := Server{
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconState: st,
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
|
||||
resp, err := s.ListCommittees(ctx, ðpb.StateCommitteesRequest{
|
||||
StateId: []byte("head"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -45,6 +45,11 @@ func (ds *Server) GetBeaconStateV2(ctx context.Context, req *ethpbv2.BeaconState
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if slot's block is optimistic: %v", err)
|
||||
}
|
||||
blockRoot, err := beaconSt.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not calculate root of latest block header")
|
||||
}
|
||||
isFinalized := ds.FinalizationFetcher.IsFinalized(ctx, blockRoot)
|
||||
|
||||
switch beaconSt.Version() {
|
||||
case version.Phase0:
|
||||
@@ -58,6 +63,7 @@ func (ds *Server) GetBeaconStateV2(ctx context.Context, req *ethpbv2.BeaconState
|
||||
State: ðpbv2.BeaconStateContainer_Phase0State{Phase0State: protoSt},
|
||||
},
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
Finalized: isFinalized,
|
||||
}, nil
|
||||
case version.Altair:
|
||||
protoState, err := migration.BeaconStateAltairToProto(beaconSt)
|
||||
@@ -70,6 +76,7 @@ func (ds *Server) GetBeaconStateV2(ctx context.Context, req *ethpbv2.BeaconState
|
||||
State: ðpbv2.BeaconStateContainer_AltairState{AltairState: protoState},
|
||||
},
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
Finalized: isFinalized,
|
||||
}, nil
|
||||
case version.Bellatrix:
|
||||
protoState, err := migration.BeaconStateBellatrixToProto(beaconSt)
|
||||
@@ -82,6 +89,7 @@ func (ds *Server) GetBeaconStateV2(ctx context.Context, req *ethpbv2.BeaconState
|
||||
State: ðpbv2.BeaconStateContainer_BellatrixState{BellatrixState: protoState},
|
||||
},
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
Finalized: isFinalized,
|
||||
}, nil
|
||||
case version.Capella:
|
||||
protoState, err := migration.BeaconStateCapellaToProto(beaconSt)
|
||||
@@ -94,6 +102,7 @@ func (ds *Server) GetBeaconStateV2(ctx context.Context, req *ethpbv2.BeaconState
|
||||
State: ðpbv2.BeaconStateContainer_CapellaState{CapellaState: protoState},
|
||||
},
|
||||
ExecutionOptimistic: isOptimistic,
|
||||
Finalized: isFinalized,
|
||||
}, nil
|
||||
default:
|
||||
return nil, status.Error(codes.Internal, "Unsupported state version")
|
||||
|
||||
@@ -33,6 +33,7 @@ func TestGetBeaconStateV2(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: &blockchainmock.ChainService{},
|
||||
OptimisticModeFetcher: &blockchainmock.ChainService{},
|
||||
FinalizationFetcher: &blockchainmock.ChainService{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetBeaconStateV2(context.Background(), ðpbv2.BeaconStateRequestV2{
|
||||
@@ -50,6 +51,7 @@ func TestGetBeaconStateV2(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: &blockchainmock.ChainService{},
|
||||
OptimisticModeFetcher: &blockchainmock.ChainService{},
|
||||
FinalizationFetcher: &blockchainmock.ChainService{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetBeaconStateV2(context.Background(), ðpbv2.BeaconStateRequestV2{
|
||||
@@ -67,6 +69,7 @@ func TestGetBeaconStateV2(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: &blockchainmock.ChainService{},
|
||||
OptimisticModeFetcher: &blockchainmock.ChainService{},
|
||||
FinalizationFetcher: &blockchainmock.ChainService{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetBeaconStateV2(context.Background(), ðpbv2.BeaconStateRequestV2{
|
||||
@@ -84,6 +87,7 @@ func TestGetBeaconStateV2(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: &blockchainmock.ChainService{},
|
||||
OptimisticModeFetcher: &blockchainmock.ChainService{},
|
||||
FinalizationFetcher: &blockchainmock.ChainService{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetBeaconStateV2(context.Background(), ðpbv2.BeaconStateRequestV2{
|
||||
@@ -109,6 +113,7 @@ func TestGetBeaconStateV2(t *testing.T) {
|
||||
},
|
||||
HeadFetcher: &blockchainmock.ChainService{},
|
||||
OptimisticModeFetcher: &blockchainmock.ChainService{Optimistic: true},
|
||||
FinalizationFetcher: &blockchainmock.ChainService{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetBeaconStateV2(context.Background(), ðpbv2.BeaconStateRequestV2{
|
||||
@@ -118,6 +123,39 @@ func TestGetBeaconStateV2(t *testing.T) {
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, true, resp.ExecutionOptimistic)
|
||||
})
|
||||
t.Run("finalized", func(t *testing.T) {
|
||||
parentRoot := [32]byte{'a'}
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.ParentRoot = parentRoot[:]
|
||||
root, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, ctx, db, blk)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
|
||||
|
||||
fakeState, _ := util.DeterministicGenesisStateBellatrix(t, 1)
|
||||
headerRoot, err := fakeState.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
chainService := &blockchainmock.ChainService{
|
||||
FinalizedRoots: map[[32]byte]bool{
|
||||
headerRoot: true,
|
||||
},
|
||||
}
|
||||
server := &Server{
|
||||
StateFetcher: &testutil.MockFetcher{
|
||||
BeaconState: fakeState,
|
||||
},
|
||||
HeadFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
FinalizationFetcher: chainService,
|
||||
BeaconDB: db,
|
||||
}
|
||||
resp, err := server.GetBeaconStateV2(context.Background(), ðpbv2.BeaconStateRequestV2{
|
||||
StateId: make([]byte, 0),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, true, resp.Finalized)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetBeaconStateSSZ(t *testing.T) {
|
||||
|
||||
@@ -17,4 +17,5 @@ type Server struct {
|
||||
StateFetcher statefetcher.Fetcher
|
||||
OptimisticModeFetcher blockchain.OptimisticModeFetcher
|
||||
ForkFetcher blockchain.ForkFetcher
|
||||
FinalizationFetcher blockchain.FinalizationFetcher
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ go_test(
|
||||
"//beacon-chain/execution/testing:go_default_library",
|
||||
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",
|
||||
"//beacon-chain/operations/attestations:go_default_library",
|
||||
"//beacon-chain/operations/blstoexec:go_default_library",
|
||||
"//beacon-chain/operations/slashings:go_default_library",
|
||||
"//beacon-chain/operations/synccommittee:go_default_library",
|
||||
"//beacon-chain/operations/voluntaryexits:go_default_library",
|
||||
@@ -69,6 +70,7 @@ go_test(
|
||||
"//beacon-chain/rpc/prysm/v1alpha1/validator:go_default_library",
|
||||
"//beacon-chain/rpc/testutil:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/sync/initial-sync/testing:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
@@ -76,7 +78,9 @@ go_test(
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//crypto/hash:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//encoding/ssz:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/eth/v1:go_default_library",
|
||||
"//proto/eth/v2:go_default_library",
|
||||
|
||||
@@ -355,6 +355,19 @@ func (vs *Server) ProduceBlockV2(ctx context.Context, req *ethpbv1.ProduceBlockR
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
capellaBlock, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Capella)
|
||||
if ok {
|
||||
block, err := migration.V1Alpha1BeaconBlockCapellaToV2(capellaBlock.Capella)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
|
||||
}
|
||||
return ðpbv2.ProduceBlockResponseV2{
|
||||
Version: ethpbv2.Version_CAPELLA,
|
||||
Data: ðpbv2.BeaconBlockContainerV2{
|
||||
Block: ðpbv2.BeaconBlockContainerV2_CapellaBlock{CapellaBlock: block},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return nil, status.Error(codes.InvalidArgument, "Unsupported block type")
|
||||
}
|
||||
|
||||
@@ -432,6 +445,21 @@ func (vs *Server) ProduceBlockV2SSZ(ctx context.Context, req *ethpbv1.ProduceBlo
|
||||
Data: sszBlock,
|
||||
}, nil
|
||||
}
|
||||
capellaBlock, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Capella)
|
||||
if ok {
|
||||
block, err := migration.V1Alpha1BeaconBlockCapellaToV2(capellaBlock.Capella)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
|
||||
}
|
||||
sszBlock, err := block.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not marshal block into SSZ format: %v", err)
|
||||
}
|
||||
return ðpbv2.SSZContainer{
|
||||
Version: ethpbv2.Version_CAPELLA,
|
||||
Data: sszBlock,
|
||||
}, nil
|
||||
}
|
||||
return nil, status.Error(codes.InvalidArgument, "Unsupported block type")
|
||||
}
|
||||
|
||||
@@ -456,14 +484,14 @@ func (vs *Server) ProduceBlindedBlock(ctx context.Context, req *ethpbv1.ProduceB
|
||||
RandaoReveal: req.RandaoReveal,
|
||||
Graffiti: req.Graffiti,
|
||||
}
|
||||
v1alpha1resp, err := vs.V1Alpha1Server.GetBeaconBlock(ctx, v1alpha1req)
|
||||
if err != nil {
|
||||
// We simply return err because it's already of a gRPC error type.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Before Bellatrix, return normal block.
|
||||
if req.Slot < primitives.Slot(params.BeaconConfig().BellatrixForkEpoch)*params.BeaconConfig().SlotsPerEpoch {
|
||||
v1alpha1resp, err := vs.V1Alpha1Server.GetBeaconBlock(ctx, v1alpha1req)
|
||||
if err != nil {
|
||||
// We simply return err because it's already of a gRPC error type.
|
||||
return nil, err
|
||||
}
|
||||
phase0Block, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Phase0)
|
||||
if ok {
|
||||
block, err := migration.V1Alpha1ToV1Block(phase0Block.Phase0)
|
||||
@@ -491,8 +519,6 @@ func (vs *Server) ProduceBlindedBlock(ctx context.Context, req *ethpbv1.ProduceB
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// After Bellatrix, return blinded block.
|
||||
optimistic, err := vs.OptimisticModeFetcher.IsOptimistic(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not determine if the node is a optimistic node: %v", err)
|
||||
@@ -500,20 +526,33 @@ func (vs *Server) ProduceBlindedBlock(ctx context.Context, req *ethpbv1.ProduceB
|
||||
if optimistic {
|
||||
return nil, status.Errorf(codes.Unavailable, "The node is currently optimistic and cannot serve validators")
|
||||
}
|
||||
b, err := vs.V1Alpha1Server.GetBeaconBlock(ctx, v1alpha1req)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Unavailable, "Could not get block from prysm API")
|
||||
bellatrixBlock, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_BlindedBellatrix)
|
||||
if ok {
|
||||
blk, err := migration.V1Alpha1BeaconBlockBlindedBellatrixToV2Blinded(bellatrixBlock.BlindedBellatrix)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
|
||||
}
|
||||
return ðpbv2.ProduceBlindedBlockResponse{
|
||||
Version: ethpbv2.Version_BELLATRIX,
|
||||
Data: ðpbv2.BlindedBeaconBlockContainer{
|
||||
Block: ðpbv2.BlindedBeaconBlockContainer_BellatrixBlock{BellatrixBlock: blk},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
blk, err := migration.V1Alpha1BeaconBlockBlindedBellatrixToV2Blinded(b.GetBlindedBellatrix())
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
|
||||
capellaBlock, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_BlindedCapella)
|
||||
if ok {
|
||||
blk, err := migration.V1Alpha1BeaconBlockBlindedCapellaToV2Blinded(capellaBlock.BlindedCapella)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
|
||||
}
|
||||
return ðpbv2.ProduceBlindedBlockResponse{
|
||||
Version: ethpbv2.Version_CAPELLA,
|
||||
Data: ðpbv2.BlindedBeaconBlockContainer{
|
||||
Block: ðpbv2.BlindedBeaconBlockContainer_CapellaBlock{CapellaBlock: blk},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return ðpbv2.ProduceBlindedBlockResponse{
|
||||
Version: ethpbv2.Version_BELLATRIX,
|
||||
Data: ðpbv2.BlindedBeaconBlockContainer{
|
||||
Block: ðpbv2.BlindedBeaconBlockContainer_BellatrixBlock{BellatrixBlock: blk},
|
||||
},
|
||||
}, nil
|
||||
return nil, status.Error(codes.InvalidArgument, "Unsupported block type")
|
||||
}
|
||||
|
||||
// ProduceBlindedBlockSSZ requests the beacon node to produce a valid unsigned blinded beacon block,
|
||||
@@ -586,6 +625,21 @@ func (vs *Server) ProduceBlindedBlockSSZ(ctx context.Context, req *ethpbv1.Produ
|
||||
Data: sszBlock,
|
||||
}, nil
|
||||
}
|
||||
capellaBlock, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Capella)
|
||||
if ok {
|
||||
block, err := migration.V1Alpha1BeaconBlockCapellaToV2Blinded(capellaBlock.Capella)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
|
||||
}
|
||||
sszBlock, err := block.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not marshal block into SSZ format: %v", err)
|
||||
}
|
||||
return ðpbv2.SSZContainer{
|
||||
Version: ethpbv2.Version_CAPELLA,
|
||||
Data: sszBlock,
|
||||
}, nil
|
||||
}
|
||||
return nil, status.Error(codes.InvalidArgument, "Unsupported block type")
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -180,6 +180,7 @@ go_test(
|
||||
"proposer_attestations_test.go",
|
||||
"proposer_bellatrix_test.go",
|
||||
"proposer_builder_test.go",
|
||||
"proposer_capella_test.go",
|
||||
"proposer_deposits_test.go",
|
||||
"proposer_empty_block_test.go",
|
||||
"proposer_execution_payload_test.go",
|
||||
|
||||
@@ -134,14 +134,17 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
return nil, status.Errorf(codes.Internal, "Could not convert block to proto: %v", err)
|
||||
}
|
||||
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().CapellaForkEpoch {
|
||||
if blk.IsBlinded() {
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: pb.(*ethpb.BlindedBeaconBlockCapella)}}, nil
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Capella{Capella: pb.(*ethpb.BeaconBlockCapella)}}, nil
|
||||
}
|
||||
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().BellatrixForkEpoch && !blk.IsBlinded() {
|
||||
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().BellatrixForkEpoch {
|
||||
if blk.IsBlinded() {
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: pb.(*ethpb.BlindedBeaconBlockBellatrix)}}, nil
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Bellatrix{Bellatrix: pb.(*ethpb.BeaconBlockBellatrix)}}, nil
|
||||
}
|
||||
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().BellatrixForkEpoch && blk.IsBlinded() {
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: pb.(*ethpb.BlindedBeaconBlockBellatrix)}}, nil
|
||||
}
|
||||
if slots.ToEpoch(req.Slot) >= params.BeaconConfig().AltairForkEpoch {
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Altair{Altair: pb.(*ethpb.BeaconBlockAltair)}}, nil
|
||||
}
|
||||
@@ -248,9 +251,16 @@ func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.
|
||||
return nil, fmt.Errorf("could not tree hash block: %v", err)
|
||||
}
|
||||
|
||||
blk, err = vs.unblindBuilderBlock(ctx, blk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if slots.ToEpoch(blk.Block().Slot()) >= params.BeaconConfig().CapellaForkEpoch {
|
||||
blk, err = vs.unblindBuilderBlockCapella(ctx, blk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
blk, err = vs.unblindBuilderBlock(ctx, blk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Do not block proposal critical path with debug logging or block feed updates.
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
consensusblocks "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/runtime/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Sets the bls to exec data for a block.
|
||||
@@ -27,3 +34,111 @@ func (vs *Server) setBlsToExecData(blk interfaces.BeaconBlock, headState state.B
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (vs *Server) unblindBuilderBlockCapella(ctx context.Context, b interfaces.SignedBeaconBlock) (interfaces.SignedBeaconBlock, error) {
|
||||
if err := consensusblocks.BeaconBlockIsNil(b); err != nil {
|
||||
return nil, errors.Wrap(err, "block is nil")
|
||||
}
|
||||
|
||||
// No-op if the input block is not version blind and capella.
|
||||
if b.Version() != version.Capella || !b.IsBlinded() {
|
||||
return b, nil
|
||||
}
|
||||
// No-op nothing if the builder has not been configured.
|
||||
if !vs.BlockBuilder.Configured() {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
agg, err := b.Block().Body().SyncAggregate()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get sync aggregate")
|
||||
}
|
||||
h, err := b.Block().Body().Execution()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get execution header")
|
||||
}
|
||||
header, ok := h.Proto().(*enginev1.ExecutionPayloadHeaderCapella)
|
||||
if !ok {
|
||||
return nil, errors.New("execution data must be execution payload header capella")
|
||||
}
|
||||
parentRoot := b.Block().ParentRoot()
|
||||
stateRoot := b.Block().StateRoot()
|
||||
randaoReveal := b.Block().Body().RandaoReveal()
|
||||
graffiti := b.Block().Body().Graffiti()
|
||||
sig := b.Signature()
|
||||
sb := ðpb.SignedBlindedBeaconBlockCapella{
|
||||
Block: ðpb.BlindedBeaconBlockCapella{
|
||||
Slot: b.Block().Slot(),
|
||||
ProposerIndex: b.Block().ProposerIndex(),
|
||||
ParentRoot: parentRoot[:],
|
||||
StateRoot: stateRoot[:],
|
||||
Body: ðpb.BlindedBeaconBlockBodyCapella{
|
||||
RandaoReveal: randaoReveal[:],
|
||||
Eth1Data: b.Block().Body().Eth1Data(),
|
||||
Graffiti: graffiti[:],
|
||||
ProposerSlashings: b.Block().Body().ProposerSlashings(),
|
||||
AttesterSlashings: b.Block().Body().AttesterSlashings(),
|
||||
Attestations: b.Block().Body().Attestations(),
|
||||
Deposits: b.Block().Body().Deposits(),
|
||||
VoluntaryExits: b.Block().Body().VoluntaryExits(),
|
||||
SyncAggregate: agg,
|
||||
ExecutionPayloadHeader: header,
|
||||
},
|
||||
},
|
||||
Signature: sig[:],
|
||||
}
|
||||
|
||||
wrappedSb, err := consensusblocks.NewSignedBeaconBlock(sb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create signed block")
|
||||
}
|
||||
|
||||
payload, err := vs.BlockBuilder.SubmitBlindedBlock(ctx, wrappedSb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not submit blinded block")
|
||||
}
|
||||
|
||||
capellaPayload, err := payload.PbCapella()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get payload")
|
||||
}
|
||||
bb := ðpb.SignedBeaconBlockCapella{
|
||||
Block: ðpb.BeaconBlockCapella{
|
||||
Slot: sb.Block.Slot,
|
||||
ProposerIndex: sb.Block.ProposerIndex,
|
||||
ParentRoot: sb.Block.ParentRoot,
|
||||
StateRoot: sb.Block.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyCapella{
|
||||
RandaoReveal: sb.Block.Body.RandaoReveal,
|
||||
Eth1Data: sb.Block.Body.Eth1Data,
|
||||
Graffiti: sb.Block.Body.Graffiti,
|
||||
ProposerSlashings: sb.Block.Body.ProposerSlashings,
|
||||
AttesterSlashings: sb.Block.Body.AttesterSlashings,
|
||||
Attestations: sb.Block.Body.Attestations,
|
||||
Deposits: sb.Block.Body.Deposits,
|
||||
VoluntaryExits: sb.Block.Body.VoluntaryExits,
|
||||
SyncAggregate: agg,
|
||||
ExecutionPayload: capellaPayload,
|
||||
},
|
||||
},
|
||||
Signature: sb.Signature,
|
||||
}
|
||||
wb, err := consensusblocks.NewSignedBeaconBlock(bb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create signed block")
|
||||
}
|
||||
|
||||
txs, err := payload.Transactions()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get transactions from payload")
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockHash": fmt.Sprintf("%#x", h.BlockHash()),
|
||||
"feeRecipient": fmt.Sprintf("%#x", h.FeeRecipient()),
|
||||
"gasUsed": h.GasUsed,
|
||||
"slot": b.Block().Slot(),
|
||||
"txs": len(txs),
|
||||
}).Info("Retrieved full capella payload from builder")
|
||||
|
||||
return wb, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
builderTest "github.com/prysmaticlabs/prysm/v3/beacon-chain/builder/testing"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/ssz"
|
||||
v1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/util"
|
||||
)
|
||||
|
||||
func TestServer_unblindBuilderCapellaBlock(t *testing.T) {
|
||||
p := emptyPayloadCapella()
|
||||
p.GasLimit = 123
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
blk interfaces.SignedBeaconBlock
|
||||
mock *builderTest.MockBuilderService
|
||||
err string
|
||||
returnedBlk interfaces.SignedBeaconBlock
|
||||
}{
|
||||
{
|
||||
name: "nil block",
|
||||
blk: nil,
|
||||
err: "signed beacon block can't be nil",
|
||||
},
|
||||
{
|
||||
name: "old block version",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
returnedBlk: func() interfaces.SignedBeaconBlock {
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "not configured",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockBellatrix())
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: false,
|
||||
},
|
||||
returnedBlk: func() interfaces.SignedBeaconBlock {
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockBellatrix())
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "submit blind block error",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBlindedBeaconBlockCapella()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
PayloadCapella: &v1.ExecutionPayloadCapella{},
|
||||
HasConfigured: true,
|
||||
ErrSubmitBlindedBlock: errors.New("can't submit"),
|
||||
},
|
||||
err: "can't submit",
|
||||
},
|
||||
{
|
||||
name: "can get payload",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBlindedBeaconBlockCapella()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
txRoot, err := ssz.TransactionsRoot([][]byte{})
|
||||
require.NoError(t, err)
|
||||
b.Block.Body.ExecutionPayloadHeader = &v1.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
TransactionsRoot: txRoot[:],
|
||||
GasLimit: 123,
|
||||
WithdrawalsRoot: make([]byte, fieldparams.RootLength),
|
||||
}
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
PayloadCapella: p,
|
||||
},
|
||||
returnedBlk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBeaconBlockCapella()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
b.Block.Body.ExecutionPayload = p
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
vs := &Server{BlockBuilder: tc.mock}
|
||||
gotBlk, err := vs.unblindBuilderBlockCapella(context.Background(), tc.blk)
|
||||
if tc.err != "" {
|
||||
require.ErrorContains(t, tc.err, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, tc.returnedBlk, gotBlk)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -141,19 +141,36 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot primitives.Slot,
|
||||
SafeBlockHash: finalizedBlockHash,
|
||||
FinalizedBlockHash: finalizedBlockHash,
|
||||
}
|
||||
|
||||
p := &enginev1.PayloadAttributes{
|
||||
Timestamp: uint64(t.Unix()),
|
||||
PrevRandao: random,
|
||||
SuggestedFeeRecipient: feeRecipient.Bytes(),
|
||||
var attr payloadattribute.Attributer
|
||||
switch st.Version() {
|
||||
case version.Capella:
|
||||
withdrawals, err := st.ExpectedWithdrawals()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV2{
|
||||
Timestamp: uint64(t.Unix()),
|
||||
PrevRandao: random,
|
||||
SuggestedFeeRecipient: feeRecipient.Bytes(),
|
||||
Withdrawals: withdrawals,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case version.Bellatrix:
|
||||
attr, err = payloadattribute.New(&enginev1.PayloadAttributes{
|
||||
Timestamp: uint64(t.Unix()),
|
||||
PrevRandao: random,
|
||||
SuggestedFeeRecipient: feeRecipient.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("unknown beacon state version")
|
||||
}
|
||||
|
||||
// This will change in subsequent hardforks like Capella.
|
||||
pa, err := payloadattribute.New(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payloadID, _, err := vs.ExecutionEngineCaller.ForkchoiceUpdated(ctx, f, pa)
|
||||
payloadID, _, err := vs.ExecutionEngineCaller.ForkchoiceUpdated(ctx, f, attr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not prepare payload")
|
||||
}
|
||||
@@ -237,5 +254,21 @@ func emptyPayload() *enginev1.ExecutionPayload {
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
Transactions: make([][]byte, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func emptyPayloadCapella() *enginev1.ExecutionPayloadCapella {
|
||||
return &enginev1.ExecutionPayloadCapella{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
Transactions: make([][]byte, 0),
|
||||
Withdrawals: make([]*enginev1.Withdrawal, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,18 @@ func TestServer_getExecutionPayload(t *testing.T) {
|
||||
Root: b2r[:],
|
||||
}))
|
||||
|
||||
capellaTransitionState, _ := util.DeterministicGenesisStateCapella(t, 1)
|
||||
wrappedHeaderCapella, err := blocks.WrappedExecutionPayloadHeaderCapella(&pb.ExecutionPayloadHeaderCapella{BlockNumber: 1})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, capellaTransitionState.SetLatestExecutionPayloadHeader(wrappedHeaderCapella))
|
||||
b2pbCapella := util.NewBeaconBlockCapella()
|
||||
b2rCapella, err := b2pbCapella.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, context.Background(), beaconDB, b2pbCapella)
|
||||
require.NoError(t, capellaTransitionState.SetFinalizedCheckpoint(ðpb.Checkpoint{
|
||||
Root: b2rCapella[:],
|
||||
}))
|
||||
|
||||
require.NoError(t, beaconDB.SaveFeeRecipientsByValidatorIDs(context.Background(), []primitives.ValidatorIndex{0}, []common.Address{{}}))
|
||||
|
||||
tests := []struct {
|
||||
@@ -87,6 +99,12 @@ func TestServer_getExecutionPayload(t *testing.T) {
|
||||
payloadID: &pb.PayloadIDBytes{0x1},
|
||||
validatorIndx: 1,
|
||||
},
|
||||
{
|
||||
name: "transition completed, capella, happy case (doesn't have fee recipient in Db)",
|
||||
st: capellaTransitionState,
|
||||
payloadID: &pb.PayloadIDBytes{0x1},
|
||||
validatorIndx: 1,
|
||||
},
|
||||
{
|
||||
name: "transition completed, happy case, (payload ID cached)",
|
||||
st: transitionSt,
|
||||
|
||||
@@ -534,6 +534,16 @@ func TestProposer_ProposeBlock_OK(t *testing.T) {
|
||||
return ðpb.GenericSignedBeaconBlock{Block: blk}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "blind capella",
|
||||
block: func(parent [32]byte) *ethpb.GenericSignedBeaconBlock {
|
||||
blockToPropose := util.NewBlindedBeaconBlockCapella()
|
||||
blockToPropose.Block.Slot = 5
|
||||
blockToPropose.Block.ParentRoot = parent[:]
|
||||
blk := ðpb.GenericSignedBeaconBlock_BlindedCapella{BlindedCapella: blockToPropose}
|
||||
return ðpb.GenericSignedBeaconBlock{Block: blk}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -561,6 +571,7 @@ func TestProposer_ProposeBlock_OK(t *testing.T) {
|
||||
HeadFetcher: c,
|
||||
BlockNotifier: c.BlockNotifier(),
|
||||
P2P: mockp2p.NewTestP2P(t),
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, PayloadCapella: emptyPayloadCapella()},
|
||||
}
|
||||
blockToPropose := tt.block(bsRoot)
|
||||
res, err := proposerServer.ProposeBeaconBlock(context.Background(), blockToPropose)
|
||||
|
||||
@@ -358,6 +358,7 @@ func (s *Service) Start() {
|
||||
},
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
ForkFetcher: s.cfg.ForkFetcher,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
}
|
||||
ethpbv1alpha1.RegisterDebugServer(s.grpcServer, debugServer)
|
||||
ethpbservice.RegisterBeaconDebugServer(s.grpcServer, debugServerV1)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/crypto/rand"
|
||||
@@ -8,6 +12,8 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
)
|
||||
|
||||
const broadcastBLSChangesRateLimit = 128
|
||||
|
||||
// This routine broadcasts known BLS changes at the Capella fork.
|
||||
func (s *Service) broadcastBLSChanges(currSlot types.Slot) {
|
||||
capellaSlotStart, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
|
||||
@@ -26,11 +32,57 @@ func (s *Service) broadcastBLSChanges(currSlot types.Slot) {
|
||||
return
|
||||
}
|
||||
source := rand.NewGenerator()
|
||||
broadcastChanges := make([]*ethpb.SignedBLSToExecutionChange, len(changes))
|
||||
for i := 0; i < len(changes); i++ {
|
||||
length := len(changes)
|
||||
broadcastChanges := make([]*ethpb.SignedBLSToExecutionChange, length)
|
||||
for i := 0; i < length; i++ {
|
||||
idx := source.Intn(len(changes))
|
||||
broadcastChanges[i] = changes[idx]
|
||||
changes = append(changes[:idx], changes[idx+1:]...)
|
||||
}
|
||||
s.cfg.p2p.BroadcastBLSChanges(s.ctx, broadcastChanges)
|
||||
|
||||
go s.rateBLSChanges(s.ctx, broadcastChanges)
|
||||
}
|
||||
|
||||
func (s *Service) broadcastBLSBatch(ctx context.Context, ptr *[]*ethpb.SignedBLSToExecutionChange) {
|
||||
limit := broadcastBLSChangesRateLimit
|
||||
if len(*ptr) < broadcastBLSChangesRateLimit {
|
||||
limit = len(*ptr)
|
||||
}
|
||||
st, err := s.cfg.chain.HeadStateReadOnly(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not get head state")
|
||||
return
|
||||
}
|
||||
for _, ch := range (*ptr)[:limit] {
|
||||
if ch != nil {
|
||||
_, err := blocks.ValidateBLSToExecutionChange(st, ch)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not validate BLS to execution change")
|
||||
continue
|
||||
}
|
||||
if err := s.cfg.p2p.Broadcast(ctx, ch); err != nil {
|
||||
log.WithError(err).Error("could not broadcast BLS to execution changes.")
|
||||
}
|
||||
}
|
||||
}
|
||||
*ptr = (*ptr)[limit:]
|
||||
}
|
||||
|
||||
func (s *Service) rateBLSChanges(ctx context.Context, changes []*ethpb.SignedBLSToExecutionChange) {
|
||||
s.broadcastBLSBatch(ctx, &changes)
|
||||
if len(changes) == 0 {
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(500 * time.Millisecond)
|
||||
for {
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
s.broadcastBLSBatch(ctx, &changes)
|
||||
if len(changes) == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,22 @@ import (
|
||||
"time"
|
||||
|
||||
mockChain "github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/signing"
|
||||
testingdb "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/operations/blstoexec"
|
||||
mockp2p "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/testing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state/stategen"
|
||||
mockSync "github.com/prysmaticlabs/prysm/v3/beacon-chain/sync/initial-sync/testing"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/util"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func TestBroadcastBLSChanges(t *testing.T) {
|
||||
@@ -46,3 +55,107 @@ func TestBroadcastBLSChanges(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
s.broadcastBLSChanges(capellaStart + 1)
|
||||
}
|
||||
|
||||
func TestRateBLSChanges(t *testing.T) {
|
||||
logHook := logTest.NewGlobal()
|
||||
params.SetupTestConfigCleanup(t)
|
||||
c := params.BeaconConfig()
|
||||
c.CapellaForkEpoch = c.BellatrixForkEpoch.Add(2)
|
||||
params.OverrideBeaconConfig(c)
|
||||
chainService := &mockChain.ChainService{
|
||||
Genesis: time.Now(),
|
||||
ValidatorsRoot: [32]byte{'A'},
|
||||
}
|
||||
p1 := mockp2p.NewTestP2P(t)
|
||||
s := NewService(context.Background(),
|
||||
WithP2P(p1),
|
||||
WithInitialSync(&mockSync.Sync{IsSyncing: false}),
|
||||
WithChainService(chainService),
|
||||
WithStateNotifier(chainService.StateNotifier()),
|
||||
WithOperationNotifier(chainService.OperationNotifier()),
|
||||
WithBlsToExecPool(blstoexec.NewPool()),
|
||||
)
|
||||
beaconDB := testingdb.SetupDB(t)
|
||||
s.cfg.stateGen = stategen.New(beaconDB, doublylinkedtree.New())
|
||||
s.cfg.beaconDB = beaconDB
|
||||
s.initCaches()
|
||||
st, keys := util.DeterministicGenesisStateCapella(t, 256)
|
||||
s.cfg.chain = &mockChain.ChainService{
|
||||
ValidatorsRoot: [32]byte{'A'},
|
||||
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(10)),
|
||||
State: st,
|
||||
}
|
||||
|
||||
for i := 0; i < 200; i++ {
|
||||
message := ðpb.BLSToExecutionChange{
|
||||
ValidatorIndex: primitives.ValidatorIndex(i),
|
||||
FromBlsPubkey: keys[i+1].PublicKey().Marshal(),
|
||||
ToExecutionAddress: bytesutil.PadTo([]byte("address"), 20),
|
||||
}
|
||||
epoch := params.BeaconConfig().CapellaForkEpoch + 1
|
||||
domain, err := signing.Domain(st.Fork(), epoch, params.BeaconConfig().DomainBLSToExecutionChange, st.GenesisValidatorsRoot())
|
||||
assert.NoError(t, err)
|
||||
htr, err := signing.SigningData(message.HashTreeRoot, domain)
|
||||
assert.NoError(t, err)
|
||||
signed := ðpb.SignedBLSToExecutionChange{
|
||||
Message: message,
|
||||
Signature: keys[i+1].Sign(htr[:]).Marshal(),
|
||||
}
|
||||
|
||||
s.cfg.blsToExecPool.InsertBLSToExecChange(signed)
|
||||
}
|
||||
|
||||
require.Equal(t, false, p1.BroadcastCalled)
|
||||
slot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
|
||||
require.NoError(t, err)
|
||||
s.broadcastBLSChanges(slot)
|
||||
time.Sleep(100 * time.Millisecond) // Need a sleep for the go routine to be ready
|
||||
require.Equal(t, true, p1.BroadcastCalled)
|
||||
require.LogsDoNotContain(t, logHook, "could not")
|
||||
|
||||
p1.BroadcastCalled = false
|
||||
time.Sleep(500 * time.Millisecond) // Need a sleep for the second batch to be broadcast
|
||||
require.Equal(t, true, p1.BroadcastCalled)
|
||||
require.LogsDoNotContain(t, logHook, "could not")
|
||||
}
|
||||
|
||||
func TestBroadcastBLSBatch_changes_slice(t *testing.T) {
|
||||
message := ðpb.BLSToExecutionChange{
|
||||
FromBlsPubkey: make([]byte, 48),
|
||||
ToExecutionAddress: make([]byte, 20),
|
||||
}
|
||||
signed := ðpb.SignedBLSToExecutionChange{
|
||||
Message: message,
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
changes := make([]*ethpb.SignedBLSToExecutionChange, 200)
|
||||
for i := 0; i < len(changes); i++ {
|
||||
changes[i] = signed
|
||||
}
|
||||
p1 := mockp2p.NewTestP2P(t)
|
||||
chainService := &mockChain.ChainService{
|
||||
Genesis: time.Now(),
|
||||
ValidatorsRoot: [32]byte{'A'},
|
||||
}
|
||||
s := NewService(context.Background(),
|
||||
WithP2P(p1),
|
||||
WithInitialSync(&mockSync.Sync{IsSyncing: false}),
|
||||
WithChainService(chainService),
|
||||
WithStateNotifier(chainService.StateNotifier()),
|
||||
WithOperationNotifier(chainService.OperationNotifier()),
|
||||
WithBlsToExecPool(blstoexec.NewPool()),
|
||||
)
|
||||
beaconDB := testingdb.SetupDB(t)
|
||||
s.cfg.stateGen = stategen.New(beaconDB, doublylinkedtree.New())
|
||||
s.cfg.beaconDB = beaconDB
|
||||
s.initCaches()
|
||||
st, _ := util.DeterministicGenesisStateCapella(t, 32)
|
||||
s.cfg.chain = &mockChain.ChainService{
|
||||
ValidatorsRoot: [32]byte{'A'},
|
||||
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(10)),
|
||||
State: st,
|
||||
}
|
||||
|
||||
s.broadcastBLSBatch(s.ctx, &changes)
|
||||
require.Equal(t, 200-128, len(changes))
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func (s *Service) validateBlsToExecutionChange(ctx context.Context, pid peer.ID,
|
||||
if s.cfg.blsToExecPool.ValidatorExists(blsChange.Message.ValidatorIndex) {
|
||||
return pubsub.ValidationIgnore, nil
|
||||
}
|
||||
st, err := s.cfg.chain.HeadState(ctx)
|
||||
st, err := s.cfg.chain.HeadStateReadOnly(ctx)
|
||||
if err != nil {
|
||||
return pubsub.ValidationIgnore, err
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ func SepoliaConfig() *BeaconChainConfig {
|
||||
cfg.AltairForkVersion = []byte{0x90, 0x00, 0x00, 0x70}
|
||||
cfg.BellatrixForkEpoch = 100
|
||||
cfg.BellatrixForkVersion = []byte{0x90, 0x00, 0x00, 0x71}
|
||||
cfg.CapellaForkEpoch = 56832
|
||||
cfg.CapellaForkVersion = []byte{0x90, 0x00, 0x00, 0x72}
|
||||
cfg.TerminalTotalDifficulty = "17000000000000000"
|
||||
cfg.DepositContractAddress = "0x7f02C3E3c98b133055B8B348B2Ac625669Ed295D"
|
||||
|
||||
@@ -24,6 +24,21 @@ type Node[T any] struct {
|
||||
next *Node[T]
|
||||
}
|
||||
|
||||
// Copy returns a copy of the origina list.
|
||||
func (l *List[T]) Copy() *List[T] {
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
list := &List[T]{}
|
||||
if l.len == 0 {
|
||||
return list
|
||||
}
|
||||
for n := l.First(); n != nil; n = n.next {
|
||||
list.Append(n.Copy())
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// First gets the reference to the first node in the list.
|
||||
func (l *List[T]) First() *Node[T] {
|
||||
return l.first
|
||||
@@ -111,3 +126,12 @@ func (n *Node[T]) Value() (T, error) {
|
||||
}
|
||||
return n.value, nil
|
||||
}
|
||||
|
||||
// Copy copies the given node and returns a new one. It does not do a deep copy
|
||||
// of T.
|
||||
func (n *Node[T]) Copy() *Node[T] {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
return NewNode(n.value)
|
||||
}
|
||||
|
||||
@@ -123,3 +123,36 @@ func TestRemove(t *testing.T) {
|
||||
require.Equal(t, 2, list.len)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNodeCopy(t *testing.T) {
|
||||
first := NewNode(1)
|
||||
second := first.Copy()
|
||||
v, err := second.Value()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, first.value, v)
|
||||
}
|
||||
|
||||
func TestListCopy(t *testing.T) {
|
||||
list := &List[int]{}
|
||||
first := NewNode(1)
|
||||
second := NewNode(2)
|
||||
third := NewNode(3)
|
||||
list.Append(first)
|
||||
list.Append(second)
|
||||
list.Append(third)
|
||||
|
||||
copied := list.Copy()
|
||||
require.Equal(t, 3, copied.Len())
|
||||
m := copied.First()
|
||||
for n := list.First(); n != nil; n = n.next {
|
||||
nv, err := n.Value()
|
||||
require.NoError(t, err)
|
||||
mv, err := m.Value()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nv, mv)
|
||||
|
||||
require.NotEqual(t, n, m)
|
||||
m, err = m.Next()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,3 +382,12 @@ func Unique[T comparable](a []T) []T {
|
||||
}
|
||||
return result[:end]
|
||||
}
|
||||
|
||||
// Reverse reverses any slice in place
|
||||
// Taken from https://github.com/faiface/generics/blob/8cf65f0b43803410724d8c671cb4d328543ba07d/examples/sliceutils/sliceutils.go
|
||||
func Reverse[E any](s []E) []E {
|
||||
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -599,3 +599,20 @@ func TestUnique(t *testing.T) {
|
||||
require.DeepEqual(t, []uint64{1, 2}, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestReverse(t *testing.T) {
|
||||
tests := []struct {
|
||||
value [][32]byte
|
||||
want [][32]byte
|
||||
}{
|
||||
{[][32]byte{}, [][32]byte{}},
|
||||
{[][32]byte{{'A'}, {'B'}, {'C'}, {'D'}},
|
||||
[][32]byte{{'D'}, {'C'}, {'B'}, {'A'}}},
|
||||
{[][32]byte{{1}, {2}, {3}, {4}},
|
||||
[][32]byte{{4}, {3}, {2}, {1}}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
b := slice.Reverse(tt.value)
|
||||
require.DeepEqual(t, tt.want, b)
|
||||
}
|
||||
}
|
||||
|
||||
15
container/thread-safe/BUILD.bazel
Normal file
15
container/thread-safe/BUILD.bazel
Normal file
@@ -0,0 +1,15 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["map.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/container/thread-safe",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["map_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = ["//testing/require:go_default_library"],
|
||||
)
|
||||
59
container/thread-safe/map.go
Normal file
59
container/thread-safe/map.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// Package threadsafe contains generic containers that are
|
||||
// protected either by Mutexes or atomics underneath the hood.
|
||||
package threadsafe
|
||||
|
||||
import "sync"
|
||||
|
||||
// Map implements a simple thread-safe map protected by a mutex.
|
||||
type Map[K comparable, V any] struct {
|
||||
items map[K]V
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// NewThreadSafeMap returns a thread-safe map instance from a normal map.
|
||||
func NewThreadSafeMap[K comparable, V any](m map[K]V) *Map[K, V] {
|
||||
return &Map[K, V]{
|
||||
items: m,
|
||||
}
|
||||
}
|
||||
|
||||
// Keys returns the keys of a thread-safe map.
|
||||
func (m *Map[K, V]) Keys() []K {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
r := make([]K, 0, len(m.items))
|
||||
for k := range m.items {
|
||||
key := k
|
||||
r = append(r, key)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Len of the thread-safe map.
|
||||
func (m *Map[K, V]) Len() int {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
return len(m.items)
|
||||
}
|
||||
|
||||
// Get an item from a thread-safe map.
|
||||
func (m *Map[K, V]) Get(k K) (V, bool) {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
v, ok := m.items[k]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// Put an item into a thread-safe map.
|
||||
func (m *Map[K, V]) Put(k K, v V) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
m.items[k] = v
|
||||
}
|
||||
|
||||
// Delete an item from a thread-safe map.
|
||||
func (m *Map[K, V]) Delete(k K) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
delete(m.items, k)
|
||||
}
|
||||
97
container/thread-safe/map_test.go
Normal file
97
container/thread-safe/map_test.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package threadsafe
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
)
|
||||
|
||||
type safeMap struct {
|
||||
items map[int]string
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *safeMap) Get(k int) (string, bool) {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
v, ok := s.items[k]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func (s *safeMap) Put(i int, str string) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
s.items[i] = str
|
||||
}
|
||||
|
||||
func (s *safeMap) Delete(i int) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
delete(s.items, i)
|
||||
}
|
||||
|
||||
func BenchmarkMap_Concrete(b *testing.B) {
|
||||
mm := &safeMap{
|
||||
items: make(map[int]string),
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := 0; j < 1000; j++ {
|
||||
mm.Put(j, "foo")
|
||||
mm.Get(j)
|
||||
mm.Delete(j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMap_Generic(b *testing.B) {
|
||||
items := make(map[int]string)
|
||||
mm := NewThreadSafeMap(items)
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := 0; j < 1000; j++ {
|
||||
mm.Put(j, "foo")
|
||||
mm.Get(j)
|
||||
mm.Delete(j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
m := map[int]string{
|
||||
1: "foo",
|
||||
200: "bar",
|
||||
10000: "baz",
|
||||
}
|
||||
|
||||
tMap := NewThreadSafeMap(m)
|
||||
keys := tMap.Keys()
|
||||
sort.IntSlice(keys).Sort()
|
||||
|
||||
require.DeepEqual(t, []int{1, 200, 10000}, keys)
|
||||
require.Equal(t, 3, tMap.Len())
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func(w *sync.WaitGroup, scopedMap *Map[int, string]) {
|
||||
defer w.Done()
|
||||
v, ok := scopedMap.Get(1)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, "foo", v)
|
||||
|
||||
scopedMap.Put(3, "nyan")
|
||||
|
||||
v, ok = scopedMap.Get(3)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, "nyan", v)
|
||||
|
||||
}(&wg, tMap)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
tMap.Delete(3)
|
||||
|
||||
_, ok := tMap.Get(3)
|
||||
require.Equal(t, false, ok)
|
||||
}
|
||||
@@ -115,6 +115,14 @@ func Uint64ToBytesLittleEndian(i uint64) []byte {
|
||||
return buf
|
||||
}
|
||||
|
||||
// Uint64ToBytesLittleEndian32 conversion of a uint64 to a fix
|
||||
// sized 32 byte array in little endian order. Returns 32 byte array.
|
||||
func Uint64ToBytesLittleEndian32(i uint64) []byte {
|
||||
buf := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(buf, i)
|
||||
return buf
|
||||
}
|
||||
|
||||
// Uint64ToBytesBigEndian conversion.
|
||||
func Uint64ToBytesBigEndian(i uint64) []byte {
|
||||
buf := make([]byte, 8)
|
||||
|
||||
@@ -300,3 +300,26 @@ func TestUint64ToBytesLittleEndian(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint64ToBytesLittleEndian32(t *testing.T) {
|
||||
tests := []struct {
|
||||
value uint64
|
||||
want [32]byte
|
||||
}{
|
||||
{
|
||||
value: 0x01000000,
|
||||
want: [32]byte{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
value: 0x00000001,
|
||||
want: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("0x%08x", tt.value), func(t *testing.T) {
|
||||
if got := bytesutil.Uint64ToBytesLittleEndian32(tt.value); !bytes.Equal(got, tt.want[:]) {
|
||||
t.Errorf("Uint64ToBytesLittleEndian32() = got %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# Bash Scripts
|
||||
|
||||
This subproject contains useful bash scripts for working with our repository. We have a simple tool that outputs coverage, a simple tool to check for gazelle requirements, update Go protobuf generated files, visibility rules tools for Bazel packages, and more.
|
||||
|
||||
|
||||
## update-go-pbs.sh
|
||||
|
||||
This script generates the *.pb.go and *.pb.gw.go files from the *.proto files.
|
||||
After running `update-go-pbs.sh` keep only the *.pb.go and *pb.gw.go for the protos that have changed before checking in.
|
||||
*Note*: the generated files may not have imports correctly linted and will need to be fixed to remote associated errors.
|
||||
@@ -23,7 +23,7 @@ readonly system
|
||||
findutil="find"
|
||||
# On OSX `find` is not GNU find compatible, so require "findutils" package.
|
||||
if [ "$system" == "darwin" ]; then
|
||||
if [[ ! -x "/usr/local/bin/gfind" ]]; then
|
||||
if [[ ! -x "/usr/local/bin/gfind" && ! -x "/opt/homebrew/bin/gfind" ]]; then
|
||||
color 31 "Make sure that GNU 'findutils' package is installed: brew install findutils"
|
||||
exit 1
|
||||
else
|
||||
|
||||
158
proto/eth/v2/beacon_state.pb.go
generated
158
proto/eth/v2/beacon_state.pb.go
generated
@@ -869,6 +869,7 @@ type BeaconStateResponseV2 struct {
|
||||
Version Version `protobuf:"varint,1,opt,name=version,proto3,enum=ethereum.eth.v2.Version" json:"version,omitempty"`
|
||||
Data *BeaconStateContainer `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
||||
ExecutionOptimistic bool `protobuf:"varint,3,opt,name=execution_optimistic,json=executionOptimistic,proto3" json:"execution_optimistic,omitempty"`
|
||||
Finalized bool `protobuf:"varint,4,opt,name=finalized,proto3" json:"finalized,omitempty"`
|
||||
}
|
||||
|
||||
func (x *BeaconStateResponseV2) Reset() {
|
||||
@@ -924,6 +925,13 @@ func (x *BeaconStateResponseV2) GetExecutionOptimistic() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *BeaconStateResponseV2) GetFinalized() bool {
|
||||
if x != nil {
|
||||
return x.Finalized
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type BeaconStateSSZResponseV2 struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -1747,7 +1755,7 @@ var file_proto_eth_v2_beacon_state_proto_rawDesc = []byte{
|
||||
0x52, 0x6f, 0x6f, 0x74, 0x22, 0x31, 0x0a, 0x14, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74,
|
||||
0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x32, 0x12, 0x19, 0x0a, 0x08,
|
||||
0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07,
|
||||
0x73, 0x74, 0x61, 0x74, 0x65, 0x49, 0x64, 0x22, 0xb9, 0x01, 0x0a, 0x15, 0x42, 0x65, 0x61, 0x63,
|
||||
0x73, 0x74, 0x61, 0x74, 0x65, 0x49, 0x64, 0x22, 0xd7, 0x01, 0x0a, 0x15, 0x42, 0x65, 0x61, 0x63,
|
||||
0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56,
|
||||
0x32, 0x12, 0x32, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0e, 0x32, 0x18, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74,
|
||||
@@ -1759,81 +1767,83 @@ var file_proto_eth_v2_beacon_state_proto_rawDesc = []byte{
|
||||
0x12, 0x31, 0x0a, 0x14, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70,
|
||||
0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13,
|
||||
0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73,
|
||||
0x74, 0x69, 0x63, 0x22, 0x62, 0x0a, 0x18, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61,
|
||||
0x74, 0x65, 0x53, 0x53, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x32, 0x12,
|
||||
0x32, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
|
||||
0x32, 0x18, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e,
|
||||
0x76, 0x32, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xc3, 0x02, 0x0a, 0x14, 0x42, 0x65, 0x61, 0x63,
|
||||
0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
|
||||
0x12, 0x41, 0x0a, 0x0c, 0x70, 0x68, 0x61, 0x73, 0x65, 0x30, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
|
||||
0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53,
|
||||
0x74, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x70, 0x68, 0x61, 0x73, 0x65, 0x30, 0x53, 0x74,
|
||||
0x61, 0x74, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x61, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x5f, 0x73, 0x74,
|
||||
0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x74, 0x68, 0x65,
|
||||
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x65, 0x61, 0x63,
|
||||
0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x6c, 0x74, 0x61, 0x69,
|
||||
0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x62, 0x65, 0x6c, 0x6c, 0x61, 0x74,
|
||||
0x72, 0x69, 0x78, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x32, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x65, 0x6c,
|
||||
0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x48, 0x00, 0x52, 0x0e, 0x62, 0x65, 0x6c, 0x6c, 0x61, 0x74,
|
||||
0x72, 0x69, 0x78, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x63, 0x61, 0x70, 0x65,
|
||||
0x6c, 0x6c, 0x61, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x23, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x32, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x61, 0x70,
|
||||
0x65, 0x6c, 0x6c, 0x61, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x53,
|
||||
0x74, 0x61, 0x74, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x4e, 0x0a,
|
||||
0x17, 0x46, 0x6f, 0x72, 0x6b, 0x43, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x64, 0x73,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
|
||||
0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x43, 0x68, 0x6f,
|
||||
0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x64, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xba, 0x01,
|
||||
0x0a, 0x0e, 0x46, 0x6f, 0x72, 0x6b, 0x43, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x64,
|
||||
0x12, 0x1a, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06,
|
||||
0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x12, 0x59, 0x0a, 0x04,
|
||||
0x73, 0x6c, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d,
|
||||
0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76,
|
||||
0x33, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65,
|
||||
0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f,
|
||||
0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x78, 0x65, 0x63, 0x75,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x22, 0x97, 0x01, 0x0a, 0x0d, 0x52,
|
||||
0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08,
|
||||
0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07,
|
||||
0x73, 0x74, 0x61, 0x74, 0x65, 0x49, 0x64, 0x12, 0x61, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x46, 0x82, 0xb5, 0x18, 0x42, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63,
|
||||
0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x33, 0x2f, 0x63, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72,
|
||||
0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x00,
|
||||
0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65,
|
||||
0x70, 0x6f, 0x63, 0x68, 0x22, 0xc7, 0x01, 0x0a, 0x0e, 0x52, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x04, 0x64,
|
||||
0x61, 0x74, 0x61, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x13, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69,
|
||||
0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69,
|
||||
0x7a, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c,
|
||||
0x69, 0x7a, 0x65, 0x64, 0x1a, 0x28, 0x0a, 0x06, 0x52, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x12, 0x1e,
|
||||
0x0a, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06,
|
||||
0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x42, 0x83,
|
||||
0x01, 0x0a, 0x13, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x42, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d,
|
||||
0x69, 0x74, 0x74, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x32, 0x67, 0x69,
|
||||
0x74, 0x69, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65,
|
||||
0x64, 0x22, 0x62, 0x0a, 0x18, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65,
|
||||
0x53, 0x53, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x32, 0x12, 0x32, 0x0a,
|
||||
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32,
|
||||
0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xc3, 0x02, 0x0a, 0x14, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e,
|
||||
0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x41,
|
||||
0x0a, 0x0c, 0x70, 0x68, 0x61, 0x73, 0x65, 0x30, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61,
|
||||
0x74, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x70, 0x68, 0x61, 0x73, 0x65, 0x30, 0x53, 0x74, 0x61, 0x74,
|
||||
0x65, 0x12, 0x41, 0x0a, 0x0c, 0x61, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74,
|
||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
|
||||
0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e,
|
||||
0x53, 0x74, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x53,
|
||||
0x74, 0x61, 0x74, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x62, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69,
|
||||
0x78, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e,
|
||||
0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x65, 0x6c, 0x6c, 0x61,
|
||||
0x74, 0x72, 0x69, 0x78, 0x48, 0x00, 0x52, 0x0e, 0x62, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69,
|
||||
0x78, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x63, 0x61, 0x70, 0x65, 0x6c, 0x6c,
|
||||
0x61, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e,
|
||||
0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x61, 0x70, 0x65, 0x6c,
|
||||
0x6c, 0x61, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x53, 0x74, 0x61,
|
||||
0x74, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x4e, 0x0a, 0x17, 0x46,
|
||||
0x6f, 0x72, 0x6b, 0x43, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x64, 0x73, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x43, 0x68, 0x6f, 0x69, 0x63,
|
||||
0x65, 0x48, 0x65, 0x61, 0x64, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xba, 0x01, 0x0a, 0x0e,
|
||||
0x46, 0x6f, 0x72, 0x6b, 0x43, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x64, 0x12, 0x1a,
|
||||
0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5,
|
||||
0x18, 0x02, 0x33, 0x32, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x12, 0x59, 0x0a, 0x04, 0x73, 0x6c,
|
||||
0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69,
|
||||
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74,
|
||||
0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x33, 0x2f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x3b, 0x65, 0x74, 0x68,
|
||||
0xaa, 0x02, 0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e,
|
||||
0x56, 0x32, 0xca, 0x02, 0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74,
|
||||
0x68, 0x5c, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f,
|
||||
0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52,
|
||||
0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70,
|
||||
0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x22, 0x97, 0x01, 0x0a, 0x0d, 0x52, 0x61, 0x6e,
|
||||
0x64, 0x61, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x74,
|
||||
0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x74,
|
||||
0x61, 0x74, 0x65, 0x49, 0x64, 0x12, 0x61, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x04, 0x42, 0x46, 0x82, 0xb5, 0x18, 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61,
|
||||
0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x33, 0x2f, 0x63, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d,
|
||||
0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x00, 0x52, 0x05,
|
||||
0x65, 0x70, 0x6f, 0x63, 0x68, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, 0x70, 0x6f,
|
||||
0x63, 0x68, 0x22, 0xc7, 0x01, 0x0a, 0x0e, 0x52, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x04, 0x64, 0x61, 0x74,
|
||||
0x61, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f,
|
||||
0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x13, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69,
|
||||
0x73, 0x74, 0x69, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65,
|
||||
0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a,
|
||||
0x65, 0x64, 0x1a, 0x28, 0x0a, 0x06, 0x52, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x12, 0x1e, 0x0a, 0x06,
|
||||
0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5,
|
||||
0x18, 0x02, 0x33, 0x32, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x42, 0x83, 0x01, 0x0a,
|
||||
0x13, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x2e, 0x76, 0x32, 0x42, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
|
||||
0x74, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63,
|
||||
0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02,
|
||||
0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x32,
|
||||
0xca, 0x02, 0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c,
|
||||
0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -193,6 +193,7 @@ message BeaconStateResponseV2 {
|
||||
Version version = 1;
|
||||
BeaconStateContainer data = 2;
|
||||
bool execution_optimistic = 3;
|
||||
bool finalized = 4;
|
||||
}
|
||||
|
||||
message BeaconStateSSZResponseV2 {
|
||||
|
||||
@@ -11,7 +11,9 @@ go_library(
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
"//crypto/hash:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//encoding/ssz:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
|
||||
@@ -3,6 +3,8 @@ package migration
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v3/crypto/hash"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/ssz"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
@@ -316,6 +318,201 @@ func V1Alpha1BeaconBlockBellatrixToV2Blinded(v1alpha1Block *ethpbalpha.BeaconBlo
|
||||
return v2Block, nil
|
||||
}
|
||||
|
||||
// V1Alpha1BeaconBlockCapellaToV2Blinded converts a v1alpha1 Capella beacon block to a v2
|
||||
// blinded Capella block.
|
||||
func V1Alpha1BeaconBlockCapellaToV2Blinded(v1alpha1Block *ethpbalpha.BeaconBlockCapella) (*ethpbv2.BlindedBeaconBlockCapella, error) {
|
||||
sourceProposerSlashings := v1alpha1Block.Body.ProposerSlashings
|
||||
resultProposerSlashings := make([]*ethpbv1.ProposerSlashing, len(sourceProposerSlashings))
|
||||
for i, s := range sourceProposerSlashings {
|
||||
resultProposerSlashings[i] = ðpbv1.ProposerSlashing{
|
||||
SignedHeader_1: ðpbv1.SignedBeaconBlockHeader{
|
||||
Message: ðpbv1.BeaconBlockHeader{
|
||||
Slot: s.Header_1.Header.Slot,
|
||||
ProposerIndex: s.Header_1.Header.ProposerIndex,
|
||||
ParentRoot: bytesutil.SafeCopyBytes(s.Header_1.Header.ParentRoot),
|
||||
StateRoot: bytesutil.SafeCopyBytes(s.Header_1.Header.StateRoot),
|
||||
BodyRoot: bytesutil.SafeCopyBytes(s.Header_1.Header.BodyRoot),
|
||||
},
|
||||
Signature: bytesutil.SafeCopyBytes(s.Header_1.Signature),
|
||||
},
|
||||
SignedHeader_2: ðpbv1.SignedBeaconBlockHeader{
|
||||
Message: ðpbv1.BeaconBlockHeader{
|
||||
Slot: s.Header_2.Header.Slot,
|
||||
ProposerIndex: s.Header_2.Header.ProposerIndex,
|
||||
ParentRoot: bytesutil.SafeCopyBytes(s.Header_2.Header.ParentRoot),
|
||||
StateRoot: bytesutil.SafeCopyBytes(s.Header_2.Header.StateRoot),
|
||||
BodyRoot: bytesutil.SafeCopyBytes(s.Header_2.Header.BodyRoot),
|
||||
},
|
||||
Signature: bytesutil.SafeCopyBytes(s.Header_2.Signature),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
sourceAttesterSlashings := v1alpha1Block.Body.AttesterSlashings
|
||||
resultAttesterSlashings := make([]*ethpbv1.AttesterSlashing, len(sourceAttesterSlashings))
|
||||
for i, s := range sourceAttesterSlashings {
|
||||
att1Indices := make([]uint64, len(s.Attestation_1.AttestingIndices))
|
||||
copy(att1Indices, s.Attestation_1.AttestingIndices)
|
||||
att2Indices := make([]uint64, len(s.Attestation_2.AttestingIndices))
|
||||
copy(att2Indices, s.Attestation_2.AttestingIndices)
|
||||
resultAttesterSlashings[i] = ðpbv1.AttesterSlashing{
|
||||
Attestation_1: ðpbv1.IndexedAttestation{
|
||||
AttestingIndices: att1Indices,
|
||||
Data: ðpbv1.AttestationData{
|
||||
Slot: s.Attestation_1.Data.Slot,
|
||||
Index: s.Attestation_1.Data.CommitteeIndex,
|
||||
BeaconBlockRoot: bytesutil.SafeCopyBytes(s.Attestation_1.Data.BeaconBlockRoot),
|
||||
Source: ðpbv1.Checkpoint{
|
||||
Epoch: s.Attestation_1.Data.Source.Epoch,
|
||||
Root: bytesutil.SafeCopyBytes(s.Attestation_1.Data.Source.Root),
|
||||
},
|
||||
Target: ðpbv1.Checkpoint{
|
||||
Epoch: s.Attestation_1.Data.Target.Epoch,
|
||||
Root: bytesutil.SafeCopyBytes(s.Attestation_1.Data.Target.Root),
|
||||
},
|
||||
},
|
||||
Signature: bytesutil.SafeCopyBytes(s.Attestation_1.Signature),
|
||||
},
|
||||
Attestation_2: ðpbv1.IndexedAttestation{
|
||||
AttestingIndices: att2Indices,
|
||||
Data: ðpbv1.AttestationData{
|
||||
Slot: s.Attestation_2.Data.Slot,
|
||||
Index: s.Attestation_2.Data.CommitteeIndex,
|
||||
BeaconBlockRoot: bytesutil.SafeCopyBytes(s.Attestation_2.Data.BeaconBlockRoot),
|
||||
Source: ðpbv1.Checkpoint{
|
||||
Epoch: s.Attestation_2.Data.Source.Epoch,
|
||||
Root: bytesutil.SafeCopyBytes(s.Attestation_2.Data.Source.Root),
|
||||
},
|
||||
Target: ðpbv1.Checkpoint{
|
||||
Epoch: s.Attestation_2.Data.Target.Epoch,
|
||||
Root: bytesutil.SafeCopyBytes(s.Attestation_2.Data.Target.Root),
|
||||
},
|
||||
},
|
||||
Signature: bytesutil.SafeCopyBytes(s.Attestation_2.Signature),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
sourceAttestations := v1alpha1Block.Body.Attestations
|
||||
resultAttestations := make([]*ethpbv1.Attestation, len(sourceAttestations))
|
||||
for i, a := range sourceAttestations {
|
||||
resultAttestations[i] = ðpbv1.Attestation{
|
||||
AggregationBits: bytesutil.SafeCopyBytes(a.AggregationBits),
|
||||
Data: ðpbv1.AttestationData{
|
||||
Slot: a.Data.Slot,
|
||||
Index: a.Data.CommitteeIndex,
|
||||
BeaconBlockRoot: bytesutil.SafeCopyBytes(a.Data.BeaconBlockRoot),
|
||||
Source: ðpbv1.Checkpoint{
|
||||
Epoch: a.Data.Source.Epoch,
|
||||
Root: bytesutil.SafeCopyBytes(a.Data.Source.Root),
|
||||
},
|
||||
Target: ðpbv1.Checkpoint{
|
||||
Epoch: a.Data.Target.Epoch,
|
||||
Root: bytesutil.SafeCopyBytes(a.Data.Target.Root),
|
||||
},
|
||||
},
|
||||
Signature: bytesutil.SafeCopyBytes(a.Signature),
|
||||
}
|
||||
}
|
||||
|
||||
sourceDeposits := v1alpha1Block.Body.Deposits
|
||||
resultDeposits := make([]*ethpbv1.Deposit, len(sourceDeposits))
|
||||
for i, d := range sourceDeposits {
|
||||
resultDeposits[i] = ðpbv1.Deposit{
|
||||
Proof: bytesutil.SafeCopy2dBytes(d.Proof),
|
||||
Data: ðpbv1.Deposit_Data{
|
||||
Pubkey: bytesutil.SafeCopyBytes(d.Data.PublicKey),
|
||||
WithdrawalCredentials: bytesutil.SafeCopyBytes(d.Data.WithdrawalCredentials),
|
||||
Amount: d.Data.Amount,
|
||||
Signature: bytesutil.SafeCopyBytes(d.Data.Signature),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
sourceExits := v1alpha1Block.Body.VoluntaryExits
|
||||
resultExits := make([]*ethpbv1.SignedVoluntaryExit, len(sourceExits))
|
||||
for i, e := range sourceExits {
|
||||
resultExits[i] = ðpbv1.SignedVoluntaryExit{
|
||||
Message: ðpbv1.VoluntaryExit{
|
||||
Epoch: e.Exit.Epoch,
|
||||
ValidatorIndex: e.Exit.ValidatorIndex,
|
||||
},
|
||||
Signature: bytesutil.SafeCopyBytes(e.Signature),
|
||||
}
|
||||
}
|
||||
|
||||
transactionsRoot, err := ssz.TransactionsRoot(v1alpha1Block.Body.ExecutionPayload.Transactions)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not calculate transactions root")
|
||||
}
|
||||
|
||||
withdrawalsRoot, err := ssz.WithdrawalSliceRoot(
|
||||
hash.CustomSHA256Hasher(),
|
||||
v1alpha1Block.Body.ExecutionPayload.Withdrawals,
|
||||
fieldparams.MaxWithdrawalsPerPayload,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not calculate transactions root")
|
||||
}
|
||||
|
||||
changes := make([]*ethpbv2.SignedBLSToExecutionChange, len(v1alpha1Block.Body.BlsToExecutionChanges))
|
||||
for i, change := range v1alpha1Block.Body.BlsToExecutionChanges {
|
||||
changes[i] = ðpbv2.SignedBLSToExecutionChange{
|
||||
Message: ðpbv2.BLSToExecutionChange{
|
||||
ValidatorIndex: change.Message.ValidatorIndex,
|
||||
FromBlsPubkey: bytesutil.SafeCopyBytes(change.Message.FromBlsPubkey),
|
||||
ToExecutionAddress: bytesutil.SafeCopyBytes(change.Message.ToExecutionAddress),
|
||||
},
|
||||
Signature: bytesutil.SafeCopyBytes(change.Signature),
|
||||
}
|
||||
}
|
||||
|
||||
resultBlockBody := ðpbv2.BlindedBeaconBlockBodyCapella{
|
||||
RandaoReveal: bytesutil.SafeCopyBytes(v1alpha1Block.Body.RandaoReveal),
|
||||
Eth1Data: ðpbv1.Eth1Data{
|
||||
DepositRoot: bytesutil.SafeCopyBytes(v1alpha1Block.Body.Eth1Data.DepositRoot),
|
||||
DepositCount: v1alpha1Block.Body.Eth1Data.DepositCount,
|
||||
BlockHash: bytesutil.SafeCopyBytes(v1alpha1Block.Body.Eth1Data.BlockHash),
|
||||
},
|
||||
Graffiti: bytesutil.SafeCopyBytes(v1alpha1Block.Body.Graffiti),
|
||||
ProposerSlashings: resultProposerSlashings,
|
||||
AttesterSlashings: resultAttesterSlashings,
|
||||
Attestations: resultAttestations,
|
||||
Deposits: resultDeposits,
|
||||
VoluntaryExits: resultExits,
|
||||
SyncAggregate: ðpbv1.SyncAggregate{
|
||||
SyncCommitteeBits: bytesutil.SafeCopyBytes(v1alpha1Block.Body.SyncAggregate.SyncCommitteeBits),
|
||||
SyncCommitteeSignature: bytesutil.SafeCopyBytes(v1alpha1Block.Body.SyncAggregate.SyncCommitteeSignature),
|
||||
},
|
||||
ExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: bytesutil.SafeCopyBytes(v1alpha1Block.Body.ExecutionPayload.ParentHash),
|
||||
FeeRecipient: bytesutil.SafeCopyBytes(v1alpha1Block.Body.ExecutionPayload.FeeRecipient),
|
||||
StateRoot: bytesutil.SafeCopyBytes(v1alpha1Block.Body.ExecutionPayload.StateRoot),
|
||||
ReceiptsRoot: bytesutil.SafeCopyBytes(v1alpha1Block.Body.ExecutionPayload.ReceiptsRoot),
|
||||
LogsBloom: bytesutil.SafeCopyBytes(v1alpha1Block.Body.ExecutionPayload.LogsBloom),
|
||||
PrevRandao: bytesutil.SafeCopyBytes(v1alpha1Block.Body.ExecutionPayload.PrevRandao),
|
||||
BlockNumber: v1alpha1Block.Body.ExecutionPayload.BlockNumber,
|
||||
GasLimit: v1alpha1Block.Body.ExecutionPayload.GasLimit,
|
||||
GasUsed: v1alpha1Block.Body.ExecutionPayload.GasUsed,
|
||||
Timestamp: v1alpha1Block.Body.ExecutionPayload.Timestamp,
|
||||
ExtraData: bytesutil.SafeCopyBytes(v1alpha1Block.Body.ExecutionPayload.ExtraData),
|
||||
BaseFeePerGas: bytesutil.SafeCopyBytes(v1alpha1Block.Body.ExecutionPayload.BaseFeePerGas),
|
||||
BlockHash: bytesutil.SafeCopyBytes(v1alpha1Block.Body.ExecutionPayload.BlockHash),
|
||||
TransactionsRoot: transactionsRoot[:],
|
||||
WithdrawalsRoot: withdrawalsRoot[:],
|
||||
},
|
||||
BlsToExecutionChanges: changes,
|
||||
}
|
||||
v2Block := ðpbv2.BlindedBeaconBlockCapella{
|
||||
Slot: v1alpha1Block.Slot,
|
||||
ProposerIndex: v1alpha1Block.ProposerIndex,
|
||||
ParentRoot: bytesutil.SafeCopyBytes(v1alpha1Block.ParentRoot),
|
||||
StateRoot: bytesutil.SafeCopyBytes(v1alpha1Block.StateRoot),
|
||||
Body: resultBlockBody,
|
||||
}
|
||||
return v2Block, nil
|
||||
}
|
||||
|
||||
// BeaconStateAltairToProto converts a state.BeaconState object to its protobuf equivalent.
|
||||
func BeaconStateAltairToProto(altairState state.BeaconState) (*ethpbv2.BeaconState, error) {
|
||||
sourceFork := altairState.Fork()
|
||||
|
||||
@@ -250,6 +250,35 @@ func Test_V1Alpha1BeaconBlockBellatrixToV2Blinded(t *testing.T) {
|
||||
assert.DeepEqual(t, alphaRoot, v2Root)
|
||||
}
|
||||
|
||||
func Test_V1Alpha1BeaconBlockCapellaToV2Blinded(t *testing.T) {
|
||||
alphaBlock := util.HydrateBeaconBlockCapella(ðpbalpha.BeaconBlockCapella{})
|
||||
alphaBlock.Slot = slot
|
||||
alphaBlock.ProposerIndex = validatorIndex
|
||||
alphaBlock.ParentRoot = parentRoot
|
||||
alphaBlock.StateRoot = stateRoot
|
||||
alphaBlock.Body.RandaoReveal = randaoReveal
|
||||
alphaBlock.Body.Eth1Data = ðpbalpha.Eth1Data{
|
||||
DepositRoot: depositRoot,
|
||||
DepositCount: depositCount,
|
||||
BlockHash: blockHash,
|
||||
}
|
||||
syncCommitteeBits := bitfield.NewBitvector512()
|
||||
syncCommitteeBits.SetBitAt(100, true)
|
||||
alphaBlock.Body.SyncAggregate = ðpbalpha.SyncAggregate{
|
||||
SyncCommitteeBits: syncCommitteeBits,
|
||||
SyncCommitteeSignature: signature,
|
||||
}
|
||||
alphaBlock.Body.ExecutionPayload.Transactions = [][]byte{[]byte("transaction1"), []byte("transaction2")}
|
||||
|
||||
v2Block, err := V1Alpha1BeaconBlockCapellaToV2Blinded(alphaBlock)
|
||||
require.NoError(t, err)
|
||||
alphaRoot, err := alphaBlock.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
v2Root, err := v2Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, alphaRoot, v2Root)
|
||||
}
|
||||
|
||||
func TestBeaconStateAltairToProto(t *testing.T) {
|
||||
source, err := util.NewBeaconStateAltair(util.FillRootsNaturalOptAltair, func(state *ethpbalpha.BeaconStateAltair) error {
|
||||
state.GenesisTime = 1
|
||||
|
||||
@@ -23,8 +23,8 @@ func (c beaconApiValidatorClient) getAttestationData(
|
||||
|
||||
query := buildURL("/eth/v1/validator/attestation_data", params)
|
||||
produceAttestationDataResponseJson := rpcmiddleware.ProduceAttestationDataResponseJson{}
|
||||
_, err := c.jsonRestHandler.GetRestJsonResponse(ctx, query, &produceAttestationDataResponseJson)
|
||||
if err != nil {
|
||||
|
||||
if _, err := c.jsonRestHandler.GetRestJsonResponse(ctx, query, &produceAttestationDataResponseJson); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get json response")
|
||||
}
|
||||
|
||||
|
||||
@@ -53,12 +53,11 @@ func (c *beaconApiValidatorClient) getFork(ctx context.Context) (*apimiddleware.
|
||||
|
||||
stateForkResponseJson := &apimiddleware.StateForkResponseJson{}
|
||||
|
||||
_, err := c.jsonRestHandler.GetRestJsonResponse(
|
||||
if _, err := c.jsonRestHandler.GetRestJsonResponse(
|
||||
ctx,
|
||||
endpoint,
|
||||
stateForkResponseJson,
|
||||
)
|
||||
if err != nil {
|
||||
); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get json response from `%s` REST endpoint", endpoint)
|
||||
}
|
||||
|
||||
@@ -70,12 +69,11 @@ func (c *beaconApiValidatorClient) getHeaders(ctx context.Context) (*apimiddlewa
|
||||
|
||||
blockHeadersResponseJson := &apimiddleware.BlockHeadersResponseJson{}
|
||||
|
||||
_, err := c.jsonRestHandler.GetRestJsonResponse(
|
||||
if _, err := c.jsonRestHandler.GetRestJsonResponse(
|
||||
ctx,
|
||||
endpoint,
|
||||
blockHeadersResponseJson,
|
||||
)
|
||||
if err != nil {
|
||||
); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get json response from `%s` REST endpoint", endpoint)
|
||||
}
|
||||
|
||||
@@ -105,12 +103,11 @@ func (c *beaconApiValidatorClient) getSyncing(ctx context.Context) (*apimiddlewa
|
||||
|
||||
syncingResponseJson := &apimiddleware.SyncingResponseJson{}
|
||||
|
||||
_, err := c.jsonRestHandler.GetRestJsonResponse(
|
||||
if _, err := c.jsonRestHandler.GetRestJsonResponse(
|
||||
ctx,
|
||||
endpoint,
|
||||
syncingResponseJson,
|
||||
)
|
||||
if err != nil {
|
||||
); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get json response from `%s` REST endpoint", endpoint)
|
||||
}
|
||||
|
||||
|
||||
@@ -53,8 +53,7 @@ func (c beaconApiStateValidatorsProvider) GetStateValidators(
|
||||
|
||||
stateValidatorsJson := &rpcmiddleware.StateValidatorsResponseJson{}
|
||||
|
||||
_, err := c.jsonRestHandler.GetRestJsonResponse(ctx, url, stateValidatorsJson)
|
||||
if err != nil {
|
||||
if _, err := c.jsonRestHandler.GetRestJsonResponse(ctx, url, stateValidatorsJson); err != nil {
|
||||
return &rpcmiddleware.StateValidatorsResponseJson{}, errors.Wrap(err, "failed to get json response")
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
@@ -77,8 +77,13 @@ func (c *beaconApiValidatorClient) getSyncCommitteeContribution(
|
||||
}
|
||||
|
||||
blockRoot := hexutil.Encode(blockRootResponse.Root)
|
||||
url := fmt.Sprintf("/eth/v1/validator/sync_committee_contribution?slot=%d&subcommittee_index=%d&beacon_block_root=%s",
|
||||
uint64(req.Slot), req.SubnetId, blockRoot)
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("slot", strconv.FormatUint(uint64(req.Slot), 10))
|
||||
params.Add("subcommittee_index", strconv.FormatUint(req.SubnetId, 10))
|
||||
params.Add("beacon_block_root", blockRoot)
|
||||
|
||||
url := buildURL("/eth/v1/validator/sync_committee_contribution", params)
|
||||
|
||||
var resp apimiddleware.ProduceSyncCommitteeContributionResponseJson
|
||||
if _, err := c.jsonRestHandler.GetRestJsonResponse(ctx, url, &resp); err != nil {
|
||||
|
||||
@@ -228,8 +228,8 @@ func TestGetSyncCommitteeContribution(t *testing.T) {
|
||||
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
||||
ctx,
|
||||
fmt.Sprintf("/eth/v1/validator/sync_committee_contribution?slot=%d&subcommittee_index=%d&beacon_block_root=%s",
|
||||
uint64(request.Slot), request.SubnetId, blockRoot),
|
||||
fmt.Sprintf("/eth/v1/validator/sync_committee_contribution?beacon_block_root=%s&slot=%d&subcommittee_index=%d",
|
||||
blockRoot, uint64(request.Slot), request.SubnetId),
|
||||
&apimiddleware.ProduceSyncCommitteeContributionResponseJson{},
|
||||
).SetArg(
|
||||
2,
|
||||
|
||||
@@ -42,6 +42,7 @@ func (*ValidatorEndpointFactory) Create(path string) (*apimiddleware.Endpoint, e
|
||||
case "/eth/v1/validator/{pubkey}/feerecipient":
|
||||
endpoint.GetResponse = &GetFeeRecipientByPubkeyResponseJson{}
|
||||
endpoint.PostRequest = &SetFeeRecipientByPubkeyRequestJson{}
|
||||
endpoint.DeleteRequest = &DeleteFeeRecipientByPubkeyRequestJson{}
|
||||
case "/eth/v1/validator/{pubkey}/gas_limit":
|
||||
endpoint.GetResponse = &GetGasLimitResponseJson{}
|
||||
endpoint.PostRequest = &SetGasLimitRequestJson{}
|
||||
|
||||
@@ -85,6 +85,10 @@ type SetFeeRecipientByPubkeyRequestJson struct {
|
||||
Ethaddress string `json:"ethaddress" hex:"true"`
|
||||
}
|
||||
|
||||
type DeleteFeeRecipientByPubkeyRequestJson struct {
|
||||
Pubkey string `json:"pubkey" hex:"true"`
|
||||
}
|
||||
|
||||
type GetGasLimitResponseJson struct {
|
||||
Data *GasLimitJson `json:"data"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user