Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow SSZ-serialized blocks in publishBlindedBlock #10679

Merged
merged 43 commits into from
May 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
71f2b49
SubmitBlockSSZ grpc
rkapka May 5, 2022
54c0ea9
SubmitBlockSSZ middleware
rkapka May 9, 2022
1ce529e
test fixes
rkapka May 9, 2022
584ffb8
Merge branch 'develop' into block-production-ssz
rkapka May 9, 2022
7388eeb
use VersionedUnmarshaller
rkapka May 10, 2022
83429fa
use VersionedUnmarshaller
rkapka May 10, 2022
5b02b71
tests
rkapka May 11, 2022
a10573b
Merge branch 'develop' into block-production-ssz
rkapka May 11, 2022
99e7591
fuzz: Add fuzz tests for sparse merkle trie (#10662)
prestonvanloon May 9, 2022
e242439
Add engine timeout values (#10645)
terencechain May 9, 2022
72e61c5
Cleanup of `stategen` package (#10607)
rkapka May 9, 2022
e930be3
Process atts and update head before proposing (#10653)
terencechain May 9, 2022
69fd6a5
Add link to e2e docs in `README` (#10672)
rkapka May 10, 2022
1fee0b2
Improve `ReceiveBlock`'s comment (#10671)
rkapka May 10, 2022
de554c9
Call fcu on invalid payload (#10565)
terencechain May 10, 2022
68906c5
Cache and use justified and finalized payload block hash (#10657)
terencechain May 10, 2022
c595d6d
Merge branch '__develop' into blinded-block-production-ssz
rkapka May 11, 2022
d4c9706
Merge branch 'block-production-ssz' into blinded-block-production-ssz
rkapka May 11, 2022
6d609c4
do not export slotFromBlock
rkapka May 11, 2022
2dd3e7a
simplify tests
rkapka May 11, 2022
4a47171
Merge branch 'block-production-ssz' into blinded-block-production-ssz
rkapka May 11, 2022
470f6dd
grpc
rkapka May 11, 2022
7a5a411
middleware
rkapka May 11, 2022
e9d6a99
Merge branch 'develop' into blinded-block-production-ssz
rkapka May 11, 2022
d21c719
extract package-level consts
rkapka May 12, 2022
ebf0476
Merge branch 'develop' into block-production-ssz
rauljordan May 12, 2022
3539031
Merge refs/heads/develop into block-production-ssz
prylabs-bulldozer[bot] May 12, 2022
d2430c2
Merge refs/heads/develop into block-production-ssz
prylabs-bulldozer[bot] May 12, 2022
63d6950
Merge refs/heads/develop into block-production-ssz
prylabs-bulldozer[bot] May 12, 2022
edc6bca
Merge refs/heads/develop into block-production-ssz
prylabs-bulldozer[bot] May 12, 2022
56611fe
Merge refs/heads/develop into block-production-ssz
prylabs-bulldozer[bot] May 12, 2022
9b87a11
Merge refs/heads/develop into block-production-ssz
prylabs-bulldozer[bot] May 12, 2022
077009c
Merge refs/heads/develop into block-production-ssz
prylabs-bulldozer[bot] May 13, 2022
3f4c412
Merge branch 'block-production-ssz' into blinded-block-production-ssz
rkapka May 13, 2022
a26ea14
Simplify SSZ handling
rkapka May 13, 2022
ea55cbf
Merge branch '__develop' into blinded-block-production-ssz
rkapka May 13, 2022
69bd08d
Merge branch 'simplify-ssz-api' into blinded-block-production-ssz
rkapka May 13, 2022
fed9c88
fix tests
rkapka May 13, 2022
3dfdc46
Merge branch 'simplify-ssz-api' into blinded-block-production-ssz
rkapka May 13, 2022
e8f8f6c
Merge branch '__develop' into blinded-block-production-ssz
rkapka May 16, 2022
8f6ffa6
test fixes
rkapka May 16, 2022
8631561
test hack
rkapka May 16, 2022
cdb600f
Merge branch 'develop' into blinded-block-production-ssz
rauljordan May 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions beacon-chain/rpc/apimiddleware/custom_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ func handleSubmitBlockSSZ(m *apimiddleware.ApiProxyMiddleware, endpoint apimiddl
return handlePostSSZ(m, endpoint, w, req, config)
}

func handleSubmitBlindedBlockSSZ(
m *apimiddleware.ApiProxyMiddleware,
endpoint apimiddleware.Endpoint,
w http.ResponseWriter,
req *http.Request,
) (handled bool) {
config := sszConfig{
sszPath: "/eth/v1/beacon/blinded_blocks/ssz",
}
return handlePostSSZ(m, endpoint, w, req, config)
}

func handleGetSSZ(
m *apimiddleware.ApiProxyMiddleware,
endpoint apimiddleware.Endpoint,
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/rpc/apimiddleware/endpoint_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func (_ *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er
OnPreDeserializeRequestBodyIntoContainer: setInitialPublishBlindedBlockPostRequest,
OnPostDeserializeRequestBodyIntoContainer: preparePublishedBlindedBlock,
}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleSubmitBlindedBlockSSZ}
case "/eth/v1/beacon/blocks/{block_id}":
endpoint.GetResponse = &blockResponseJson{}
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconBlockSSZ}
Expand Down
42 changes: 42 additions & 0 deletions beacon-chain/rpc/eth/beacon/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,48 @@ func (bs *Server) SubmitBlindedBlock(ctx context.Context, req *ethpbv2.SignedBli
return &emptypb.Empty{}, nil
}

// SubmitBlindedBlockSSZ instructs the beacon node to use the components of the `SignedBlindedBeaconBlock` to construct
// and publish a `SignedBeaconBlock` by swapping out the `transactions_root` for the corresponding full list of `transactions`.
// The beacon node should broadcast a newly constructed `SignedBeaconBlock` to the beacon network,
// to be included in the beacon chain. The beacon node is not required to validate the signed
// `BeaconBlock`, and a successful response (20X) only indicates that the broadcast has been
// successful. The beacon node is expected to integrate the new block into its state, and
// therefore validate the block internally, however blocks which fail the validation are still
// broadcast but a different status code is returned (202).
//
// The provided block must be SSZ-serialized.
func (bs *Server) SubmitBlindedBlockSSZ(ctx context.Context, req *ethpbv2.SSZContainer) (*emptypb.Empty, error) {
ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlindedBlockSSZ")
defer span.End()

md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not read eth-consensus-version header")
}
ver := md.Get("eth-consensus-version")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use the package var for this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor change, no big deal and can be done in another pr

if len(ver) == 0 {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not read eth-consensus-version header")
}
schedule := forks.NewOrderedSchedule(params.BeaconConfig())
forkVer, err := schedule.VersionForName(ver[0])
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not determine fork version: %v", err)
}
unmarshaler, err := detect.FromForkVersion(forkVer)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not create unmarshaler: %v", err)
}
block, err := unmarshaler.UnmarshalBlindedBeaconBlock(req.Data)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err)
}
root, err := block.Block().HashTreeRoot()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not compute block's hash tree root: %v", err)
}
return &emptypb.Empty{}, bs.submitBlock(ctx, root, block)
}

// GetBlock retrieves block details for given block ID.
func (bs *Server) GetBlock(ctx context.Context, req *ethpbv1.BlockRequest) (*ethpbv1.BlockResponse, error) {
ctx, span := trace.StartSpan(ctx, "beacon.GetBlock")
Expand Down
149 changes: 145 additions & 4 deletions beacon-chain/rpc/eth/beacon/blocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ func TestServer_SubmitBlockSSZ_OK(t *testing.T) {
Data: blockSsz,
}
md := metadata.MD{}
md.Set("Eth-Consensus-Version", "phase0")
md.Set(versionHeader, "phase0")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = beaconChainServer.SubmitBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err, "Could not propose block correctly")
Expand Down Expand Up @@ -646,7 +646,7 @@ func TestServer_SubmitBlockSSZ_OK(t *testing.T) {
Data: blockSsz,
}
md := metadata.MD{}
md.Set("Eth-Consensus-Version", "altair")
md.Set(versionHeader, "altair")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = beaconChainServer.SubmitBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err, "Could not propose block correctly")
Expand All @@ -655,7 +655,6 @@ func TestServer_SubmitBlockSSZ_OK(t *testing.T) {
t.Run("Bellatrix", func(t *testing.T) {
// INFO: This code block can be removed once Bellatrix
// fork epoch is set to a value other than math.MaxUint64
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig()
cfg.BellatrixForkEpoch = cfg.AltairForkEpoch + 1000
cfg.ForkVersionSchedule[bytesutil.ToBytes4(cfg.BellatrixForkVersion)] = cfg.AltairForkEpoch + 1000
Expand Down Expand Up @@ -698,13 +697,155 @@ func TestServer_SubmitBlockSSZ_OK(t *testing.T) {
Data: blockSsz,
}
md := metadata.MD{}
md.Set("Eth-Consensus-Version", "bellatrix")
md.Set(versionHeader, "bellatrix")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = beaconChainServer.SubmitBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err, "Could not propose block correctly")
})
}

func TestServer_SubmitBlindedBlockSSZ_OK(t *testing.T) {
t.Run("Phase 0", func(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()

genesis := util.NewBeaconBlock()
wsb, err := wrapper.WrappedSignedBeaconBlock(genesis)
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(context.Background(), wsb), "Could not save genesis block")

numDeposits := uint64(64)
beaconState, _ := util.DeterministicGenesisState(t, numDeposits)
bsRoot, err := beaconState.HashTreeRoot(ctx)
require.NoError(t, err)
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state")

c := &mock.ChainService{Root: bsRoot[:], State: beaconState}
beaconChainServer := &Server{
BeaconDB: beaconDB,
BlockReceiver: c,
ChainInfoFetcher: c,
BlockNotifier: c.BlockNotifier(),
Broadcaster: mockp2p.NewTestP2P(t),
HeadFetcher: c,
}
req := util.NewBeaconBlock()
req.Block.Slot = 5
req.Block.ParentRoot = bsRoot[:]
wsb, err = wrapper.WrappedSignedBeaconBlock(req)
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(ctx, wsb))
blockSsz, err := req.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: blockSsz,
}
md := metadata.MD{}
md.Set(versionHeader, "phase0")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = beaconChainServer.SubmitBlindedBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err, "Could not propose block correctly")
})

t.Run("Altair", func(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()

genesis := util.NewBeaconBlockAltair()
wrapped, err := wrapper.WrappedSignedBeaconBlock(genesis)
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapped), "Could not save genesis block")

numDeposits := uint64(64)
beaconState, _ := util.DeterministicGenesisState(t, numDeposits)
bsRoot, err := beaconState.HashTreeRoot(ctx)
require.NoError(t, err)
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state")

c := &mock.ChainService{Root: bsRoot[:], State: beaconState}
beaconChainServer := &Server{
BeaconDB: beaconDB,
BlockReceiver: c,
ChainInfoFetcher: c,
BlockNotifier: c.BlockNotifier(),
Broadcaster: mockp2p.NewTestP2P(t),
HeadFetcher: c,
}
req := util.NewBeaconBlockAltair()
req.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().AltairForkEpoch))
req.Block.ParentRoot = bsRoot[:]
wrapped, err = wrapper.WrappedSignedBeaconBlock(req)
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(ctx, wrapped))
blockSsz, err := req.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: blockSsz,
}
md := metadata.MD{}
md.Set(versionHeader, "altair")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = beaconChainServer.SubmitBlindedBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err, "Could not propose block correctly")
})

t.Run("Bellatrix", func(t *testing.T) {
// INFO: This code block can be removed once Bellatrix
// fork epoch is set to a value other than math.MaxUint64
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig()
cfg.BellatrixForkEpoch = cfg.AltairForkEpoch + 1000
cfg.ForkVersionSchedule[bytesutil.ToBytes4(cfg.BellatrixForkVersion)] = cfg.AltairForkEpoch + 1000
params.OverrideBeaconConfig(cfg)

beaconDB := dbTest.SetupDB(t)
ctx := context.Background()

genesis := util.NewBeaconBlockBellatrix()
wrapped, err := wrapper.WrappedSignedBeaconBlock(genesis)
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapped), "Could not save genesis block")

numDeposits := uint64(64)
beaconState, _ := util.DeterministicGenesisState(t, numDeposits)
bsRoot, err := beaconState.HashTreeRoot(ctx)
require.NoError(t, err)
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state")

c := &mock.ChainService{Root: bsRoot[:], State: beaconState}
beaconChainServer := &Server{
BeaconDB: beaconDB,
BlockReceiver: c,
ChainInfoFetcher: c,
BlockNotifier: c.BlockNotifier(),
Broadcaster: mockp2p.NewTestP2P(t),
HeadFetcher: c,
}
req := util.NewBlindedBeaconBlockBellatrix()
req.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().BellatrixForkEpoch))
req.Block.ParentRoot = bsRoot[:]
wrapped, err = wrapper.WrappedSignedBeaconBlock(req)
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(ctx, wrapped))
blockSsz, err := req.MarshalSSZ()
require.NoError(t, err)
blockReq := &ethpbv2.SSZContainer{
Data: blockSsz,
}
md := metadata.MD{}
md.Set(versionHeader, "bellatrix")
sszCtx := metadata.NewIncomingContext(ctx, md)
_, err = beaconChainServer.SubmitBlindedBlockSSZ(sszCtx, blockReq)
assert.NoError(t, err, "Could not propose block correctly")
})
}

func TestSubmitBlindedBlock(t *testing.T) {
t.Run("Phase 0", func(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
Expand Down
1 change: 0 additions & 1 deletion encoding/ssz/detect/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ go_test(
"//consensus-types/primitives:go_default_library",
"//consensus-types/wrapper:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"//testing/require:go_default_library",
Expand Down
55 changes: 46 additions & 9 deletions encoding/ssz/detect/configfork.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,18 +145,39 @@ func (cf *VersionedUnmarshaler) UnmarshalBeaconBlock(marshaled []byte) (interfac
if err != nil {
return nil, err
}
if err := cf.validateVersion(slot); err != nil {
return nil, err
}

// heuristic to make sure block is from the same version as the VersionedUnmarshaler.
// Look up the version for the epoch that the block is from, then ensure that it matches the Version in the
// VersionedUnmarshaler.
epoch := slots.ToEpoch(slot)
fs := forks.NewOrderedSchedule(cf.Config)
ver, err := fs.VersionForEpoch(epoch)
var blk ssz.Unmarshaler
switch cf.Fork {
case version.Phase0:
blk = &ethpb.SignedBeaconBlock{}
case version.Altair:
blk = &ethpb.SignedBeaconBlockAltair{}
case version.Bellatrix:
blk = &ethpb.SignedBeaconBlockBellatrix{}
default:
forkName := version.String(cf.Fork)
return nil, fmt.Errorf("unable to initialize BeaconBlock for fork version=%s at slot=%d", forkName, slot)
}
err = blk.UnmarshalSSZ(marshaled)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal SignedBeaconBlock in UnmarshalSSZ")
}
return wrapper.WrappedSignedBeaconBlock(blk)
}

// UnmarshalBlindedBeaconBlock uses internal knowledge in the VersionedUnmarshaler to pick the right concrete blinded SignedBeaconBlock type,
// then Unmarshal()s the type and returns an instance of block.SignedBeaconBlock if successful.
// For Phase0 and Altair it works exactly line UnmarshalBeaconBlock.
func (cf *VersionedUnmarshaler) UnmarshalBlindedBeaconBlock(marshaled []byte) (interfaces.SignedBeaconBlock, error) {
slot, err := slotFromBlock(marshaled)
if err != nil {
return nil, err
}
if ver != cf.Version {
return nil, errors.Wrapf(errBlockForkMismatch, "slot=%d, epoch=%d, version=%#x", slot, epoch, ver)
if err := cf.validateVersion(slot); err != nil {
return nil, err
}

var blk ssz.Unmarshaler
Expand All @@ -166,7 +187,7 @@ func (cf *VersionedUnmarshaler) UnmarshalBeaconBlock(marshaled []byte) (interfac
case version.Altair:
blk = &ethpb.SignedBeaconBlockAltair{}
case version.Bellatrix:
blk = &ethpb.SignedBeaconBlockBellatrix{}
blk = &ethpb.SignedBlindedBeaconBlockBellatrix{}
default:
forkName := version.String(cf.Fork)
return nil, fmt.Errorf("unable to initialize BeaconBlock for fork version=%s at slot=%d", forkName, slot)
Expand All @@ -177,3 +198,19 @@ func (cf *VersionedUnmarshaler) UnmarshalBeaconBlock(marshaled []byte) (interfac
}
return wrapper.WrappedSignedBeaconBlock(blk)
}

// Heuristic to make sure block is from the same version as the VersionedUnmarshaler.
// Look up the version for the epoch that the block is from, then ensure that it matches the Version in the
// VersionedUnmarshaler.
func (cf *VersionedUnmarshaler) validateVersion(slot types.Slot) error {
epoch := slots.ToEpoch(slot)
fs := forks.NewOrderedSchedule(cf.Config)
ver, err := fs.VersionForEpoch(epoch)
if err != nil {
return err
}
if ver != cf.Version {
return errors.Wrapf(errBlockForkMismatch, "slot=%d, epoch=%d, version=%#x", slot, epoch, ver)
}
return nil
}
Loading