Skip to main content

Overview

ForkId implements EIP-2124 fork identification for DevP2P network validation. It helps peers determine if they’re on compatible forks before exchanging data.
import * as ForkId from '@voltaire/primitives/ForkId'

const forkId = ForkId.from({
  hash: new Uint8Array([0xfc, 0x64, 0xec, 0x04]),
  next: 1920000n,  // DAO fork block
})

Type Definition

type ForkIdType = {
  readonly hash: Uint8Array  // CRC32 checksum (4 bytes)
  readonly next: BlockNumberType  // Next fork block (0 if none)
}
Fields:
  • hash - CRC32 checksum of genesis hash + fork block numbers
  • next - Block number of next known fork (0 if no upcoming forks)

Creating ForkId

from

Create from hash and next block number.
// From Uint8Array
const forkId1 = ForkId.from({
  hash: new Uint8Array([0xfc, 0x64, 0xec, 0x04]),
  next: 1920000n,
})

// From hex string
const forkId2 = ForkId.from({
  hash: "0xfc64ec04",
  next: 1920000n,
})

// From number
const forkId3 = ForkId.from({
  hash: 0xfc64ec04,
  next: 0n,  // No upcoming forks
})
Validation:
  • hash must be exactly 4 bytes
  • next is required (use 0 for no upcoming forks)

Methods

toBytes

Encode to 12-byte binary format for DevP2P handshake.
const bytes = ForkId.toBytes({
  hash: new Uint8Array([0xfc, 0x64, 0xec, 0x04]),
  next: 1920000n,
})

console.log(bytes.length) // 12
// Format: hash (4 bytes) || next (8 bytes big-endian)

matches

Check if two ForkIds are compatible per EIP-2124.
const local = ForkId.from({
  hash: new Uint8Array([0xfc, 0x64, 0xec, 0x04]),
  next: 1920000n,
})

const remote = ForkId.from({
  hash: new Uint8Array([0xfc, 0x64, 0xec, 0x04]),
  next: 1920000n,
})

if (ForkId.matches(local, remote)) {
  console.log('Peers are compatible')
} else {
  console.log('Fork incompatible - disconnect peer')
}

Compatibility Rules

Two ForkIds are compatible if:
  1. Identical - Same hash and next block
  2. Remote has no future forks - Same hash, remote next is 0
  3. Local has no future forks - Same hash, local next is 0
  4. Remote is ahead - Different hash but remote next >= local next
// Case 1: Identical
const a = ForkId.from({ hash: 0xfc64ec04, next: 1920000n })
const b = ForkId.from({ hash: 0xfc64ec04, next: 1920000n })
ForkId.matches(a, b) // true

// Case 2: Remote doesn't know about future forks
const local = ForkId.from({ hash: 0xfc64ec04, next: 2000000n })
const remote = ForkId.from({ hash: 0xfc64ec04, next: 0n })
ForkId.matches(local, remote) // true

// Case 3: Incompatible forks
const mainnet = ForkId.from({ hash: 0xfc64ec04, next: 1920000n })
const classic = ForkId.from({ hash: 0x12345678, next: 0n })
ForkId.matches(mainnet, classic) // false

Ethereum Mainnet Fork History

ForkBlockCRC32 Hash
Frontier00xfc64ec04
Homestead1,150,0000x97c2c34c
DAO Fork1,920,0000x91d1f948
Tangerine2,463,0000x7a64da13
Spurious2,675,0000x3edd5b10
Byzantium4,370,0000xa00bc324
Constantinople7,280,0000x668db0af
Istanbul9,069,0000x879d6e30
Berlin12,244,0000xe029e991
London12,965,0000x0eb440f6
Merge15,537,3940xb715077d
Shanghai17,034,8700xf0afd0e3
Cancun19,426,5870xdce96c2d

DevP2P Handshake

ForkId is exchanged during the DevP2P status message:
// During peer connection
const localForkId = ForkId.from({
  hash: calculateForkHash(chainConfig, headBlock),
  next: getNextForkBlock(chainConfig),
})

const remoteForkId = parseStatusMessage(peerStatus)

if (!ForkId.matches(localForkId, remoteForkId)) {
  disconnectPeer(peer, 'Incompatible fork')
}

See Also