Skip to content

Commit ded9c88

Browse files
all: Make DA footprint gas scalar configurable (#675)
Co-authored-by: Sebastian Stammler <[email protected]>
1 parent 91c6a04 commit ded9c88

File tree

7 files changed

+203
-117
lines changed

7 files changed

+203
-117
lines changed

core/chain_makers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,14 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
421421
b.header.RequestsHash = &reqHash
422422
}
423423

424+
if config.IsDAFootprintBlockLimit(b.header.Time) {
425+
gasUsed, err := types.CalcGasUsedJovian(b.txs, b.header.GasUsed)
426+
if err != nil {
427+
panic(err)
428+
}
429+
b.header.GasUsed = gasUsed
430+
}
431+
424432
body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals}
425433
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts)
426434
if err != nil {

core/state_processor.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
8989
ProcessParentBlockHash(block.ParentHash(), evm)
9090
}
9191

92-
var (
93-
daFootprint uint64
94-
isJovian = p.config.IsJovian(block.Time())
95-
)
96-
9792
// Iterate over and process the individual transactions
9893
for i, tx := range block.Transactions() {
9994
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
@@ -108,10 +103,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
108103
}
109104
receipts = append(receipts, receipt)
110105
allLogs = append(allLogs, receipt.Logs...)
111-
112-
if tx.Type() != types.DepositTxType && isJovian {
113-
daFootprint += tx.RollupCostData().EstimatedDASize().Uint64() * params.DAFootprintGasScalar
114-
}
115106
}
116107

117108
isIsthmus := p.config.IsIsthmus(block.Time())
@@ -138,8 +129,12 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
138129
requests = [][]byte{}
139130
}
140131

141-
if isJovian && *usedGas < daFootprint {
142-
*usedGas = daFootprint
132+
if p.config.IsDAFootprintBlockLimit(block.Time()) {
133+
gasUsed, err := types.CalcGasUsedJovian(block.Transactions(), *usedGas)
134+
if err != nil {
135+
return nil, fmt.Errorf("failed to calculate Jovian gas used: %w", err)
136+
}
137+
*usedGas = gasUsed
143138
}
144139

145140
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)

core/types/rollup_cost.go

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package types
1919
import (
2020
"bytes"
2121
"encoding/binary"
22+
"errors"
2223
"fmt"
2324
"math/big"
2425

@@ -41,6 +42,9 @@ const (
4142
// array. baseFeeScalar is in the first four bytes of the segment, blobBaseFeeScalar the next
4243
// four.
4344
scalarSectionStart = 32 - BaseFeeScalarSlotOffset - 4
45+
46+
IsthmusL1AttributesLen = 176
47+
JovianL1AttributesLen = 178
4448
)
4549

4650
func init() {
@@ -57,6 +61,8 @@ var (
5761
EcotoneL1AttributesSelector = []byte{0x44, 0x0a, 0x5e, 0x20}
5862
// IsthmusL1AttributesSelector is the selector indicating Isthmus style L1 gas attributes.
5963
IsthmusL1AttributesSelector = []byte{0x09, 0x89, 0x99, 0xbe}
64+
// JovianL1AttributesSelector is the selector indicating Jovian style L1 gas attributes.
65+
JovianL1AttributesSelector = []byte{0x3d, 0xb6, 0xbe, 0x2b}
6066

6167
// L1BlockAddr is the address of the L1Block contract which stores the L1 gas attributes.
6268
L1BlockAddr = common.HexToAddress("0x4200000000000000000000000000000000000015")
@@ -512,6 +518,57 @@ func extractL1GasParamsPostIsthmus(data []byte) (gasParams, error) {
512518
}, nil
513519
}
514520

521+
// ExtractDAFootprintGasScalar extracts the DA footprint gas scalar from the L1 attributes transaction data
522+
// of a Jovian-enabled block.
523+
func ExtractDAFootprintGasScalar(data []byte) (uint16, error) {
524+
if len(data) < JovianL1AttributesLen {
525+
return 0, fmt.Errorf("L1 attributes transaction data too short for DA footprint gas scalar: %d", len(data))
526+
}
527+
// Future forks need to be added here
528+
if !bytes.Equal(data[0:4], JovianL1AttributesSelector) {
529+
return 0, fmt.Errorf("L1 attributes transaction data does not have Jovian selector")
530+
}
531+
daFootprintGasScalar := binary.BigEndian.Uint16(data[JovianL1AttributesLen-2 : JovianL1AttributesLen])
532+
return daFootprintGasScalar, nil
533+
}
534+
535+
// CalcGasUsedJovian calculates the gas used for an OP Stack chain.
536+
// Jovian introduces a DA footprint block limit, which potentially increases the gasUsed.
537+
// CalcGasUsedJovian must not be called for pre-Jovian blocks.
538+
func CalcGasUsedJovian(txs []*Transaction, evmGasUsed uint64) (uint64, error) {
539+
if len(txs) == 0 || !txs[0].IsDepositTx() {
540+
return 0, errors.New("missing deposit transaction")
541+
}
542+
543+
// First Jovian block doesn't set the DA footprint gas scalar yet and
544+
// it must not have user transactions.
545+
data := txs[0].Data()
546+
if len(data) == IsthmusL1AttributesLen {
547+
if !txs[len(txs)-1].IsDepositTx() {
548+
// sufficient to check last transaction because deposits precede non-deposit txs
549+
return 0, errors.New("unexpected non-deposit transactions in Jovian activation block")
550+
}
551+
return evmGasUsed, nil
552+
} // ExtractDAFootprintGasScalar catches all invalid lengths
553+
554+
daFootprintGasScalar, err := ExtractDAFootprintGasScalar(data)
555+
if err != nil {
556+
return 0, err
557+
}
558+
var cumulativeDAFootprint uint64
559+
for _, tx := range txs {
560+
if tx.IsDepositTx() {
561+
continue
562+
}
563+
cumulativeDAFootprint += tx.RollupCostData().EstimatedDASize().Uint64()
564+
}
565+
daFootprint := uint64(daFootprintGasScalar) * cumulativeDAFootprint
566+
if evmGasUsed < daFootprint {
567+
return daFootprint, nil
568+
}
569+
return evmGasUsed, nil
570+
}
571+
515572
// L1Cost computes the the data availability fee for transactions in blocks prior to the Ecotone
516573
// upgrade. It is used by e2e tests so must remain exported.
517574
func L1Cost(rollupDataGas uint64, l1BaseFee, overhead, scalar *big.Int) *big.Int {
@@ -572,13 +629,13 @@ func ExtractEcotoneFeeParams(l1FeeParams []byte) (l1BaseFeeScalar, l1BlobBaseFee
572629
offset := scalarSectionStart
573630
l1BaseFeeScalar = new(big.Int).SetBytes(l1FeeParams[offset : offset+4])
574631
l1BlobBaseFeeScalar = new(big.Int).SetBytes(l1FeeParams[offset+4 : offset+8])
575-
return
632+
return l1BaseFeeScalar, l1BlobBaseFeeScalar
576633
}
577634

578635
func ExtractOperatorFeeParams(operatorFeeParams common.Hash) (operatorFeeScalar, operatorFeeConstant *big.Int) {
579636
operatorFeeScalar = new(big.Int).SetBytes(operatorFeeParams[20:24])
580637
operatorFeeConstant = new(big.Int).SetBytes(operatorFeeParams[24:32])
581-
return
638+
return operatorFeeScalar, operatorFeeConstant
582639
}
583640

584641
func bedrockCalldataGasUsed(costData RollupCostData) (calldataGasUsed *big.Int) {

miner/miner_optimism_test.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package miner
22

33
import (
4+
"encoding/binary"
45
"testing"
56

67
"github.com/ethereum/go-ethereum/common"
@@ -13,6 +14,8 @@ import (
1314
"github.com/stretchr/testify/require"
1415
)
1516

17+
const testDAFootprintGasScalar = 400
18+
1619
// TestDAFootprintMining tests that the miner correctly limits the DA footprint of the block.
1720
// It builds a block via the miner from txpool
1821
// transactions and then imports the block into the chain, asserting that
@@ -40,7 +43,7 @@ func TestDAFootprintMining(t *testing.T) {
4043
if txs[i].IsDepositTx() {
4144
continue
4245
}
43-
daFootprint += txs[i].RollupCostData().EstimatedDASize().Uint64() * params.DAFootprintGasScalar
46+
daFootprint += txs[i].RollupCostData().EstimatedDASize().Uint64() * testDAFootprintGasScalar
4447
}
4548
require.Less(t, txGas, block.GasUsed(), "total tx gas used must be smaller than block gas used")
4649
require.Equal(t, daFootprint, block.GasUsed(), "total DA footprint used should be equal to block gas used")
@@ -79,16 +82,23 @@ func testMineAndExecute(t *testing.T, numTxs uint64, cfg *params.ChainConfig, as
7982
}
8083
}
8184

85+
parent := b.chain.CurrentBlock()
86+
ts := parent.Time + 12
87+
dtx := new(types.DepositTx)
88+
if cfg.IsDAFootprintBlockLimit(parent.Time) {
89+
dtx = jovianDepositTx(testDAFootprintGasScalar)
90+
}
91+
8292
genParams := &generateParams{
8393
parentHash: b.chain.CurrentBlock().Hash(),
84-
timestamp: b.chain.CurrentBlock().Time + 12,
94+
timestamp: ts,
8595
withdrawals: types.Withdrawals{},
8696
beaconRoot: new(common.Hash),
8797
gasLimit: ptr(uint64(1e6)), // Small gas limit to easily fill block
88-
txs: types.Transactions{types.NewTx(&types.DepositTx{})},
98+
txs: types.Transactions{types.NewTx(dtx)},
8999
eip1559Params: eip1559.EncodeHolocene1559Params(250, 6),
90100
}
91-
if cfg.IsJovian(b.chain.CurrentBlock().Time) {
101+
if cfg.IsMinBaseFee(ts) {
92102
genParams.minBaseFee = new(uint64)
93103
}
94104
r := w.generateWork(genParams, false)
@@ -102,6 +112,13 @@ func testMineAndExecute(t *testing.T, numTxs uint64, cfg *params.ChainConfig, as
102112
require.NoError(t, err, "block import/execution failed")
103113
}
104114

115+
func jovianDepositTx(daFootprintGasScalar uint16) *types.DepositTx {
116+
data := make([]byte, types.JovianL1AttributesLen)
117+
copy(data[0:4], types.JovianL1AttributesSelector)
118+
binary.BigEndian.PutUint16(data[types.JovianL1AttributesLen-2:types.JovianL1AttributesLen], daFootprintGasScalar)
119+
return &types.DepositTx{Data: data}
120+
}
121+
105122
func ptr[T any](v T) *T {
106123
return &v
107124
}

0 commit comments

Comments
 (0)