348348 byBlock: seq [uint64 ]
349349 byTime: seq [uint64 ]
350350 genesisCRC: uint32
351- cache : seq [ForkID ]
351+ forkHistory : seq [ForkID ]
352352
353353func newID * (calc: ForkIdCalculator , head, time: uint64 ): ForkID =
354354 # Create a fork ID for a specific block height and timestamp:
@@ -369,28 +369,82 @@ 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 )
372+ func calculateForkHistory (calc: var ForkIdCalculator ) =
373+ if calc.forkHistory .len == 0 :
374+ calc.forkHistory = newSeqOfCap [ForkID ](calc.byBlock.len + calc.byTime.len + 1 )
375375 var crc = calc.genesisCRC
376376
377377 # Build cache of all valid ForkIds
378378 # Each entry is (crc before fork, fork number)
379379 for fork in calc.byBlock:
380- calc.cache .add ( (crc, fork) )
380+ calc.forkHistory .add ( (crc, fork) )
381381 crc = crc32 (crc, fork.toBytesBE)
382382
383383 for fork in calc.byTime:
384- calc.cache .add ( (crc, fork) )
384+ calc.forkHistory .add ( (crc, fork) )
385385 crc = crc32 (crc, fork.toBytesBE)
386386
387387 # Add last fork ID (after all forks, next=0)
388- calc.cache.add ( (crc, 0 'u64 ) )
388+ calc.forkHistory.add ( (crc, 0 'u64 ) )
389+
390+ func compatible * (calc: var ForkIdCalculator , forkId: ForkID , number: uint64 , time: uint64 ): bool =
391+ # Check forkId compatibility at a specific head position according to EIP-2124 / EIP-6122.
392+ # The number and time represent the local chain state at which compatibility needs to be checked
393+ # In regular use this should be the current header block number and timestamp, but for testing
394+ # arbitrary points in history can be used.
395+ calc.calculateForkHistory ()
396+
397+ # Calculate our current local fork ID at the given head position (= block and/or time)
398+ let localId = calc.newID (number, time)
399+
400+ # Calculate position of local fork ID in history
401+ var localForkPos = - 1
402+ for i, historicalId in calc.forkHistory:
403+ if localId.crc == historicalId.crc:
404+ localForkPos = i
405+ break
406+
407+ # Based on position of local fork ID, determine if the head is block or time based
408+ let head =
409+ if localForkPos >= calc.byBlock.len ():
410+ time
411+ else :
412+ number
413+
414+ # 1: local and remote FORK_HASH matches
415+ if forkId.crc == localId.crc:
416+ if forkId.nextFork != 0 : # announced fork
417+ if head >= forkId.nextFork:
418+ # 1a - next fork already passed locally
419+ return false
420+ else :
421+ # 1b - next fork not yet passed locally
422+ return true
389423
390- for id in calc.cache :
391- if id == forkId:
424+ else :
425+ # 1b - no next fork announced
392426 return true
393-
427+ # TODO : should we have a condition here about our nextFork already beeing passed based on actual time?
428+ # return localId.nextFork == 0 or localId.nextFork > "current time"
429+
430+ # 2 + 3: FORK_HASH is subset or superset of local past forks
431+ for i, historicalId in calc.forkHistory:
432+ if forkId.crc == historicalId.crc:
433+ # Need to know if remote (=forkId) is ahead or behind localId
434+ if i > localForkPos:
435+ # 3: Remote is at a future fork we also know about
436+ # (local is syncing)
437+ return true
438+ # TODO : Should we still check its next fork?
439+ if forkId.nextFork == historicalId.nextFork:
440+ # 2: Remote is at a past fork we also know about and its nextFork matches the one for that past fork
441+ # (remote is syncing)
442+ return true
443+ else :
444+ # 4: Remote is at a past fork we also know about but its nextFork doesn't match
445+ return false
446+
447+ # 4: incompatible
394448 false
395449
396450func initForkIdCalculator * (map: ForkTransitionTable ,
0 commit comments