import std/[os, strutils, stats], confutils, chronicles, json_serialization, stew/[byteutils, io2], snappy, ../research/simutils, ../beacon_chain/spec/eth2_apis/eth2_rest_serialization, ../beacon_chain/spec/datatypes/[phase0, altair, bellatrix], ../beacon_chain/spec/[ eth2_ssz_serialization, forks, helpers, state_transition], ../beacon_chain/networking/network_metadata type Cmd* = enum hashTreeRoot = "Compute hash tree root of SSZ object" pretty = "Pretty-print SSZ object" transition = "Run state transition function" slots = "Apply empty slots" NcliConf* = object eth2Network* {. desc: "The Eth2 network preset to use" name: "network" }: Option[string] printTimes* {. defaultValue: false # false to avoid polluting minimal output name: "print-times" desc: "Print timing information".}: bool # TODO confutils argument pragma doesn't seem to do much; also, the cases # are largely equivalent, but this helps create command line usage text case cmd* {.command}: Cmd of hashTreeRoot: htrKind* {. argument desc: "kind of SSZ object: attester_slashing, attestation, signed_block, block, block_body, block_header, deposit, deposit_data, eth1_data, state, proposer_slashing, or voluntary_exit"}: string htrFile* {. argument desc: "filename of SSZ or JSON-encoded object of which to compute hash tree root"}: string of pretty: prettyKind* {. argument desc: "kind of SSZ object: attester_slashing, attestation, signed_block, block, block_body, block_header, deposit, deposit_data, eth1_data, state, proposer_slashing, or voluntary_exit"}: string prettyFile* {. argument desc: "filename of SSZ or JSON-encoded object to pretty-print"}: string of transition: preState* {. argument desc: "State to which to apply specified block"}: string blck* {. argument desc: "Block to apply to preState"}: string postState* {. argument desc: "Filename of state resulting from applying blck to preState"}: string verifyStateRoot* {. argument desc: "Verify state root (default true)" defaultValue: true}: bool of slots: preState2* {. argument desc: "State to which to apply specified block"}: string slot* {. argument desc: "Block to apply to preState"}: uint64 postState2* {. argument desc: "Filename of state resulting from applying blck to preState"}: string template saveSSZFile(filename: string, value: ForkedHashedBeaconState) = case value.kind: of ConsensusFork.Phase0: SSZ.saveFile(filename, value.phase0Data.data) of ConsensusFork.Altair: SSZ.saveFile(filename, value.altairData.data) of ConsensusFork.Bellatrix: SSZ.saveFile(filename, value.bellatrixData.data) of ConsensusFork.Capella: SSZ.saveFile(filename, value.capellaData.data) of ConsensusFork.EIP4844: SSZ.saveFile(filename, value.eip4844Data.data) proc loadFile(filename: string, T: type): T = let ext = splitFile(filename).ext bytes = readAllBytes(filename).expect("file exists") if cmpIgnoreCase(ext, ".ssz") == 0: SSZ.decode(bytes, T) elif cmpIgnoreCase(ext, ".ssz_snappy") == 0: SSZ.decode(snappy.decode(bytes), T) elif cmpIgnoreCase(ext, ".json") == 0: # JSON.loadFile(file, t) echo "TODO needs porting to RestJson" quit 1 else: echo "Unknown file type: ", ext quit 1 proc doTransition(conf: NcliConf) = type Timers = enum tLoadState = "Load state from file" tTransition = "Apply slot" tSaveState = "Save state to file" var timers: array[Timers, RunningStat] let cfg = getRuntimeConfig(conf.eth2Network) stateY = withTimerRet(timers[tLoadState]): newClone(readSszForkedHashedBeaconState( cfg, readAllBytes(conf.preState).tryGet())) blckX = readSszForkedSignedBeaconBlock( cfg, readAllBytes(conf.blck).tryGet()) flags = if not conf.verifyStateRoot: {skipStateRootValidation} else: {} var cache = StateCache() info = ForkedEpochInfo() let res = withTimerRet(timers[tTransition]): withBlck(blckX): state_transition( cfg, stateY[], blck, cache, info, flags, noRollback) if res.isErr(): error "State transition failed", error = res.error() quit 1 else: withTimer(timers[tSaveState]): saveSSZFile(conf.postState, stateY[]) if conf.printTimes: printTimers(false, timers) proc doSlots(conf: NcliConf) = type Timers = enum tLoadState = "Load state from file" tApplySlot = "Apply slot" tApplyEpochSlot = "Apply epoch slot" tSaveState = "Save state to file" var timers: array[Timers, RunningStat] let cfg = getRuntimeConfig(conf.eth2Network) stateY = withTimerRet(timers[tLoadState]): newClone(readSszForkedHashedBeaconState( cfg, readAllBytes(conf.preState2).tryGet())) var cache = StateCache() info = ForkedEpochInfo() for i in 0'u64..