diff --git a/pkg/eth/api.go b/pkg/eth/api.go index 868fc398..d21e364a 100644 --- a/pkg/eth/api.go +++ b/pkg/eth/api.go @@ -247,8 +247,7 @@ Uncles */ -// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true -// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. +// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. func (pea *PublicEthAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error) { block, err := pea.B.BlockByNumber(ctx, blockNr) if block != nil && err == nil { @@ -404,7 +403,7 @@ func (pea *PublicEthAPI) GetBlockTransactionCountByHash(ctx context.Context, blo // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. func (pea *PublicEthAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction { if block, _ := pea.B.BlockByNumber(ctx, blockNr); block != nil { - return newRPCTransactionFromBlockIndex(block, uint64(index)) + return newRPCTransactionFromBlockIndex(block, uint64(index), pea.B.ChainConfig()) } if pea.config.ProxyOnError { @@ -421,7 +420,7 @@ func (pea *PublicEthAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. func (pea *PublicEthAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction { if block, _ := pea.B.BlockByHash(ctx, blockHash); block != nil { - return newRPCTransactionFromBlockIndex(block, uint64(index)) + return newRPCTransactionFromBlockIndex(block, uint64(index), pea.B.ChainConfig()) } if pea.config.ProxyOnError { @@ -475,7 +474,7 @@ func (pea *PublicEthAPI) GetTransactionByHash(ctx context.Context, hash common.H return nil, err } - return NewRPCTransaction(tx, blockHash, blockNumber, index, header.BaseFee), nil + return NewRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, pea.B.ChainConfig()), nil } if pea.config.ProxyOnError { var tx *RPCTransaction @@ -1242,11 +1241,7 @@ func (pea *PublicEthAPI) writeStateDiffFor(blockHash common.Hash) { // rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field func (pea *PublicEthAPI) rpcMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { - fields, err := RPCMarshalBlock(b, inclTx, fullTx) - if err != nil { - log.Errorf("error RPC marshalling block with hash %s: %s", b.Hash().String(), err) - return nil, err - } + fields := RPCMarshalBlock(b, inclTx, fullTx, pea.B.ChainConfig()) if inclTx { td, err := pea.B.GetTd(b.Hash()) if err != nil { @@ -1255,12 +1250,12 @@ func (pea *PublicEthAPI) rpcMarshalBlock(b *types.Block, inclTx bool, fullTx boo } fields["totalDifficulty"] = (*hexutil.Big)(td) } - return fields, err + return fields, nil } // rpcMarshalBlockWithUncleHashes uses the generalized output filler, then adds the total difficulty field func (pea *PublicEthAPI) rpcMarshalBlockWithUncleHashes(b *types.Block, uncleHashes []common.Hash, inclTx bool, fullTx bool) (map[string]interface{}, error) { - fields, err := RPCMarshalBlockWithUncleHashes(b, uncleHashes, inclTx, fullTx) + fields, err := RPCMarshalBlockWithUncleHashes(b, uncleHashes, inclTx, fullTx, pea.B.ChainConfig()) if err != nil { return nil, err } diff --git a/pkg/eth/backend_utils.go b/pkg/eth/backend_utils.go index 95ac499b..f467c27e 100644 --- a/pkg/eth/backend_utils.go +++ b/pkg/eth/backend_utils.go @@ -28,9 +28,9 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" @@ -84,26 +84,23 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} { // RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are // returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain // transaction hashes. -func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { +func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *params.ChainConfig) map[string]interface{} { fields := RPCMarshalHeader(block.Header()) fields["size"] = hexutil.Uint64(block.Size()) if inclTx { - formatTx := func(tx *types.Transaction) (interface{}, error) { - return tx.Hash(), nil + formatTx := func(idx int, tx *types.Transaction) interface{} { + return tx.Hash() } if fullTx { - formatTx = func(tx *types.Transaction) (interface{}, error) { - return NewRPCTransactionFromBlockHash(block, tx.Hash()), nil + formatTx = func(idx int, tx *types.Transaction) interface{} { + return newRPCTransactionFromBlockIndex(block, uint64(idx), config) } } txs := block.Transactions() transactions := make([]interface{}, len(txs)) - var err error for i, tx := range txs { - if transactions[i], err = formatTx(tx); err != nil { - return nil, err - } + transactions[i] = formatTx(i, tx) } fields["transactions"] = transactions } @@ -113,12 +110,14 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool) (map[string]i uncleHashes[i] = uncle.Hash() } fields["uncles"] = uncleHashes - - return fields, nil + if block.Header().WithdrawalsHash != nil { + fields["withdrawals"] = block.Withdrawals() + } + return fields } // RPCMarshalBlockWithUncleHashes marshals the block with the provided uncle hashes -func RPCMarshalBlockWithUncleHashes(block *types.Block, uncleHashes []common.Hash, inclTx bool, fullTx bool) (map[string]interface{}, error) { +func RPCMarshalBlockWithUncleHashes(block *types.Block, uncleHashes []common.Hash, inclTx bool, fullTx bool, config *params.ChainConfig) (map[string]interface{}, error) { fields := RPCMarshalHeader(block.Header()) fields["size"] = hexutil.Uint64(block.Size()) @@ -128,7 +127,7 @@ func RPCMarshalBlockWithUncleHashes(block *types.Block, uncleHashes []common.Has } if fullTx { formatTx = func(tx *types.Transaction) (interface{}, error) { - return NewRPCTransactionFromBlockHash(block, tx.Hash()), nil + return NewRPCTransactionFromBlockHash(block, tx.Hash(), config), nil } } txs := block.Transactions() @@ -146,11 +145,11 @@ func RPCMarshalBlockWithUncleHashes(block *types.Block, uncleHashes []common.Has return fields, nil } -// NewRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation. -func NewRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction { +// newRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation. +func NewRPCTransactionFromBlockHash(b *types.Block, hash common.Hash, config *params.ChainConfig) *RPCTransaction { for idx, tx := range b.Transactions() { if tx.Hash() == hash { - return newRPCTransactionFromBlockIndex(b, uint64(idx)) + return newRPCTransactionFromBlockIndex(b, uint64(idx), config) } } return nil @@ -169,11 +168,12 @@ func SignerForTx(tx *types.Transaction) types.Signer { // NewRPCTransaction returns a transaction that will serialize to the RPC // representation, with the given location metadata set (if available). -func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, baseFee *big.Int) *RPCTransaction { - signer := SignerForTx(tx) +func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, blockTime uint64, index uint64, baseFee *big.Int, config *params.ChainConfig) *RPCTransaction { + signer := types.MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime) from, _ := types.Sender(signer, tx) v, r, s := tx.RawSignatureValues() result := &RPCTransaction{ + Type: hexutil.Uint64(tx.Type()), From: from, Gas: hexutil.Uint64(tx.Gas()), GasPrice: (*hexutil.Big)(tx.GasPrice()), @@ -182,7 +182,6 @@ func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber Nonce: hexutil.Uint64(tx.Nonce()), To: tx.To(), Value: (*hexutil.Big)(tx.Value()), - Type: hexutil.Uint64(tx.Type()), V: (*hexutil.Big)(v), R: (*hexutil.Big)(r), S: (*hexutil.Big)(s), @@ -192,34 +191,69 @@ func NewRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) result.TransactionIndex = (*hexutil.Uint64)(&index) } + switch tx.Type() { case types.LegacyTxType: // if a legacy transaction has an EIP-155 chain id, include it explicitly if id := tx.ChainId(); id.Sign() != 0 { result.ChainID = (*hexutil.Big)(id) } + case types.AccessListTxType: al := tx.AccessList() + yparity := hexutil.Uint64(v.Sign()) result.Accesses = &al result.ChainID = (*hexutil.Big)(tx.ChainId()) + result.YParity = &yparity + case types.DynamicFeeTxType: al := tx.AccessList() + yparity := hexutil.Uint64(v.Sign()) result.Accesses = &al result.ChainID = (*hexutil.Big)(tx.ChainId()) + result.YParity = &yparity result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) // if the transaction has been mined, compute the effective gas price if baseFee != nil && blockHash != (common.Hash{}) { - // price = min(tip, gasFeeCap - baseFee) + baseFee - price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap()) - result.GasPrice = (*hexutil.Big)(price) + // price = min(gasTipCap + baseFee, gasFeeCap) + result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee)) } else { result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) } + + case types.BlobTxType: + al := tx.AccessList() + yparity := hexutil.Uint64(v.Sign()) + result.Accesses = &al + result.ChainID = (*hexutil.Big)(tx.ChainId()) + result.YParity = &yparity + result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) + result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) + // if the transaction has been mined, compute the effective gas price + if baseFee != nil && blockHash != (common.Hash{}) { + result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee)) + } else { + result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) + } + result.MaxFeePerBlobGas = (*hexutil.Big)(tx.BlobGasFeeCap()) + result.BlobVersionedHashes = tx.BlobHashes() } return result } +// effectiveGasPrice computes the transaction gas fee, based on the given basefee value. +// +// price = min(gasTipCap + baseFee, gasFeeCap) +func effectiveGasPrice(tx *types.Transaction, baseFee *big.Int) *big.Int { + fee := tx.GasTipCap() + fee = fee.Add(fee, baseFee) + if tx.GasFeeCapIntCmp(fee) < 0 { + return tx.GasFeeCap() + } + return fee +} + type rpcBlock struct { Hash common.Hash `json:"hash"` Transactions []rpcTransaction `json:"transactions"` @@ -281,6 +315,15 @@ func getBlockAndUncleHashes(cli *rpc.Client, ctx context.Context, method string, return types.NewBlockWithHeader(head).WithBody(txs, nil), body.UncleHashes, nil } +// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation. +func newRPCTransactionFromBlockIndex(b *types.Block, index uint64, config *params.ChainConfig) *RPCTransaction { + txs := b.Transactions() + if index >= uint64(len(txs)) { + return nil + } + return NewRPCTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time(), index, b.BaseFee(), config) +} + // newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. func newRPCRawTransactionFromBlockIndex(b *types.Block, index uint64) hexutil.Bytes { txs := b.Transactions() @@ -291,15 +334,6 @@ func newRPCRawTransactionFromBlockIndex(b *types.Block, index uint64) hexutil.By return blob } -// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation. -func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction { - txs := b.Transactions() - if index >= uint64(len(txs)) { - return nil - } - return NewRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee()) -} - func toFilterArg(q ethereum.FilterQuery) (interface{}, error) { arg := map[string]interface{}{ "address": q.Addresses, diff --git a/pkg/eth/types.go b/pkg/eth/types.go index 71c11f19..65b02b8c 100644 --- a/pkg/eth/types.go +++ b/pkg/eth/types.go @@ -33,26 +33,30 @@ import ( ) // RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction +// Note: copied from go-ethereum/internal/ethapi type RPCTransaction struct { - BlockHash *common.Hash `json:"blockHash"` - BlockNumber *hexutil.Big `json:"blockNumber"` - From common.Address `json:"from"` - Gas hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` - GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` - Hash common.Hash `json:"hash"` - Input hexutil.Bytes `json:"input"` - Nonce hexutil.Uint64 `json:"nonce"` - To *common.Address `json:"to"` - TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` - Value *hexutil.Big `json:"value"` - Type hexutil.Uint64 `json:"type"` - Accesses *types.AccessList `json:"accessList,omitempty"` - ChainID *hexutil.Big `json:"chainId,omitempty"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` + BlockHash *common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From common.Address `json:"from"` + Gas hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` + GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` + MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"` + Hash common.Hash `json:"hash"` + Input hexutil.Bytes `json:"input"` + Nonce hexutil.Uint64 `json:"nonce"` + To *common.Address `json:"to"` + TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` + Value *hexutil.Big `json:"value"` + Type hexutil.Uint64 `json:"type"` + Accesses *types.AccessList `json:"accessList,omitempty"` + ChainID *hexutil.Big `json:"chainId,omitempty"` + BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` + YParity *hexutil.Uint64 `json:"yParity,omitempty"` } // RPCReceipt represents a receipt that will serialize to the RPC representation of a receipt