Skip to content

Commit

Permalink
feat: implement eip4788
Browse files Browse the repository at this point in the history
  • Loading branch information
terencechain committed Jul 21, 2023
1 parent 20b0fdd commit e9c0e11
Show file tree
Hide file tree
Showing 15 changed files with 408 additions and 157 deletions.
25 changes: 21 additions & 4 deletions beacon-chain/blockchain/execution_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func (s *Service) getPayloadHash(ctx context.Context, root []byte) ([32]byte, er
// notifyNewPayload signals execution engine on a new payload.
// It returns true if the EL has returned VALID for the block
func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int,
preStateHeader interfaces.ExecutionData, blk interfaces.ReadOnlySignedBeaconBlock) (bool, error) {
preStateHeader interfaces.ExecutionData, blk interfaces.ReadOnlySignedBeaconBlock, blkRoot [32]byte) (bool, error) {
ctx, span := trace.StartSpan(ctx, "blockChain.notifyNewPayload")
defer span.End()

Expand Down Expand Up @@ -223,9 +223,9 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int,
for i := range versionedHashes {
versionedHashes[i] = kzgToVersionedHash(kzgs[i])
}
lastValidHash, err = s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload, versionedHashes)
lastValidHash, err = s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload, versionedHashes, blkRoot)
} else {
lastValidHash, err = s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload, [][32]byte{} /*empty version hashes before Deneb*/)
lastValidHash, err = s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload, [][32]byte{}, [32]byte{} /*empty version hashes and root before Deneb*/)
}
switch err {
case nil:
Expand Down Expand Up @@ -326,7 +326,24 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,

var attr payloadattribute.Attributer
switch st.Version() {
case version.Capella, version.Deneb:
case version.Deneb:
withdrawals, err := st.ExpectedWithdrawals()
if err != nil {
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")
return false, emptyAttri, 0
}
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV3{
Timestamp: uint64(t.Unix()),
PrevRandao: prevRando,
SuggestedFeeRecipient: feeRecipient.Bytes(),
Withdrawals: withdrawals,
ParentBeaconBlockRoot: headRoot,
})
if err != nil {
log.WithError(err).Error("Could not get payload attribute")
return false, emptyAttri, 0
}
case version.Capella:
withdrawals, err := st.ExpectedWithdrawals()
if err != nil {
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")
Expand Down
10 changes: 8 additions & 2 deletions beacon-chain/blockchain/execution_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ func Test_NotifyNewPayload(t *testing.T) {
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
postVersion, postHeader, err := getStateVersionAndPayload(tt.postState)
require.NoError(t, err)
isValidPayload, err := service.notifyNewPayload(ctx, postVersion, postHeader, tt.blk)
isValidPayload, err := service.notifyNewPayload(ctx, postVersion, postHeader, tt.blk, [32]byte{})
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
if tt.invalidBlock {
Expand Down Expand Up @@ -740,7 +740,7 @@ func Test_NotifyNewPayload_SetOptimisticToValid(t *testing.T) {
service.cfg.ExecutionEngineCaller = e
postVersion, postHeader, err := getStateVersionAndPayload(bellatrixState)
require.NoError(t, err)
validated, err := service.notifyNewPayload(ctx, postVersion, postHeader, bellatrixBlk)
validated, err := service.notifyNewPayload(ctx, postVersion, postHeader, bellatrixBlk, [32]byte{})
require.NoError(t, err)
require.Equal(t, true, validated)
}
Expand Down Expand Up @@ -894,6 +894,12 @@ func Test_GetPayloadAttributeDeneb(t *testing.T) {
a, err = attr.Withdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(a))

attrV3, err := attr.PbV3()
require.NoError(t, err)
hr := service.headRoot()
require.Equal(t, hr, [32]byte(attrV3.ParentBeaconBlockRoot))

}

func Test_UpdateLastValidatedCheckpoint(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion beacon-chain/blockchain/process_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.ReadOnlySi
for i, b := range blks {
isValidPayload, err = s.notifyNewPayload(ctx,
postVersionAndHeaders[i].version,
postVersionAndHeaders[i].header, b)
postVersionAndHeaders[i].header, b, blockRoots[i])
if err != nil {
return s.handleInvalidExecutionError(ctx, err, blockRoots[i], b.Block().ParentRoot())
}
Expand Down
2 changes: 1 addition & 1 deletion beacon-chain/blockchain/receive_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ func (s *Service) sendBlockAttestationsToSlasher(signed interfaces.ReadOnlySigne

// validateExecutionOnBlock notifies the engine of the incoming block execution payload and returns true if the payload is valid
func (s *Service) validateExecutionOnBlock(ctx context.Context, ver int, header interfaces.ExecutionData, signed interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte) (bool, error) {
isValidPayload, err := s.notifyNewPayload(ctx, ver, header, signed)
isValidPayload, err := s.notifyNewPayload(ctx, ver, header, signed, blockRoot)
if err != nil {
return false, s.handleInvalidExecutionError(ctx, err, blockRoot, signed.Block().ParentRoot())
}
Expand Down
19 changes: 15 additions & 4 deletions beacon-chain/execution/engine_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const (
ForkchoiceUpdatedMethod = "engine_forkchoiceUpdatedV1"
// ForkchoiceUpdatedMethodV2 v2 request string for JSON-RPC.
ForkchoiceUpdatedMethodV2 = "engine_forkchoiceUpdatedV2"
// ForkchoiceUpdatedMethodV3 v3 request string for JSON-RPC.
ForkchoiceUpdatedMethodV3 = "engine_forkchoiceUpdatedV3"
// GetPayloadMethod v1 request string for JSON-RPC.
GetPayloadMethod = "engine_getPayloadV1"
// GetPayloadMethodV2 v2 request string for JSON-RPC.
Expand Down Expand Up @@ -97,7 +99,7 @@ type ExecutionPayloadReconstructor interface {
// EngineCaller defines a client that can interact with an Ethereum
// execution node's engine service via JSON-RPC.
type EngineCaller interface {
NewPayload(ctx context.Context, payload interfaces.ExecutionData, versionedHashes [][32]byte) ([]byte, error)
NewPayload(ctx context.Context, payload interfaces.ExecutionData, versionedHashes [][32]byte, parentBlockRoot [32]byte) ([]byte, error)
ForkchoiceUpdated(
ctx context.Context, state *pb.ForkchoiceState, attrs payloadattribute.Attributer,
) (*pb.PayloadIDBytes, []byte, error)
Expand All @@ -112,7 +114,7 @@ type EngineCaller interface {
var EmptyBlockHash = errors.New("Block hash is empty 0x0000...")

// NewPayload calls the engine_newPayloadVX method via JSON-RPC.
func (s *Service) NewPayload(ctx context.Context, payload interfaces.ExecutionData, versionedHashes [][32]byte) ([]byte, error) {
func (s *Service) NewPayload(ctx context.Context, payload interfaces.ExecutionData, versionedHashes [][32]byte, parentBlockRoot [32]byte) ([]byte, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.NewPayload")
defer span.End()
start := time.Now()
Expand Down Expand Up @@ -149,7 +151,7 @@ func (s *Service) NewPayload(ctx context.Context, payload interfaces.ExecutionDa
if !ok {
return nil, errors.New("execution data must be a Deneb execution payload")
}
err := s.rpcClient.CallContext(ctx, result, NewPayloadMethodV3, payloadPb, versionedHashes)
err := s.rpcClient.CallContext(ctx, result, NewPayloadMethodV3, payloadPb, versionedHashes, parentBlockRoot)
if err != nil {
return nil, handleRPCError(err)
}
Expand Down Expand Up @@ -200,7 +202,7 @@ func (s *Service) ForkchoiceUpdated(
if err != nil {
return nil, nil, handleRPCError(err)
}
case version.Capella, version.Deneb:
case version.Capella:
a, err := attrs.PbV2()
if err != nil {
return nil, nil, err
Expand All @@ -209,6 +211,15 @@ func (s *Service) ForkchoiceUpdated(
if err != nil {
return nil, nil, handleRPCError(err)
}
case version.Deneb:
a, err := attrs.PbV3()
if err != nil {
return nil, nil, err
}
err = s.rpcClient.CallContext(ctx, result, ForkchoiceUpdatedMethodV3, state, a)
if err != nil {
return nil, nil, handleRPCError(err)
}
default:
return nil, nil, fmt.Errorf("unknown payload attribute version: %v", attrs.Version())
}
Expand Down
30 changes: 15 additions & 15 deletions beacon-chain/execution/engine_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func TestClient_IPC(t *testing.T) {
require.Equal(t, true, ok)
wrappedPayload, err := blocks.WrappedExecutionPayload(req)
require.NoError(t, err)
latestValidHash, err := srv.NewPayload(ctx, wrappedPayload, [][32]byte{})
latestValidHash, err := srv.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{})
require.NoError(t, err)
require.DeepEqual(t, bytesutil.ToBytes32(want.LatestValidHash), bytesutil.ToBytes32(latestValidHash))
})
Expand All @@ -129,7 +129,7 @@ func TestClient_IPC(t *testing.T) {
require.Equal(t, true, ok)
wrappedPayload, err := blocks.WrappedExecutionPayloadCapella(req, 0)
require.NoError(t, err)
latestValidHash, err := srv.NewPayload(ctx, wrappedPayload, [][32]byte{})
latestValidHash, err := srv.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{})
require.NoError(t, err)
require.DeepEqual(t, bytesutil.ToBytes32(want.LatestValidHash), bytesutil.ToBytes32(latestValidHash))
})
Expand Down Expand Up @@ -467,7 +467,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayload(execPayload)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{})
require.NoError(t, err)
require.DeepEqual(t, want.LatestValidHash, resp)
})
Expand All @@ -481,7 +481,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayloadCapella(execPayload, 0)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{})
require.NoError(t, err)
require.DeepEqual(t, want.LatestValidHash, resp)
})
Expand All @@ -495,7 +495,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload, 0)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{'a'})
require.NoError(t, err)
require.DeepEqual(t, want.LatestValidHash, resp)
})
Expand All @@ -509,7 +509,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayload(execPayload)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{})
require.ErrorIs(t, ErrAcceptedSyncingPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
Expand All @@ -523,7 +523,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayloadCapella(execPayload, 0)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{})
require.ErrorIs(t, ErrAcceptedSyncingPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
Expand All @@ -537,7 +537,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload, 0)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{'a'})
require.ErrorIs(t, ErrAcceptedSyncingPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
Expand All @@ -551,7 +551,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayload(execPayload)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{})
require.ErrorIs(t, ErrInvalidBlockHashPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
Expand All @@ -565,7 +565,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayloadCapella(execPayload, 0)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{})
require.ErrorIs(t, ErrInvalidBlockHashPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
Expand All @@ -579,7 +579,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload, 0)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{'a'})
require.ErrorIs(t, ErrInvalidBlockHashPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
Expand All @@ -593,7 +593,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayload(execPayload)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{})
require.ErrorIs(t, ErrInvalidPayloadStatus, err)
require.DeepEqual(t, want.LatestValidHash, resp)
})
Expand All @@ -607,7 +607,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayloadCapella(execPayload, 0)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{})
require.ErrorIs(t, ErrInvalidPayloadStatus, err)
require.DeepEqual(t, want.LatestValidHash, resp)
})
Expand All @@ -621,7 +621,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayloadDeneb(execPayload, 0)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{'a'})
require.ErrorIs(t, ErrInvalidPayloadStatus, err)
require.DeepEqual(t, want.LatestValidHash, resp)
})
Expand All @@ -635,7 +635,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
wrappedPayload, err := blocks.WrappedExecutionPayload(execPayload)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{})
resp, err := client.NewPayload(ctx, wrappedPayload, [][32]byte{}, [32]byte{})
require.ErrorIs(t, ErrUnknownPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
Expand Down
2 changes: 1 addition & 1 deletion beacon-chain/execution/testing/mock_engine_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type EngineClient struct {
}

// NewPayload --
func (e *EngineClient) NewPayload(_ context.Context, _ interfaces.ExecutionData, _ [][32]byte) ([]byte, error) {
func (e *EngineClient) NewPayload(_ context.Context, _ interfaces.ExecutionData, _ [][32]byte, _ [32]byte) ([]byte, error) {
return e.NewPayloadResp, e.ErrNewPayload
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,22 @@ func (vs *Server) getLocalPayloadAndBlobs(ctx context.Context, blk interfaces.Re
}
var attr payloadattribute.Attributer
switch st.Version() {
case version.Capella, version.Deneb:
case version.Deneb:
withdrawals, err := st.ExpectedWithdrawals()
if err != nil {
return nil, nil, err
}
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV3{
Timestamp: uint64(t.Unix()),
PrevRandao: random,
SuggestedFeeRecipient: feeRecipient.Bytes(),
Withdrawals: withdrawals,
ParentBeaconBlockRoot: headRoot[:],
})
if err != nil {
return nil, nil, err
}
case version.Capella:
withdrawals, err := st.ExpectedWithdrawals()
if err != nil {
return nil, nil, err
Expand Down
17 changes: 17 additions & 0 deletions consensus-types/payload-attribute/getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,20 @@ func (a *data) PbV2() (*enginev1.PayloadAttributesV2, error) {
Withdrawals: a.withdrawals,
}, nil
}

// PbV3 returns the payload attribute in version 3.
func (a *data) PbV3() (*enginev1.PayloadAttributesV3, error) {
if a == nil {
return nil, errNilPayloadAttribute
}
if a.version != version.Deneb {
return nil, consensus_types.ErrNotSupported("PayloadAttributePbV3", a.version)
}
return &enginev1.PayloadAttributesV3{
Timestamp: a.timeStamp,
PrevRandao: a.prevRandao,
SuggestedFeeRecipient: a.suggestedFeeRecipient,
Withdrawals: a.withdrawals,
ParentBeaconBlockRoot: a.parentBeaconBlockRoot,
}, nil
}
Loading

0 comments on commit e9c0e11

Please sign in to comment.