diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index c83eb18d05..2ed15e41bb 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -903,10 +903,11 @@ func (m callMsg) Gas() uint64 { return m.CallMsg.Gas } func (m callMsg) Value() *big.Int { return m.CallMsg.Value } func (m callMsg) Data() []byte { return m.CallMsg.Data } func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList } -func (m callMsg) IsSystemTx() bool { return false } -func (m callMsg) IsDepositTx() bool { return false } -func (m callMsg) Mint() *big.Int { return nil } -func (m callMsg) RollupDataGas() uint64 { return 0 } + +func (m callMsg) IsSystemTx() bool { return false } +func (m callMsg) IsDepositTx() bool { return false } +func (m callMsg) Mint() *big.Int { return nil } +func (m callMsg) RollupDataGas() types.RollupGasData { return types.RollupGasData{} } // filterBackend implements filters.Backend to support filtering for logs without // taking bloom-bits acceleration structures into account. diff --git a/core/blockchain.go b/core/blockchain.go index 99a110f4f9..d0afcfd3c1 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2035,7 +2035,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error) // the processing of a block. These logs are later announced as deleted or reborn. func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log { receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64()) - receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Transactions()) + receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Time(), b.Transactions()) var logs []*types.Log for _, receipt := range receipts { diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index e4dac3407f..2789ee390c 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -636,7 +636,12 @@ func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *para log.Error("Missing body but have receipt", "hash", hash, "number", number) return nil } - if err := receipts.DeriveFields(config, hash, number, body.Transactions); err != nil { + header := ReadHeader(db, hash, number) + if header == nil { + log.Error("Missing header but have receipt", "hash", hash, "number", number) + return nil + } + if err := receipts.DeriveFields(config, hash, number, header.Time, body.Transactions); err != nil { log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err) return nil } diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 84eae1d90d..fd701e27b5 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -347,6 +347,9 @@ func TestBlockReceiptStorage(t *testing.T) { tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil) tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil) + header := &types.Header{ + Number: big.NewInt(0), + } body := &types.Body{Transactions: types.Transactions{tx1, tx2}} // Create the two receipts to manage afterwards @@ -378,13 +381,16 @@ func TestBlockReceiptStorage(t *testing.T) { receipts := []*types.Receipt{receipt1, receipt2} // Check that no receipt entries are in a pristine database - hash := common.BytesToHash([]byte{0x03, 0x14}) + hash := header.Hash() if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 { t.Fatalf("non existent receipts returned: %v", rs) } // Insert the body that corresponds to the receipts WriteBody(db, hash, 0, body) + // Insert the header that corresponds to the receipts + WriteHeader(db, header) + // Insert the receipt slice into the database and check presence WriteReceipts(db, hash, 0, receipts) if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) == 0 { diff --git a/core/state_transition.go b/core/state_transition.go index 4f1853a094..b76840eb2a 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -79,10 +79,10 @@ type Message interface { Gas() uint64 Value() *big.Int - IsSystemTx() bool // IsSystemTx indicates the message, if also a deposit, does not emit gas usage. - IsDepositTx() bool // IsDepositTx indicates the message is force-included and can persist a mint. - Mint() *big.Int // Mint is the amount to mint before EVM processing, or nil if there is no minting. - RollupDataGas() uint64 // RollupDataGas indicates the rollup cost of the message, 0 if not a rollup or no cost. + IsSystemTx() bool // IsSystemTx indicates the message, if also a deposit, does not emit gas usage. + IsDepositTx() bool // IsDepositTx indicates the message is force-included and can persist a mint. + Mint() *big.Int // Mint is the amount to mint before EVM processing, or nil if there is no minting. + RollupDataGas() types.RollupGasData // RollupDataGas indicates the rollup cost of the message, 0 if not a rollup or no cost. Nonce() uint64 IsFake() bool @@ -224,7 +224,7 @@ func (st *StateTransition) buyGas() error { mgval = mgval.Mul(mgval, st.gasPrice) var l1Cost *big.Int if st.evm.Context.L1CostFunc != nil { - l1Cost = st.evm.Context.L1CostFunc(st.evm.Context.BlockNumber.Uint64(), st.msg) + l1Cost = st.evm.Context.L1CostFunc(st.evm.Context.BlockNumber.Uint64(), st.evm.Context.Time, st.msg) } if l1Cost != nil { mgval = mgval.Add(mgval, l1Cost) @@ -474,7 +474,7 @@ func (st *StateTransition) innerTransitionDb() (*ExecutionResult, error) { // Note optimismConfig will not be nil if rules.IsOptimismBedrock is true if optimismConfig := st.evm.ChainConfig().Optimism; optimismConfig != nil && rules.IsOptimismBedrock { st.state.AddBalance(params.OptimismBaseFeeRecipient, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.evm.Context.BaseFee)) - if cost := st.evm.Context.L1CostFunc(st.evm.Context.BlockNumber.Uint64(), st.msg); cost != nil { + if cost := st.evm.Context.L1CostFunc(st.evm.Context.BlockNumber.Uint64(), st.evm.Context.Time, st.msg); cost != nil { st.state.AddBalance(params.OptimismL1FeeRecipient, cost) } } diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 5c6fffa2d4..49273f61b2 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -1326,7 +1326,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { costFn := types.NewL1CostFunc(pool.chainconfig, statedb) pool.l1CostFn = func(message types.RollupMessage) *big.Int { - return costFn(newHead.Number.Uint64(), message) + return costFn(newHead.Number.Uint64(), newHead.Time, message) } // Inject any transactions discarded due to reorgs diff --git a/core/types/receipt.go b/core/types/receipt.go index 832b246c30..3c610f321e 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -473,7 +473,7 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { // DeriveFields fills the receipts with their computed fields based on consensus // data and contextual infos like containing block and transactions. -func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error { +func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, time uint64, txs Transactions) error { signer := MakeSigner(config, new(big.Int).SetUint64(number)) logIndex := uint(0) @@ -526,9 +526,10 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu feeScalar := new(big.Float).Quo(fscalar, fdivisor) for i := 0; i < len(rs); i++ { if !txs[i].IsDepositTx() { + gas := txs[i].RollupDataGas().DataGas(time, config) rs[i].L1GasPrice = l1Basefee - rs[i].L1GasUsed = new(big.Int).SetUint64(txs[i].RollupDataGas()) - rs[i].L1Fee = L1Cost(txs[i].RollupDataGas(), l1Basefee, overhead, scalar) + rs[i].L1GasUsed = new(big.Int).SetUint64(gas) + rs[i].L1Fee = L1Cost(gas, l1Basefee, overhead, scalar) rs[i].FeeScalar = feeScalar } } diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index c8f71f55f5..f776a79f77 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -223,7 +223,7 @@ func TestDeriveFields(t *testing.T) { hash := common.BytesToHash([]byte{0x03, 0x14}) clearComputedFieldsOnReceipts(t, receipts) - if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), txs); err != nil { + if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), 0, txs); err != nil { t.Fatalf("DeriveFields(...) = %v, want ", err) } // Iterate over all the computed fields and check that they're correct diff --git a/core/types/rollup_l1_cost.go b/core/types/rollup_l1_cost.go index edb2138881..24871c4159 100644 --- a/core/types/rollup_l1_cost.go +++ b/core/types/rollup_l1_cost.go @@ -23,8 +23,22 @@ import ( "github.com/ethereum/go-ethereum/params" ) +type RollupGasData struct { + Zeroes, Ones uint64 +} + +func (r RollupGasData) DataGas(time uint64, cfg *params.ChainConfig) (gas uint64) { + gas = r.Zeroes * params.TxDataZeroGas + if cfg.IsRegolith(time) { + gas += r.Ones * params.TxDataNonZeroGasEIP2028 + } else { + gas += (r.Ones + 68) * params.TxDataNonZeroGasEIP2028 + } + return gas +} + type RollupMessage interface { - RollupDataGas() uint64 + RollupDataGas() RollupGasData IsDepositTx() bool } @@ -34,7 +48,7 @@ type StateGetter interface { // L1CostFunc is used in the state transition to determine the cost of a rollup message. // Returns nil if there is no cost. -type L1CostFunc func(blockNum uint64, msg RollupMessage) *big.Int +type L1CostFunc func(blockNum uint64, blockTime uint64, msg RollupMessage) *big.Int var ( L1BaseFeeSlot = common.BigToHash(big.NewInt(1)) @@ -50,8 +64,8 @@ var L1BlockAddr = common.HexToAddress("0x420000000000000000000000000000000000001 func NewL1CostFunc(config *params.ChainConfig, statedb StateGetter) L1CostFunc { cacheBlockNum := ^uint64(0) var l1BaseFee, overhead, scalar *big.Int - return func(blockNum uint64, msg RollupMessage) *big.Int { - rollupDataGas := msg.RollupDataGas() // Only fake txs for RPC view-calls are 0. + return func(blockNum uint64, blockTime uint64, msg RollupMessage) *big.Int { + rollupDataGas := msg.RollupDataGas().DataGas(blockTime, config) // Only fake txs for RPC view-calls are 0. if config.Optimism == nil || msg.IsDepositTx() || rollupDataGas == 0 { return nil } diff --git a/core/types/rollup_l1_cost_test.go b/core/types/rollup_l1_cost_test.go new file mode 100644 index 0000000000..e43ea967ee --- /dev/null +++ b/core/types/rollup_l1_cost_test.go @@ -0,0 +1,30 @@ +package types + +import ( + "math/rand" + "testing" + + "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/require" +) + +func TestRollupGasData(t *testing.T) { + for i := 0; i < 100; i++ { + zeroes := rand.Uint64() + ones := rand.Uint64() + + r := RollupGasData{ + Zeroes: zeroes, + Ones: ones, + } + time := uint64(1) + cfg := ¶ms.ChainConfig{ + RegolithTime: &time, + } + gasPreRegolith := r.DataGas(0, cfg) + gasPostRegolith := r.DataGas(1, cfg) + + require.Equal(t, r.Zeroes*params.TxDataZeroGas+(r.Ones+68)*params.TxDataNonZeroGasEIP2028, gasPreRegolith) + require.Equal(t, r.Zeroes*params.TxDataZeroGas+r.Ones*params.TxDataNonZeroGasEIP2028, gasPostRegolith) + } +} diff --git a/core/types/transaction.go b/core/types/transaction.go index 8f8f14cfb4..2847bc1f73 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -29,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -59,7 +58,7 @@ type Transaction struct { size atomic.Value from atomic.Value - // cache how much gas the tx takes on L1 for its share of rollup data + // cache of RollupGasData details to compute the gas the tx takes on L1 for its share of rollup data rollupGas atomic.Value } @@ -347,31 +346,27 @@ func (tx *Transaction) Cost() *big.Int { } // RollupDataGas is the amount of gas it takes to confirm the tx on L1 as a rollup -func (tx *Transaction) RollupDataGas() uint64 { +func (tx *Transaction) RollupDataGas() RollupGasData { if tx.Type() == DepositTxType { - return 0 + return RollupGasData{} } if v := tx.rollupGas.Load(); v != nil { - return v.(uint64) + return v.(RollupGasData) } data, err := tx.MarshalBinary() if err != nil { // Silent error, invalid txs will not be marshalled/unmarshalled for batch submission anyway. log.Error("failed to encode tx for L1 cost computation", "err", err) } - var zeroes uint64 - var ones uint64 + var out RollupGasData for _, byt := range data { if byt == 0 { - zeroes++ + out.Zeroes++ } else { - ones++ + out.Ones++ } } - zeroesGas := zeroes * params.TxDataZeroGas - onesGas := (ones + 68) * params.TxDataNonZeroGasEIP2028 - total := zeroesGas + onesGas - tx.rollupGas.Store(total) - return total + tx.rollupGas.Store(out) + return out } // RawSignatureValues returns the V, R, S signature values of the transaction. @@ -685,7 +680,7 @@ type Message struct { isSystemTx bool isDepositTx bool mint *big.Int - l1CostGas uint64 + l1CostGas RollupGasData } func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, isFake bool) Message { @@ -705,7 +700,7 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b isSystemTx: false, isDepositTx: false, mint: nil, - l1CostGas: 0, + l1CostGas: RollupGasData{}, } } @@ -748,10 +743,11 @@ func (m Message) Nonce() uint64 { return m.nonce } func (m Message) Data() []byte { return m.data } func (m Message) AccessList() AccessList { return m.accessList } func (m Message) IsFake() bool { return m.isFake } -func (m Message) IsSystemTx() bool { return m.isSystemTx } -func (m Message) IsDepositTx() bool { return m.isDepositTx } -func (m Message) Mint() *big.Int { return m.mint } -func (m Message) RollupDataGas() uint64 { return m.l1CostGas } + +func (m Message) IsSystemTx() bool { return m.isSystemTx } +func (m Message) IsDepositTx() bool { return m.isDepositTx } +func (m Message) Mint() *big.Int { return m.mint } +func (m Message) RollupDataGas() RollupGasData { return m.l1CostGas } // copyAddressPtr copies an address. func copyAddressPtr(a *common.Address) *common.Address { diff --git a/light/odr_util.go b/light/odr_util.go index c49af3a1fb..dac2853085 100644 --- a/light/odr_util.go +++ b/light/odr_util.go @@ -175,7 +175,7 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num genesis := rawdb.ReadCanonicalHash(odr.Database(), 0) config := rawdb.ReadChainConfig(odr.Database(), genesis) - if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Transactions()); err != nil { + if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.Transactions()); err != nil { return nil, err } rawdb.WriteReceipts(odr.Database(), hash, number, receipts)