Skip to content

Commit 352a8cc

Browse files
authored
Fix forkId compatibility according to EIP-2124 and EIP-6122 (#3803)
* Fix forkId compatibility according to EIP-2124 and EIP-6122 * Rework ForkIdCalculator init Make the forkHistory calculation happen at init instead of lazily at first compatible call. Also use the ForIdCalculator.init style
1 parent 7d5b622 commit 352a8cc

File tree

4 files changed

+333
-47
lines changed

4 files changed

+333
-47
lines changed

execution_chain/common/common.nim

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ type
105105
func setForkId(com: CommonRef, genesis: Header) =
106106
com.genesisHash = genesis.computeBlockHash
107107
let genesisCRC = crc32(0, com.genesisHash.data)
108-
com.forkIdCalculator = initForkIdCalculator(
108+
com.forkIdCalculator = ForkIdCalculator.init(
109109
com.forkTransitionTable,
110110
genesisCRC,
111111
genesis.timestamp.uint64)
@@ -351,19 +351,21 @@ func isLondonOrLater*(com: CommonRef, number: BlockNumber): bool =
351351
com.toHardFork(number.forkDeterminationInfo) >= London
352352

353353
func forkId*(com: CommonRef, head, time: uint64): ForkID {.gcsafe.} =
354-
## EIP 2364/2124
354+
## Get ForkId for given block number / timestamp (EIP 2364/2124)
355355
com.forkIdCalculator.newID(head, time)
356356

357357
func forkId*(com: CommonRef, forkActivationTime: EthTime): ForkID {.gcsafe.} =
358-
# Only works for timestamp based forks
358+
## Get ForkId for given timestamp (EIP 2364/2124)
359+
## Only works for timestamp based forks
359360
com.forkIdCalculator.newID(0'u64, forkActivationTime.uint64)
360361

361362
func forkId*(com: CommonRef, head: BlockNumber, time: EthTime): ForkID {.gcsafe.} =
362-
## EIP 2364/2124
363+
## Get ForkId for given block number / timestamp (EIP-2124 + EIP-6122)
363364
com.forkIdCalculator.newID(head, time.uint64)
364365

365-
func compatibleForkId*(com: CommonRef, id: ForkID): bool =
366-
com.forkIdCalculator.compatible(id)
366+
func compatibleForkId*(com: CommonRef, forkId: ForkID, blockNumber: BlockNumber, time: EthTime): bool =
367+
## Check if a fork ID is compatible at a specific head position
368+
com.forkIdCalculator.compatible(forkId, blockNumber, time.uint64)
367369

368370
func isEIP155*(com: CommonRef, number: BlockNumber): bool =
369371
com.config.eip155Block.isSome and number >= com.config.eip155Block.value

execution_chain/common/hardforks.nim

Lines changed: 89 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
# This file may not be copied, modified, or distributed except according to
88
# those terms.
99

10+
{.push raises: [].}
11+
1012
import
1113
std/[strutils],
1214
eth/common/[headers],
@@ -15,8 +17,6 @@ import
1517
../utils/utils,
1618
./evmforks
1719

18-
{.push raises: [].}
19-
2020
type
2121
HardFork* = enum
2222
Frontier
@@ -348,10 +348,10 @@ type
348348
byBlock: seq[uint64]
349349
byTime: seq[uint64]
350350
genesisCRC: uint32
351-
cache: seq[ForkID]
351+
forkHistory: seq[ForkID]
352352

353353
func newID*(calc: ForkIdCalculator, head, time: uint64): ForkID =
354-
# Create a fork ID for a specific block height and timestamp:
354+
## Create a fork ID for a specific block height and timestamp
355355
var crc = calc.genesisCRC
356356
for fork in calc.byBlock:
357357
if fork <= head:
@@ -369,39 +369,76 @@ func newID*(calc: ForkIdCalculator, head, time: uint64): ForkID =
369369

370370
(crc, 0'u64)
371371

372-
func compatible*(calc: var ForkIdCalculator, forkId: ForkID): bool =
373-
if calc.cache.len == 0:
374-
calc.cache = newSeqOfCap[ForkID](calc.byBlock.len + calc.byTime.len + 1)
375-
var crc = calc.genesisCRC
376-
377-
# Build cache of all valid ForkIds
378-
# Each entry is (crc before fork, fork number)
379-
for fork in calc.byBlock:
380-
calc.cache.add( (crc, fork) )
381-
crc = crc32(crc, fork.toBytesBE)
382-
383-
for fork in calc.byTime:
384-
calc.cache.add( (crc, fork) )
385-
crc = crc32(crc, fork.toBytesBE)
386-
387-
# Add last fork ID (after all forks, next=0)
388-
calc.cache.add( (crc, 0'u64) )
372+
func compatible*(calc: ForkIdCalculator, forkId: ForkID, number: uint64, time: uint64): bool =
373+
## Check forkId compatibility at a specific head position according to EIP-2124 / EIP-6122.
374+
## The number and time represent the local chain state at which compatibility needs to be checked
375+
## In regular use this should be the current header block number and timestamp, but for testing
376+
## arbitrary points in history can be used.
377+
378+
# Calculate our current local fork ID at the given head position (= block and/or time)
379+
let localId = calc.newID(number, time)
380+
381+
# Calculate position of local fork ID in history
382+
var localForkPos = -1
383+
for i, historicalId in calc.forkHistory:
384+
if localId.crc == historicalId.crc:
385+
localForkPos = i
386+
break
387+
388+
# Based on position of local fork ID, determine if the head is block or time based
389+
let head =
390+
if localForkPos >= calc.byBlock.len():
391+
time
392+
else:
393+
number
394+
395+
# 1: local and remote FORK_HASH matches
396+
if forkId.crc == localId.crc:
397+
if forkId.nextFork != 0: # announced fork
398+
if head >= forkId.nextFork:
399+
# 1a - next fork already passed locally
400+
return false
401+
else:
402+
# 1b - next fork not yet passed locally
403+
return true
389404

390-
for id in calc.cache:
391-
if id == forkId:
405+
else:
406+
# 1b - no next fork announced
392407
return true
393408

409+
# 2 + 3: FORK_HASH is subset or superset of local past forks
410+
for i, historicalId in calc.forkHistory:
411+
if forkId.crc == historicalId.crc:
412+
# Need to know if remote (=forkId) is ahead or behind localId
413+
if i > localForkPos:
414+
# 3: Remote is at a future fork we also know about
415+
# (local is syncing)
416+
return true
417+
# TODO: Should we still check its next fork?
418+
if forkId.nextFork == historicalId.nextFork:
419+
# 2: Remote is at a past fork we also know about and its nextFork matches the one for that past fork
420+
# (remote is syncing)
421+
return true
422+
else:
423+
# 4: Remote is at a past fork we also know about but its nextFork doesn't match
424+
return false
425+
426+
# 4: incompatible
394427
false
395428

396-
func initForkIdCalculator*(map: ForkTransitionTable,
397-
genesisCRC: uint32,
398-
genesisTime: uint64): ForkIdCalculator =
399-
# Build ForkIdCalculator from the chain's fork configuration
429+
func init*(
430+
_: type ForkIdCalculator,
431+
map: ForkTransitionTable,
432+
genesisCRC: uint32,
433+
genesisTime: uint64,
434+
): ForkIdCalculator =
435+
## Build ForkIdCalculator from the chain's fork configuration
400436

401437
# Extract the fork rule block number aggregate it
402438
var forksByBlock: seq[uint64]
403439
for fork, val in map.blockNumberThresholds:
404-
if val.isNone: continue
440+
if val.isNone:
441+
continue
405442
let val64 = val.get
406443
if forksByBlock.len == 0:
407444
forksByBlock.add val64
@@ -424,7 +461,8 @@ func initForkIdCalculator*(map: ForkTransitionTable,
424461
# Extract the fork rule timestamp number aggregate it
425462
var forksByTime: seq[uint64]
426463
for fork, val in map.timeThresholds:
427-
if val.isNone: continue
464+
if val.isNone:
465+
continue
428466
let val64 = val.get.uint64
429467
if forksByTime.len == 0:
430468
forksByTime.add val64
@@ -435,9 +473,28 @@ func initForkIdCalculator*(map: ForkTransitionTable,
435473
while forksByTime.len > 0 and forksByTime[0] <= genesisTime:
436474
forksByTime.delete(0)
437475

438-
result.genesisCRC = genesisCRC
439-
result.byBlock = system.move(forksByBlock)
440-
result.byTime = system.move(forksByTime)
476+
# Calculate the fork ids for the full fork history
477+
var forkHistory = newSeqOfCap[ForkID](forksByBlock.len + forksByTime.len + 1)
478+
var crc = genesisCRC
479+
480+
# Each entry is (crc before fork, fork number)
481+
for fork in forksByBlock:
482+
forkHistory.add((crc, fork))
483+
crc = crc32(crc, fork.toBytesBE)
484+
485+
for fork in forksByTime:
486+
forkHistory.add((crc, fork))
487+
crc = crc32(crc, fork.toBytesBE)
488+
489+
# Add last fork ID (after all forks, next=0)
490+
forkHistory.add((crc, 0'u64))
491+
492+
ForkIdCalculator(
493+
genesisCRC: genesisCRC,
494+
byBlock: system.move(forksByBlock),
495+
byTime: system.move(forksByTime),
496+
forkHistory: system.move(forkHistory),
497+
)
441498

442499
# ------------------------------------------------------------------------------
443500
# End

execution_chain/nimbus_execution_client.nim

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ proc setupP2P(nimbus: NimbusNode, config: ExecutionClientConf, com: CommonRef) =
109109
com.forkId(header.number, header.timestamp)
110110

111111
func compatibleForkIdProc(id: ForkID): bool =
112-
com.compatibleForkId(id)
112+
let header = fc.latestHeader()
113+
com.compatibleForkId(id, header.number, header.timestamp)
113114

114115
let forkIdProcs = ForkIdProcs(
115116
forkId: forkIdProc,

0 commit comments

Comments
 (0)