diff --git a/cmd/rpcdaemon/commands/engine_api.go b/cmd/rpcdaemon/commands/engine_api.go index f27e605dd8a..549d8befe15 100644 --- a/cmd/rpcdaemon/commands/engine_api.go +++ b/cmd/rpcdaemon/commands/engine_api.go @@ -1,23 +1,28 @@ package commands import ( + "bytes" "context" "encoding/binary" "fmt" "math/big" "github.com/holiman/uint256" + "github.com/ledgerwatch/log/v3" + "github.com/ledgerwatch/erigon-lib/gointerfaces" "github.com/ledgerwatch/erigon-lib/gointerfaces/remote" types2 "github.com/ledgerwatch/erigon-lib/gointerfaces/types" "github.com/ledgerwatch/erigon-lib/kv" "github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/common/hexutil" + cmath "github.com/ledgerwatch/erigon/common/math" + "github.com/ledgerwatch/erigon/common/u256" "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/ethdb/privateapi" + "github.com/ledgerwatch/erigon/rlp" "github.com/ledgerwatch/erigon/turbo/rpchelper" - "github.com/ledgerwatch/log/v3" ) // ExecutionPayloadV1 represents an execution payload (aka block) without withdrawals @@ -57,6 +62,12 @@ type ExecutionPayloadV2 struct { Withdrawals []*types.Withdrawal `json:"withdrawals" gencodec:"required"` } +// GetPayloadV2Response represents the response of the getPayloadV2 method +type GetPayloadV2Response struct { + ExecutionPayload ExecutionPayloadV2 `json:"executionPayload" gencodec:"required"` + BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"` +} + // PayloadAttributes represent the attributes required to start assembling a payload type ForkChoiceState struct { HeadHash common.Hash `json:"headBlockHash" gencodec:"required"` @@ -93,7 +104,7 @@ type EngineAPI interface { ForkchoiceUpdatedV1(ctx context.Context, forkChoiceState *ForkChoiceState, payloadAttributes *PayloadAttributesV1) (map[string]interface{}, error) ForkchoiceUpdatedV2(ctx context.Context, forkChoiceState *ForkChoiceState, payloadAttributes *PayloadAttributesV2) (map[string]interface{}, error) GetPayloadV1(ctx context.Context, payloadID hexutil.Bytes) (*ExecutionPayloadV1, error) - GetPayloadV2(ctx context.Context, payloadID hexutil.Bytes) (*ExecutionPayloadV2, error) + GetPayloadV2(ctx context.Context, payloadID hexutil.Bytes) (*GetPayloadV2Response, error) ExchangeTransitionConfigurationV1(ctx context.Context, transitionConfiguration *TransitionConfiguration) (*TransitionConfiguration, error) } @@ -375,7 +386,35 @@ func (e *EngineImpl) GetPayloadV1(ctx context.Context, payloadID hexutil.Bytes) }, nil } -func (e *EngineImpl) GetPayloadV2(ctx context.Context, payloadID hexutil.Bytes) (*ExecutionPayloadV2, error) { +func getTxValueForBlockValue(transaction []byte, baseFee *big.Int) (*big.Int, error) { + // calculate blockValue by summing tips - see: https://github.com/ethereum/execution-apis/pull/314 + s := rlp.NewStream(bytes.NewReader(transaction), uint64(len(transaction))) + t, err := types.DecodeTransaction(s) + if err != nil { + log.Error("Failed to decode transaction", "err", err) + return nil, err + } + + // convert baseFee to uint256 + baseFeeUint256, overflow := uint256.FromBig(baseFee) + if overflow { + log.Warn("baseFee overflow") + return nil, fmt.Errorf("baseFee overflow") + } + + effectiveTip := uint256.NewInt(t.GetGas()) + if t.GetFeeCap().Gt(baseFeeUint256) { + effectiveTip = cmath.Min256(t.GetTip(), new(uint256.Int).Sub(t.GetFeeCap(), baseFeeUint256)) + } else { + effectiveTip = u256.Num0 + } + amount := new(uint256.Int).SetUint64(t.GetGas()) + amount.Mul(amount, effectiveTip) // gasUsed * effectiveTip = how much goes to the block producer (miner, validator) + + return amount.ToBig(), nil +} + +func (e *EngineImpl) GetPayloadV2(ctx context.Context, payloadID hexutil.Bytes) (*GetPayloadV2Response, error) { if e.internalCL { log.Error("EXTERNAL CONSENSUS LAYER IS NOT ENABLED, PLEASE RESTART WITH FLAG --externalcl") return nil, fmt.Errorf("engine api should not be used, restart with --externalcl") @@ -397,12 +436,18 @@ func (e *EngineImpl) GetPayloadV2(ctx context.Context, payloadID hexutil.Bytes) baseFee = gointerfaces.ConvertH256ToUint256Int(payload.BaseFeePerGas).ToBig() } - // Convert slice of hexutil.Bytes to a slice of slice of bytes + blockValue := big.NewInt(0) transactions := make([]hexutil.Bytes, len(payload.Transactions)) for i, transaction := range payload.Transactions { transactions[i] = transaction + txVal, err := getTxValueForBlockValue(transaction, baseFee) + if err != nil { + return nil, err + } + blockValue.Add(blockValue, txVal) } - return &ExecutionPayloadV2{ + + epl := ExecutionPayloadV2{ ParentHash: gointerfaces.ConvertH256ToHash(payload.ParentHash), FeeRecipient: gointerfaces.ConvertH160toAddress(payload.Coinbase), StateRoot: gointerfaces.ConvertH256ToHash(payload.StateRoot), @@ -418,6 +463,10 @@ func (e *EngineImpl) GetPayloadV2(ctx context.Context, payloadID hexutil.Bytes) BlockHash: gointerfaces.ConvertH256ToHash(payload.BlockHash), Transactions: transactions, Withdrawals: privateapi.ConvertWithdrawalsFromRpc(ep.Withdrawals), + } + return &GetPayloadV2Response{ + epl, + (*hexutil.Big)(blockValue), }, nil } diff --git a/core/types/block_test.go b/core/types/block_test.go index bb643d1f66f..d976be00c54 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -18,6 +18,7 @@ package types import ( "bytes" + "encoding/hex" "encoding/json" "math/big" "reflect" @@ -438,3 +439,48 @@ func TestWithdrawalsEncoding(t *testing.T) { assert.Equal(t, block2, &decoded2) } + +func TestBlockRawBodyPreShanghai(t *testing.T) { + require := require.New(t) + + const rawBodyForStorageRlp = "f901f4c0f901f0f901eda00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000" + bstring, _ := hex.DecodeString(rawBodyForStorageRlp) + + body := new(RawBody) + rlp.DecodeBytes(bstring, body) + + require.Nil(body.Withdrawals) + require.Equal(1, len(body.Uncles)) + require.Equal(0, len(body.Transactions)) +} + +func TestBlockRawBodyPostShanghaiNoWithdrawals(t *testing.T) { + require := require.New(t) + + const rawBodyForStorageRlp = "f901f5c0f901f0f901eda00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0" + bstring, _ := hex.DecodeString(rawBodyForStorageRlp) + + body := new(RawBody) + rlp.DecodeBytes(bstring, body) + + require.NotNil(body.Withdrawals) + require.Equal(0, len(body.Withdrawals)) + require.Equal(1, len(body.Uncles)) + require.Equal(0, len(body.Transactions)) +} + +func TestBlockRawBodyPostShanghaiWithdrawals(t *testing.T) { + require := require.New(t) + + const rawBodyForStorageRlp = "f90230c0f901f0f901eda00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f83adc0f82157c94ff000000000000000000000000000000000000008203e8dc1082157d94ff000000000000000000000000000000000000008203e9" + + bstring, _ := hex.DecodeString(rawBodyForStorageRlp) + + body := new(RawBody) + rlp.DecodeBytes(bstring, body) + + require.NotNil(body.Withdrawals) + require.Equal(1, len(body.Uncles)) + require.Equal(0, len(body.Transactions)) + require.Equal(2, len(body.Withdrawals)) +}