diff --git a/nimbus/common/chain_config.nim b/nimbus/common/chain_config.nim index 5e97bb594..bb0600379 100644 --- a/nimbus/common/chain_config.nim +++ b/nimbus/common/chain_config.nim @@ -262,10 +262,6 @@ proc toHardFork*(map: ForkTransitionTable, forkDeterminer: ForkDeterminationInfo # should always have a match doAssert(false, "unreachable code") -func forkDeterminationInfoForHeader*(header: BlockHeader): ForkDeterminationInfo = - # FIXME-Adam-mightAlsoNeedTTD? - forkDeterminationInfo(header.blockNumber, header.timestamp) - proc validateChainConfig*(conf: ChainConfig): bool = result = true @@ -448,6 +444,7 @@ proc chainConfigForNetwork*(id: NetworkId): ChainConfig = muirGlacierBlock: some(0.toBlockNumber), berlinBlock: some(0.toBlockNumber), londonBlock: some(0.toBlockNumber), + mergeForkBlock: some(1735371.toBlockNumber), terminalTotalDifficulty: some(sepoliaTTD), shanghaiTime: some(1_677_557_088.EthTime) ) diff --git a/nimbus/common/common.nim b/nimbus/common/common.nim index f6654e284..128be42c5 100644 --- a/nimbus/common/common.nim +++ b/nimbus/common/common.nim @@ -58,7 +58,7 @@ type forkTransitionTable: ForkTransitionTable # Eth wire protocol need this - forkIds: array[HardFork, ForkID] + forkIdCalculator: ForkIdCalculator networkId: NetworkId # synchronizer need this @@ -117,10 +117,13 @@ proc consensusTransition(com: CommonRef, fork: HardFork) = # this could happen during reorg com.consensusType = com.config.consensusType -proc setForkId(com: CommonRef, blockZero: BlockHeader) = - com.genesisHash = blockZero.blockHash +proc setForkId(com: CommonRef, genesis: BlockHeader) = + com.genesisHash = genesis.blockHash let genesisCRC = crc32(0, com.genesisHash.data) - com.forkIds = calculateForkIds(com.config, genesisCRC) + com.forkIdCalculator = initForkIdCalculator( + com.forkTransitionTable, + genesisCRC, + genesis.timestamp.uint64) proc daoCheck(conf: ChainConfig) = if not conf.daoForkSupport or conf.daoForkBlock.isNone: @@ -160,7 +163,7 @@ proc init(com : CommonRef, # already at the MergeFork const TimeZero = EthTime(0) - # com.forkIds and com.blockZeroHash is set + # com.forkIdCalculator and com.genesisHash are set # by setForkId if genesis.isNil.not: com.hardForkTransition(ForkDeterminationInfo( @@ -246,7 +249,7 @@ proc clone*(com: CommonRef, db: CoreDbRef): CommonRef = pruneTrie : com.pruneTrie, config : com.config, forkTransitionTable: com.forkTransitionTable, - forkIds : com.forkIds, + forkIdCalculator: com.forkIdCalculator, genesisHash : com.genesisHash, genesisHeader: com.genesisHeader, syncProgress : com.syncProgress, @@ -315,7 +318,7 @@ func toEVMFork*(com: CommonRef): EVMFork = func isLondon*(com: CommonRef, number: BlockNumber): bool = # TODO: Fixme, use only London comparator - com.toHardFork(number.blockNumberToForkDeterminationInfo) >= London + com.toHardFork(number.forkDeterminationInfo) >= London func isLondon*(com: CommonRef, number: BlockNumber, timestamp: EthTime): bool = # TODO: Fixme, use only London comparator @@ -339,10 +342,13 @@ proc minerAddress*(com: CommonRef; header: BlockHeader): EthAddress account.value -func forkId*(com: CommonRef, forkDeterminer: ForkDeterminationInfo): ForkID {.gcsafe.} = +func forkId*(com: CommonRef, head, time: uint64): ForkID {.gcsafe.} = ## EIP 2364/2124 - let fork = com.toHardFork(forkDeterminer) - com.forkIds[fork] + com.forkIdCalculator.newID(head, time) + +func forkId*(com: CommonRef, head: BlockNumber, time: EthTime): ForkID {.gcsafe.} = + ## EIP 2364/2124 + com.forkIdCalculator.newID(head.truncate(uint64), time.uint64) func isEIP155*(com: CommonRef, number: BlockNumber): bool = com.config.eip155Block.isSome and number >= com.config.eip155Block.get diff --git a/nimbus/common/hardforks.nim b/nimbus/common/hardforks.nim index 552e167b7..c81e560ea 100644 --- a/nimbus/common/hardforks.nim +++ b/nimbus/common/hardforks.nim @@ -84,13 +84,13 @@ type # is because it's perfectly fine, if mergeForkBlock is # set, to not bother with TTD anymore. But I'm not sure # it makes sense to allow time to be optional. See the - # comment below on blockNumberToForkDeterminationInfo. + # comment below on forkDeterminationInfo. ForkDeterminationInfo* = object blockNumber*: BlockNumber time*: Option[EthTime] td*: Option[DifficultyInt] -func blockNumberToForkDeterminationInfo*(n: BlockNumber): ForkDeterminationInfo = +func forkDeterminationInfo*(n: BlockNumber): ForkDeterminationInfo = # FIXME: All callers of this function are suspect; I'm guess we should # always be using both block number and time. But we have a few places, # like various tests, where we only have block number and the tests are @@ -104,6 +104,10 @@ func forkDeterminationInfo*(n: BlockNumber, t: EthTime): ForkDeterminationInfo = func forkDeterminationInfoIncludingTd*(n: BlockNumber, t: EthTime, td: DifficultyInt): ForkDeterminationInfo = ForkDeterminationInfo(blockNumber: n, time: some(t), td: some(td)) +func forkDeterminationInfo*(header: BlockHeader): ForkDeterminationInfo = + # FIXME-Adam-mightAlsoNeedTTD? + forkDeterminationInfo(header.blockNumber, header.timestamp) + proc adjustForNextBlock*(n: BlockNumber): BlockNumber = n + 1 @@ -343,72 +347,74 @@ const # ------------------------------------------------------------------------------ # Fork ID helpers # ------------------------------------------------------------------------------ +type + ForkIdCalculator* = object + byBlock: seq[uint64] + byTime: seq[uint64] + genesisCRC: uint32 -func toNextFork(n: Option[BlockNumber]): uint64 = - if n.isSome: - n.get.truncate(uint64) - else: - 0'u64 +func newID*(calc: ForkIdCalculator, head, time: uint64): ForkID = + var hash = calc.genesisCRC + for fork in calc.byBlock: + if fork <= head: + # Fork already passed, checksum the previous hash and the fork number + hash = crc32(hash, fork.toBytesBE) + continue + return (hash, fork) -# EIP-6122: ForkID now works with timestamps too. -func toNextFork(t: Option[EthTime]): uint64 = - if t.isSome: - t.get.uint64 - else: - 0'u64 + for fork in calc.byTime: + if fork <= time: + # Fork already passed, checksum the previous hash and fork timestamp + hash = crc32(hash, fork.toBytesBE) + continue + return (hash, fork) -func arrayMappingHardForkToNextFork(c: ChainConfig): array[HardFork, uint64] = - return [ - 0'u64, - toNextFork(c.homesteadBlock), - toNextFork(c.daoForkBlock), - toNextFork(c.eip150Block), - toNextFork(c.eip158Block), - toNextFork(c.byzantiumBlock), - toNextFork(c.constantinopleBlock), - toNextFork(c.petersburgBlock), - toNextFork(c.istanbulBlock), - toNextFork(c.muirGlacierBlock), - toNextFork(c.berlinBlock), - toNextFork(c.londonBlock), - toNextFork(c.arrowGlacierBlock), - toNextFork(c.grayGlacierBlock), - toNextFork(c.mergeForkBlock), - toNextFork(c.shanghaiTime), - toNextFork(c.cancunTime), - ] + (hash, 0'u64) -func getNextFork(next: array[HardFork, uint64], fork: HardFork): uint64 = - if fork == high(HardFork): - result = 0 - return +func initForkIdCalculator*(map: ForkTransitionTable, + genesisCRC: uint32, + genesisTime: uint64): ForkIdCalculator = - result = next[fork] - for x in fork..high(HardFork): - if result != next[x]: - result = next[x] - break + # Extract the fork rule block number aggregate it + var forksByBlock: seq[uint64] + for fork, val in map.blockNumberThresholds: + if val.isNone: continue + let val64 = val.get.truncate(uint64) + if forksByBlock.len == 0: + forksByBlock.add val64 + elif forksByBlock[^1] != val64: + # Deduplicate fork identifiers applying multiple forks + forksByBlock.add val64 -func calculateForkId(next: array[HardFork, uint64], fork: HardFork, - prevCRC: uint32, prevFork: uint64): ForkID = - result.nextFork = getNextFork(next, fork) + if map.mergeForkTransitionThreshold.blockNumber.isSome: + let val64 = map.mergeForkTransitionThreshold.blockNumber.get.truncate(uint64) + if forksByBlock.len == 0: + forksByBlock.add val64 + elif forksByBlock[^1] != val64: + # Deduplicate fork identifiers applying multiple forks + forksByBlock.add val64 - if result.nextFork != prevFork: - result.crc = crc32(prevCRC, toBytesBE(prevFork)) - else: - result.crc = prevCRC + # Skip any forks in block 0, that's the genesis ruleset + if forksByBlock.len > 0 and forksByBlock[0] == 0: + forksByBlock.delete(0) -func calculateForkIds*(c: ChainConfig, - genesisCRC: uint32): array[HardFork, ForkID] = - let next = arrayMappingHardForkToNextFork(c) + # Extract the fork rule timestamp number aggregate it + var forksByTime: seq[uint64] + for fork, val in map.timeThresholds: + if val.isNone: continue + let val64 = val.get.uint64 + if forksByTime.len == 0: + forksByTime.add val64 + elif forksByTime[^1] != val64: + forksByTime.add val64 - var prevCRC = genesisCRC - var prevFork = getNextFork(next, Frontier) + # Skip any forks before genesis. + while forksByTime.len > 0 and forksByTime[0] <= genesisTime: + forksByTime.delete(0) - for fork in HardFork: - result[fork] = calculateForkId(next, fork, prevCRC, prevFork) - prevFork = result[fork].nextFork - prevCRC = result[fork].crc + result.genesisCRC = genesisCRC + result.byBlock = system.move(forksByBlock) + result.byTime = system.move(forksByTime) # ------------------------------------------------------------------------------ # End diff --git a/nimbus/core/executor/process_transaction.nim b/nimbus/core/executor/process_transaction.nim index 0edcf184b..65acd71dc 100644 --- a/nimbus/core/executor/process_transaction.nim +++ b/nimbus/core/executor/process_transaction.nim @@ -182,7 +182,7 @@ proc asyncProcessTransaction*( {.async, gcsafe.} = ## Variant of `asyncProcessTransaction()` with `*fork* derived ## from the `vmState` argument. - let fork = vmState.com.toEVMFork(header.forkDeterminationInfoForHeader) + let fork = vmState.com.toEVMFork(header.forkDeterminationInfo) return await vmState.asyncProcessTransaction(tx, sender, header, fork) proc processTransaction*( diff --git a/nimbus/core/pow/difficulty.nim b/nimbus/core/pow/difficulty.nim index bb902e66b..363f51fd7 100644 --- a/nimbus/core/pow/difficulty.nim +++ b/nimbus/core/pow/difficulty.nim @@ -171,7 +171,7 @@ template calcDifficultyGrayGlacier*(timeStamp: EthTime, parent: BlockHeader): Di makeDifficultyCalculator(11_400_000, timeStamp, parent) func calcDifficulty*(com: CommonRef, timeStamp: EthTime, parent: BlockHeader): DifficultyInt = - let next = com.toHardFork(parent.forkDeterminationInfoForHeader.adjustForNextBlock) + let next = com.toHardFork(parent.forkDeterminationInfo.adjustForNextBlock) if next >= GrayGlacier: result = calcDifficultyGrayGlacier(timeStamp, parent) elif next >= ArrowGlacier: diff --git a/nimbus/core/tx_pool/tx_chain/tx_basefee.nim b/nimbus/core/tx_pool/tx_chain/tx_basefee.nim index 61a8a7f03..029fbdc9f 100644 --- a/nimbus/core/tx_pool/tx_chain/tx_basefee.nim +++ b/nimbus/core/tx_pool/tx_chain/tx_basefee.nim @@ -35,7 +35,7 @@ proc baseFeeGet*(com: CommonRef; parent: BlockHeader): GasPrice = # Note that the baseFee is calculated for the next header let - forkDeterminer = forkDeterminationInfoForHeader(parent) + forkDeterminer = forkDeterminationInfo(parent) parentFork = com.toEVMFork(forkDeterminer) nextFork = com.toEVMFork(forkDeterminer.adjustForNextBlock) diff --git a/nimbus/sync/handlers/eth.nim b/nimbus/sync/handlers/eth.nim index e7471a767..795b24eba 100644 --- a/nimbus/sync/handlers/eth.nim +++ b/nimbus/sync/handlers/eth.nim @@ -392,7 +392,7 @@ method getStatus*(ctx: EthWireRef): EthState db = ctx.db com = ctx.chain.com bestBlock = db.getCanonicalHead() - forkId = com.forkId(bestBlock.forkDeterminationInfoForHeader) + forkId = com.forkId(bestBlock.blockNumber, bestBlock.timestamp) EthState( totalDifficulty: db.headTotalDifficulty, @@ -400,7 +400,7 @@ method getStatus*(ctx: EthWireRef): EthState bestBlockHash: bestBlock.blockHash, forkId: ChainForkId( forkHash: forkId.crc.toBytesBE, - forkNext: forkId.nextFork.toBlockNumber + forkNext: forkId.nextFork ) ) diff --git a/nimbus/sync/protocol/eth/eth_types.nim b/nimbus/sync/protocol/eth/eth_types.nim index 89e7aba02..98e0994cd 100644 --- a/nimbus/sync/protocol/eth/eth_types.nim +++ b/nimbus/sync/protocol/eth/eth_types.nim @@ -25,7 +25,7 @@ type ChainForkId* = object forkHash*: array[4, byte] # The RLP encoding must be exactly 4 bytes. - forkNext*: BlockNumber # The RLP encoding must be variable-length + forkNext*: uint64 # The RLP encoding must be variable-length EthWireBase* = ref object of RootRef diff --git a/tests/test_custom_network.nim b/tests/test_custom_network.nim index c296c7b0e..125b027d7 100644 --- a/tests/test_custom_network.nim +++ b/tests/test_custom_network.nim @@ -212,7 +212,7 @@ proc genesisLoadRunner(noisy = true; params = params) check mcom.ttd.get == sSpcs.termTotalDff - check mcom.toHardFork(sSpcs.mergeFork.toBlockNumber.blockNumberToForkDeterminationInfo) == MergeFork + check mcom.toHardFork(sSpcs.mergeFork.toBlockNumber.forkDeterminationInfo) == MergeFork test &"Construct persistent ChainDBRef on {tmpDir}, {persistPruneInfo}": # Before allocating the database, the data directory needs to be @@ -229,7 +229,7 @@ proc genesisLoadRunner(noisy = true; params = params) check dcom.ttd.get == sSpcs.termTotalDff - check dcom.toHardFork(sSpcs.mergeFork.toBlockNumber.blockNumberToForkDeterminationInfo) == MergeFork + check dcom.toHardFork(sSpcs.mergeFork.toBlockNumber.forkDeterminationInfo) == MergeFork test "Initialise in-memory Genesis": mcom.initializeEmptyDb diff --git a/tests/test_forkid.nim b/tests/test_forkid.nim index 4edd6ea14..01f7c9509 100644 --- a/tests/test_forkid.nim +++ b/tests/test_forkid.nim @@ -1,47 +1,60 @@ import unittest2, - ../nimbus/common/common + ../nimbus/common/common, + ../nimbus/utils/utils const MainNetIDs = [ - (blockNumber: 0'u64, id: (crc: 0xfc64ec04'u32, nextFork: 1150000'u64)), # Unsynced - (blockNumber: 1149999'u64, id: (crc: 0xfc64ec04'u32, nextFork: 1150000'u64)), # Last Frontier block - (blockNumber: 1150000'u64, id: (crc: 0x97c2c34c'u32, nextFork: 1920000'u64)), # First Homestead block - (blockNumber: 1919999'u64, id: (crc: 0x97c2c34c'u32, nextFork: 1920000'u64)), # Last Homestead block - (blockNumber: 1920000'u64, id: (crc: 0x91d1f948'u32, nextFork: 2463000'u64)), # First DAO block - (blockNumber: 2462999'u64, id: (crc: 0x91d1f948'u32, nextFork: 2463000'u64)), # Last DAO block - (blockNumber: 2463000'u64, id: (crc: 0x7a64da13'u32, nextFork: 2675000'u64)), # First Tangerine block - (blockNumber: 2674999'u64, id: (crc: 0x7a64da13'u32, nextFork: 2675000'u64)), # Last Tangerine block - (blockNumber: 2675000'u64, id: (crc: 0x3edd5b10'u32, nextFork: 4370000'u64)), # First Spurious block - (blockNumber: 4369999'u64, id: (crc: 0x3edd5b10'u32, nextFork: 4370000'u64)), # Last Spurious block - (blockNumber: 4370000'u64, id: (crc: 0xa00bc324'u32, nextFork: 7280000'u64)), # First Byzantium block - (blockNumber: 7279999'u64, id: (crc: 0xa00bc324'u32, nextFork: 7280000'u64)), # Last Byzantium block - (blockNumber: 7280000'u64, id: (crc: 0x668db0af'u32, nextFork: 9069000'u64)), # First and last Constantinople, first Petersburg block - (blockNumber: 7987396'u64, id: (crc: 0x668db0af'u32, nextFork: 9069000'u64)), # Past Petersburg block - (blockNumber: 9068999'u64, id: (crc: 0x668db0af'u32, nextFork: 9069000'u64)), # Last Petersburg block - (blockNumber: 9069000'u64, id: (crc: 0x879D6E30'u32, nextFork: 9200000'u64)), # First Istanbul block - (blockNumber: 9199999'u64, id: (crc: 0x879D6E30'u32, nextFork: 9200000'u64)), # Last Istanbul block - (blockNumber: 9200000'u64, id: (crc: 0xE029E991'u32, nextFork: 12244000'u64)), # First MuirGlacier block - (blockNumber: 12243999'u64, id: (crc: 0xE029E991'u32, nextFork: 12244000'u64)), # Last MuirGlacier block - (blockNumber: 12244000'u64, id: (crc: 0x0eb440f6'u32, nextFork: 12965000'u64)), # First Berlin block - (blockNumber: 12964999'u64, id: (crc: 0x0eb440f6'u32, nextFork: 12965000'u64)), # Last Berlin block - (blockNumber: 12965000'u64, id: (crc: 0xb715077d'u32, nextFork: 13773000'u64)), # First London block - (blockNumber: 13772999'u64, id: (crc: 0xb715077d'u32, nextFork: 13773000'u64)), # Last London block - (blockNumber: 13773000'u64, id: (crc: 0x20c327fc'u32, nextFork: 15050000'u64)), # First Arrow Glacier block - (blockNumber: 15049999'u64, id: (crc: 0x20c327fc'u32, nextFork: 15050000'u64)), # Last Arrow Glacier block - (blockNumber: 15050000'u64, id: (crc: 0xf0afd0e3'u32, nextFork: 0'u64)), # First Gray Glacier block - (blockNumber: 20000000'u64, id: (crc: 0xf0afd0e3'u32, nextFork: 0'u64)), # Future Gray Glacier block + (number: 0'u64 , time: 0'u64, id: (crc: 0xfc64ec04'u32, next: 1150000'u64)), # Unsynced + (number: 1149999'u64 , time: 0'u64, id: (crc: 0xfc64ec04'u32, next: 1150000'u64)), # Last Frontier block + (number: 1150000'u64 , time: 0'u64, id: (crc: 0x97c2c34c'u32, next: 1920000'u64)), # First Homestead block + (number: 1919999'u64 , time: 0'u64, id: (crc: 0x97c2c34c'u32, next: 1920000'u64)), # Last Homestead block + (number: 1920000'u64 , time: 0'u64, id: (crc: 0x91d1f948'u32, next: 2463000'u64)), # First DAO block + (number: 2462999'u64 , time: 0'u64, id: (crc: 0x91d1f948'u32, next: 2463000'u64)), # Last DAO block + (number: 2463000'u64 , time: 0'u64, id: (crc: 0x7a64da13'u32, next: 2675000'u64)), # First Tangerine block + (number: 2674999'u64 , time: 0'u64, id: (crc: 0x7a64da13'u32, next: 2675000'u64)), # Last Tangerine block + (number: 2675000'u64 , time: 0'u64, id: (crc: 0x3edd5b10'u32, next: 4370000'u64)), # First Spurious block + (number: 4369999'u64 , time: 0'u64, id: (crc: 0x3edd5b10'u32, next: 4370000'u64)), # Last Spurious block + (number: 4370000'u64 , time: 0'u64, id: (crc: 0xa00bc324'u32, next: 7280000'u64)), # First Byzantium block + (number: 7279999'u64 , time: 0'u64, id: (crc: 0xa00bc324'u32, next: 7280000'u64)), # Last Byzantium block + (number: 7280000'u64 , time: 0'u64, id: (crc: 0x668db0af'u32, next: 9069000'u64)), # First and last Constantinople, first Petersburg block + (number: 7987396'u64 , time: 0'u64, id: (crc: 0x668db0af'u32, next: 9069000'u64)), # Past Petersburg block + (number: 9068999'u64 , time: 0'u64, id: (crc: 0x668db0af'u32, next: 9069000'u64)), # Last Petersburg block + (number: 9069000'u64 , time: 0'u64, id: (crc: 0x879D6E30'u32, next: 9200000'u64)), # First Istanbul block + (number: 9199999'u64 , time: 0'u64, id: (crc: 0x879D6E30'u32, next: 9200000'u64)), # Last Istanbul block + (number: 9200000'u64 , time: 0'u64, id: (crc: 0xE029E991'u32, next: 12244000'u64)), # First MuirGlacier block + (number: 12243999'u64, time: 0'u64, id: (crc: 0xE029E991'u32, next: 12244000'u64)), # Last MuirGlacier block + (number: 12244000'u64, time: 0'u64, id: (crc: 0x0eb440f6'u32, next: 12965000'u64)), # First Berlin block + (number: 12964999'u64, time: 0'u64, id: (crc: 0x0eb440f6'u32, next: 12965000'u64)), # Last Berlin block + (number: 12965000'u64, time: 0'u64, id: (crc: 0xb715077d'u32, next: 13773000'u64)), # First London block + (number: 13772999'u64, time: 0'u64, id: (crc: 0xb715077d'u32, next: 13773000'u64)), # Last London block + (number: 13773000'u64, time: 0'u64, id: (crc: 0x20c327fc'u32, next: 15050000'u64)), # First Arrow Glacier block + (number: 15049999'u64, time: 0'u64, id: (crc: 0x20c327fc'u32, next: 15050000'u64)), # Last Arrow Glacier block + (number: 15050000'u64, time: 0'u64, id: (crc: 0xf0afd0e3'u32, next: 1681338455'u64)), # First Gray Glacier block + (number: 20000000'u64, time: 1681338454'u64, id: (crc: 0xf0afd0e3'u32, next: 1681338455'u64)), # Last Gray Glacier block + (number: 20000000'u64, time: 1681338455'u64, id: (crc: 0xdce96c2d'u32, next: 0'u64)), # First Shanghai block + (number: 30000000'u64, time: 2000000000'u64, id: (crc: 0xdce96c2d'u32, next: 0'u64)), # Future Shanghai block ] GoerliNetIDs = [ - (blockNumber: 0'u64, id: (crc: 0xa3f5ab08'u32, nextFork: 1561651'u64)), # Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block - (blockNumber: 1561650'u64, id: (crc: 0xa3f5ab08'u32, nextFork: 1561651'u64)), # Last Petersburg block - (blockNumber: 1561651'u64, id: (crc: 0xc25efa5c'u32, nextFork: 4460644'u64)), # First Istanbul block - (blockNumber: 4460643'u64, id: (crc: 0xc25efa5c'u32, nextFork: 4460644'u64)), # Future Istanbul block - (blockNumber: 4460644'u64, id: (crc: 0x757a1c47'u32, nextFork: 5062605'u64)), # First Berlin block - (blockNumber: 5062604'u64, id: (crc: 0x757a1c47'u32, nextFork: 5062605'u64)), # Last Berlin block - (blockNumber: 5062605'u64, id: (crc: 0xb8c6299d'u32, nextFork: 0'u64)), # First London block - (blockNumber: 10000000'u64, id: (crc: 0xb8c6299d'u32, nextFork: 0'u64)), # Future London block + (number: 0'u64 , time: 0'u64, id: (crc: 0xa3f5ab08'u32, next: 1561651'u64)), # Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block + (number: 1561650'u64, time: 0'u64, id: (crc: 0xa3f5ab08'u32, next: 1561651'u64)), # Last Petersburg block + (number: 1561651'u64, time: 0'u64, id: (crc: 0xc25efa5c'u32, next: 4460644'u64)), # First Istanbul block + (number: 4460643'u64, time: 0'u64, id: (crc: 0xc25efa5c'u32, next: 4460644'u64)), # Future Istanbul block + (number: 4460644'u64, time: 0'u64, id: (crc: 0x757a1c47'u32, next: 5062605'u64)), # First Berlin block + (number: 5062604'u64, time: 0'u64, id: (crc: 0x757a1c47'u32, next: 5062605'u64)), # Last Berlin block + (number: 5062605'u64, time: 0'u64, id: (crc: 0xb8c6299d'u32, next: 1678832736'u64)), # First London block + (number: 6000000'u64, time: 1678832735'u64, id: (crc: 0xB8C6299D'u32, next: 1678832736'u64)), # Last London block + (number: 6000001'u64, time: 1678832736'u64, id: (crc: 0xf9843abf'u32, next: 0'u64)), # First Shanghai block + (number: 6500000'u64, time: 2678832736'u64, id: (crc: 0xf9843abf'u32, next: 0'u64)), # Future Shanghai block + ] + + SepoliaNetIDs = [ + (number: 0'u64, time: 0'u64, id: (crc: 0xfe3366e7'u32, next: 1735371'u64)), # Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin and first London block + (number: 1735370'u64, time: 0'u64, id: (crc: 0xfe3366e7'u32, next: 1735371'u64)), # Last London block + (number: 1735371'u64, time: 0'u64, id: (crc: 0xb96cbd13'u32, next: 1677557088'u64)), # First MergeNetsplit block + (number: 1735372'u64, time: 1677557087'u64, id: (crc: 0xb96cbd13'u32, next: 1677557088'u64)), # Last MergeNetsplit block + (number: 1735372'u64, time: 1677557088'u64, id: (crc: 0xf7f9bc08'u32, next: 0'u64)), # First Shanghai block ] template runTest(network: untyped, name: string) = @@ -50,15 +63,67 @@ template runTest(network: untyped, name: string) = params = networkParams(network) com = CommonRef.new(newCoreDbRef LegacyDbMemory, true, network, params) - for x in `network IDs`: - let id = com.forkId(x.blockNumber.toBlockNumber.blockNumberToForkDeterminationInfo) + for i, x in `network IDs`: + let id = com.forkId(x.number, x.time) check id.crc == x.id.crc - check id.nextFork == x.id.nextFork + check id.nextFork == x.id.next + +func config(shanghai, cancun: uint64): ChainConfig = + ChainConfig( + chainID: ChainId(1337), + homesteadBlock: some(0.u256), + dAOForkBlock: none(BlockNumber), + dAOForkSupport: true, + eIP150Block: some(0.u256), + eIP155Block: some(0.u256), + eIP158Block: some(0.u256), + byzantiumBlock: some(0.u256), + constantinopleBlock: some(0.u256), + petersburgBlock: some(0.u256), + istanbulBlock: some(0.u256), + muirGlacierBlock: some(0.u256), + berlinBlock: some(0.u256), + londonBlock: some(0.u256), + terminalTotalDifficulty: some(0.u256), + terminalTotalDifficultyPassed: some(true), + mergeForkBlock: some(0.u256), + shanghaiTime: some(shanghai.EthTime), + cancunTime: some(cancun.EthTime), + ) + +func calcID(conf: ChainConfig, crc: uint32, time: uint64): ForkID = + let map = conf.toForkTransitionTable + let calc = map.initForkIdCalculator(crc, time) + calc.newID(0, time) + +template runGenesisTimeIdTests() = + let + time = 1690475657'u64 + genesis = common.BlockHeader(timestamp: time.EthTime) + genesisCRC = crc32(0, genesis.blockHash.data) + cases = [ + # Shanghai active before genesis, skip + (c: config(time-1, time+1), want: (crc: genesisCRC, next: time + 1)), + + # Shanghai active at genesis, skip + (c: config(time, time+1), want: (crc: genesisCRC, next: time + 1)), + + # Shanghai not active, skip + (c: config(time+1, time+2), want: (crc: genesisCRC, next: time + 1)), + ] + + for i, x in cases: + let get = calcID(x.c, genesisCRC, time) + check get.crc == x.want.crc + check get.nextFork == x.want.next proc forkIdMain*() = suite "Fork ID tests": runTest(MainNet, "MainNet") runTest(GoerliNet, "GoerliNet") + runTest(SepoliaNet, "SepoliaNet") + test "Genesis Time Fork ID": + runGenesisTimeIdTests() when isMainModule: forkIdMain() diff --git a/tests/test_generalstate_json.nim b/tests/test_generalstate_json.nim index 0198b2726..ec04911a2 100644 --- a/tests/test_generalstate_json.nim +++ b/tests/test_generalstate_json.nim @@ -92,7 +92,7 @@ proc testFixtureIndexes(ctx: var TestCtx, testStatusIMPL: var TestStatus) = var gasUsed: GasInt let sender = ctx.tx.getSender() - let fork = com.toEVMFork(ctx.header.forkDeterminationInfoForHeader) + let fork = com.toEVMFork(ctx.header.forkDeterminationInfo) vmState.mutateStateDB: setupStateDB(ctx.pre, db) diff --git a/tools/evmstate/evmstate.nim b/tools/evmstate/evmstate.nim index f33778bf6..02a2203f4 100644 --- a/tools/evmstate/evmstate.nim +++ b/tools/evmstate/evmstate.nim @@ -107,7 +107,7 @@ proc writeRootHashToStderr(vmState: BaseVMState) = proc runExecution(ctx: var StateContext, conf: StateConf, pre: JsonNode): StateResult = let com = CommonRef.new(newCoreDbRef LegacyDbMemory, ctx.chainConfig, pruneTrie = false) - fork = com.toEVMFork(ctx.header.forkDeterminationInfoForHeader) + fork = com.toEVMFork(ctx.header.forkDeterminationInfo) stream = newFileStream(stderr) tracer = if conf.jsonEnabled: newJSonTracer(stream, ctx.tracerFlags, conf.pretty)