Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

devnet-5: Add EIP-7840: Add blob schedule to EL config files #3005

Merged
merged 3 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading