nimbus-eth2/research/state_sim.nim

160 lines
5.1 KiB
Nim
Raw Normal View History

import
2019-03-04 13:04:26 +00:00
confutils, stats, times,
json, strformat,
options, sequtils, random,
../tests/[testutil],
../beacon_chain/spec/[beaconstate, crypto, datatypes, digest, helpers, validator],
../beacon_chain/[attestation_pool, extras, ssz, state_transition, fork_choice]
2019-03-04 13:04:26 +00:00
proc `%`(v: uint64): JsonNode =
if v > uint64(high(BiggestInt)): newJString($v) else: newJInt(BiggestInt(v))
proc `%`(v: Eth2Digest): JsonNode = newJString($v)
proc `%`(v: ValidatorSig|ValidatorPubKey): JsonNode = newJString($v)
2019-03-04 13:04:26 +00:00
type Timers = enum
tBlock = "Process non-epoch slot with block"
2019-03-23 04:55:41 +00:00
tEpoch = "Process epoch slot with block"
2019-03-04 13:04:26 +00:00
tHashBlock = "Tree-hash block"
tShuffle = "Retrieve committee once using get_crosslink_committee"
2019-03-04 13:04:26 +00:00
tAttest = "Combine committee attestations"
template withTimer(stats: var RunningStat, body: untyped) =
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
proc `%`*(x: Slot): JsonNode {.borrow.}
proc `%`*(x: Epoch): JsonNode {.borrow.}
proc writeJson*(prefix, slot, v: auto) =
var f: File
defer: close(f)
2019-03-04 13:04:26 +00:00
discard open(f, fmt"{prefix:04}-{humaneSlotNum(slot):08}.json", fmWrite)
write(f, pretty(%*(v)))
cli do(slots = 1945,
validators = SLOTS_PER_EPOCH * 8, # One per shard is minimum
json_interval = SLOTS_PER_EPOCH,
prefix = 0,
attesterRatio {.desc: "ratio of validators that attest in each round"} = 0.9,
validate = true):
let
flags = if validate: {} else: {skipValidation}
genesisState = get_genesis_beacon_state(
2019-01-18 00:14:22 +00:00
makeInitialDeposits(validators, flags), 0, Eth1Data(), flags)
genesisBlock = get_initial_beacon_block(genesisState)
var
attestations: array[MIN_ATTESTATION_INCLUSION_DELAY, seq[Attestation]]
state = genesisState
latest_block_root = signing_root(genesisBlock)
2019-03-04 13:04:26 +00:00
timers: array[Timers, RunningStat]
attesters: RunningStat
r: Rand
blck: BeaconBlock
cache = get_empty_per_epoch_cache()
2019-03-04 13:04:26 +00:00
proc maybeWrite() =
if state.slot mod json_interval.uint64 == 0:
writeJson(prefix, state.slot, state)
write(stdout, ":")
else:
write(stdout, ".")
2019-03-04 13:04:26 +00:00
for i in 0..<slots:
maybeWrite()
let
attestations_idx = state.slot mod MIN_ATTESTATION_INCLUSION_DELAY
2019-03-04 13:04:26 +00:00
body = BeaconBlockBody(attestations: attestations[attestations_idx])
attestations[attestations_idx] = @[]
2019-03-04 13:04:26 +00:00
let t =
2019-03-23 04:52:45 +00:00
if (state.slot > GENESIS_SLOT and
(state.slot + 1) mod SLOTS_PER_EPOCH == 0): tEpoch
2019-03-04 13:04:26 +00:00
else: tBlock
withTimer(timers[t]):
blck = addBlock(state, latest_block_root, body, flags)
latest_block_root = withTimerRet(timers[tHashBlock]):
signing_root(blck)
if attesterRatio > 0.0:
# attesterRatio is the fraction of attesters that actually do their
# work for every slot - we'll randomize it deterministically to give
# some variation
let
epoch = slot_to_epoch(state.slot)
scass = withTimerRet(timers[tShuffle]):
mapIt(
0'u64 .. (get_epoch_committee_count(state, epoch) - 1),
get_crosslink_committee(state, epoch,
(it + get_epoch_start_shard(state, epoch)) mod SHARD_COUNT,
cache))
for scas in scass:
var
attestation: Attestation
first = true
attesters.push scas.len()
2019-03-04 13:04:26 +00:00
withTimer(timers[tAttest]):
for v in scas:
2019-03-04 13:04:26 +00:00
if (rand(r, high(int)).float * attesterRatio).int <= high(int):
if first:
attestation = makeAttestation(state, latest_block_root, v, flags)
2019-03-04 13:04:26 +00:00
first = false
else:
attestation.combine(
makeAttestation(state, latest_block_root, v, flags), flags)
if not first:
# add the attestation if any of the validators attested, as given
# by the randomness. We have to delay when the attestation is
# actually added to the block per the attestation delay rule!
attestations[
(state.slot + MIN_ATTESTATION_INCLUSION_DELAY - 1) mod
MIN_ATTESTATION_INCLUSION_DELAY].add attestation
flushFile(stdout)
2019-03-04 13:04:26 +00:00
if (state.slot) mod SLOTS_PER_EPOCH == 0:
echo &" slot: {humaneSlotNum(state.slot)} ",
&"epoch: {humaneEpochNum(state.slot.slot_to_epoch)}"
maybeWrite() # catch that last state as well..
echo "done!"
2019-03-04 13:04:26 +00:00
echo "Validators: ", validators, ", epoch length: ", SLOTS_PER_EPOCH
echo "Validators per attestation (mean): ", attesters.mean
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"
2019-03-04 13:04:26 +00:00
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