Skip to content
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
14 changes: 8 additions & 6 deletions execution_chain/common/common.nim
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ type
func setForkId(com: CommonRef, genesis: Header) =
com.genesisHash = genesis.computeBlockHash
let genesisCRC = crc32(0, com.genesisHash.data)
com.forkIdCalculator = initForkIdCalculator(
com.forkIdCalculator = ForkIdCalculator.init(
com.forkTransitionTable,
genesisCRC,
genesis.timestamp.uint64)
Expand Down Expand Up @@ -351,19 +351,21 @@ func isLondonOrLater*(com: CommonRef, number: BlockNumber): bool =
com.toHardFork(number.forkDeterminationInfo) >= London

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

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

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

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

func isEIP155*(com: CommonRef, number: BlockNumber): bool =
com.config.eip155Block.isSome and number >= com.config.eip155Block.value
Expand Down
121 changes: 89 additions & 32 deletions execution_chain/common/hardforks.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# This file may not be copied, modified, or distributed except according to
# those terms.

{.push raises: [].}

import
std/[strutils],
eth/common/[headers],
Expand All @@ -15,8 +17,6 @@ import
../utils/utils,
./evmforks

{.push raises: [].}

type
HardFork* = enum
Frontier
Expand Down Expand Up @@ -348,10 +348,10 @@ type
byBlock: seq[uint64]
byTime: seq[uint64]
genesisCRC: uint32
cache: seq[ForkID]
forkHistory: seq[ForkID]

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

(crc, 0'u64)

func compatible*(calc: var ForkIdCalculator, forkId: ForkID): bool =
if calc.cache.len == 0:
calc.cache = newSeqOfCap[ForkID](calc.byBlock.len + calc.byTime.len + 1)
var crc = calc.genesisCRC

# Build cache of all valid ForkIds
# Each entry is (crc before fork, fork number)
for fork in calc.byBlock:
calc.cache.add( (crc, fork) )
crc = crc32(crc, fork.toBytesBE)

for fork in calc.byTime:
calc.cache.add( (crc, fork) )
crc = crc32(crc, fork.toBytesBE)

# Add last fork ID (after all forks, next=0)
calc.cache.add( (crc, 0'u64) )
func compatible*(calc: ForkIdCalculator, forkId: ForkID, number: uint64, time: uint64): bool =
## Check forkId compatibility at a specific head position according to EIP-2124 / EIP-6122.
## The number and time represent the local chain state at which compatibility needs to be checked
## In regular use this should be the current header block number and timestamp, but for testing
## arbitrary points in history can be used.

# Calculate our current local fork ID at the given head position (= block and/or time)
let localId = calc.newID(number, time)

# Calculate position of local fork ID in history
var localForkPos = -1
for i, historicalId in calc.forkHistory:
if localId.crc == historicalId.crc:
localForkPos = i
break

# Based on position of local fork ID, determine if the head is block or time based
let head =
if localForkPos >= calc.byBlock.len():
time
else:
number

# 1: local and remote FORK_HASH matches
if forkId.crc == localId.crc:
if forkId.nextFork != 0: # announced fork
if head >= forkId.nextFork:
# 1a - next fork already passed locally
return false
else:
# 1b - next fork not yet passed locally
return true

for id in calc.cache:
if id == forkId:
else:
# 1b - no next fork announced
return true

# 2 + 3: FORK_HASH is subset or superset of local past forks
for i, historicalId in calc.forkHistory:
if forkId.crc == historicalId.crc:
# Need to know if remote (=forkId) is ahead or behind localId
if i > localForkPos:
# 3: Remote is at a future fork we also know about
# (local is syncing)
return true
# TODO: Should we still check its next fork?
if forkId.nextFork == historicalId.nextFork:
# 2: Remote is at a past fork we also know about and its nextFork matches the one for that past fork
# (remote is syncing)
return true
else:
# 4: Remote is at a past fork we also know about but its nextFork doesn't match
return false

# 4: incompatible
false

func initForkIdCalculator*(map: ForkTransitionTable,
genesisCRC: uint32,
genesisTime: uint64): ForkIdCalculator =
# Build ForkIdCalculator from the chain's fork configuration
func init*(
_: type ForkIdCalculator,
map: ForkTransitionTable,
genesisCRC: uint32,
genesisTime: uint64,
): ForkIdCalculator =
## Build ForkIdCalculator from the chain's fork configuration

# Extract the fork rule block number aggregate it
var forksByBlock: seq[uint64]
for fork, val in map.blockNumberThresholds:
if val.isNone: continue
if val.isNone:
continue
let val64 = val.get
if forksByBlock.len == 0:
forksByBlock.add val64
Expand All @@ -424,7 +461,8 @@ func initForkIdCalculator*(map: ForkTransitionTable,
# Extract the fork rule timestamp number aggregate it
var forksByTime: seq[uint64]
for fork, val in map.timeThresholds:
if val.isNone: continue
if val.isNone:
continue
let val64 = val.get.uint64
if forksByTime.len == 0:
forksByTime.add val64
Expand All @@ -435,9 +473,28 @@ func initForkIdCalculator*(map: ForkTransitionTable,
while forksByTime.len > 0 and forksByTime[0] <= genesisTime:
forksByTime.delete(0)

result.genesisCRC = genesisCRC
result.byBlock = system.move(forksByBlock)
result.byTime = system.move(forksByTime)
# Calculate the fork ids for the full fork history
var forkHistory = newSeqOfCap[ForkID](forksByBlock.len + forksByTime.len + 1)
var crc = genesisCRC

# Each entry is (crc before fork, fork number)
for fork in forksByBlock:
forkHistory.add((crc, fork))
crc = crc32(crc, fork.toBytesBE)

for fork in forksByTime:
forkHistory.add((crc, fork))
crc = crc32(crc, fork.toBytesBE)

# Add last fork ID (after all forks, next=0)
forkHistory.add((crc, 0'u64))

ForkIdCalculator(
genesisCRC: genesisCRC,
byBlock: system.move(forksByBlock),
byTime: system.move(forksByTime),
forkHistory: system.move(forkHistory),
)

# ------------------------------------------------------------------------------
# End
Expand Down
3 changes: 2 additions & 1 deletion execution_chain/nimbus_execution_client.nim
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ proc setupP2P(nimbus: NimbusNode, config: ExecutionClientConf, com: CommonRef) =
com.forkId(header.number, header.timestamp)

func compatibleForkIdProc(id: ForkID): bool =
com.compatibleForkId(id)
let header = fc.latestHeader()
com.compatibleForkId(id, header.number, header.timestamp)

let forkIdProcs = ForkIdProcs(
forkId: forkIdProc,
Expand Down
Loading