diff --git a/cmd/devp2p/internal/ethtest/testdata/genesis.json b/cmd/devp2p/internal/ethtest/testdata/genesis.json index 4cfebdcac10d..25f8769618c7 100644 --- a/cmd/devp2p/internal/ethtest/testdata/genesis.json +++ b/cmd/devp2p/internal/ethtest/testdata/genesis.json @@ -18,7 +18,13 @@ "shanghaiTime": 780, "cancunTime": 840, "terminalTotalDifficulty": 9454784, - "ethash": {} + "ethash": {}, + "blobSchedule": { + "cancun": { + "target": 3, + "max": 6 + } + } }, "nonce": "0x0", "timestamp": "0x0", @@ -108,4 +114,4 @@ "baseFeePerGas": null, "excessBlobGas": null, "blobGasUsed": null -} \ No newline at end of file +} diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index aef497885ed9..d53ebf40e07b 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -190,7 +190,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, parentExcessBlobGas := pre.Env.ParentExcessBlobGas parentBlobGasUsed := pre.Env.ParentBlobGasUsed if parentExcessBlobGas != nil && parentBlobGasUsed != nil { - excessBlobGas = eip4844.CalcExcessBlobGas(*parentExcessBlobGas, *parentBlobGasUsed) + parent := &types.Header{ + ParentHash: common.Hash{}, + Number: new(big.Int).SetUint64(pre.Env.Number - 1), + Time: pre.Env.ParentTimestamp, + ExcessBlobGas: pre.Env.ParentExcessBlobGas, + BlobGasUsed: pre.Env.ParentBlobGasUsed, + } + excessBlobGas = eip4844.CalcExcessBlobGas(chainConfig, parent) vmContext.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas) } } @@ -234,7 +241,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, txBlobGas := uint64(0) if tx.Type() == types.BlobTxType { txBlobGas = uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes())) - if used, max := blobGasUsed+txBlobGas, uint64(params.MaxBlobGasPerBlock); used > max { + if used, max := blobGasUsed+txBlobGas, chainConfig.MaxBlobsPerBlock(pre.Env.Number)*params.BlobTxBlobGasPerBlob; used > max { err := fmt.Errorf("blob gas (%d) would exceed maximum allowance %d", used, max) log.Warn("rejected tx", "index", i, "err", err) rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) diff --git a/consensus/misc/eip4844/eip4844.go b/consensus/misc/eip4844/eip4844.go index 3b59dfbea253..7d3006fa3bbf 100644 --- a/consensus/misc/eip4844/eip4844.go +++ b/consensus/misc/eip4844/eip4844.go @@ -42,7 +42,7 @@ 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 max := config.LatestMaxBlobsPerBlock(header.Time); *header.BlobGasUsed > max*params.BlobTxBlobGasPerBlob { + if max := config.MaxBlobsPerBlock(header.Time) * params.BlobTxBlobGasPerBlob; *header.BlobGasUsed > max { return fmt.Errorf("blob gas used %d exceeds maximum allowance %d", *header.BlobGasUsed, max) } if *header.BlobGasUsed%params.BlobTxBlobGasPerBlob != 0 { @@ -61,7 +61,7 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade // blobs on top of the excess blob gas. func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header) uint64 { var ( - target = config.LatestTargetBlobsPerBlock(parent.Time) * params.BlobTxBlobGasPerBlob + target = config.TargetBlobsPerBlock(parent.Time) * params.BlobTxBlobGasPerBlob parentExcessBlobGas uint64 parentBlobGasUsed uint64 ) diff --git a/consensus/misc/eip4844/eip4844_test.go b/consensus/misc/eip4844/eip4844_test.go index ec417380fcb0..20a4b4507877 100644 --- a/consensus/misc/eip4844/eip4844_test.go +++ b/consensus/misc/eip4844/eip4844_test.go @@ -21,10 +21,16 @@ import ( "math/big" "testing" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" ) func TestCalcExcessBlobGas(t *testing.T) { + var ( + config = params.MainnetChainConfig + targetBlobs = config.TargetBlobsPerBlock(*config.CancunTime) + targetBlobGas = targetBlobs * params.BlobTxBlobGasPerBlob + ) var tests = []struct { excess uint64 blobs uint64 @@ -34,23 +40,29 @@ func TestCalcExcessBlobGas(t *testing.T) { // slots are below - or equal - to the target. {0, 0, 0}, {0, 1, 0}, - {0, params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob, 0}, + {0, targetBlobs, 0}, // If the target blob gas is exceeded, the excessBlobGas should increase // by however much it was overshot - {0, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) + 1, params.BlobTxBlobGasPerBlob}, - {1, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) + 1, params.BlobTxBlobGasPerBlob + 1}, - {1, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) + 2, 2*params.BlobTxBlobGasPerBlob + 1}, + {0, targetBlobs + 1, params.BlobTxBlobGasPerBlob}, + {1, targetBlobs + 1, params.BlobTxBlobGasPerBlob + 1}, + {1, targetBlobs + 2, 2*params.BlobTxBlobGasPerBlob + 1}, // The excess blob gas should decrease by however much the target was // under-shot, capped at zero. - {params.BlobTxTargetBlobGasPerBlock, params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob, params.BlobTxTargetBlobGasPerBlock}, - {params.BlobTxTargetBlobGasPerBlock, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) - 1, params.BlobTxTargetBlobGasPerBlock - params.BlobTxBlobGasPerBlob}, - {params.BlobTxTargetBlobGasPerBlock, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) - 2, params.BlobTxTargetBlobGasPerBlock - (2 * params.BlobTxBlobGasPerBlob)}, - {params.BlobTxBlobGasPerBlob - 1, (params.BlobTxTargetBlobGasPerBlock / params.BlobTxBlobGasPerBlob) - 1, 0}, + {targetBlobGas, targetBlobs, targetBlobGas}, + {targetBlobGas, targetBlobs - 1, targetBlobGas - params.BlobTxBlobGasPerBlob}, + {targetBlobGas, targetBlobs - 2, targetBlobGas - (2 * params.BlobTxBlobGasPerBlob)}, + {params.BlobTxBlobGasPerBlob - 1, targetBlobs - 1, 0}, } for i, tt := range tests { - result := CalcExcessBlobGas(tt.excess, tt.blobs*params.BlobTxBlobGasPerBlob) + blobGasUsed := tt.blobs * params.BlobTxBlobGasPerBlob + parent := &types.Header{ + Time: *config.CancunTime, + ExcessBlobGas: &tt.excess, + BlobGasUsed: &blobGasUsed, + } + result := CalcExcessBlobGas(config, parent) if result != tt.want { t.Errorf("test %d: excess blob gas mismatch: have %v, want %v", i, result, tt.want) } diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index f72e6285dfa3..b3b1ffde4a31 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -42,7 +42,7 @@ func TestGeneratePOSChain(t *testing.T) { aa = common.Address{0xaa} bb = common.Address{0xbb} funds = big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(params.Ether)) - config = *params.AllEthashProtocolChanges + config = *params.MergedTestChainConfig gspec = &Genesis{ Config: &config, Alloc: types.GenesisAlloc{ @@ -57,10 +57,6 @@ func TestGeneratePOSChain(t *testing.T) { db = rawdb.NewMemoryDatabase() ) - config.TerminalTotalDifficulty = common.Big0 - config.ShanghaiTime = u64(0) - config.CancunTime = u64(0) - // init 0xaa with some storage elements storage := make(map[common.Hash]common.Hash) storage[common.Hash{0x00}] = common.Hash{0x00} diff --git a/core/state_processor_test.go b/core/state_processor_test.go index b7c59b5124e7..b17c0ad2b2e8 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -46,25 +46,7 @@ func u64(val uint64) *uint64 { return &val } // contain invalid transactions func TestStateProcessorErrors(t *testing.T) { var ( - config = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - Ethash: new(params.EthashConfig), - TerminalTotalDifficulty: big.NewInt(0), - ShanghaiTime: new(uint64), - CancunTime: new(uint64), - PragueTime: new(uint64), - } + config = params.MergedTestChainConfig signer = types.LatestSigner(config) key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") key2, _ = crypto.HexToECDSA("0202020202020202020202020202020202020202020202020202002020202020") diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 9a493a1ac998..fae00978e792 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -51,11 +51,6 @@ const ( // transaction. There can be multiple of these embedded into a single tx. blobSize = params.BlobTxFieldElementsPerBlob * params.BlobTxBytesPerFieldElement - // maxBlobsPerTransaction is the maximum number of blobs a single transaction - // is allowed to contain. Whilst the spec states it's unlimited, the block - // data slots are protocol bound, which implicitly also limit this. - // maxBlobsPerTransaction = params.MaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob - // txAvgSize is an approximate byte size of a transaction metadata to avoid // tiny overflows causing all txs to move a shelf higher, wasting disk space. txAvgSize = 4 * 1024 @@ -223,6 +218,11 @@ func newBlobTxMeta(id uint64, size uint32, tx *types.Transaction) *blobTxMeta { // very relaxed ones can be included even if the fees go up, when the closer // ones could already be invalid. // +// - Because the maximum number of blobs allowed in a block can change per +// fork, the pool is designed to handle the maximum number of blobs allowed +// in the chain's latest defined fork -- even if it isn't active. This +// avoids needing to upgrade the database around the fork boundary. +// // When the pool eventually reaches saturation, some old transactions - that may // never execute - will need to be evicted in favor of newer ones. The eviction // strategy is quite complex: @@ -387,7 +387,8 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.Addres fails = append(fails, id) } } - store, err := billy.Open(billy.Options{Path: queuedir, Repair: true}, newSlotter(), index) + slotter := newSlotter(p.chain.Config().LatestMaxBlobsPerBlock()) + store, err := billy.Open(billy.Options{Path: queuedir, Repair: true}, slotter, index) if err != nil { return err } @@ -420,7 +421,7 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.Addres // Pool initialized, attach the blob limbo to it to track blobs included // recently but not yet finalized - p.limbo, err = newLimbo(limbodir) + p.limbo, err = newLimbo(limbodir, p.chain.Config().LatestMaxBlobsPerBlock()) if err != nil { p.Close() return err @@ -1598,7 +1599,7 @@ func (p *BlobPool) updateStorageMetrics() { metrics.GetOrRegisterGauge(fmt.Sprintf(shelfSlotusedGaugeName, shelf.SlotSize/blobSize), nil).Update(int64(shelf.FilledSlots)) metrics.GetOrRegisterGauge(fmt.Sprintf(shelfSlotgapsGaugeName, shelf.SlotSize/blobSize), nil).Update(int64(shelf.GappedSlots)) - if uint64(shelf.SlotSize/blobSize) > p.chain.Config().LatestMaxBlobsPerBlock(p.head.Time)*params.BlobTxBlobGasPerBlob { + if shelf.SlotSize/blobSize > uint32(p.chain.Config().LatestMaxBlobsPerBlock()*params.BlobTxBlobGasPerBlob) { oversizedDataused += slotDataused oversizedDatagaps += slotDatagaps oversizedSlotused += shelf.FilledSlots @@ -1751,7 +1752,7 @@ func (p *BlobPool) Clear() { // The transaction addition may attempt to reserve the sender addr which // can't happen until Clear releases the reservation lock. Clear cannot // acquire the subpool lock until the transaction addition is completed. - for acct, _ := range p.index { + for acct := range p.index { p.reserve(acct, false) } p.lookup = newLookup() diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index e4441bec5dad..d982a96ac298 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -51,6 +51,8 @@ var ( testBlobVHashes [][32]byte ) +const testMaxBlobsPerBlock = 6 + func init() { for i := 0; i < 10; i++ { testBlob := &kzg4844.Blob{byte(i)} @@ -415,7 +417,7 @@ func TestOpenDrops(t *testing.T) { defer os.RemoveAll(storage) os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) - store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil) + store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(testMaxBlobsPerBlock), nil) // Insert a malformed transaction to verify that decoding errors (or format // changes) are handled gracefully (case 1) @@ -738,7 +740,7 @@ func TestOpenIndex(t *testing.T) { defer os.RemoveAll(storage) os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) - store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil) + store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(testMaxBlobsPerBlock), nil) // Insert a sequence of transactions with varying price points to check that // the cumulative minimum will be maintained. @@ -827,7 +829,7 @@ func TestOpenHeap(t *testing.T) { defer os.RemoveAll(storage) os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) - store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil) + store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(testMaxBlobsPerBlock), nil) // Insert a few transactions from a few accounts. To remove randomness from // the heap initialization, use a deterministic account/tx/priority ordering. @@ -914,7 +916,7 @@ func TestOpenCap(t *testing.T) { defer os.RemoveAll(storage) os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) - store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil) + store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(testMaxBlobsPerBlock), nil) // Insert a few transactions from a few accounts var ( @@ -1369,7 +1371,7 @@ func TestAdd(t *testing.T) { defer os.RemoveAll(storage) // late defer, still ok os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) - store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil) + store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(testMaxBlobsPerBlock), nil) // Insert the seed transactions for the pool startup var ( diff --git a/core/txpool/blobpool/limbo.go b/core/txpool/blobpool/limbo.go index 32381a393613..f2af1fca1af9 100644 --- a/core/txpool/blobpool/limbo.go +++ b/core/txpool/blobpool/limbo.go @@ -48,7 +48,7 @@ type limbo struct { } // newLimbo opens and indexes a set of limboed blob transactions. -func newLimbo(datadir string) (*limbo, error) { +func newLimbo(datadir string, maxBlobsPerTransaction uint64) (*limbo, error) { l := &limbo{ index: make(map[common.Hash]uint64), groups: make(map[uint64]map[uint64]common.Hash), @@ -60,7 +60,7 @@ func newLimbo(datadir string) (*limbo, error) { fails = append(fails, id) } } - store, err := billy.Open(billy.Options{Path: datadir, Repair: true}, newSlotter(), index) + store, err := billy.Open(billy.Options{Path: datadir, Repair: true}, newSlotter(maxBlobsPerTransaction), index) if err != nil { return nil, err } diff --git a/core/txpool/blobpool/slotter.go b/core/txpool/blobpool/slotter.go index 2478e6edd88b..cc8aa387c708 100644 --- a/core/txpool/blobpool/slotter.go +++ b/core/txpool/blobpool/slotter.go @@ -25,13 +25,13 @@ 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() func() (uint32, bool) { +func newSlotter(maxBlobsPerTransaction uint64) func() (uint32, bool) { slotsize := uint32(txAvgSize) slotsize -= uint32(blobSize) // underflows, it's ok, will overflow back in the first return return func() (size uint32, done bool) { slotsize += blobSize - finished := slotsize > 9*blobSize+txMaxSize + finished := slotsize > uint32(maxBlobsPerTransaction)*blobSize+txMaxSize return slotsize, finished } diff --git a/core/txpool/blobpool/slotter_test.go b/core/txpool/blobpool/slotter_test.go index a7b43b4d2224..8d46f47d2ca6 100644 --- a/core/txpool/blobpool/slotter_test.go +++ b/core/txpool/blobpool/slotter_test.go @@ -21,7 +21,7 @@ import "testing" // Tests that the slotter creates the expected database shelves. func TestNewSlotter(t *testing.T) { // Generate the database shelve sizes - slotter := newSlotter() + slotter := newSlotter(6) var shelves []uint32 for { diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index e0a155f12b8d..89c5d0edfeb7 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -1310,6 +1310,7 @@ func setupBodies(t *testing.T) (*node.Node, *eth.Ethereum, []*types.Block) { genesis.Config.ShanghaiTime = &time genesis.Config.CancunTime = &time genesis.Config.PragueTime = &time + genesis.Config.BlobScheduleConfig = params.DefaultBlobSchedule n, ethservice := startEthService(t, genesis, blocks) @@ -1626,6 +1627,7 @@ func TestParentBeaconBlockRoot(t *testing.T) { time := blocks[len(blocks)-1].Time() + 5 genesis.Config.ShanghaiTime = &time genesis.Config.CancunTime = &time + genesis.Config.BlobScheduleConfig = params.DefaultBlobSchedule n, ethservice := startEthService(t, genesis, blocks) defer n.Close() @@ -1708,6 +1710,7 @@ func TestWitnessCreationAndConsumption(t *testing.T) { timestamp := blocks[len(blocks)-2].Time() + 5 genesis.Config.ShanghaiTime = ×tamp genesis.Config.CancunTime = ×tamp + genesis.Config.BlobScheduleConfig = params.DefaultBlobSchedule n, ethservice := startEthService(t, genesis, blocks[:9]) defer n.Close() diff --git a/eth/fetcher/tx_fetcher_test.go b/eth/fetcher/tx_fetcher_test.go index f80b1d6096d3..52b35910862d 100644 --- a/eth/fetcher/tx_fetcher_test.go +++ b/eth/fetcher/tx_fetcher_test.go @@ -1108,7 +1108,7 @@ func TestTransactionFetcherBandwidthLimiting(t *testing.T) { doTxNotify{peer: "C", hashes: []common.Hash{{0x07}, {0x08}}, types: []byte{types.BlobTxType, types.BlobTxType}, - sizes: []uint32{params.MaxBlobGasPerBlock, params.MaxBlobGasPerBlock}, + sizes: []uint32{params.BlobTxBlobGasPerBlob * 10, params.BlobTxBlobGasPerBlob * 10}, }, doWait{time: txArriveTimeout, step: true}, isWaiting(nil), @@ -1125,8 +1125,8 @@ func TestTransactionFetcherBandwidthLimiting(t *testing.T) { {common.Hash{0x06}, types.LegacyTxType, maxTxRetrievalSize}, }, "C": { - {common.Hash{0x07}, types.BlobTxType, params.MaxBlobGasPerBlock}, - {common.Hash{0x08}, types.BlobTxType, params.MaxBlobGasPerBlock}, + {common.Hash{0x07}, types.BlobTxType, params.BlobTxBlobGasPerBlob * 10}, + {common.Hash{0x08}, types.BlobTxType, params.BlobTxBlobGasPerBlob * 10}, }, }, fetching: map[string][]common.Hash{ diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index af7f38abca36..b2be7e201cb6 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -105,7 +105,7 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { // Compute gas used ratio for normal and blob gas. bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit) if blobGasUsed := bf.header.BlobGasUsed; blobGasUsed != nil { - bf.results.blobGasUsedRatio = float64(*blobGasUsed) / float64(config.LatestMaxBlobsPerBlock(bf.header.Time)) + bf.results.blobGasUsedRatio = float64(*blobGasUsed) / float64(config.MaxBlobsPerBlock(bf.header.Time)) } if len(percentiles) == 0 { diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index fdba2e584b42..f002a826256f 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -155,6 +155,12 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, cancunBlock *big.Int, pe ts := gspec.Timestamp + cancunBlock.Uint64()*10 // fixed 10 sec block time in blockgen config.ShanghaiTime = &ts config.CancunTime = &ts + config.BlobScheduleConfig = ¶ms.BlobScheduleConfig{ + Cancun: ¶ms.BlobConfig{ + Target: 3, + Max: 6, + }, + } signer = types.LatestSigner(gspec.Config) } diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 6f06b7c0d5be..6cd1dc857d84 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -360,6 +360,7 @@ func TestSupplySelfdestruct(t *testing.T) { cancunTime := uint64(0) gspec.Config.ShanghaiTime = &cancunTime gspec.Config.CancunTime = &cancunTime + gspec.Config.BlobScheduleConfig = params.DefaultBlobSchedule postCancunOutput, postCancunChain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc) if err != nil { diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json b/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json index 05da3b42e194..d1e1cd33fcc2 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json @@ -41,7 +41,13 @@ "grayGlacierBlock": 0, "shanghaiTime": 0, "cancunTime": 0, - "terminalTotalDifficulty": 0 + "terminalTotalDifficulty": 0, + "blobSchedule": { + "cancun": { + "target": 3, + "max": 6 + } + } } }, "context": { diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json index f8adbabf6377..06b1004f41f6 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json @@ -41,7 +41,13 @@ "grayGlacierBlock": 0, "shanghaiTime": 0, "cancunTime": 0, - "terminalTotalDifficulty": 0 + "terminalTotalDifficulty": 0, + "blobSchedule": { + "cancun": { + "target": 3, + "max": 6 + } + } } }, "context": { @@ -54,7 +60,9 @@ }, "input": "0x03f8b1820539806485174876e800825208940c2c51a0990aee1d73c1228de1586883415575088080c083020000f842a00100c9fbdf97f747e85847b4f3fff408f89c26842f77c882858bf2c89923849aa00138e3896f3c27f2389147507f8bcec52028b0efca6ee842ed83c9158873943880a0dbac3f97a532c9b00e6239b29036245a5bfbb96940b9d848634661abee98b945a03eec8525f261c2e79798f7b45a5d6ccaefa24576d53ba5023e919b86841c0675", "result": { - "0x0000000000000000000000000000000000000000": { "balance": "0x272e0528" }, + "0x0000000000000000000000000000000000000000": { + "balance": "0x272e0528" + }, "0x0c2c51a0990aee1d73c1228de158688341557508": { "balance": "0xde0b6b3a7640000" } diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json index b7d5ee1c54b9..af683a252390 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json @@ -53,7 +53,17 @@ "shanghaiTime": 0, "cancunTime": 0, "pragueTime": 0, - "terminalTotalDifficulty": 0 + "terminalTotalDifficulty": 0, + "blobSchedule": { + "cancun": { + "target": 3, + "max": 6 + }, + "prauge": { + "target": 3, + "max": 6 + } + } } }, "context": { diff --git a/eth/tracers/internal/tracetest/util.go b/eth/tracers/internal/tracetest/util.go index abc2699498d4..e938c0a9d281 100644 --- a/eth/tracers/internal/tracetest/util.go +++ b/eth/tracers/internal/tracetest/util.go @@ -53,7 +53,7 @@ func (c *callContext) toBlockContext(genesis *core.Genesis) vm.BlockContext { } if genesis.ExcessBlobGas != nil && genesis.BlobGasUsed != nil { - excessBlobGas := eip4844.CalcExcessBlobGas(*genesis.ExcessBlobGas, *genesis.BlobGasUsed) + excessBlobGas := eip4844.CalcExcessBlobGas(genesis.Config, genesis.ToBlock().Header()) context.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas) } return context diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 4ad8a552d268..360e0e9e0ab9 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -29,7 +29,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -65,7 +65,7 @@ var ( ) var genesis = &core.Genesis{ - Config: params.AllEthashProtocolChanges, + Config: params.AllDevChainProtocolChanges, Alloc: types.GenesisAlloc{ testAddr: {Balance: testBalance}, revertContractAddr: {Code: revertCode}, @@ -136,7 +136,7 @@ func generateTestChain() []*types.Block { g.AddTx(testTx2) } } - _, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 2, generate) + _, blocks, _ := core.GenerateChainWithGenesis(genesis, beacon.NewFaker(), 2, generate) return append([]*types.Block{genesis.ToBlock()}, blocks...) } @@ -223,7 +223,7 @@ func testHeader(t *testing.T, chain []*types.Block, client *rpc.Client) { if got != nil && got.Number != nil && got.Number.Sign() == 0 { got.Number = big.NewInt(0) // hack to make DeepEqual work } - if !reflect.DeepEqual(got, tt.want) { + if got.Hash() != tt.want.Hash() { t.Fatalf("HeaderByNumber(%v) got = %v, want %v", tt.block, got, tt.want) } }) @@ -314,7 +314,7 @@ func testChainID(t *testing.T, client *rpc.Client) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if id == nil || id.Cmp(params.AllEthashProtocolChanges.ChainID) != 0 { + if id == nil || id.Cmp(params.AllDevChainProtocolChanges.ChainID) != 0 { t.Fatalf("ChainID returned wrong number: %+v", id) } } diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index a86e3d506415..53233576c009 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -124,7 +124,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas if args.BlobHashes != nil && len(args.BlobHashes) == 0 { return errors.New(`need at least 1 blob for a blob transaction`) } - maxBlobsPerTransaction := int(b.ChainConfig().LatestMaxBlobsPerBlock(b.CurrentHeader().Time)) + maxBlobsPerTransaction := int(b.ChainConfig().MaxBlobsPerBlock(b.CurrentHeader().Time)) if args.BlobHashes != nil && len(args.BlobHashes) > maxBlobsPerTransaction { return fmt.Errorf(`too many blobs in transaction (have=%d, max=%d)`, len(args.BlobHashes), maxBlobsPerTransaction) } diff --git a/miner/worker.go b/miner/worker.go index e7c3e0f9e0b0..ffdcd7dede46 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -281,7 +281,7 @@ func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transactio // isn't really a better place right now. The blob gas limit is checked at block validation time // and not during execution. This means core.ApplyTransaction will not return an error if the // tx has too many blobs. So we have to explicitly check it here. - if (env.blobs+len(sc.Blobs))*params.BlobTxBlobGasPerBlob > int(miner.chainConfig.LatestMaxBlobsPerBlock(env.header.Time)*params.BlobTxBlobGasPerBlob) { + if (env.blobs+len(sc.Blobs))*params.BlobTxBlobGasPerBlob > int(miner.chainConfig.MaxBlobsPerBlock(env.header.Time)*params.BlobTxBlobGasPerBlob) { return errors.New("max data blobs reached") } receipt, err := miner.applyTransaction(env, tx) @@ -330,7 +330,7 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran } // If we don't have enough blob space for any further blob transactions, // skip that list altogether - if !blobTxs.Empty() && env.blobs*params.BlobTxBlobGasPerBlob >= int(miner.chainConfig.LatestMaxBlobsPerBlock(env.header.Time)*params.BlobTxBlobGasPerBlob) { + if !blobTxs.Empty() && env.blobs*params.BlobTxBlobGasPerBlob >= int(miner.chainConfig.MaxBlobsPerBlock(env.header.Time)*params.BlobTxBlobGasPerBlob) { log.Trace("Not enough blob space for further blob transactions") blobTxs.Clear() // Fall though to pick up any plain txs @@ -364,10 +364,16 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran txs.Pop() continue } - if left := uint64(int(miner.chainConfig.LatestMaxBlobsPerBlock(env.header.Time)*params.BlobTxBlobGasPerBlob) - env.blobs*params.BlobTxBlobGasPerBlob); left < ltx.BlobGas { - log.Trace("Not enough blob gas left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas) - txs.Pop() - continue + + // Most of the blob gas logic works regardless if the chain supports blobs + // or not, however the max check panics when called on a chain without a + // defined schedule, so we need to verify it's safe to call. + if miner.chainConfig.IsCancun(env.header.Number, env.header.Time) { + if left := uint64(int(miner.chainConfig.MaxBlobsPerBlock(env.header.Time)*params.BlobTxBlobGasPerBlob) - env.blobs*params.BlobTxBlobGasPerBlob); left < ltx.BlobGas { + log.Trace("Not enough blob gas left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas) + txs.Pop() + continue + } } // Transaction seems to fit, pull it up from the pool tx := ltx.Resolve() diff --git a/params/config.go b/params/config.go index 01fe8e201145..aee706764ba9 100644 --- a/params/config.go +++ b/params/config.go @@ -60,6 +60,12 @@ var ( CancunTime: newUint64(1710338135), DepositContractAddress: common.HexToAddress("0x00000000219ab540356cbb839cbe05303d7705fa"), Ethash: new(EthashConfig), + BlobScheduleConfig: &BlobScheduleConfig{ + Cancun: &BlobConfig{ + Target: 3, + Max: 6, + }, + }, } // HoleskyChainConfig contains the chain parameters to run a node on the Holesky test network. HoleskyChainConfig = &ChainConfig{ @@ -157,6 +163,16 @@ var ( CancunTime: newUint64(0), TerminalTotalDifficulty: big.NewInt(0), PragueTime: newUint64(0), + BlobScheduleConfig: &BlobScheduleConfig{ + Cancun: &BlobConfig{ + Target: 3, + Max: 6, + }, + Prague: &BlobConfig{ + Target: 6, + Max: 9, + }, + }, } // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced @@ -244,6 +260,16 @@ var ( TerminalTotalDifficulty: big.NewInt(0), Ethash: new(EthashConfig), Clique: nil, + BlobScheduleConfig: &BlobScheduleConfig{ + Cancun: &BlobConfig{ + Target: 3, + Max: 6, + }, + Prague: &BlobConfig{ + Target: 6, + Max: 9, + }, + }, } // NonActivatedConfig defines the chain configuration without activating @@ -277,6 +303,19 @@ var ( TestRules = TestChainConfig.Rules(new(big.Int), false, 0) ) +var ( + DefaultBlobSchedule = &BlobScheduleConfig{ + Cancun: &BlobConfig{ + Target: 3, + Max: 6, + }, + Prague: &BlobConfig{ + Target: 6, + Max: 9, + }, + } +) + // NetworkNames are user friendly names to use in the chain spec banner. var NetworkNames = map[string]string{ MainnetChainConfig.ChainID.String(): "mainnet", @@ -439,9 +478,9 @@ type BlobScheduleConfig struct { Prague *BlobConfig `json:"prague,omitempty"` } -// LatestTargetBlobsPerBlock returns the target blobs per block associated with +// TargetBlobsPerBlock returns the target blobs per block associated with // requested time. -func (c *ChainConfig) LatestTargetBlobsPerBlock(time uint64) uint64 { +func (c *ChainConfig) TargetBlobsPerBlock(time uint64) uint64 { if c.BlobScheduleConfig == nil { panic("blob schedule not defined") } @@ -459,9 +498,9 @@ func (c *ChainConfig) LatestTargetBlobsPerBlock(time uint64) uint64 { } } -// LatestMaxBlobsPerBlock returns the max blobs per block associated with +// MaxBlobsPerBlock returns the max blobs per block associated with // requested time. -func (c *ChainConfig) LatestMaxBlobsPerBlock(time uint64) uint64 { +func (c *ChainConfig) MaxBlobsPerBlock(time uint64) uint64 { if c.BlobScheduleConfig == nil { panic("blob schedule not defined") } @@ -479,6 +518,23 @@ func (c *ChainConfig) LatestMaxBlobsPerBlock(time uint64) uint64 { } } +// LatestMaxBlobsPerBlock returns the latest max blobs per block defined by the +// configuration, regardless of the currently active fork. +func (c *ChainConfig) LatestMaxBlobsPerBlock() uint64 { + s := c.BlobScheduleConfig + if s == nil { + return 0 + } + switch { + case s.Prague != nil: + return s.Prague.Max + case s.Cancun != nil: + return s.Cancun.Max + default: + panic("blob schedule empty") + } +} + // IsHomestead returns whether num is either equal to the homestead block or greater. func (c *ChainConfig) IsHomestead(num *big.Int) bool { return isBlockForked(c.HomesteadBlock, num) diff --git a/tests/init.go b/tests/init.go index 4bb83f9300bc..04422ea0d3d6 100644 --- a/tests/init.go +++ b/tests/init.go @@ -334,6 +334,12 @@ var Forks = map[string]*params.ChainConfig{ TerminalTotalDifficulty: big.NewInt(0), ShanghaiTime: u64(0), CancunTime: u64(0), + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: ¶ms.BlobConfig{ + Target: 3, + Max: 6, + }, + }, }, "ShanghaiToCancunAtTime15k": { ChainID: big.NewInt(1), @@ -353,6 +359,12 @@ var Forks = map[string]*params.ChainConfig{ TerminalTotalDifficulty: big.NewInt(0), ShanghaiTime: u64(0), CancunTime: u64(15_000), + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: ¶ms.BlobConfig{ + Target: 3, + Max: 6, + }, + }, }, "Prague": { ChainID: big.NewInt(1), @@ -374,6 +386,16 @@ var Forks = map[string]*params.ChainConfig{ CancunTime: u64(0), PragueTime: u64(0), DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: ¶ms.BlobConfig{ + Target: 3, + Max: 6, + }, + Prague: ¶ms.BlobConfig{ + Target: 6, + Max: 9, + }, + }, }, "CancunToPragueAtTime15k": { ChainID: big.NewInt(1), @@ -395,6 +417,16 @@ var Forks = map[string]*params.ChainConfig{ CancunTime: u64(0), PragueTime: u64(15_000), DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: ¶ms.BlobConfig{ + Target: 3, + Max: 6, + }, + Prague: ¶ms.BlobConfig{ + Target: 6, + Max: 9, + }, + }, }, } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 6e66bbaa7218..7232857fa226 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -281,7 +281,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh // - the block body is verified against the header in block_validator.go:ValidateBody // Here, we just do this shortcut smaller fix, since state tests do not // utilize those codepaths - if len(msg.BlobHashes)*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock { + if config.IsCancun(new(big.Int), block.Time()) && uint64(len(msg.BlobHashes)) > config.MaxBlobsPerBlock(block.Time()) { return st, common.Hash{}, 0, errors.New("blob gas exceeds maximum") } }