Compare commits

...

68 Commits

Author SHA1 Message Date
Raul Jordan
f754e98727 Merge branch 'only-save-payload' into frank 2022-05-26 17:28:39 -04:00
Raul Jordan
2269e6aac8 Merge branch 'develop' into frank 2022-05-26 17:28:37 -04:00
Raul Jordan
f5b2dd986a Merge branch 'develop' into only-save-payload 2022-05-26 17:28:28 -04:00
Raul Jordan
9203d63f90 Merge branch 'only-save-payload' into frank 2022-05-26 16:01:47 -04:00
Raul Jordan
b9b24afb69 no log 2022-05-26 16:01:36 -04:00
Raul Jordan
28c44fca75 rem 2022-05-26 16:01:22 -04:00
Raul Jordan
182abac1c1 Merge branch 'ee-error-cleaner' into frank 2022-05-26 16:01:01 -04:00
Raul Jordan
60d839b8e9 clean up unecessary stack trace 2022-05-26 16:00:22 -04:00
Raul Jordan
bedefd5ead Merge branch 'ee-error-cleaner' into frank 2022-05-26 15:58:52 -04:00
Raul Jordan
8103095cc0 fix up 2022-05-26 15:58:41 -04:00
Raul Jordan
9f02b541dd cleaner rpc responses 2022-05-26 15:55:07 -04:00
Raul Jordan
76f201ee8f no log 2022-05-26 15:31:03 -04:00
Raul Jordan
31c39aac96 fix up some failing tests 2022-05-26 15:21:07 -04:00
Raul Jordan
51109f61b4 Merge branch 'develop' into only-save-payload 2022-05-26 19:14:06 +00:00
Raul Jordan
dc94612272 Merge branch 'develop' into only-save-payload 2022-05-25 13:43:49 +00:00
Raul Jordan
eb150622ed Merge branch 'only-save-payload' of github.com:prysmaticlabs/prysm into only-save-payload 2022-05-24 23:29:59 -04:00
Raul Jordan
27e210f6b8 item 2022-05-24 23:29:54 -04:00
Raul Jordan
bbcbb8dc26 Merge branch 'develop' into only-save-payload 2022-05-25 03:17:13 +00:00
Raul Jordan
04a96da75d build 2022-05-24 23:16:21 -04:00
Raul Jordan
1acb3b6346 gaz 2022-05-24 14:07:25 -04:00
Raul Jordan
ff69994b7b reconstructs with payloads 2022-05-24 14:06:27 -04:00
Raul Jordan
7ad27324fd right test 2022-05-24 13:59:22 -04:00
Raul Jordan
1cba6c306e Merge branch 'develop' into only-save-payload 2022-05-24 13:46:56 -04:00
Raul Jordan
247c2da608 work on more rpc tests 2022-05-24 12:58:29 -04:00
Raul Jordan
f749702ed7 begin populating 2022-05-23 20:50:33 -04:00
Raul Jordan
dbd6232e6f Merge branch 'develop' into only-save-payload 2022-05-23 20:42:27 -04:00
Raul Jordan
d8b6b6d17c test with real txs 2022-05-23 20:40:44 -04:00
Raul Jordan
e2a06625cf Merge branch 'develop' into only-save-payload 2022-05-23 19:53:45 -04:00
Raul Jordan
92f9aff295 begin testing happy case of reconstruction func 2022-05-23 19:53:34 -04:00
Raul Jordan
ab734442a3 test fixes 2022-05-23 17:44:55 -04:00
Raul Jordan
36b1efb12f Merge branch 'develop' into only-save-payload 2022-05-23 16:32:54 -04:00
Raul Jordan
5a4a4c2016 engine client correct use 2022-05-20 18:32:21 -04:00
Raul Jordan
ba6c28c48d correct logic 2022-05-20 18:29:48 -04:00
Raul Jordan
01ae8d58d5 wrapper tests 2022-05-20 18:21:58 -04:00
Raul Jordan
574b03d2ed begin some tests 2022-05-20 18:02:35 -04:00
Raul Jordan
0c6feb60b1 builds 2022-05-20 17:51:56 -04:00
Raul Jordan
70143cff56 Merge branch 'payload-utils' into only-save-payload 2022-05-20 17:43:15 -04:00
Raul Jordan
49aedf8459 move to consensus-types folder 2022-05-20 17:43:00 -04:00
Raul Jordan
ea5e8b99b7 clean move 2022-05-20 17:30:40 -04:00
Raul Jordan
3611afb448 gaz 2022-05-20 17:28:04 -04:00
Raul Jordan
d3a1cff406 beacon block is nil wrapper 2022-05-20 17:26:45 -04:00
Raul Jordan
e3c07ac84f gazelle 2022-05-20 17:24:41 -04:00
Raul Jordan
57d52089bc handle err 2022-05-20 17:18:46 -04:00
Raul Jordan
d0b92aa42b future proof the code 2022-05-20 17:17:11 -04:00
Raul Jordan
35a7cc43e3 Merge branch 'develop' into only-save-payload 2022-05-20 17:10:15 -04:00
Raul Jordan
c214525e70 simpler logic 2022-05-20 16:40:43 -04:00
Raul Jordan
fcd9f0830e simplify 2022-05-20 16:27:08 -04:00
Raul Jordan
8c8380f28c better version check 2022-05-20 16:02:53 -04:00
Raul Jordan
5885e44670 Merge branch 'develop' into only-save-payload 2022-05-20 15:49:42 -04:00
Raul Jordan
11e0f4025a metrics for responding to blocks by range 2022-05-17 20:58:30 -04:00
Raul Jordan
05ed96dc25 add empty payload check 2022-05-17 20:35:40 -04:00
Raul Jordan
c57baa00f7 Merge branch 'develop' into only-save-payload 2022-05-17 19:55:41 -04:00
Raul Jordan
76b2e23232 ensure can respond to peer requests 2022-05-13 16:07:15 -04:00
Raul Jordan
68e67c3023 add logging times 2022-05-13 15:53:01 -04:00
Raul Jordan
eaa3d756e7 reconstruct full blocks during blocks by range request 2022-05-13 15:50:07 -04:00
Raul Jordan
cdf4c8d3fe api responses with real txs 2022-05-13 15:35:36 -04:00
Raul Jordan
ada07f5358 block reconstruction working as expected 2022-05-13 13:56:58 -04:00
Raul Jordan
114277d0b0 payload api reconstruct 2022-05-13 13:24:47 -04:00
Raul Jordan
0b6bf2c316 ensure can overcome sync hurdles 2022-05-13 00:05:07 -04:00
Raul Jordan
2299b00cd8 building up payloads 2022-05-12 17:08:42 -04:00
Raul Jordan
4ba8c98acd Merge branch 'develop' into only-save-payload 2022-05-12 15:52:59 -04:00
Raul Jordan
63f858d2da Merge branch 'develop' into only-save-payload 2022-05-12 15:52:49 -04:00
Raul Jordan
e7d9b33904 reconstruct full 2022-05-11 23:58:49 -04:00
Raul Jordan
77657dca93 reconstruct 2022-05-11 23:53:32 -04:00
Raul Jordan
c755751410 Merge branch 'develop' into only-save-payload 2022-05-11 14:24:58 -04:00
Raul Jordan
571edeaf43 wrap signed blinded 2022-05-05 17:10:04 -04:00
Raul Jordan
e2e8528f97 build from blinded 2022-05-05 17:05:23 -04:00
Raul Jordan
2cfbc92c17 experiment with only saving blinded blocks 2022-05-05 17:03:10 -04:00
30 changed files with 1087 additions and 176 deletions

View File

@@ -142,14 +142,7 @@ func (s *Service) getPayloadHash(ctx context.Context, root []byte) ([32]byte, er
if err != nil {
return [32]byte{}, err
}
if blocks.IsPreBellatrixVersion(blk.Block().Version()) {
return params.BeaconConfig().ZeroHash, nil
}
payload, err := blk.Block().Body().ExecutionPayload()
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not get execution payload")
}
return bytesutil.ToBytes32(payload.BlockHash), nil
return getBlockPayloadHash(blk.Block())
}
// notifyForkchoiceUpdate signals execution engine on a new payload.

View File

@@ -118,7 +118,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
}
isValidPayload, err := s.notifyNewPayload(ctx, postStateVersion, postStateHeader, signed)
if err != nil {
return errors.Wrap(err, "could not verify new payload")
return fmt.Errorf("could not verify new payload: %v", err)
}
if isValidPayload {
if err := s.validateMergeTransitionBlock(ctx, preStateVersion, preStateHeader, signed); err != nil {
@@ -622,15 +622,24 @@ func (s *Service) InsertSlashingsToForkChoiceStore(ctx context.Context, slashing
}
func getBlockPayloadHash(blk interfaces.BeaconBlock) ([32]byte, error) {
payloadHash := [32]byte{}
var blockHashFromPayload [32]byte
if blocks.IsPreBellatrixVersion(blk.Version()) {
return payloadHash, nil
return blockHashFromPayload, nil
}
payload, err := blk.Body().ExecutionPayload()
if err != nil {
return payloadHash, err
switch {
case errors.Is(err, wrapper.ErrUnsupportedField):
payloadHeader, err := blk.Body().ExecutionPayloadHeader()
if err != nil {
return blockHashFromPayload, err
}
blockHashFromPayload = bytesutil.ToBytes32(payloadHeader.BlockHash)
case err != nil:
return blockHashFromPayload, err
default:
blockHashFromPayload = bytesutil.ToBytes32(payload.BlockHash)
}
return bytesutil.ToBytes32(payload.BlockHash), nil
return blockHashFromPayload, nil
}
// This saves post state info to DB or cache. This also saves post state info to fork choice store.

View File

@@ -238,7 +238,7 @@ func ProcessOperationsNoVerifyAttsSigs(
if err != nil {
return nil, err
}
case version.Altair, version.Bellatrix:
case version.Altair, version.Bellatrix, version.BellatrixBlind:
state, err = altairOperations(ctx, state, signedBeaconBlock)
if err != nil {
return nil, err

View File

@@ -700,11 +700,6 @@ func unmarshalBlock(_ context.Context, enc []byte) (interfaces.SignedBeaconBlock
if err := rawBlock.UnmarshalSSZ(enc[len(altairKey):]); err != nil {
return nil, err
}
case hasBellatrixKey(enc):
rawBlock = &ethpb.SignedBeaconBlockBellatrix{}
if err := rawBlock.UnmarshalSSZ(enc[len(bellatrixKey):]); err != nil {
return nil, err
}
case hasBellatrixBlindKey(enc):
rawBlock = &ethpb.SignedBlindedBeaconBlockBellatrix{}
if err := rawBlock.UnmarshalSSZ(enc[len(bellatrixBlindKey):]); err != nil {
@@ -722,19 +717,34 @@ func unmarshalBlock(_ context.Context, enc []byte) (interfaces.SignedBeaconBlock
// marshal versioned beacon block from struct type down to bytes.
func marshalBlock(_ context.Context, blk interfaces.SignedBeaconBlock) ([]byte, error) {
obj, err := blk.MarshalSSZ()
if err != nil {
var encodedBlock []byte
var blindedBlock interfaces.SignedBeaconBlock
var err error
// If the block supports blinding of execution payloads, we wrap as
// a signed, blinded beacon block and then marshal to bytes. Otherwise,
// We just marshal the block as it is.
blindedBlock, err = wrapper.WrapSignedBlindedBeaconBlock(blk)
switch {
case errors.Is(err, wrapper.ErrUnsupportedSignedBeaconBlock):
encodedBlock, err = blk.MarshalSSZ()
if err != nil {
return nil, err
}
case err != nil:
return nil, err
default:
encodedBlock, err = blindedBlock.MarshalSSZ()
if err != nil {
return nil, err
}
}
switch blk.Version() {
case version.BellatrixBlind:
return snappy.Encode(nil, append(bellatrixBlindKey, obj...)), nil
case version.Bellatrix:
return snappy.Encode(nil, append(bellatrixKey, obj...)), nil
case version.Bellatrix, version.BellatrixBlind:
return snappy.Encode(nil, append(bellatrixBlindKey, encodedBlock...)), nil
case version.Altair:
return snappy.Encode(nil, append(altairKey, obj...)), nil
return snappy.Encode(nil, append(altairKey, encodedBlock...)), nil
case version.Phase0:
return snappy.Encode(nil, obj), nil
return snappy.Encode(nil, encodedBlock), nil
default:
return nil, errors.New("Unknown block version")
}

View File

@@ -678,6 +678,7 @@ func (b *BeaconNode) registerSyncService() error {
regularsync.WithStateGen(b.stateGen),
regularsync.WithSlasherAttestationsFeed(b.slasherAttestationsFeed),
regularsync.WithSlasherBlockHeadersFeed(b.slasherBlockHeadersFeed),
regularsync.WithExecutionPayloadReconstructor(web3Service),
)
return b.services.RegisterService(rs)
}
@@ -783,48 +784,49 @@ func (b *BeaconNode) registerRPCService() error {
p2pService := b.fetchP2P()
rpcService := rpc.NewService(b.ctx, &rpc.Config{
Host: host,
Port: port,
BeaconMonitoringHost: beaconMonitoringHost,
BeaconMonitoringPort: beaconMonitoringPort,
CertFlag: cert,
KeyFlag: key,
BeaconDB: b.db,
Broadcaster: p2pService,
PeersFetcher: p2pService,
PeerManager: p2pService,
MetadataProvider: p2pService,
ChainInfoFetcher: chainService,
HeadUpdater: chainService,
HeadFetcher: chainService,
CanonicalFetcher: chainService,
ForkFetcher: chainService,
FinalizationFetcher: chainService,
BlockReceiver: chainService,
AttestationReceiver: chainService,
GenesisTimeFetcher: chainService,
GenesisFetcher: chainService,
OptimisticModeFetcher: chainService,
AttestationsPool: b.attestationPool,
ExitPool: b.exitPool,
SlashingsPool: b.slashingsPool,
SlashingChecker: slasherService,
SyncCommitteeObjectPool: b.syncCommitteePool,
POWChainService: web3Service,
POWChainInfoFetcher: web3Service,
ChainStartFetcher: chainStartFetcher,
MockEth1Votes: mockEth1DataVotes,
SyncService: syncService,
DepositFetcher: depositFetcher,
PendingDepositFetcher: b.depositCache,
BlockNotifier: b,
StateNotifier: b,
OperationNotifier: b,
StateGen: b.stateGen,
EnableDebugRPCEndpoints: enableDebugRPCEndpoints,
MaxMsgSize: maxMsgSize,
ProposerIdsCache: b.proposerIdsCache,
ExecutionEngineCaller: web3Service,
Host: host,
Port: port,
BeaconMonitoringHost: beaconMonitoringHost,
BeaconMonitoringPort: beaconMonitoringPort,
CertFlag: cert,
KeyFlag: key,
BeaconDB: b.db,
Broadcaster: p2pService,
PeersFetcher: p2pService,
PeerManager: p2pService,
MetadataProvider: p2pService,
ChainInfoFetcher: chainService,
HeadUpdater: chainService,
HeadFetcher: chainService,
CanonicalFetcher: chainService,
ForkFetcher: chainService,
FinalizationFetcher: chainService,
BlockReceiver: chainService,
AttestationReceiver: chainService,
GenesisTimeFetcher: chainService,
GenesisFetcher: chainService,
OptimisticModeFetcher: chainService,
AttestationsPool: b.attestationPool,
ExitPool: b.exitPool,
SlashingsPool: b.slashingsPool,
SlashingChecker: slasherService,
SyncCommitteeObjectPool: b.syncCommitteePool,
POWChainService: web3Service,
POWChainInfoFetcher: web3Service,
ChainStartFetcher: chainStartFetcher,
MockEth1Votes: mockEth1DataVotes,
SyncService: syncService,
DepositFetcher: depositFetcher,
PendingDepositFetcher: b.depositCache,
BlockNotifier: b,
StateNotifier: b,
OperationNotifier: b,
StateGen: b.stateGen,
EnableDebugRPCEndpoints: enableDebugRPCEndpoints,
MaxMsgSize: maxMsgSize,
ProposerIdsCache: b.proposerIdsCache,
ExecutionEngineCaller: web3Service,
ExecutionPayloadReconstructor: web3Service,
})
return b.services.RegisterService(rpcService)

View File

@@ -39,6 +39,8 @@ go_library(
"//beacon-chain/state/v1:go_default_library",
"//config/features:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/wrapper:go_default_library",
"//container/trie:go_default_library",
"//contracts/deposit:go_default_library",
"//crypto/hash:go_default_library",
@@ -101,6 +103,7 @@ go_test(
"//beacon-chain/state/stategen:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/forks/bellatrix:go_default_library",
"//consensus-types/primitives:go_default_library",
"//consensus-types/wrapper:go_default_library",
"//container/trie:go_default_library",

View File

@@ -14,8 +14,11 @@ import (
"github.com/holiman/uint256"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
@@ -46,6 +49,15 @@ type ForkchoiceUpdatedResponse struct {
PayloadId *pb.PayloadIDBytes `json:"payloadId"`
}
// ExecutionPayloadReconstructor defines a service that can reconstruct a full beacon
// block with an execution payload from a signed beacon block and a connection
// to an execution client's engine API.
type ExecutionPayloadReconstructor interface {
ReconstructFullBellatrixBlock(
ctx context.Context, blindedBlock interfaces.SignedBeaconBlock,
) (interfaces.SignedBeaconBlock, error)
}
// EngineCaller defines a client that can interact with an Ethereum
// execution node's engine service via JSON-RPC.
type EngineCaller interface {
@@ -58,6 +70,7 @@ type EngineCaller interface {
ctx context.Context, cfg *pb.TransitionConfiguration,
) error
ExecutionBlockByHash(ctx context.Context, hash common.Hash) (*pb.ExecutionBlock, error)
ExecutionBlockByHashWithTxs(ctx context.Context, hash common.Hash) (*pb.ExecutionBlockWithTxs, error)
GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error)
}
@@ -284,19 +297,81 @@ func (s *Service) LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock,
func (s *Service) ExecutionBlockByHash(ctx context.Context, hash common.Hash) (*pb.ExecutionBlock, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExecutionBlockByHash")
defer span.End()
result := &pb.ExecutionBlock{}
err := s.rpcClient.CallContext(ctx, result, ExecutionBlockByHashMethod, hash, false /* no full transaction objects */)
return result, handleRPCError(err)
}
// ExecutionBlockByHashWithTxs fetches an execution engine block by hash by calling
// eth_blockByHash via JSON-RPC and asks for full transactions in the response.
func (s *Service) ExecutionBlockByHashWithTxs(ctx context.Context, hash common.Hash) (*pb.ExecutionBlockWithTxs, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExecutionBlockByHash")
defer span.End()
start := time.Now()
defer func() {
executionBlockByHashWithTxsLatency.Observe(float64(time.Since(start).Milliseconds()))
}()
result := &pb.ExecutionBlockWithTxs{}
err := s.rpcClient.CallContext(ctx, result, ExecutionBlockByHashMethod, hash, true /* full transaction objects */)
return result, handleRPCError(err)
}
// ReconstructFullBellatrixBlock takes in a blinded beacon block and reconstructs
// a beacon block with a full execution payload via the engine API.
func (s *Service) ReconstructFullBellatrixBlock(
ctx context.Context, blindedBlock interfaces.SignedBeaconBlock,
) (interfaces.SignedBeaconBlock, error) {
if err := wrapper.BeaconBlockIsNil(blindedBlock); err != nil {
return nil, errors.Wrap(err, "cannot reconstruct bellatrix block from nil data")
}
if !blindedBlock.Block().IsBlinded() {
return nil, errors.New("can only reconstruct block from blinded block format")
}
start := time.Now()
defer func() {
executionPayloadReconstructionLatency.Observe(float64(time.Since(start).Milliseconds()))
reconstructedExecutionPayloadCount.Add(1)
}()
header, err := blindedBlock.Block().Body().ExecutionPayloadHeader()
if err != nil {
return nil, err
}
executionBlockHash := common.BytesToHash(header.BlockHash)
executionBlock, err := s.ExecutionBlockByHashWithTxs(ctx, executionBlockHash)
if err != nil {
return nil, fmt.Errorf("could not fetch execution block with txs by hash %#x: %v", executionBlockHash, err)
}
payload := fullPayloadFromExecutionBlock(header, executionBlock)
return wrapper.BuildSignedBeaconBlockFromExecutionPayload(blindedBlock, payload)
}
func fullPayloadFromExecutionBlock(header *ethpb.ExecutionPayloadHeader, block *pb.ExecutionBlockWithTxs) *pb.ExecutionPayload {
return &pb.ExecutionPayload{
ParentHash: header.ParentHash,
FeeRecipient: header.FeeRecipient,
StateRoot: header.StateRoot,
ReceiptsRoot: header.ReceiptsRoot,
LogsBloom: header.LogsBloom,
PrevRandao: header.PrevRandao,
BlockNumber: header.BlockNumber,
GasLimit: header.GasLimit,
GasUsed: header.GasUsed,
Timestamp: header.Timestamp,
ExtraData: header.ExtraData,
BaseFeePerGas: header.BaseFeePerGas,
BlockHash: block.Hash,
Transactions: block.Transactions,
}
}
// Handles errors received from the RPC server according to the specification.
func handleRPCError(err error) error {
if err == nil {
return nil
}
if isTimeout(err) {
return errors.Wrapf(ErrHTTPTimeout, "%s", err)
return ErrHTTPTimeout
}
e, ok := err.(rpc.Error)
if !ok {
@@ -307,7 +382,7 @@ func handleRPCError(err error) error {
"here https://docs.prylabs.network/docs/execution-node/authentication")
return fmt.Errorf("could not authenticate connection to execution client: %v", err)
}
return errors.Wrap(err, "got an unexpected error")
return fmt.Errorf("got an unexpected error in JSON-RPC response: %v", err)
}
switch e.ErrorCode() {
case -32700:
@@ -330,7 +405,7 @@ func handleRPCError(err error) error {
// Only -32000 status codes are data errors in the RPC specification.
errWithData, ok := err.(rpc.DataError)
if !ok {
return errors.Wrap(err, "got an unexpected error")
return fmt.Errorf("got an unexpected error in JSON-RPC response: %v", err)
}
return errors.Wrapf(ErrServer, "%v", errWithData.ErrorData())
default:

View File

@@ -12,20 +12,26 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/holiman/uint256"
"github.com/pkg/errors"
mocks "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/forks/bellatrix"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
"google.golang.org/protobuf/proto"
)
var (
_ = EngineCaller(&Service{})
_ = ExecutionPayloadReconstructor(&Service{})
_ = EngineCaller(&mocks.EngineClient{})
)
@@ -534,6 +540,87 @@ func Test_tDStringToUint256(t *testing.T) {
require.ErrorContains(t, "hex number > 256 bits", err)
}
func TestReconstructFullBellatrixBlock(t *testing.T) {
ctx := context.Background()
t.Run("nil block", func(t *testing.T) {
service := &Service{}
_, err := service.ReconstructFullBellatrixBlock(ctx, nil)
require.ErrorContains(t, "nil data", err)
})
t.Run("only blinded block", func(t *testing.T) {
want := "can only reconstruct block from blinded block format"
service := &Service{}
bellatrixBlock := util.NewBeaconBlockBellatrix()
wrapped, err := wrapper.WrappedSignedBeaconBlock(bellatrixBlock)
require.NoError(t, err)
_, err = service.ReconstructFullBellatrixBlock(ctx, wrapped)
require.ErrorContains(t, want, err)
})
t.Run("properly reconstructs block with correct payload", func(t *testing.T) {
fix := fixtures()
payload, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload)
require.Equal(t, true, ok)
jsonPayload := make(map[string]interface{})
tx := types.NewTransaction(
0,
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
big.NewInt(0), 0, big.NewInt(0),
nil,
)
txs := []*types.Transaction{tx}
encodedBinaryTxs := make([][]byte, 1)
var err error
encodedBinaryTxs[0], err = txs[0].MarshalBinary()
require.NoError(t, err)
payload.Transactions = encodedBinaryTxs
jsonPayload["transactions"] = txs
num := big.NewInt(1)
encodedNum := hexutil.EncodeBig(num)
jsonPayload["hash"] = hexutil.Encode(payload.BlockHash)
jsonPayload["number"] = encodedNum
jsonPayload["difficulty"] = encodedNum
jsonPayload["size"] = encodedNum
jsonPayload["baseFeePerGas"] = encodedNum
header, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
respJSON := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": jsonPayload,
}
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
service := &Service{}
service.rpcClient = rpcClient
blindedBlock := util.NewBlindedBeaconBlockBellatrix()
blindedBlock.Block.Body.ExecutionPayloadHeader = header
wrapped, err := wrapper.WrappedSignedBeaconBlock(blindedBlock)
require.NoError(t, err)
reconstructed, err := service.ReconstructFullBellatrixBlock(ctx, wrapped)
require.NoError(t, err)
got, err := reconstructed.Block().Body().ExecutionPayload()
require.NoError(t, err)
require.DeepEqual(t, payload, got)
})
}
func TestExchangeTransitionConfiguration(t *testing.T) {
fix := fixtures()
ctx := context.Background()

View File

@@ -27,4 +27,22 @@ var (
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
},
)
executionBlockByHashWithTxsLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "execution_block_by_hash_with_txs_latency_milliseconds",
Help: "Captures RPC latency for retrieving in milliseconds",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
},
)
executionPayloadReconstructionLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "execution_payload_reconstruction_latency_milliseconds",
Help: "Captures RPC latency for retrieving in milliseconds",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
},
)
reconstructedExecutionPayloadCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "reconstructed_execution_payload_count",
Help: "Count the number of execution payloads that are reconstructed using JSON-RPC from payload headers",
})
)

View File

@@ -18,6 +18,8 @@ go_library(
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/v1:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces: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",

View File

@@ -9,26 +9,31 @@ import (
"github.com/holiman/uint256"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
)
// EngineClient --
type EngineClient struct {
NewPayloadResp []byte
PayloadIDBytes *pb.PayloadIDBytes
ForkChoiceUpdatedResp []byte
ExecutionPayload *pb.ExecutionPayload
ExecutionBlock *pb.ExecutionBlock
Err error
ErrLatestExecBlock error
ErrExecBlockByHash error
ErrForkchoiceUpdated error
ErrNewPayload error
BlockByHashMap map[[32]byte]*pb.ExecutionBlock
TerminalBlockHash []byte
TerminalBlockHashExists bool
OverrideValidHash [32]byte
NewPayloadResp []byte
PayloadIDBytes *pb.PayloadIDBytes
ForkChoiceUpdatedResp []byte
ExecutionPayload *pb.ExecutionPayload
ExecutionBlock *pb.ExecutionBlock
Err error
ErrLatestExecBlock error
ErrExecBlockByHash error
ErrForkchoiceUpdated error
ErrNewPayload error
ExecutionPayloadByBlockHash map[[32]byte]*pb.ExecutionPayload
BlockByHashMap map[[32]byte]*pb.ExecutionBlock
BlockWithTxsByHashMap map[[32]byte]*pb.ExecutionBlockWithTxs
NumReconstructedPayloads uint64
TerminalBlockHash []byte
TerminalBlockHashExists bool
OverrideValidHash [32]byte
}
// NewPayload --
@@ -70,6 +75,33 @@ func (e *EngineClient) ExecutionBlockByHash(_ context.Context, h common.Hash) (*
return b, e.ErrExecBlockByHash
}
func (e *EngineClient) ReconstructFullBellatrixBlock(
_ context.Context, blindedBlock interfaces.SignedBeaconBlock,
) (interfaces.SignedBeaconBlock, error) {
if !blindedBlock.Block().IsBlinded() {
return nil, errors.New("block must be blinded")
}
header, err := blindedBlock.Block().Body().ExecutionPayloadHeader()
if err != nil {
return nil, err
}
payload, ok := e.ExecutionPayloadByBlockHash[bytesutil.ToBytes32(header.BlockHash)]
if !ok {
return nil, errors.New("block not found")
}
e.NumReconstructedPayloads++
return wrapper.BuildSignedBeaconBlockFromExecutionPayload(blindedBlock, payload)
}
// ExecutionBlockByHashWithTxs --
func (e *EngineClient) ExecutionBlockByHashWithTxs(_ context.Context, h common.Hash) (*pb.ExecutionBlockWithTxs, error) {
b, ok := e.BlockWithTxsByHashMap[h]
if !ok {
return nil, errors.New("block not found")
}
return b, e.ErrExecBlockByHash
}
// GetTerminalBlockHash --
func (e *EngineClient) GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error) {
ttd := new(big.Int)

View File

@@ -29,6 +29,7 @@ go_library(
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/operations/voluntaryexits:go_default_library",
"//beacon-chain/p2p:go_default_library",
"//beacon-chain/powchain:go_default_library",
"//beacon-chain/rpc/eth/helpers:go_default_library",
"//beacon-chain/rpc/prysm/v1alpha1/validator:go_default_library",
"//beacon-chain/rpc/statefetcher:go_default_library",

View File

@@ -470,8 +470,42 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (
ExecutionOptimistic: isOptimistic,
}, nil
}
if _, err := blk.PbBlindedBellatrixBlock(); err == nil {
signedFullBlock, err := bs.ExecutionPayloadReconstructor.ReconstructFullBellatrixBlock(ctx, blk)
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not reconstruct full execution payload to create signed beacon block: %v",
err,
)
}
bellatrixBlk, err = signedFullBlock.PbBellatrixBlock()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
v2Blk, err := migration.V1Alpha1BeaconBlockBellatrixToV2(bellatrixBlk.Block)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get block root: %v", err)
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not check if block is optimistic: %v", err)
}
return &ethpbv2.BlockResponseV2{
Version: ethpbv2.Version_BELLATRIX,
Data: &ethpbv2.SignedBeaconBlockContainerV2{
Message: &ethpbv2.SignedBeaconBlockContainerV2_BellatrixBlock{BellatrixBlock: v2Blk},
Signature: blk.Signature(),
},
ExecutionOptimistic: isOptimistic,
}, nil
}
// ErrUnsupportedBellatrixBlock means that we have another block type
if !errors.Is(err, wrapper.ErrUnsupportedBellatrixBlock) {
if !errors.Is(err, wrapper.ErrUnsupportedBlindedBellatrixBlock) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
v1alpha1validator "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/validator"
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/statefetcher"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
@@ -21,22 +22,23 @@ import (
// Server defines a server implementation of the gRPC Beacon Chain service,
// providing RPC endpoints to access data relevant to the Ethereum Beacon Chain.
type Server struct {
BeaconDB db.ReadOnlyDatabase
ChainInfoFetcher blockchain.ChainInfoFetcher
GenesisTimeFetcher blockchain.TimeFetcher
BlockReceiver blockchain.BlockReceiver
BlockNotifier blockfeed.Notifier
OperationNotifier operation.Notifier
Broadcaster p2p.Broadcaster
AttestationsPool attestations.Pool
SlashingsPool slashings.PoolManager
VoluntaryExitsPool voluntaryexits.PoolManager
StateGenService stategen.StateManager
StateFetcher statefetcher.Fetcher
HeadFetcher blockchain.HeadFetcher
OptimisticModeFetcher blockchain.OptimisticModeFetcher
V1Alpha1ValidatorServer *v1alpha1validator.Server
SyncChecker sync.Checker
CanonicalHistory *stategen.CanonicalHistory
HeadUpdater blockchain.HeadUpdater
BeaconDB db.ReadOnlyDatabase
ChainInfoFetcher blockchain.ChainInfoFetcher
GenesisTimeFetcher blockchain.TimeFetcher
BlockReceiver blockchain.BlockReceiver
BlockNotifier blockfeed.Notifier
OperationNotifier operation.Notifier
Broadcaster p2p.Broadcaster
AttestationsPool attestations.Pool
SlashingsPool slashings.PoolManager
VoluntaryExitsPool voluntaryexits.PoolManager
StateGenService stategen.StateManager
StateFetcher statefetcher.Fetcher
HeadFetcher blockchain.HeadFetcher
OptimisticModeFetcher blockchain.OptimisticModeFetcher
V1Alpha1ValidatorServer *v1alpha1validator.Server
SyncChecker sync.Checker
CanonicalHistory *stategen.CanonicalHistory
HeadUpdater blockchain.HeadUpdater
ExecutionPayloadReconstructor powchain.ExecutionPayloadReconstructor
}

View File

@@ -70,48 +70,49 @@ type Service struct {
// Config options for the beacon node RPC server.
type Config struct {
Host string
Port string
CertFlag string
KeyFlag string
BeaconMonitoringHost string
BeaconMonitoringPort int
BeaconDB db.HeadAccessDatabase
ChainInfoFetcher blockchain.ChainInfoFetcher
HeadUpdater blockchain.HeadUpdater
HeadFetcher blockchain.HeadFetcher
CanonicalFetcher blockchain.CanonicalFetcher
ForkFetcher blockchain.ForkFetcher
FinalizationFetcher blockchain.FinalizationFetcher
AttestationReceiver blockchain.AttestationReceiver
BlockReceiver blockchain.BlockReceiver
POWChainService powchain.Chain
ChainStartFetcher powchain.ChainStartFetcher
POWChainInfoFetcher powchain.ChainInfoFetcher
GenesisTimeFetcher blockchain.TimeFetcher
GenesisFetcher blockchain.GenesisFetcher
EnableDebugRPCEndpoints bool
MockEth1Votes bool
AttestationsPool attestations.Pool
ExitPool voluntaryexits.PoolManager
SlashingsPool slashings.PoolManager
SlashingChecker slasherservice.SlashingChecker
SyncCommitteeObjectPool synccommittee.Pool
SyncService chainSync.Checker
Broadcaster p2p.Broadcaster
PeersFetcher p2p.PeersProvider
PeerManager p2p.PeerManager
MetadataProvider p2p.MetadataProvider
DepositFetcher depositcache.DepositFetcher
PendingDepositFetcher depositcache.PendingDepositsFetcher
StateNotifier statefeed.Notifier
BlockNotifier blockfeed.Notifier
OperationNotifier opfeed.Notifier
StateGen *stategen.State
MaxMsgSize int
ExecutionEngineCaller powchain.EngineCaller
ProposerIdsCache *cache.ProposerPayloadIDsCache
OptimisticModeFetcher blockchain.OptimisticModeFetcher
Host string
Port string
CertFlag string
KeyFlag string
BeaconMonitoringHost string
BeaconMonitoringPort int
BeaconDB db.HeadAccessDatabase
ChainInfoFetcher blockchain.ChainInfoFetcher
HeadUpdater blockchain.HeadUpdater
HeadFetcher blockchain.HeadFetcher
CanonicalFetcher blockchain.CanonicalFetcher
ForkFetcher blockchain.ForkFetcher
FinalizationFetcher blockchain.FinalizationFetcher
AttestationReceiver blockchain.AttestationReceiver
BlockReceiver blockchain.BlockReceiver
POWChainService powchain.Chain
ChainStartFetcher powchain.ChainStartFetcher
POWChainInfoFetcher powchain.ChainInfoFetcher
GenesisTimeFetcher blockchain.TimeFetcher
GenesisFetcher blockchain.GenesisFetcher
EnableDebugRPCEndpoints bool
MockEth1Votes bool
AttestationsPool attestations.Pool
ExitPool voluntaryexits.PoolManager
SlashingsPool slashings.PoolManager
SlashingChecker slasherservice.SlashingChecker
SyncCommitteeObjectPool synccommittee.Pool
SyncService chainSync.Checker
Broadcaster p2p.Broadcaster
PeersFetcher p2p.PeersProvider
PeerManager p2p.PeerManager
MetadataProvider p2p.MetadataProvider
DepositFetcher depositcache.DepositFetcher
PendingDepositFetcher depositcache.PendingDepositsFetcher
StateNotifier statefeed.Notifier
BlockNotifier blockfeed.Notifier
OperationNotifier opfeed.Notifier
StateGen *stategen.State
MaxMsgSize int
ExecutionEngineCaller powchain.EngineCaller
ProposerIdsCache *cache.ProposerPayloadIDsCache
OptimisticModeFetcher blockchain.OptimisticModeFetcher
ExecutionPayloadReconstructor powchain.ExecutionPayloadReconstructor
}
// NewService instantiates a new RPC service instance that will
@@ -304,11 +305,12 @@ func (s *Service) Start() {
StateGenService: s.cfg.StateGen,
ReplayerBuilder: ch,
},
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
HeadFetcher: s.cfg.HeadFetcher,
VoluntaryExitsPool: s.cfg.ExitPool,
V1Alpha1ValidatorServer: validatorServer,
SyncChecker: s.cfg.SyncService,
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
HeadFetcher: s.cfg.HeadFetcher,
VoluntaryExitsPool: s.cfg.ExitPool,
V1Alpha1ValidatorServer: validatorServer,
SyncChecker: s.cfg.SyncService,
ExecutionPayloadReconstructor: s.cfg.ExecutionPayloadReconstructor,
}
ethpbv1alpha1.RegisterNodeServer(s.grpcServer, nodeServer)
ethpbservice.RegisterBeaconNodeServer(s.grpcServer, nodeServerV1)

View File

@@ -76,6 +76,7 @@ go_library(
"//beacon-chain/p2p/encoder:go_default_library",
"//beacon-chain/p2p/peers:go_default_library",
"//beacon-chain/p2p/types:go_default_library",
"//beacon-chain/powchain:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//cache/lru:go_default_library",
@@ -186,6 +187,7 @@ go_test(
"//beacon-chain/p2p/testing:go_default_library",
"//beacon-chain/p2p/types:go_default_library",
"//beacon-chain/powchain:go_default_library",
"//beacon-chain/powchain/testing:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/state/v1:go_default_library",
@@ -194,6 +196,7 @@ go_test(
"//cmd/beacon-chain/flags:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/forks/bellatrix:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//consensus-types/wrapper:go_default_library",
@@ -202,6 +205,7 @@ go_test(
"//encoding/bytesutil:go_default_library",
"//encoding/ssz/equality:go_default_library",
"//network/forks:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/attestation:go_default_library",
"//proto/prysm/v1alpha1/metadata:go_default_library",
@@ -211,6 +215,8 @@ go_test(
"//time:go_default_library",
"//time/slots:go_default_library",
"@com_github_d4l3k_messagediff//:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enr:go_default_library",
"@com_github_golang_snappy//:go_default_library",
"@com_github_kevinms_leakybucket_go//:go_default_library",

View File

@@ -75,7 +75,13 @@ var (
Buckets: []float64{10, 50, 100, 200, 400, 800, 1600, 3200},
},
)
rpcBlocksByRangeResponseLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "rpc_blocks_by_range_response_latency_milliseconds",
Help: "Captures total time to respond to rpc blocks by range requests in a milliseconds distribution",
Buckets: []float64{250, 500, 1000, 1500, 2000, 3000, 4000, 10000},
},
)
arrivalBlockPropagationHistogram = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "block_arrival_latency_milliseconds",

View File

@@ -11,6 +11,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/operations/synccommittee"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
)
@@ -120,3 +121,10 @@ func WithSlasherBlockHeadersFeed(slasherBlockHeadersFeed *event.Feed) Option {
return nil
}
}
func WithExecutionPayloadReconstructor(r powchain.ExecutionPayloadReconstructor) Option {
return func(s *Service) error {
s.cfg.executionPayloadReconstructor = r
return nil
}
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/monitoring/tracing"
pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
@@ -158,18 +159,29 @@ func (s *Service) writeBlockRangeToStream(ctx context.Context, startSlot, endSlo
tracing.AnnotateError(span, err)
return err
}
start := time.Now()
for _, b := range blks {
if b == nil || b.IsNil() || b.Block().IsNil() {
if err := wrapper.BeaconBlockIsNil(b); err != nil {
continue
}
if chunkErr := s.chunkBlockWriter(stream, b); chunkErr != nil {
log.WithError(chunkErr).Debug("Could not send a chunked response")
blockToWrite := b
if blockToWrite.Block().IsBlinded() {
fullBlock, err := s.cfg.executionPayloadReconstructor.ReconstructFullBellatrixBlock(ctx, blockToWrite)
if err != nil {
log.WithError(err).Error("Could not get reconstruct full bellatrix block from blinded body")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
return err
}
blockToWrite = fullBlock
}
if chunkErr := s.chunkBlockWriter(stream, blockToWrite); chunkErr != nil {
log.WithError(chunkErr).Error("Could not send a chunked response")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, chunkErr)
return chunkErr
}
}
rpcBlocksByRangeResponseLatency.Observe(float64(time.Since(start).Milliseconds()))
// Return error in the event we have an invalid parent.
return err
}

View File

@@ -3,10 +3,13 @@ package sync
import (
"context"
"io"
"math/big"
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/kevinms/leakybucket-go"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/protocol"
@@ -17,12 +20,15 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
"github.com/prysmaticlabs/prysm/cmd/beacon-chain/flags"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/forks/bellatrix"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
@@ -155,6 +161,121 @@ func TestRPCBeaconBlocksByRange_ReturnCorrectNumberBack(t *testing.T) {
}
}
func TestRPCBeaconBlocksByRange_CanReconstructFullPayloadBlocks(t *testing.T) {
p1 := p2ptest.NewTestP2P(t)
p2 := p2ptest.NewTestP2P(t)
p1.Connect(p2)
assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
d := db.SetupDB(t)
req := &ethpb.BeaconBlocksByRangeRequest{
StartSlot: 200,
Step: 21,
Count: 33,
}
// Start service with 160 as allowed blocks capacity (and almost zero capacity recovery).
parent := bytesutil.PadTo([]byte("parentHash"), fieldparams.RootLength)
stateRoot := bytesutil.PadTo([]byte("stateRoot"), fieldparams.RootLength)
receiptsRoot := bytesutil.PadTo([]byte("receiptsRoot"), fieldparams.RootLength)
logsBloom := bytesutil.PadTo([]byte("logs"), fieldparams.LogsBloomLength)
tx := gethTypes.NewTransaction(
0,
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
big.NewInt(0), 0, big.NewInt(0),
nil,
)
txs := []*gethTypes.Transaction{tx}
encodedBinaryTxs := make([][]byte, 1)
var err error
encodedBinaryTxs[0], err = txs[0].MarshalBinary()
require.NoError(t, err)
blockHash := bytesutil.ToBytes32([]byte("foo"))
payload := &enginev1.ExecutionPayload{
ParentHash: parent,
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
StateRoot: stateRoot,
ReceiptsRoot: receiptsRoot,
LogsBloom: logsBloom,
PrevRandao: blockHash[:],
BlockNumber: 0,
GasLimit: 0,
GasUsed: 0,
Timestamp: 0,
ExtraData: make([]byte, 0),
BlockHash: blockHash[:],
BaseFeePerGas: bytesutil.PadTo([]byte("baseFeePerGas"), fieldparams.RootLength),
Transactions: encodedBinaryTxs,
}
header, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
endSlot := req.StartSlot.Add(req.Step * (req.Count - 1))
expectedRoots := make([][32]byte, req.Count)
// Populate the database with blocks that would match the request.
for i, j := endSlot, req.Count-1; i >= req.StartSlot; i -= types.Slot(req.Step) {
blk := util.NewBlindedBeaconBlockBellatrix()
blk.Block.Body.ExecutionPayloadHeader = header
blk.Block.Slot = i
rt, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
expectedRoots[j] = rt
wsb, err := wrapper.WrappedSignedBeaconBlock(blk)
require.NoError(t, err)
require.NoError(t, d.SaveBlock(context.Background(), wsb))
j--
}
mockEngine := &mockPOW.EngineClient{
ExecutionPayloadByBlockHash: map[[32]byte]*enginev1.ExecutionPayload{
blockHash: payload,
},
}
r := &Service{cfg: &config{
p2p: p1,
beaconDB: d,
chain: &chainMock.ChainService{},
executionPayloadReconstructor: mockEngine,
},
rateLimiter: newRateLimiter(p1),
}
pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
topic := string(pcl)
r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(0.000001, int64(req.Count*10), false)
var wg sync.WaitGroup
wg.Add(1)
p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
defer wg.Done()
prevSlot := types.Slot(0)
require.Equal(t, uint64(len(expectedRoots)), req.Count, "Number of roots not expected")
for i, j := req.StartSlot, 0; i < req.StartSlot.Add(req.Count*req.Step); i += types.Slot(req.Step) {
expectSuccess(t, stream)
res := &ethpb.SignedBeaconBlockBellatrix{}
assert.NoError(t, r.cfg.p2p.Encoding().DecodeWithMaxLength(stream, res))
if res.Block.Slot < prevSlot {
t.Errorf("Received block is unsorted with slot %d lower than previous slot %d", res.Block.Slot, prevSlot)
}
rt, err := res.Block.HashTreeRoot()
require.NoError(t, err)
assert.Equal(t, expectedRoots[j], rt, "roots not equal")
prevSlot = res.Block.Slot
j++
}
require.Equal(t, uint64(33), mockEngine.NumReconstructedPayloads, "wrong number of reconstructed payloads")
})
stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
require.NoError(t, err)
require.NoError(t, r.beaconBlocksByRangeRPCHandler(context.Background(), req, stream1))
if util.WaitTimeout(&wg, 1*time.Second) {
t.Fatal("Did not receive stream within 1 sec")
}
}
func TestRPCBeaconBlocksByRange_RPCHandlerReturnsSortedBlocks(t *testing.T) {
p1 := p2ptest.NewTestP2P(t)
p2 := p2ptest.NewTestP2P(t)

View File

@@ -9,6 +9,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
)
// sendRecentBeaconBlocksRequest sends a recent beacon blocks request to a peer to get
@@ -68,13 +69,24 @@ func (s *Service) beaconBlocksRootRPCHandler(ctx context.Context, msg interface{
s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream)
return err
}
if blk == nil || blk.IsNil() {
if err := wrapper.BeaconBlockIsNil(blk); err != nil {
continue
}
if blk.Block().IsBlinded() {
blk, err = s.cfg.executionPayloadReconstructor.ReconstructFullBellatrixBlock(ctx, blk)
if err != nil {
log.WithError(err).Error("Could not get reconstruct full bellatrix block from blinded body")
s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream)
return err
}
}
if err := s.chunkBlockWriter(stream, blk); err != nil {
return err
}
}
closeStream(stream, log)
return nil
}

View File

@@ -2,10 +2,13 @@ package sync
import (
"context"
"math/big"
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/kevinms/leakybucket-go"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/protocol"
@@ -16,9 +19,14 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
p2pTypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/forks/bellatrix"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
@@ -75,6 +83,103 @@ func TestRecentBeaconBlocksRPCHandler_ReturnsBlocks(t *testing.T) {
}
}
func TestRecentBeaconBlocksRPCHandler_ReturnsBlocks_ReconstructsExecutionPayloads(t *testing.T) {
p1 := p2ptest.NewTestP2P(t)
p2 := p2ptest.NewTestP2P(t)
p1.Connect(p2)
assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
d := db.SetupDB(t)
// Start service with 160 as allowed blocks capacity (and almost zero capacity recovery).
parent := bytesutil.PadTo([]byte("parentHash"), fieldparams.RootLength)
stateRoot := bytesutil.PadTo([]byte("stateRoot"), fieldparams.RootLength)
receiptsRoot := bytesutil.PadTo([]byte("receiptsRoot"), fieldparams.RootLength)
logsBloom := bytesutil.PadTo([]byte("logs"), fieldparams.LogsBloomLength)
tx := gethTypes.NewTransaction(
0,
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
big.NewInt(0), 0, big.NewInt(0),
nil,
)
txs := []*gethTypes.Transaction{tx}
encodedBinaryTxs := make([][]byte, 1)
var err error
encodedBinaryTxs[0], err = txs[0].MarshalBinary()
require.NoError(t, err)
blockHash := bytesutil.ToBytes32([]byte("foo"))
payload := &enginev1.ExecutionPayload{
ParentHash: parent,
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
StateRoot: stateRoot,
ReceiptsRoot: receiptsRoot,
LogsBloom: logsBloom,
PrevRandao: blockHash[:],
BlockNumber: 0,
GasLimit: 0,
GasUsed: 0,
Timestamp: 0,
ExtraData: make([]byte, 0),
BlockHash: blockHash[:],
BaseFeePerGas: bytesutil.PadTo([]byte("baseFeePerGas"), fieldparams.RootLength),
Transactions: encodedBinaryTxs,
}
header, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
var blkRoots p2pTypes.BeaconBlockByRootsReq
// Populate the database with blocks that would match the request.
for i := types.Slot(1); i < 11; i++ {
blk := util.NewBlindedBeaconBlockBellatrix()
blk.Block.Body.ExecutionPayloadHeader = header
blk.Block.Slot = i
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
wsb, err := wrapper.WrappedSignedBeaconBlock(blk)
require.NoError(t, err)
require.NoError(t, d.SaveBlock(context.Background(), wsb))
blkRoots = append(blkRoots, root)
}
mockEngine := &mockPOW.EngineClient{
ExecutionPayloadByBlockHash: map[[32]byte]*enginev1.ExecutionPayload{
blockHash: payload,
},
}
r := &Service{cfg: &config{
p2p: p1,
beaconDB: d,
executionPayloadReconstructor: mockEngine,
}, rateLimiter: newRateLimiter(p1)}
r.cfg.chain = &mock.ChainService{ValidatorsRoot: [32]byte{}}
pcl := protocol.ID(p2p.RPCBlocksByRootTopicV1)
topic := string(pcl)
r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(10000, 10000, false)
var wg sync.WaitGroup
wg.Add(1)
p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
defer wg.Done()
for i := range blkRoots {
expectSuccess(t, stream)
res := util.NewBeaconBlockBellatrix()
assert.NoError(t, r.cfg.p2p.Encoding().DecodeWithMaxLength(stream, res))
if uint64(res.Block.Slot) != uint64(i+1) {
t.Errorf("Received unexpected block slot %d but wanted %d", res.Block.Slot, i+1)
}
}
require.Equal(t, uint64(10), mockEngine.NumReconstructedPayloads)
})
stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
require.NoError(t, err)
err = r.beaconBlocksRootRPCHandler(context.Background(), &blkRoots, stream1)
assert.NoError(t, err)
if util.WaitTimeout(&wg, 1*time.Second) {
t.Fatal("Did not receive stream within 1 sec")
}
}
func TestRecentBeaconBlocks_RPCRequestSent(t *testing.T) {
p1 := p2ptest.NewTestP2P(t)
p2 := p2ptest.NewTestP2P(t)

View File

@@ -30,6 +30,7 @@ func WriteBlockChunk(stream libp2pcore.Stream, chain blockchain.ChainInfoFetcher
return err
}
var obtainedCtx []byte
switch blk.Version() {
case version.Phase0:
valRoot := chain.GenesisValidatorsRoot()
@@ -45,7 +46,7 @@ func WriteBlockChunk(stream libp2pcore.Stream, chain blockchain.ChainInfoFetcher
return err
}
obtainedCtx = digest[:]
case version.Bellatrix:
case version.Bellatrix, version.BellatrixBlind:
valRoot := chain.GenesisValidatorsRoot()
digest, err := forks.ForkDigestFromEpoch(params.BeaconConfig().BellatrixForkEpoch, valRoot[:])
if err != nil {

View File

@@ -29,6 +29,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/operations/synccommittee"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
lruwrpr "github.com/prysmaticlabs/prysm/cache/lru"
"github.com/prysmaticlabs/prysm/cmd/beacon-chain/flags"
@@ -68,21 +69,22 @@ type validationFn func(ctx context.Context) (pubsub.ValidationResult, error)
// config to hold dependencies for the sync service.
type config struct {
attestationNotifier operation.Notifier
p2p p2p.P2P
beaconDB db.NoHeadAccessDatabase
attPool attestations.Pool
exitPool voluntaryexits.PoolManager
slashingPool slashings.PoolManager
syncCommsPool synccommittee.Pool
chain blockchainService
initialSync Checker
stateNotifier statefeed.Notifier
blockNotifier blockfeed.Notifier
operationNotifier operation.Notifier
stateGen *stategen.State
slasherAttestationsFeed *event.Feed
slasherBlockHeadersFeed *event.Feed
attestationNotifier operation.Notifier
p2p p2p.P2P
beaconDB db.NoHeadAccessDatabase
attPool attestations.Pool
exitPool voluntaryexits.PoolManager
slashingPool slashings.PoolManager
syncCommsPool synccommittee.Pool
chain blockchainService
initialSync Checker
stateNotifier statefeed.Notifier
blockNotifier blockfeed.Notifier
operationNotifier operation.Notifier
executionPayloadReconstructor powchain.ExecutionPayloadReconstructor
stateGen *stategen.State
slasherAttestationsFeed *event.Feed
slasherBlockHeadersFeed *event.Feed
}
// This defines the interface for interacting with block chain service

View File

@@ -14,6 +14,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/consensus-types/wrapper",
visibility = ["//visibility:public"],
deps = [
"//consensus-types/forks/bellatrix:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/engine/v1:go_default_library",
@@ -39,6 +40,8 @@ go_test(
],
deps = [
":go_default_library",
"//config/fieldparams:go_default_library",
"//consensus-types/forks/bellatrix:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
@@ -48,5 +51,6 @@ go_test(
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
)

View File

@@ -1,8 +1,12 @@
package wrapper
import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/consensus-types/forks/bellatrix"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
@@ -145,6 +149,115 @@ func BuildSignedBeaconBlock(blk interfaces.BeaconBlock, signature []byte) (inter
}
}
// BuildSignedBeaconBlockFromExecutionPayload takes a signed, blinded beacon block and converts into
// a full, signed beacon block by specifying an execution payload.
func BuildSignedBeaconBlockFromExecutionPayload(
blk interfaces.SignedBeaconBlock, payload *enginev1.ExecutionPayload,
) (interfaces.SignedBeaconBlock, error) {
if err := BeaconBlockIsNil(blk); err != nil {
return nil, err
}
b := blk.Block()
payloadHeader, err := b.Body().ExecutionPayloadHeader()
switch {
case errors.Is(err, ErrUnsupportedField):
return nil, errors.Wrap(err, "can only build signed beacon block from blinded format")
case err != nil:
return nil, errors.Wrap(err, "could not get execution payload header")
default:
}
payloadRoot, err := payload.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "could not hash tree root execution payload")
}
payloadHeaderRoot, err := payloadHeader.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "could not hash tree root payload header")
}
if payloadRoot != payloadHeaderRoot {
return nil, fmt.Errorf(
"payload %#x and header %#x roots do not match",
payloadRoot,
payloadHeaderRoot,
)
}
syncAgg, err := b.Body().SyncAggregate()
if err != nil {
return nil, errors.Wrap(err, "could not get sync aggregate from block body")
}
bellatrixFullBlock := &eth.SignedBeaconBlockBellatrix{
Block: &eth.BeaconBlockBellatrix{
Slot: b.Slot(),
ProposerIndex: b.ProposerIndex(),
ParentRoot: b.ParentRoot(),
StateRoot: b.StateRoot(),
Body: &eth.BeaconBlockBodyBellatrix{
RandaoReveal: b.Body().RandaoReveal(),
Eth1Data: b.Body().Eth1Data(),
Graffiti: b.Body().Graffiti(),
ProposerSlashings: b.Body().ProposerSlashings(),
AttesterSlashings: b.Body().AttesterSlashings(),
Attestations: b.Body().Attestations(),
Deposits: b.Body().Deposits(),
VoluntaryExits: b.Body().VoluntaryExits(),
SyncAggregate: syncAgg,
ExecutionPayload: payload,
},
},
Signature: blk.Signature(),
}
return wrappedBellatrixSignedBeaconBlock(bellatrixFullBlock)
}
// WrapSignedBlindedBeaconBlock converts a signed beacon block into a blinded format.
func WrapSignedBlindedBeaconBlock(blk interfaces.SignedBeaconBlock) (interfaces.SignedBeaconBlock, error) {
if err := BeaconBlockIsNil(blk); err != nil {
return nil, err
}
if blk.Block().IsBlinded() {
return blk, nil
}
b := blk.Block()
payload, err := b.Body().ExecutionPayload()
switch {
case errors.Is(err, ErrUnsupportedField):
return nil, ErrUnsupportedSignedBeaconBlock
case err != nil:
return nil, errors.Wrap(err, "could not get execution payload")
default:
}
syncAgg, err := b.Body().SyncAggregate()
if err != nil {
return nil, err
}
header, err := bellatrix.PayloadToHeader(payload)
if err != nil {
return nil, err
}
blindedBlock := &eth.SignedBlindedBeaconBlockBellatrix{
Block: &eth.BlindedBeaconBlockBellatrix{
Slot: b.Slot(),
ProposerIndex: b.ProposerIndex(),
ParentRoot: b.ParentRoot(),
StateRoot: b.StateRoot(),
Body: &eth.BlindedBeaconBlockBodyBellatrix{
RandaoReveal: b.Body().RandaoReveal(),
Eth1Data: b.Body().Eth1Data(),
Graffiti: b.Body().Graffiti(),
ProposerSlashings: b.Body().ProposerSlashings(),
AttesterSlashings: b.Body().AttesterSlashings(),
Attestations: b.Body().Attestations(),
Deposits: b.Body().Deposits(),
VoluntaryExits: b.Body().VoluntaryExits(),
SyncAggregate: syncAgg,
ExecutionPayloadHeader: header,
},
},
Signature: blk.Signature(),
}
return wrappedBellatrixSignedBlindedBeaconBlock(blindedBlock)
}
func UnwrapGenericSignedBeaconBlock(gb *eth.GenericSignedBeaconBlock) (interfaces.SignedBeaconBlock, error) {
if gb == nil {
return nil, ErrNilObjectWrapped

View File

@@ -3,11 +3,121 @@ package wrapper_test
import (
"testing"
"github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/consensus-types/forks/bellatrix"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
)
func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) {
t.Run("nil block check", func(t *testing.T) {
_, err := wrapper.BuildSignedBeaconBlockFromExecutionPayload(nil, nil)
require.ErrorIs(t, wrapper.ErrNilSignedBeaconBlock, err)
})
t.Run("unsupported field payload header", func(t *testing.T) {
altairBlock := util.NewBeaconBlockAltair()
blk, err := wrapper.WrappedSignedBeaconBlock(altairBlock)
require.NoError(t, err)
_, err = wrapper.BuildSignedBeaconBlockFromExecutionPayload(blk, nil)
require.Equal(t, true, errors.Is(err, wrapper.ErrUnsupportedField))
})
t.Run("payload header root and payload root mismatch", func(t *testing.T) {
payload := &enginev1.ExecutionPayload{
ParentHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, fieldparams.RootLength),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
}
header, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
blindedBlock := util.NewBlindedBeaconBlockBellatrix()
// Modify the header.
header.GasUsed += 1
blindedBlock.Block.Body.ExecutionPayloadHeader = header
blk, err := wrapper.WrappedSignedBeaconBlock(blindedBlock)
require.NoError(t, err)
_, err = wrapper.BuildSignedBeaconBlockFromExecutionPayload(blk, payload)
require.ErrorContains(t, "roots do not match", err)
})
t.Run("ok", func(t *testing.T) {
payload := &enginev1.ExecutionPayload{
ParentHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, fieldparams.RootLength),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
}
header, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
blindedBlock := util.NewBlindedBeaconBlockBellatrix()
blindedBlock.Block.Body.ExecutionPayloadHeader = header
blk, err := wrapper.WrappedSignedBeaconBlock(blindedBlock)
require.NoError(t, err)
builtBlock, err := wrapper.BuildSignedBeaconBlockFromExecutionPayload(blk, payload)
require.NoError(t, err)
got, err := builtBlock.Block().Body().ExecutionPayload()
require.NoError(t, err)
require.DeepEqual(t, payload, got)
})
}
func TestWrapSignedBlindedBeaconBlock(t *testing.T) {
t.Run("nil block check", func(t *testing.T) {
_, err := wrapper.BuildSignedBeaconBlockFromExecutionPayload(nil, nil)
require.ErrorIs(t, wrapper.ErrNilSignedBeaconBlock, err)
})
t.Run("unsupported field execution payload", func(t *testing.T) {
altairBlock := util.NewBeaconBlockAltair()
blk, err := wrapper.WrappedSignedBeaconBlock(altairBlock)
require.NoError(t, err)
_, err = wrapper.BuildSignedBeaconBlockFromExecutionPayload(blk, nil)
require.Equal(t, true, errors.Is(err, wrapper.ErrUnsupportedField))
})
t.Run("ok", func(t *testing.T) {
payload := &enginev1.ExecutionPayload{
ParentHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, fieldparams.RootLength),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
}
bellatrixBlk := util.NewBeaconBlockBellatrix()
bellatrixBlk.Block.Body.ExecutionPayload = payload
want, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
blk, err := wrapper.WrappedSignedBeaconBlock(bellatrixBlk)
require.NoError(t, err)
builtBlock, err := wrapper.WrapSignedBlindedBeaconBlock(blk)
require.NoError(t, err)
got, err := builtBlock.Block().Body().ExecutionPayloadHeader()
require.NoError(t, err)
require.DeepEqual(t, want, got)
})
}
func TestWrappedSignedBeaconBlock(t *testing.T) {
tests := []struct {
name string

View File

@@ -72,6 +72,7 @@ go_library(
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
"@com_github_golang_protobuf//proto:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@io_bazel_rules_go//proto/wkt:timestamp_go_proto",
"@go_googleapis//google/api:annotations_go_proto",
"@org_golang_google_protobuf//encoding/protojson:go_default_library",

View File

@@ -5,6 +5,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
)
@@ -157,6 +158,141 @@ func (e *ExecutionBlock) UnmarshalJSON(enc []byte) error {
return nil
}
type ExecutionBlockWithTxs struct {
ExecutionBlock
}
type executionBlockWithTxsJSON struct {
Number string `json:"number"`
Hash hexutil.Bytes `json:"hash"`
ParentHash hexutil.Bytes `json:"parentHash"`
Sha3Uncles hexutil.Bytes `json:"sha3Uncles"`
Miner hexutil.Bytes `json:"miner"`
StateRoot hexutil.Bytes `json:"stateRoot"`
TransactionsRoot hexutil.Bytes `json:"transactionsRoot"`
ReceiptsRoot hexutil.Bytes `json:"receiptsRoot"`
LogsBloom hexutil.Bytes `json:"logsBloom"`
Difficulty string `json:"difficulty"`
TotalDifficulty string `json:"totalDifficulty"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
GasUsed hexutil.Uint64 `json:"gasUsed"`
Timestamp hexutil.Uint64 `json:"timestamp"`
BaseFeePerGas string `json:"baseFeePerGas"`
ExtraData hexutil.Bytes `json:"extraData"`
MixHash hexutil.Bytes `json:"mixHash"`
Nonce hexutil.Bytes `json:"nonce"`
Size string `json:"size"`
Transactions []*types.Transaction `json:"transactions"`
Uncles []hexutil.Bytes `json:"uncles"`
}
// MarshalJSON defines a custom json.Marshaler interface implementation
// that uses custom json.Marshalers for the hexutil.Bytes and hexutil.Uint64 types.
func (e *ExecutionBlockWithTxs) MarshalJSON() ([]byte, error) {
transactions := make([]hexutil.Bytes, len(e.Transactions))
for i, tx := range e.Transactions {
transactions[i] = tx
}
uncles := make([]hexutil.Bytes, len(e.Uncles))
for i, ucl := range e.Uncles {
uncles[i] = ucl
}
num := new(big.Int).SetBytes(e.Number)
numHex := hexutil.EncodeBig(num)
diff := new(big.Int).SetBytes(e.Difficulty)
diffHex := hexutil.EncodeBig(diff)
size := new(big.Int).SetBytes(e.Size)
sizeHex := hexutil.EncodeBig(size)
baseFee := new(big.Int).SetBytes(bytesutil.ReverseByteOrder(e.BaseFeePerGas))
baseFeeHex := hexutil.EncodeBig(baseFee)
return json.Marshal(executionBlockJSON{
Number: numHex,
Hash: e.Hash,
ParentHash: e.ParentHash,
Sha3Uncles: e.Sha3Uncles,
Miner: e.Miner,
StateRoot: e.StateRoot,
TransactionsRoot: e.TransactionsRoot,
ReceiptsRoot: e.ReceiptsRoot,
LogsBloom: e.LogsBloom,
Difficulty: diffHex,
TotalDifficulty: e.TotalDifficulty,
GasLimit: hexutil.Uint64(e.GasLimit),
GasUsed: hexutil.Uint64(e.GasUsed),
Timestamp: hexutil.Uint64(e.Timestamp),
ExtraData: e.ExtraData,
MixHash: e.MixHash,
Nonce: e.Nonce,
Size: sizeHex,
BaseFeePerGas: baseFeeHex,
Transactions: transactions,
Uncles: uncles,
})
}
// UnmarshalJSON defines a custom json.Unmarshaler interface implementation
// that uses custom json.Unmarshalers for the hexutil.Bytes and hexutil.Uint64 types.
func (e *ExecutionBlockWithTxs) UnmarshalJSON(enc []byte) error {
dec := executionBlockWithTxsJSON{}
if err := json.Unmarshal(enc, &dec); err != nil {
return err
}
*e = ExecutionBlockWithTxs{}
num, err := hexutil.DecodeBig(dec.Number)
if err != nil {
return err
}
e.Number = num.Bytes()
e.Hash = dec.Hash
e.ParentHash = dec.ParentHash
e.Sha3Uncles = dec.Sha3Uncles
e.Miner = dec.Miner
e.StateRoot = dec.StateRoot
e.TransactionsRoot = dec.TransactionsRoot
e.ReceiptsRoot = dec.ReceiptsRoot
e.LogsBloom = dec.LogsBloom
diff, err := hexutil.DecodeBig(dec.Difficulty)
if err != nil {
return err
}
e.Difficulty = diff.Bytes()
e.TotalDifficulty = dec.TotalDifficulty
e.GasLimit = uint64(dec.GasLimit)
e.GasUsed = uint64(dec.GasUsed)
e.Timestamp = uint64(dec.Timestamp)
e.ExtraData = dec.ExtraData
e.MixHash = dec.MixHash
e.Nonce = dec.Nonce
size, err := hexutil.DecodeBig(dec.Size)
if err != nil {
return err
}
e.Size = size.Bytes()
baseFee, err := hexutil.DecodeBig(dec.BaseFeePerGas)
if err != nil {
return err
}
e.BaseFeePerGas = bytesutil.PadTo(bytesutil.ReverseByteOrder(baseFee.Bytes()), fieldparams.RootLength)
transactions := make([][]byte, len(dec.Transactions))
for i, tx := range dec.Transactions {
enc, err := tx.MarshalBinary()
if err != nil {
return err
}
transactions[i] = enc
}
e.Transactions = transactions
uncles := make([][]byte, len(dec.Uncles))
for i, ucl := range dec.Uncles {
uncles[i] = ucl
}
e.Uncles = uncles
return nil
}
type executionPayloadJSON struct {
ParentHash hexutil.Bytes `json:"parentHash"`
FeeRecipient hexutil.Bytes `json:"feeRecipient"`

View File

@@ -79,6 +79,10 @@ func (m *engineMock) NewPayload(context.Context, *pb.ExecutionPayload) ([]byte,
return nil, nil
}
func (s *engineMock) ExecutionBlockByHashWithTxs(_ context.Context, _ common.Hash) (*pb.ExecutionBlockWithTxs, error) {
return nil, nil
}
func (m *engineMock) LatestExecutionBlock(context.Context) (*pb.ExecutionBlock, error) {
return nil, nil
}