From 91c6a048c2725b1c9c12a334afd0673e99c037ab Mon Sep 17 00:00:00 2001 From: Sebastian Stammler Date: Tue, 14 Oct 2025 17:01:23 +0200 Subject: [PATCH 01/17] core,miner,parms: DA footprint block limit (constant gas scalar) (#655) --- core/state_processor.go | 13 ++++ miner/miner_optimism_test.go | 107 +++++++++++++++++++++++++++++++++ miner/payload_building_test.go | 57 +++++++++--------- miner/worker.go | 37 ++++++++++++ params/config.go | 5 +- params/opstack_params.go | 5 ++ 6 files changed, 194 insertions(+), 30 deletions(-) create mode 100644 miner/miner_optimism_test.go create mode 100644 params/opstack_params.go diff --git a/core/state_processor.go b/core/state_processor.go index c29d0c24de..7c3be411df 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -89,6 +89,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg ProcessParentBlockHash(block.ParentHash(), evm) } + var ( + daFootprint uint64 + isJovian = p.config.IsJovian(block.Time()) + ) + // Iterate over and process the individual transactions for i, tx := range block.Transactions() { msg, err := TransactionToMessage(tx, signer, header.BaseFee) @@ -103,6 +108,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) + + if tx.Type() != types.DepositTxType && isJovian { + daFootprint += tx.RollupCostData().EstimatedDASize().Uint64() * params.DAFootprintGasScalar + } } isIsthmus := p.config.IsIsthmus(block.Time()) @@ -129,6 +138,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg requests = [][]byte{} } + if isJovian && *usedGas < daFootprint { + *usedGas = daFootprint + } + // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) p.chain.engine.Finalize(p.chain, header, tracingStateDB, block.Body()) diff --git a/miner/miner_optimism_test.go b/miner/miner_optimism_test.go new file mode 100644 index 0000000000..8730cd039c --- /dev/null +++ b/miner/miner_optimism_test.go @@ -0,0 +1,107 @@ +package miner + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/beacon" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/consensus/misc/eip1559" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/require" +) + +// TestDAFootprintMining tests that the miner correctly limits the DA footprint of the block. +// It builds a block via the miner from txpool +// transactions and then imports the block into the chain, asserting that +// execution succeeds. +func TestDAFootprintMining(t *testing.T) { + requireTxGas := func(t *testing.T, block *types.Block, receipts []*types.Receipt) { + var txGas uint64 + for _, receipt := range receipts { + txGas += receipt.GasUsed + } + require.Equal(t, txGas, block.GasUsed(), "total tx gas used should be equal to block gas used") + } + + requireDAFootprint := func(t *testing.T, block *types.Block, receipts []*types.Receipt) { + var ( + txGas uint64 + daFootprint uint64 + txs = block.Transactions() + ) + + require.Equal(t, len(receipts), len(txs)) + + for i, receipt := range receipts { + txGas += receipt.GasUsed + if txs[i].IsDepositTx() { + continue + } + daFootprint += txs[i].RollupCostData().EstimatedDASize().Uint64() * params.DAFootprintGasScalar + } + require.Less(t, txGas, block.GasUsed(), "total tx gas used must be smaller than block gas used") + require.Equal(t, daFootprint, block.GasUsed(), "total DA footprint used should be equal to block gas used") + } + t.Run("jovian-at-limit", func(t *testing.T) { + testMineAndExecute(t, 17, jovianConfig(), func(t *testing.T, block *types.Block, receipts []*types.Receipt) { + require.Len(t, receipts, 19) // including 1 test pending tx and 1 deposit tx + requireDAFootprint(t, block, receipts) + }) + }) + t.Run("jovian-above-limit", func(t *testing.T) { + testMineAndExecute(t, 18, jovianConfig(), func(t *testing.T, block *types.Block, receipts []*types.Receipt) { + require.Len(t, receipts, 19) // same as for 17, because 18th tx from pool shouldn't have been included + requireDAFootprint(t, block, receipts) + }) + }) + t.Run("isthmus", func(t *testing.T) { + testMineAndExecute(t, 39, isthmusConfig(), func(t *testing.T, block *types.Block, receipts []*types.Receipt) { + require.Len(t, receipts, 41) // including 1 test pending tx and 1 deposit tx + requireTxGas(t, block, receipts) + }) + }) +} + +func testMineAndExecute(t *testing.T, numTxs uint64, cfg *params.ChainConfig, assertFn func(t *testing.T, block *types.Block, receipts []*types.Receipt)) { + db := rawdb.NewMemoryDatabase() + w, b := newTestWorker(t, cfg, beacon.New(ethash.NewFaker()), db, 0) + + // Start from nonce 1 to avoid colliding with the preloaded pending tx. + txs := genTxs(1, numTxs) + + // Add to txpool for the miner to pick up. + if errs := b.txPool.Add(txs, false); len(errs) > 0 { + for _, err := range errs { + require.NoError(t, err, "failed adding tx to pool") + } + } + + genParams := &generateParams{ + parentHash: b.chain.CurrentBlock().Hash(), + timestamp: b.chain.CurrentBlock().Time + 12, + withdrawals: types.Withdrawals{}, + beaconRoot: new(common.Hash), + gasLimit: ptr(uint64(1e6)), // Small gas limit to easily fill block + txs: types.Transactions{types.NewTx(&types.DepositTx{})}, + eip1559Params: eip1559.EncodeHolocene1559Params(250, 6), + } + if cfg.IsJovian(b.chain.CurrentBlock().Time) { + genParams.minBaseFee = new(uint64) + } + r := w.generateWork(genParams, false) + require.NoError(t, r.err, "block generation failed") + require.NotNil(t, r.block, "no block generated") + + assertFn(t, r.block, r.receipts) + + // Import the block into the chain, which executes it via StateProcessor. + _, err := b.chain.InsertChain(types.Blocks{r.block}) + require.NoError(t, err, "block import/execution failed") +} + +func ptr[T any](v T) *T { + return &v +} diff --git a/miner/payload_building_test.go b/miner/payload_building_test.go index 387ce3bbaa..f7e7838d57 100644 --- a/miner/payload_building_test.go +++ b/miner/payload_building_test.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" @@ -74,7 +75,10 @@ const ( numDAFilterTxs = 256 ) -var zero = uint64(0) +var ( + zero = uint64(0) + validEIP1559Params = eip1559.EncodeHolocene1559Params(250, 6) +) func init() { testTxPoolConfig = legacypool.DefaultConfig @@ -127,7 +131,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength) copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes()) e.Authorize(testBankAddress) - case *ethash.Ethash: + case *ethash.Ethash, *beacon.Beacon: default: t.Fatalf("unexpected consensus engine type: %T", engine) } @@ -203,23 +207,23 @@ func TestDAFilters(t *testing.T) { } func holoceneConfig() *params.ChainConfig { - config := *params.TestChainConfig - config.LondonBlock = big.NewInt(0) - t := uint64(0) - config.CanyonTime = &t - config.HoloceneTime = &t - canyonDenom := uint64(250) - config.Optimism = ¶ms.OptimismConfig{ - EIP1559Elasticity: 6, - EIP1559Denominator: 50, - EIP1559DenominatorCanyon: &canyonDenom, - } + config := *params.OptimismTestConfig + config.IsthmusTime = nil + config.JovianTime = nil + config.PragueTime = nil + config.OsakaTime = nil return &config } -func jovianConfig() *params.ChainConfig { +func isthmusConfig() *params.ChainConfig { config := holoceneConfig() - zero := uint64(0) + config.IsthmusTime = &zero + config.PragueTime = &zero + return config +} + +func jovianConfig() *params.ChainConfig { + config := isthmusConfig() config.JovianTime = &zero return config } @@ -230,8 +234,8 @@ func newPayloadArgs(parentHash common.Hash, params1559 []byte, minBaseFee *uint6 return &BuildPayloadArgs{ Parent: parentHash, Timestamp: testTimestamp, - Random: common.Hash{}, FeeRecipient: testRecipient, + Withdrawals: types.Withdrawals{}, NoTxPool: true, EIP1559Params: params1559, MinBaseFee: minBaseFee, @@ -377,8 +381,7 @@ func testDAFilters(t *testing.T, maxDATxSize, maxDABlockSize *big.Int, expectedT txs := genTxs(1, numDAFilterTxs) b.txPool.Add(txs, false) - params1559 := []byte{0, 1, 2, 3, 4, 5, 6, 7} - args := newPayloadArgs(b.chain.CurrentBlock().Hash(), params1559, &zero) + args := newPayloadArgs(b.chain.CurrentBlock().Hash(), validEIP1559Params, &zero) args.NoTxPool = false payload, err := w.buildPayload(args, false) @@ -426,14 +429,14 @@ func TestBuildPayloadInvalidHoloceneParams(t *testing.T) { } } -func TestBuildPayloadInvalidMinBaseFeeExtraData(t *testing.T) { +func TestBuildPayloadInvalidJovianBuildPayloadArgs(t *testing.T) { t.Parallel() db := rawdb.NewMemoryDatabase() config := jovianConfig() w, b := newTestWorker(t, config, ethash.NewFaker(), db, 0) // 0 denominators shouldn't be allowed - badParams := eip1559.EncodeMinBaseFeeExtraData(0, 6, 0) + badParams := eip1559.EncodeHolocene1559Params(0, 6) args := newPayloadArgs(b.chain.CurrentBlock().Hash(), badParams, &zero) payload, err := w.buildPayload(args, false) @@ -441,13 +444,11 @@ func TestBuildPayloadInvalidMinBaseFeeExtraData(t *testing.T) { t.Fatalf("expected error, got none") } - // missing minBaseFee shouldn't be allowed (use Holocene encoder) - badParams = eip1559.EncodeHoloceneExtraData(250, 6) - args = newPayloadArgs(b.chain.CurrentBlock().Hash(), badParams, &zero) - payload, err = w.buildPayload(args, false) - if err == nil && (payload == nil || payload.err == nil) { - t.Fatalf("expected error, got none") - } + // missing minBaseFee is wrong input, panics + args = newPayloadArgs(b.chain.CurrentBlock().Hash(), validEIP1559Params, nil) + require.Panics(t, func() { + w.buildPayload(args, false) + }) } func genTxs(startNonce, count uint64) types.Transactions { @@ -466,7 +467,7 @@ func genTxs(startNonce, count uint64) types.Transactions { Nonce: nonce, To: &testUserAddress, Value: big.NewInt(1000), - Gas: params.TxGas + uint64(len(randomBytes))*16, + Gas: params.TxGas + uint64(len(randomBytes))*40, GasPrice: big.NewInt(params.InitialBaseFee), Data: randomBytes, }) diff --git a/miner/worker.go b/miner/worker.go index 389ee60dd1..afd4b5c002 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -74,6 +74,9 @@ type environment struct { coinbase common.Address evm *vm.EVM + // OP-Stack addition: calldata footprint + daFootprint uint64 + header *types.Header txs []*types.Transaction receipts []*types.Receipt @@ -198,6 +201,11 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay } } + // OP-Stack addition: Jovian maxes the block.gasUsed with the calldata footprint + if miner.chainConfig.IsJovian(work.header.Time) && work.daFootprint > work.header.GasUsed { + work.header.GasUsed = work.daFootprint + } + body := types.Body{Transactions: work.txs, Withdrawals: genParam.withdrawals} if intr := genParam.interrupt; intr != nil && genParam.isUpdate && intr.Load() != commitInterruptNone { @@ -480,6 +488,8 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (* return receipt, err } +var minTransactionDAFootprint = new(big.Int).Mul(types.MinTransactionSize, big.NewInt(params.DAFootprintGasScalar)) + func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error { var ( isCancun = miner.chainConfig.IsCancun(env.header.Number, env.header.Time) @@ -488,7 +498,12 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran if env.gasPool == nil { env.gasPool = new(core.GasPool).AddGas(gasLimit) } + + // OP-Stack additions: throttling and DA footprint limit blockDABytes := new(big.Int) + daFootprintLeft := big.NewInt(int64(gasLimit)) + isJovian := miner.chainConfig.IsJovian(env.header.Time) + for { // Check interruption signal and abort building if it's fired. if interrupt != nil { @@ -501,6 +516,11 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) break } + // If we don't have enough DA space for any further transactions then we're done. + if isJovian && daFootprintLeft.Cmp(minTransactionDAFootprint) < 0 { + log.Debug("Not enough DA space for further transactions", "have", daFootprintLeft, "want", minTransactionDAFootprint) + break + } // If we don't have enough blob space for any further blob transactions, // skip that list altogether if !blobTxs.Empty() && env.blobs >= eip4844.MaxBlobsPerBlock(miner.chainConfig, env.header.Time) { @@ -550,6 +570,19 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran } } + // OP-Stack addition: Jovian DA footprint limit + var txDAFootprint *big.Int + // Note that commitTransaction is only called after deposit transactions have already been committed, + // so we don't need to resolve the transaction here and exclude deposits. + if isJovian { + txDAFootprint = new(big.Int).Mul(ltx.DABytes, big.NewInt(params.DAFootprintGasScalar)) + if daFootprintLeft.Cmp(txDAFootprint) < 0 { + log.Debug("Not enough DA space left for transaction", "hash", ltx.Hash, "left", daFootprintLeft, "needed", txDAFootprint) + txs.Pop() + continue + } + } + // OP-Stack addition: sequencer throttling daBytesAfter := new(big.Int) if ltx.DABytes != nil && miner.config.MaxDABlockSize != nil { @@ -622,6 +655,10 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran case errors.Is(err, nil): // Everything ok, collect the logs and shift in the next transaction from the same account blockDABytes = daBytesAfter + if isJovian { + daFootprintLeft.Sub(daFootprintLeft, txDAFootprint) + env.daFootprint += txDAFootprint.Uint64() // note, it's guaranteed to not overflow because of the max calldata size + } txs.Shift() default: diff --git a/params/config.go b/params/config.go index b165c83985..656036d0e1 100644 --- a/params/config.go +++ b/params/config.go @@ -377,6 +377,7 @@ var ( OptimismTestConfig = func() *ChainConfig { conf := *MergedTestChainConfig // copy the config conf.BlobScheduleConfig = nil + conf.OsakaTime = nil // needs to be removed when production fork introduces Osaka conf.BedrockBlock = big.NewInt(0) zero := uint64(0) conf.RegolithTime = &zero @@ -386,9 +387,9 @@ var ( conf.GraniteTime = &zero conf.HoloceneTime = &zero conf.IsthmusTime = &zero - conf.InteropTime = nil conf.JovianTime = nil - conf.Optimism = &OptimismConfig{EIP1559Elasticity: 50, EIP1559Denominator: 10, EIP1559DenominatorCanyon: uint64ptr(250)} + conf.InteropTime = nil + conf.Optimism = &OptimismConfig{EIP1559Elasticity: 6, EIP1559Denominator: 50, EIP1559DenominatorCanyon: uint64ptr(250)} return &conf }() ) diff --git a/params/opstack_params.go b/params/opstack_params.go new file mode 100644 index 0000000000..a90e32d97d --- /dev/null +++ b/params/opstack_params.go @@ -0,0 +1,5 @@ +package params + +// Jovian + +const DAFootprintGasScalar = 400 From ded9c88e417b1bc196ae1e6475078952dcd1cd53 Mon Sep 17 00:00:00 2001 From: Josh Klopfenstein Date: Tue, 14 Oct 2025 09:16:48 -0700 Subject: [PATCH 02/17] all: Make DA footprint gas scalar configurable (#675) Co-authored-by: Sebastian Stammler --- core/chain_makers.go | 8 ++ core/state_processor.go | 17 ++-- core/types/rollup_cost.go | 61 +++++++++++- miner/miner_optimism_test.go | 25 ++++- miner/payload_building_test.go | 163 +++++++++++++++++---------------- miner/worker.go | 41 ++++++--- params/opstack_params.go | 5 - 7 files changed, 203 insertions(+), 117 deletions(-) delete mode 100644 params/opstack_params.go diff --git a/core/chain_makers.go b/core/chain_makers.go index 4a64830ef8..f4adb78504 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -421,6 +421,14 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse b.header.RequestsHash = &reqHash } + if config.IsDAFootprintBlockLimit(b.header.Time) { + gasUsed, err := types.CalcGasUsedJovian(b.txs, b.header.GasUsed) + if err != nil { + panic(err) + } + b.header.GasUsed = gasUsed + } + body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals} block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts) if err != nil { diff --git a/core/state_processor.go b/core/state_processor.go index 7c3be411df..94124e845e 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -89,11 +89,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg ProcessParentBlockHash(block.ParentHash(), evm) } - var ( - daFootprint uint64 - isJovian = p.config.IsJovian(block.Time()) - ) - // Iterate over and process the individual transactions for i, tx := range block.Transactions() { msg, err := TransactionToMessage(tx, signer, header.BaseFee) @@ -108,10 +103,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) - - if tx.Type() != types.DepositTxType && isJovian { - daFootprint += tx.RollupCostData().EstimatedDASize().Uint64() * params.DAFootprintGasScalar - } } isIsthmus := p.config.IsIsthmus(block.Time()) @@ -138,8 +129,12 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg requests = [][]byte{} } - if isJovian && *usedGas < daFootprint { - *usedGas = daFootprint + if p.config.IsDAFootprintBlockLimit(block.Time()) { + gasUsed, err := types.CalcGasUsedJovian(block.Transactions(), *usedGas) + if err != nil { + return nil, fmt.Errorf("failed to calculate Jovian gas used: %w", err) + } + *usedGas = gasUsed } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) diff --git a/core/types/rollup_cost.go b/core/types/rollup_cost.go index 529e847ccf..82ff0a6a61 100644 --- a/core/types/rollup_cost.go +++ b/core/types/rollup_cost.go @@ -19,6 +19,7 @@ package types import ( "bytes" "encoding/binary" + "errors" "fmt" "math/big" @@ -41,6 +42,9 @@ const ( // array. baseFeeScalar is in the first four bytes of the segment, blobBaseFeeScalar the next // four. scalarSectionStart = 32 - BaseFeeScalarSlotOffset - 4 + + IsthmusL1AttributesLen = 176 + JovianL1AttributesLen = 178 ) func init() { @@ -57,6 +61,8 @@ var ( EcotoneL1AttributesSelector = []byte{0x44, 0x0a, 0x5e, 0x20} // IsthmusL1AttributesSelector is the selector indicating Isthmus style L1 gas attributes. IsthmusL1AttributesSelector = []byte{0x09, 0x89, 0x99, 0xbe} + // JovianL1AttributesSelector is the selector indicating Jovian style L1 gas attributes. + JovianL1AttributesSelector = []byte{0x3d, 0xb6, 0xbe, 0x2b} // L1BlockAddr is the address of the L1Block contract which stores the L1 gas attributes. L1BlockAddr = common.HexToAddress("0x4200000000000000000000000000000000000015") @@ -512,6 +518,57 @@ func extractL1GasParamsPostIsthmus(data []byte) (gasParams, error) { }, nil } +// ExtractDAFootprintGasScalar extracts the DA footprint gas scalar from the L1 attributes transaction data +// of a Jovian-enabled block. +func ExtractDAFootprintGasScalar(data []byte) (uint16, error) { + if len(data) < JovianL1AttributesLen { + return 0, fmt.Errorf("L1 attributes transaction data too short for DA footprint gas scalar: %d", len(data)) + } + // Future forks need to be added here + if !bytes.Equal(data[0:4], JovianL1AttributesSelector) { + return 0, fmt.Errorf("L1 attributes transaction data does not have Jovian selector") + } + daFootprintGasScalar := binary.BigEndian.Uint16(data[JovianL1AttributesLen-2 : JovianL1AttributesLen]) + return daFootprintGasScalar, nil +} + +// CalcGasUsedJovian calculates the gas used for an OP Stack chain. +// Jovian introduces a DA footprint block limit, which potentially increases the gasUsed. +// CalcGasUsedJovian must not be called for pre-Jovian blocks. +func CalcGasUsedJovian(txs []*Transaction, evmGasUsed uint64) (uint64, error) { + if len(txs) == 0 || !txs[0].IsDepositTx() { + return 0, errors.New("missing deposit transaction") + } + + // First Jovian block doesn't set the DA footprint gas scalar yet and + // it must not have user transactions. + data := txs[0].Data() + if len(data) == IsthmusL1AttributesLen { + if !txs[len(txs)-1].IsDepositTx() { + // sufficient to check last transaction because deposits precede non-deposit txs + return 0, errors.New("unexpected non-deposit transactions in Jovian activation block") + } + return evmGasUsed, nil + } // ExtractDAFootprintGasScalar catches all invalid lengths + + daFootprintGasScalar, err := ExtractDAFootprintGasScalar(data) + if err != nil { + return 0, err + } + var cumulativeDAFootprint uint64 + for _, tx := range txs { + if tx.IsDepositTx() { + continue + } + cumulativeDAFootprint += tx.RollupCostData().EstimatedDASize().Uint64() + } + daFootprint := uint64(daFootprintGasScalar) * cumulativeDAFootprint + if evmGasUsed < daFootprint { + return daFootprint, nil + } + return evmGasUsed, nil +} + // L1Cost computes the the data availability fee for transactions in blocks prior to the Ecotone // upgrade. It is used by e2e tests so must remain exported. func L1Cost(rollupDataGas uint64, l1BaseFee, overhead, scalar *big.Int) *big.Int { @@ -572,13 +629,13 @@ func ExtractEcotoneFeeParams(l1FeeParams []byte) (l1BaseFeeScalar, l1BlobBaseFee offset := scalarSectionStart l1BaseFeeScalar = new(big.Int).SetBytes(l1FeeParams[offset : offset+4]) l1BlobBaseFeeScalar = new(big.Int).SetBytes(l1FeeParams[offset+4 : offset+8]) - return + return l1BaseFeeScalar, l1BlobBaseFeeScalar } func ExtractOperatorFeeParams(operatorFeeParams common.Hash) (operatorFeeScalar, operatorFeeConstant *big.Int) { operatorFeeScalar = new(big.Int).SetBytes(operatorFeeParams[20:24]) operatorFeeConstant = new(big.Int).SetBytes(operatorFeeParams[24:32]) - return + return operatorFeeScalar, operatorFeeConstant } func bedrockCalldataGasUsed(costData RollupCostData) (calldataGasUsed *big.Int) { diff --git a/miner/miner_optimism_test.go b/miner/miner_optimism_test.go index 8730cd039c..ce1d59513b 100644 --- a/miner/miner_optimism_test.go +++ b/miner/miner_optimism_test.go @@ -1,6 +1,7 @@ package miner import ( + "encoding/binary" "testing" "github.com/ethereum/go-ethereum/common" @@ -13,6 +14,8 @@ import ( "github.com/stretchr/testify/require" ) +const testDAFootprintGasScalar = 400 + // TestDAFootprintMining tests that the miner correctly limits the DA footprint of the block. // It builds a block via the miner from txpool // transactions and then imports the block into the chain, asserting that @@ -40,7 +43,7 @@ func TestDAFootprintMining(t *testing.T) { if txs[i].IsDepositTx() { continue } - daFootprint += txs[i].RollupCostData().EstimatedDASize().Uint64() * params.DAFootprintGasScalar + daFootprint += txs[i].RollupCostData().EstimatedDASize().Uint64() * testDAFootprintGasScalar } require.Less(t, txGas, block.GasUsed(), "total tx gas used must be smaller than block gas used") 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 } } + parent := b.chain.CurrentBlock() + ts := parent.Time + 12 + dtx := new(types.DepositTx) + if cfg.IsDAFootprintBlockLimit(parent.Time) { + dtx = jovianDepositTx(testDAFootprintGasScalar) + } + genParams := &generateParams{ parentHash: b.chain.CurrentBlock().Hash(), - timestamp: b.chain.CurrentBlock().Time + 12, + timestamp: ts, withdrawals: types.Withdrawals{}, beaconRoot: new(common.Hash), gasLimit: ptr(uint64(1e6)), // Small gas limit to easily fill block - txs: types.Transactions{types.NewTx(&types.DepositTx{})}, + txs: types.Transactions{types.NewTx(dtx)}, eip1559Params: eip1559.EncodeHolocene1559Params(250, 6), } - if cfg.IsJovian(b.chain.CurrentBlock().Time) { + if cfg.IsMinBaseFee(ts) { genParams.minBaseFee = new(uint64) } r := w.generateWork(genParams, false) @@ -102,6 +112,13 @@ func testMineAndExecute(t *testing.T, numTxs uint64, cfg *params.ChainConfig, as require.NoError(t, err, "block import/execution failed") } +func jovianDepositTx(daFootprintGasScalar uint16) *types.DepositTx { + data := make([]byte, types.JovianL1AttributesLen) + copy(data[0:4], types.JovianL1AttributesSelector) + binary.BigEndian.PutUint16(data[types.JovianL1AttributesLen-2:types.JovianL1AttributesLen], daFootprintGasScalar) + return &types.DepositTx{Data: data} +} + func ptr[T any](v T) *T { return &v } diff --git a/miner/payload_building_test.go b/miner/payload_building_test.go index f7e7838d57..4665a43a96 100644 --- a/miner/payload_building_test.go +++ b/miner/payload_building_test.go @@ -25,6 +25,8 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" @@ -40,7 +42,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" - "github.com/stretchr/testify/require" ) var ( @@ -122,7 +123,7 @@ type testWorkerBackend struct { } func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend { - var gspec = &core.Genesis{ + gspec := &core.Genesis{ Config: chainConfig, Alloc: types.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, } @@ -170,23 +171,42 @@ func TestBuildPayload(t *testing.T) { // the builder routine t.Run("with-tx-pool", func(t *testing.T) { testBuildPayload(t, false, false, nil, params.TestChainConfig) }) t.Run("with-tx-pool-interrupt", func(t *testing.T) { testBuildPayload(t, false, true, nil, params.TestChainConfig) }) - params1559 := []byte{0, 1, 2, 3, 4, 5, 6, 7} - t.Run("with-params-holocene", func(t *testing.T) { testBuildPayload(t, false, false, params1559, holoceneConfig()) }) - t.Run("with-params-no-tx-pool-holocene", func(t *testing.T) { testBuildPayload(t, true, false, params1559, holoceneConfig()) }) - t.Run("with-params-interrupt-holocene", func(t *testing.T) { testBuildPayload(t, false, true, params1559, holoceneConfig()) }) - t.Run("with-params-jovian", func(t *testing.T) { testBuildPayload(t, false, false, params1559, jovianConfig()) }) - t.Run("with-params-no-tx-pool-jovian", func(t *testing.T) { testBuildPayload(t, true, false, params1559, jovianConfig()) }) - t.Run("with-params-interrupt-jovian", func(t *testing.T) { testBuildPayload(t, false, true, params1559, jovianConfig()) }) - - t.Run("wrong-config-no-params", func(t *testing.T) { testBuildPayloadWrongConfig(t, nil, holoceneConfig()) }) - t.Run("wrong-config-params-holocene", func(t *testing.T) { testBuildPayloadWrongConfig(t, params1559, holoceneConfig()) }) - t.Run("wrong-config-params-jovian", func(t *testing.T) { testBuildPayloadWrongConfig(t, params1559, jovianConfig()) }) + t.Run("with-params-holocene", func(t *testing.T) { testBuildPayload(t, false, false, validEIP1559Params, holoceneConfig()) }) + t.Run("with-params-no-tx-pool-holocene", func(t *testing.T) { testBuildPayload(t, true, false, validEIP1559Params, holoceneConfig()) }) + t.Run("with-params-interrupt-holocene", func(t *testing.T) { testBuildPayload(t, false, true, validEIP1559Params, holoceneConfig()) }) + t.Run("with-params-jovian", func(t *testing.T) { testBuildPayload(t, false, false, validEIP1559Params, jovianConfig()) }) + t.Run("with-params-no-tx-pool-jovian", func(t *testing.T) { testBuildPayload(t, true, false, validEIP1559Params, jovianConfig()) }) + t.Run("with-params-interrupt-jovian", func(t *testing.T) { testBuildPayload(t, false, true, validEIP1559Params, jovianConfig()) }) zeroParams := make([]byte, 8) t.Run("with-zero-params-holocene", func(t *testing.T) { testBuildPayload(t, true, false, zeroParams, holoceneConfig()) }) t.Run("with-zero-params-jovian", func(t *testing.T) { testBuildPayload(t, true, false, zeroParams, jovianConfig()) }) } +func TestBuildPayloadError(t *testing.T) { + t.Run("pre-holocene-with-params", func(t *testing.T) { + cfg := holoceneConfig() + cfg.HoloceneTime = nil + testBuildPayloadError(t, cfg, + "got eip1559 params, expected none", + func(args *BuildPayloadArgs) { args.EIP1559Params = validEIP1559Params }) + }) + t.Run("holocene-no-params", func(t *testing.T) { + testBuildPayloadError(t, holoceneConfig(), + "holocene eip-1559 params should be 8 bytes, got 0", + func(args *BuildPayloadArgs) { args.EIP1559Params = nil }) + }) + t.Run("holocene-bad-params", func(t *testing.T) { + testBuildPayloadError(t, holoceneConfig(), + "holocene params cannot have a 0 denominator unless elasticity is also 0", + func(args *BuildPayloadArgs) { args.EIP1559Params = eip1559.EncodeHolocene1559Params(0, 6) }) + }) + t.Run("jovian-no-minbasefee", func(t *testing.T) { + testBuildPayloadError(t, jovianConfig(), "missing minBaseFee", + func(args *BuildPayloadArgs) { args.MinBaseFee = nil }) + }) +} + func TestDAFilters(t *testing.T) { // Each test case inserts one pending small (DA cost 100) transaction followed by // numDAFilterTxs transactions that have random calldata (min DA size >> 100) @@ -228,29 +248,44 @@ func jovianConfig() *params.ChainConfig { return config } -// newPayloadArgs returns a BuildPaylooadArgs with the given parentHash, eip-1559 params, -// minBaseFee, testTimestamp for Timestamp, and testRecipient for recipient. NoTxPool is set to true. -func newPayloadArgs(parentHash common.Hash, params1559 []byte, minBaseFee *uint64) *BuildPayloadArgs { - return &BuildPayloadArgs{ - Parent: parentHash, - Timestamp: testTimestamp, - FeeRecipient: testRecipient, - Withdrawals: types.Withdrawals{}, - NoTxPool: true, - EIP1559Params: params1559, - MinBaseFee: minBaseFee, +// newPayloadArgs returns valid BuildPaylooadArgs for the given chain config with the given parentHash, +// testTimestamp for Timestamp, and testRecipient for recipient. +// OP-Stack chains will have one dummy deposit transaction in Transactions. +// NoTxPool is set to true. +// A test can modify individual fields afterwards to enable the transaction +// pool, create invalid eip-1559 params, minBaseFee, etc. +func newPayloadArgs(parentHash common.Hash, cfg *params.ChainConfig) *BuildPayloadArgs { + args := &BuildPayloadArgs{ + Parent: parentHash, + Timestamp: testTimestamp, + FeeRecipient: testRecipient, + Withdrawals: types.Withdrawals{}, + NoTxPool: true, + } + + if !cfg.IsOptimism() { + return args } + + if cfg.IsHolocene(args.Timestamp) { + args.EIP1559Params = validEIP1559Params + } + dtx := new(types.DepositTx) + if cfg.IsDAFootprintBlockLimit(args.Timestamp) { + dtx = jovianDepositTx(testDAFootprintGasScalar) + } + args.Transactions = []*types.Transaction{types.NewTx(dtx)} + if cfg.IsMinBaseFee(args.Timestamp) { + args.MinBaseFee = ptr(uint64(1e9)) + } + + return args } func testBuildPayload(t *testing.T, noTxPool, interrupt bool, params1559 []byte, config *params.ChainConfig) { t.Parallel() db := rawdb.NewMemoryDatabase() - var minBaseFee *uint64 - if config.IsMinBaseFee(testTimestamp) { - val := uint64(1e9) - minBaseFee = &val - } w, b := newTestWorker(t, config, ethash.NewFaker(), db, 0) const numInterruptTxs = 256 @@ -262,8 +297,9 @@ func testBuildPayload(t *testing.T, noTxPool, interrupt bool, params1559 []byte, b.txPool.Add(txs, false) } - args := newPayloadArgs(b.chain.CurrentBlock().Hash(), params1559, minBaseFee) + args := newPayloadArgs(b.chain.CurrentBlock().Hash(), config) args.NoTxPool = noTxPool + args.EIP1559Params = params1559 // payload resolution now interrupts block building, so we have to // wait for the payloading building process to build its first block @@ -273,6 +309,9 @@ func testBuildPayload(t *testing.T, noTxPool, interrupt bool, params1559 []byte, } verify := func(outer *engine.ExecutionPayloadEnvelope, txs int) { t.Helper() + if config.IsOptimism() { + txs++ // account for dummy deposit tx + } if outer == nil { t.Fatal("ExecutionPayloadEnvelope is nil") } @@ -318,7 +357,7 @@ func testBuildPayload(t *testing.T, noTxPool, interrupt bool, params1559 []byte, } if versionByte == eip1559.MinBaseFeeExtraDataVersionByte { buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, *minBaseFee) + binary.BigEndian.PutUint64(buf, *args.MinBaseFee) expected = append(expected, buf...) } } @@ -345,7 +384,7 @@ func testBuildPayload(t *testing.T, noTxPool, interrupt bool, params1559 []byte, if e != uint64(expectedElasticity) { t.Fatalf("elasticity doesn't match. want: %d, got %d", expectedElasticity, e) } - require.Equal(t, minBaseFee, extractedMinBaseFee, "minBaseFee doesn't match") + require.Equal(t, args.MinBaseFee, extractedMinBaseFee, "minBaseFee doesn't match") } if noTxPool { @@ -381,7 +420,7 @@ func testDAFilters(t *testing.T, maxDATxSize, maxDABlockSize *big.Int, expectedT txs := genTxs(1, numDAFilterTxs) b.txPool.Add(txs, false) - args := newPayloadArgs(b.chain.CurrentBlock().Hash(), validEIP1559Params, &zero) + args := newPayloadArgs(b.chain.CurrentBlock().Hash(), config) args.NoTxPool = false payload, err := w.buildPayload(args, false) @@ -390,65 +429,27 @@ func testDAFilters(t *testing.T, maxDATxSize, maxDABlockSize *big.Int, expectedT } payload.WaitFull() result := payload.ResolveFull().ExecutionPayload - if len(result.Transactions) != expectedTxCount { + if len(result.Transactions) != expectedTxCount+1 { // account for dummy deposit tx t.Fatalf("Unexpected transaction set: got %d, expected %d", len(result.Transactions), expectedTxCount) } } -func testBuildPayloadWrongConfig(t *testing.T, params1559 []byte, config *params.ChainConfig) { +func testBuildPayloadError(t *testing.T, config *params.ChainConfig, expErrStr string, mod func(*BuildPayloadArgs)) { t.Parallel() db := rawdb.NewMemoryDatabase() - wrongConfig := *config - if len(params1559) != 0 { - // deactivate holocene and jovian and make sure non-empty params get rejected - wrongConfig.HoloceneTime = nil - wrongConfig.JovianTime = nil - } - w, b := newTestWorker(t, &wrongConfig, ethash.NewFaker(), db, 0) - - args := newPayloadArgs(b.chain.CurrentBlock().Hash(), params1559, &zero) - payload, err := w.buildPayload(args, false) - if err == nil && (payload == nil || payload.err == nil) { - t.Fatalf("expected error, got none") - } -} - -func TestBuildPayloadInvalidHoloceneParams(t *testing.T) { - t.Parallel() - db := rawdb.NewMemoryDatabase() - config := holoceneConfig() w, b := newTestWorker(t, config, ethash.NewFaker(), db, 0) - // 0 denominators shouldn't be allowed - badParams := eip1559.EncodeHolocene1559Params(0, 6) - - args := newPayloadArgs(b.chain.CurrentBlock().Hash(), badParams, &zero) + args := newPayloadArgs(b.chain.CurrentBlock().Hash(), config) + mod(args) payload, err := w.buildPayload(args, false) - if err == nil && (payload == nil || payload.err == nil) { - t.Fatalf("expected error, got none") - } -} - -func TestBuildPayloadInvalidJovianBuildPayloadArgs(t *testing.T) { - t.Parallel() - db := rawdb.NewMemoryDatabase() - config := jovianConfig() - w, b := newTestWorker(t, config, ethash.NewFaker(), db, 0) - - // 0 denominators shouldn't be allowed - badParams := eip1559.EncodeHolocene1559Params(0, 6) - - args := newPayloadArgs(b.chain.CurrentBlock().Hash(), badParams, &zero) - payload, err := w.buildPayload(args, false) - if err == nil && (payload == nil || payload.err == nil) { + require.Nil(t, payload) + if err != nil { + require.ErrorContains(t, err, expErrStr) + } else if payload.err != nil { + require.ErrorContains(t, payload.err, expErrStr) + } else { t.Fatalf("expected error, got none") } - - // missing minBaseFee is wrong input, panics - args = newPayloadArgs(b.chain.CurrentBlock().Hash(), validEIP1559Params, nil) - require.Panics(t, func() { - w.buildPayload(args, false) - }) } func genTxs(startNonce, count uint64) types.Transactions { diff --git a/miner/worker.go b/miner/worker.go index afd4b5c002..b8729588f7 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -74,8 +74,9 @@ type environment struct { coinbase common.Address evm *vm.EVM - // OP-Stack addition: calldata footprint - daFootprint uint64 + // OP-Stack addition: DA footprint block limit + daFootprintGasScalar uint16 + daFootprint uint64 header *types.Header txs []*types.Transaction @@ -202,7 +203,7 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay } // OP-Stack addition: Jovian maxes the block.gasUsed with the calldata footprint - if miner.chainConfig.IsJovian(work.header.Time) && work.daFootprint > work.header.GasUsed { + if miner.chainConfig.IsDAFootprintBlockLimit(work.header.Time) && work.daFootprint > work.header.GasUsed { work.header.GasUsed = work.daFootprint } @@ -313,10 +314,13 @@ func (miner *Miner) prepareWork(genParams *generateParams, witness bool) (*envir } if genParams.gasLimit != nil { // override gas limit if specified header.GasLimit = *genParams.gasLimit - } else if miner.chain.Config().Optimism != nil && miner.config.GasCeil != 0 { + } else if miner.chain.Config().IsOptimism() && miner.config.GasCeil != 0 { // configure the gas limit of pending blocks with the miner gas limit config when using optimism header.GasLimit = miner.config.GasCeil } + if miner.chainConfig.IsMinBaseFee(header.Time) && genParams.minBaseFee == nil { + return nil, errors.New("missing minBaseFee") + } if cfg := miner.chainConfig; cfg.IsHolocene(header.Time) { if err := eip1559.ValidateHolocene1559Params(genParams.eip1559Params); err != nil { return nil, err @@ -357,6 +361,15 @@ func (miner *Miner) prepareWork(genParams *generateParams, witness bool) (*envir return nil, err } env.noTxs = genParams.noTxs + if miner.chainConfig.IsDAFootprintBlockLimit(parent.Time) { + if len(genParams.txs) == 0 || !genParams.txs[0].IsDepositTx() { + return nil, errors.New("missing L1 attributes deposit transaction") + } + env.daFootprintGasScalar, err = types.ExtractDAFootprintGasScalar(genParams.txs[0].Data()) + if err != nil { + return nil, err + } + } if header.ParentBeaconRoot != nil { core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, env.evm) } @@ -488,8 +501,6 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (* return receipt, err } -var minTransactionDAFootprint = new(big.Int).Mul(types.MinTransactionSize, big.NewInt(params.DAFootprintGasScalar)) - func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error { var ( isCancun = miner.chainConfig.IsCancun(env.header.Number, env.header.Time) @@ -501,8 +512,8 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran // OP-Stack additions: throttling and DA footprint limit blockDABytes := new(big.Int) - daFootprintLeft := big.NewInt(int64(gasLimit)) - isJovian := miner.chainConfig.IsJovian(env.header.Time) + isJovian := miner.chainConfig.IsDAFootprintBlockLimit(env.header.Time) + minTransactionDAFootprint := types.MinTransactionSize.Uint64() * uint64(env.daFootprintGasScalar) for { // Check interruption signal and abort building if it's fired. @@ -516,11 +527,14 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) break } + + daFootprintLeft := gasLimit - env.daFootprint // If we don't have enough DA space for any further transactions then we're done. - if isJovian && daFootprintLeft.Cmp(minTransactionDAFootprint) < 0 { + if isJovian && daFootprintLeft < minTransactionDAFootprint { log.Debug("Not enough DA space for further transactions", "have", daFootprintLeft, "want", minTransactionDAFootprint) break } + // If we don't have enough blob space for any further blob transactions, // skip that list altogether if !blobTxs.Empty() && env.blobs >= eip4844.MaxBlobsPerBlock(miner.chainConfig, env.header.Time) { @@ -571,12 +585,12 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran } // OP-Stack addition: Jovian DA footprint limit - var txDAFootprint *big.Int + var txDAFootprint uint64 // Note that commitTransaction is only called after deposit transactions have already been committed, // so we don't need to resolve the transaction here and exclude deposits. if isJovian { - txDAFootprint = new(big.Int).Mul(ltx.DABytes, big.NewInt(params.DAFootprintGasScalar)) - if daFootprintLeft.Cmp(txDAFootprint) < 0 { + txDAFootprint = ltx.DABytes.Uint64() * uint64(env.daFootprintGasScalar) + if daFootprintLeft < txDAFootprint { log.Debug("Not enough DA space left for transaction", "hash", ltx.Hash, "left", daFootprintLeft, "needed", txDAFootprint) txs.Pop() continue @@ -656,8 +670,7 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran // Everything ok, collect the logs and shift in the next transaction from the same account blockDABytes = daBytesAfter if isJovian { - daFootprintLeft.Sub(daFootprintLeft, txDAFootprint) - env.daFootprint += txDAFootprint.Uint64() // note, it's guaranteed to not overflow because of the max calldata size + env.daFootprint += txDAFootprint } txs.Shift() diff --git a/params/opstack_params.go b/params/opstack_params.go deleted file mode 100644 index a90e32d97d..0000000000 --- a/params/opstack_params.go +++ /dev/null @@ -1,5 +0,0 @@ -package params - -// Jovian - -const DAFootprintGasScalar = 400 From 5c6d276814f2cce1d4dda25ed9e5a3a1c52e59a4 Mon Sep 17 00:00:00 2001 From: George Knee Date: Thu, 16 Oct 2025 10:14:51 +0100 Subject: [PATCH 03/17] core/types: implement operator fee fix (Jovian) (#696) * fix!: multiply operatorFeeScalar by 100 instead of dividing by 1e6 * apply comments * apply comments * remove dup IsOperatorFeeFix --------- Co-authored-by: fakedev9999 --- core/types/rollup_cost.go | 30 +++++++++++++++++++++++++++--- core/types/rollup_cost_test.go | 31 ++++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/core/types/rollup_cost.go b/core/types/rollup_cost.go index 82ff0a6a61..0c628d8ace 100644 --- a/core/types/rollup_cost.go +++ b/core/types/rollup_cost.go @@ -83,6 +83,7 @@ var ( // attributes OperatorFeeParamsSlot = common.BigToHash(big.NewInt(8)) + oneHundred = big.NewInt(100) oneMillion = big.NewInt(1_000_000) ecotoneDivisor = big.NewInt(1_000_000 * 16) fjordDivisor = big.NewInt(1_000_000_000_000) @@ -232,7 +233,11 @@ func NewOperatorCostFunc(config *params.ChainConfig, statedb StateGetter) Operat } operatorFeeScalar, operatorFeeConstant := ExtractOperatorFeeParams(operatorFeeParams) - return newOperatorCostFunc(operatorFeeScalar, operatorFeeConstant) + // Return the Operator Fee fix version if the feature is active + if config.IsOperatorFeeFix(blockTime) { + return newOperatorCostFuncOperatorFeeFix(operatorFeeScalar, operatorFeeConstant) + } + return newOperatorCostFuncIsthmus(operatorFeeScalar, operatorFeeConstant) } return func(gas uint64, blockTime uint64) *uint256.Int { @@ -245,7 +250,8 @@ func NewOperatorCostFunc(config *params.ChainConfig, statedb StateGetter) Operat } } -func newOperatorCostFunc(operatorFeeScalar *big.Int, operatorFeeConstant *big.Int) operatorCostFunc { +// newOperatorCostFuncIsthmus returns the operator cost function introduced with Isthmus. +func newOperatorCostFuncIsthmus(operatorFeeScalar *big.Int, operatorFeeConstant *big.Int) operatorCostFunc { return func(gas uint64) *uint256.Int { fee := new(big.Int).SetUint64(gas) fee = fee.Mul(fee, operatorFeeScalar) @@ -254,7 +260,25 @@ func newOperatorCostFunc(operatorFeeScalar *big.Int, operatorFeeConstant *big.In feeU256, overflow := uint256.FromBig(fee) if overflow { - // This should never happen, as (u64.max * u32.max / 1e6) + u64.max is an int of bit length 77 + // This should never happen, as ((u64.max * u32.max) / 1e6) + u64.max fits in 77 bits + panic("overflow in operator cost calculation") + } + + return feeU256 + } +} + +// newOperatorCostFuncOperatorFeeFix returns the operator cost function for the operator fee fix feature. +func newOperatorCostFuncOperatorFeeFix(operatorFeeScalar *big.Int, operatorFeeConstant *big.Int) operatorCostFunc { + return func(gas uint64) *uint256.Int { + fee := new(big.Int).SetUint64(gas) + fee = fee.Mul(fee, operatorFeeScalar) + fee = fee.Mul(fee, oneHundred) + fee = fee.Add(fee, operatorFeeConstant) + + feeU256, overflow := uint256.FromBig(fee) + if overflow { + // This should never happen, as (u64.max * u32.max * 100) + u64.max fits in 103 bits panic("overflow in operator cost calculation") } diff --git a/core/types/rollup_cost_test.go b/core/types/rollup_cost_test.go index b595a14f62..c07b53f023 100644 --- a/core/types/rollup_cost_test.go +++ b/core/types/rollup_cost_test.go @@ -33,6 +33,7 @@ var ( // the emptyTx is out of bounds for the linear regression so it uses the minimum size fjordFee = big.NewInt(3203000) // 100_000_000 * (2 * 1000 * 1e6 * 16 + 3 * 10 * 1e6) / 1e12 ithmusOperatorFee = uint256.NewInt(1256417826611659930) // 1618 * 1439103868 / 1e6 + 1256417826609331460 + jovianOperatorFee = uint256.NewInt(1256650673615173860) // 1618 * 1439103868 * 100 + 1256417826609331460 bedrockGas = big.NewInt(1618) regolithGas = big.NewInt(530) // 530 = 1618 - (16*68) @@ -461,6 +462,13 @@ func TestNewOperatorCostFunc(t *testing.T) { fee = costFunc(bedrockGas.Uint64(), time) require.NotNil(t, fee) require.Equal(t, ithmusOperatorFee, fee) + + // emptyTx fee w/ jovian config should be not 0 + config.JovianTime = &time + costFunc = NewOperatorCostFunc(config, statedb) + fee = costFunc(bedrockGas.Uint64(), time) + require.NotNil(t, fee) + require.Equal(t, jovianOperatorFee, fee) } func TestFlzCompressLen(t *testing.T) { @@ -516,14 +524,16 @@ var emptyTxWithGas = NewTransaction( // combines the L1 cost and operator cost. func TestTotalRollupCostFunc(t *testing.T) { zero := uint64(0) - later := uint64(10) + isthmusTime := uint64(10) + jovianTime := uint64(20) config := ¶ms.ChainConfig{ Optimism: params.OptimismTestConfig.Optimism, RegolithTime: &zero, EcotoneTime: &zero, FjordTime: &zero, HoloceneTime: &zero, - IsthmusTime: &later, + IsthmusTime: &isthmusTime, + JovianTime: &jovianTime, } statedb := &testStateGetter{ baseFee: baseFee, @@ -537,13 +547,24 @@ func TestTotalRollupCostFunc(t *testing.T) { } costFunc := NewTotalRollupCostFunc(config, statedb) - cost := costFunc(emptyTxWithGas, later-1) + + // Pre-Isthmus: only L1 cost + cost := costFunc(emptyTxWithGas, isthmusTime-1) require.NotNil(t, cost) expCost := uint256.MustFromBig(fjordFee) require.Equal(t, expCost, cost, "pre-Isthmus total rollup cost should only contain L1 cost") - cost = costFunc(emptyTxWithGas, later+1) + // Isthmus: L1 cost + Isthmus operator cost + cost = costFunc(emptyTxWithGas, isthmusTime+1) require.NotNil(t, cost) + expCost = uint256.MustFromBig(fjordFee) expCost.Add(expCost, ithmusOperatorFee) - require.Equal(t, expCost, cost, "Isthmus total rollup cost should contain L1 cost and operator cost") + require.Equal(t, expCost, cost, "Isthmus total rollup cost should contain L1 cost and Isthmus operator cost") + + // Jovian: L1 cost + fixed operator cost + cost = costFunc(emptyTxWithGas, jovianTime+1) + require.NotNil(t, cost) + expCost = uint256.MustFromBig(fjordFee) + expCost.Add(expCost, jovianOperatorFee) + require.Equal(t, expCost, cost, "Jovian total rollup cost should contain L1 cost and Jovian operator cost") } From e061c298e04bd2cbb989458fc8e1786eba39503e Mon Sep 17 00:00:00 2001 From: Sebastian Stammler Date: Thu, 16 Oct 2025 16:55:44 +0200 Subject: [PATCH 04/17] consensus/beacon: Fix OP Legacy header verification dispatch (#697) --- consensus/beacon/consensus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index a8aee6f774..a2364eab16 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -125,7 +125,7 @@ func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *ty // Check >0 TDs with pre-merge, --0 TDs with post-merge rules if header.Difficulty.Sign() > 0 || // OP-Stack: transitioned networks must use legacy consensus pre-Bedrock - cfg.IsOptimismBedrock(header.Number) { + cfg.IsOptimismPreBedrock(header.Number) { return beacon.ethone.VerifyHeader(chain, header) } return beacon.verifyHeader(chain, header, parent) From 6658a36c9862694c82eabd803ca885370667401f Mon Sep 17 00:00:00 2001 From: Sebastian Stammler Date: Fri, 17 Oct 2025 19:49:28 +0200 Subject: [PATCH 05/17] all: Store DA footprint in blob gas used header field (#694) --- consensus/beacon/consensus.go | 10 ++++ consensus/misc/eip1559/eip1559.go | 26 +++++++--- consensus/misc/eip1559/eip1559_test.go | 69 ++++++++++++++++---------- consensus/misc/eip4844/eip4844.go | 16 +++--- core/block_validator.go | 19 ++++++- core/chain_makers.go | 8 --- core/state_processor.go | 8 --- core/types/block.go | 1 + core/types/rollup_cost.go | 21 ++++---- miner/miner_optimism_test.go | 52 ++++++++++++++----- miner/worker.go | 21 ++++---- 11 files changed, 158 insertions(+), 93 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index a2364eab16..366cce89fd 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -410,6 +410,16 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea } } + // Store DA footprint in BlobGasUsed header field if it hasn't already been set yet. + // Builder code may already calculate it during block building to avoid recalculating it here. + if chain.Config().IsDAFootprintBlockLimit(header.Time) && (header.BlobGasUsed == nil || *header.BlobGasUsed == 0) { + daFootprint, err := types.CalcDAFootprint(body.Transactions) + if err != nil { + return nil, fmt.Errorf("error calculating DA footprint: %w", err) + } + header.BlobGasUsed = &daFootprint + } + // Assemble the final block. block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil), chain.Config()) diff --git a/consensus/misc/eip1559/eip1559.go b/consensus/misc/eip1559/eip1559.go index 373d6f83ef..eb5dfed657 100644 --- a/consensus/misc/eip1559/eip1559.go +++ b/consensus/misc/eip1559/eip1559.go @@ -36,7 +36,7 @@ func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Heade if !config.IsLondon(parent.Number) { parentGasLimit = parent.GasLimit * config.ElasticityMultiplier() } - if config.Optimism == nil { // gasLimit can adjust instantly in optimism + if !config.IsOptimism() { // OP Stack gasLimit can adjust instantly if err := misc.VerifyGaslimit(parentGasLimit, header.GasLimit); err != nil { return err } @@ -75,7 +75,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header, time uint64) } // OPStack addition: calculate the base fee using the upstream code. - baseFee := calcBaseFeeInner(parent, elasticity, denominator) + baseFee := calcBaseFeeInner(config, parent, elasticity, denominator) // OPStack addition: enforce minimum base fee. // If the minimum base fee is 0, this has no effect. @@ -89,10 +89,20 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header, time uint64) return baseFee } -func calcBaseFeeInner(parent *types.Header, elasticity uint64, denominator uint64) *big.Int { +func calcBaseFeeInner(config *params.ChainConfig, parent *types.Header, elasticity uint64, denominator uint64) *big.Int { parentGasTarget := parent.GasLimit / elasticity - // If the parent gasUsed is the same as the target, the baseFee remains unchanged. - if parent.GasUsed == parentGasTarget { + parentGasMetered := parent.GasUsed + if config.IsDAFootprintBlockLimit(parent.Time) { + if parent.BlobGasUsed == nil { + panic("Jovian parent block has nil BlobGasUsed") + } else if *parent.BlobGasUsed > parent.GasUsed { + // Jovian updates the base fee based on the maximum of total transactions gas used and total DA footprint (which is + // stored in the BlobGasUsed field of the header). + parentGasMetered = *parent.BlobGasUsed + } + } + // If the parent gasMetered is the same as the target, the baseFee remains unchanged. + if parentGasMetered == parentGasTarget { return new(big.Int).Set(parent.BaseFee) } @@ -101,10 +111,10 @@ func calcBaseFeeInner(parent *types.Header, elasticity uint64, denominator uint6 denom = new(big.Int) ) - if parent.GasUsed > parentGasTarget { + if parentGasMetered > parentGasTarget { // If the parent block used more gas than its target, the baseFee should increase. // max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator) - num.SetUint64(parent.GasUsed - parentGasTarget) + num.SetUint64(parentGasMetered - parentGasTarget) num.Mul(num, parent.BaseFee) num.Div(num, denom.SetUint64(parentGasTarget)) num.Div(num, denom.SetUint64(denominator)) @@ -115,7 +125,7 @@ func calcBaseFeeInner(parent *types.Header, elasticity uint64, denominator uint6 } else { // Otherwise if the parent block used less gas than its target, the baseFee should decrease. // max(0, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator) - num.SetUint64(parentGasTarget - parent.GasUsed) + num.SetUint64(parentGasTarget - parentGasMetered) num.Mul(num, parent.BaseFee) num.Div(num, denom.SetUint64(parentGasTarget)) num.Div(num, denom.SetUint64(denominator)) diff --git a/consensus/misc/eip1559/eip1559_test.go b/consensus/misc/eip1559/eip1559_test.go index b8139f963b..9e86341099 100644 --- a/consensus/misc/eip1559/eip1559_test.go +++ b/consensus/misc/eip1559/eip1559_test.go @@ -57,17 +57,19 @@ func config() *params.ChainConfig { return config } -var TestCanyonTime = uint64(10) -var TestHoloceneTime = uint64(12) -var TestJovianTime = uint64(14) +var ( + testCanyonTime = uint64(10) + testHoloceneTime = uint64(12) + testJovianTime = uint64(14) +) func opConfig() *params.ChainConfig { config := copyConfig(params.TestChainConfig) config.LondonBlock = big.NewInt(5) eip1559DenominatorCanyon := uint64(250) - config.CanyonTime = &TestCanyonTime - config.HoloceneTime = &TestHoloceneTime - config.JovianTime = &TestJovianTime + config.CanyonTime = &testCanyonTime + config.HoloceneTime = &testHoloceneTime + config.JovianTime = &testJovianTime config.Optimism = ¶ms.OptimismConfig{ EIP1559Elasticity: 6, EIP1559Denominator: 50, @@ -227,59 +229,74 @@ func TestCalcBaseFeeOptimismHolocene(t *testing.T) { // TestCalcBaseFeeJovian tests that the minimum base fee is enforced // when the computed base fee is less than the minimum base fee, // if the feature is active and not enforced otherwise. +// It also tests that the base fee udpate will take the DA footprint as stored +// in the blob gas used field into account if it is larger than the gas used +// field. func TestCalcBaseFeeJovian(t *testing.T) { parentGasLimit := uint64(30_000_000) denom := uint64(50) elasticity := uint64(3) + parentGasTarget := parentGasLimit / elasticity + const zeroParentBlobGasUsed = 0 - preJovian := TestJovianTime - 1 - postJovian := TestJovianTime + preJovian := testJovianTime - 1 + postJovian := testJovianTime tests := []struct { - parentBaseFee int64 - parentGasUsed uint64 - parentTime uint64 - minBaseFee uint64 - expectedBaseFee uint64 + parentBaseFee int64 + parentGasUsed uint64 + parentBlobGasUsed uint64 + parentTime uint64 + minBaseFee uint64 + expectedBaseFee uint64 }{ // Test 0: gas used is below target, and the new calculated base fee is very low. // But since we are pre Jovian, we don't enforce the minBaseFee. - {1, parentGasLimit/elasticity - 1_000_000, preJovian, 1e9, 1}, + {1, parentGasTarget - 1_000_000, zeroParentBlobGasUsed, preJovian, 1e9, 1}, // Test 1: gas used is exactly the target gas, but the base fee is set too low so // the base fee is expected to be the minBaseFee - {1, parentGasLimit / elasticity, postJovian, 1e9, 1e9}, + {1, parentGasTarget, zeroParentBlobGasUsed, postJovian, 1e9, 1e9}, // Test 2: gas used exceeds gas target, but the new calculated base fee is still // too low so the base fee is expected to be the minBaseFee - {1, parentGasLimit/elasticity + 1_000_000, postJovian, 1e9, 1e9}, + {1, parentGasTarget + 1_000_000, zeroParentBlobGasUsed, postJovian, 1e9, 1e9}, // Test 3: gas used exceeds gas target, but the new calculated base fee is higher // than the minBaseFee, so don't enforce minBaseFee. See the calculation below: // gasUsedDelta = gasUsed - parentGasTarget = 20_000_000 - 30_000_000 / 3 = 10_000_000 // 2e9 * 10_000_000 / 10_000_000 / 50 = 40_000_000 // 2e9 + 40_000_000 = 2_040_000_000, which is greater than minBaseFee - {2e9, parentGasLimit/elasticity + 10_000_000, postJovian, 1e9, 2_040_000_000}, + {2e9, parentGasTarget + 10_000_000, zeroParentBlobGasUsed, postJovian, 1e9, 2_040_000_000}, // Test 4: gas used is below target, but the new calculated base fee is still // too low so the base fee is expected to be the minBaseFee - {1, parentGasLimit/elasticity - 1_000_000, postJovian, 1e9, 1e9}, + {1, parentGasTarget - 1_000_000, zeroParentBlobGasUsed, postJovian, 1e9, 1e9}, // Test 5: gas used is below target, and the new calculated base fee is higher // than the minBaseFee, so don't enforce minBaseFee. See the calculation below: // gasUsedDelta = gasUsed - parentGasTarget = 9_000_000 - 30_000_000 / 3 = -1_000_000 // 2_097_152 * -1_000_000 / 10_000_000 / 50 = -4194.304 // 2_097_152 - 4194.304 = 2_092_957.696, which is greater than minBaseFee - {2_097_152, parentGasLimit/elasticity - 1_000_000, postJovian, 2e6, 2_092_958}, + {2_097_152, parentGasTarget - 1_000_000, zeroParentBlobGasUsed, postJovian, 2e6, 2_092_958}, // Test 6: parent base fee already at minimum, below target => no change - {1e4, parentGasLimit/elasticity - 1, postJovian, 1e4, 1e4}, + {1e4, parentGasTarget - 1, zeroParentBlobGasUsed, postJovian, 1e4, 1e4}, // Test 7: parent base fee already at minimum, above target => small increase as usual - {1e4, parentGasLimit/elasticity + 1, postJovian, 1e4, 1e4 + 1}, + {1e4, parentGasTarget + 1, zeroParentBlobGasUsed, postJovian, 1e4, 1e4 + 1}, + + // Test 8: Pre-Jovian: parent base fee already at minimum, gas used at target, blob gas used at limit + // => no increase, minBaseFee ignored, high blob gas used ignored + {1e4, parentGasTarget, parentGasLimit, preJovian, 1e6, 1e4}, + // Test 9: parent base fee already at minimum, gas used at target, da footprint above target => small increase + {1e4, parentGasTarget, parentGasTarget + 1, postJovian, 1e4, 1e4 + 1}, + // Test 10: Test 3, but with high blob gas used instead of gas used + {2e9, parentGasTarget, parentGasTarget + 10_000_000, postJovian, 1e9, 2_040_000_000}, } for i, test := range tests { testName := fmt.Sprintf("test %d", i) t.Run(testName, func(t *testing.T) { parent := &types.Header{ - Number: common.Big32, - GasLimit: parentGasLimit, - GasUsed: test.parentGasUsed, - BaseFee: big.NewInt(test.parentBaseFee), - Time: test.parentTime, + Number: common.Big32, + GasLimit: parentGasLimit, + GasUsed: test.parentGasUsed, + BlobGasUsed: &test.parentBlobGasUsed, + BaseFee: big.NewInt(test.parentBaseFee), + Time: test.parentTime, } parent.Extra = EncodeOptimismExtraData(opConfig(), test.parentTime, denom, elasticity, &test.minBaseFee) have, want := CalcBaseFee(opConfig(), parent, parent.Time+2), big.NewInt(int64(test.expectedBaseFee)) diff --git a/consensus/misc/eip4844/eip4844.go b/consensus/misc/eip4844/eip4844.go index 0b0941390a..a7df6f2cbc 100644 --- a/consensus/misc/eip4844/eip4844.go +++ b/consensus/misc/eip4844/eip4844.go @@ -110,12 +110,16 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade return errors.New("header is missing blobGasUsed") } - // Verify that the blob gas used remains within reasonable limits. - if !config.IsOptimism() && *header.BlobGasUsed > bcfg.maxBlobGas() { - return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, bcfg.maxBlobGas()) - } - if *header.BlobGasUsed%params.BlobTxBlobGasPerBlob != 0 { - return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, params.BlobTxBlobGasPerBlob) + // OP Stack sets a zero blobGasUsed pre-Jovian. Post-Jovian, it stores the DA footprint, which is + // probably not a multiple of [params.BlobTxBlobGasPerBlob]. + if !config.IsOptimism() { + // Verify that the blob gas used remains within reasonable limits. + if *header.BlobGasUsed > bcfg.maxBlobGas() { + return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, bcfg.maxBlobGas()) + } + if *header.BlobGasUsed%params.BlobTxBlobGasPerBlob != 0 { + return fmt.Errorf("blob gas used %d not a multiple of blob gas per blob %d", header.BlobGasUsed, params.BlobTxBlobGasPerBlob) + } } // Verify the excessBlobGas is correct based on the parent header diff --git a/core/block_validator.go b/core/block_validator.go index 33987fa369..05c03235ed 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -106,7 +106,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { } // Check blob gas usage. - if header.BlobGasUsed != nil { + if !v.config.IsOptimism() && header.BlobGasUsed != nil { if want := *header.BlobGasUsed / params.BlobTxBlobGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated return fmt.Errorf("blob gas used mismatch (header %v, calculated %v)", *header.BlobGasUsed, blobs*params.BlobTxBlobGasPerBlob) } @@ -116,6 +116,23 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { } } + // OP Stack Jovian DA footprint block limit. + if v.config.IsDAFootprintBlockLimit(header.Time) { + if header.BlobGasUsed == nil { + return errors.New("nil blob gas used in post-Jovian block header, should store DA footprint") + } + blobGasUsed := *header.BlobGasUsed + daFootprint, err := types.CalcDAFootprint(block.Transactions()) + if err != nil { + return fmt.Errorf("failed to calculate DA footprint: %w", err) + } else if blobGasUsed != daFootprint { + return fmt.Errorf("invalid DA footprint in blobGasUsed field (remote: %d local: %d)", blobGasUsed, daFootprint) + } + if daFootprint > block.GasLimit() { + return fmt.Errorf("DA footprint %d exceeds block gas limit %d", daFootprint, block.GasLimit()) + } + } + // Ancestor block must be known. if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) { diff --git a/core/chain_makers.go b/core/chain_makers.go index f4adb78504..4a64830ef8 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -421,14 +421,6 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse b.header.RequestsHash = &reqHash } - if config.IsDAFootprintBlockLimit(b.header.Time) { - gasUsed, err := types.CalcGasUsedJovian(b.txs, b.header.GasUsed) - if err != nil { - panic(err) - } - b.header.GasUsed = gasUsed - } - body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals} block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts) if err != nil { diff --git a/core/state_processor.go b/core/state_processor.go index 94124e845e..c29d0c24de 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -129,14 +129,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg requests = [][]byte{} } - if p.config.IsDAFootprintBlockLimit(block.Time()) { - gasUsed, err := types.CalcGasUsedJovian(block.Transactions(), *usedGas) - if err != nil { - return nil, fmt.Errorf("failed to calculate Jovian gas used: %w", err) - } - *usedGas = gasUsed - } - // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) p.chain.engine.Finalize(p.chain, header, tracingStateDB, block.Body()) diff --git a/core/types/block.go b/core/types/block.go index 31d7c6a9d8..1a3f0f1773 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -96,6 +96,7 @@ type Header struct { WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` // BlobGasUsed was added by EIP-4844 and is ignored in legacy headers. + // OP Stack stores the DA footprint in this field starting with the Jovian fork. BlobGasUsed *uint64 `json:"blobGasUsed" rlp:"optional"` // ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers. diff --git a/core/types/rollup_cost.go b/core/types/rollup_cost.go index 0c628d8ace..ffbdd2098f 100644 --- a/core/types/rollup_cost.go +++ b/core/types/rollup_cost.go @@ -556,10 +556,11 @@ func ExtractDAFootprintGasScalar(data []byte) (uint16, error) { return daFootprintGasScalar, nil } -// CalcGasUsedJovian calculates the gas used for an OP Stack chain. -// Jovian introduces a DA footprint block limit, which potentially increases the gasUsed. -// CalcGasUsedJovian must not be called for pre-Jovian blocks. -func CalcGasUsedJovian(txs []*Transaction, evmGasUsed uint64) (uint64, error) { +// CalcDAFootprint calculates the total DA footprint of a block for an OP Stack chain. +// Jovian introduces a DA footprint block limit which is stored in the BlobGasUsed header field and that is taken +// into account during base fee updates. +// CalcDAFootprint must not be called for pre-Jovian blocks. +func CalcDAFootprint(txs []*Transaction) (uint64, error) { if len(txs) == 0 || !txs[0].IsDepositTx() { return 0, errors.New("missing deposit transaction") } @@ -572,25 +573,21 @@ func CalcGasUsedJovian(txs []*Transaction, evmGasUsed uint64) (uint64, error) { // sufficient to check last transaction because deposits precede non-deposit txs return 0, errors.New("unexpected non-deposit transactions in Jovian activation block") } - return evmGasUsed, nil + return 0, nil } // ExtractDAFootprintGasScalar catches all invalid lengths daFootprintGasScalar, err := ExtractDAFootprintGasScalar(data) if err != nil { return 0, err } - var cumulativeDAFootprint uint64 + var daFootprint uint64 for _, tx := range txs { if tx.IsDepositTx() { continue } - cumulativeDAFootprint += tx.RollupCostData().EstimatedDASize().Uint64() + daFootprint += tx.RollupCostData().EstimatedDASize().Uint64() * uint64(daFootprintGasScalar) } - daFootprint := uint64(daFootprintGasScalar) * cumulativeDAFootprint - if evmGasUsed < daFootprint { - return daFootprint, nil - } - return evmGasUsed, nil + return daFootprint, nil } // L1Cost computes the the data availability fee for transactions in blocks prior to the Ecotone diff --git a/miner/miner_optimism_test.go b/miner/miner_optimism_test.go index ce1d59513b..4be405f159 100644 --- a/miner/miner_optimism_test.go +++ b/miner/miner_optimism_test.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" @@ -21,15 +22,16 @@ const testDAFootprintGasScalar = 400 // transactions and then imports the block into the chain, asserting that // execution succeeds. func TestDAFootprintMining(t *testing.T) { - requireTxGas := func(t *testing.T, block *types.Block, receipts []*types.Receipt) { + requirePreJovianBehavior := func(t *testing.T, block *types.Block, receipts []*types.Receipt) { var txGas uint64 for _, receipt := range receipts { txGas += receipt.GasUsed } require.Equal(t, txGas, block.GasUsed(), "total tx gas used should be equal to block gas used") + require.Zero(t, *block.Header().BlobGasUsed, "expected 0 blob gas used") } - requireDAFootprint := func(t *testing.T, block *types.Block, receipts []*types.Receipt) { + requireLargeDAFootprintBehavior := func(t *testing.T, block *types.Block, receipts []*types.Receipt) { var ( txGas uint64 daFootprint uint64 @@ -45,30 +47,56 @@ func TestDAFootprintMining(t *testing.T) { } daFootprint += txs[i].RollupCostData().EstimatedDASize().Uint64() * testDAFootprintGasScalar } - require.Less(t, txGas, block.GasUsed(), "total tx gas used must be smaller than block gas used") - require.Equal(t, daFootprint, block.GasUsed(), "total DA footprint used should be equal to block gas used") + require.Equal(t, txGas, block.GasUsed(), "total tx gas used should be equal to block gas used") + require.Greater(t, daFootprint, block.GasUsed(), "total DA footprint used should be greater than block gas used") + require.LessOrEqual(t, daFootprint, block.GasLimit(), "total DA footprint used should be less or equal block gas limit") } + t.Run("jovian-one-min-tx", func(t *testing.T) { + testMineAndExecute(t, 0, jovianConfig(), func(t *testing.T, _ *core.BlockChain, block *types.Block, receipts []*types.Receipt) { + require.Len(t, receipts, 2) // 1 test pending tx and 1 deposit tx + requireLargeDAFootprintBehavior(t, block, receipts) + + // Double-confirm DA footprint calculation manually in this simple transaction case. + daFootprint, err := types.CalcDAFootprint(block.Transactions()) + require.NoError(t, err, "failed to calculate DA footprint") + require.Equal(t, daFootprint, *block.Header().BlobGasUsed, + "header blob gas used should match calculated DA footprint") + require.Equal(t, testDAFootprintGasScalar*types.MinTransactionSize.Uint64(), daFootprint, + "simple pending transaction should lead to min DA footprint") + }) + }) t.Run("jovian-at-limit", func(t *testing.T) { - testMineAndExecute(t, 17, jovianConfig(), func(t *testing.T, block *types.Block, receipts []*types.Receipt) { + testMineAndExecute(t, 17, jovianConfig(), func(t *testing.T, _ *core.BlockChain, block *types.Block, receipts []*types.Receipt) { require.Len(t, receipts, 19) // including 1 test pending tx and 1 deposit tx - requireDAFootprint(t, block, receipts) + requireLargeDAFootprintBehavior(t, block, receipts) }) }) t.Run("jovian-above-limit", func(t *testing.T) { - testMineAndExecute(t, 18, jovianConfig(), func(t *testing.T, block *types.Block, receipts []*types.Receipt) { + testMineAndExecute(t, 18, jovianConfig(), func(t *testing.T, _ *core.BlockChain, block *types.Block, receipts []*types.Receipt) { require.Len(t, receipts, 19) // same as for 17, because 18th tx from pool shouldn't have been included - requireDAFootprint(t, block, receipts) + requireLargeDAFootprintBehavior(t, block, receipts) }) }) t.Run("isthmus", func(t *testing.T) { - testMineAndExecute(t, 39, isthmusConfig(), func(t *testing.T, block *types.Block, receipts []*types.Receipt) { + testMineAndExecute(t, 39, isthmusConfig(), func(t *testing.T, _ *core.BlockChain, block *types.Block, receipts []*types.Receipt) { require.Len(t, receipts, 41) // including 1 test pending tx and 1 deposit tx - requireTxGas(t, block, receipts) + requirePreJovianBehavior(t, block, receipts) + }) + }) + + t.Run("jovian-invalid-blobGasUsed", func(t *testing.T) { + testMineAndExecute(t, 0, jovianConfig(), func(t *testing.T, bc *core.BlockChain, block *types.Block, receipts []*types.Receipt) { + require.Len(t, receipts, 2) // 1 test pending tx and 1 deposit tx + header := block.Header() + *header.BlobGasUsed += 1 // invalidate blobGasUsed + invalidBlock := block.WithSeal(header) + _, err := bc.InsertChain(types.Blocks{invalidBlock}) + require.ErrorContains(t, err, "invalid DA footprint in blobGasUsed field (remote: 40001 local: 40000)") }) }) } -func testMineAndExecute(t *testing.T, numTxs uint64, cfg *params.ChainConfig, assertFn func(t *testing.T, block *types.Block, receipts []*types.Receipt)) { +func testMineAndExecute(t *testing.T, numTxs uint64, cfg *params.ChainConfig, assertFn func(*testing.T, *core.BlockChain, *types.Block, []*types.Receipt)) { db := rawdb.NewMemoryDatabase() w, b := newTestWorker(t, cfg, beacon.New(ethash.NewFaker()), db, 0) @@ -105,7 +133,7 @@ func testMineAndExecute(t *testing.T, numTxs uint64, cfg *params.ChainConfig, as require.NoError(t, r.err, "block generation failed") require.NotNil(t, r.block, "no block generated") - assertFn(t, r.block, r.receipts) + assertFn(t, b.chain, r.block, r.receipts) // Import the block into the chain, which executes it via StateProcessor. _, err := b.chain.InsertChain(types.Blocks{r.block}) diff --git a/miner/worker.go b/miner/worker.go index b8729588f7..2bab122a20 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -76,7 +76,6 @@ type environment struct { // OP-Stack addition: DA footprint block limit daFootprintGasScalar uint16 - daFootprint uint64 header *types.Header txs []*types.Transaction @@ -202,11 +201,6 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay } } - // OP-Stack addition: Jovian maxes the block.gasUsed with the calldata footprint - if miner.chainConfig.IsDAFootprintBlockLimit(work.header.Time) && work.daFootprint > work.header.GasUsed { - work.header.GasUsed = work.daFootprint - } - body := types.Body{Transactions: work.txs, Withdrawals: genParam.withdrawals} if intr := genParam.interrupt; intr != nil && genParam.isUpdate && intr.Load() != commitInterruptNone { @@ -528,11 +522,14 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran break } - daFootprintLeft := gasLimit - env.daFootprint - // If we don't have enough DA space for any further transactions then we're done. - if isJovian && daFootprintLeft < minTransactionDAFootprint { - log.Debug("Not enough DA space for further transactions", "have", daFootprintLeft, "want", minTransactionDAFootprint) - break + var daFootprintLeft uint64 + if isJovian { + daFootprintLeft = gasLimit - *env.header.BlobGasUsed + // If we don't have enough DA space for any further transactions then we're done. + if daFootprintLeft < minTransactionDAFootprint { + log.Debug("Not enough DA space for further transactions", "have", daFootprintLeft, "want", minTransactionDAFootprint) + break + } } // If we don't have enough blob space for any further blob transactions, @@ -670,7 +667,7 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran // Everything ok, collect the logs and shift in the next transaction from the same account blockDABytes = daBytesAfter if isJovian { - env.daFootprint += txDAFootprint + *env.header.BlobGasUsed += txDAFootprint } txs.Shift() From f31eb355e063292dcd94514b7a47e71306580b17 Mon Sep 17 00:00:00 2001 From: Josh Klopfenstein Date: Wed, 22 Oct 2025 10:25:55 -0700 Subject: [PATCH 06/17] all: update c-kzg-4844 to 2.1.5 (#702) --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index a6dbd8dc30..8707533271 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 - github.com/ethereum/c-kzg-4844/v2 v2.1.0 + github.com/ethereum/c-kzg-4844/v2 v2.1.5 github.com/ethereum/go-verkle v0.2.2 github.com/fatih/color v1.16.0 github.com/ferranbt/fastssz v0.1.4 @@ -62,7 +62,7 @@ require ( github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/status-im/keycard-go v0.2.0 github.com/stretchr/testify v1.10.0 - github.com/supranational/blst v0.3.14 + github.com/supranational/blst v0.3.15 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/urfave/cli/v2 v2.27.5 go.uber.org/automaxprocs v1.5.2 diff --git a/go.sum b/go.sum index 7da7a0894b..69320f34b8 100644 --- a/go.sum +++ b/go.sum @@ -115,8 +115,8 @@ github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= -github.com/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w= -github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= +github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= +github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= @@ -355,8 +355,8 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= -github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.15 h1:rd9viN6tfARE5wv3KZJ9H8e1cg0jXW8syFCcsbHa76o= +github.com/supranational/blst v0.3.15/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= From f305011e5706b21b93c4cdd5fd008c6bfdce175c Mon Sep 17 00:00:00 2001 From: Sam Stokes <35908605+bitwiseguy@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:02:04 -0400 Subject: [PATCH 07/17] superchain: update scr import to include worldchain-sepolia isthmus time (#704) --- superchain-registry-commit.txt | 2 +- superchain/superchain-configs.zip | Bin 3830798 -> 3910346 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/superchain-registry-commit.txt b/superchain-registry-commit.txt index 5ce6d82034..69a0518153 100644 --- a/superchain-registry-commit.txt +++ b/superchain-registry-commit.txt @@ -1 +1 @@ -d56233c1e5254fc2fd769d5b33269502a1fe9ef8 \ No newline at end of file +6748f8050bfab9930abdfc46288f9629af2f7b38 \ No newline at end of file diff --git a/superchain/superchain-configs.zip b/superchain/superchain-configs.zip index c9eb8f0a1238331a32b10607d2f7fe684452350a..933ddf3f157718d9d2688712df6541b137fd3705 100644 GIT binary patch delta 81868 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≪N+0(L1=9C$Lgxt+Ql{xg-0L)# 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 z zA0%Hz^$-{%csd@ znb1X~&Pmw}ryfrL7^A`3gItYVxz3^$SbMqVZs^>67aAZEurit0cm*^0)LAJWV$u494GGtG)+BrHZKp+K)iE0d@+rfnxr} zdMlfJWO)G_72bJ($`a2GvLsIhv*f=1PeXX^D|k$atYeZc2?H;_)KLaAoibz;tOfoI zha2^eWh~5l+0Mk6Rp+*O0)~}x141q?DbgjT49t+PcyK#q1RTzhD3}4A5~;!H#kEnw zNur6r;{L3j@WM8>&Z@Bmza&x>WBHW0CU$VhSB~gbAD7an_Z4i3VklUKsCV8Ebf6uT z)2GYbFud|=MCh38rFtY8(z-d(l47%w4Xt=#PP@pEAuVbpeI}Ho)`+q6`?bH&DkjIj zJMv4 zJeVw(dN7hSU^(28K15P4?{_XKQ2`6-v^(rU@8cnAZD9jp;}!Pdhfoq%vYYuT-+)EB zoD3|1X(iy0Y%sFZ#3DZ1|m zdm!9X4NJC?y)$utF=@DF|^m=8@)B0JHc1%E({CmM~|yg zQC2*ATVSHH@R7C5neJnqB8cXK1X-0LsW_BUZn90N-t^b;Z@;oMJVL$i^AsvP70n3b zA*$v`NHB@bs)f&CmY~J*pCL84@Ch)bi5!qE|Cy5#^3|hM$HXMTy4RK8(m%$BdZN}~ zH68;T4i{zxSU&vT*Qdd(?;p#__`QcophZrm(HflK!LF3pPYZso{mo}0BCJ;7C^XfO zcDh}Q>i(WOTEOnRO>rh#dA$o*DnHwCuGATOcY0#*pAzy1%0YnpCsNSoq$VdxAMINa9}ndDF|S#ciJD;Lt#K3 zzONJ%4NCpJSnjK5XzYU3T|W2^I{t?~KW=)sJ`mYE9rT-(>@;F(Rwd^CVH!V*Zmig& zb`Jmi0bP`~aayZcOiAI)0#Z2JLu6(PakD>Qy{MSfZQ_fM0Wtc)=EpwC#3O*EJXrA* zEjP=&Zi3kO+SMZ7NR7dJrPDJMr^livZq(2|FQ>*#4d#;9WM#NSM(kth?sj%5cpvMV za;-$A?q}h+jR*zjYTMAcbqa3VD(uqDtpz73D0ksB)n06UFfirK6lQ2S{&@RbdY^IeZ?($D#?135eWywpx)=6J>C)XjV-41x<{ zI2(ypoU{!3_0M9S+|tp3eh+OZ$KF?lv=x8SoCG?uQRFozA!QWC5=bR^@QN1oe>rOn zWi;GU^$cpad`A|HSPmcSVIX+~$rU*LEauIYgDJ9Q3+toTbC`&}dA^1zv>U$VbjL<# z(|lW`>fa5fKCARpiqh3+Flks;;o}}7yP(foiP!w*yimQ)%OWNh3f9(!ZeJMt+Kfrf z!7s;HSa-0@quZ;N9hh^<^BFdt{oMlnfT65^IVy8NDQz8-GHRTB8U*4!rt!6Yd!@|U zk6LMxVMVB}til0_Iw!rl>_%axV@KyWu58|ncQ0+J(8?%Y$sgA%+>h{s*TOvPUj_7A zIIUwojpj?{X?6&A?;w?NmPO zqzFP4jj376-pPg35)52kvt95RtBZFjaM< zGOUK*6%rtV!j}ysbKjYh`I9>x&jXJn7J?g!W#lOgxLMOM$<>h9Iqr3P$Bv<9VGcpo zHJH|#4b)Exaf2aM`7z zSDL0NDyh6iI6-pPblP-hlXCOT?Is>!IG_1QL;FvX{jgc@;*=y{SwT)l{A)_6Y?N`L z0Zu}HY7?b9*P9?KD={FS?vo@4pXh?xoX68aI>EH?D^;Nr^2c*m;VT4tefY7mv@5&( zq$WyurFO=sP*fp$pS87V%)dY0vSQ_B*LIP(t8Tp^@1osF+`cADo(oNUo|Y#* z_JKK+F$&v(*wEt2nv?Y((v_NT!pwxdx|*jiw5N%mS*9c8*aw)s5ng9EERbswwM=@_ zQ8ee+=fUYZ5^d4%el*F=xVDlSY}#Y2gjs!)9{+tnw9X@NAK_-$-J{&diQiJ0^LMQQ zfwC+(FI(?(Lm>yQLq$TgSzbpX?VhBOf=wj5;=6PEUp!|i z)T@&Sne6Wlg2w6vB?B__3F?a_fqD}^)sqcb^$-Db8U|b&*Pw;PqbVUL|-%;aO9@1OIY$Mcvb2*FihLUvfjK!XE9n?Le zXwq;T;3DZ-E%vKMupc}1tR+~x>;7EE{U+hp z`Q}gqvb-^qR5(qVnOel+7`_tH7L%lg)XS`)I2t?=kI?bVxVJ&ybQIWnOGs+Akbl@0 zMMKh*UUNSL^iOZDi!U#Ev9n21fgYvsio< z*+A>p)JKT&xnu!Wop_ZLo~j zKa>($xW=N{jR=7H6HSN`P3(%l<^r|(uSzU%V#HCxFg6F7wOP- z_)Hw~CEWfK?-;_}$NG>N;k6BJk)WN-O(ZL#J5X1P*ddmt8U#iM&3?kEmFaz`YF1y| z*-Qp~%tPq3C;Im3LsBw%s)2-tFxN=r0OB+xF6_E9$No&p7TvmB4q*;)MsG;790KX% z6U%uZtXjkUm`s-A?CNR&x^;$E+@h>~sftQ&+gx7mUh2`AAK7L_CLfdDnEvb8+nou0 z_H;?efSYao^UM{cFGY7nJI&i2`<#b!L%V&8AQGo58*23hE$qzj*=PUKO05V%74-c0 zrakI48PxRtaqaTzW7}=pi{C}pR2&|kRDOL}k)KKs$cRiROPR^Yyw+3u%GJp0EH?Km`)&5EAo=IM z*s=gH1 zQ|Bz5XKWH9g+Tg^RR1JRZ!J)pl3Ffg0lDXqopivO@h63APycMK|IyW|v^we$_r6@| zwq!|%=P&+!YZ3u$+%U$*JTemM_z2$2XC#ZTvXqWs1UO;}zfH>ybR2eDJGsNPlT&z@ zrGn#Em1NVOJ8y09Y|w{n)9&dZWapBp>wJw!$V`p=Jug%Qhviawx0$!HKk_+iigmRb zUqU}I$(ZBA3(>H;b(9e?E~(uAXwu=tCmak16U1rK`>+_FiZ_ta+ras`%x8M}4m+9i z&dys4@;)fx3P&H@@?MOKqK4z9{qDji@G3nZ&i?47e;OGN8>*CbW?+ zKZU^%FN@xCAvV?i&@G(FTL1ZKctk@s9qp_Tm1Y>B9*M+b_qStCi-BkBGO}{(dwBcH z&ARs}(a^t)ZeYZhMRpKKeStXEVBy+yl2=>(%?R#C63+Nj`K{k!+`HI6fiv4R#M8C& z;BL%ALUpADzu9i{gF~I-k~*w7i|gTwEpNQMQE#BYMas)9CS&+9G1&6_N;bMUKgrnu zE9`=hnf8ElDIxP!W0YJ)`CDN}kj&w0+_yg7=^QC42}xJtCg$B&TmVw>Rplf+e~sAp zT(+ojtjy<>*hY?Ff#$vXm%dfn`!j6AuELn;d(qeLInpdS)AaiSa#MHEwUmhz+Rnt*8_+0c=i|g)e)+DB21W2>}8<(E526Z+Wep8<0N7U|1VDDBO z$flp)Q5|@CEQa%dQkEfHHnFEA6v_-yC_HL?y*E{t2wrS;pV=dNF|0_=g+~-PCP^^K zT$1wbk+2>zQj3N-8|(oRT@;t;wHUPiR`IpnQG12&9EO*XJ~>}_|G*aJYK)`sLf5Ey zM@)_t-H!F3L5q?tti$xj>tZX#F3vzhID4{X?Sst+dgwgnkSj3cPSzPQb3+o#dAu^R zy>sJt@JuWUs&MHl^%HKy7oRQ`Bxge<^#yZRBM9OXsd8eO>Iv$r}&eH%Fk;c!B6$8nVBQNLvCqHGO55+3!!AaKwJ6YDE$m zh_c|?^<&MIf1utf3qO*rO$VlkNUP9-dJm}nl$%CWo^+adL!9d>dd6xjEcOnQFy$!u zk6(3bTz?3C$)$^FqCUe;w(+J%6;27=&e_tkhwln0M32N@J@JlP==C(D3_hWMALsh) z|7Q+ebu;y3RmV?B7#CUTX*#pidsre}dEAl(&o8b_+_r<0)YM4wX3>pF5389Ex^PEm zFXLj|Svc&T9^_n{{ReR3g4B3lcQ|=`q=kPAPLk|_v`+}?xvC@1pQF0Lou2HKVz3A{ zSl`jurqX=+aKisu;jy7eq1Q>6!A*d?tX%pT5agDGEg*D~Wi}V4sy87Dyd4$}>Rq6H z0fWcn<&}Jb_EXD+Ix@f;|jCZGBV_uUa}@yW)PG8D%JlH=i1KoV}NA~C9NZSPs=T?bpXFH^)0QPNt~Xi96OMTaUBF{Sw=GiZ-7m@|g*Wjy zaXXM(D!s+yk!}8ZwGjVqBZ42}f3K^-o>WO9-%aXZ@nb|``mZHj_d#?lB;@dQbPx^? zO803TH0>pa36nLlG0V~I2jjM^1rvjYz?V}@9j}Xr_v3;a$&oCIBYg7|R~XSBelIKJ z)h~?(Szkr;K!(c1_$_o1eKa3&@Wdp}kWe6H;M>Z8=0U=-{_Co@df~8Tr)rg^NVe3X z0V2SC(8?p^@3wxAX>l#_*>9xvHM~*pLO7%cwm)eLGavUiP}0KrEOaTKJ`at=+fMnL zk=t9IeW6vPoBH#uo-?}cMPYaRNIz|i`gw(Ro4scih(mT!YEAeA`q-up+&3$jtnFL;HY=quwHJyL~8YdYt~u>Hpkih4;QA zD9@7ThVM%VMtG*R$eLhCw5SlX1&;2H zOYo<5P0d%$b_@5BU0{M98yM}2O2qPi1xT1K9iSG5@HiNq3@R0?%{a^Tps15HY2!A+ zW`!pqpf4;n3+^PAh##~A2G8@7#_Wd?8Qpguagte%I$CXmzSKm-oiM(R?ncEyA{TzF z0j83sr7_H{mIHV1GFiT69#B|++YkGw|5pHBo)I@sUBdY7P^YVs_U?v3UinTBhKX^^ zAN>X+V@GtX=*{X=Qa}2O%T8(7YLQ*Ofsp^v6m&$#Id{??XN=v za$2ROZ?rNL0%(Yu?an!RY|oVfAeMt=3QaU*`Jt4-X?wu|QuY(PT({v#x@y=Sspl5B z>JTSs7<&Ji>@3+&S#9djoWtXbGTpZ31zh*uWte>_b=hEyvdsyA`ucRKM z#dc&LF;;sWoe7llD{Eu@w*Wv9qq@vMPNE_I%bT5HqV zC-Jeh+Z%i1eLk`QRrg*JL{<`8!W%(dD4F5FKk0E_*}FNn%eYl$NlQsqv9Z2aPBlN9 z(f~!4{A@`$1J6;n)dIP=dnXoa(_2`4Vd%rUiyuPiQpM{)@MQhShGzXn?Me#gXAK0S zdEJQ?he7G_i5g{~B92+WAjPR(NR&| z^V{p0p%`gkcU&AAHZK9Qt_i`K5hR-LD!weURrAJD+Ly733gAb#@PTIwbXfKZ4|DHX zUoQ25GfEB9kB{1A)`m7@HODgqiMGw{>oi_PLs*i*aLA>0frXt0#ac08qTo$uiMDSV zSIh$v0DO*K!-BaeE9i$}pSaY?*)hK4vKh z+dU%_8Ecwn)NYIN^FMbsixYOl!o0rsx}^qKs@@%{ZFo&ah#h^{+nXdj1I^nT_Iz3V z0XjTcoA5)&5!6qgeobzE=WUIxZ_YOtWN!8=5?wcnIT+asLVQ*!W*q=q5cM|j8hd+7 zL!LH-A`m#zA!XP&KOpW6LXAf$N6B!G2@Y&$Z5X=g1na!&sGbT6CqH@S47Crp;nrbbt@g`2-m}4eQw4nP|Uh}Wg+(>eS(poJ_pox!2-&vI%EnDKf zM=@$#!LYY@!QBprR)k`R%7vBC%a_)Qa-KoD!W2j|X;(1B&}EC>qg{7b3U9wl-IYAz zJQmAn`D2W8ZAKXTzO<%BDmEBE=PZ7g_-*7DKmz1)V@utyUmpfNd zCF_I~;Rt@lZ6_srfhcd{D>Tt3RbnF+On2rc1A9UirME*i%xvrzp^`cMwlvoe4x?g% z*7uN~W7FkE;2516oB_Tq%(1hviw+|TU%o>s4TFCT=v5g6rfKOwzs00~Aak=Ds}71?L_9DKieb>yKy`I@Rw@^rY| zT@nV0QS~-S-ac}9v-U;xrqoxk;IB>aMh1kSx45xW`TX{KdhXHD$d=c9P-&Vo^I9JX z&3u5VHtE)#c&{wI3Da$WiW%M#6|GGUa#_&Nn9Fc1r2x^)*OX3IUIlR{4A*nzv|n^@ z{%R&pvv>QbI{V*!VTdA#KlUrRw8R|HD?yK+fc37ijKx5B_MF9Z3w@(M8>=?f&?pV5 zSDLdBjE&yMkxq+5w#xhv;#RV1v+W}G@ePBi<%eetoOEQph{rz^IhUvWQohnDBeZN7 z9!-31z1weZz_B;c-|~wBDgu-^Yu@`FYZ`@AF*oMRd9^d_UQ2yMB*Yjx7ADYvzsl`2 zw~tcUS1iCXKCKhN>YsMa#j19mGdf{{ESyjiv`or)jwCL?nb6HwKqotD+h*A(rE#8u zY+I7Irb8u5O?38oG6J0yxvyv$_3txb<5ySlaYg38S=NXa?H2t!V(d@4fcNPRslt}U zEkd=Mq<~{{N)eA4O9DkYzb=wxKfhJ5bj%;?&-S<@79|{7wwLOJpVj4c7>i@C)p$AaH?fJn zBighhC+A+C@)4f<{&-)^0Wox|uUDLYPLz0&Ch1f|?iAYj-oL4Xmthr+2y1x}5}XP{ zIp7d#Mc6~~MwWe6wZ5de%MOLM^hZ%4s5~v^{^)9^AHW?4Dv(-6@~gu z&kaoO=!W&!qj`?S3D7~znWQkS7USjSEe`#(`he(77!;2Vca>spg zKY2_YF_lKpOQr$xW76}|n|@0KwWVuR-lmJvUi2SlZ$IllhjK8b_+RdoKWWSTEO;Y8 zIzH^hwEaMrt1!a1aRlKe#NS@W7EvP*_xTn5hlS+*FUgw2cONLwZc;n;$SC%z1G91o z;T6Z%+H_J+Md;kUO=ZVT6+1`ls78Oo-Suq=?caY2G3PSsQf>WNt@v9!U7i{o-hD8={&zRN(taPHM^^zvulqh36Uo1XD zzEOYiXoSoRfW>r-EBY~aL%ulsEr=bYV_d}*(*NmdQrzP zgjBC}dj7s#-e;!ggydEkR$t>j=?=7{iZ4ORa7Qre%eFTlE7zD6^o{CV-X1W#B^H~> z-0A|A!n3feRtzVfaHe)6XZ9#et#3Bd>;E>8Xnev|z5ZKGAMz_n(2WjTgg6YIN@Y~~ z@?B_rcykla+J505#S`PVLq5iCs9KJtvcPg46=$F9O1q`M-% z+ZJp|VCM0@(|}pNjzpns4_dChMWXrgb_X9+zaZ~A1#u*ip1>zV##m>N{mo@n?I9*3 z*&44*lkgrB^WG+U71u#|er9|igwZOYDzO~Era2#ag&UfJm%I0`tWti)crbf8{2|F7 zBI`c=f&<=MzDC04#Gu!j*LWDB$;?Mjb!00w*nKh$dexNJdh+hcha;PTX~x{a;)Zwk z9AM-83aJ}3%7T%nu#A%~QQl8jJI11yW`BUQvP8C(I){Zaj7+DD z4ihLFUV2=4`Zuj$TX!Q> z=meuPX}3bhU+Zabe5eOYJ@$QA{?c0V-KncvCxlmQQYmcl(!GXD(eRJe`rAC@S57H2 z8(MiRyKZD}hBReoiKr<@dNMqJ$N3bL4!r_J%5+-hlcWm)zvDfl;QifE%2%1r)cmI~<*|dnVYiE){J7bNrZs76f z97~?tUV{EzoS=k!_(V(XVxHVNeOzR8?b}sd*icj^YRs~NS~Hd@o5Yj_CSnd5R*tI_ zj&9vO`4DRG6f&IUN}bnFX4ihJ`&TN;UgcmnrlxT{W;YmnpK$~V=3FOPLbEHKL_0`& z>FROJEge}G_&i`sIuWF)cktd)ho5^ZAhWfI^_(}Ld@rt!gpWa=x#GN*nRvztPyimKzZ z!i6^{ja)Q)FwuJ*b_WYR!Jqjq{usKWam~JaJy4^u3EZ)2H2d?xR&-!nY--e+8-E1G zXzE@XUxAL=cXK9`0==9mpLM7D2A96}vjMNKm~5b}-nV*-jz|*SPU1td#NwZ&PQ@Pt z%C8+bU1H+UR&)Z zqJ=)g1@#7wAa)4{%dMEzwDCc+&Om+~(N@apgpA8P5jN)F2qE;IB@c5y`6zS0Gs7qp zLDE8hGU^mpZ}3QKSZ9W9oKY9H&TKKt93NShWOnso!0UMqGMM@Fb4UY3uWf($dPZmC zr$wpCKqghW1>&QIa+XLGp8#i0lyBSYedO7`!PVGtaan zL%b8p$FiSp+31YdArg6z-cHLT7}Hyk&wddkilYJsTpPEn#Vap)*MvpUwZ<| zSqWu_vL7{z(OW*G`&P=T^&qW=)g1v;65<~t(+E_!vLe@)#<#m{og(}wqb zDm4F>X@o05RirZ)QUNPW>LTOywVWb%j3vTrI^>7U>>UQH?pmd$thhkx{xhB%+QViF z&s%a+5C@l>xLZioe(YkIN$XuB*;A#Ry%FJj-}AFsQxLo$TieB-gd1GDOm}~cq=dll zDRvY<%4t%WK=*8_5#Y|jVSHbu;mQ}66D@eyjtjrCyl5sQvs2m(d7Ss;e5 z-RV9*nV&GJnJ<*y!E$M6EW4}fZ682}1F-%^H?s=xe=fAx*Hl4BhT~Zj-Ipi>Z@JH! z`zJ0^S2@{yV#X=JftAMB?7Q2gO4w3_#jl|hk+1S`k*a~IxNwMFnFH^9`hsJo>9eEG z)9vkA&Gm5&f@QB^DZ4Ylo`6q38hW?PWzOO2D_dbS{>DFRjEaXsY&1^GZkbST@Ml=D zt8|g6wmpNg$@T!uPV=Vb%o2l-_U|k`!TRslcnAjQt(=r=@$Miu|M!2Ee=N0K*b066 z>^8QIMop_Ly@c{d_$)aMo2;IAo^uk1{IGmiS2)s85hlsWV-R33XoGZ&Vb){;gNHq+mV-!W9t>v%}G)BX~vcz)-FzQ5;%puxzAzS}Uc4$MEgc8+g$| zv&=t5^N$wI1u^_EzaZC(2xCW#CLkQ|)soaEJHRO*jKA3>Kqj4r<+|lWopVHZrf_ox zu*__v$bYdb&Sx{s=t!4UO(PLR=s0$VD$h4k!b6DkW(WTkeE?ypP_|>b5&POQuC-l> z_s~DySR!ytZpJsiOlg&?{#2R{p8CcuY^;N0|M*qO+_`21Gp5%TFp)dn%bB{P644&W z?o5W;Tvs)TWn&ktNVfdSZ7#th<-p{*e=Uvq`}i*&4{pM2D3UkJTAlC8n)H{ocGfW z=HE00Q`h_fwHJX0V(afoblfoE=4SYR#oc|YFEOR@x$dO3IobFU@NB`{i8eTrWSZH* zVL8df$`o|NWTI}(&GW{Mixt~=u(kL5G!dkYvA$!HyPo$F%L=An#}kI!>xYb_zD~|o zF6)ng^Jc-14}%{9Xe(J4jUCQnvU;n$`Lu!Dn9KKRXgleC{HjWazz z{+O=9XRj~{IBO%ee2I<`xet5(qfsimv>L<6qmY(``09zvtxP|P*Tbcrg7i1(uYn4L z>Q{cEb7tMf3yH9D*nI1GNf2)z?T@X?nlGzk+};|p(PcWB2%X&HW-Lg!UD6%{$Ngfw zCiSlj1s4%H1{V8$v*FJ zyVvznJ#vR1s#G{+>(d&=HoVf$zi~u{^i;Q+N^k^Fd^Ctuc6}Q&Q;x1MjfG@4y)I_U z*P>B7vhR!6o}A{|3Z8v~@issp`Bwe)S&E&c);Yk(>=%sbVujFLVkf`VH#Frg!^yGe z%y{iKJ5au!h2Zn^=wEr!32oeGhY-0dtEs2kNRReXkNTrRSR`}?mopK%0r*r4$S&Ag z^wLns^sI-M*269E7hdPrtYXWRt}mZzRY;I^PfBR}OGw37Q%026-%cTCfk^3pZdQMd zQ+GVI;1BQ5M(kf2z-`lz4Aai@2Fk@M$E$+#^-^1zAkQr0q zP>=o$%P1`uf;%u7gSn7vhk^JS3ITFvH0z4 z$Iin;pg2y5y|w-RRt~!KWG8r==vnP8F-5T}U+d+%@lvan4l;`vs*p(ffavzMP!riP zO+z+aSGZl8837yJ;sT$Vd^W!#$$;bIVkLXI`e*Td-#H#16tf8LbRWBYf9my$0Mx)D`zJ;M9eGcnjMrr0NS|pvciEqjZ$DZ;Zw2 zxzB=2E;((8=!&@@kPN&u_{+pEEaJ`UgRuKMrqgJ#{p4aQ)JreHMYKP13KDpoI%RJGu@&QRdw|QfwibwIg7BonPt-QZs08NBn%P z7k+J+YdxiYgb>m-Uv^fU^1Em<&hIyzchO6i10k~*y^rSCy8J)Bq5SA6f2@H|q7q0y zk2(;=d~pH`(Tx3e#fbsWCT7SI-1Jzjyr;pJ*R%GS!(4Z6%N!k(&AOcslNi+&=BJ9- zlo0IZzmdFf2t(w)R40s#5bOBnY2QS*_|cy-Do*7V9JPj0XNtni1*uZsLn4wtWVxI3 ze)n=K3$pwhvWj9h+Byn9bGISaX~w6S^j0cbAC{EM{rs!igpl{9f}c;in0o~tKipyc zyd-U#?VGVVQOCa}Jx`WgeJ~wWdkI8vGM>AAs*w#~PmKhm9v}2CvJl;){$E{%zS>T< zm}=f878|(ao7Q!d`~kd2`xKJz4oxJXP0jN~^Ex7${Z6UOwsP~{RLe1QqG$w%62r;t ze9)e3_`<;Dc|KtdRqX%4@N3f}2^>{;$^5s$3x_8BPqbBQ5Z(CiS&^e2(g`;+4EhO6@$Ch6G?@5O#XQQT+5Ib?U*RH8{Lgkq}hp>d#}4@ zTJf9q_1Le93_ZD2@qN0bh?UZEKrn&+4QhD=N-AmQs(b?tq7(`1bRJVily(YIXXHz1 zcAlswQ6cE*y~cGMX&0+;i?1RLU;813SK$;)TlH#a304j#B>N*t!SZ#6fAwoQgL0po zQ<=l$tHO27eL^M?a}X*M|DKsQp|_`KS%Jh9QP_e1qLttvGSgg+CYod6D-1eXCC9N5 z{xA9Z_lcB*1>Wq1Q+HaOG!(Qz%_aKoK>ty z;kv;r$Cl!xDzZlQgDBQ9D3nAHN`D#?{N!*+ zs2Qh7HG0Apbht>V+8u?nyb3V3KDACD35neUTlP~2EX)XJEu!NcvAOfMJS2i?ri6KQ zq(rFq0~_!$be0*qSNMIw5{QV0-#eo5RS2LUgn|ePVkk(UAccYq3UVkYprC|;3JPi{ zXrQ2lf({CLC>Wq%gn|hQW++&oV1mP)|5?z`U?|fTa#t9^@^W3K1m%%Cw=nb2?yB5T9hKgd`<^ zrVDik`3)eX3pGD1pE@T`2IT*fEi0vdSE2$={*@inQax?)0CajhEI?Qf%n5qZ{Er|= z4@^SN_?cL01bTNjM}vW3{_jm51{Qie5A)Fdr-ka|YUk$7VQb}W1#szsDS$LRsQD1% zf9FyYbdiAxJ*Wv<^ZziS>VpM9Q&xbqKGf&8cK^|NZ-EI^=|gq69H4c4;6xwH1M+f) zmdODT1E?J2mf}bXK>ii)Jb`Khuo5W5CpE~O=3g^xfXNVQk1imU#*5~^@q7OhECvHf zhG02RVJN_31ZD>Q8iKh&8j=4hhM?!@e@7Ca#0V?`a*s{L{zMFry{E+l5R9SG%g6sm zpk@rsI&Lzw%=BMFgT_$an-pNz7_0~i$N*SPp!_CTfakyCeeS;_9x!17eghgQ{BMQM z6#8?kd?{dP3LPvgl|YFpG;EmK)YmdnfcRgk2+g3B&-JN)s>lE}GiZRT%|M?SlqSv$ z+H&~ozlt2FvI{^qhtkIP0&3<^2G+q;<{nDGP#27y>ePpg{x2*4O-aBAuwxFELq(o| zfw6ISvv+a;EZcZU00j$ZT#3^FjwKi`wO|wv$hCkPqo4ne0NAvE8f#qoSJ40^to>Ij zf8jrA!A^Bt`d)C(E3n40v6X0DTk%{eMLd|3ugQ zQ~h zYTH^6xMUY#{%`IU-a+lXhX?^z@1P#rCkg{#FX*-K2N4XQ%?bY}7p!ndn4-YC7nl(< S6ebjggb@ZNJ5m&y%Krz?KH_-* delta 3513 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 From 6706f9375284b3606c598a4378418bc90d96924a Mon Sep 17 00:00:00 2001 From: George Knee Date: Fri, 24 Oct 2025 12:17:16 +0100 Subject: [PATCH 08/17] core: add gauge metrics + histograms for block gas used and blob gas used (#705) * core: add gauge metric for block gas used and blob gas used This can be used to track the DA footprint per block * encapsulate OPStack additions into new file and function * add histogram metrics for (blob)GasUsed * update fork.yaml * typos --- core/blockchain.go | 13 +++++++++---- core/blockchain_optimism.go | 27 +++++++++++++++++++++++++++ core/headerchain.go | 16 ++++++++++++---- fork.yaml | 5 ++++- metrics/gauge.go | 10 ++++++++++ 5 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 core/blockchain_optimism.go diff --git a/core/blockchain.go b/core/blockchain.go index 8f4eb804ee..365b864dc0 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -64,7 +64,6 @@ var ( headFastBlockGauge = metrics.NewRegisteredGauge("chain/head/receipt", nil) headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil) headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil) - headBaseFeeGauge = metrics.NewRegisteredGauge("chain/head/basefee", nil) chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil) chainMgaspsMeter = metrics.NewRegisteredResettingTimer("chain/mgasps", nil) @@ -1230,7 +1229,9 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) { bc.currentBlock.Store(block.Header()) headBlockGauge.Update(int64(block.NumberU64())) - headBaseFeeGauge.TryUpdate(block.Header().BaseFee) + + // OPStack addition + updateOptimismBlockMetrics(block.Header()) } // stopWithoutSaving stops the blockchain service. If any imports are currently in progress @@ -1398,7 +1399,9 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ bc.currentSnapBlock.Store(header) headHeaderGauge.Update(header.Number.Int64()) headFastBlockGauge.Update(header.Number.Int64()) - headBaseFeeGauge.TryUpdate(header.BaseFee) + + // OPStack addition + updateOptimismBlockMetrics(header) return nil } // writeAncient writes blockchain and corresponding receipt chain into ancient store. @@ -2771,7 +2774,9 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e bc.currentSnapBlock.Store(last) headHeaderGauge.Update(last.Number.Int64()) headFastBlockGauge.Update(last.Number.Int64()) - headBaseFeeGauge.TryUpdate(last.BaseFee) + + // OPStack addition + updateOptimismBlockMetrics(last) return 0, nil } diff --git a/core/blockchain_optimism.go b/core/blockchain_optimism.go new file mode 100644 index 0000000000..fa7ee47390 --- /dev/null +++ b/core/blockchain_optimism.go @@ -0,0 +1,27 @@ +package core + +import ( + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/metrics" +) + +// OPStack additions +var ( + headBaseFeeGauge = metrics.NewRegisteredGauge("chain/head/basefee", nil) + headGasUsedGauge = metrics.NewRegisteredGauge("chain/head/gas_used", nil) + headBlobGasUsedGauge = metrics.NewRegisteredGauge("chain/head/blob_gas_used", nil) + + headGasUsedHist = metrics.NewRegisteredHistogram("chain/head/gas_used_hist", nil, metrics.NewExpDecaySample(1028, 0.015)) + headBlobGasUsedHist = metrics.NewRegisteredHistogram("chain/head/blob_gas_used_hist", nil, metrics.NewExpDecaySample(1028, 0.015)) +) + +func updateOptimismBlockMetrics(header *types.Header) error { + headBaseFeeGauge.TryUpdate(header.BaseFee) + headGasUsedGauge.Update(int64(header.GasUsed)) + headBlobGasUsedGauge.TryUpdateUint64(header.BlobGasUsed) + headGasUsedHist.Update(int64(header.GasUsed)) + if header.BlobGasUsed != nil { + headBlobGasUsedHist.Update(int64(*header.BlobGasUsed)) + } + return nil +} diff --git a/core/headerchain.go b/core/headerchain.go index 4174aadef1..ca403535e1 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -92,7 +92,9 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c } hc.currentHeaderHash = hc.CurrentHeader().Hash() headHeaderGauge.Update(hc.CurrentHeader().Number.Int64()) - headBaseFeeGauge.TryUpdate(hc.CurrentHeader().BaseFee) + + // OPStack addition + updateOptimismBlockMetrics(hc.CurrentHeader()) return hc, nil } @@ -183,7 +185,9 @@ func (hc *HeaderChain) Reorg(headers []*types.Header) error { hc.currentHeaderHash = last.Hash() hc.currentHeader.Store(types.CopyHeader(last)) headHeaderGauge.Update(last.Number.Int64()) - headBaseFeeGauge.TryUpdate(last.BaseFee) + + // OPStack addition + updateOptimismBlockMetrics(last) return nil } @@ -486,7 +490,9 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) { hc.currentHeader.Store(head) hc.currentHeaderHash = head.Hash() headHeaderGauge.Update(head.Number.Int64()) - headBaseFeeGauge.TryUpdate(head.BaseFee) + + // OPStack addition + updateOptimismBlockMetrics(head) } type ( @@ -573,7 +579,9 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat hc.currentHeader.Store(parent) hc.currentHeaderHash = parentHash headHeaderGauge.Update(parent.Number.Int64()) - headBaseFeeGauge.TryUpdate(parent.BaseFee) + + // OPStack addition + updateOptimismBlockMetrics(parent) // If this is the first iteration, wipe any leftover data upwards too so // we don't end up with dangling daps in the database diff --git a/fork.yaml b/fork.yaml index 7957643d17..a0af43cd66 100644 --- a/fork.yaml +++ b/fork.yaml @@ -199,7 +199,10 @@ def: - title: Warn on missing hardfork data and emit additional metrics globs: - "core/blockchain.go" - - title: Additional metrics + - title: Define additional header-based metrics + globs: + - "core/blockchain_optimism.go" + - title: Add hooks for additional header-chain metrics globs: - "core/headerchain.go" - title: Optional Engine API extensions diff --git a/metrics/gauge.go b/metrics/gauge.go index 909fca1304..4f93e22487 100644 --- a/metrics/gauge.go +++ b/metrics/gauge.go @@ -45,6 +45,7 @@ func (g *Gauge) Update(v int64) { (*atomic.Int64)(g).Store(v) } +// OPStack addition // TryUpdate updates the gauge if the value is non-nil, converting it to int64. func (g *Gauge) TryUpdate(v *big.Int) { if v == nil { @@ -53,6 +54,15 @@ func (g *Gauge) TryUpdate(v *big.Int) { (*atomic.Int64)(g).Store(v.Int64()) } +// OPStack additon +// TryUpdate updates the gauge if the value is non-nil, converting it to int64. +func (g *Gauge) TryUpdateUint64(v *uint64) { + if v == nil { + return + } + (*atomic.Int64)(g).Store(int64(*v)) +} + // UpdateIfGt updates the gauge's value if v is larger then the current value. func (g *Gauge) UpdateIfGt(v int64) { value := (*atomic.Int64)(g) From 96738d21616cd7c37101fd49234e361f70e7b64c Mon Sep 17 00:00:00 2001 From: Sam Stokes <35908605+bitwiseguy@users.noreply.github.com> Date: Fri, 24 Oct 2025 15:07:59 -0400 Subject: [PATCH 09/17] superchain: update scr import to include jovian activation timestamp (#707) --- superchain-registry-commit.txt | 2 +- superchain/superchain-configs.zip | Bin 3910346 -> 3911175 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/superchain-registry-commit.txt b/superchain-registry-commit.txt index 69a0518153..e0f00d5b74 100644 --- a/superchain-registry-commit.txt +++ b/superchain-registry-commit.txt @@ -1 +1 @@ -6748f8050bfab9930abdfc46288f9629af2f7b38 \ No newline at end of file +495e615df91fd99bfc1468d9e22065fb3a64020a \ No newline at end of file diff --git a/superchain/superchain-configs.zip b/superchain/superchain-configs.zip index 933ddf3f157718d9d2688712df6541b137fd3705..c4fae570be2e2bc328fa2899f85101b351e0b2fe 100644 GIT binary patch delta 35588 zcmZ6yWl&rJm#rP#-6as5#@*fBU4y%OkOqR&xVu|$cL)&N-7R=AIqN1z@2QQ}u z7b~Z^B`>R`IWMoNr5P&+7mqow1sfX+7pJ8uy9pNu3mc0`y}~#Y;xno+od?vXPjqle zWYCmA#kh4TV}&6zTbEy2G{2(}ub>CgtY1L9;))+AX?o%B38DJBg5EHk zAokNyleVDofM`*B7n*^Ru2MrHTn<%A#GnA}!6lD4oDdeg2@-2npSY2Bd>_wxk?DjQ{*eyHJsRSF`BsZf8CD0(?iV^|hQCoR@5^3z z&4hQH3Znt3ZT=JpdI0l4CBh}7Uc<>{y<%CemAYdqz^v!A_v`xNr$CgPr@`Ih&U(A+ zgTNQ{B6Dk2gLna&NaRXRv>R}c3QjRY& zBilRfqCc!glsCmgh@NRP2m)k%B*v27w*>z}v9I)Xgaih>^W$T}U9C3by{GXxF9@MId`}}?CJ@2D{&jorNY^!@hIoT6FG}r{zYUzYklt176%5>6bKVXCJV*FfFz4wsqy`^`Cq^Cn1=7!Kuz<98x($kN)*Jj96f#p|H z-MEpj%dVe1l&7x9J%LJQ@c8y2Xf@Rb6o*yVMeh zi^jQ~$wSHP3HBeP*kDNEKQ&gu9YZ0!R6L(&f*I3I`bmoiG&OOvU~+eIurJXZk6o8S z@4BUjKRCPZB1PKIA7%?Q8HQaFMU4)$o!nI4QW9YVvhA`Jl*5+)JINM@i2+{ zIWmJPD{vP-0AUIvS!D{Gc-^~LusPki7oo5|&MSmb$8DJ#Z?^5*wm8M(k&saGm63bAUdhFLOH;rX8 z`O4!a?Zh>(Oc=33N_&fqxnGm#?w}R`6bfIja^roG->y9(Wk~7H-Viy|P&9LcKqb1g zQagc!0ajm~6}K055lcesSZA?MXsn&`@g{Hd=U?dKF$S-kKD`t8$;rN$80SQ5a{u%| z@;IXbW-4Hu-6NKqn5}dpY;y2ivs9>;8*Ba|*oX(B9 z@~+g>lqk%(GJ7x`6am5VjK4%9N-b_cY@y&!9i>VqLu9I!>EtL73j{^HDl|7tVjIU?D@pXmm%Spbq*S7ls=<4N zRIjncR(ka(S;PxF72Zi^DAfQ}6DV{!oK|{XkQooJz#$QlvJk|?m}O^ZYZr43viv&c zQ#$y4(d3Jdz~=Lf`vDf>r06;_qQ$IHeA3pk6e)K{jwJTA;?C7+W%BPM7nG)Ru`r?P1lE>nS zmfb^qkG%Hj>pdMWu>+RDBI!m`6VdR%)x%`g)1%}1>eN=w)V9{>r;pDC9y1?b(3HYU zPYZe#eT;njyaYrpJ*k^Cq2gwK`6b(TN+0WAr3h+onjpWj0ME?wxh6lgM&Jh#>`YqI z-CB#8*<1H}*9_g7Y!QGux4U(zkiI;%QOAqX)myrE(lxd4YYx4(tGE|t>?cR+nM6g5 z5*H??+VzZb6NOQJO^ocC!ZxI8IoVh~LL6C0`vYj|@%<5Y<)BvtSzzv@wAgIXgLcoO z9hkU(J9XD{0eq<)=mqmTpWAsFJGVWj+-Vjy|5&P6pGqrveEwJd<2`xU4^467M!D?! z=}=8*9^8U)$ChxJnQZ)*>rjdd(kp<}$*Wlsri4z^Ao4G~ofd8f{9RbOc!@K_!4oKA zqtJ_v6?JQ;It`YgK~T=vqFX!0n0l3w$NIQsRqT z+6@U~foMz_hwd@g+cB&iKYC# z2e#W7(1sYfh;xTf10~Fx%8s1)K}vPHjiU0O=AQe3>SL=T{g@?29}5xv77DbM)@Jqe z=uzf0*qF;9&YOP!@^Jvgc(9JsdLa8ErOJ+Xac)N!>NoESfa+=umX}OA;it>+Ul>Tf z;)j($7t0x%YNdg2@sl{Ul%ff3sN@P=qf>wyoLgVJWE8b0Q9-?5fhYBjyTsWx3LoPj zsf9q?p=tsliAKdVxNeCe!Izq#RbtZD3->8M_t`gfB)kIyvr?H-$us^@7E>dvYOYxx zc<{)?d#i+~RYjhpOW0QD{}Zjq#=k3f$^S|}eUc22VM*TX?(<>BOZ|2LXhRxXEm;a|*o3u6ywgvwKb6RTvwv!rpShs1t zlkcf;{h76nAk6UN>0%RCY<}k|FioPjfE%qKM^f;;d3ea|QJCeaqjDX!bKQOHL@=B8 zk^fBTL^1}h3J@oK`XIuqgL|RV)CnZB7w;_*f$@lIg^vO$)|ZDsAo7Tm_l0I6(uRS> zlsVhVd5aHx>63wOC7#jqR}F(%>I2g*>ld9(F>nl{#kaQI<6z`H-WS&WtB1BeY=Ju{oy zdz|?dj3C)3JwyVydrgBOldr0{r)OETWVk`bCZC#B{E5N&J|%K@KEvf5>Pn5}QB?6e zZe~-IIdgHjg}zp#50u}F@dTI3YIXhU@6WWf0kQl!p5Kup)MVilXKasV11zOY94=ke z+Jq(L|0Si0NIl=f0)Q*_k8d~?3S$$+nO;Hhq`P1(jSg@?gK%6jEtY%Mb&V^=`dA{_ z5|Nt5TaFzctlQIDhqOBm-xzdL0QpcO^aX@;#n!B_D$nID;es}0p zGPlS)uBQ6hJa*df^;>qDd66mf)OWhU7@$ik3z+1oUahfy9+jh5LXMLsDh3s{^%6HQfKVu_PNt?gNW=UVkL z{|xeT?1cD3lz3pU5=BSb=S@bx$GcCSpk>3%pwD8WR*QZnbK}1D{JZIf2(horT#dQP z*`gG8EIuhj=?U@3XdQxCc%{8Wi#NX@d;AK_cAljF0wKI6N!GdB-(~SHN;%E zp$gHIDx@#tZ8VoMDOQcgq>bu?gNP`lIdF+VU`%=cTSn7}BlzKyM5wTUjQWz%O$hOf z-*mA1rM&hk1A&ZFUEBj1+(obt>F?Q00S*^+e5?L`c+P1Zrfara0Try=K%?KPtzDg;hT04-h2xV;hW8u^Q*wsIJmRY zqlO{4DvAi|^#_PfSbrPfw1qEzztG*QHGT($5mcc`7xrSXOA$0#WH^MJcc9956c7BV z17{%xykV2ap#k!YguY?+ipayr(7yArnUx2P;79ePeTfK;v6U$MY)0%FZ92Sf6tnOa zZ6&awmtj~|6(a?zy&b2Z9QPqIcfS61^Wku)KU`kL4Du|dOpGNORcp=vVUchan z)j0g9%Yjm6kKSa9rGI$K4yOEdib9)(Ikg28MNxkJ{>~!H5S=tH3~0=7L3(>FZ*kGQiq=k+US8sf+%sFwosuWjyxXns zlQ%~&N1&bL)7olJG1gD1+;Gs6sO6Xsqt9`hPK_Rcc7w2LdAytd6caaYy$I7g80mjF z(NLIATTMQpQc}JjhRZ4sFi&+rY}Kj@BvE& z!o+ZYsd}Ceon6MJv?VxfEe5G(Gmdb7)`0YU$@*0Kw54=3=G0Pa@kV?9uNfB^Jmjjn zQxW_VQBClw$^n#PK<82XRCe4?8^O$`YcCN>5 zTfOBe<&z(*(>XS=Z`rrr&ef};<>O=BZFG>%4Gu;I`usrsKkBQ*FLh{}La+iFIaw5@ z=Cn{$xZh}ZVZ>ngGzx@M-cDGwj$bxwadAhX;XQYTIpjI+EVaY*xSgnU_^G3|5aR-# zkgp`!aNT4Bk*fj5;tpBM4M-oTm$cHfs{{Hz=a~-QFU_nYD3$rGtDV-4E-on*Q5MP$ zKZ5H-6ac_}bS0o{??o)@!5zf{Ks01MGgwn02``E2<>uVez3c`8_ zKoNU?x6o3Pl$op&%N)*R@T8D5&AH^LBd`&Nm(wV`a7dH}rNmmZHXPX_n-;mg>5&0| zyf-1{ux@q`@z6;1eUMOTAWH+*zJxyfB#<6e7q=tp&Ejx33EpRVvJ(8$j;O>-N%oy6 zJ^K2(c9j^VTxAAUxMrrD$AwVV|0b_*Wy@suU+ z#v2lK=5({-8PMSZV!J6(v2NFSB279WPrcv{cpoKVuG1tsTZ%cuqS@8G`(z082?FgH zRDCv1s8Sgt+G8;^?V-+)bRBuQ^^xx3a6nr`i8KW0$uM$8OQa98^|`6RizZ->&4Io( zV?&LCoku_7XAq=Ct_r!VYSnECh-95b)FQS39TDN6BBlm+b*0Heeo_hp zI`9?j+O}tr4Nk*+ix<}TvcdtuC5a|V7XN$3l3c!q^0MajR9@DzS`qJVSMn~yix`Cyq=i+wMW?V@p5Z0&r5T|<4D}$4`y04>Ia{R zpX~>bzK6K7bkMWswBteW^d!Q8*IZ5G2^gXo^3!x#z(l`@{;b_P)}JTj0v&2gxU&QQ!yF+NRMni zgO(+s6A*$yDdQqCB{1t%Y=vzUh5K~|D?b3ehW95bS-*J~YJ8YFt{&@B5d#PNx`5cN z4eeY{LSbPezsI+lrnt{rFQz3ElS_rTKXSumzUryNQa%HoyM(DiC@&RrRPq^MFCi|> zjvuE_BiwZci?#!!q`w6s#m`Yn^Z%o{{ZqaDOX(0%vIyO(LJ}3J!Yksp#XIAyrOiNW z=om1~XzmC>`)?H;l=MarUey&qFwVu0zjd8d5(R!=D<1bm;tc!rRD5M1dC08oNsXY9 z=ne+cOscerG8zKOT5;$QF@kpJC)$Jiwk80>ZWk?+L5??yC6%+^dU%4e|9gC~S=^14 z7~!bn=a{=cZ4o~l)&wN#=^;8?p%W~rt2e)f0yV+kAvmaWQ_c>SwK8BKJKQG~a`?FM zWkxR?0?$G6Oh;kf&p(6fIH>Z|!Aw@Xch0AnMaS$e{1gqt$h##pcA@Zlfor!Zu zZ>)`cX#P^dq-H6nlLQSYxw;^9M!oAYR6qIK+iY18fF5RFS+bcd;g)8b!`qzloGHPa zz?s6g4M~rqq&C3WB)wO|`I?Z%NHbN#kzxZ(pM{TOVZBgzM4#j`U_7BBJfG#li9LK; z;9@?Ski_%%tGOfbOV5m8E*yQlzD6GjzavR6n(%$#iT zm}@nOHc+J>BGuy=%^XqcfntE>-_$UL$;p5c2|gx(anR&j{QdB_pP%j8mIgcKAnQC8 zS&1^Y0v&|LA=`G~*xn{3!>*d#z7Yy#oZ0lU*|-&Up#9_$5nS=FdqkNo(-uB25{oXv z6v`1~TgD+0Cou^xf%_-Bzs3`a1Z8Ie(~^!28|;&e(~_fI3#W{hx;1Sy`gweLHEPbu z40s=({x{4eQ=o`cFwC4ueV_5tt|IsmfhC&jvFlvu{>xyh-MG6VT0i;EG>g>PK-xzM zL5GSYP-I0ibR>3lUM#02cGY$3K)9AV>w~!p~*USw&%0@ zv-|b%c9Y}t5SZ!EJg?W)G77JuOYT|t58?uDmcO+$0;GJxKb_C-Tpyxty#xDzXHBQ4 zs3ZkQ@Wc*p6v%=r9lVS0EbZMyk9{mC(rvKN1AZ9Tx`HQq!nVsNLmrNNi}NRS8yKaV zwOUSvI6G$w%=l4+f*FSAFGIr1Yim@hclMMHk_B{XlodNAzj#d8R5gBc$YbnAsEEQ8 zWR;(EJ-;V@%Ai*lZr^$-`{w=uoU%%$z&^is^9N*pHsFVuw~y?(xcjOv>__?$`+Md1 zeV-XG3K-dY7Rwqdd@M91myciYI8OBOOojH2+u^)N>It_;vpo<8wdNF1$9$oK{!8|0 zC-4=?9;EtrcV!SdfpW+X0{q|SKi*PwhB=S{uWp0aP{dmy7k=e{J8d1{SW?I}41ZZ= zsykHjpw5=aCeQi9@O7{PScqfvP?)|5|CuN7A-c-~x_9(`;#H&+3KtNI!1~xF-4>BD zHg@N2McaU2FSR|su2Er;~tJFij8mX0%M8EG%hDb)&JOp!*+ zYMIS8(SIF1ZWwM=t0DmuywGAy%GV#tfXE1;X4!%l5B3X9;Ht;ev#hrcEsnx3NS>{t zLQqBN7eTfhv&ZduZ*xjz9g4yb#d60=?n1Q%aHTixl5tT2Qf9PllD~du*cBrqV$;Uw zZn4puEG~?+ZZ@sL0m&fO25(6aDepnQu08d5taDzm4YCxQ1eySem$6+__9+Xh?Xy^Z zglIKQ$k1aCr-(EX%Z@oyH*zj?S$pQ6M@b6ba~x&HNO^sGvXPq97op$NihC5bzrD$~ zvWp+y;n(?f?Q=C27N>IaAo7L}X^{F_)O~k$tskS zDKh4RDi%>{v#wZgaQ~a$?zOu9crd*J|MHzAA`WDrBRJOqlZ*bxEvMTU@-8U#7o_M0 zv@@7o7}ieyWUd04lw$iz<83N{c!SA5axvT3k0#D0GEAU+Cj!P(z3K@X_#+g*54gJU zbnv}2@ITGA5QT|0SH0?C_&t@C9`O*1%zQB?Htfo76+NhBPZ3P_T zTEw8~(7@~3ufv@YBA4)|oSQ_vu3RP+1a}PENi^(0!?TDWprPA2*T)W25G#eB)bN}I zpwt;m1me#TaJue(IZzoIYF4HSVX=_Qg?6P2fiOjBHDEt)QXSc^#wM~^7AjLZH5E@o z(r$f!b@Ss|D)j>?T9b|oz4Pa~NCHPp(UcEL>C7un`PvM)87uiD2u{*DeV!?F<(-lUxiP(6?g>x5laxG$K(KBd^$pAS`hTYGT z9fQXf`tfE~&^q7hd%uLTNldCnGg0q<&Yl`c6YXtA!^lKZaWOgO-N#VRK8t(U1&i)! zBh&DMiGT{gA(Hb>{7x2VmQ{-kk;QdF66UFqN$Hd|rVCqCxKEql@I#7}CTQU-kwTG^ zHFe(o@0qLm{46~V#>-}v1#qm2tVXO%h3In^)fk3KU;#9h_0!oCz-#hq2mnur*ewSw6P~@eXlXaCEa4yW<}^iJAm8-xQ43Li4|xKB zk-)m1jL1D~F4;PNgJ`gt3#K&|NwK+rV)Sj{c5&f`J}PB#j@D9l&i2OKV15Hv$wiOA zC|!=+EV$}Graty3aPT#p7dwTy}ip&EXk{)^}#{n*O+kp7`o3YG!= zU~2ULtGG{rcgjs*W-b0Pn+A;JNBF0>|IO`%TXwfG@||e%$p3KboAh7U(i?A=&`LJ# z3Q2x}cCnh`ZEUpCLOJt$@bz@t<^wjakSct3SUf*(Yxx}J4}5%oLp7a$+VxCxb)1hY zYk3W5Jbvqi+t%5BQ-0~pL#PdW_c3nGxjalC2d>%03~YIMX2Dmk3BT}(UYX7dz0_RS zunqs%M03G=HAF<-?3;!Fuv{QB5+kehQT!rQsM>R`3fp^cvJtl5=55ne2t1x6v;fV9 z2K6~suB1P-LmnxW6k7Eo*5r6{cqQs}lG{4)iU%{u>n6`7gO@6-8%vWvO98zaB12l5 zK;?RubM?hx(mg^Pct+1F1H;ba4*W1|LW<~G{bK_e!Q&5I^Ni5LmG=#d)Ce0z{Qgy^ z)wFZ60iM@Sic;t%0PpY_L&Udxi{1u5!%yUsOa+ev=pfI<>{^24{#-)DW7 z+aZqY3ogR7X^2B}mdy8mej1v@SDXAA5q7i)1hS_gQj$0pC`_FWs zEfDjl(1cFRfh}=PU+|2XTRx*L4+|a|X!nE#L~uR3ar80l_|0FKLW%Rg#22*!#TU&J zXPx{wW~e=i;%HtM%nv_>gziU`9>|Y5#0*z0NF28&&41@wL5G}!J7+d1iK@`J?lda3X-D$h0 z)0t*DLSj<(rjrG zwsiYZd<|W)3M`z?rmqpCu+|10T{lb}F~arA*@>4%IoWLTO?st|a;ki6>1TMu@7b!}}Qb`2Ij9N|%1gq#0 zieD_H#6|XzP*VgYqYNJq|67mUa@nBrz-&8!EoQv`BteDzBtUn(6>P!(o}Akg&kP@z zy{uR5eCuHlCD7e>uDZF|{lqyvp-yT~_IFv|OvUGavdFe{HlWrVw=hX94b+`q3I}X} zb~`=!Lllp>y$W(D1^;$`9};~e?G)~EY#ZzwZ*k;5t|2>W=0@yfba#w#mqhU`$LLGc z>=T#J7ql>>kdEW)vQtWSLf0 zj=#+V<6quW^*GEiDxaJ5ajn88JJ#VLJaxn1S>cTufuaJsyI72$Rv0Mc$ao}`YlZ@GoV3n3?2)=i)Noh9O#!r zF()3AVjQUVBYh8HL?=_67m|XmhvrKg*j8>60qkb9&0!y>UBG@@s_JRY>HS2YiY$!r zMB{zt_+q1-mk4fEXj)~l7}&5$)-53=mnzr!<8?UJ?>Xl%Ym0rYKk}Pwmt-q>fhBwa znsl$;;}xr9maB4JLX!8c3?TT>zjSZH`Fb!!At@>+@{8o$J$inivORk~*b5lI>(iA0 z@>|}}i8I+Vcj$HsY`6>R#fkiBM_&s}Ib`p$K(Kly+%L{e%9BQJ8hRKdD7$TbTdvyk zZAe+xulL99eVj*gY`x`0Yq7_jLESLl4BAS-|q0;mR|OOKl4GZO=pFfx0j%GUz}37BKj zbrW4${*blyY+cNCz$t^DU}mNF_m%3E)*Le4b3XjfUYCA2bLAN$U}C2h)s zhUE67i~@~BWNBrdJN&$;C^j8$^rk`yMt@2lzTD@(-uv3vAOi9RL;=2}7fj;NY_%WV zm_C+n7gm8pfnonJVJmCmodxe zkBSV+56J(GY-HkoMjaSg%YQXQ+%%*pe&i$vVLZA-zCiUmbnIWwUbL+LH8l73h^s~I z{|tx3@Qe3)=;kJMl14dWePK+h5oQ9q{Q?KGy^ee1|HffQW}~UC9U+psTmCV++G_+m zvHHOl|Nff3c)YSMLFC1rKabe71sB)2q8QnqAYey!5q=Ox{iq337s^Ie-&8^Ch1FrQ z6u~)G;OU|Ldezxw8}&dHj$UfMiqse{bD&;n9+idTUmuC#GISD%Q1->8-U0~2geSTA z;)LhV>PZW%$(#{7^1r|IrbaP~7VO$zJ`Am)g9FF4gK3}L3yN{?fXsy>;%>IR%L8Vm z*;rB6$7#p67x*qD-Qm=j3bcq@?4HU6!op=C$U5pL+WCtT(<=nD63Hg4P|^!eUa*Bu z;Vpgi$?OD`M2eC|Cr03~7I>gdxQ9cFe=no(vzJ98Tk%u4vF$v+B8*7U1b^{c z=*=+(nv<9pqX)6mZHg3t4(v5|HpxhD^aN}DS30CdFg{V0>48Hg+4k8&Xh8tC}dOuE( zylNnAkS_h9*}~RvA5=2|fZhV;TWOml*6$lQRnN+$ycdYacBia{wWz|oQA;Ox2ZYnhRp1jv1)2&G{y2o+rPWX%x+H5)KkUiGy>^hj@ z8mXW|ZH|Z<%HlX6XEGsCU-L5H@^kw{n+;02_RL*8c};Uj19RL(+NNig2%{2Ii(PLS z{3q#D%vo0P1?_*}Lj6}=nkAD}O$XD(3zzgooCk>3LWKuEXBnh8E3N|n7k^E`d^E-U zlCQUiVpQMvyC4Jl0L$0Ou1Cu-r}CjQvNuMaq6_iS!$&d*>e~A==KP z%@#lm|LZQ{`){Wge9oaBIW4el={u%n)iaNtKi)Hs&os0vjxvurgd`O%+gNm;=qJkz z;PzdWRZdxAfJWG`*=g4T3hhz1@*Od-^YWz1&jjfrF?l;yFHHGNd!TFlo&xzzx;T|l zS(@Jj29S*fV3%l#FvVB}*`vw2R>WH(BPk)z5r4!a*g4YjaJK-n`t|>?`igDm+;*y7NQ8Cy z-`-L9=K9>VU-`cl(?nVG^29Kd=e(8f9>OUQnk?GEx4%ZzSSzhT5ZQ(F)gKYM+_y`z z7+&cU8dS1#u#Py+DUZR}u4_6fXyC#MZF~?pLa3V6cC|1VT!&cEIB$o(EUQj*3u<3D zMOmH*W?-N(+9-mcskR998X}}f7pphW@Hp)ebO?-rp(8chTOV$S`-rtOz-U+*X!irw zdd~813(kA)fy@mPqHZ>MXvuHM{#&2fGv8nprK+h3$S%vcUvJ%VZv>Z4 zemN;gFQYnqcY+J4bvWA2?KuZ;ksp6G*d;kILa6tRkJ;rK;RMVsW00(7^Oso9g{WF#O7)eSG z(!MXKuGa^nv1aouB<0C1zX)H*I?Oxk+qog)6g%Tkkk5~qWqU~Jjt=955{p<{DhU0v z?NuAI5?w3s_c$1ZOa9?0Cm9mJ&A-C-JBK9}w!axqtWC~a;v^Xb7)&_&U1iBPT2Fqg z8e>kSR0O7ahOGSH*D~?rB1aEsKX$Pufd9-|VX(3v5OBNO$T-{;WUm<`uM+Hyk6RtFtZhHzg4wjfoD8#Fpn>LfJO zf{ZIP>NMCib*(%KfUYYnW0n=y0jp$B^bg^=W0Tq4-w8WNEfuOo*w`HlKH?|1j0pY` zD*pfacx!Qx6fGpg<*kYP#l9gjHLHIg>=)%iB|%)|7*yZE0yHZ*Xvcs3rx$^CXA(G6 zCU3JT`JY~d!Kex#iCq=Ll2W`eRbxZHu`mP}^(lxA;11!saX)+t&S?Jil`zK_hw$|f zsz;;>s~4okE8wMcrup=pe8ZvL?{ft#hsLil;1T}6 zJLQ5mPI3g8P=@~us7WeDsKC*G?fvdkaC<*H!)PTdh*(Ar5q+gTC?gyB`*8_vi()+v zQ>JQ8EF7h!Mn)$W&jtex9gA)EK1LP+uXl#CPSpA8R z3EyAo&m+1VOh4I2aE_SAlk)6M^OjpsEk5W!B=OKxK}8zaj!iMp&hyMkRG|3 zlmk7!b?e1%De{aSj2-tal{hJSGc~)p%^eevoggOQenmR?X^{v>P5O)uI!gr0^U)>xTmKPjOxQor2eEIf+)PyGrLyhGTI&qpVu%If+CfU=#Y6wF1!2!?suAIOToNH5U5jebdYw%m}W_RGjpB^`|fm) zPOZs$=da!l@>4_0+OLb-Jbq&c3)^T4HO)i}+q+&osOd}6wlBOmm25T0)ZqWc;L*_# zvbUo62fh_Y6Nm%Jzye3*LlsIcQw;b~haKh;9m!d-cbZZA8O_qjI2fQ*Z*B}jwGB%Z z#VcltjYDJ;DjvZqUXj3dSV>ERN)T0|mJcm8WYSHj1=AV`;(wByQe%0OfGwG5XjAwwzy zwv9oqfHXahfpjKn`Tu6 zx+Q{AD=?)*U)4FR55K#ff7JO*2vdFOlKJ(T%>BEgqf9Zhl*UlC2R26KocH68e%1+Z zvJ>h9o=ppVN$SLKpTj``u!<&(R__Uv=>S0NP0=>!fZLdH*Qo4<@`&I@_rjH@cOKL$ z-@mo%Hbkvwe&WAaMoqD*W&o4ibdHe}?HglaD7ryh6)q=XWO~2)-jNz7O=_SJ^*^Z= zWTPxhdEKF;xE{6SDksmZAVHF7%qu|3YMOzilu4imS~D*=(#CN z5Z`%hdf!ZQPF56SSXG~{fdch6@W(WAXmdJ9WQyM|gVk#)m3H9v&QxTp-#B1;5hhMb zx77Nwh@%Wg%j3N(|G27OB0};mS!ku{=28!=eAjj`Xi}TvI`pXr%4Oj3Kp7y);csDV z!IyIzBpIBgyp#mZ!m^LH*dz+H%$*KBFYf|W=X&_{VQy7f$YCrkWC^H`;l5c*OTc#| z;i@X=5*wIx#fk$-S|Y=kMwGcl6WoYv*^9By3vzdhOJAXaqpIti3yQz+(65ju6nwuk z-pO9+;;BuG0#kUChxUP(^DRE-);X~@=)q)SOT%* zw)#{wcX7~2OEBaUi1`wSSDFdDAMmWztJ)A3ye=k>sfAKfa&C9~x{{LJ^DLVao$KHaIG~TIy z!%$FRIi%?Ky7q?Z_ZKDreJT{GeezDwO2(t5=i#I?i|+_0f2q=;H~j%u@f1I(zxnq#VXW zvP&3`d@EUxoeW?=pURFb0Mgk=@1gKw(@xvXU*KfZ^*EE(Xv_hJuFk;a7vW3R9v7Geb zB?P|2uy-Q)Q|8(++sc|@DgRK-USy}vTiV+w&}zvOn#V2v?DV{Rk-qo0h(N6e^8nso zXX|fmt;LPkOc_z{+GqO+lfY%DwM!HCTCeRG%d2aYzlNAGp=F@DeF-(NQK(!#Ax!d` z{iWZHGS}n{yYtQE8#UAq{C@U3cwVjNyxj|oFLlWRpuctbr=}SR<~*qtfY@-NrAT6(mW<`!h*p!~hIEN~+Sm z=yAn3t6B{yb`Prqz8Ia#bm0Edm=s%4fkmQC_@^caJ8N-T&M>}c0vxemHt1$X7P=HJ zi7MQlU2xuwK%FjAK>)-}{!7S#lDME(O17&;3m}xO;8+Wu6PftCXkDFb&kGlX4<{Wz z6U$$nU8?heiESvIrKBF>?=Ud)vdMeJ*2scuV_=dw1!#(5KYhxK0G4_r^2H+H;F^dP zYY~o)A@mF}@`h(V%EJvAf}5h?v&PZPErSQR0QSg(>Vt8-qdyaE?DJ)mEyJ4i0WYz2(A9A?d#CA4vblRrqHrNO`gy%!8q={_i4p5{u&(AV-4Z4y^*;n8-{!S*L$r zPJD3YOTj*V__ zQQ$IZ{pHdg#5jS=ai&V^tI2ui9v$J;G-^XVSBxWQAzsV;h+lH#%% zF#4Mj@Kw6~7Qq+zrJ<0lFszPY%`0xD6+h}m&AqH%XlWn;5{Cn7i=37%Cz~jNWdM6m zH4p>QJESA1!STbtHq;+2>k8Vs(NlC5D4EB zHCB3ina53D`Ui*f50^V>RKYt>^=L@_F}(61@Tfnmr)EetkK7kO3d^AU%iv1)u;;9m z?NJMCi2~hT!>5wHE^~x5S9*4I-LF#4ajrO$XmH2ki}#n|!i78!Ge#ZCp(fX8*Xskk zhu6$ALmLFR?4I1f-`iW3$)t4!!Tw%+FOFw=e*;ABYfc9y1#K9yDi(0c_Iw2fgAs7t zfB`g{pVhTDdcs>st}nS2Mz&d1UQjqPsGf1;-v0=+aO-5ymLw7IS9JKaLNBtm8jGBo z2oO&XSQX1L$m1?{)gnA2GHLIp6<&4>6FMTxhBy7C^O2g11fG-h27yR&-vl>FmS}34 z>Fa__ssvZPTt^kZd|m%s5k2I{_QP@;6^Ij!L@ll+cUMRiA@Y+evt@~&erc7|@BnY} zSxg|a&GL8X!L}clK^R6{=;hjvE$04P^q&FZZ*c>4_I(T97DEveL|3Y#v7Ja94{i z39~1!OD}VovP77^y%h{rHZ*jCY=BHDNcv8mEg{%H7^+oN-jWPcqhnyQri$QGA(`x% zpWome@=uZf`)<~T?0z$yo zRN5)cDbtL6WeO$kHGfMRd`Oz&1s2!i*<#3awoL|#7(}im*;r!j~Is3SICYd zx)I_mIp>Yu+8-xZAINx*oMG zYme%(l(ePN^p|kliVyKS)!U#jf^wiY(E1f3+-_oEOFbs3&Xov=sxW|23&G2xEgkg< zi^CbN0w!&ZBPa93T?cX4=8^p84<0DB^b=kHu|mLd@znhzN=O}X^2Tz)-^Z~l&E2&v zpwCR1TTy0h1=8EHr2=r$*8oH9Y`4ef%miO8+toT2JI|9PWChK5NI0tDZYJkt_}f_C z7+R;9?6`3OeZ?ZMm~wydhF&D)i zVSxWFLBMng0KB~H`OY9DsN+n6Qwl!2;9BYDp~~y#in{~Sq1T;0R(!9>Go%`Q6rc3j z=lwpqin_iL!^&uBy{f4nfg2{D)k>=7sK;k@K;7TQ|GMPuzz8d4Sf^)A*zEcRIT$-O zDZR4Es7U1hBkL=`s_445rMtVOyPE@oq%_hgCEeW|B&CG|9J;$3=@O6>1O@4o?vnH$ ze7^sAKfjmjx-SlgwP(-lnYH(x759RPc^8lQzCKZ;j5(1hQ=i>`$>lhOb(EC+ioW!eotqO*rrImmm$<#-x3LLqBV~VUy{+ z@~bP_#*grgBOB(vzn%;E7dewA8aw&elPJGh8d5|hjl_o@LB~V28PPqF%%Y!y77^GJ zx@Y3EX>J#jeOs5GFMsc#a(pq<6EP%i;p!T;%%DOI93HtMz9l4b>ma+mn-+PCNYwc- zza|6u=uQjHJ7(OG42&wL4rAvyAX0rTDZ?I0+Y?5Y*&ZeQhTFH|U9~Vb0rZr=dRwEr zb1~lQf$OE8jHDqpL-`1kiEaP8DMKra^0*p3dbX-cO*;&G2A}S+dry)Zf-oX!H#^wZ zqWZlw+7m0~^KXabw?ZfIioaQQdV50#`(65s&*W<85ObV<>_)x+mV_|Cq!z~JY*}b= z^#Ou$LGcQH1%And%)Z zj_kw1Q?A8spOAR374ATy`AH|$(E;p7vGDI#h|Y-mLGb|1NRsV@q3vMVG;jX2sL9$`dhoWjsh=r*WgRV zvUhhIm`vT<^BqTz%AOs6X-#BIR;+_fu83XHEyAZ&+Dz8r8x`XxsF7YZ%hDN@`Qd)F z&yPfyq2XU)uoA!;e(NW?5%pn!GCRs(#UE^}LmO`P_S6Tdv!?BF|KtmeJb7cuk(}~B z$^Q)9bUB_wp&3C@2vP$1hn%oHHCcIMpjPO}EF~SvAR`dQ%P_Kvi zY&cmGZuNkY2HkNU7_rk<8%Qr+35sC~2fWleCn)s83+8U}+G22ye%AXg#l!gEUVSIE zbkWfMibH*AvmaFv}iebdg6_6A?8` zH|4}bY8|~qyw?ywdPOT|kp!+^bdDaHW%a^{ZK;jqI$$V)c3q4i8`(V5v)Q)U4Qogq zuz6(^iTjGV(nZ8ss3h2L@$`D zy@g&Z zS_nMc0Eb6}Wi*ieADIs%6{A?W`5&~GXzWeNP<1t}<+Hsj#4a?#GZ2Q}c$8_|ZV8Y~ zrCH9q7U?WDxJN2+fq{?`KRGH{Yq745`mU=5XUiNR9}}|LG>f!lWQf9GW%+ld*T<>l z9Zk3tB|@=!;ExAGL)BDyX$fl2S5b;m&XN?=nGMR4<~=wCOq~56JH1zy@k-uXtELPYPHJ>2A%7=I zD@wUoF^Mw{y=n)n$Otz$Dan-Rjk3xR*0zG^-mUxyuqxn2%WhfS)or10pM@)-b$4`< z(wK1$C-oXOo6XL%>j4vO6$v>uFIe*(mmi2V~&@)5s( zO9(a0^mj$e=s+m?i*@UAA^9KqjUfv}dCUtXeZJxfw5nPvk6ufwqcVCeNBhbc(NGRb zDZmh)kF~N+sZ)Ark`uM<`-?*p?vKJcfsC7$W*>88z==W&hV$DC%moNK=^r+sbjOl? zUpJRAP-tT|RwyF7H)|p=XvAi`f9vTLCcT9Zw9W`k+r(Os&V5zdN_!u-6tp^@=dd~y zMRTzRkHW}neM@6?S#)^M`$3_g^p+!~co@mA74{q2YITD$v86Bxqhut`t>7;@W$lus zjL!kl2;hiTLzQI9FZB65Hw|LOb!P}y$c{Vq@7P(Rmk)b$_DJGQB#r4s4^&?T?X`cV zHMT>3s1?t=CDC-2yzpY8AKv>VaG~gj>G|`Gj0f8#Mr2dh)ir_Z>CF{!zT$K8vZUgm z`70^soy(nKy&kD)N=fqw0aE?M7O8>FW@|%|tdHRSwky*M({W46q+mB1+TVwto%mx* zYAKpmR8?hJ8PXt0K8%VvJd$DwnaP>H@=33YWLVKmHMXnpao^XE;LCiDb!4Mv7i^zEGzqHriCt#KZHJA zZsCJ`Nd`47 z3(Q*Nj()X)eTg}G>Cur5Ud_#bn@xKM%)@b)3R@NSNiLHm$J7JGK6G8PM1OrcTmfvO_?@*ungK)rsIsa!M)Ro<{H)Oli1-Ji zO4&z5<^X0log&t<0Q$5sW?(Tc;vvgQW>q%S3;+jEQY|>vUS`tqt28A4bd7}-*0CEP z=^+)!(A;Lt=Z#aIkE6>O%yt?~`NH(ou;_;@aisRT0pDeMVlsDYu0;w-{z-o87ukoo z4YrG+>1W*=f;=slR6i_a;irE6`0ky)FlL#w=IxbfOY6*{{ak9%{p;F_ zU!_>H4qn$`dGWv%dF_hnzTk(w`aU@A9|{cnKZ6`%^T6&y^*=6cGTIzGFAgc4V1B$a z_Ya~}5RxeGDPp-Csuw>RRH$3ICpHlghEe}4piq_+JQW$T>@=d;{<9gvDbRLQKR?B_X`j3N0a zYE&6^8UtXgfuZrP+<_W3q8VFt>@(AQ0l42Y%T*b9zU@T-&LO#R=zBBRa+AI(xh1Mlf|1&?8vum_WI%v0(1Q1bp}AAHV~P#LG+r#&Hpoc0^my6UbScaeNzWWJQ+P;e76pZe z9h#5kEjCB}<1!fgyK?+G#}a(}Ox zsgAL27lJ*p1)kl$k8A)qhVGVPa%r-mwc$tHb`C;v_Xp#9))}aB4x%mN3oeB9@3Y_7 ziCPgF?%!lqBGWJG{}dWPuuy?_AV|Hh|H;5M_Em)!FB8$*%&(ABY=)Yu!v~zvM=E!P zSK`a%NS&-({XVrfN)aW=J8nWm@&g(f>=dbK>Fw^lXm&<(#9la^I5&%-i9qGOWC(u> z_e)BlZ;D%&!Q-iU6+7P~&UlXexvTHPVUwmw3K9^Upy$ar6V4zV&%sZ9ZzcC>X<5b8 zL<=2Tj!&WY5g%c5sD7M=zef?YMc6B;m7x=o7o%awK)mCbJd4ZMU4?{d(=_d#I?}3 z7_25$n=8=E!Ap^3Y+T;+j`LpPK+RvQZ|>DQ5BY{zJ^E@TIvaXX7x)>sI+_Os4HX0t z`^c1SIt`%A!-B%J97iN1JRy!Tf#!sb$ZS>C3KV z6!1ps;olA`pssvZ(@`%a{qBQMTXu#Zsmku<>aC)S95~9DZn`Q5a_BRP=Z4Q{DUy%34PX9{imODpE z9q$R*kd4rj9eE!45CbOp%@kR&7IaRw$rh}?pqKZvh$oStQ7FWGkm@Q_p7e`Vy$+4g z@_|UZU59ltd0UzSu9oX<&6?a17jcWff2p+Dxf^d=s-j6+U?vapy;% zzcu6BaX#(4Ec4qHm0!EOsCGyu^qawoUdbZkg(Uk%>8A=JPh`APqQ4D^#2*&JZGjnC z6xd~vLrjbCAkQI~V8tiPf7g@c|HniZ6^!o(<~oT40hq*mq=M&`UJQJTZa4qLNc z^766dzgC}zs({V|&TlPedNI$9&k@k)ddFuo-kTqAanwKbHpSM2cjk;O5MOUB2s%yt z%((FP3tw~;h_?R(#sZ8EZ_s*50v51@IyU|4#NwUjHdetclkQs4SaS@|#|0kXFe$=E zC@KP}GK_eS5~(qfIuPmlHR--Zx!Azx=>;JZh)PTc+s!jjR(LOdiS7B=iBkJn7m;t(tyInpNMq^|Rr^9Vb+2 zZl-pxZdVfgTQ_!mb~lz)vYtXzYsHr&yf|){Pp|%t9X;~IUCmmns}&x4}RcH{|z&giNoN=lJgQQevO6E@jQc9{U`Ve%PKehojaoC zH4%miUZ16EQ6oV!*We&ytTLp72mavJX!_*=Be(oneVK@|FK%q6X05z3afTioyPr0> zd1C#Aze}sb?q;$t(#92?j%u=phFBb$N^l9`$L;V})m%bVC?dH^a3WfYwzVm_G4O?? zh1@-0LSAF9xmF)HjM-r3J2r;97Yq$5q_&b0{Up2!JGqt3eNwBRV=Ba??BKpEY>8i*WXFlmD5n`K;S{KSzXS&#PR7U3S_%yH9+Ex%% zjnAI9xm-s7&7S@{^sG3eUIrP8@jLCy4c_(+I4^9igv72FUYOg_#2?z|`2y=4I>UXz zJ~97`>q2$N;uVeW@3}Q$9wU%8bQ!lg4M9Dz5o>QkUHKI*jEm-H^>U9rDNn2t#O%4{ zsd${W515lnlc5+B2ivEvHjtB7HesilOv7Ja;?JPGbPx$sDBpeko_YUz`|{T$7S22? zE?99yt&$+cLZ0}wH0nSp-HS>Y^`_z!q$u_D$juGWb2Cc7TCu6#&Tn7?H!9I2PnTX< z_Hmk~=tDD6RU>O%I+OPJM{((U_J`}_)#*fvn)k4#)W}UM6R@Kx8 zpLfx6v4+fSxtp_=gez?wwS^SA&iJrzN+6G<)o~AmjU=w|1}%Iq2`#j%#S>B&#J1m& z!r;|(8JT(ih`~WGv?R$5q<)Q^8CWl2kbl)WK-{~42pdbB!Y(!yUp=rffb*2zOseLo zaLYr7mNIr;e2VH9HCBKxY9YmOse1kP2CW%pp@kt{jP-2dsF$K5jY>8ZR|6C4&fFb) z+6hVg-UG91|2o24P%2H5=X70;l*)f#(&My z)kWD_apKI<-0yy8V_8`@&!oWTIreTqN$B;6y0r#&l&-w>Fq&F@kGf4v)5{bhVq%R@ zZbV1!+lu%IvqjPvS?VY#_wat|XT1s-W(<4+Qeho^$`EU*BDYkcQzEdyGwh+yIL{G= z(0NBmQq?sO544o{;-8YvFBQv_iy6c&RP3$9VI^2h(^8ndR$qibT>*lS_^H}RS!|`e}Mw-yQiFUMkQ#b z^l>sJ()A`Si#s7GIy& z^+s=sHyEUs!eM}0h2jeU)s6#JTb8HQ7Rr_gB87-$ql1029v|N)!SP;IHYx}ttbe1- zCtzud&cj30HL+I>d=Oa0vcEB57BkFU_R}(YC@*d`H+F+q#I8F^G-1Dw7Oz}@{a#xZGm;Z1QR;j zF-TTP3y!9XxI(-`3;?vcnXI21eTbMTkJ5SkJx(SHrR?W5L`e*p(thuWAveKe%Aq|k ztck93b3jqVmfm~_TsGsD5y>Cg8_0W4IuV7jkqss4Uk zSMImOA}mGYb>XJy9Hic^!z=(pQ+!?;(v1|$@P3}Z%hcOf!ommG3nHOEjQI_+V%5IC zda)}m!C@us0Hn?#co2S$^!eyM;JX4+@#7?!;H5?8eh!Dik5SuP(r zjT%pT+GYB5UFtC`{pE$|=&-ic?G%@N`}iUKzE zZx4|qm0C=3Y@q+<{#pw;a4peTKg@s+35oxryOQ`Wxn3Z?8CJc{~Itszkif?dfnv3upbJAFC4^^0XwH|H)MY|dwkS`TN zk5CC?Ic0{mn9HV897$|B*sYH$S*|es%PfBfWPR}$q`Fb)+tUS=@Ilgw;T7qbB4{?! zSYqf?gz={)i!$jr`X{qw?`a=1j(P{EK{a*>3O2!g2L)IyF~$~k1jQflTluk6Y#XG5$l=9e#XE5bSqk*(HaUi!@Bt zNv9lS{}k6>Kr)gponk1zY_tt9L>x4E)=9knG->mBn?7Q560ktt8+6QL61Xc9Pn?Qf zNWt~9h-62JeCzn?Gfmn+mM-5;!Mgj2l_&yvF*Snd>t7fQ6VH=dtkey4Y{_ z0zC1iQB`h-fvsi@#`dCQsw+T(>cq!oVqpUtGfzA>uh9cb5te;}UVih?#O=;R${||K zoaV;)h068NOyH#_!MMbU_n=TbD(E8%mh#k#t?zft%4dzN!{Fc8#xgQXZO5~}>?_qC zpQzG)2K?d%^2%Si*Q(xBd3T|01r<->f&;4Tw?~`rmVZ+WaBFWYzF3K1F;|i#p+4n2 zeLyi|Sr?y=NHsl3-MbE5CHn2tEemA2>2wFDI|~HTe%zNF)9ru}48ik&>ur((%sa|- zM8lzv2rn%8mS1w`uRpEF3pZp!#ToC(tk>hO{9(SKIj`ttCdw!chIQpqEie>=*;G{T z1gFXeqWXD^C-AphNVz?#aO%$x{NM%M1%|Q-e2!U8N__KZQ(w(w_6K-K!YFlqNQ9e0 zYd5{K4dfXklZ&ETt9B2cRL9lurj6N*Tg#iA_hoESWTOo_WEkV3mZ1MNQ)=>yN3IjF zNfsOBA5Q>Jd+#Cxc{95lio$i3$YCW}5Y;r?AWyS0OM?+L~ zUBA8?e+22Q9>Vu8Kn|Sonx*n$Zqs3)e%+^2#)kFfD=O#jg9HPd+L>|HRi^2&04Jim ziWufp$q*t(LQ&D+dE*5*b9W4{Z7p1aI<3HcT;05Bc#&hnK1{)rW3}??T_B15h6{?N z(TQK`45z`IhN4~`I}S-R)OimQY@JqA&qYU_Qst#o>N<8iz2yTiBKCeq!-?XcM6xij zwJe5mR?4WFBD4R&b zOrlt*Fyosv7y9T;y6Sa5yQCKpf6#%g-Uk_b7aZ%USvB3HQo8J7)0i@xGL4^rk@th; z_RotjbQMtX2D!2!IMiZ`l(0oWy@!z?9~KCqCJ+!Zkd?0NwDCWV24TIxXv172gQzIj z_9cYG{0JH96Z5xjw9(~sjHi#c>WNuQFMV}2CuPXH1mk(*3uLLD-|*}GUS4EunGB!b zzbqF^pI@lAJ0zaRl3D=YpHK(BIn3?a+jf}kYQ>6OvRg4+aMlIlj<=Cx!;Y-poy>gC z1BW<9wpKoPHJhU?T^M~odFBDX@r^@NVm63%93W6%#p)9O)H6w8it_&5>7@16MXusQ zZa`IR&4V*bga+svT=+wBbWtj>_ioZuOaGcY6|w~F;$AD8nNTjD!joh}s^ntT4b#ZN z8DZ&KGhpSHVx9^$8=7m@tx=*lc#wgm1XpOkxE%}|s=pQ=6o#YD1#qwD^Hnr^svq6g&?7M?78vImw$*upjV5rKnRh`A?5?MlGvQq27bj36|U ze1K4z_~q6eF-oV$sDwc?z2N;;O)dLe z-n7L*8na(Jl2;{>Xbcy#C5G3XIox8vV4*8CCXM&eR!k}o<+~Rbe%>dy zMZIOulZHYz+{S;&-0@*O_Fy~ zu~@YXP=v;-EDqFP1)iBI6L!94(wC3DhqBM|Q+f?J?gWY(le{5`EHaHFjm3fG;=7u0 zrHlO*T9oPHSjs`wndl z1E+sFV-c|N7lQI`sDQ6?@@EHeWWKcLgWA;kH)CszyhR)g`KF*lh!bh2vD8|o1o=~( zqy+C-sGC_-@T51`xKmGqqsi*ylzgAKDNt50+gx0b9YM#QTmUykk~VBns)KSi5950r z8dZW`zUXs#lav+bO6&>QZ^jd|8+JR0Mx3uc4#brz=_bpEfb;S5-Z!VM`y^Rbn!esf zYA;O?)R<7D)$B?b28)`9ApI%AG>^VdEZEwL2e5AM2lIaWHO zYlxN_FWC7W^P>*JFe0|{`@7?G4eWn|g5#A5!20&v6*=03TD4wT7(;>C8*?Uh*Q@&- zeSVcRuZY^6;~io-I8}VCeZeP7)QAeDBAIyHJoZpqGdiO14`Csct{;(%Up~E(pFPgR zu=fW3{@&}#ov=LM6pYi%l-qK*I4k2l*4tX=dZwGQX`BON%rR&rrqvgoy@l1~xkh~q zzMowq)2XY56aD$+(7lTDj+m4t`iq~n5y^b}6V01$sj6ug?=Z8XMnZ@Di%^j~o*RNa zI!i(ndT6Mzdcu8Rdt6~Z%h3Zk$XbfZ_P#3v=K;>B&$@nKJ!Vf4*Tz+Llrzuc%X2Lo z+fTfy3{~jLv;Xjs7{To3FqhVNcC|eaEab_nStwoZ#Pm)6`#tdk z?vdCSrY<+f#^7$N)mhn<({qa6f$fr)#D>9LdFsW&+LM>dvN>4#ZwXy=BSOG6c~~I@ zTjHT-!;T?tTP6cW2X@Xg%8D=1 z^o0;}(Kyih)lQOZhKZAqWrbx?kl~~Ab>n<5Pt{g68{}gPIbG{#g>lH`wlZlE)o7=S z6zN$TYju$=P`McBB}0_8##F&s1s%@n`D}Tch7G{PAZp;X=PSA?FjKUm962-!yTG=L zH|L>&qExE9$ox6kh_`@VtI@=tRt+ayX+CzT(6;|F$@b%qtGc~Ndhv5&4u8y(q>77GK|!#~xWrg%t?CXj(f>YPHUf$KucCMWc_6Gsgy3(X_bGl+~x> z&7}%yD&Ziw$R>|(^04&I^vEBkFJGv&d!tqBAA1UwtERgfI0s8IjkzmS70~Bt+O#GZ zyph%TDha`Db}hZ#@OkkftU1D#A$YOq?WlF7GD%Y%X^UZvzrpObWn5k?UN(DKMzhOKM#Lv&7>w*e>vC5y23w132>0Dv(E{=ElE>L zw-6Ptmr!r&yDuzViBO*{Wp2e~bZo{s|&cMg%>C zfKVaWP4M7F0l=AY#~OOrirFngx?|csqxcoRHkvY6k~p!zh+f}@)rP_e93b~qB!z+s zRay=+$1A)aT0k0?dY{53Zi1zC_J;0y?vnp%bYS-d)0Uuslv z=v~<-W$U<6nTX$+b(nLHPOwy}TGM8C-+z|ORgiXUq zm?&7%C7QC702|;pC@k@AEmzb+ThW5VKzfHd0*Q(B#6s%|+R>dkXlVle#oq@gwgtKH z03qeA^XJid!Fb_MLfR9QVP6@H$Pm6)YB~=RSD|6a^1`SLj+Mk5(+Q4-)uf+Xmf>_BcV<- zP6X}PP!CTs2BRc%d9r>rU`P}{7L=B)+&SP?)2||?J(1F2ToenDTvqT@%tQG!d7&ID z?g^Rf=3)pIFUZ85m|Rz!9<=&Ewq5CqXmkdK;n11vcy`S`VB#mPo4uS2bnk$C75xt(|^_`_M4$0})2< zWRn!djOIbSj9)+SSa~Ga?%S)SsBEKzts*c*a}?U3&X`l95{XG4k%)0RzvGnay!F4> z_%)fx%wYTzzs5~APtx=?$(JfxdJcOqe2w)xvI+z#6qZj+6z7s6jnvLFs|DC5p>Y1m z=Q7PlUoXAKq%?@O0u@9jQ0;a27;5*c>AgoiVdyZTlvyO~#+j=Y-#S8y^c9qvHW0lu z%1x^&u-8A;=iK_I^0hxBXcVtPE|w5r1_*#u-;l{Bb;u4=IB2Zx#Hu=+d+W)7d8US( z_}}oJXA#K|l0%q1QY@6d&KQDRu^@bkqBpt6 zh-Q^GzNkYorq6TY_hqHIMF6#7=B%+k!vsvgnE2K_JAsm`Uw zZ+(Eyw*hu_BCvzg+y7q)=zj$h{gd=tfAA)eL85twnh8GYDZuu1^5&8UK!vW?USV{Q zXKhcy1q2fE>4Uyn zX}y%AlbWY_d=tteQz7iA4S1={Exn-7ofNf3^UPN@tPO73B*Dbs&r7~q-=+e=_>zNx z$=7)Ma1tJH zjxE{1?;LtDvzVvTv@D$BS0^rmBXEj8Y4snTW+ErvJ8rJk@P`HpWluc7j_|FoqeNLW z+pL5KMtk+LVd`R!Xsmf3k$`6&;XmOyUtXS)#&EowG@I~10XOYNm_Wwm1leIR)4fq+ zN!G2c)_1kRDHtTZW?|H~+~<6FR`PXl zFruITl#excp^zIUKpcue!zrW{Wk%;lSGx87V;^_t>@aqivYGYkIlFR~N{Zf(tb`9U zCF#O`cDB*ihLN+bGQ)@R+Z1f zWB0z5=V%W5>8se1Xq)yk_9v}XN~BW>@L_-;f1}XY$zqpzZyTo(^&F zTQ?m-tw@#i$J@k23f7nNqo{3j{o7e=dKV!O)Y=6wkdKlPgoyu?%glT2w5HA&Hjd^oV`DZUq7bzX?5TsKZB!vF13~ zExHNMf!D#B&l`nGp=a|d(+l$vYCyvNp_Y^F>~G>neY!~P!{JUx)M&O*ItDN@eK%da z+$%1{KrdC4m!S+o4>4-q5OK=ccYKqf55_(CuW?t92|JaM1{JtkgPSygF^Ga<=G_imWaw?C_Klzqwpf zw|V7laMb`>yxSxvZRm@y`v?v{l(>77yf#+uI_bwXWLeyg3^6NTa{;5n=H0*8JGthc z>pjLAG7YascR7t(=hO3`Dp*;9XwAZ@AmoYKljryiayE4bd&3j0aOga5gSRKThbB*! z_8L%hP%iPI5nC_yHjlIe8vFt?8M2V?;^V%~(dbcIfJMf0{yZg*5=3qrgBI>j7RJ{DOpy{1O>Fu&Lcjk_=4R%^` zOUa(MtVGkL@bG0?BtY(}X*K?`LiSR;s#F@oT79u>P4PsZ{PhJn#?g)uT!G+`-?giL znn`~9HX-mb-b~|>$1AOo$gKH19VH>6nO3_0Nrw@L4C>Jpz_zxlg;xmT-rv%>;Ln%S zDPr~@mI*qqusdXg^VW1V($J#LFt(#+;b&=-`&BhpbuX80d>7l7D=Zm;W%7jA*D^nv z;9A8L2?_PiFdGxm870hDrj5pVCpUx?0I{k@mE5-|zq92=D*n2-o_b zpp=ow%v%#aU-D0B#~HJN1%v=KNBa1&Wc1_5FGqFp?1Kk`E!h43oQa-N{hQcEuHNLw|b%_#6CPQOg=}js2W) z$#a0Q!xO2Coy@<7!F1Q2jI!2lD*0O(dc@9k*9xQ5L7X?rtMEL-bho_5x1nR?(oAbG z{&2}o{-VjJ?!*ScF4q&i{sPpUWX`gsdI(cT3M<-cW$@dBi^F)-ij(fjr7*+HZo$a9 z+x)iGX9SGkil*8mSNiRSy4-E0xygpJsTK~=l+NEGq8lt*O%Ww$ejuameIdGBh|QG^ zQdWi3bO~XSW}b+lX!NG^0qdlj1*P!}EoytAvQKE>CUSLwr36g{dZQ~NM?{Cx$ zv849_FKMdyk~T6URXJ!F_=&SrnXYKO6=jFphWp(xswIU~!-uisHLblv4onObiVAD3 zZ%4Ful|MFwqcSApHOY=p2V~Lr^qh)%rEBabeZKMEw2Qy9?Z8oBvT?nkVYuVAjMFBz z^2TT*Y^8^fZPq5Gui6MdxghT0s&?z8=to5c<1(XY_MzGg#B&c@G(OJAEkXqvn^sib z4Y>swC2+u8JtXyow&>x&gPhZPmJ+b!EH4yb=Q7 zP_iPz@SmY4?!zoBo3iEn8H@~1)e{18y>RrQ6N~xhj?uJtpP21Xc0Us0QfPWMeSO>X z+_ma;6wU1SN}`OU`9uu8mF(FK^Bo#KB02>cwLE1GYib=Ig|Af?&$U`jG_}yIpqhl2 zrD5$ZKSy0QiSW%(WN|usMc98ntB~p! z{4!@%4eBcR#<^o?x5={Gfu_>hED~}Y+YmtCbhSZvpH8&pIONtRpj2zbTt}y5-K^~P zuEj?lUZto9;SK%67_^B5GStWe!~s%wFxc`_s$R1A8sV=3BV5RLt^+fy ztwZjeT`+h>;&7JzLYQPgYP^{<2mk}I?#D@F1go;tRjSGNxb9e&$kSg4u)a*}TMU5f z^b+2fzN6e+0F4oz>=f~vzD}r5Jw&It1ZlBmGO~ z$2-(F+8+CRnMwbF7)o~P%ptkV!|*o#^0O>bCqq2R*_9=L zPN1jXxovm5JrHkp_FNla6i5bD(S*-ddr;mJ`M%yyP4eAMChKE+gl+I%_zH(1DWHa& z1l-(Yz=}%o=LXxZ5QFysUSBss398a)G<+Mp9?V;STJYdVZ84NaFR{MJKLA>2KPnq6 z!bC0c=_r%8g}0ydD)l~{P5fJLGWOmk;zBX%heb2-86h^J!(Q#)Eu($ec30y0$EAgq z2YXKsp^fTv$Cf8nZWz0WC9JU(QPPfQ>vdJ>nC(;_cPM6bz(dn5J8P3>f-vW($F)(< z+UEGZ=DzK?`Hf_FBHv9OZwyRQoZw<}4jS5wge8UJ)SqxkV0vM*)aIe~GgGM?YGRR( z2vVj*laSoM-w5SkL$4kKD%zN1E~~;oHauQFo|CMNhEj$Lu0Sb6kQD{antWHJtUPCa3zz|1ampJVJ7*C2AQ_ty`=rX zG6dEWLM4gV1FbCzLr}?l&rc(o!7RDC%s8v9ba8BqUAMI@)0k?^(}s~&FMcTI2(W+9 zwL&eRzy|6wfIq#@=Mp8DaM7f}%+lIvoJsT!Q^K*erfy{C3Rr^}`tGYf1l=w2d*FCB zY}lODjjm6cuY93?Z$Hqg!cXr_!5do%mJa4xmd{41vy2f&OW8QET~wzJ&Z*l3zu>h3 z0@X5Gu=B)~*Q>&A*e|UHDK3r+z7K}=f6;zTVWhEE2+YcEq82ZrcKQQit-fRrF}Q^o zBovp;=sTglw2>w7QkP?{ycghICLI0$#iuNT=7*A-_kvf}00K0cS4JPfO1rvWX6$)Y z(Tee>^c98#Tr@Q}2S|SqUq+mJ``fp{l%wIU^Iw|~@FKi)bui0~tE`%~EJ8=7h}V!m zw4`fI^Xa*lV^iPk_5>Yq4ovE#ySahgLIb3e{uhOc9`MX}nYyBq-D0Nd)Y zPEBZLkq5SIr)+F!%Rg{WtY3(*tUv6ag zb2UEP0p?Mk1qzOp2Z7{jFKI&-&M9 zSGA|mlVD~KE+bt9e@SV7_c>2$#V#%%)*$608cVGD3}+e|N~e|S+lkYqA0Sjhh z*M9wm|8b!SRiK&m?#=hSo9xRiz(&FH;+TbQq=!ySN;?G&*_WJhU%3SX90JuZ z1iu)|5TsZ$5n}(w70-NLO!HXa69WWq7vG9e{~83pD13!jS_`A2hBq|t z5%bTUI1A-HOIe8(IrG4<5GRi-34DjDSm-NT76-X*iu$$Busn4Uc%wiSEH9WaNVQ0qcDwif>!c%}!%L45VeAVPxABUQ+wl@2t z*hQ_8+uk7?b~(0I?0iI1?Z1q;8Nr|DqE6~(*fT-t9Zed2p5!X4f7k>K-u_BBA+lYH z|5D9S=ynQU)&Phjo2gJ8@&}VrNaiCT1SCK@Cv05H;m{K0=`-C^wH8b|U&E^za*r1_ zoA5cp4K?=j(=v0?ab{xacZ|rV{CEn5;67J2O1cw%GMgwmD@m+;p+Div-AUn2KOs-j z)sPYVS}u*&&(+!BX1v zow({Ule59?Y9A3s80CvyKMd%Ydq#uVHv3w-o}6c>LD6@@5|p)_>`c^Fi0!C$>E=(w z30tb2ht<6xDd(kRQ!L1BC3CAbf~UsG>Uoo)6TH@&@+4BUz8e0P=2ro2logNet&}+# zj?UjC;iJq()wl4hGdAbf;Y*62lPOZawzjL9!ZdW{r~~k_QC$7VEc)K(K2Z+DZuY35CZ8qb4bzi4Kn@r6KM^G8-ZEa&F`hs_6YU| zadryA#UBt1jX|7tY@T?_{)!d8$%oJc95 zauGm7SW2i{1V|R95!x03;v@U3c;{bj&7dgnL9FEe)(`xz6DMJYssR#Y|JE)0FNu9< z%6kwS`M7Ft7u^SiPfR* z(I8paSS@HrG)RQ}U!u_ex|nnWNSZzwlsyJG&Hs=U{@>HoG={u*LkabY0gg>*4%sy& zhju@;_z!W~|2^eqOXz+K@KnTFLlmvZ{u$DL`MUn=7ERef!Lh(i{EHmuuOUcqf>1h= z{&Sqa$VL8=N_T@`yHY{f;(+rM_JX8&P(yv5KCk#f#C@p$Y5gy^!hem$hd{`QKN%Dw z9%$hb4CxHwfLc9$rV0l>GeO(qL0qt7k$V&nZZ&O{%IDL2qGc>H<}da zgz~Ru*(K1nM34e3v;v9<0dD7i0@I#7BmPU$u?DIF0kM$(9kue0$Uk#YPy;jx0!$76 z`n!SN{?=>v7U;rLFaLVS{_CQ>+o7~cz%l-{r~?v2f1QU*H`F!>Bn4~N53NZ86b5++ zdYuG%1*@X-^rg5CZA?MMYF!(~;$K?BO+D4{HAzy%M`K(*6=k*Ozx=B5GnID{5@kOoqLMWcm^ zrUR{#T%l3vAO%#yc$8;W7LFEfHjp4{BIsp0hzB;T70R3ev^Beix@7=U)#d@T3-}GU zWR3ytG{K;H>H!-noCzX@JJrL4PHAKQ=@c7!sfzW_a6Db};X67FXl^FZo_qwlp9zwK zbsd57Wr397bYEgagMAZKr?dQ{1pnhT~Sr765oG4E&@Ks(P z3G`hz2_@7a7sLV=(M0<67ap`K7bF1Zu<`#UJ>B<#v@FDgGG19^Pb?LfuD=i%yaD^= zrW@>$Qzaq%i5Ku_k<{_Pp@rJ z;+p<@5zs<|?aI>){Zzz3M@u*`1qM!UTx=c1RQ+lC-NnEFxu~c%eWRS3+;p!cK((5C z)TVRp1oFVaymSfBrlhme4=w>}h&n%Ab}2A8j$EIfxfB?iEzhU#1o5}MpU%1r7-OGx zru#1g#?%LGz3CS;^yH`SS_U+5$6~$dGZyH{P2WGqnr*tlOlz*`qRWA%GU-k?Tn>yE z{c9jvczW-0Yjft#2L{tK?;1dZ^9M*>@rUUmD}erM{V+X#g>^6&yMhTc%}jqd)mm!$ zwH3fbb2V-{*Gg+IX6ZDu>A#cAG^W?D1SZ4XP19Ge1UhW-r0HK*0z=W9%WAqfyA?DP XD^~$ccM+UEb(OU~+l_W>V5$KC#mQ6y delta 34889 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 QE%2X=837?96a;+we|q!4m;e9( From a19e6a129a78652de916efb44922ae261002ba1b Mon Sep 17 00:00:00 2001 From: Paul Dowman Date: Mon, 27 Oct 2025 16:37:12 -0600 Subject: [PATCH 10/17] Add new precompile limits for Jovian (#709) This introduces more realistic limits on accelerated precompiles for the Jovian hard fork. --- core/vm/contracts.go | 103 ++++++++++++++++++++++++++++++++++++++ core/vm/contracts_test.go | 5 ++ params/protocol_params.go | 5 ++ 3 files changed, 113 insertions(+) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 7d20d5bcbb..18dfb9a9d2 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -227,7 +227,29 @@ var PrecompiledContractsIsthmus = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{0x01, 0x00}): &p256VerifyFjord{}, } +var PrecompiledContractsJovian = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, + common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, + common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingJovian{}, + common.BytesToAddress([]byte{9}): &blake2F{}, + common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{}, + common.BytesToAddress([]byte{0x0b}): &bls12381G1Add{}, + common.BytesToAddress([]byte{0x0c}): &bls12381G1MultiExpJovian{}, + common.BytesToAddress([]byte{0x0d}): &bls12381G2Add{}, + common.BytesToAddress([]byte{0x0e}): &bls12381G2MultiExpJovian{}, + common.BytesToAddress([]byte{0x0f}): &bls12381PairingJovian{}, + common.BytesToAddress([]byte{0x10}): &bls12381MapG1{}, + common.BytesToAddress([]byte{0x11}): &bls12381MapG2{}, + common.BytesToAddress([]byte{0x01, 0x00}): &p256VerifyFjord{}, +} + var ( + PrecompiledAddressesJovian []common.Address PrecompiledAddressesIsthmus []common.Address PrecompiledAddressesGranite []common.Address PrecompiledAddressesFjord []common.Address @@ -271,11 +293,16 @@ func init() { for k := range PrecompiledContractsIsthmus { PrecompiledAddressesIsthmus = append(PrecompiledAddressesIsthmus, k) } + for k := range PrecompiledContractsJovian { + PrecompiledAddressesJovian = append(PrecompiledAddressesJovian, k) + } } func activePrecompiledContracts(rules params.Rules) PrecompiledContracts { // note: the order of these switch cases is important switch { + case rules.IsOptimismJovian: + return PrecompiledContractsJovian case rules.IsOptimismIsthmus: return PrecompiledContractsIsthmus case rules.IsOptimismGranite: @@ -309,6 +336,8 @@ func ActivePrecompiledContracts(rules params.Rules) PrecompiledContracts { // ActivePrecompiles returns the precompile addresses enabled with the current configuration. func ActivePrecompiles(rules params.Rules) []common.Address { switch { + case rules.IsOptimismJovian: + return PrecompiledAddressesJovian case rules.IsOptimismIsthmus: return PrecompiledAddressesIsthmus case rules.IsOptimismGranite: @@ -823,6 +852,23 @@ func (c *bn256PairingGranite) Name() string { return "BN254_PAIRING" } +type bn256PairingJovian struct{} + +func (c *bn256PairingJovian) RequiredGas(input []byte) uint64 { + return new(bn256PairingIstanbul).RequiredGas(input) +} + +func (c *bn256PairingJovian) Run(input []byte) ([]byte, error) { + if len(input) > int(params.Bn256PairingMaxInputSizeJovian) { + return nil, errBadPairingInputSize + } + return runBn256Pairing(input) +} + +func (c *bn256PairingJovian) Name() string { + return "BN254_PAIRING" +} + // bn256PairingIstanbul implements a pairing pre-compile for the bn256 curve // conforming to Istanbul consensus rules. type bn256PairingIstanbul struct{} @@ -986,6 +1032,25 @@ func (c *bls12381G1MultiExpIsthmus) Name() string { return "BLS12_G1MSM" } +type bls12381G1MultiExpJovian struct { +} + +func (c *bls12381G1MultiExpJovian) RequiredGas(input []byte) uint64 { + return new(bls12381G1MultiExp).RequiredGas(input) +} + +func (c *bls12381G1MultiExpJovian) Run(input []byte) ([]byte, error) { + if len(input) > int(params.Bls12381G1MulMaxInputSizeJovian) { + return nil, errBLS12381MaxG1Size + } + + return new(bls12381G1MultiExp).Run(input) +} + +func (c *bls12381G1MultiExpJovian) Name() string { + return "BLS12_G1MSM" +} + // bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile for Prague (no size limits). func (c *bls12381G1Add) Name() string { return "BLS12_G1ADD" @@ -1115,6 +1180,25 @@ func (c *bls12381G2MultiExpIsthmus) Name() string { return "BLS12_G2MSM" } +type bls12381G2MultiExpJovian struct { +} + +func (c *bls12381G2MultiExpJovian) RequiredGas(input []byte) uint64 { + return new(bls12381G2MultiExp).RequiredGas(input) +} + +func (c *bls12381G2MultiExpJovian) Run(input []byte) ([]byte, error) { + if len(input) > int(params.Bls12381G2MulMaxInputSizeJovian) { + return nil, errBLS12381MaxG2Size + } + + return new(bls12381G2MultiExp).Run(input) +} + +func (c *bls12381G2MultiExpJovian) Name() string { + return "BLS12_G2MSM" +} + // bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile. type bls12381G2MultiExp struct{} @@ -1198,6 +1282,25 @@ func (c *bls12381PairingIsthmus) Name() string { return "BLS12_PAIRING_CHECK" } +type bls12381PairingJovian struct { +} + +func (c *bls12381PairingJovian) RequiredGas(input []byte) uint64 { + return new(bls12381Pairing).RequiredGas(input) +} + +func (c *bls12381PairingJovian) Run(input []byte) ([]byte, error) { + if len(input) > int(params.Bls12381PairingMaxInputSizeJovian) { + return nil, errBLS12381MaxPairingSize + } + + return new(bls12381Pairing).Run(input) +} + +func (c *bls12381PairingJovian) Name() string { + return "BLS12_PAIRING_CHECK" +} + // bls12381Pairing implements EIP-2537 Pairing precompile. type bls12381Pairing struct{} diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 74f2f55bc6..2596c52298 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -74,6 +74,11 @@ var allPrecompiles = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{0x0b}): &p256Verify{}, common.BytesToAddress([]byte{0x01, 0x00}): &p256VerifyFjord{}, + + common.BytesToAddress([]byte{0x2f, 0x08}): &bn256PairingJovian{}, + common.BytesToAddress([]byte{0x2f, 0x0e}): &bls12381PairingJovian{}, + common.BytesToAddress([]byte{0x2f, 0x0b}): &bls12381G1MultiExpJovian{}, + common.BytesToAddress([]byte{0x2f, 0x0d}): &bls12381G2MultiExpJovian{}, } // EIP-152 test vectors diff --git a/params/protocol_params.go b/params/protocol_params.go index 86c090b31e..cd49a521e0 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -185,6 +185,11 @@ const ( Bls12381G2MulMaxInputSizeIsthmus uint64 = 488448 // Maximum input size for BLS12-381 G2 multiple-scalar-multiply operation Bls12381PairingMaxInputSizeIsthmus uint64 = 235008 // Maximum input size for BLS12-381 pairing check + Bn256PairingMaxInputSizeJovian uint64 = 81984 // bn256Pairing limit (427 pairs) + Bls12381G1MulMaxInputSizeJovian uint64 = 288960 // BLS12-381 G1 MSM limit (1,806 pairs) + Bls12381G2MulMaxInputSizeJovian uint64 = 278784 // BLS12-381 G2 MSM limit (968 pairs) + Bls12381PairingMaxInputSizeJovian uint64 = 156672 // BLS12-381 pairing limit (408 pairs) + // The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529, // up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529 RefundQuotient uint64 = 2 From 0a1c7b4f77532f06888f54220ac41bc39224426f Mon Sep 17 00:00:00 2001 From: Sebastian Stammler Date: Tue, 28 Oct 2025 14:51:57 +0100 Subject: [PATCH 11/17] superchain: Update for new Jovian timestamps (#712) --- superchain-registry-commit.txt | 2 +- superchain/superchain-configs.zip | Bin 3911175 -> 3911177 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/superchain-registry-commit.txt b/superchain-registry-commit.txt index e0f00d5b74..6c56d56893 100644 --- a/superchain-registry-commit.txt +++ b/superchain-registry-commit.txt @@ -1 +1 @@ -495e615df91fd99bfc1468d9e22065fb3a64020a \ No newline at end of file +d34fd7ed60dde93c83976dad6d66c20316d536a8 \ No newline at end of file diff --git a/superchain/superchain-configs.zip b/superchain/superchain-configs.zip index c4fae570be2e2bc328fa2899f85101b351e0b2fe..c4c4ed00557125a0a811f2148afca2c0f0cad4e8 100644 GIT binary patch delta 31214 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 From aa940538653c59df97b432bc24a2a4e4b17a06a8 Mon Sep 17 00:00:00 2001 From: Josh Klopfenstein Date: Wed, 5 Nov 2025 01:57:38 -0800 Subject: [PATCH 16/17] triedb/pathdb: sync ancient store before journal (#32557) (#718) This pull request addresses the corrupted path database with log indicating: `history head truncation out of range, tail: 122557, head: 212208, target: 212557` This is a rare edge case where the in-memory layers, including the write buffer in the disk layer, are fully persisted (e.g., written to file), but the state history freezer is not properly closed (e.g., Geth is terminated after journaling but before freezer.Close). In this situation, the recent state history writes will be truncated on the next startup, while the in-memory layers resolve correctly. As a result, the state history falls behind the disk layer (including the write buffer). In this pull request, the state history freezer is always synced before journal, ensuring the state history writes are always persisted before the others. Edit: It's confirmed that devops team has 10s container termination setting. It explains why Geth didn't finish the entire termination without state history being closed. https://github.com/ethpandaops/fusaka-devnets/pull/63/files Co-authored-by: rjl493456442 --- triedb/pathdb/journal.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index 4639932763..47a632fd37 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -333,7 +333,16 @@ func (db *Database) Journal(root common.Hash) error { if db.readOnly { return errDatabaseReadOnly } - + // Forcibly sync the ancient store before persisting the in-memory layers. + // This prevents an edge case where the in-memory layers are persisted + // but the ancient store is not properly closed, resulting in recent writes + // being lost. After a restart, the ancient store would then be misaligned + // with the disk layer, causing data corruption. + if db.stateFreezer != nil { + if err := db.stateFreezer.SyncAncient(); err != nil { + return err + } + } // Store the journal into the database and return var ( file *os.File From 0f7e8b6a9a0e0641f3f7e39aa879b417f39129dc Mon Sep 17 00:00:00 2001 From: blockchaindevsh Date: Tue, 11 Nov 2025 13:10:36 +0800 Subject: [PATCH 17/17] fix BlockValidator.ValidateBody --- core/block_validator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/block_validator.go b/core/block_validator.go index 05c03235ed..a6a598fc71 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -106,7 +106,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { } // Check blob gas usage. - if !v.config.IsOptimism() && header.BlobGasUsed != nil { + if (!v.config.IsOptimism() || v.config.IsL2Blob(header.Number, header.Time)) && header.BlobGasUsed != nil { if want := *header.BlobGasUsed / params.BlobTxBlobGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated return fmt.Errorf("blob gas used mismatch (header %v, calculated %v)", *header.BlobGasUsed, blobs*params.BlobTxBlobGasPerBlob) }

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 W0`jUsF&SX08p?w@7YPOD&;J3g`F{ui From 4fd8b05caf3f9d8ee8c6d86fe7735418b1fb804d Mon Sep 17 00:00:00 2001 From: Sebastian Stammler Date: Tue, 28 Oct 2025 18:25:48 +0100 Subject: [PATCH 12/17] core/types: Populate Jovian receipt fields (#710) * core/types: Move receipt tests OP diff to separate file * core/types: Populate Jovian receipt fields --- core/types/gen_receipt_json.go | 14 +- core/types/receipt.go | 42 +-- core/types/receipt_opstack.go | 54 +++ core/types/receipt_opstack_test.go | 500 ++++++++++++++++++++++++ core/types/receipt_test.go | 587 +---------------------------- fork.yaml | 5 +- internal/ethapi/api.go | 10 +- 7 files changed, 594 insertions(+), 618 deletions(-) create mode 100644 core/types/receipt_opstack.go create mode 100644 core/types/receipt_opstack_test.go diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go index def81319aa..b3735d7156 100644 --- a/core/types/gen_receipt_json.go +++ b/core/types/gen_receipt_json.go @@ -42,6 +42,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) { L1BlobBaseFeeScalar *hexutil.Uint64 `json:"l1BlobBaseFeeScalar,omitempty"` OperatorFeeScalar *hexutil.Uint64 `json:"operatorFeeScalar,omitempty"` OperatorFeeConstant *hexutil.Uint64 `json:"operatorFeeConstant,omitempty"` + DAFootprintGasScalar *hexutil.Uint64 `json:"daFootprintGasScalar,omitempty"` } var enc Receipt enc.Type = hexutil.Uint64(r.Type) @@ -68,12 +69,9 @@ func (r Receipt) MarshalJSON() ([]byte, error) { enc.FeeScalar = r.FeeScalar enc.L1BaseFeeScalar = (*hexutil.Uint64)(r.L1BaseFeeScalar) enc.L1BlobBaseFeeScalar = (*hexutil.Uint64)(r.L1BlobBaseFeeScalar) - if r.OperatorFeeScalar != nil { - enc.OperatorFeeScalar = (*hexutil.Uint64)(r.OperatorFeeScalar) - } - if r.OperatorFeeConstant != nil { - enc.OperatorFeeConstant = (*hexutil.Uint64)(r.OperatorFeeConstant) - } + enc.OperatorFeeScalar = (*hexutil.Uint64)(r.OperatorFeeScalar) + enc.OperatorFeeConstant = (*hexutil.Uint64)(r.OperatorFeeConstant) + enc.DAFootprintGasScalar = (*hexutil.Uint64)(r.DAFootprintGasScalar) return json.Marshal(&enc) } @@ -106,6 +104,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { L1BlobBaseFeeScalar *hexutil.Uint64 `json:"l1BlobBaseFeeScalar,omitempty"` OperatorFeeScalar *hexutil.Uint64 `json:"operatorFeeScalar,omitempty"` OperatorFeeConstant *hexutil.Uint64 `json:"operatorFeeConstant,omitempty"` + DAFootprintGasScalar *hexutil.Uint64 `json:"daFootprintGasScalar,omitempty"` } var dec Receipt if err := json.Unmarshal(input, &dec); err != nil { @@ -194,5 +193,8 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { if dec.OperatorFeeConstant != nil { r.OperatorFeeConstant = (*uint64)(dec.OperatorFeeConstant) } + if dec.DAFootprintGasScalar != nil { + r.DAFootprintGasScalar = (*uint64)(dec.DAFootprintGasScalar) + } return nil } diff --git a/core/types/receipt.go b/core/types/receipt.go index 3f64dd6411..e385df2d3b 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -86,15 +86,16 @@ type Receipt struct { TransactionIndex uint `json:"transactionIndex"` // Optimism: extend receipts with L1 and operator fee info - L1GasPrice *big.Int `json:"l1GasPrice,omitempty"` // Present from pre-bedrock. L1 Basefee after Bedrock - L1BlobBaseFee *big.Int `json:"l1BlobBaseFee,omitempty"` // Always nil prior to the Ecotone hardfork - L1GasUsed *big.Int `json:"l1GasUsed,omitempty"` // Present from pre-bedrock, deprecated as of Fjord - L1Fee *big.Int `json:"l1Fee,omitempty"` // Present from pre-bedrock - FeeScalar *big.Float `json:"l1FeeScalar,omitempty"` // Present from pre-bedrock to Ecotone. Nil after Ecotone - L1BaseFeeScalar *uint64 `json:"l1BaseFeeScalar,omitempty"` // Always nil prior to the Ecotone hardfork - L1BlobBaseFeeScalar *uint64 `json:"l1BlobBaseFeeScalar,omitempty"` // Always nil prior to the Ecotone hardfork - OperatorFeeScalar *uint64 `json:"operatorFeeScalar,omitempty"` // Always nil prior to the Isthmus hardfork - OperatorFeeConstant *uint64 `json:"operatorFeeConstant,omitempty"` // Always nil prior to the Isthmus hardfork + L1GasPrice *big.Int `json:"l1GasPrice,omitempty"` // Present from pre-bedrock. L1 Basefee after Bedrock + L1BlobBaseFee *big.Int `json:"l1BlobBaseFee,omitempty"` // Always nil prior to the Ecotone hardfork + L1GasUsed *big.Int `json:"l1GasUsed,omitempty"` // Present from pre-bedrock, deprecated as of Fjord + L1Fee *big.Int `json:"l1Fee,omitempty"` // Present from pre-bedrock + FeeScalar *big.Float `json:"l1FeeScalar,omitempty"` // Present from pre-bedrock to Ecotone. Nil after Ecotone + L1BaseFeeScalar *uint64 `json:"l1BaseFeeScalar,omitempty"` // Always nil prior to the Ecotone hardfork + L1BlobBaseFeeScalar *uint64 `json:"l1BlobBaseFeeScalar,omitempty"` // Always nil prior to the Ecotone hardfork + OperatorFeeScalar *uint64 `json:"operatorFeeScalar,omitempty"` // Always nil prior to the Isthmus hardfork + OperatorFeeConstant *uint64 `json:"operatorFeeConstant,omitempty"` // Always nil prior to the Isthmus hardfork + DAFootprintGasScalar *uint64 `json:"daFootprintGasScalar,omitempty"` // Always nil prior to the Jovian hardfork } type receiptMarshaling struct { @@ -121,6 +122,7 @@ type receiptMarshaling struct { DepositReceiptVersion *hexutil.Uint64 OperatorFeeScalar *hexutil.Uint64 OperatorFeeConstant *hexutil.Uint64 + DAFootprintGasScalar *hexutil.Uint64 } // receiptRLP is the consensus encoding of a receipt. @@ -612,26 +614,8 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, blockHash common.Has logIndex += uint(len(rs[i].Logs)) } - if config.Optimism != nil && len(txs) >= 2 && config.IsBedrock(new(big.Int).SetUint64(blockNumber)) { - gasParams, err := extractL1GasParams(config, blockTime, txs[0].Data()) - if err != nil { - return err - } - for i := 0; i < len(rs); i++ { - if txs[i].IsDepositTx() { - continue - } - rs[i].L1GasPrice = gasParams.l1BaseFee - rs[i].L1BlobBaseFee = gasParams.l1BlobBaseFee - rs[i].L1Fee, rs[i].L1GasUsed = gasParams.costFunc(txs[i].RollupCostData()) - rs[i].FeeScalar = gasParams.feeScalar - rs[i].L1BaseFeeScalar = u32ptrTou64ptr(gasParams.l1BaseFeeScalar) - rs[i].L1BlobBaseFeeScalar = u32ptrTou64ptr(gasParams.l1BlobBaseFeeScalar) - if gasParams.operatorFeeScalar != nil && gasParams.operatorFeeConstant != nil && (*gasParams.operatorFeeScalar != 0 || *gasParams.operatorFeeConstant != 0) { - rs[i].OperatorFeeScalar = u32ptrTou64ptr(gasParams.operatorFeeScalar) - rs[i].OperatorFeeConstant = gasParams.operatorFeeConstant - } - } + if config.IsOptimismBedrock(new(big.Int).SetUint64(blockNumber)) && len(txs) >= 2 { + return rs.deriveOPStackFields(config, blockTime, txs) } return nil } diff --git a/core/types/receipt_opstack.go b/core/types/receipt_opstack.go new file mode 100644 index 0000000000..9bc69efca5 --- /dev/null +++ b/core/types/receipt_opstack.go @@ -0,0 +1,54 @@ +package types + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/params" +) + +// deriveOPStackFields derives the OP Stack specific fields for each receipt. +// It must only be called for blocks with at least one transaction (the L1 attributes deposit). +func (rs Receipts) deriveOPStackFields(config *params.ChainConfig, blockTime uint64, txs []*Transaction) error { + // Exit early if there are only deposit transactions, for which no fields are derived. + if txs[len(txs)-1].IsDepositTx() { + return nil + } + + l1AttributesData := txs[0].Data() + gasParams, err := extractL1GasParams(config, blockTime, l1AttributesData) + if err != nil { + return fmt.Errorf("failed to extract L1 gas params: %w", err) + } + + var daFootprintGasScalar uint64 + isJovian := config.IsJovian(blockTime) + if isJovian { + scalar, err := ExtractDAFootprintGasScalar(l1AttributesData) + if err != nil { + return fmt.Errorf("failed to extract DA footprint gas scalar: %w", err) + } + daFootprintGasScalar = uint64(scalar) + } + + for i := range rs { + if txs[i].IsDepositTx() { + continue + } + rs[i].L1GasPrice = gasParams.l1BaseFee + rs[i].L1BlobBaseFee = gasParams.l1BlobBaseFee + rcd := txs[i].RollupCostData() + rs[i].L1Fee, rs[i].L1GasUsed = gasParams.costFunc(rcd) + rs[i].FeeScalar = gasParams.feeScalar + rs[i].L1BaseFeeScalar = u32ptrTou64ptr(gasParams.l1BaseFeeScalar) + rs[i].L1BlobBaseFeeScalar = u32ptrTou64ptr(gasParams.l1BlobBaseFeeScalar) + if gasParams.operatorFeeScalar != nil && gasParams.operatorFeeConstant != nil && (*gasParams.operatorFeeScalar != 0 || *gasParams.operatorFeeConstant != 0) { + rs[i].OperatorFeeScalar = u32ptrTou64ptr(gasParams.operatorFeeScalar) + rs[i].OperatorFeeConstant = gasParams.operatorFeeConstant + } + if isJovian { + rs[i].DAFootprintGasScalar = &daFootprintGasScalar + rs[i].BlobGasUsed = daFootprintGasScalar * rcd.EstimatedDASize().Uint64() + } + } + return nil +} diff --git a/core/types/receipt_opstack_test.go b/core/types/receipt_opstack_test.go new file mode 100644 index 0000000000..c49bc7bbd4 --- /dev/null +++ b/core/types/receipt_opstack_test.go @@ -0,0 +1,500 @@ +package types + +import ( + "bytes" + "encoding/json" + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/kylelemons/godebug/diff" + "github.com/stretchr/testify/require" +) + +var ( + bedrockGenesisTestConfig = func() *params.ChainConfig { + conf := *params.AllCliqueProtocolChanges // copy the config + conf.Clique = nil + conf.BedrockBlock = big.NewInt(0) + conf.Optimism = ¶ms.OptimismConfig{EIP1559Elasticity: 50, EIP1559Denominator: 10} + return &conf + }() + ecotoneTestConfig = func() *params.ChainConfig { + conf := *bedrockGenesisTestConfig // copy the config + time := uint64(0) + conf.EcotoneTime = &time + return &conf + }() + isthmusTestConfig = func() *params.ChainConfig { + conf := *ecotoneTestConfig // copy the config + time := uint64(0) + conf.FjordTime = &time + conf.GraniteTime = &time + conf.HoloceneTime = &time + conf.IsthmusTime = &time + return &conf + }() + jovianTestConfig = func() *params.ChainConfig { + conf := *isthmusTestConfig // copy the config + time := uint64(0) + conf.JovianTime = &time + return &conf + }() + + depositReceiptNoNonce = &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + { + Address: common.BytesToAddress([]byte{0x01, 0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + }, + Type: DepositTxType, + } + nonce = uint64(1234) + depositReceiptWithNonce = &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + DepositNonce: &nonce, + DepositReceiptVersion: nil, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + { + Address: common.BytesToAddress([]byte{0x01, 0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + }, + Type: DepositTxType, + } + version = CanyonDepositReceiptVersion + depositReceiptWithNonceAndVersion = &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + DepositNonce: &nonce, + DepositReceiptVersion: &version, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + { + Address: common.BytesToAddress([]byte{0x01, 0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x00, 0xff}, + }, + }, + Type: DepositTxType, + } + + daFootprintGasScalar = uint16(400) +) + +func clearComputedFieldsOnOPStackReceipts(receipts []*Receipt) []*Receipt { + receipts = clearComputedFieldsOnReceipts(receipts) + for _, receipt := range receipts { + receipt.L1GasPrice = nil + receipt.L1BlobBaseFee = nil + receipt.L1GasUsed = nil + receipt.L1Fee = nil + receipt.FeeScalar = nil + receipt.L1BaseFeeScalar = nil + receipt.L1BlobBaseFeeScalar = nil + receipt.OperatorFeeScalar = nil + receipt.OperatorFeeConstant = nil + receipt.DAFootprintGasScalar = nil + } + return receipts +} + +func getOptimismTxReceipts(l1AttributesPayload []byte, l1GasPrice, l1GasUsed, l1Fee *big.Int, feeScalar *big.Float) ([]*Transaction, []*Receipt) { + // Create a few transactions to have receipts for + txs := Transactions{ + NewTx(&DepositTx{ + To: nil, // contract creation + Value: big.NewInt(6), + Gas: 50, + Data: l1AttributesPayload, + }), + emptyTx, + } + + // Create the corresponding receipts + receipts := Receipts{ + &Receipt{ + Type: DepositTxType, + PostState: common.Hash{5}.Bytes(), + CumulativeGasUsed: 50 + 15, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x33}), + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[0].Hash(), + TxIndex: 0, + BlockHash: blockHash, + Index: 0, + }, + { + Address: common.BytesToAddress([]byte{0x03, 0x33}), + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[0].Hash(), + TxIndex: 0, + BlockHash: blockHash, + Index: 1, + }, + }, + TxHash: txs[0].Hash(), + ContractAddress: common.HexToAddress("0x3bb898b4bbe24f68a4e9be46cfe72d1787fd74f4"), + GasUsed: 65, + EffectiveGasPrice: big.NewInt(0), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 0, + DepositNonce: &depNonce1, + }, + &Receipt{ + Type: LegacyTxType, + EffectiveGasPrice: big.NewInt(0), + PostState: common.Hash{4}.Bytes(), + CumulativeGasUsed: 10, + Logs: []*Log{}, + // derived fields: + TxHash: txs[1].Hash(), + GasUsed: 18446744073709551561, + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 1, + L1GasPrice: l1GasPrice, + L1GasUsed: l1GasUsed, + L1Fee: l1Fee, + FeeScalar: feeScalar, + }, + } + for _, receipt := range receipts { + receipt.Bloom = CreateBloom(receipt) + } + + return txs, receipts +} + +func getOptimismEcotoneTxReceipts(l1AttributesPayload []byte, l1GasPrice, l1BlobBaseFee, l1GasUsed, l1Fee *big.Int, baseFeeScalar, blobBaseFeeScalar *uint64) ([]*Transaction, []*Receipt) { + txs, receipts := getOptimismTxReceipts(l1AttributesPayload, l1GasPrice, l1GasUsed, l1Fee, nil) + receipts[1].L1BlobBaseFee = l1BlobBaseFee + receipts[1].L1BaseFeeScalar = baseFeeScalar + receipts[1].L1BlobBaseFeeScalar = blobBaseFeeScalar + return txs, receipts +} + +func getOptimismIsthmusTxReceipts(l1AttributesPayload []byte, l1GasPrice, l1BlobBaseFee, l1GasUsed, l1Fee *big.Int, baseFeeScalar, blobBaseFeeScalar, operatorFeeScalar, operatorFeeConstant *uint64) ([]*Transaction, []*Receipt) { + txs, receipts := getOptimismEcotoneTxReceipts(l1AttributesPayload, l1GasPrice, l1BlobBaseFee, l1GasUsed, l1Fee, baseFeeScalar, blobBaseFeeScalar) + receipts[1].OperatorFeeScalar = operatorFeeScalar + receipts[1].OperatorFeeConstant = operatorFeeConstant + return txs, receipts +} + +func getOptimismJovianTxReceipts(l1AttributesPayload []byte, l1GasPrice, l1BlobBaseFee, l1GasUsed, l1Fee *big.Int, baseFeeScalar, blobBaseFeeScalar, operatorFeeScalar, operatorFeeConstant, daFootprintGasScalar *uint64) ([]*Transaction, []*Receipt) { + txs, receipts := getOptimismIsthmusTxReceipts(l1AttributesPayload, l1GasPrice, l1BlobBaseFee, l1GasUsed, l1Fee, baseFeeScalar, blobBaseFeeScalar, operatorFeeScalar, operatorFeeConstant) + receipts[1].DAFootprintGasScalar = daFootprintGasScalar + if daFootprintGasScalar != nil { + receipts[1].BlobGasUsed = *daFootprintGasScalar * txs[1].RollupCostData().EstimatedDASize().Uint64() + } + return txs, receipts +} + +func TestDeriveOptimismBedrockTxReceipts(t *testing.T) { + // Bedrock style l1 attributes with L1Scalar=7_000_000 (becomes 7 after division), L1Overhead=50, L1BaseFee=1000*1e6 + payload := common.Hex2Bytes("015d8eb900000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000006acfc0015d8eb900000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000006acfc0") + // the parameters we use below are defined in rollup_test.go + l1GasPrice := baseFee + l1GasUsed := bedrockGas + feeScalar := big.NewFloat(float64(scalar.Uint64() / 1e6)) + l1Fee := bedrockFee + txs, receipts := getOptimismTxReceipts(payload, l1GasPrice, l1GasUsed, l1Fee, feeScalar) + + // Re-derive receipts. + baseFee := big.NewInt(1000) + derivedReceipts := clearComputedFieldsOnOPStackReceipts(receipts) + err := Receipts(derivedReceipts).DeriveFields(bedrockGenesisTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) + require.NoError(t, err) + checkBedrockReceipts(t, receipts, derivedReceipts) + + // Should get same result with the Ecotone config because it will assume this is "first ecotone block" + // if it sees the bedrock style L1 attributes. + err = Receipts(derivedReceipts).DeriveFields(ecotoneTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) + require.NoError(t, err) + checkBedrockReceipts(t, receipts, derivedReceipts) +} + +func TestDeriveOptimismEcotoneTxReceipts(t *testing.T) { + // Ecotone style l1 attributes with baseFeeScalar=2, blobBaseFeeScalar=3, baseFee=1000*1e6, blobBaseFee=10*1e6 + payload := common.Hex2Bytes("440a5e20000000020000000300000000000004d200000000000004d200000000000004d2000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000098968000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2") + // the parameters we use below are defined in rollup_test.go + baseFeeScalarUint64 := baseFeeScalar.Uint64() + blobBaseFeeScalarUint64 := blobBaseFeeScalar.Uint64() + txs, receipts := getOptimismEcotoneTxReceipts(payload, baseFee, blobBaseFee, ecotoneGas, ecotoneFee, &baseFeeScalarUint64, &blobBaseFeeScalarUint64) + + // Re-derive receipts. + baseFee := big.NewInt(1000) + derivedReceipts := clearComputedFieldsOnOPStackReceipts(receipts) + // Should error out if we try to process this with a pre-Ecotone config + err := Receipts(derivedReceipts).DeriveFields(bedrockGenesisTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) + require.Error(t, err) + + err = Receipts(derivedReceipts).DeriveFields(ecotoneTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) + require.NoError(t, err) + diffReceipts(t, receipts, derivedReceipts) +} + +func TestDeriveOptimismIsthmusTxReceipts(t *testing.T) { + // Isthmus style l1 attributes with baseFeeScalar=2, blobBaseFeeScalar=3, baseFee=1000*1e6, blobBaseFee=10*1e6, operatorFeeScalar=1439103868, operatorFeeConstant=1256417826609331460 + payload := common.Hex2Bytes("098999be000000020000000300000000000004d200000000000004d200000000000004d2000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000098968000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d255c6fb7c116fb15b44847d04") + // the parameters we use below are defined in rollup_test.go + baseFeeScalarUint64 := baseFeeScalar.Uint64() + blobBaseFeeScalarUint64 := blobBaseFeeScalar.Uint64() + operatorFeeScalarUint64 := operatorFeeScalar.Uint64() + operatorFeeConstantUint64 := operatorFeeConstant.Uint64() + txs, receipts := getOptimismIsthmusTxReceipts(payload, baseFee, blobBaseFee, minimumFjordGas, fjordFee, &baseFeeScalarUint64, &blobBaseFeeScalarUint64, &operatorFeeScalarUint64, &operatorFeeConstantUint64) + + // Re-derive receipts. + baseFee := big.NewInt(1000) + derivedReceipts := clearComputedFieldsOnOPStackReceipts(receipts) + // Should error out if we try to process this with a pre-Isthmus config + err := Receipts(derivedReceipts).DeriveFields(bedrockGenesisTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) + require.Error(t, err) + + err = Receipts(derivedReceipts).DeriveFields(isthmusTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) + require.NoError(t, err) + diffReceipts(t, receipts, derivedReceipts) +} + +func TestDeriveOptimismIsthmusTxReceiptsNoOperatorFee(t *testing.T) { + // Isthmus style l1 attributes with baseFeeScalar=2, blobBaseFeeScalar=3, baseFee=1000*1e6, blobBaseFee=10*1e6, operatorFeeScalar=0, operatorFeeConstant=0 + payload := common.Hex2Bytes("098999be000000020000000300000000000004d200000000000004d200000000000004d2000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000098968000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000") + // the parameters we use below are defined in rollup_test.go + baseFeeScalarUint64 := baseFeeScalar.Uint64() + blobBaseFeeScalarUint64 := blobBaseFeeScalar.Uint64() + txs, receipts := getOptimismIsthmusTxReceipts(payload, baseFee, blobBaseFee, minimumFjordGas, fjordFee, &baseFeeScalarUint64, &blobBaseFeeScalarUint64, nil, nil) + + // Re-derive receipts. + baseFee := big.NewInt(1000) + derivedReceipts := clearComputedFieldsOnOPStackReceipts(receipts) + // Should error out if we try to process this with a pre-Isthmus config + err := Receipts(derivedReceipts).DeriveFields(bedrockGenesisTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) + require.Error(t, err) + + err = Receipts(derivedReceipts).DeriveFields(isthmusTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) + require.NoError(t, err) + diffReceipts(t, receipts, derivedReceipts) +} + +func TestDeriveOptimismJovianTxReceipts(t *testing.T) { + // Jovian style l1 attributes with baseFeeScalar=2, blobBaseFeeScalar=3, baseFee=1000*1e6, blobBaseFee=10*1e6, operatorFeeScalar=1439103868, operatorFeeConstant=1256417826609331460, daFootprintGasScalar=400 + payload := common.Hex2Bytes("3db6be2b000000020000000300000000000004d200000000000004d200000000000004d2000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000098968000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d255c6fb7c116fb15b44847d040190") + // the parameters we use below are defined in rollup_test.go + baseFeeScalarUint64 := baseFeeScalar.Uint64() + blobBaseFeeScalarUint64 := blobBaseFeeScalar.Uint64() + operatorFeeScalarUint64 := operatorFeeScalar.Uint64() + operatorFeeConstantUint64 := operatorFeeConstant.Uint64() + daFootprintGasScalarUint64 := uint64(daFootprintGasScalar) + txs, receipts := getOptimismJovianTxReceipts(payload, baseFee, blobBaseFee, minimumFjordGas, fjordFee, &baseFeeScalarUint64, &blobBaseFeeScalarUint64, &operatorFeeScalarUint64, &operatorFeeConstantUint64, &daFootprintGasScalarUint64) + + // Re-derive receipts. + baseFee := big.NewInt(1000) + derivedReceipts := clearComputedFieldsOnOPStackReceipts(receipts) + // Should error out if we try to process this with a pre-Jovian config + err := Receipts(derivedReceipts).DeriveFields(bedrockGenesisTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) + require.Error(t, err) + + err = Receipts(derivedReceipts).DeriveFields(jovianTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) + require.NoError(t, err) + diffReceipts(t, receipts, derivedReceipts) +} + +func diffReceipts(t *testing.T, receipts, derivedReceipts []*Receipt) { + // Check diff of receipts against derivedReceipts. + r1, err := json.MarshalIndent(receipts, "", " ") + if err != nil { + t.Fatal("error marshaling input receipts:", err) + } + r2, err := json.MarshalIndent(derivedReceipts, "", " ") + if err != nil { + t.Fatal("error marshaling derived receipts:", err) + } + d := diff.Diff(string(r1), string(r2)) + if d != "" { + t.Fatal("receipts differ:", d) + } +} + +func checkBedrockReceipts(t *testing.T, receipts, derivedReceipts []*Receipt) { + diffReceipts(t, receipts, derivedReceipts) + + // Check that we preserved the invariant: l1Fee = l1GasPrice * l1GasUsed * l1FeeScalar + // but with more difficult int math... + l2Rcpt := derivedReceipts[1] + l1GasCost := new(big.Int).Mul(l2Rcpt.L1GasPrice, l2Rcpt.L1GasUsed) + l1Fee := new(big.Float).Mul(new(big.Float).SetInt(l1GasCost), l2Rcpt.FeeScalar) + require.Equal(t, new(big.Float).SetInt(l2Rcpt.L1Fee), l1Fee) +} + +func TestBedrockDepositReceiptUnchanged(t *testing.T) { + expectedRlp := common.FromHex("7EF90156A003000000000000000000000000000000000000000000000000000000000000000AB9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F0D7940000000000000000000000000000000000000033C001D7940000000000000000000000000000000000000333C002") + // Deposit receipt with no nonce + receipt := &Receipt{ + Type: DepositTxType, + PostState: common.Hash{3}.Bytes(), + CumulativeGasUsed: 10, + Logs: []*Log{ + {Address: common.BytesToAddress([]byte{0x33}), Data: []byte{1}, Topics: []common.Hash{}}, + {Address: common.BytesToAddress([]byte{0x03, 0x33}), Data: []byte{2}, Topics: []common.Hash{}}, + }, + TxHash: common.Hash{}, + ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}), + GasUsed: 4, + } + + rlp, err := receipt.MarshalBinary() + require.NoError(t, err) + require.Equal(t, expectedRlp, rlp) + + // Consensus values should be unchanged after reparsing + parsed := new(Receipt) + err = parsed.UnmarshalBinary(rlp) + require.NoError(t, err) + require.Equal(t, receipt.Status, parsed.Status) + require.Equal(t, receipt.CumulativeGasUsed, parsed.CumulativeGasUsed) + require.Equal(t, receipt.Bloom, parsed.Bloom) + require.EqualValues(t, receipt.Logs, parsed.Logs) + // And still shouldn't have a nonce + require.Nil(t, parsed.DepositNonce) + // ..or a deposit nonce + require.Nil(t, parsed.DepositReceiptVersion) +} + +// Regolith introduced an inconsistency in behavior between EncodeIndex and MarshalBinary for a +// deposit transaction receipt. TestReceiptEncodeIndexBugIsEnshrined makes sure this difference is +// preserved for backwards compatibility purposes, but also that there is no discrepancy for the +// post-Canyon encoding. +func TestReceiptEncodeIndexBugIsEnshrined(t *testing.T) { + // Check that a post-Regolith, pre-Canyon receipt produces the expected difference between + // EncodeIndex and MarshalBinary. + buf := new(bytes.Buffer) + receipts := Receipts{depositReceiptWithNonce} + receipts.EncodeIndex(0, buf) + indexBytes := buf.Bytes() + + regularBytes, _ := receipts[0].MarshalBinary() + + require.NotEqual(t, indexBytes, regularBytes) + + // Confirm the buggy encoding is as expected, which means it should encode as if it had no + // nonce specified (like that of a non-deposit receipt, whose encoding would differ only in the + // type byte). + buf.Reset() + tempReceipt := *depositReceiptWithNonce + tempReceipt.Type = eip1559Receipt.Type + buggyBytes, _ := tempReceipt.MarshalBinary() + + require.Equal(t, indexBytes[1:], buggyBytes[1:]) + + // check that the post-Canyon encoding has no differences between EncodeIndex and + // MarshalBinary. + buf.Reset() + receipts = Receipts{depositReceiptWithNonceAndVersion} + receipts.EncodeIndex(0, buf) + indexBytes = buf.Bytes() + + regularBytes, _ = receipts[0].MarshalBinary() + + require.Equal(t, indexBytes, regularBytes) + + // Check that bumping the nonce post-canyon changes the hash + bumpedReceipt := *depositReceiptWithNonceAndVersion + bumpedNonce := nonce + 1 + bumpedReceipt.DepositNonce = &bumpedNonce + bumpedBytes, _ := bumpedReceipt.MarshalBinary() + require.NotEqual(t, regularBytes, bumpedBytes) +} + +func TestRoundTripReceipt(t *testing.T) { + tests := []struct { + name string + rcpt *Receipt + }{ + {name: "Legacy", rcpt: legacyReceipt}, + {name: "AccessList", rcpt: accessListReceipt}, + {name: "EIP1559", rcpt: eip1559Receipt}, + {name: "DepositNoNonce", rcpt: depositReceiptNoNonce}, + {name: "DepositWithNonce", rcpt: depositReceiptWithNonce}, + {name: "DepositWithNonceAndVersion", rcpt: depositReceiptWithNonceAndVersion}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + data, err := test.rcpt.MarshalBinary() + require.NoError(t, err) + + d := &Receipt{} + err = d.UnmarshalBinary(data) + require.NoError(t, err) + require.Equal(t, test.rcpt, d) + require.Equal(t, test.rcpt.DepositNonce, d.DepositNonce) + require.Equal(t, test.rcpt.DepositReceiptVersion, d.DepositReceiptVersion) + }) + + t.Run(fmt.Sprintf("%sRejectExtraData", test.name), func(t *testing.T) { + data, err := test.rcpt.MarshalBinary() + require.NoError(t, err) + data = append(data, 1, 2, 3, 4) + d := &Receipt{} + err = d.UnmarshalBinary(data) + require.Error(t, err) + }) + } +} + +func TestRoundTripReceiptForStorage(t *testing.T) { + tests := []struct { + name string + rcpt *Receipt + }{ + {name: "Legacy", rcpt: legacyReceipt}, + {name: "AccessList", rcpt: accessListReceipt}, + {name: "EIP1559", rcpt: eip1559Receipt}, + {name: "DepositNoNonce", rcpt: depositReceiptNoNonce}, + {name: "DepositWithNonce", rcpt: depositReceiptWithNonce}, + {name: "DepositWithNonceAndVersion", rcpt: depositReceiptWithNonceAndVersion}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + data, err := rlp.EncodeToBytes((*ReceiptForStorage)(test.rcpt)) + require.NoError(t, err) + + d := &ReceiptForStorage{} + err = rlp.DecodeBytes(data, d) + require.NoError(t, err) + // Only check the stored fields - the others are derived later + require.Equal(t, test.rcpt.Status, d.Status) + require.Equal(t, test.rcpt.CumulativeGasUsed, d.CumulativeGasUsed) + require.Equal(t, test.rcpt.Logs, d.Logs) + require.Equal(t, test.rcpt.DepositNonce, d.DepositNonce) + require.Equal(t, test.rcpt.DepositReceiptVersion, d.DepositReceiptVersion) + }) + } +} diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index 85c93dc375..9d513e0039 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -19,7 +19,6 @@ package types import ( "bytes" "encoding/json" - "fmt" "math" "math/big" "reflect" @@ -31,30 +30,9 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/holiman/uint256" "github.com/kylelemons/godebug/diff" - "github.com/stretchr/testify/require" ) var ( - bedrockGenesisTestConfig = func() *params.ChainConfig { - conf := *params.AllCliqueProtocolChanges // copy the config - conf.Clique = nil - conf.BedrockBlock = big.NewInt(0) - conf.Optimism = ¶ms.OptimismConfig{EIP1559Elasticity: 50, EIP1559Denominator: 10} - return &conf - }() - ecotoneTestConfig = func() *params.ChainConfig { - conf := *bedrockGenesisTestConfig // copy the config - time := uint64(0) - conf.EcotoneTime = &time - return &conf - }() - isthmusTestConfig = func() *params.ChainConfig { - conf := *bedrockGenesisTestConfig // copy the config - time := uint64(0) - conf.IsthmusTime = &time - return &conf - }() - legacyReceipt = &Receipt{ Status: ReceiptStatusFailed, CumulativeGasUsed: 1, @@ -105,63 +83,6 @@ var ( }, Type: DynamicFeeTxType, } - depositReceiptNoNonce = &Receipt{ - Status: ReceiptStatusFailed, - CumulativeGasUsed: 1, - Logs: []*Log{ - { - Address: common.BytesToAddress([]byte{0x11}), - Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, - Data: []byte{0x01, 0x00, 0xff}, - }, - { - Address: common.BytesToAddress([]byte{0x01, 0x11}), - Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, - Data: []byte{0x01, 0x00, 0xff}, - }, - }, - Type: DepositTxType, - } - nonce = uint64(1234) - depositReceiptWithNonce = &Receipt{ - Status: ReceiptStatusFailed, - CumulativeGasUsed: 1, - DepositNonce: &nonce, - DepositReceiptVersion: nil, - Logs: []*Log{ - { - Address: common.BytesToAddress([]byte{0x11}), - Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, - Data: []byte{0x01, 0x00, 0xff}, - }, - { - Address: common.BytesToAddress([]byte{0x01, 0x11}), - Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, - Data: []byte{0x01, 0x00, 0xff}, - }, - }, - Type: DepositTxType, - } - version = CanyonDepositReceiptVersion - depositReceiptWithNonceAndVersion = &Receipt{ - Status: ReceiptStatusFailed, - CumulativeGasUsed: 1, - DepositNonce: &nonce, - DepositReceiptVersion: &version, - Logs: []*Log{ - { - Address: common.BytesToAddress([]byte{0x11}), - Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, - Data: []byte{0x01, 0x00, 0xff}, - }, - { - Address: common.BytesToAddress([]byte{0x01, 0x11}), - Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, - Data: []byte{0x01, 0x00, 0xff}, - }, - }, - Type: DepositTxType, - } // Create a few transactions to have receipts for to2 = common.HexToAddress("0x2") @@ -240,11 +161,13 @@ var ( Gas: 60, }), } + + blockNumber = big.NewInt(1) + blockTime = uint64(2) + blockHash = common.BytesToHash([]byte{0x03, 0x14}) + depNonce1 = uint64(7) depNonce2 = uint64(8) - blockNumber = big.NewInt(1) - blockTime = uint64(2) - blockHash = common.BytesToHash([]byte{0x03, 0x14}) canyonDepositReceiptVersion = CanyonDepositReceiptVersion ) @@ -493,11 +416,11 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) { // Tests that receipt data can be correctly derived from the contextual infos func TestDeriveFields(t *testing.T) { // Re-derive receipts. - baseFee := big.NewInt(1000) + basefee := big.NewInt(1000) blobGasPrice := big.NewInt(920) receipts := getTestReceipts() derivedReceipts := clearComputedFieldsOnReceipts(receipts) - err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, baseFee, blobGasPrice, txs) + err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, blobGasPrice, txs) if err != nil { t.Fatalf("DeriveFields(...) = %v, want ", err) } @@ -728,499 +651,3 @@ func clearComputedFieldsOnLogs(logs []*Log) []*Log { } return l } - -func getOptimismEcotoneTxReceipts(l1AttributesPayload []byte, l1GasPrice, l1BlobGasPrice, l1GasUsed, l1Fee *big.Int, baseFeeScalar, blobBaseFeeScalar *uint64) ([]*Transaction, []*Receipt) { - // Create a few transactions to have receipts for - txs := Transactions{ - NewTx(&DepositTx{ - To: nil, // contract creation - Value: big.NewInt(6), - Gas: 50, - Data: l1AttributesPayload, - }), - emptyTx, - } - - // Create the corresponding receipts - receipts := Receipts{ - &Receipt{ - Type: DepositTxType, - PostState: common.Hash{5}.Bytes(), - CumulativeGasUsed: 50 + 15, - Logs: []*Log{ - { - Address: common.BytesToAddress([]byte{0x33}), - // derived fields: - BlockNumber: blockNumber.Uint64(), - TxHash: txs[0].Hash(), - TxIndex: 0, - BlockHash: blockHash, - Index: 0, - }, - { - Address: common.BytesToAddress([]byte{0x03, 0x33}), - // derived fields: - BlockNumber: blockNumber.Uint64(), - TxHash: txs[0].Hash(), - TxIndex: 0, - BlockHash: blockHash, - Index: 1, - }, - }, - TxHash: txs[0].Hash(), - ContractAddress: common.HexToAddress("0x3bb898b4bbe24f68a4e9be46cfe72d1787fd74f4"), - GasUsed: 65, - EffectiveGasPrice: big.NewInt(0), - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 0, - DepositNonce: &depNonce1, - }, - &Receipt{ - Type: LegacyTxType, - EffectiveGasPrice: big.NewInt(0), - PostState: common.Hash{4}.Bytes(), - CumulativeGasUsed: 10, - Logs: []*Log{}, - // derived fields: - TxHash: txs[1].Hash(), - GasUsed: 18446744073709551561, - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 1, - L1GasPrice: l1GasPrice, - L1BlobBaseFee: l1BlobGasPrice, - L1GasUsed: l1GasUsed, - L1Fee: l1Fee, - L1BaseFeeScalar: baseFeeScalar, - L1BlobBaseFeeScalar: blobBaseFeeScalar, - }, - } - for _, receipt := range receipts { - receipt.Bloom = CreateBloom(receipt) - } - - return txs, receipts -} - -func getOptimismIsthmusTxReceipts(l1AttributesPayload []byte, l1GasPrice, l1BlobGasPrice, l1GasUsed, l1Fee *big.Int, baseFeeScalar, blobBaseFeeScalar, operatorFeeScalar, operatorFeeConstant *uint64) ([]*Transaction, []*Receipt) { - // Create a few transactions to have receipts for - txs := Transactions{ - NewTx(&DepositTx{ - To: nil, // contract creation - Value: big.NewInt(6), - Gas: 50, - Data: l1AttributesPayload, - }), - emptyTx, - } - - // Create the corresponding receipts - receipts := Receipts{ - &Receipt{ - Type: DepositTxType, - PostState: common.Hash{5}.Bytes(), - CumulativeGasUsed: 50 + 15, - Logs: []*Log{ - { - Address: common.BytesToAddress([]byte{0x33}), - // derived fields: - BlockNumber: blockNumber.Uint64(), - TxHash: txs[0].Hash(), - TxIndex: 0, - BlockHash: blockHash, - Index: 0, - }, - { - Address: common.BytesToAddress([]byte{0x03, 0x33}), - // derived fields: - BlockNumber: blockNumber.Uint64(), - TxHash: txs[0].Hash(), - TxIndex: 0, - BlockHash: blockHash, - Index: 1, - }, - }, - TxHash: txs[0].Hash(), - ContractAddress: common.HexToAddress("0x3bb898b4bbe24f68a4e9be46cfe72d1787fd74f4"), - GasUsed: 65, - EffectiveGasPrice: big.NewInt(0), - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 0, - DepositNonce: &depNonce1, - }, - &Receipt{ - Type: LegacyTxType, - EffectiveGasPrice: big.NewInt(0), - PostState: common.Hash{4}.Bytes(), - CumulativeGasUsed: 10, - Logs: []*Log{}, - // derived fields: - TxHash: txs[1].Hash(), - GasUsed: 18446744073709551561, - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 1, - L1GasPrice: l1GasPrice, - L1BlobBaseFee: l1BlobGasPrice, - L1GasUsed: l1GasUsed, - L1Fee: l1Fee, - L1BaseFeeScalar: baseFeeScalar, - L1BlobBaseFeeScalar: blobBaseFeeScalar, - OperatorFeeScalar: operatorFeeScalar, - OperatorFeeConstant: operatorFeeConstant, - }, - } - for _, receipt := range receipts { - receipt.Bloom = CreateBloom(receipt) - } - - return txs, receipts -} - -func getOptimismTxReceipts(l1AttributesPayload []byte, l1GasPrice, l1GasUsed, l1Fee *big.Int, feeScalar *big.Float) ([]*Transaction, []*Receipt) { - // Create a few transactions to have receipts for - txs := Transactions{ - NewTx(&DepositTx{ - To: nil, // contract creation - Value: big.NewInt(6), - Gas: 50, - Data: l1AttributesPayload, - }), - emptyTx, - } - - // Create the corresponding receipts - receipts := Receipts{ - &Receipt{ - Type: DepositTxType, - PostState: common.Hash{5}.Bytes(), - CumulativeGasUsed: 50 + 15, - Logs: []*Log{ - { - Address: common.BytesToAddress([]byte{0x33}), - // derived fields: - BlockNumber: blockNumber.Uint64(), - TxHash: txs[0].Hash(), - TxIndex: 0, - BlockHash: blockHash, - Index: 0, - }, - { - Address: common.BytesToAddress([]byte{0x03, 0x33}), - // derived fields: - BlockNumber: blockNumber.Uint64(), - TxHash: txs[0].Hash(), - TxIndex: 0, - BlockHash: blockHash, - Index: 1, - }, - }, - TxHash: txs[0].Hash(), - ContractAddress: common.HexToAddress("0x3bb898b4bbe24f68a4e9be46cfe72d1787fd74f4"), - GasUsed: 65, - EffectiveGasPrice: big.NewInt(0), - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 0, - DepositNonce: &depNonce1, - }, - &Receipt{ - Type: LegacyTxType, - EffectiveGasPrice: big.NewInt(0), - PostState: common.Hash{4}.Bytes(), - CumulativeGasUsed: 10, - Logs: []*Log{}, - // derived fields: - TxHash: txs[1].Hash(), - GasUsed: 18446744073709551561, - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 1, - L1GasPrice: l1GasPrice, - L1GasUsed: l1GasUsed, - L1Fee: l1Fee, - FeeScalar: feeScalar, - }, - } - for _, receipt := range receipts { - receipt.Bloom = CreateBloom(receipt) - } - - return txs, receipts -} - -func TestDeriveOptimismBedrockTxReceipts(t *testing.T) { - // Bedrock style l1 attributes with L1Scalar=7_000_000 (becomes 7 after division), L1Overhead=50, L1BaseFee=1000*1e6 - payload := common.Hex2Bytes("015d8eb900000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000006acfc0015d8eb900000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000006acfc0") - // the parameters we use below are defined in rollup_test.go - l1GasPrice := baseFee - l1GasUsed := bedrockGas - feeScalar := big.NewFloat(float64(scalar.Uint64() / 1e6)) - l1Fee := bedrockFee - txs, receipts := getOptimismTxReceipts(payload, l1GasPrice, l1GasUsed, l1Fee, feeScalar) - - // Re-derive receipts. - baseFee := big.NewInt(1000) - derivedReceipts := clearComputedFieldsOnReceipts(receipts) - err := Receipts(derivedReceipts).DeriveFields(bedrockGenesisTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) - if err != nil { - t.Fatalf("DeriveFields(...) = %v, want ", err) - } - checkBedrockReceipts(t, receipts, derivedReceipts) - - // Should get same result with the Ecotone config because it will assume this is "first ecotone block" - // if it sees the bedrock style L1 attributes. - err = Receipts(derivedReceipts).DeriveFields(ecotoneTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) - if err != nil { - t.Fatalf("DeriveFields(...) = %v, want ", err) - } - checkBedrockReceipts(t, receipts, derivedReceipts) -} - -func TestDeriveOptimismEcotoneTxReceipts(t *testing.T) { - // Ecotone style l1 attributes with baseFeeScalar=2, blobBaseFeeScalar=3, baseFee=1000*1e6, blobBaseFee=10*1e6 - payload := common.Hex2Bytes("440a5e20000000020000000300000000000004d200000000000004d200000000000004d2000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000098968000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2") - // the parameters we use below are defined in rollup_test.go - baseFeeScalarUint64 := baseFeeScalar.Uint64() - blobBaseFeeScalarUint64 := blobBaseFeeScalar.Uint64() - txs, receipts := getOptimismEcotoneTxReceipts(payload, baseFee, blobBaseFee, ecotoneGas, ecotoneFee, &baseFeeScalarUint64, &blobBaseFeeScalarUint64) - - // Re-derive receipts. - baseFee := big.NewInt(1000) - derivedReceipts := clearComputedFieldsOnReceipts(receipts) - // Should error out if we try to process this with a pre-Ecotone config - err := Receipts(derivedReceipts).DeriveFields(bedrockGenesisTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) - if err == nil { - t.Fatalf("expected error from deriving ecotone receipts with pre-ecotone config, got none") - } - - err = Receipts(derivedReceipts).DeriveFields(ecotoneTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) - if err != nil { - t.Fatalf("DeriveFields(...) = %v, want ", err) - } - diffReceipts(t, receipts, derivedReceipts) -} - -func TestDeriveOptimismIsthmusTxReceipts(t *testing.T) { - // Isthmus style l1 attributes with baseFeeScalar=2, blobBaseFeeScalar=3, baseFee=1000*1e6, blobBaseFee=10*1e6, operatorFeeScalar=7, operatorFeeConstant=9 - payload := common.Hex2Bytes("098999be000000020000000300000000000004d200000000000004d200000000000004d2000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000098968000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d255c6fb7c116fb15b44847d04") - // the parameters we use below are defined in rollup_test.go - baseFeeScalarUint64 := baseFeeScalar.Uint64() - blobBaseFeeScalarUint64 := blobBaseFeeScalar.Uint64() - operatorFeeScalarUint64 := operatorFeeScalar.Uint64() - operatorFeeConstantUint64 := operatorFeeConstant.Uint64() - txs, receipts := getOptimismIsthmusTxReceipts(payload, baseFee, blobBaseFee, minimumFjordGas, fjordFee, &baseFeeScalarUint64, &blobBaseFeeScalarUint64, &operatorFeeScalarUint64, &operatorFeeConstantUint64) - - // Re-derive receipts. - baseFee := big.NewInt(1000) - derivedReceipts := clearComputedFieldsOnReceipts(receipts) - // Should error out if we try to process this with a pre-Isthmus config - err := Receipts(derivedReceipts).DeriveFields(bedrockGenesisTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) - if err == nil { - t.Fatalf("expected error from deriving isthmus receipts with pre-isthmus config, got none") - } - - err = Receipts(derivedReceipts).DeriveFields(isthmusTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) - if err != nil { - t.Fatalf("DeriveFields(...) = %v, want ", err) - } - diffReceipts(t, receipts, derivedReceipts) -} - -func TestDeriveOptimismIsthmusTxReceiptsNoOperatorFee(t *testing.T) { - // Isthmus style l1 attributes with baseFeeScalar=2, blobBaseFeeScalar=3, baseFee=1000*1e6, blobBaseFee=10*1e6, operatorFeeScalar=7, operatorFeeConstant=9 - payload := common.Hex2Bytes("098999be000000020000000300000000000004d200000000000004d200000000000004d2000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000098968000000000000000000000000000000000000000000000000000000000000004d200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000") - // the parameters we use below are defined in rollup_test.go - baseFeeScalarUint64 := baseFeeScalar.Uint64() - blobBaseFeeScalarUint64 := blobBaseFeeScalar.Uint64() - txs, receipts := getOptimismIsthmusTxReceipts(payload, baseFee, blobBaseFee, minimumFjordGas, fjordFee, &baseFeeScalarUint64, &blobBaseFeeScalarUint64, nil, nil) - - // Re-derive receipts. - baseFee := big.NewInt(1000) - derivedReceipts := clearComputedFieldsOnReceipts(receipts) - // Should error out if we try to process this with a pre-Isthmus config - err := Receipts(derivedReceipts).DeriveFields(bedrockGenesisTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) - if err == nil { - t.Fatalf("expected error from deriving isthmus receipts with pre-isthmus config, got none") - } - - err = Receipts(derivedReceipts).DeriveFields(isthmusTestConfig, blockHash, blockNumber.Uint64(), 0, baseFee, nil, txs) - if err != nil { - t.Fatalf("DeriveFields(...) = %v, want ", err) - } - diffReceipts(t, receipts, derivedReceipts) -} - -func diffReceipts(t *testing.T, receipts, derivedReceipts []*Receipt) { - // Check diff of receipts against derivedReceipts. - r1, err := json.MarshalIndent(receipts, "", " ") - if err != nil { - t.Fatal("error marshaling input receipts:", err) - } - r2, err := json.MarshalIndent(derivedReceipts, "", " ") - if err != nil { - t.Fatal("error marshaling derived receipts:", err) - } - d := diff.Diff(string(r1), string(r2)) - if d != "" { - t.Fatal("receipts differ:", d) - } -} - -func checkBedrockReceipts(t *testing.T, receipts, derivedReceipts []*Receipt) { - diffReceipts(t, receipts, derivedReceipts) - - // Check that we preserved the invariant: l1Fee = l1GasPrice * l1GasUsed * l1FeeScalar - // but with more difficult int math... - l2Rcpt := derivedReceipts[1] - l1GasCost := new(big.Int).Mul(l2Rcpt.L1GasPrice, l2Rcpt.L1GasUsed) - l1Fee := new(big.Float).Mul(new(big.Float).SetInt(l1GasCost), l2Rcpt.FeeScalar) - require.Equal(t, new(big.Float).SetInt(l2Rcpt.L1Fee), l1Fee) -} - -func TestBedrockDepositReceiptUnchanged(t *testing.T) { - expectedRlp := common.FromHex("7EF90156A003000000000000000000000000000000000000000000000000000000000000000AB9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F0D7940000000000000000000000000000000000000033C001D7940000000000000000000000000000000000000333C002") - // Deposit receipt with no nonce - receipt := &Receipt{ - Type: DepositTxType, - PostState: common.Hash{3}.Bytes(), - CumulativeGasUsed: 10, - Logs: []*Log{ - {Address: common.BytesToAddress([]byte{0x33}), Data: []byte{1}, Topics: []common.Hash{}}, - {Address: common.BytesToAddress([]byte{0x03, 0x33}), Data: []byte{2}, Topics: []common.Hash{}}, - }, - TxHash: common.Hash{}, - ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}), - GasUsed: 4, - } - - rlp, err := receipt.MarshalBinary() - require.NoError(t, err) - require.Equal(t, expectedRlp, rlp) - - // Consensus values should be unchanged after reparsing - parsed := new(Receipt) - err = parsed.UnmarshalBinary(rlp) - require.NoError(t, err) - require.Equal(t, receipt.Status, parsed.Status) - require.Equal(t, receipt.CumulativeGasUsed, parsed.CumulativeGasUsed) - require.Equal(t, receipt.Bloom, parsed.Bloom) - require.EqualValues(t, receipt.Logs, parsed.Logs) - // And still shouldn't have a nonce - require.Nil(t, parsed.DepositNonce) - // ..or a deposit nonce - require.Nil(t, parsed.DepositReceiptVersion) -} - -// Regolith introduced an inconsistency in behavior between EncodeIndex and MarshalBinary for a -// deposit transaction receipt. TestReceiptEncodeIndexBugIsEnshrined makes sure this difference is -// preserved for backwards compatibility purposes, but also that there is no discrepancy for the -// post-Canyon encoding. -func TestReceiptEncodeIndexBugIsEnshrined(t *testing.T) { - // Check that a post-Regolith, pre-Canyon receipt produces the expected difference between - // EncodeIndex and MarshalBinary. - buf := new(bytes.Buffer) - receipts := Receipts{depositReceiptWithNonce} - receipts.EncodeIndex(0, buf) - indexBytes := buf.Bytes() - - regularBytes, _ := receipts[0].MarshalBinary() - - require.NotEqual(t, indexBytes, regularBytes) - - // Confirm the buggy encoding is as expected, which means it should encode as if it had no - // nonce specified (like that of a non-deposit receipt, whose encoding would differ only in the - // type byte). - buf.Reset() - tempReceipt := *depositReceiptWithNonce - tempReceipt.Type = eip1559Receipt.Type - buggyBytes, _ := tempReceipt.MarshalBinary() - - require.Equal(t, indexBytes[1:], buggyBytes[1:]) - - // check that the post-Canyon encoding has no differences between EncodeIndex and - // MarshalBinary. - buf.Reset() - receipts = Receipts{depositReceiptWithNonceAndVersion} - receipts.EncodeIndex(0, buf) - indexBytes = buf.Bytes() - - regularBytes, _ = receipts[0].MarshalBinary() - - require.Equal(t, indexBytes, regularBytes) - - // Check that bumping the nonce post-canyon changes the hash - bumpedReceipt := *depositReceiptWithNonceAndVersion - bumpedNonce := nonce + 1 - bumpedReceipt.DepositNonce = &bumpedNonce - bumpedBytes, _ := bumpedReceipt.MarshalBinary() - require.NotEqual(t, regularBytes, bumpedBytes) -} - -func TestRoundTripReceipt(t *testing.T) { - tests := []struct { - name string - rcpt *Receipt - }{ - {name: "Legacy", rcpt: legacyReceipt}, - {name: "AccessList", rcpt: accessListReceipt}, - {name: "EIP1559", rcpt: eip1559Receipt}, - {name: "DepositNoNonce", rcpt: depositReceiptNoNonce}, - {name: "DepositWithNonce", rcpt: depositReceiptWithNonce}, - {name: "DepositWithNonceAndVersion", rcpt: depositReceiptWithNonceAndVersion}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - data, err := test.rcpt.MarshalBinary() - require.NoError(t, err) - - d := &Receipt{} - err = d.UnmarshalBinary(data) - require.NoError(t, err) - require.Equal(t, test.rcpt, d) - require.Equal(t, test.rcpt.DepositNonce, d.DepositNonce) - require.Equal(t, test.rcpt.DepositReceiptVersion, d.DepositReceiptVersion) - }) - - t.Run(fmt.Sprintf("%sRejectExtraData", test.name), func(t *testing.T) { - data, err := test.rcpt.MarshalBinary() - require.NoError(t, err) - data = append(data, 1, 2, 3, 4) - d := &Receipt{} - err = d.UnmarshalBinary(data) - require.Error(t, err) - }) - } -} - -func TestRoundTripReceiptForStorage(t *testing.T) { - tests := []struct { - name string - rcpt *Receipt - }{ - {name: "Legacy", rcpt: legacyReceipt}, - {name: "AccessList", rcpt: accessListReceipt}, - {name: "EIP1559", rcpt: eip1559Receipt}, - {name: "DepositNoNonce", rcpt: depositReceiptNoNonce}, - {name: "DepositWithNonce", rcpt: depositReceiptWithNonce}, - {name: "DepositWithNonceAndVersion", rcpt: depositReceiptWithNonceAndVersion}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - data, err := rlp.EncodeToBytes((*ReceiptForStorage)(test.rcpt)) - require.NoError(t, err) - - d := &ReceiptForStorage{} - err = rlp.DecodeBytes(data, d) - require.NoError(t, err) - // Only check the stored fields - the others are derived later - require.Equal(t, test.rcpt.Status, d.Status) - require.Equal(t, test.rcpt.CumulativeGasUsed, d.CumulativeGasUsed) - require.Equal(t, test.rcpt.Logs, d.Logs) - require.Equal(t, test.rcpt.DepositNonce, d.DepositNonce) - require.Equal(t, test.rcpt.DepositReceiptVersion, d.DepositReceiptVersion) - }) - } -} diff --git a/fork.yaml b/fork.yaml index a0af43cd66..f4c1d93b5f 100644 --- a/fork.yaml +++ b/fork.yaml @@ -253,12 +253,15 @@ def: - title: "User API enhancements" description: "Encode the Deposit Tx properties, the L1 costs, and daisy-chain RPC-calls for pre-Bedrock historical data" sub: - - title: "Receipts metadata" + - title: "Receipts metadata & deposit receipts" description: | Pre-Bedrock L1-cost receipt data is loaded from the database if available, and post-Bedrock the L1-cost metadata is hydrated on-the-fly based on the L1 fee information in the corresponding block. + We also populate receipts with L1 block attributes like Operator Fee and DA Footprint parameters. + Furthermore, OP Stack introduces Deposit receipts, a special kind of receipts for Deposit transactions. globs: - "core/types/receipt.go" + - "core/types/receipt_opstack.go" - "core/types/gen_receipt_json.go" - "core/rawdb/accessors_chain.go" - title: "API Backend" diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 0c0d386db1..fc0b606716 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1710,7 +1710,7 @@ func marshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber u "effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice), } - if chainConfig.Optimism != nil && !tx.IsDepositTx() { + if chainConfig.IsOptimism() && !tx.IsDepositTx() { fields["l1GasPrice"] = (*hexutil.Big)(receipt.L1GasPrice) fields["l1GasUsed"] = (*hexutil.Big)(receipt.L1GasUsed) fields["l1Fee"] = (*hexutil.Big)(receipt.L1Fee) @@ -1735,8 +1735,14 @@ func marshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber u if receipt.OperatorFeeConstant != nil { fields["operatorFeeConstant"] = hexutil.Uint64(*receipt.OperatorFeeConstant) } + // Fields added in Jovian + if receipt.DAFootprintGasScalar != nil { + fields["daFootprintGasScalar"] = hexutil.Uint64(*receipt.DAFootprintGasScalar) + // Jovian repurposes blobGasUsed for DA footprint gas used + fields["blobGasUsed"] = hexutil.Uint64(receipt.BlobGasUsed) + } } - if chainConfig.Optimism != nil && tx.IsDepositTx() && receipt.DepositNonce != nil { + if chainConfig.IsOptimism() && tx.IsDepositTx() && receipt.DepositNonce != nil { fields["depositNonce"] = hexutil.Uint64(*receipt.DepositNonce) if receipt.DepositReceiptVersion != nil { fields["depositReceiptVersion"] = hexutil.Uint64(*receipt.DepositReceiptVersion) From 37ffc0cf36e3316f317cecd5794c759fc2e5c917 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 15 Sep 2025 15:34:57 +0200 Subject: [PATCH 13/17] core/txpool/blobpool: migrate billy to new slot size (#31966) Implements a migration path for the blobpool slotter --------- Co-authored-by: lightclient Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com> Co-authored-by: Gary Rong --- core/txpool/blobpool/blobpool.go | 21 ++++- core/txpool/blobpool/blobpool_test.go | 109 ++++++++++++++++++++++++++ core/txpool/blobpool/limbo.go | 16 +++- core/txpool/blobpool/slotter.go | 84 +++++++++++++++++++- core/txpool/blobpool/slotter_test.go | 45 ++++++++++- crypto/kzg4844/kzg4844.go | 4 +- go.mod | 2 +- go.sum | 4 +- 8 files changed, 274 insertions(+), 11 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 5f1ea1cf8c..0e9c258ac4 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -55,6 +55,12 @@ const ( // tiny overflows causing all txs to move a shelf higher, wasting disk space. txAvgSize = 4 * 1024 + // txBlobOverhead is an approximation of the overhead that an additional blob + // has on transaction size. This is added to the slotter to avoid tiny + // overflows causing all txs to move a shelf higher, wasting disk space. A + // small buffer is added to the proof overhead. + txBlobOverhead = uint32(kzg4844.CellProofsPerBlob*len(kzg4844.Proof{}) + 64) + // txMaxSize is the maximum size a single transaction can have, outside // the included blobs. Since blob transactions are pulled instead of pushed, // and only a small metadata is kept in ram, the rest is on disk, there is @@ -83,6 +89,10 @@ const ( // limboedTransactionStore is the subfolder containing the currently included // but not yet finalized transaction blobs. limboedTransactionStore = "limbo" + + // storeVersion is the current slotter layout used for the billy.Database + // store. + storeVersion = 1 ) // blobTxMeta is the minimal subset of types.BlobTx necessary to validate and @@ -389,6 +399,14 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser } p.head, p.state = head, state + // Create new slotter for pre-Osaka blob configuration. + slotter := newSlotter(eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) + + // See if we need to migrate the queue blob store after fusaka + slotter, err = tryMigrate(p.chain.Config(), slotter, queuedir) + if err != nil { + return err + } // Index all transactions on disk and delete anything unprocessable var fails []uint64 index := func(id uint64, size uint32, blob []byte) { @@ -396,7 +414,6 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser fails = append(fails, id) } } - slotter := newSlotter(eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) store, err := billy.Open(billy.Options{Path: queuedir, Repair: true}, slotter, index) if err != nil { return err @@ -430,7 +447,7 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reser // Pool initialized, attach the blob limbo to it to track blobs included // recently but not yet finalized - p.limbo, err = newLimbo(limbodir, eip4844.LatestMaxBlobsPerBlock(p.chain.Config())) + p.limbo, err = newLimbo(p.chain.Config(), limbodir) if err != nil { p.Close() return err diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index aaa7aed3e3..8f318cbc52 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -1163,6 +1163,115 @@ func TestChangingSlotterSize(t *testing.T) { } } +// TestBillyMigration tests the billy migration from the default slotter to +// the PeerDAS slotter. This tests both the migration of the slotter +// as well as increasing the slotter size of the new slotter. +func TestBillyMigration(t *testing.T) { + //log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true))) + + // Create a temporary folder for the persistent backend + storage := t.TempDir() + + os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) + os.MkdirAll(filepath.Join(storage, limboedTransactionStore), 0700) + // Create the billy with the old slotter + oldSlotter := newSlotterEIP7594(6) + store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, oldSlotter, nil) + + // Create transactions from a few accounts. + var ( + key1, _ = crypto.GenerateKey() + key2, _ = crypto.GenerateKey() + key3, _ = crypto.GenerateKey() + + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + addr3 = crypto.PubkeyToAddress(key3.PublicKey) + + tx1 = makeMultiBlobTx(0, 1, 1000, 100, 6, 0, key1, types.BlobSidecarVersion0) + tx2 = makeMultiBlobTx(0, 1, 800, 70, 6, 0, key2, types.BlobSidecarVersion0) + tx3 = makeMultiBlobTx(0, 1, 800, 110, 24, 0, key3, types.BlobSidecarVersion0) + + blob1, _ = rlp.EncodeToBytes(tx1) + blob2, _ = rlp.EncodeToBytes(tx2) + ) + + // Write the two safely sized txs to store. note: although the store is + // configured for a blob count of 6, it can also support around ~1mb of call + // data - all this to say that we aren't using the the absolute largest shelf + // available. + store.Put(blob1) + store.Put(blob2) + store.Close() + + // Mimic a blobpool with max blob count of 6 upgrading to a max blob count of 24. + for _, maxBlobs := range []int{6, 24} { + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(addr3, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.Commit(0, true, false) + + // Make custom chain config where the max blob count changes based on the loop variable. + zero := uint64(0) + config := ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + LondonBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + CancunTime: &zero, + OsakaTime: &zero, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: ¶ms.BlobConfig{ + Target: maxBlobs / 2, + Max: maxBlobs, + UpdateFraction: params.DefaultCancunBlobConfig.UpdateFraction, + }, + Osaka: ¶ms.BlobConfig{ + Target: maxBlobs / 2, + Max: maxBlobs, + UpdateFraction: params.DefaultCancunBlobConfig.UpdateFraction, + }, + }, + } + chain := &testBlockChain{ + config: config, + basefee: uint256.NewInt(1050), + blobfee: uint256.NewInt(105), + statedb: statedb, + } + pool := New(Config{Datadir: storage}, chain, nil) + if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil { + t.Fatalf("failed to create blob pool: %v", err) + } + + // Try to add the big blob tx. In the initial iteration it should overflow + // the pool. On the subsequent iteration it should be accepted. + errs := pool.Add([]*types.Transaction{tx3}, true) + if _, ok := pool.index[addr3]; ok && maxBlobs == 6 { + t.Errorf("expected insert of oversized blob tx to fail: blobs=24, maxBlobs=%d, err=%v", maxBlobs, errs[0]) + } else if !ok && maxBlobs == 10 { + t.Errorf("expected insert of oversized blob tx to succeed: blobs=24, maxBlobs=%d, err=%v", maxBlobs, errs[0]) + } + + // Verify the regular two txs are always available. + if got := pool.Get(tx1.Hash()); got == nil { + t.Errorf("expected tx %s from %s in pool", tx1.Hash(), addr1) + } + if got := pool.Get(tx2.Hash()); got == nil { + t.Errorf("expected tx %s from %s in pool", tx2.Hash(), addr2) + } + + // Verify all the calculated pool internals. Interestingly, this is **not** + // a duplication of the above checks, this actually validates the verifier + // using the above already hard coded checks. + // + // Do not remove this, nor alter the above to be generic. + verifyPoolInternals(t, pool) + + pool.Close() + } +} + // TestBlobCountLimit tests the blobpool enforced limits on the max blob count. func TestBlobCountLimit(t *testing.T) { var ( diff --git a/core/txpool/blobpool/limbo.go b/core/txpool/blobpool/limbo.go index 99d1b4ad6b..50c40c9d83 100644 --- a/core/txpool/blobpool/limbo.go +++ b/core/txpool/blobpool/limbo.go @@ -20,8 +20,10 @@ import ( "errors" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/holiman/billy" ) @@ -48,11 +50,21 @@ type limbo struct { } // newLimbo opens and indexes a set of limboed blob transactions. -func newLimbo(datadir string, maxBlobsPerTransaction int) (*limbo, error) { +func newLimbo(config *params.ChainConfig, datadir string) (*limbo, error) { l := &limbo{ index: make(map[common.Hash]uint64), groups: make(map[uint64]map[uint64]common.Hash), } + + // Create new slotter for pre-Osaka blob configuration. + slotter := newSlotter(eip4844.LatestMaxBlobsPerBlock(config)) + + // See if we need to migrate the limbo after fusaka. + slotter, err := tryMigrate(config, slotter, datadir) + if err != nil { + return nil, err + } + // Index all limboed blobs on disk and delete anything unprocessable var fails []uint64 index := func(id uint64, size uint32, data []byte) { @@ -60,7 +72,7 @@ func newLimbo(datadir string, maxBlobsPerTransaction int) (*limbo, error) { fails = append(fails, id) } } - store, err := billy.Open(billy.Options{Path: datadir, Repair: true}, newSlotter(maxBlobsPerTransaction), index) + store, err := billy.Open(billy.Options{Path: datadir, Repair: true}, slotter, index) if err != nil { return nil, err } diff --git a/core/txpool/blobpool/slotter.go b/core/txpool/blobpool/slotter.go index 84ccc0f27b..9b793e366c 100644 --- a/core/txpool/blobpool/slotter.go +++ b/core/txpool/blobpool/slotter.go @@ -16,6 +16,49 @@ package blobpool +import ( + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/billy" +) + +// tryMigrate checks if the billy needs to be migrated and migrates if needed. +// Returns a slotter that can be used for the database. +func tryMigrate(config *params.ChainConfig, slotter billy.SlotSizeFn, datadir string) (billy.SlotSizeFn, error) { + // Check if we need to migrate our blob db to the new slotter. + if config.OsakaTime != nil { + // Open the store using the version slotter to see if any version has been + // written. + var version int + index := func(_ uint64, _ uint32, blob []byte) { + version = max(version, parseSlotterVersion(blob)) + } + store, err := billy.Open(billy.Options{Path: datadir}, newVersionSlotter(), index) + if err != nil { + return nil, err + } + store.Close() + + // If the version found is less than the currently configured store version, + // perform a migration then write the updated version of the store. + if version < storeVersion { + newSlotter := newSlotterEIP7594(eip4844.LatestMaxBlobsPerBlock(config)) + if err := billy.Migrate(billy.Options{Path: datadir, Repair: true}, slotter, newSlotter); err != nil { + return nil, err + } + store, err = billy.Open(billy.Options{Path: datadir}, newVersionSlotter(), nil) + if err != nil { + return nil, err + } + writeSlotterVersion(store, storeVersion) + store.Close() + } + // Set the slotter to the format now that the Osaka is active. + slotter = newSlotterEIP7594(eip4844.LatestMaxBlobsPerBlock(config)) + } + return slotter, nil +} + // newSlotter creates a helper method for the Billy datastore that returns the // individual shelf sizes used to store transactions in. // @@ -25,7 +68,7 @@ package blobpool // The slotter also creates a shelf for 0-blob transactions. Whilst those are not // allowed in the current protocol, having an empty shelf is not a relevant use // of resources, but it makes stress testing with junk transactions simpler. -func newSlotter(maxBlobsPerTransaction int) func() (uint32, bool) { +func newSlotter(maxBlobsPerTransaction int) billy.SlotSizeFn { slotsize := uint32(txAvgSize) slotsize -= uint32(blobSize) // underflows, it's ok, will overflow back in the first return @@ -36,3 +79,42 @@ func newSlotter(maxBlobsPerTransaction int) func() (uint32, bool) { return slotsize, finished } } + +// newSlotterEIP7594 creates a different slotter for EIP-7594 transactions. +// EIP-7594 (PeerDAS) changes the average transaction size which means the current +// static 4KB average size is not enough anymore. +// This slotter adds a dynamic overhead component to the slotter, which also +// captures the notion that blob transactions with more blobs are also more likely to +// to have more calldata. +func newSlotterEIP7594(maxBlobsPerTransaction int) billy.SlotSizeFn { + slotsize := uint32(txAvgSize) + slotsize -= uint32(blobSize) + txBlobOverhead // underflows, it's ok, will overflow back in the first return + + return func() (size uint32, done bool) { + slotsize += blobSize + txBlobOverhead + finished := slotsize > uint32(maxBlobsPerTransaction)*(blobSize+txBlobOverhead)+txMaxSize + + return slotsize, finished + } +} + +// newVersionSlotter creates a slotter with a single 8 byte shelf to store +// version metadata in. +func newVersionSlotter() billy.SlotSizeFn { + return func() (size uint32, done bool) { + return 8, true + } +} + +// parseSlotterVersion will parse the slotter's version from a given data blob. +func parseSlotterVersion(blob []byte) int { + if len(blob) > 0 { + return int(blob[0]) + } + return 0 +} + +// writeSlotterVersion writes the current slotter version into the store. +func writeSlotterVersion(store billy.Database, version int) { + store.Put([]byte{byte(version)}) +} diff --git a/core/txpool/blobpool/slotter_test.go b/core/txpool/blobpool/slotter_test.go index 8d46f47d2c..e4cf232f4e 100644 --- a/core/txpool/blobpool/slotter_test.go +++ b/core/txpool/blobpool/slotter_test.go @@ -16,7 +16,9 @@ package blobpool -import "testing" +import ( + "testing" +) // Tests that the slotter creates the expected database shelves. func TestNewSlotter(t *testing.T) { @@ -58,3 +60,44 @@ func TestNewSlotter(t *testing.T) { } } } + +// Tests that the slotter creates the expected database shelves. +func TestNewSlotterEIP7594(t *testing.T) { + // Generate the database shelve sizes + slotter := newSlotterEIP7594(6) + + var shelves []uint32 + for { + shelf, done := slotter() + shelves = append(shelves, shelf) + if done { + break + } + } + // Compare the database shelves to the expected ones + want := []uint32{ + 0*blobSize + 0*txBlobOverhead + txAvgSize, // 0 blob + some expected tx infos + 1*blobSize + 1*txBlobOverhead + txAvgSize, // 1 blob + some expected tx infos + 2*blobSize + 2*txBlobOverhead + txAvgSize, // 2 blob + some expected tx infos (could be fewer blobs and more tx data) + 3*blobSize + 3*txBlobOverhead + txAvgSize, // 3 blob + some expected tx infos (could be fewer blobs and more tx data) + 4*blobSize + 4*txBlobOverhead + txAvgSize, // 4 blob + some expected tx infos (could be fewer blobs and more tx data) + 5*blobSize + 5*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 6*blobSize + 6*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 7*blobSize + 7*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 8*blobSize + 8*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 9*blobSize + 9*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 10*blobSize + 10*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 11*blobSize + 11*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 12*blobSize + 12*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 13*blobSize + 13*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos < 4 blobs + max tx metadata size + 14*blobSize + 14*txBlobOverhead + txAvgSize, // 1-6 blobs + unexpectedly large tx infos >= 4 blobs + max tx metadata size + } + if len(shelves) != len(want) { + t.Errorf("shelves count mismatch: have %d, want %d", len(shelves), len(want)) + } + for i := 0; i < len(shelves) && i < len(want); i++ { + if shelves[i] != want[i] { + t.Errorf("shelf %d mismatch: have %d, want %d", i, shelves[i], want[i]) + } + } +} diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index 9da2386368..3ccc204838 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -34,10 +34,10 @@ var ( blobT = reflect.TypeFor[Blob]() commitmentT = reflect.TypeFor[Commitment]() proofT = reflect.TypeFor[Proof]() - - CellProofsPerBlob = 128 ) +const CellProofsPerBlob = 128 + // Blob represents a 4844 data blob. type Blob [131072]byte diff --git a/go.mod b/go.mod index 8707533271..3197ca1877 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/graph-gophers/graphql-go v1.3.0 github.com/hashicorp/go-bexpr v0.1.10 - github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 + github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db github.com/holiman/bloomfilter/v2 v2.0.3 github.com/holiman/uint256 v1.3.2 github.com/huin/goupnp v1.3.0 diff --git a/go.sum b/go.sum index 69320f34b8..e036aa0532 100644 --- a/go.sum +++ b/go.sum @@ -191,8 +191,8 @@ github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY4 github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= -github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330= +github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db/go.mod h1:xTEYN9KCHxuYHs+NmrmzFcnvHMzLLNiGFafCb1n3Mfg= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= From 3e7dd84c9ae22bef7d350f021ed243a3c54cb9c9 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 3 Nov 2025 17:41:22 +0100 Subject: [PATCH 14/17] params: set osaka and BPO1 & BPO2 mainnet dates (#33063) Sets the fusaka, bpo1, bpo2 timestamps for mainnet see: https://notes.ethereum.org/@bbusa/fusaka-bpo-timeline --- core/forkid/forkid_test.go | 19 ++++++++++++++----- core/txpool/blobpool/blobpool_test.go | 6 +++--- params/config.go | 6 ++++++ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index dc6e6fe817..c78ff23cd6 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -76,10 +76,16 @@ func TestCreation(t *testing.T) { {20000000, 1681338454, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1681338455}}, // Last Gray Glacier block {20000000, 1681338455, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}}, // First Shanghai block {30000000, 1710338134, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}}, // Last Shanghai block - {40000000, 1710338135, ID{Hash: checksumToBytes(0x9f3d2254), Next: 1746612311}}, // First Cancun block + {30000000, 1710338135, ID{Hash: checksumToBytes(0x9f3d2254), Next: 1746612311}}, // First Cancun block {30000000, 1746022486, ID{Hash: checksumToBytes(0x9f3d2254), Next: 1746612311}}, // Last Cancun block - {30000000, 1746612311, ID{Hash: checksumToBytes(0xc376cf8b), Next: 0}}, // First Prague block - {50000000, 2000000000, ID{Hash: checksumToBytes(0xc376cf8b), Next: 0}}, // Future Prague block + {30000000, 1746612311, ID{Hash: checksumToBytes(0xc376cf8b), Next: 1764798551}}, // First Prague block + {30000000, 1764798550, ID{Hash: checksumToBytes(0xc376cf8b), Next: 1764798551}}, // Last Prague block + {30000000, 1764798551, ID{Hash: checksumToBytes(0x5167e2a6), Next: 1765290071}}, // First Osaka block + {30000000, 1765290070, ID{Hash: checksumToBytes(0x5167e2a6), Next: 1765290071}}, // Last Osaka block + {30000000, 1765290071, ID{Hash: checksumToBytes(0xcba2a1c0), Next: 1767747671}}, // First BPO1 block + {30000000, 1767747670, ID{Hash: checksumToBytes(0xcba2a1c0), Next: 1767747671}}, // Last BPO1 block + {30000000, 1767747671, ID{Hash: checksumToBytes(0x07c9462e), Next: 0}}, // First BPO2 block + {50000000, 2000000000, ID{Hash: checksumToBytes(0x07c9462e), Next: 0}}, // Future BPO2 block }, }, // Sepolia test cases @@ -162,6 +168,9 @@ func TestValidation(t *testing.T) { legacyConfig.ShanghaiTime = nil legacyConfig.CancunTime = nil legacyConfig.PragueTime = nil + legacyConfig.OsakaTime = nil + legacyConfig.BPO1Time = nil + legacyConfig.BPO2Time = nil tests := []struct { config *params.ChainConfig @@ -361,11 +370,11 @@ func TestValidation(t *testing.T) { // Local is mainnet Shanghai, remote is random Shanghai. {params.MainnetChainConfig, 20000000, 1681338455, ID{Hash: checksumToBytes(0x12345678), Next: 0}, ErrLocalIncompatibleOrStale}, - // Local is mainnet Prague, far in the future. Remote announces Gopherium (non existing fork) + // Local is mainnet BPO2, far in the future. Remote announces Gopherium (non existing fork) // at some future timestamp 8888888888, for itself, but past block for local. Local is incompatible. // // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). - {params.MainnetChainConfig, 88888888, 8888888888, ID{Hash: checksumToBytes(0xc376cf8b), Next: 8888888888}, ErrLocalIncompatibleOrStale}, + {params.MainnetChainConfig, 88888888, 8888888888, ID{Hash: checksumToBytes(0x07c9462e), Next: 8888888888}, ErrLocalIncompatibleOrStale}, // Local is mainnet Shanghai. Remote is also in Shanghai, but announces Gopherium (non existing // fork) at timestamp 1668000000, before Cancun. Local is incompatible. diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 8f318cbc52..551c854d9b 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -984,7 +984,7 @@ func TestOpenCap(t *testing.T) { storage := t.TempDir() os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) - store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(testMaxBlobsPerBlock), nil) + store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotterEIP7594(testMaxBlobsPerBlock), nil) // Insert a few transactions from a few accounts var ( @@ -1006,7 +1006,7 @@ func TestOpenCap(t *testing.T) { keep = []common.Address{addr1, addr3} drop = []common.Address{addr2} - size = uint64(2 * (txAvgSize + blobSize)) + size = 2 * (txAvgSize + blobSize + uint64(txBlobOverhead)) ) store.Put(blob1) store.Put(blob2) @@ -1015,7 +1015,7 @@ func TestOpenCap(t *testing.T) { // Verify pool capping twice: first by reducing the data cap, then restarting // with a high cap to ensure everything was persisted previously - for _, datacap := range []uint64{2 * (txAvgSize + blobSize), 100 * (txAvgSize + blobSize)} { + for _, datacap := range []uint64{2 * (txAvgSize + blobSize + uint64(txBlobOverhead)), 1000 * (txAvgSize + blobSize + uint64(txBlobOverhead))} { // Create a blob pool out of the pre-seeded data, but cap it to 2 blob transaction statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) diff --git a/params/config.go b/params/config.go index 656036d0e1..e585e66d8c 100644 --- a/params/config.go +++ b/params/config.go @@ -68,11 +68,17 @@ var ( ShanghaiTime: newUint64(1681338455), CancunTime: newUint64(1710338135), PragueTime: newUint64(1746612311), + OsakaTime: newUint64(1764798551), + BPO1Time: newUint64(1765290071), + BPO2Time: newUint64(1767747671), DepositContractAddress: common.HexToAddress("0x00000000219ab540356cbb839cbe05303d7705fa"), Ethash: new(EthashConfig), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, + Osaka: DefaultOsakaBlobConfig, + BPO1: DefaultBPO1BlobConfig, + BPO2: DefaultBPO2BlobConfig, }, } // HoleskyChainConfig contains the chain parameters to run a node on the Holesky test network. From 9a4dd63333573d7aeb585c9fa6c727bb5d26f65b Mon Sep 17 00:00:00 2001 From: Josh Klopfenstein Date: Mon, 3 Nov 2025 12:46:19 -0800 Subject: [PATCH 15/17] superchain: update for new Jovian timestamps (#716) --- superchain-registry-commit.txt | 2 +- superchain/superchain-configs.zip | Bin 3911177 -> 3911169 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/superchain-registry-commit.txt b/superchain-registry-commit.txt index 6c56d56893..b5243710e5 100644 --- a/superchain-registry-commit.txt +++ b/superchain-registry-commit.txt @@ -1 +1 @@ -d34fd7ed60dde93c83976dad6d66c20316d536a8 \ No newline at end of file +720185c32b0599fa31b14f101cbc990ec39c0a36 diff --git a/superchain/superchain-configs.zip b/superchain/superchain-configs.zip index c4c4ed00557125a0a811f2148afca2c0f0cad4e8..c0317192e512f006f24062a658e85d4250eaf8c7 100644 GIT binary patch delta 30426 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