Skip to content

Commit cde9c26

Browse files
Fix L1 cost function (bnb-chain#61)
The gas price oracle calculates L1 data costs using an unsigned transaction. However, `op-geth` was calculating L1 data costs using a _signed_ transaction, which included an additional 68 unnecessary bytes (1088 gas) in the cost. This PR removes those extra bytes as part of the Regolith hardfork. Co-authored-by: protolambda <proto@protolambda.com>
1 parent 428dc6e commit cde9c26

12 files changed

+96
-43
lines changed

accounts/abi/bind/backends/simulated.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -903,10 +903,11 @@ func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
903903
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
904904
func (m callMsg) Data() []byte { return m.CallMsg.Data }
905905
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
906-
func (m callMsg) IsSystemTx() bool { return false }
907-
func (m callMsg) IsDepositTx() bool { return false }
908-
func (m callMsg) Mint() *big.Int { return nil }
909-
func (m callMsg) RollupDataGas() uint64 { return 0 }
906+
907+
func (m callMsg) IsSystemTx() bool { return false }
908+
func (m callMsg) IsDepositTx() bool { return false }
909+
func (m callMsg) Mint() *big.Int { return nil }
910+
func (m callMsg) RollupDataGas() types.RollupGasData { return types.RollupGasData{} }
910911

911912
// filterBackend implements filters.Backend to support filtering for logs without
912913
// taking bloom-bits acceleration structures into account.

core/blockchain.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -2035,7 +2035,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error)
20352035
// the processing of a block. These logs are later announced as deleted or reborn.
20362036
func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log {
20372037
receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64())
2038-
receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Transactions())
2038+
receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Time(), b.Transactions())
20392039

20402040
var logs []*types.Log
20412041
for _, receipt := range receipts {

core/rawdb/accessors_chain.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,12 @@ func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *para
636636
log.Error("Missing body but have receipt", "hash", hash, "number", number)
637637
return nil
638638
}
639-
if err := receipts.DeriveFields(config, hash, number, body.Transactions); err != nil {
639+
header := ReadHeader(db, hash, number)
640+
if header == nil {
641+
log.Error("Missing header but have receipt", "hash", hash, "number", number)
642+
return nil
643+
}
644+
if err := receipts.DeriveFields(config, hash, number, header.Time, body.Transactions); err != nil {
640645
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
641646
return nil
642647
}

core/rawdb/accessors_chain_test.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,9 @@ func TestBlockReceiptStorage(t *testing.T) {
347347
tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
348348
tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil)
349349

350+
header := &types.Header{
351+
Number: big.NewInt(0),
352+
}
350353
body := &types.Body{Transactions: types.Transactions{tx1, tx2}}
351354

352355
// Create the two receipts to manage afterwards
@@ -378,13 +381,16 @@ func TestBlockReceiptStorage(t *testing.T) {
378381
receipts := []*types.Receipt{receipt1, receipt2}
379382

380383
// Check that no receipt entries are in a pristine database
381-
hash := common.BytesToHash([]byte{0x03, 0x14})
384+
hash := header.Hash()
382385
if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 {
383386
t.Fatalf("non existent receipts returned: %v", rs)
384387
}
385388
// Insert the body that corresponds to the receipts
386389
WriteBody(db, hash, 0, body)
387390

391+
// Insert the header that corresponds to the receipts
392+
WriteHeader(db, header)
393+
388394
// Insert the receipt slice into the database and check presence
389395
WriteReceipts(db, hash, 0, receipts)
390396
if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) == 0 {

core/state_transition.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ type Message interface {
7979
Gas() uint64
8080
Value() *big.Int
8181

82-
IsSystemTx() bool // IsSystemTx indicates the message, if also a deposit, does not emit gas usage.
83-
IsDepositTx() bool // IsDepositTx indicates the message is force-included and can persist a mint.
84-
Mint() *big.Int // Mint is the amount to mint before EVM processing, or nil if there is no minting.
85-
RollupDataGas() uint64 // RollupDataGas indicates the rollup cost of the message, 0 if not a rollup or no cost.
82+
IsSystemTx() bool // IsSystemTx indicates the message, if also a deposit, does not emit gas usage.
83+
IsDepositTx() bool // IsDepositTx indicates the message is force-included and can persist a mint.
84+
Mint() *big.Int // Mint is the amount to mint before EVM processing, or nil if there is no minting.
85+
RollupDataGas() types.RollupGasData // RollupDataGas indicates the rollup cost of the message, 0 if not a rollup or no cost.
8686

8787
Nonce() uint64
8888
IsFake() bool
@@ -224,7 +224,7 @@ func (st *StateTransition) buyGas() error {
224224
mgval = mgval.Mul(mgval, st.gasPrice)
225225
var l1Cost *big.Int
226226
if st.evm.Context.L1CostFunc != nil {
227-
l1Cost = st.evm.Context.L1CostFunc(st.evm.Context.BlockNumber.Uint64(), st.msg)
227+
l1Cost = st.evm.Context.L1CostFunc(st.evm.Context.BlockNumber.Uint64(), st.evm.Context.Time, st.msg)
228228
}
229229
if l1Cost != nil {
230230
mgval = mgval.Add(mgval, l1Cost)
@@ -474,7 +474,7 @@ func (st *StateTransition) innerTransitionDb() (*ExecutionResult, error) {
474474
// Note optimismConfig will not be nil if rules.IsOptimismBedrock is true
475475
if optimismConfig := st.evm.ChainConfig().Optimism; optimismConfig != nil && rules.IsOptimismBedrock {
476476
st.state.AddBalance(params.OptimismBaseFeeRecipient, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.evm.Context.BaseFee))
477-
if cost := st.evm.Context.L1CostFunc(st.evm.Context.BlockNumber.Uint64(), st.msg); cost != nil {
477+
if cost := st.evm.Context.L1CostFunc(st.evm.Context.BlockNumber.Uint64(), st.evm.Context.Time, st.msg); cost != nil {
478478
st.state.AddBalance(params.OptimismL1FeeRecipient, cost)
479479
}
480480
}

core/txpool/txpool.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
13261326

13271327
costFn := types.NewL1CostFunc(pool.chainconfig, statedb)
13281328
pool.l1CostFn = func(message types.RollupMessage) *big.Int {
1329-
return costFn(newHead.Number.Uint64(), message)
1329+
return costFn(newHead.Number.Uint64(), newHead.Time, message)
13301330
}
13311331

13321332
// Inject any transactions discarded due to reorgs

core/types/receipt.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
473473

474474
// DeriveFields fills the receipts with their computed fields based on consensus
475475
// data and contextual infos like containing block and transactions.
476-
func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error {
476+
func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, time uint64, txs Transactions) error {
477477
signer := MakeSigner(config, new(big.Int).SetUint64(number))
478478

479479
logIndex := uint(0)
@@ -526,9 +526,10 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
526526
feeScalar := new(big.Float).Quo(fscalar, fdivisor)
527527
for i := 0; i < len(rs); i++ {
528528
if !txs[i].IsDepositTx() {
529+
gas := txs[i].RollupDataGas().DataGas(time, config)
529530
rs[i].L1GasPrice = l1Basefee
530-
rs[i].L1GasUsed = new(big.Int).SetUint64(txs[i].RollupDataGas())
531-
rs[i].L1Fee = L1Cost(txs[i].RollupDataGas(), l1Basefee, overhead, scalar)
531+
rs[i].L1GasUsed = new(big.Int).SetUint64(gas)
532+
rs[i].L1Fee = L1Cost(gas, l1Basefee, overhead, scalar)
532533
rs[i].FeeScalar = feeScalar
533534
}
534535
}

core/types/receipt_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func TestDeriveFields(t *testing.T) {
223223
hash := common.BytesToHash([]byte{0x03, 0x14})
224224

225225
clearComputedFieldsOnReceipts(t, receipts)
226-
if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), txs); err != nil {
226+
if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), 0, txs); err != nil {
227227
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
228228
}
229229
// Iterate over all the computed fields and check that they're correct

core/types/rollup_l1_cost.go

+18-4
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,22 @@ import (
2323
"github.com/ethereum/go-ethereum/params"
2424
)
2525

26+
type RollupGasData struct {
27+
Zeroes, Ones uint64
28+
}
29+
30+
func (r RollupGasData) DataGas(time uint64, cfg *params.ChainConfig) (gas uint64) {
31+
gas = r.Zeroes * params.TxDataZeroGas
32+
if cfg.IsRegolith(time) {
33+
gas += r.Ones * params.TxDataNonZeroGasEIP2028
34+
} else {
35+
gas += (r.Ones + 68) * params.TxDataNonZeroGasEIP2028
36+
}
37+
return gas
38+
}
39+
2640
type RollupMessage interface {
27-
RollupDataGas() uint64
41+
RollupDataGas() RollupGasData
2842
IsDepositTx() bool
2943
}
3044

@@ -34,7 +48,7 @@ type StateGetter interface {
3448

3549
// L1CostFunc is used in the state transition to determine the cost of a rollup message.
3650
// Returns nil if there is no cost.
37-
type L1CostFunc func(blockNum uint64, msg RollupMessage) *big.Int
51+
type L1CostFunc func(blockNum uint64, blockTime uint64, msg RollupMessage) *big.Int
3852

3953
var (
4054
L1BaseFeeSlot = common.BigToHash(big.NewInt(1))
@@ -50,8 +64,8 @@ var L1BlockAddr = common.HexToAddress("0x420000000000000000000000000000000000001
5064
func NewL1CostFunc(config *params.ChainConfig, statedb StateGetter) L1CostFunc {
5165
cacheBlockNum := ^uint64(0)
5266
var l1BaseFee, overhead, scalar *big.Int
53-
return func(blockNum uint64, msg RollupMessage) *big.Int {
54-
rollupDataGas := msg.RollupDataGas() // Only fake txs for RPC view-calls are 0.
67+
return func(blockNum uint64, blockTime uint64, msg RollupMessage) *big.Int {
68+
rollupDataGas := msg.RollupDataGas().DataGas(blockTime, config) // Only fake txs for RPC view-calls are 0.
5569
if config.Optimism == nil || msg.IsDepositTx() || rollupDataGas == 0 {
5670
return nil
5771
}

core/types/rollup_l1_cost_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package types
2+
3+
import (
4+
"math/rand"
5+
"testing"
6+
7+
"github.com/ethereum/go-ethereum/params"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestRollupGasData(t *testing.T) {
12+
for i := 0; i < 100; i++ {
13+
zeroes := rand.Uint64()
14+
ones := rand.Uint64()
15+
16+
r := RollupGasData{
17+
Zeroes: zeroes,
18+
Ones: ones,
19+
}
20+
time := uint64(1)
21+
cfg := &params.ChainConfig{
22+
RegolithTime: &time,
23+
}
24+
gasPreRegolith := r.DataGas(0, cfg)
25+
gasPostRegolith := r.DataGas(1, cfg)
26+
27+
require.Equal(t, r.Zeroes*params.TxDataZeroGas+(r.Ones+68)*params.TxDataNonZeroGasEIP2028, gasPreRegolith)
28+
require.Equal(t, r.Zeroes*params.TxDataZeroGas+r.Ones*params.TxDataNonZeroGasEIP2028, gasPostRegolith)
29+
}
30+
}

core/types/transaction.go

+16-20
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"github.com/ethereum/go-ethereum/common/math"
3030
"github.com/ethereum/go-ethereum/crypto"
3131
"github.com/ethereum/go-ethereum/log"
32-
"github.com/ethereum/go-ethereum/params"
3332
"github.com/ethereum/go-ethereum/rlp"
3433
)
3534

@@ -59,7 +58,7 @@ type Transaction struct {
5958
size atomic.Value
6059
from atomic.Value
6160

62-
// cache how much gas the tx takes on L1 for its share of rollup data
61+
// cache of RollupGasData details to compute the gas the tx takes on L1 for its share of rollup data
6362
rollupGas atomic.Value
6463
}
6564

@@ -347,31 +346,27 @@ func (tx *Transaction) Cost() *big.Int {
347346
}
348347

349348
// RollupDataGas is the amount of gas it takes to confirm the tx on L1 as a rollup
350-
func (tx *Transaction) RollupDataGas() uint64 {
349+
func (tx *Transaction) RollupDataGas() RollupGasData {
351350
if tx.Type() == DepositTxType {
352-
return 0
351+
return RollupGasData{}
353352
}
354353
if v := tx.rollupGas.Load(); v != nil {
355-
return v.(uint64)
354+
return v.(RollupGasData)
356355
}
357356
data, err := tx.MarshalBinary()
358357
if err != nil { // Silent error, invalid txs will not be marshalled/unmarshalled for batch submission anyway.
359358
log.Error("failed to encode tx for L1 cost computation", "err", err)
360359
}
361-
var zeroes uint64
362-
var ones uint64
360+
var out RollupGasData
363361
for _, byt := range data {
364362
if byt == 0 {
365-
zeroes++
363+
out.Zeroes++
366364
} else {
367-
ones++
365+
out.Ones++
368366
}
369367
}
370-
zeroesGas := zeroes * params.TxDataZeroGas
371-
onesGas := (ones + 68) * params.TxDataNonZeroGasEIP2028
372-
total := zeroesGas + onesGas
373-
tx.rollupGas.Store(total)
374-
return total
368+
tx.rollupGas.Store(out)
369+
return out
375370
}
376371

377372
// RawSignatureValues returns the V, R, S signature values of the transaction.
@@ -685,7 +680,7 @@ type Message struct {
685680
isSystemTx bool
686681
isDepositTx bool
687682
mint *big.Int
688-
l1CostGas uint64
683+
l1CostGas RollupGasData
689684
}
690685

691686
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
705700
isSystemTx: false,
706701
isDepositTx: false,
707702
mint: nil,
708-
l1CostGas: 0,
703+
l1CostGas: RollupGasData{},
709704
}
710705
}
711706

@@ -748,10 +743,11 @@ func (m Message) Nonce() uint64 { return m.nonce }
748743
func (m Message) Data() []byte { return m.data }
749744
func (m Message) AccessList() AccessList { return m.accessList }
750745
func (m Message) IsFake() bool { return m.isFake }
751-
func (m Message) IsSystemTx() bool { return m.isSystemTx }
752-
func (m Message) IsDepositTx() bool { return m.isDepositTx }
753-
func (m Message) Mint() *big.Int { return m.mint }
754-
func (m Message) RollupDataGas() uint64 { return m.l1CostGas }
746+
747+
func (m Message) IsSystemTx() bool { return m.isSystemTx }
748+
func (m Message) IsDepositTx() bool { return m.isDepositTx }
749+
func (m Message) Mint() *big.Int { return m.mint }
750+
func (m Message) RollupDataGas() RollupGasData { return m.l1CostGas }
755751

756752
// copyAddressPtr copies an address.
757753
func copyAddressPtr(a *common.Address) *common.Address {

light/odr_util.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num
175175
genesis := rawdb.ReadCanonicalHash(odr.Database(), 0)
176176
config := rawdb.ReadChainConfig(odr.Database(), genesis)
177177

178-
if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Transactions()); err != nil {
178+
if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.Transactions()); err != nil {
179179
return nil, err
180180
}
181181
rawdb.WriteReceipts(odr.Database(), hash, number, receipts)

0 commit comments

Comments
 (0)