Skip to content

Commit

Permalink
devnet-5: Add EIP-7840: Add blob schedule to EL config files (#3005)
Browse files Browse the repository at this point in the history
* devnet-5: Add EIP-7840: Add blob schedule to EL config files

* Fix test_transaction_json

* Add missing blobSchedule to common/helpers
  • Loading branch information
jangko authored Jan 16, 2025
1 parent 19ea82b commit ac053bf
Show file tree
Hide file tree
Showing 18 changed files with 305 additions and 57 deletions.
66 changes: 62 additions & 4 deletions nimbus/common/chain_config.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2021-2024 Status Research & Development GmbH
# Copyright (c) 2021-2025 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
Expand Down Expand Up @@ -72,6 +72,7 @@ NetworkParams.useDefaultReaderIn JGenesis
GenesisAccount.useDefaultReaderIn JGenesis
Genesis.useDefaultReaderIn JGenesis
ChainConfig.useDefaultReaderIn JGenesis
BlobSchedule.useDefaultReaderIn JGenesis

# ------------------------------------------------------------------------------
# Private helper functions
Expand Down Expand Up @@ -274,6 +275,43 @@ proc readValue(reader: var JsonReader[JGenesis], value: var GenesisAlloc)
for key in reader.readObjectFields:
value[Address.fromHex(key)] = reader.readValue(GenesisAccount)

const
BlobScheduleTable: array[Cancun..HardFork.high, string] = [
"cancun",
"prague",
"osaka"
]

func ofStmt(fork: HardFork, keyName: string, reader: NimNode, value: NimNode): NimNode =
let branchStmt = quote do:
`value`[`fork`] = reader.readValue(Opt[BlobSchedule])

nnkOfBranch.newTree(
newLit(keyName),
branchStmt
)

macro blobScheduleParser(reader, key, value: typed): untyped =
# Automated blob schedule parser generator
var caseStmt = nnkCaseStmt.newTree(
quote do: `key`
)

for fork in Cancun..HardFork.high:
let keyName = BlobScheduleTable[fork]
caseStmt.add ofStmt(fork, keyName, reader, value)

caseStmt.add nnkElse.newTree(
quote do: discard
)
result = caseStmt

proc readValue(reader: var JsonReader[JGenesis], value: var array[Cancun..HardFork.high, Opt[BlobSchedule]])
{.gcsafe, raises: [SerializationError, IOError].} =
wrapError:
for key in reader.readObjectFields:
blobScheduleParser(reader, key, value)

macro fillArrayOfBlockNumberBasedForkOptionals(conf, tmp: typed): untyped =
result = newStmtList()
for i, x in forkBlockField:
Expand Down Expand Up @@ -304,7 +342,7 @@ func toHardFork*(map: ForkTransitionTable, forkDeterminer: ForkDeterminationInfo
# should always have a match
doAssert(false, "unreachable code")

proc validateChainConfig*(conf: ChainConfig): bool =
proc validateChainConfig(conf: ChainConfig): bool =
result = true

# FIXME: factor this to remove the duplication between the
Expand Down Expand Up @@ -353,6 +391,15 @@ proc validateChainConfig*(conf: ChainConfig): bool =
if cur.time.isSome:
lastTimeBasedFork = cur

proc configureBlobSchedule(conf: ChainConfig) =
var prevFork = Cancun
if conf.blobSchedule[Cancun].isNone:
conf.blobSchedule[Cancun] = Opt.some(BlobSchedule())
for fork in Prague..HardFork.high:
if conf.blobSchedule[fork].isNone:
conf.blobSchedule[fork] = conf.blobSchedule[prevFork]
prevFork = fork

proc parseGenesis*(data: string): Genesis
{.gcsafe.} =
try:
Expand Down Expand Up @@ -393,6 +440,7 @@ proc validateNetworkParams(params: var NetworkParams, input: string, inputIsFile
warn "Loaded custom network contains no 'config' data"
params.config = ChainConfig()

configureBlobSchedule(params.config)
validateChainConfig(params.config)

proc loadNetworkParams*(fileName: string, params: var NetworkParams):
Expand Down Expand Up @@ -435,6 +483,13 @@ proc parseGenesisAlloc*(data: string, ga: var GenesisAlloc): bool

return true

func defaultBlobSchedule*(): array[Cancun..HardFork.high, Opt[BlobSchedule]] =
[
Cancun: Opt.some(BlobSchedule(target: 3'u64, max: 6'u64)),
Prague: Opt.some(BlobSchedule(target: 6'u64, max: 9'u64)),
Osaka : Opt.some(BlobSchedule(target: 6'u64, max: 9'u64)),
]

func chainConfigForNetwork*(id: NetworkId): ChainConfig =
# For some public networks, NetworkId and ChainId value are identical
# but that is not always the case
Expand All @@ -446,8 +501,8 @@ func chainConfigForNetwork*(id: NetworkId): ChainConfig =
MAINNET_DEPOSIT_CONTRACT_ADDRESS = address"0x00000000219ab540356cbb839cbe05303d7705fa"
ChainConfig(
chainId: MainNet.ChainId,
# Genesis (Frontier): # 2015-07-30 15:26:13 UTC
# Frontier Thawing: 200_000.BlockNumber, # 2015-09-07 21:33:09 UTC
# Genesis (Frontier): # 2015-07-30 15:26:13 UTC
# Frontier Thawing: 200_000.BlockNumber, # 2015-09-07 21:33:09 UTC
homesteadBlock: Opt.some(1_150_000.BlockNumber), # 2016-03-14 18:49:53 UTC
daoForkBlock: Opt.some(1_920_000.BlockNumber), # 2016-07-20 13:20:40 UTC
daoForkSupport: true,
Expand All @@ -469,6 +524,7 @@ func chainConfigForNetwork*(id: NetworkId): ChainConfig =
shanghaiTime: Opt.some(1_681_338_455.EthTime), # 2023-04-12 10:27:35 UTC
cancunTime: Opt.some(1_710_338_135.EthTime), # 2024-03-13 13:55:35 UTC
depositContractAddress: Opt.some(MAINNET_DEPOSIT_CONTRACT_ADDRESS),
blobSchedule: defaultBlobSchedule(),
)
of SepoliaNet:
const sepoliaTTD = parse("17000000000000000",UInt256)
Expand All @@ -491,6 +547,7 @@ func chainConfigForNetwork*(id: NetworkId): ChainConfig =
terminalTotalDifficulty: Opt.some(sepoliaTTD),
shanghaiTime: Opt.some(1_677_557_088.EthTime),
cancunTime: Opt.some(1_706_655_072.EthTime), # 2024-01-30 22:51:12
blobSchedule: defaultBlobSchedule(),
)
of HoleskyNet:
#https://github.com/eth-clients/holesky
Expand All @@ -513,6 +570,7 @@ func chainConfigForNetwork*(id: NetworkId): ChainConfig =
shanghaiTime: Opt.some(1_696_000_704.EthTime),
cancunTime: Opt.some(1_707_305_664.EthTime), # 2024-02-07 11:34:24
depositContractAddress: Opt.some(HOLESKYNET_DEPOSIT_CONTRACT_ADDRESS),
blobSchedule: defaultBlobSchedule(),
)
else:
ChainConfig()
Expand Down
11 changes: 8 additions & 3 deletions nimbus/common/chain_config_hash.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2024 Status Research & Development GmbH
# Copyright (c) 2024-2025 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
Expand All @@ -25,8 +25,8 @@ import
# complicated steps before the genesis hash is ready. See `CommonRef.init`.
# If the genesis happen to exists in database belonging to other network,
# it will replace the one in CommonRef cache.
# That is the reason why using genesis header or genesis hash + ChainId is
# not a good solution to prevent loading existing data directory for
# That is the reason why using genesis header or genesis hash + ChainId is
# not a good solution to prevent loading existing data directory for
# the wrong network.
# But the ChainConfig + raw Genesis hash will make the job done before
# CommonRef creation.
Expand Down Expand Up @@ -66,6 +66,11 @@ func update[T: ref](ctx: var sha256, val: T) =
for f in fields(val[]):
ctx.update(f)

func update(ctx: var sha256, list: openArray[Opt[BlobSchedule]]) =
mixin update
for val in list:
ctx.update(val)

# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
Expand Down
8 changes: 8 additions & 0 deletions nimbus/common/common.nim
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,14 @@ func extraData*(com: CommonRef): string =
func gasLimit*(com: CommonRef): uint64 =
com.gasLimit

func maxBlobsPerBlock*(com: CommonRef, fork: HardFork): uint64 =
doAssert(fork >= Cancun)
com.config.blobSchedule[fork].expect("blobSchedule initialized").max

func targetBlobsPerBlock*(com: CommonRef, fork: HardFork): uint64 =
doAssert(fork >= Cancun)
com.config.blobSchedule[fork].expect("blobSchedule initialized").target

# ------------------------------------------------------------------------------
# Setters
# ------------------------------------------------------------------------------
Expand Down
7 changes: 6 additions & 1 deletion nimbus/common/hardforks.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2022-2024 Status Research & Development GmbH
# Copyright (c) 2022-2025 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
Expand Down Expand Up @@ -134,6 +134,10 @@ func isGTETransitionThreshold*(map: ForkTransitionTable, forkDeterminer: ForkDet
raise newException(Defect, "Why is this hard fork not in one of the above categories?")

type
BlobSchedule* = object
target*: uint64
max* : uint64

# if you add more fork block
# please update forkBlockField constant too
ChainConfig* = ref object
Expand Down Expand Up @@ -172,6 +176,7 @@ type

terminalTotalDifficulty*: Opt[UInt256]
depositContractAddress*: Opt[Address]
blobSchedule* : array[Cancun..HardFork.high, Opt[BlobSchedule]]

# These are used for checking that the values of the fields
# are in a valid order.
Expand Down
20 changes: 15 additions & 5 deletions nimbus/core/eip7691.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2024 Status Research & Development GmbH
# Copyright (c) 2024-2025 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
Expand All @@ -11,7 +11,10 @@
{.push raises: [].}

import
../constants
../constants,
../common/hardforks,
../common/evmforks,
../common/common

func getMaxBlobGasPerBlock*(electra: bool): uint64 =
if electra: MAX_BLOB_GAS_PER_BLOCK_ELECTRA.uint64
Expand All @@ -25,6 +28,13 @@ func getBlobBaseFeeUpdateFraction*(electra: bool): uint64 =
if electra: BLOB_BASE_FEE_UPDATE_FRACTION_ELECTRA.uint64
else: BLOB_BASE_FEE_UPDATE_FRACTION.uint64

func getMaxBlobsPerBlock*(electra: bool): uint64 =
if electra: MAX_BLOBS_PER_BLOCK_ELECTRA.uint64
else: MAX_BLOBS_PER_BLOCK.uint64
const
EVMForkToFork: array[FkCancun..EVMFork.high, HardFork] = [
Cancun,
Prague,
Osaka
]

func getMaxBlobsPerBlock*(com: CommonRef, fork: EVMFork): uint64 =
doAssert(fork >= FkCancun)
com.maxBlobsPerBlock(EVMForkToFork[fork])
5 changes: 3 additions & 2 deletions nimbus/core/executor/process_transaction.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Copyright (c) 2018-2025 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
Expand Down Expand Up @@ -103,7 +103,8 @@ proc processTransactionImpl(
# of the `processTransaction()` function. So there is no `return err()`
# statement, here.
let
txRes = roDB.validateTransaction(tx, sender, header.gasLimit, baseFee256, excessBlobGas, fork)
com = vmState.com
txRes = roDB.validateTransaction(tx, sender, header.gasLimit, baseFee256, excessBlobGas, com, fork)
res = if txRes.isOk:
# EIP-1153
vmState.ledger.clearTransientStorage()
Expand Down
1 change: 1 addition & 0 deletions nimbus/core/tx_pool/tx_desc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ proc addTx*(xp: TxPoolRef, ptx: PooledTransaction): Result[void, TxError] =
return err(txErrorAlreadyKnown)

validateTxBasic(
xp.com,
ptx.tx,
xp.nextFork,
validateFork = true).isOkOr:
Expand Down
11 changes: 6 additions & 5 deletions nimbus/core/tx_pool/tx_packer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ proc classifyValidatePacked(vmState: BaseVMState; item: TxItemRef): bool =
baseFee = vmState.blockCtx.baseFeePerGas.get(0.u256)
fork = vmState.fork
gasLimit = vmState.blockCtx.gasLimit
excessBlobGas = calcExcessBlobGas(vmState.parent, vmState.fork >= FkPrague)
excessBlobGas = calcExcessBlobGas(vmState.parent, fork >= FkPrague)

roDB.validateTransaction(
item.tx, item.sender, gasLimit, baseFee, excessBlobGas, fork).isOk
item.tx, item.sender, gasLimit, baseFee, excessBlobGas, vmState.com, fork).isOk

proc classifyPacked(vmState: BaseVMState; moreBurned: GasInt): bool =
## Classifier for *packing* (i.e. adding up `gasUsed` values after executing
Expand Down Expand Up @@ -169,9 +169,10 @@ proc vmExecGrabItem(pst: var TxPacker; item: TxItemRef, xp: TxPoolRef): bool =
electra = vmState.fork >= FkPrague

# EIP-4844
let maxBlobsPerBlock = getMaxBlobsPerBlock(electra)
if (pst.numBlobPerBlock + item.tx.versionedHashes.len).uint64 > maxBlobsPerBlock:
return ContinueWithNextAccount
if item.tx.txType == TxEip4844:
let maxBlobsPerBlock = getMaxBlobsPerBlock(vmState.com, vmState.fork)
if (pst.numBlobPerBlock + item.tx.versionedHashes.len).uint64 > maxBlobsPerBlock:
return ContinueWithNextAccount

let
blobGasUsed = item.tx.getTotalBlobGas
Expand Down
10 changes: 6 additions & 4 deletions nimbus/core/validate.nim
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ func gasCost*(tx: Transaction): UInt256 =
tx.gasLimit.u256 * tx.gasPrice.u256

proc validateTxBasic*(
com: CommonRef,
tx: Transaction; ## tx to validate
fork: EVMFork,
validateFork: bool = true): Result[void, string] =
Expand Down Expand Up @@ -260,9 +261,9 @@ proc validateTxBasic*(
if tx.versionedHashes.len == 0:
return err("invalid tx: there must be at least one blob")

let maxBlobsPerBlob = getMaxBlobsPerBlock(fork >= FkPrague)
if tx.versionedHashes.len.uint64 > maxBlobsPerBlob:
return err(&"invalid tx: versioned hashes len exceeds MAX_BLOBS_PER_BLOCK={maxBlobsPerBlob}. get={tx.versionedHashes.len}")
let maxBlobsPerBlock = getMaxBlobsPerBlock(com, fork)
if tx.versionedHashes.len.uint64 > maxBlobsPerBlock:
return err(&"invalid tx: versioned hashes len exceeds MAX_BLOBS_PER_BLOCK={maxBlobsPerBlock}. get={tx.versionedHashes.len}")

for i, bv in tx.versionedHashes:
if bv.data[0] != VERSIONED_HASH_VERSION_KZG:
Expand All @@ -282,9 +283,10 @@ proc validateTransaction*(
maxLimit: GasInt; ## gasLimit from block header
baseFee: UInt256; ## baseFee from block header
excessBlobGas: uint64; ## excessBlobGas from parent block header
com: CommonRef,
fork: EVMFork): Result[void, string] =

? validateTxBasic(tx, fork)
? validateTxBasic(com, tx, fork)

let
balance = roDB.getBalance(sender)
Expand Down
27 changes: 27 additions & 0 deletions tests/customgenesis/blobschedule_cancun_osaka.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"config": {
"chainId": 7,
"homesteadBlock": 0,
"eip150Block": 0,
"eip150Hash": "0x5de1ee4135274003348e80b788e5afa4b18b18d320a5622218d5c493fedf5689",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"blobSchedule": {
"cancun": {
"target": 3,
"max": 6
},
"osaka": {
"target": 6,
"max": 9
}
}
}
}
27 changes: 27 additions & 0 deletions tests/customgenesis/blobschedule_cancun_prague.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"config": {
"chainId": 7,
"homesteadBlock": 0,
"eip150Block": 0,
"eip150Hash": "0x5de1ee4135274003348e80b788e5afa4b18b18d320a5622218d5c493fedf5689",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"blobSchedule": {
"cancun": {
"target": 3,
"max": 6
},
"prague": {
"target": 6,
"max": 9
}
}
}
}
Loading

0 comments on commit ac053bf

Please sign in to comment.