diff --git a/core/state_transition.go b/core/state_transition.go index 61d006c844a3..603f9ebb1059 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -50,17 +50,18 @@ The state transitioning model does all the necessary work to work out a valid ne 6) Derive new state root */ type StateTransition struct { - gp *GasPool - msg Message - gas uint64 - gasPrice *big.Int - gasFeeCap *big.Int - gasTipCap *big.Int - initialGas uint64 - value *big.Int - data []byte - state vm.StateDB - evm *vm.EVM + gp *GasPool + msg Message + gas uint64 + gasPrice *big.Int + gasFeeCap *big.Int + gasTipCap *big.Int + maxFeePerDataGas *big.Int + initialGas uint64 + value *big.Int + data []byte + state vm.StateDB + evm *vm.EVM } // Message represents a message sent to a contract. diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go index f73cd49a99f3..48902e8703c1 100644 --- a/core/types/access_list_tx.go +++ b/core/types/access_list_tx.go @@ -94,18 +94,19 @@ func (tx *AccessListTx) copy() TxData { } // accessors for innerTx. -func (tx *AccessListTx) txType() byte { return AccessListTxType } -func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID } -func (tx *AccessListTx) accessList() AccessList { return tx.AccessList } -func (tx *AccessListTx) dataHashes() []common.Hash { return nil } -func (tx *AccessListTx) data() []byte { return tx.Data } -func (tx *AccessListTx) gas() uint64 { return tx.Gas } -func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice } -func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice } -func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice } -func (tx *AccessListTx) value() *big.Int { return tx.Value } -func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } -func (tx *AccessListTx) to() *common.Address { return tx.To } +func (tx *AccessListTx) txType() byte { return AccessListTxType } +func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID } +func (tx *AccessListTx) accessList() AccessList { return tx.AccessList } +func (tx *AccessListTx) dataHashes() []common.Hash { return nil } +func (tx *AccessListTx) data() []byte { return tx.Data } +func (tx *AccessListTx) gas() uint64 { return tx.Gas } +func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice } +func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice } +func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice } +func (tx *AccessListTx) maxFeePerDataGas() *big.Int { return nil } +func (tx *AccessListTx) value() *big.Int { return tx.Value } +func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } +func (tx *AccessListTx) to() *common.Address { return tx.To } func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S diff --git a/core/types/data_blob_tx.go b/core/types/data_blob_tx.go index fb4d4277b958..235ce3078e8c 100644 --- a/core/types/data_blob_tx.go +++ b/core/types/data_blob_tx.go @@ -316,29 +316,30 @@ func (alv AccessListView) HashTreeRoot(hFn tree.HashFn) tree.Root { } type BlobTxMessage struct { - ChainID Uint256View - Nonce Uint64View - GasTipCap Uint256View // a.k.a. maxPriorityFeePerGas - GasFeeCap Uint256View // a.k.a. maxFeePerGas - Gas Uint64View - To AddressOptionalSSZ // nil means contract creation - Value Uint256View - Data TxDataView - AccessList AccessListView + ChainID Uint256View + Nonce Uint64View + GasTipCap Uint256View // a.k.a. maxPriorityFeePerGas + GasFeeCap Uint256View // a.k.a. maxFeePerGas + Gas Uint64View + To AddressOptionalSSZ // nil means contract creation + Value Uint256View + Data TxDataView + AccessList AccessListView + MaxFeePerDataGas Uint256View BlobVersionedHashes VersionedHashesView } func (tx *BlobTxMessage) Deserialize(dr *codec.DecodingReader) error { - return dr.Container(&tx.ChainID, &tx.Nonce, &tx.GasTipCap, &tx.GasFeeCap, &tx.Gas, &tx.To, &tx.Value, &tx.Data, &tx.AccessList, &tx.BlobVersionedHashes) + return dr.Container(&tx.ChainID, &tx.Nonce, &tx.GasTipCap, &tx.GasFeeCap, &tx.Gas, &tx.To, &tx.Value, &tx.Data, &tx.AccessList, &tx.MaxFeePerDataGas, &tx.BlobVersionedHashes) } func (tx *BlobTxMessage) Serialize(w *codec.EncodingWriter) error { - return w.Container(&tx.ChainID, &tx.Nonce, &tx.GasTipCap, &tx.GasFeeCap, &tx.Gas, &tx.To, &tx.Value, &tx.Data, &tx.AccessList, &tx.BlobVersionedHashes) + return w.Container(&tx.ChainID, &tx.Nonce, &tx.GasTipCap, &tx.GasFeeCap, &tx.Gas, &tx.To, &tx.Value, &tx.Data, &tx.AccessList, &tx.MaxFeePerDataGas, &tx.BlobVersionedHashes) } func (tx *BlobTxMessage) ByteLength() uint64 { - return codec.ContainerLength(&tx.ChainID, &tx.Nonce, &tx.GasTipCap, &tx.GasFeeCap, &tx.Gas, &tx.To, &tx.Value, &tx.Data, &tx.AccessList, &tx.BlobVersionedHashes) + return codec.ContainerLength(&tx.ChainID, &tx.Nonce, &tx.GasTipCap, &tx.GasFeeCap, &tx.Gas, &tx.To, &tx.Value, &tx.Data, &tx.AccessList, &tx.MaxFeePerDataGas, &tx.BlobVersionedHashes) } func (tx *BlobTxMessage) FixedLength() uint64 { @@ -346,7 +347,7 @@ func (tx *BlobTxMessage) FixedLength() uint64 { } func (tx *BlobTxMessage) HashTreeRoot(hFn tree.HashFn) tree.Root { - return hFn.HashTreeRoot(&tx.ChainID, &tx.Nonce, &tx.GasTipCap, &tx.GasFeeCap, &tx.Gas, &tx.To, &tx.Value, &tx.Data, &tx.AccessList, &tx.BlobVersionedHashes) + return hFn.HashTreeRoot(&tx.ChainID, &tx.Nonce, &tx.GasTipCap, &tx.GasFeeCap, &tx.Gas, &tx.To, &tx.Value, &tx.Data, &tx.AccessList, &tx.MaxFeePerDataGas, &tx.BlobVersionedHashes) } // copy creates a deep copy of the transaction data and initializes all fields. @@ -361,6 +362,7 @@ func (tx *BlobTxMessage) copy() *BlobTxMessage { Value: tx.Value, Data: common.CopyBytes(tx.Data), AccessList: make([]AccessTuple, len(tx.AccessList)), + MaxFeePerDataGas: tx.MaxFeePerDataGas, BlobVersionedHashes: make([]common.Hash, len(tx.BlobVersionedHashes)), } copy(cpy.AccessList, tx.AccessList) @@ -416,18 +418,19 @@ func u256ToBig(v *Uint256View) *big.Int { } // accessors for innerTx. -func (stx *SignedBlobTx) txType() byte { return BlobTxType } -func (stx *SignedBlobTx) chainID() *big.Int { return u256ToBig(&stx.Message.ChainID) } -func (stx *SignedBlobTx) accessList() AccessList { return AccessList(stx.Message.AccessList) } -func (stx *SignedBlobTx) dataHashes() []common.Hash { return stx.Message.BlobVersionedHashes } -func (stx *SignedBlobTx) data() []byte { return stx.Message.Data } -func (stx *SignedBlobTx) gas() uint64 { return uint64(stx.Message.Gas) } -func (stx *SignedBlobTx) gasFeeCap() *big.Int { return u256ToBig(&stx.Message.GasFeeCap) } -func (stx *SignedBlobTx) gasTipCap() *big.Int { return u256ToBig(&stx.Message.GasTipCap) } -func (stx *SignedBlobTx) gasPrice() *big.Int { return u256ToBig(&stx.Message.GasFeeCap) } -func (stx *SignedBlobTx) value() *big.Int { return u256ToBig(&stx.Message.Value) } -func (stx *SignedBlobTx) nonce() uint64 { return uint64(stx.Message.Nonce) } -func (stx *SignedBlobTx) to() *common.Address { return (*common.Address)(stx.Message.To.Address) } +func (stx *SignedBlobTx) txType() byte { return BlobTxType } +func (stx *SignedBlobTx) chainID() *big.Int { return u256ToBig(&stx.Message.ChainID) } +func (stx *SignedBlobTx) accessList() AccessList { return AccessList(stx.Message.AccessList) } +func (stx *SignedBlobTx) dataHashes() []common.Hash { return stx.Message.BlobVersionedHashes } +func (stx *SignedBlobTx) data() []byte { return stx.Message.Data } +func (stx *SignedBlobTx) gas() uint64 { return uint64(stx.Message.Gas) } +func (stx *SignedBlobTx) gasFeeCap() *big.Int { return u256ToBig(&stx.Message.GasFeeCap) } +func (stx *SignedBlobTx) gasTipCap() *big.Int { return u256ToBig(&stx.Message.GasTipCap) } +func (stx *SignedBlobTx) maxFeePerDataGas() *big.Int { return u256ToBig(&stx.Message.MaxFeePerDataGas) } +func (stx *SignedBlobTx) gasPrice() *big.Int { return u256ToBig(&stx.Message.GasFeeCap) } +func (stx *SignedBlobTx) value() *big.Int { return u256ToBig(&stx.Message.Value) } +func (stx *SignedBlobTx) nonce() uint64 { return uint64(stx.Message.Nonce) } +func (stx *SignedBlobTx) to() *common.Address { return (*common.Address)(stx.Message.To.Address) } func (stx *SignedBlobTx) rawSignatureValues() (v, r, s *big.Int) { return big.NewInt(int64(stx.Signature.V)), u256ToBig(&stx.Signature.R), u256ToBig(&stx.Signature.S) diff --git a/core/types/dynamic_fee_tx.go b/core/types/dynamic_fee_tx.go index 9dfec62420f7..69b2d4e6951d 100644 --- a/core/types/dynamic_fee_tx.go +++ b/core/types/dynamic_fee_tx.go @@ -82,18 +82,19 @@ func (tx *DynamicFeeTx) copy() TxData { } // accessors for innerTx. -func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType } -func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID } -func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList } -func (tx *DynamicFeeTx) dataHashes() []common.Hash { return nil } -func (tx *DynamicFeeTx) data() []byte { return tx.Data } -func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas } -func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap } -func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap } -func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap } -func (tx *DynamicFeeTx) value() *big.Int { return tx.Value } -func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce } -func (tx *DynamicFeeTx) to() *common.Address { return tx.To } +func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType } +func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID } +func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList } +func (tx *DynamicFeeTx) dataHashes() []common.Hash { return nil } +func (tx *DynamicFeeTx) data() []byte { return tx.Data } +func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas } +func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap } +func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap } +func (tx *DynamicFeeTx) maxFeePerDataGas() *big.Int { return nil } +func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap } +func (tx *DynamicFeeTx) value() *big.Int { return tx.Value } +func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce } +func (tx *DynamicFeeTx) to() *common.Address { return tx.To } func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go index c0f3fb797e8c..eae210f6342b 100644 --- a/core/types/legacy_tx.go +++ b/core/types/legacy_tx.go @@ -91,18 +91,19 @@ func (tx *LegacyTx) copy() TxData { } // accessors for innerTx. -func (tx *LegacyTx) txType() byte { return LegacyTxType } -func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) } -func (tx *LegacyTx) accessList() AccessList { return nil } -func (tx *LegacyTx) dataHashes() []common.Hash { return nil } -func (tx *LegacyTx) data() []byte { return tx.Data } -func (tx *LegacyTx) gas() uint64 { return tx.Gas } -func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice } -func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice } -func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice } -func (tx *LegacyTx) value() *big.Int { return tx.Value } -func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } -func (tx *LegacyTx) to() *common.Address { return tx.To } +func (tx *LegacyTx) txType() byte { return LegacyTxType } +func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) } +func (tx *LegacyTx) accessList() AccessList { return nil } +func (tx *LegacyTx) dataHashes() []common.Hash { return nil } +func (tx *LegacyTx) data() []byte { return tx.Data } +func (tx *LegacyTx) gas() uint64 { return tx.Gas } +func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice } +func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice } +func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice } +func (tx *LegacyTx) maxFeePerDataGas() *big.Int { return nil } +func (tx *LegacyTx) value() *big.Int { return tx.Value } +func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } +func (tx *LegacyTx) to() *common.Address { return tx.To } func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) { return tx.V, tx.R, tx.S diff --git a/core/types/transaction.go b/core/types/transaction.go index 09fcba84b3c5..4abd45d924b2 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -102,7 +102,7 @@ type TxWrapData interface { // TxData is the underlying data of a transaction. // -// This is implemented by DynamicFeeTx, LegacyTx and AccessListTx. +// This is implemented by DynamicFeeTx, LegacyTx, AccessListTx & SignedBlobTx. type TxData interface { txType() byte // returns the type ID copy() TxData // creates a deep copy and initializes all fields @@ -115,6 +115,7 @@ type TxData interface { gasPrice() *big.Int gasTipCap() *big.Int gasFeeCap() *big.Int + maxFeePerDataGas() *big.Int value() *big.Int nonce() uint64 to() *common.Address @@ -385,6 +386,11 @@ func (tx *Transaction) GasTipCap() *big.Int { return new(big.Int).Set(tx.inner.g // GasFeeCap returns the fee cap per gas of the transaction. func (tx *Transaction) GasFeeCap() *big.Int { return new(big.Int).Set(tx.inner.gasFeeCap()) } +// MaxFeePerDataGas returns the max_fee_per_data_gas value for the transaction +func (tx *Transaction) MaxFeePerDataGas() *big.Int { + return new(big.Int).Set(tx.inner.maxFeePerDataGas()) +} + // Value returns the ether amount of the transaction. func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) } diff --git a/interfaces.go b/interfaces.go index e3c5ac3e7103..0926016e9214 100644 --- a/interfaces.go +++ b/interfaces.go @@ -132,17 +132,18 @@ type ChainSyncReader interface { // CallMsg contains parameters for contract calls. type CallMsg struct { - From common.Address // the sender of the 'transaction' - To *common.Address // the destination contract (nil for contract creation) - Gas uint64 // if 0, the call executes with near-infinite gas - GasPrice *big.Int // wei <-> gas exchange ratio - GasFeeCap *big.Int // EIP-1559 fee cap per gas. - GasTipCap *big.Int // EIP-1559 tip per gas. - Value *big.Int // amount of wei sent along with the call - Data []byte // input data, usually an ABI-encoded contract method invocation + From common.Address // the sender of the 'transaction' + To *common.Address // the destination contract (nil for contract creation) + Gas uint64 // if 0, the call executes with near-infinite gas + GasPrice *big.Int // wei <-> gas exchange ratio + GasFeeCap *big.Int // EIP-1559 fee cap per gas. + GasTipCap *big.Int // EIP-1559 tip per gas. + MaxFeePerDataGas *big.Int // EIP-4844 max_fee_per_data_gas + Value *big.Int // amount of wei sent along with the call + Data []byte // input data, usually an ABI-encoded contract method invocation AccessList types.AccessList // EIP-2930 access list. - DataHashes []common.Hash // versioned data hashes for mini-danksharding + DataHashes []common.Hash // versioned data hashes for EIP-4844 } // A ContractCaller provides contract calls, essentially transactions that are executed by diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index d37ca735ae2a..4e3472b2d94e 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -228,6 +228,7 @@ func (s *TxPoolAPI) Inspect() map[string]map[string]map[string]string { // Define a formatter to flatten a transaction into a string var format = func(tx *types.Transaction) string { + // TODO: handle data gas for txs with blobs (EIP-4844) if to := tx.To(); to != nil { return fmt.Sprintf("%s: %v wei + %v gas × %v wei", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice()) } @@ -1264,6 +1265,7 @@ type RPCTransaction struct { Value *hexutil.Big `json:"value"` Type hexutil.Uint64 `json:"type"` Accesses *types.AccessList `json:"accessList,omitempty"` + MaxFeePerDataGas *hexutil.Big `json:"maxFeePerDataGas,omitempty"` BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` ChainID *hexutil.Big `json:"chainId,omitempty"` V *hexutil.Big `json:"v"` @@ -1313,6 +1315,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber result.ChainID = (*hexutil.Big)(tx.ChainId()) result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) + result.MaxFeePerDataGas = (*hexutil.Big)(tx.MaxFeePerDataGas()) // if the transaction has been mined, compute the effective gas price if baseFee != nil && blockHash != (common.Hash{}) { // price = min(tip, gasFeeCap - baseFee) + baseFee