# beacon_chain # Copyright (c) 2021-2023 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. when (NimMajor, NimMinor) < (1, 4): {.push raises: [Defect].} else: {.push raises: [].} import stew/assign2, chronicles, ../extras, "."/[block_id, eth2_merkleization, eth2_ssz_serialization, presets], ./datatypes/[phase0, altair, bellatrix, capella, eip4844], ./mev/bellatrix_mev # TODO re-export capella, but for now it could cause knock-on effects, so stage # it sequentially export extras, block_id, phase0, altair, bellatrix, eth2_merkleization, eth2_ssz_serialization, presets, bellatrix_mev # This file contains helpers for dealing with forks - we have two ways we can # deal with forks: # * generics - this means using the static typing and differentiating forks # at compile time - this is preferred in fork-specific code where the fork # is known up-front, for example spec functions. # * variants - this means using a variant object and determining the fork at # runtime - this carries the obvious risk and complexity of dealing with # runtime checking, but is of course needed for external data that may be # of any fork kind. # # For generics, we define `Forky*` type classes that cover "similar" objects # across forks - for variants, they're called `Forked*` instead. # See withXxx and `init` for convenient ways of moving between these two worlds. # A clever programmer would use templates, macros and dark magic to create all # these types and converters :) type BeaconStateFork* {.pure.} = enum Phase0, Altair, Bellatrix, Capella, EIP4844 ForkyBeaconState* = phase0.BeaconState | altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | eip4844.BeaconState ForkyHashedBeaconState* = phase0.HashedBeaconState | altair.HashedBeaconState | bellatrix.HashedBeaconState | capella.HashedBeaconState | eip4844.HashedBeaconState ForkedHashedBeaconState* = object case kind*: BeaconStateFork of BeaconStateFork.Phase0: phase0Data*: phase0.HashedBeaconState of BeaconStateFork.Altair: altairData*: altair.HashedBeaconState of BeaconStateFork.Bellatrix: bellatrixData*: bellatrix.HashedBeaconState of BeaconStateFork.Capella: capellaData*: capella.HashedBeaconState of BeaconStateFork.EIP4844: eip4844Data*: eip4844.HashedBeaconState BeaconBlockFork* {.pure.} = enum Phase0, Altair, Bellatrix, Capella, EIP4844 ForkyExecutionPayload* = bellatrix.ExecutionPayload | capella.ExecutionPayload | eip4844.ExecutionPayload ForkyBeaconBlockBody* = phase0.BeaconBlockBody | altair.BeaconBlockBody | bellatrix.BeaconBlockBody | capella.BeaconBlockBody | eip4844.BeaconBlockBody ForkySigVerifiedBeaconBlockBody* = phase0.SigVerifiedBeaconBlockBody | altair.SigVerifiedBeaconBlockBody | bellatrix.SigVerifiedBeaconBlockBody | capella.SigVerifiedBeaconBlockBody | eip4844.SigVerifiedBeaconBlockBody ForkyTrustedBeaconBlockBody* = phase0.TrustedBeaconBlockBody | altair.TrustedBeaconBlockBody | bellatrix.TrustedBeaconBlockBody | capella.TrustedBeaconBlockBody | eip4844.TrustedBeaconBlockBody SomeForkyBeaconBlockBody* = ForkyBeaconBlockBody | ForkySigVerifiedBeaconBlockBody | ForkyTrustedBeaconBlockBody ForkyBeaconBlock* = phase0.BeaconBlock | altair.BeaconBlock | bellatrix.BeaconBlock | capella.BeaconBlock | eip4844.BeaconBlock ForkySigVerifiedBeaconBlock* = phase0.SigVerifiedBeaconBlock | altair.SigVerifiedBeaconBlock | bellatrix.SigVerifiedBeaconBlock | capella.SigVerifiedBeaconBlock | eip4844.SigVerifiedBeaconBlock ForkyTrustedBeaconBlock* = phase0.TrustedBeaconBlock | altair.TrustedBeaconBlock | bellatrix.TrustedBeaconBlock | capella.TrustedBeaconBlock | eip4844.TrustedBeaconBlock SomeForkyBeaconBlock* = ForkyBeaconBlock | ForkySigVerifiedBeaconBlock | ForkyTrustedBeaconBlock ForkedBeaconBlock* = object case kind*: BeaconBlockFork of BeaconBlockFork.Phase0: phase0Data*: phase0.BeaconBlock of BeaconBlockFork.Altair: altairData*: altair.BeaconBlock of BeaconBlockFork.Bellatrix: bellatrixData*: bellatrix.BeaconBlock of BeaconBlockFork.Capella: capellaData*: capella.BeaconBlock of BeaconBlockFork.EIP4844: eip4844Data*: eip4844.BeaconBlock Web3SignerForkedBeaconBlock* = object case kind*: BeaconBlockFork of BeaconBlockFork.Phase0: phase0Data*: phase0.BeaconBlock of BeaconBlockFork.Altair: altairData*: altair.BeaconBlock of BeaconBlockFork.Bellatrix: bellatrixData*: BeaconBlockHeader of BeaconBlockFork.Capella: capellaData*: BeaconBlockHeader of BeaconBlockFork.EIP4844: eip4844Data*: BeaconBlockHeader ForkedBlindedBeaconBlock* = object case kind*: BeaconBlockFork of BeaconBlockFork.Phase0: phase0Data*: phase0.BeaconBlock of BeaconBlockFork.Altair: altairData*: altair.BeaconBlock of BeaconBlockFork.Bellatrix: bellatrixData*: BlindedBeaconBlock of BeaconBlockFork.Capella: capellaData*: BlindedBeaconBlock of BeaconBlockFork.EIP4844: eip4844Data*: BlindedBeaconBlock ForkedTrustedBeaconBlock* = object case kind*: BeaconBlockFork of BeaconBlockFork.Phase0: phase0Data*: phase0.TrustedBeaconBlock of BeaconBlockFork.Altair: altairData*: altair.TrustedBeaconBlock of BeaconBlockFork.Bellatrix: bellatrixData*: bellatrix.TrustedBeaconBlock of BeaconBlockFork.Capella: capellaData*: capella.TrustedBeaconBlock of BeaconBlockFork.EIP4844: eip4844Data*: eip4844.TrustedBeaconBlock ForkySignedBeaconBlock* = phase0.SignedBeaconBlock | altair.SignedBeaconBlock | bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock | eip4844.SignedBeaconBlock ForkedSignedBeaconBlock* = object case kind*: BeaconBlockFork of BeaconBlockFork.Phase0: phase0Data*: phase0.SignedBeaconBlock of BeaconBlockFork.Altair: altairData*: altair.SignedBeaconBlock of BeaconBlockFork.Bellatrix: bellatrixData*: bellatrix.SignedBeaconBlock of BeaconBlockFork.Capella: capellaData*: capella.SignedBeaconBlock of BeaconBlockFork.EIP4844: eip4844Data*: eip4844.SignedBeaconBlock ForkySignedBlindedBeaconBlock* = phase0.SignedBeaconBlock | altair.SignedBeaconBlock | SignedBlindedBeaconBlock ForkedSignedBlindedBeaconBlock* = object case kind*: BeaconBlockFork of BeaconBlockFork.Phase0: phase0Data*: phase0.SignedBeaconBlock of BeaconBlockFork.Altair: altairData*: altair.SignedBeaconBlock of BeaconBlockFork.Bellatrix: bellatrixData*: SignedBlindedBeaconBlock of BeaconBlockFork.Capella: capellaData*: SignedBlindedBeaconBlock of BeaconBlockFork.EIP4844: eip4844Data*: SignedBlindedBeaconBlock ForkySigVerifiedSignedBeaconBlock* = phase0.SigVerifiedSignedBeaconBlock | altair.SigVerifiedSignedBeaconBlock | bellatrix.SigVerifiedSignedBeaconBlock | capella.SigVerifiedSignedBeaconBlock | eip4844.SigVerifiedSignedBeaconBlock ForkyMsgTrustedSignedBeaconBlock* = phase0.MsgTrustedSignedBeaconBlock | altair.MsgTrustedSignedBeaconBlock | bellatrix.MsgTrustedSignedBeaconBlock | capella.MsgTrustedSignedBeaconBlock | eip4844.MsgTrustedSignedBeaconBlock ForkyTrustedSignedBeaconBlock* = phase0.TrustedSignedBeaconBlock | altair.TrustedSignedBeaconBlock | bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock | eip4844.TrustedSignedBeaconBlock ForkedMsgTrustedSignedBeaconBlock* = object case kind*: BeaconBlockFork of BeaconBlockFork.Phase0: phase0Data*: phase0.MsgTrustedSignedBeaconBlock of BeaconBlockFork.Altair: altairData*: altair.MsgTrustedSignedBeaconBlock of BeaconBlockFork.Bellatrix: bellatrixData*: bellatrix.MsgTrustedSignedBeaconBlock of BeaconBlockFork.Capella: capellaData*: capella.MsgTrustedSignedBeaconBlock of BeaconBlockFork.EIP4844: eip4844Data*: eip4844.MsgTrustedSignedBeaconBlock ForkedTrustedSignedBeaconBlock* = object case kind*: BeaconBlockFork of BeaconBlockFork.Phase0: phase0Data*: phase0.TrustedSignedBeaconBlock of BeaconBlockFork.Altair: altairData*: altair.TrustedSignedBeaconBlock of BeaconBlockFork.Bellatrix: bellatrixData*: bellatrix.TrustedSignedBeaconBlock of BeaconBlockFork.Capella: capellaData*: capella.TrustedSignedBeaconBlock of BeaconBlockFork.EIP4844: eip4844Data*: eip4844.TrustedSignedBeaconBlock SomeForkySignedBeaconBlock* = ForkySignedBeaconBlock | ForkySigVerifiedSignedBeaconBlock | ForkyMsgTrustedSignedBeaconBlock | ForkyTrustedSignedBeaconBlock EpochInfoFork* {.pure.} = enum Phase0 Altair ForkedEpochInfo* = object case kind*: EpochInfoFork of EpochInfoFork.Phase0: phase0Data*: phase0.EpochInfo of EpochInfoFork.Altair: altairData*: altair.EpochInfo ForkyEpochInfo* = phase0.EpochInfo | altair.EpochInfo ForkDigests* = object phase0*: ForkDigest altair*: ForkDigest bellatrix*: ForkDigest capella*: ForkDigest eip4844*: ForkDigest template toFork*[T: phase0.BeaconState | phase0.HashedBeaconState]( t: type T): BeaconStateFork = BeaconStateFork.Phase0 template toFork*[T: altair.BeaconState | altair.HashedBeaconState]( t: type T): BeaconStateFork = BeaconStateFork.Altair template toFork*[T: bellatrix.BeaconState | bellatrix.HashedBeaconState]( t: type T): BeaconStateFork = BeaconStateFork.Bellatrix template toFork*[T: capella.BeaconState | capella.HashedBeaconState]( t: type T): BeaconStateFork = BeaconStateFork.Capella template toFork*[T: eip4844.BeaconState | eip4844.HashedBeaconState]( t: type T): BeaconStateFork = BeaconStateFork.EIP4844 # TODO when https://github.com/nim-lang/Nim/issues/21086 fixed, use return type # `ref T` func new*(T: type ForkedHashedBeaconState, data: phase0.BeaconState): ref ForkedHashedBeaconState = (ref T)(kind: BeaconStateFork.Phase0, phase0Data: phase0.HashedBeaconState( data: data, root: hash_tree_root(data))) func new*(T: type ForkedHashedBeaconState, data: altair.BeaconState): ref ForkedHashedBeaconState = (ref T)(kind: BeaconStateFork.Altair, altairData: altair.HashedBeaconState( data: data, root: hash_tree_root(data))) func new*(T: type ForkedHashedBeaconState, data: bellatrix.BeaconState): ref ForkedHashedBeaconState = (ref T)(kind: BeaconStateFork.Bellatrix, bellatrixData: bellatrix.HashedBeaconState( data: data, root: hash_tree_root(data))) func new*(T: type ForkedHashedBeaconState, data: capella.BeaconState): ref ForkedHashedBeaconState = (ref T)(kind: BeaconStateFork.Capella, capellaData: capella.HashedBeaconState( data: data, root: hash_tree_root(data))) func new*(T: type ForkedHashedBeaconState, data: eip4844.BeaconState): ref ForkedHashedBeaconState = (ref T)(kind: BeaconStateFork.EIP4844, eip4844Data: eip4844.HashedBeaconState( data: data, root: hash_tree_root(data))) template init*(T: type ForkedBeaconBlock, blck: phase0.BeaconBlock): T = T(kind: BeaconBlockFork.Phase0, phase0Data: blck) template init*(T: type ForkedBeaconBlock, blck: altair.BeaconBlock): T = T(kind: BeaconBlockFork.Altair, altairData: blck) template init*(T: type ForkedBeaconBlock, blck: bellatrix.BeaconBlock): T = T(kind: BeaconBlockFork.Bellatrix, bellatrixData: blck) template init*(T: type ForkedBeaconBlock, blck: capella.BeaconBlock): T = T(kind: BeaconBlockFork.Capella, capellaData: blck) template init*(T: type ForkedTrustedBeaconBlock, blck: phase0.TrustedBeaconBlock): T = T(kind: BeaconBlockFork.Phase0, phase0Data: blck) template init*(T: type ForkedTrustedBeaconBlock, blck: altair.TrustedBeaconBlock): T = T(kind: BeaconBlockFork.Altair, altairData: blck) template init*(T: type ForkedTrustedBeaconBlock, blck: bellatrix.TrustedBeaconBlock): T = T(kind: BeaconBlockFork.Bellatrix, bellatrixData: blck) template init*(T: type ForkedTrustedBeaconBlock, blck: capella.TrustedBeaconBlock): T = T(kind: BeaconBlockFork.Capella, capellaData: blck) template init*(T: type ForkedSignedBeaconBlock, blck: phase0.SignedBeaconBlock): T = T(kind: BeaconBlockFork.Phase0, phase0Data: blck) template init*(T: type ForkedSignedBeaconBlock, blck: altair.SignedBeaconBlock): T = T(kind: BeaconBlockFork.Altair, altairData: blck) template init*(T: type ForkedSignedBeaconBlock, blck: bellatrix.SignedBeaconBlock): T = T(kind: BeaconBlockFork.Bellatrix, bellatrixData: blck) template init*(T: type ForkedSignedBeaconBlock, blck: capella.SignedBeaconBlock): T = T(kind: BeaconBlockFork.Capella, capellaData: blck) template init*(T: type ForkedSignedBeaconBlock, blck: eip4844.SignedBeaconBlock): T = T(kind: BeaconBlockFork.EIP4844, eip4844Data: blck) func init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock, blockRoot: Eth2Digest, signature: ValidatorSig): T = case forked.kind of BeaconBlockFork.Phase0: T(kind: BeaconBlockFork.Phase0, phase0Data: phase0.SignedBeaconBlock(message: forked.phase0Data, root: blockRoot, signature: signature)) of BeaconBlockFork.Altair: T(kind: BeaconBlockFork.Altair, altairData: altair.SignedBeaconBlock(message: forked.altairData, root: blockRoot, signature: signature)) of BeaconBlockFork.Bellatrix: T(kind: BeaconBlockFork.Bellatrix, bellatrixData: bellatrix.SignedBeaconBlock(message: forked.bellatrixData, root: blockRoot, signature: signature)) of BeaconBlockFork.Capella: T(kind: BeaconBlockFork.Capella, capellaData: capella.SignedBeaconBlock(message: forked.capellaData, root: blockRoot, signature: signature)) of BeaconBlockFork.EIP4844: T(kind: BeaconBlockFork.EIP4844, eip4844Data: eip4844.SignedBeaconBlock(message: forked.eip4844Data, root: blockRoot, signature: signature)) func init*(T: type ForkedSignedBlindedBeaconBlock, forked: ForkedBlindedBeaconBlock, blockRoot: Eth2Digest, signature: ValidatorSig): T = case forked.kind of BeaconBlockFork.Phase0: T(kind: BeaconBlockFork.Phase0, phase0Data: phase0.SignedBeaconBlock(message: forked.phase0Data, root: blockRoot, signature: signature)) of BeaconBlockFork.Altair: T(kind: BeaconBlockFork.Altair, altairData: altair.SignedBeaconBlock(message: forked.altairData, root: blockRoot, signature: signature)) of BeaconBlockFork.Bellatrix: T(kind: BeaconBlockFork.Bellatrix, bellatrixData: SignedBlindedBeaconBlock(message: forked.bellatrixData, signature: signature)) of BeaconBlockFork.Capella: T(kind: BeaconBlockFork.Capella, capellaData: SignedBlindedBeaconBlock(message: forked.capellaData, signature: signature)) of BeaconBlockFork.EIP4844: T(kind: BeaconBlockFork.EIP4844, eip4844Data: SignedBlindedBeaconBlock(message: forked.eip4844Data, signature: signature)) template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: phase0.MsgTrustedSignedBeaconBlock): T = T(kind: BeaconBlockFork.Phase0, phase0Data: blck) template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: altair.MsgTrustedSignedBeaconBlock): T = T(kind: BeaconBlockFork.Altair, altairData: blck) template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: bellatrix.MsgTrustedSignedBeaconBlock): T = T(kind: BeaconBlockFork.Bellatrix, bellatrixData: blck) template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: capella.MsgTrustedSignedBeaconBlock): T = T(kind: BeaconBlockFork.Capella, capellaData: blck) template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: eip4844.MsgTrustedSignedBeaconBlock): T = T(kind: BeaconBlockFork.EIP4844, eip4844Data: blck) template init*(T: type ForkedTrustedSignedBeaconBlock, blck: phase0.TrustedSignedBeaconBlock): T = T(kind: BeaconBlockFork.Phase0, phase0Data: blck) template init*(T: type ForkedTrustedSignedBeaconBlock, blck: altair.TrustedSignedBeaconBlock): T = T(kind: BeaconBlockFork.Altair, altairData: blck) template init*(T: type ForkedTrustedSignedBeaconBlock, blck: bellatrix.TrustedSignedBeaconBlock): T = T(kind: BeaconBlockFork.Bellatrix, bellatrixData: blck) template init*(T: type ForkedTrustedSignedBeaconBlock, blck: capella.TrustedSignedBeaconBlock): T = T(kind: BeaconBlockFork.Capella, capellaData: blck) template init*(T: type ForkedTrustedSignedBeaconBlock, blck: eip4844.TrustedSignedBeaconBlock): T = T(kind: BeaconBlockFork.EIP4844, eip4844Data: blck) template toString*(kind: BeaconBlockFork): string = case kind of BeaconBlockFork.Phase0: "phase0" of BeaconBlockFork.Altair: "altair" of BeaconBlockFork.Bellatrix: "bellatrix" of BeaconBlockFork.Capella: "capella" of BeaconBlockFork.EIP4844: "eip4844" template toString*(kind: BeaconStateFork): string = case kind of BeaconStateFork.Phase0: "phase0" of BeaconStateFork.Altair: "altair" of BeaconStateFork.Bellatrix: "bellatrix" of BeaconStateFork.Capella: "capella" of BeaconStateFork.EIP4844: "eip4844" template toFork*[T: phase0.BeaconBlock | phase0.SignedBeaconBlock | phase0.TrustedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock | phase0.MsgTrustedSignedBeaconBlock | phase0.TrustedSignedBeaconBlock]( t: type T): BeaconBlockFork = BeaconBlockFork.Phase0 template toFork*[T: altair.BeaconBlock | altair.SignedBeaconBlock | altair.TrustedBeaconBlock | altair.SigVerifiedSignedBeaconBlock | altair.MsgTrustedSignedBeaconBlock | altair.TrustedSignedBeaconBlock]( t: type T): BeaconBlockFork = BeaconBlockFork.Altair template toFork*[T: bellatrix.ExecutionPayload | bellatrix.BeaconBlock | bellatrix.SignedBeaconBlock | bellatrix.TrustedBeaconBlock | bellatrix.SigVerifiedSignedBeaconBlock | bellatrix.MsgTrustedSignedBeaconBlock | bellatrix.TrustedSignedBeaconBlock]( t: type T): BeaconBlockFork = BeaconBlockFork.Bellatrix template toFork*[T: capella.ExecutionPayload | capella.BeaconBlock | capella.SignedBeaconBlock | capella.TrustedBeaconBlock | capella.SigVerifiedSignedBeaconBlock | capella.MsgTrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock]( t: type T): BeaconBlockFork = BeaconBlockFork.Capella template toFork*[T: eip4844.ExecutionPayload | eip4844.BeaconBlock | eip4844.SignedBeaconBlock | eip4844.TrustedBeaconBlock | eip4844.SigVerifiedSignedBeaconBlock | eip4844.MsgTrustedSignedBeaconBlock | eip4844.TrustedSignedBeaconBlock]( t: type T): BeaconBlockFork = BeaconBlockFork.EIP4844 template init*(T: type ForkedEpochInfo, info: phase0.EpochInfo): T = T(kind: EpochInfoFork.Phase0, phase0Data: info) template init*(T: type ForkedEpochInfo, info: altair.EpochInfo): T = T(kind: EpochInfoFork.Altair, altairData: info) template withState*(x: ForkedHashedBeaconState, body: untyped): untyped = case x.kind of BeaconStateFork.EIP4844: const stateFork {.inject, used.} = BeaconStateFork.EIP4844 template forkyState: untyped {.inject, used.} = x.eip4844Data body of BeaconStateFork.Capella: const stateFork {.inject, used.} = BeaconStateFork.Capella template forkyState: untyped {.inject, used.} = x.capellaData body of BeaconStateFork.Bellatrix: const stateFork {.inject, used.} = BeaconStateFork.Bellatrix template forkyState: untyped {.inject, used.} = x.bellatrixData body of BeaconStateFork.Altair: const stateFork {.inject, used.} = BeaconStateFork.Altair template forkyState: untyped {.inject, used.} = x.altairData body of BeaconStateFork.Phase0: const stateFork {.inject, used.} = BeaconStateFork.Phase0 template forkyState: untyped {.inject, used.} = x.phase0Data body template withEpochInfo*(x: ForkedEpochInfo, body: untyped): untyped = case x.kind of EpochInfoFork.Phase0: const infoFork {.inject.} = EpochInfoFork.Phase0 template info: untyped {.inject.} = x.phase0Data body of EpochInfoFork.Altair: const infoFork {.inject.} = EpochInfoFork.Altair template info: untyped {.inject.} = x.altairData body template withEpochInfo*( state: phase0.BeaconState, x: var ForkedEpochInfo, body: untyped): untyped = if x.kind != EpochInfoFork.Phase0: # Rare, should never happen even, so efficiency a non-issue x = ForkedEpochInfo(kind: EpochInfoFork.Phase0) template info: untyped {.inject.} = x.phase0Data body template withEpochInfo*( state: altair.BeaconState | bellatrix.BeaconState | capella.BeaconState | eip4844.BeaconState, x: var ForkedEpochInfo, body: untyped): untyped = if x.kind != EpochInfoFork.Altair: # Rare, so efficiency not critical x = ForkedEpochInfo(kind: EpochInfoFork.Altair) template info: untyped {.inject.} = x.altairData body func assign*(tgt: var ForkedHashedBeaconState, src: ForkedHashedBeaconState) = if tgt.kind == src.kind: case tgt.kind of BeaconStateFork.EIP4844: assign(tgt.eip4844Data, src.eip4844Data): of BeaconStateFork.Capella: assign(tgt.capellaData, src.capellaData): of BeaconStateFork.Bellatrix: assign(tgt.bellatrixData, src.bellatrixData): of BeaconStateFork.Altair: assign(tgt.altairData, src.altairData): of BeaconStateFork.Phase0: assign(tgt.phase0Data, src.phase0Data): else: # Ensure case object and discriminator get updated simultaneously, even # with nimOldCaseObjects. This is infrequent. tgt = src template getStateField*(x: ForkedHashedBeaconState, y: untyped): untyped = # The use of `unsafeAddr` avoids excessive copying in certain situations, e.g., # ``` # for index, validator in getStateField(stateData.data, validators): # ``` # Without `unsafeAddr`, the `validators` list would be copied to a temporary variable. (case x.kind of BeaconStateFork.EIP4844: unsafeAddr x.eip4844Data.data.y of BeaconStateFork.Capella: unsafeAddr x.capellaData.data.y of BeaconStateFork.Bellatrix: unsafeAddr x.bellatrixData.data.y of BeaconStateFork.Altair: unsafeAddr x.altairData.data.y of BeaconStateFork.Phase0: unsafeAddr x.phase0Data.data.y)[] func getStateRoot*(x: ForkedHashedBeaconState): Eth2Digest = withState(x): forkyState.root func setStateRoot*(x: var ForkedHashedBeaconState, root: Eth2Digest) = withState(x): forkyState.root = root func stateForkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): BeaconStateFork = ## Return the current fork for the given epoch. static: doAssert high(BeaconStateFork) == BeaconStateFork.EIP4844 doAssert BeaconStateFork.EIP4844 > BeaconStateFork.Capella doAssert BeaconStateFork.Capella > BeaconStateFork.Bellatrix doAssert BeaconStateFork.Bellatrix > BeaconStateFork.Altair doAssert BeaconStateFork.Altair > BeaconStateFork.Phase0 doAssert GENESIS_EPOCH == 0 if epoch >= cfg.EIP4844_FORK_EPOCH: BeaconStateFork.EIP4844 elif epoch >= cfg.CAPELLA_FORK_EPOCH: BeaconStateFork.Capella elif epoch >= cfg.BELLATRIX_FORK_EPOCH: BeaconStateFork.Bellatrix elif epoch >= cfg.ALTAIR_FORK_EPOCH: BeaconStateFork.Altair else: BeaconStateFork.Phase0 func blockForkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): BeaconBlockFork = ## Return the current fork for the given epoch. static: doAssert high(BeaconBlockFork) == BeaconBlockFork.EIP4844 if epoch >= cfg.EIP4844_FORK_EPOCH: BeaconBlockFork.EIP4844 elif epoch >= cfg.CAPELLA_FORK_EPOCH: BeaconBlockFork.Capella elif epoch >= cfg.BELLATRIX_FORK_EPOCH: BeaconBlockFork.Bellatrix elif epoch >= cfg.ALTAIR_FORK_EPOCH: BeaconBlockFork.Altair else: BeaconBlockFork.Phase0 func stateForkForDigest*( forkDigests: ForkDigests, forkDigest: ForkDigest): Opt[BeaconStateFork] = static: doAssert high(BeaconStateFork) == BeaconStateFork.EIP4844 if forkDigest == forkDigests.eip4844: ok BeaconStateFork.EIP4844 elif forkDigest == forkDigests.capella: ok BeaconStateFork.Capella elif forkDigest == forkDigests.bellatrix: ok BeaconStateFork.Bellatrix elif forkDigest == forkDigests.altair: ok BeaconStateFork.Altair elif forkDigest == forkDigests.phase0: ok BeaconStateFork.Phase0 else: err() func atStateFork*( forkDigests: ForkDigests, stateFork: BeaconStateFork): ForkDigest = case stateFork of BeaconStateFork.EIP4844: forkDigests.eip4844 of BeaconStateFork.Capella: forkDigests.capella of BeaconStateFork.Bellatrix: forkDigests.bellatrix of BeaconStateFork.Altair: forkDigests.altair of BeaconStateFork.Phase0: forkDigests.phase0 template atEpoch*( forkDigests: ForkDigests, epoch: Epoch, cfg: RuntimeConfig): ForkDigest = forkDigests.atStateFork(cfg.stateForkAtEpoch(epoch)) template asSigned*( x: ForkedMsgTrustedSignedBeaconBlock | ForkedTrustedSignedBeaconBlock ): ForkedSignedBeaconBlock = isomorphicCast[ForkedSignedBeaconBlock](x) template asSigned*( x: ref ForkedMsgTrustedSignedBeaconBlock | ref ForkedTrustedSignedBeaconBlock ): ref ForkedSignedBeaconBlock = isomorphicCast[ref ForkedSignedBeaconBlock](x) template asMsgTrusted*( x: ForkedSignedBeaconBlock | ForkedTrustedSignedBeaconBlock ): ForkedMsgTrustedSignedBeaconBlock = isomorphicCast[ForkedMsgTrustedSignedBeaconBlock](x) template asMsgTrusted*( x: ref ForkedSignedBeaconBlock | ref ForkedTrustedSignedBeaconBlock ): ref ForkedMsgTrustedSignedBeaconBlock = isomorphicCast[ref ForkedMsgTrustedSignedBeaconBlock](x) template asTrusted*( x: ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock ): ForkedTrustedSignedBeaconBlock = isomorphicCast[ForkedTrustedSignedBeaconBlock](x) template asTrusted*( x: ref ForkedSignedBeaconBlock | ref ForkedMsgTrustedSignedBeaconBlock ): ref ForkedTrustedSignedBeaconBlock = isomorphicCast[ref ForkedTrustedSignedBeaconBlock](x) template withBlck*( x: ForkedBeaconBlock | Web3SignerForkedBeaconBlock | ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock | ForkedTrustedSignedBeaconBlock | ForkedBlindedBeaconBlock | ForkedSignedBlindedBeaconBlock, body: untyped): untyped = case x.kind of BeaconBlockFork.Phase0: const stateFork {.inject, used.} = BeaconStateFork.Phase0 template blck: untyped {.inject.} = x.phase0Data body of BeaconBlockFork.Altair: const stateFork {.inject, used.} = BeaconStateFork.Altair template blck: untyped {.inject.} = x.altairData body of BeaconBlockFork.Bellatrix: const stateFork {.inject, used.} = BeaconStateFork.Bellatrix template blck: untyped {.inject.} = x.bellatrixData body of BeaconBlockFork.Capella: const stateFork {.inject, used.} = BeaconStateFork.Capella template blck: untyped {.inject.} = x.capellaData body of BeaconBlockFork.EIP4844: const stateFork {.inject, used.} = BeaconStateFork.EIP4844 template blck: untyped {.inject.} = x.eip4844Data body func proposer_index*(x: ForkedBeaconBlock): uint64 = withBlck(x): blck.proposer_index func hash_tree_root*(x: ForkedBeaconBlock | Web3SignerForkedBeaconBlock): Eth2Digest = withBlck(x): hash_tree_root(blck) template getForkedBlockField*( x: ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock | ForkedTrustedSignedBeaconBlock, y: untyped): untyped = # unsafeAddr avoids a copy of the field in some cases (case x.kind of BeaconBlockFork.Phase0: unsafeAddr x.phase0Data.message.y of BeaconBlockFork.Altair: unsafeAddr x.altairData.message.y of BeaconBlockFork.Bellatrix: unsafeAddr x.bellatrixData.message.y of BeaconBlockFork.Capella: unsafeAddr x.capellaData.message.y of BeaconBlockFork.EIP4844: unsafeAddr x.eip4844Data.message.y)[] template signature*(x: ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock | ForkedSignedBlindedBeaconBlock): ValidatorSig = withBlck(x): blck.signature template signature*(x: ForkedTrustedSignedBeaconBlock): TrustedSig = withBlck(x): blck.signature template root*(x: ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock | ForkedTrustedSignedBeaconBlock): Eth2Digest = withBlck(x): blck.root template slot*(x: ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock | ForkedTrustedSignedBeaconBlock): Slot = withBlck(x): blck.message.slot template shortLog*(x: ForkedBeaconBlock | ForkedBlindedBeaconBlock): auto = withBlck(x): shortLog(blck) template shortLog*(x: ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock | ForkedTrustedSignedBeaconBlock | ForkedSignedBlindedBeaconBlock): auto = withBlck(x): shortLog(blck) chronicles.formatIt ForkedBeaconBlock: it.shortLog chronicles.formatIt ForkedSignedBeaconBlock: it.shortLog chronicles.formatIt ForkedMsgTrustedSignedBeaconBlock: it.shortLog chronicles.formatIt ForkedTrustedSignedBeaconBlock: it.shortLog template withStateAndBlck*( s: ForkedHashedBeaconState, b: ForkedBeaconBlock | ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock | ForkedTrustedSignedBeaconBlock, body: untyped): untyped = case s.kind of BeaconStateFork.EIP4844: const stateFork {.inject.} = BeaconStateFork.EIP4844 template forkyState: untyped {.inject.} = s.eip4844Data template blck: untyped {.inject.} = b.eip4844Data body of BeaconStateFork.Capella: const stateFork {.inject.} = BeaconStateFork.Capella template forkyState: untyped {.inject.} = s.capellaData template blck: untyped {.inject.} = b.capellaData body of BeaconStateFork.Bellatrix: const stateFork {.inject.} = BeaconStateFork.Bellatrix template forkyState: untyped {.inject.} = s.bellatrixData template blck: untyped {.inject.} = b.bellatrixData body of BeaconStateFork.Altair: const stateFork {.inject.} = BeaconStateFork.Altair template forkyState: untyped {.inject.} = s.altairData template blck: untyped {.inject.} = b.altairData body of BeaconStateFork.Phase0: const stateFork {.inject.} = BeaconStateFork.Phase0 template forkyState: untyped {.inject.} = s.phase0Data template blck: untyped {.inject.} = b.phase0Data body func toBeaconBlockHeader*( blck: SomeForkyBeaconBlock | BlindedBeaconBlock): BeaconBlockHeader = ## Reduce a given `BeaconBlock` to its `BeaconBlockHeader`. BeaconBlockHeader( slot: blck.slot, proposer_index: blck.proposer_index, parent_root: blck.parent_root, state_root: blck.state_root, body_root: blck.body.hash_tree_root()) template toBeaconBlockHeader*( blck: SomeForkySignedBeaconBlock): BeaconBlockHeader = ## Reduce a given `SignedBeaconBlock` to its `BeaconBlockHeader`. blck.message.toBeaconBlockHeader template toBeaconBlockHeader*( blckParam: ForkedMsgTrustedSignedBeaconBlock | ForkedTrustedSignedBeaconBlock): BeaconBlockHeader = ## Reduce a given signed beacon block to its `BeaconBlockHeader`. withBlck(blckParam): blck.toBeaconBlockHeader() func genesisFork*(cfg: RuntimeConfig): Fork = Fork( previous_version: cfg.GENESIS_FORK_VERSION, current_version: cfg.GENESIS_FORK_VERSION, epoch: GENESIS_EPOCH) func altairFork*(cfg: RuntimeConfig): Fork = Fork( previous_version: cfg.GENESIS_FORK_VERSION, current_version: cfg.ALTAIR_FORK_VERSION, epoch: cfg.ALTAIR_FORK_EPOCH) func bellatrixFork*(cfg: RuntimeConfig): Fork = Fork( previous_version: cfg.ALTAIR_FORK_VERSION, current_version: cfg.BELLATRIX_FORK_VERSION, epoch: cfg.BELLATRIX_FORK_EPOCH) func capellaFork*(cfg: RuntimeConfig): Fork = Fork( previous_version: cfg.BELLATRIX_FORK_VERSION, current_version: cfg.CAPELLA_FORK_VERSION, epoch: cfg.CAPELLA_FORK_EPOCH) func eip4844Fork*(cfg: RuntimeConfig): Fork = Fork( previous_version: cfg.CAPELLA_FORK_VERSION, current_version: cfg.EIP4844_FORK_VERSION, epoch: cfg.EIP4844_FORK_EPOCH) func forkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Fork = case cfg.stateForkAtEpoch(epoch) of BeaconStateFork.EIP4844: cfg.eip4844Fork of BeaconStateFork.Capella: cfg.capellaFork of BeaconStateFork.Bellatrix: cfg.bellatrixFork of BeaconStateFork.Altair: cfg.altairFork of BeaconStateFork.Phase0: cfg.genesisFork func forkVersionAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Version = case cfg.stateForkAtEpoch(epoch) of BeaconStateFork.EIP4844: cfg.EIP4844_FORK_VERSION of BeaconStateFork.Capella: cfg.CAPELLA_FORK_VERSION of BeaconStateFork.Bellatrix: cfg.BELLATRIX_FORK_VERSION of BeaconStateFork.Altair: cfg.ALTAIR_FORK_VERSION of BeaconStateFork.Phase0: cfg.GENESIS_FORK_VERSION func nextForkEpochAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Epoch = case cfg.stateForkAtEpoch(epoch) of BeaconStateFork.EIP4844: FAR_FUTURE_EPOCH of BeaconStateFork.Capella: cfg.EIP4844_FORK_EPOCH of BeaconStateFork.Bellatrix: cfg.CAPELLA_FORK_EPOCH of BeaconStateFork.Altair: cfg.BELLATRIX_FORK_EPOCH of BeaconStateFork.Phase0: cfg.ALTAIR_FORK_EPOCH func getForkSchedule*(cfg: RuntimeConfig): array[5, Fork] = ## This procedure returns list of known and/or scheduled forks. ## ## This procedure is used by HTTP REST framework and validator client. ## ## NOTE: Update this procedure when new fork will be scheduled. [cfg.genesisFork(), cfg.altairFork(), cfg.bellatrixFork(), cfg.capellaFork(), cfg.eip4844Fork()] type # The first few fields of a state, shared across all forks BeaconStateHeader = object genesis_time: uint64 genesis_validators_root: Eth2Digest slot: Slot func readSszForkedHashedBeaconState*( cfg: RuntimeConfig, slot: Slot, data: openArray[byte]): ForkedHashedBeaconState {.raises: [Defect, SszError].} = # TODO https://github.com/nim-lang/Nim/issues/19357 result = ForkedHashedBeaconState( kind: cfg.stateForkAtEpoch(slot.epoch())) withState(result): readSszBytes(data, forkyState.data) forkyState.root = hash_tree_root(forkyState.data) func readSszForkedHashedBeaconState*(cfg: RuntimeConfig, data: openArray[byte]): ForkedHashedBeaconState {.raises: [Defect, SszError].} = ## Read a state picking the right fork by first reading the slot from the byte ## source if data.len() < sizeof(BeaconStateHeader): raise (ref MalformedSszError)(msg: "Not enough data for BeaconState header") let header = SSZ.decode( data.toOpenArray(0, sizeof(BeaconStateHeader) - 1), BeaconStateHeader) # TODO https://github.com/nim-lang/Nim/issues/19357 result = readSszForkedHashedBeaconState(cfg, header.slot, data) type ForkedBeaconBlockHeader = object message*: uint32 # message offset signature*: ValidatorSig slot: Slot # start of BeaconBlock func readSszForkedSignedBeaconBlock*( cfg: RuntimeConfig, data: openArray[byte]): ForkedSignedBeaconBlock {.raises: [Defect, SszError].} = ## Helper to read a header from bytes when it's not certain what kind of block ## it is if data.len() < sizeof(ForkedBeaconBlockHeader): raise (ref MalformedSszError)(msg: "Not enough data for SignedBeaconBlock header") let header = SSZ.decode( data.toOpenArray(0, sizeof(ForkedBeaconBlockHeader) - 1), ForkedBeaconBlockHeader) # TODO https://github.com/nim-lang/Nim/issues/19357 result = ForkedSignedBeaconBlock( kind: cfg.blockForkAtEpoch(header.slot.epoch())) withBlck(result): readSszBytes(data, blck) func toBeaconBlockFork*(fork: BeaconStateFork): BeaconBlockFork = case fork of BeaconStateFork.Phase0: BeaconBlockFork.Phase0 of BeaconStateFork.Altair: BeaconBlockFork.Altair of BeaconStateFork.Bellatrix: BeaconBlockFork.Bellatrix of BeaconStateFork.Capella: BeaconBlockFork.Capella of BeaconStateFork.EIP4844: BeaconBlockFork.EIP4844 # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/phase0/beacon-chain.md#compute_fork_data_root func compute_fork_data_root*(current_version: Version, genesis_validators_root: Eth2Digest): Eth2Digest = ## Return the 32-byte fork data root for the ``current_version`` and ## ``genesis_validators_root``. ## This is used primarily in signature domains to avoid collisions across ## forks/chains. hash_tree_root(ForkData( current_version: current_version, genesis_validators_root: genesis_validators_root )) # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/phase0/beacon-chain.md#compute_fork_digest func compute_fork_digest*(current_version: Version, genesis_validators_root: Eth2Digest): ForkDigest = ## Return the 4-byte fork digest for the ``current_version`` and ## ``genesis_validators_root``. ## This is a digest primarily used for domain separation on the p2p layer. ## 4-bytes suffices for practical separation of forks/chains. array[4, byte](result)[0..3] = compute_fork_data_root( current_version, genesis_validators_root).data.toOpenArray(0, 3) func init*(T: type ForkDigests, cfg: RuntimeConfig, genesis_validators_root: Eth2Digest): T = static: doAssert high(BeaconStateFork) == BeaconStateFork.EIP4844 T( phase0: compute_fork_digest(cfg.GENESIS_FORK_VERSION, genesis_validators_root), altair: compute_fork_digest(cfg.ALTAIR_FORK_VERSION, genesis_validators_root), bellatrix: compute_fork_digest(cfg.BELLATRIX_FORK_VERSION, genesis_validators_root), capella: compute_fork_digest(cfg.CAPELLA_FORK_VERSION, genesis_validators_root), eip4844: compute_fork_digest(cfg.EIP4844_FORK_VERSION, genesis_validators_root) ) func toBlockId*(header: BeaconBlockHeader): BlockId = BlockId(root: header.hash_tree_root(), slot: header.slot) func toBlockId*(blck: SomeForkySignedBeaconBlock): BlockId = BlockId(root: blck.root, slot: blck.message.slot) func toBlockId*(blck: ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock | ForkedTrustedSignedBeaconBlock): BlockId = withBlck(blck): BlockId(root: blck.root, slot: blck.message.slot)