nimbus-eth2/research/simutils.nim
Jacek Sieka a7a65bce42
disentangle eth2 types from the ssz library (#2785)
* reorganize ssz dependencies

This PR continues the work in
https://github.com/status-im/nimbus-eth2/pull/2646,
https://github.com/status-im/nimbus-eth2/pull/2779 as well as past
issues with serialization and type, to disentangle SSZ from eth2 and at
the same time simplify imports and exports with a structured approach.

The principal idea here is that when a library wants to introduce SSZ
support, they do so via 3 files:

* `ssz_codecs` which imports and reexports `codecs` - this covers the
basic byte conversions and ensures no overloads get lost
* `xxx_merkleization` imports and exports `merkleization` to specialize
and get access to `hash_tree_root` and friends
* `xxx_ssz_serialization` imports and exports `ssz_serialization` to
specialize ssz for a specific library

Those that need to interact with SSZ always import the `xxx_` versions
of the modules and never `ssz` itself so as to keep imports simple and
safe.

This is similar to how the REST / JSON-RPC serializers are structured in
that someone wanting to serialize spec types to REST-JSON will import
`eth2_rest_serialization` and nothing else.

* split up ssz into a core library that is independendent of eth2 types
* rename `bytes_reader` to `codec` to highlight that it contains coding
and decoding of bytes and native ssz types
* remove tricky List init overload that causes compile issues
* get rid of top-level ssz import
* reenable merkleization tests
* move some "standard" json serializers to spec
* remove `ValidatorIndex` serialization for now
* remove test_ssz_merkleization
* add tests for over/underlong byte sequences
* fix broken seq[byte] test - seq[byte] is not an SSZ type

There are a few things this PR doesn't solve:

* like #2646 this PR is weak on how to handle root and other
dontSerialize fields that "sometimes" should be computed - the same
problem appears in REST / JSON-RPC etc

* Fix a build problem on macOS

* Another way to fix the macOS builds

Co-authored-by: Zahary Karadjov <zahary@gmail.com>
2021-08-18 20:57:58 +02:00

151 lines
5.1 KiB
Nim

import
stats, os, strformat, times,
../tests/testblockutil,
../beacon_chain/beacon_chain_db,
../beacon_chain/spec/datatypes/[phase0, altair],
../beacon_chain/spec/[beaconstate, forks, helpers],
../beacon_chain/consensus_object_pools/[blockchain_dag, block_pools_types],
../beacon_chain/eth1/eth1_monitor
template withTimer*(stats: var RunningStat, body: untyped) =
# TODO unify timing somehow
let start = cpuTime()
block:
body
let stop = cpuTime()
stats.push stop - start
template withTimerRet*(stats: var RunningStat, body: untyped): untyped =
let start = cpuTime()
let tmp = block:
body
let stop = cpuTime()
stats.push stop - start
tmp
func verifyConsensus*(state: phase0.BeaconState, attesterRatio: auto) =
if attesterRatio < 0.63:
doAssert state.current_justified_checkpoint.epoch == 0
doAssert state.finalized_checkpoint.epoch == 0
# Quorum is 2/3 of validators, and at low numbers, quantization effects
# can dominate, so allow for play above/below attesterRatio of 2/3.
if attesterRatio < 0.72:
return
let current_epoch = get_current_epoch(state)
if current_epoch >= 3:
doAssert state.current_justified_checkpoint.epoch + 1 >= current_epoch
if current_epoch >= 4:
doAssert state.finalized_checkpoint.epoch + 2 >= current_epoch
func verifyConsensus*(state: ForkedHashedBeaconState, attesterRatio: auto) =
if attesterRatio < 0.63:
doAssert getStateField(state, current_justified_checkpoint).epoch == 0
doAssert getStateField(state, finalized_checkpoint).epoch == 0
# Quorum is 2/3 of validators, and at low numbers, quantization effects
# can dominate, so allow for play above/below attesterRatio of 2/3.
if attesterRatio < 0.72:
return
let current_epoch = get_current_epoch(state)
if current_epoch >= 3:
doAssert getStateField(
state, current_justified_checkpoint).epoch + 1 >= current_epoch
if current_epoch >= 4:
doAssert getStateField(
state, finalized_checkpoint).epoch + 2 >= current_epoch
proc loadGenesis*(validators: Natural, validate: bool):
(ref phase0.HashedBeaconState, DepositContractSnapshot) =
let
genesisFn =
&"genesis_{const_preset}_{validators}_{SPEC_VERSION}.ssz"
contractSnapshotFn =
&"deposit_contract_snapshot_{const_preset}_{validators}_{SPEC_VERSION}.ssz"
res = (ref phase0.HashedBeaconState)()
if fileExists(genesisFn) and fileExists(contractSnapshotFn):
res.data = SSZ.loadFile(genesisFn, phase0.BeaconState)
res.root = hash_tree_root(res.data)
if res.data.slot != GENESIS_SLOT:
echo "Can only start from genesis state"
quit 1
if res.data.validators.len != validators:
echo &"Supplied genesis file has {res.data.validators.len} validators, while {validators} where requested, running anyway"
echo &"Loaded {genesisFn}..."
# TODO check that the private keys are interop keys
let contractSnapshot = SSZ.loadFile(contractSnapshotFn,
DepositContractSnapshot)
(res, contractSnapshot)
else:
echo "Genesis file not found, making one up (use nimbus_beacon_node createTestnet to make one)"
echo "Preparing validators..."
let
flags = if validate: {} else: {skipBlsValidation}
deposits = makeInitialDeposits(validators.uint64, flags)
echo "Generating Genesis..."
var merkleizer = init DepositsMerkleizer
for d in deposits:
merkleizer.addChunk hash_tree_root(d).data
let contractSnapshot = DepositContractSnapshot(
depositContractState: merkleizer.toDepositContractState)
res.data = initialize_beacon_state_from_eth1(
defaultRuntimeConfig,
Eth2Digest(),
0,
deposits,
flags)[]
res.root = hash_tree_root(res.data)
echo &"Saving to {genesisFn}..."
SSZ.saveFile(genesisFn, res.data)
echo &"Saving to {contractSnapshotFn}..."
SSZ.saveFile(contractSnapshotFn, contractSnapshot)
(res, contractSnapshot)
proc printTimers*[Timers: enum](
validate: bool,
timers: array[Timers, RunningStat]
) =
proc fmtTime(t: float): string = &"{t * 1000 :>12.3f}, "
echo "All time are ms"
echo &"{\"Average\" :>12}, {\"StdDev\" :>12}, {\"Min\" :>12}, " &
&"{\"Max\" :>12}, {\"Samples\" :>12}, {\"Test\" :>12}"
if not validate:
echo "Validation is turned off meaning that no BLS operations are performed"
for t in Timers:
echo fmtTime(timers[t].mean), fmtTime(timers[t].standardDeviationS),
fmtTime(timers[t].min), fmtTime(timers[t].max), &"{timers[t].n :>12}, ",
$t
proc printTimers*[Timers: enum](
state: phase0.BeaconState, attesters: RunningStat, validate: bool,
timers: array[Timers, RunningStat]) =
echo "Validators: ", state.validators.len, ", epoch length: ", SLOTS_PER_EPOCH
echo "Validators per attestation (mean): ", attesters.mean
printTimers(validate, timers)
proc printTimers*[Timers: enum](
state: ForkedHashedBeaconState, attesters: RunningStat, validate: bool,
timers: array[Timers, RunningStat]) =
echo "Validators: ", getStateField(state, validators).len, ", epoch length: ", SLOTS_PER_EPOCH
echo "Validators per attestation (mean): ", attesters.mean
printTimers(validate, timers)