2019-12-23 12:39:27 +00:00
|
|
|
# beacon_chain
|
2021-02-15 16:40:00 +00:00
|
|
|
# Copyright (c) 2019-2021 Status Research & Development GmbH
|
2019-12-23 12:39:27 +00:00
|
|
|
# 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.
|
|
|
|
|
2020-05-01 15:51:24 +00:00
|
|
|
# `state_sim` runs the state transition function in isolation, creating blocks
|
|
|
|
# and attesting to them as if the network was running as a whole.
|
|
|
|
|
2018-12-21 22:37:46 +00:00
|
|
|
import
|
2020-05-01 15:51:24 +00:00
|
|
|
confutils, stats, times,
|
2019-07-03 07:35:05 +00:00
|
|
|
strformat,
|
2019-06-29 09:17:24 +00:00
|
|
|
options, sequtils, random, tables,
|
2020-08-01 18:24:25 +00:00
|
|
|
../tests/testblockutil,
|
2021-08-12 13:08:20 +00:00
|
|
|
../beacon_chain/spec/datatypes/phase0,
|
|
|
|
../beacon_chain/spec/[beaconstate, forks, helpers],
|
2020-05-01 15:51:24 +00:00
|
|
|
./simutils
|
2018-12-21 22:37:46 +00:00
|
|
|
|
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"
|
2019-11-25 06:31:37 +00:00
|
|
|
tShuffle = "Retrieve committee once using get_beacon_committee"
|
2019-03-04 13:04:26 +00:00
|
|
|
tAttest = "Combine committee attestations"
|
|
|
|
|
2021-08-25 14:51:52 +00:00
|
|
|
func jsonName(prefix, slot: auto): string =
|
2020-01-22 15:36:16 +00:00
|
|
|
fmt"{prefix:04}-{shortLog(slot):08}.json"
|
|
|
|
|
|
|
|
proc writeJson*(fn, v: auto) =
|
2018-12-21 22:37:46 +00:00
|
|
|
var f: File
|
|
|
|
defer: close(f)
|
2020-01-22 15:36:16 +00:00
|
|
|
Json.saveFile(fn, v, pretty = true)
|
2018-12-21 22:37:46 +00:00
|
|
|
|
2021-03-17 13:35:59 +00:00
|
|
|
cli do(slots = SLOTS_PER_EPOCH * 5,
|
|
|
|
validators = SLOTS_PER_EPOCH * 400, # One per shard is minimum
|
2019-02-20 01:33:58 +00:00
|
|
|
json_interval = SLOTS_PER_EPOCH,
|
2020-01-22 15:36:16 +00:00
|
|
|
write_last_json = false,
|
2020-07-13 14:44:58 +00:00
|
|
|
prefix: int = 0,
|
2020-10-13 17:21:25 +00:00
|
|
|
attesterRatio {.desc: "ratio of validators that attest in each round"} = 0.82,
|
2019-04-05 14:18:13 +00:00
|
|
|
validate = true):
|
2018-12-21 22:37:46 +00:00
|
|
|
let
|
2020-03-05 12:52:10 +00:00
|
|
|
flags = if validate: {} else: {skipBlsValidation}
|
2021-06-11 17:51:46 +00:00
|
|
|
(hashedState, _) = loadGenesis(validators, validate)
|
|
|
|
genesisBlock = get_initial_beacon_block(hashedState.data)
|
|
|
|
state = (ref ForkedHashedBeaconState)(
|
2021-10-18 16:37:27 +00:00
|
|
|
phase0Data: hashedState[], kind: BeaconStateFork.Phase0)
|
2018-12-21 22:37:46 +00:00
|
|
|
|
2020-01-22 15:36:16 +00:00
|
|
|
echo "Starting simulation..."
|
|
|
|
|
2018-12-21 22:37:46 +00:00
|
|
|
var
|
2019-06-29 09:17:24 +00:00
|
|
|
attestations = initTable[Slot, seq[Attestation]]()
|
2019-12-16 18:08:50 +00:00
|
|
|
latest_block_root = hash_tree_root(genesisBlock.message)
|
2019-03-04 13:04:26 +00:00
|
|
|
timers: array[Timers, RunningStat]
|
|
|
|
attesters: RunningStat
|
2020-05-03 17:44:04 +00:00
|
|
|
r = initRand(1)
|
2021-08-12 13:08:20 +00:00
|
|
|
signedBlock: phase0.SignedBeaconBlock
|
2020-07-15 10:44:18 +00:00
|
|
|
cache = StateCache()
|
2018-12-21 22:37:46 +00:00
|
|
|
|
2020-01-22 15:36:16 +00:00
|
|
|
proc maybeWrite(last: bool) =
|
|
|
|
if write_last_json:
|
2021-06-11 17:51:46 +00:00
|
|
|
if getStateField(state[], slot) mod json_interval.uint64 == 0:
|
2020-01-22 15:36:16 +00:00
|
|
|
write(stdout, ":")
|
|
|
|
else:
|
|
|
|
write(stdout, ".")
|
|
|
|
|
|
|
|
if last:
|
2021-10-18 16:37:27 +00:00
|
|
|
writeJson("state.json", state[].phase0Data)
|
2018-12-21 22:37:46 +00:00
|
|
|
else:
|
2021-06-11 17:51:46 +00:00
|
|
|
if getStateField(state[], slot) mod json_interval.uint64 == 0:
|
2021-10-18 16:37:27 +00:00
|
|
|
writeJson(jsonName(prefix, getStateField(state[], slot)), state[].phase0Data.data)
|
2020-01-22 15:36:16 +00:00
|
|
|
write(stdout, ":")
|
|
|
|
else:
|
|
|
|
write(stdout, ".")
|
2018-12-21 22:37:46 +00:00
|
|
|
|
2019-11-27 22:48:12 +00:00
|
|
|
# TODO doAssert against this up-front
|
|
|
|
# indexed attestation: validator index beyond max validators per committee
|
|
|
|
# len(indices) <= MAX_VALIDATORS_PER_COMMITTEE
|
|
|
|
|
2019-03-04 13:04:26 +00:00
|
|
|
for i in 0..<slots:
|
2020-01-22 15:36:16 +00:00
|
|
|
maybeWrite(false)
|
2021-10-18 16:37:27 +00:00
|
|
|
verifyConsensus(state[].phase0Data.data, attesterRatio)
|
2019-03-04 13:04:26 +00:00
|
|
|
|
2018-12-27 23:40:22 +00:00
|
|
|
let
|
2021-06-11 17:51:46 +00:00
|
|
|
attestations_idx = getStateField(state[], slot)
|
2020-03-19 23:48:03 +00:00
|
|
|
blockAttestations = attestations.getOrDefault(attestations_idx)
|
2018-12-27 23:40:22 +00:00
|
|
|
|
2019-06-29 09:17:24 +00:00
|
|
|
attestations.del attestations_idx
|
2020-07-26 18:55:48 +00:00
|
|
|
doAssert attestations.lenu64 <=
|
|
|
|
SLOTS_PER_EPOCH + MIN_ATTESTATION_INCLUSION_DELAY
|
2018-12-27 23:40:22 +00:00
|
|
|
|
2019-03-04 13:04:26 +00:00
|
|
|
let t =
|
2021-06-11 17:51:46 +00:00
|
|
|
if (getStateField(state[], slot) > GENESIS_SLOT and
|
|
|
|
(getStateField(state[], slot) + 1).isEpoch): tEpoch
|
2019-03-04 13:04:26 +00:00
|
|
|
else: tBlock
|
|
|
|
|
|
|
|
withTimer(timers[t]):
|
2020-03-19 23:48:03 +00:00
|
|
|
signedBlock = addTestBlock(
|
2020-06-04 12:03:16 +00:00
|
|
|
state[], latest_block_root, cache, attestations = blockAttestations,
|
2021-10-18 16:37:27 +00:00
|
|
|
flags = flags).phase0Data
|
2019-03-04 13:04:26 +00:00
|
|
|
latest_block_root = withTimerRet(timers[tHashBlock]):
|
2020-02-29 15:15:44 +00:00
|
|
|
hash_tree_root(signedBlock.message)
|
2020-07-16 13:16:51 +00:00
|
|
|
signedBlock.root = latest_block_root
|
2018-12-27 23:40:22 +00:00
|
|
|
|
|
|
|
if attesterRatio > 0.0:
|
|
|
|
# attesterRatio is the fraction of attesters that actually do their
|
2019-03-11 15:33:24 +00:00
|
|
|
# work for every slot - we'll randomize it deterministically to give
|
2018-12-27 23:40:22 +00:00
|
|
|
# some variation
|
2019-06-24 09:21:56 +00:00
|
|
|
let
|
2021-06-11 17:51:46 +00:00
|
|
|
target_slot = getStateField(state[], slot) + MIN_ATTESTATION_INCLUSION_DELAY - 1
|
2020-07-27 10:59:57 +00:00
|
|
|
committees_per_slot =
|
2021-06-11 17:51:46 +00:00
|
|
|
get_committee_count_per_slot(state[], target_slot.epoch, cache)
|
2021-05-21 09:23:28 +00:00
|
|
|
|
2020-06-09 13:49:46 +00:00
|
|
|
let
|
2019-06-24 09:21:56 +00:00
|
|
|
scass = withTimerRet(timers[tShuffle]):
|
|
|
|
mapIt(
|
2020-07-23 17:01:07 +00:00
|
|
|
0 ..< committees_per_slot.int,
|
2021-06-11 17:51:46 +00:00
|
|
|
get_beacon_committee(state[], target_slot, it.CommitteeIndex, cache))
|
2021-05-21 09:23:28 +00:00
|
|
|
|
2019-11-27 22:48:12 +00:00
|
|
|
for i, scas in scass:
|
2018-12-27 23:40:22 +00:00
|
|
|
var
|
|
|
|
attestation: Attestation
|
|
|
|
first = true
|
|
|
|
|
2019-06-24 09:21:56 +00:00
|
|
|
attesters.push scas.len()
|
2019-03-04 13:04:26 +00:00
|
|
|
|
|
|
|
withTimer(timers[tAttest]):
|
2020-08-15 17:33:58 +00:00
|
|
|
var agg {.noInit.}: AggregateSignature
|
2019-06-24 09:21:56 +00:00
|
|
|
for v in scas:
|
2019-03-04 13:04:26 +00:00
|
|
|
if (rand(r, high(int)).float * attesterRatio).int <= high(int):
|
|
|
|
if first:
|
2019-11-15 22:37:39 +00:00
|
|
|
attestation =
|
2021-06-11 17:51:46 +00:00
|
|
|
makeAttestation(state[], latest_block_root, scas, target_slot,
|
2020-11-04 21:52:47 +00:00
|
|
|
i.CommitteeIndex, v, cache, flags)
|
2021-04-26 20:39:44 +00:00
|
|
|
agg.init(attestation.signature.load.get())
|
2019-03-04 13:04:26 +00:00
|
|
|
first = false
|
|
|
|
else:
|
2020-08-15 17:33:58 +00:00
|
|
|
let att2 =
|
2021-06-11 17:51:46 +00:00
|
|
|
makeAttestation(state[], latest_block_root, scas, target_slot,
|
2020-11-04 21:52:47 +00:00
|
|
|
i.CommitteeIndex, v, cache, flags)
|
2020-08-15 17:33:58 +00:00
|
|
|
if not att2.aggregation_bits.overlaps(attestation.aggregation_bits):
|
2021-04-12 20:25:09 +00:00
|
|
|
attestation.aggregation_bits.incl(att2.aggregation_bits)
|
2020-08-15 17:33:58 +00:00
|
|
|
if skipBlsValidation notin flags:
|
2021-04-26 20:39:44 +00:00
|
|
|
agg.aggregate(att2.signature.load.get())
|
|
|
|
attestation.signature = agg.finish().toValidatorSig()
|
2018-12-27 23:40:22 +00:00
|
|
|
|
|
|
|
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!
|
2019-06-29 09:17:24 +00:00
|
|
|
let target_slot =
|
2019-11-12 05:35:52 +00:00
|
|
|
attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY - 1
|
2019-06-29 09:17:24 +00:00
|
|
|
|
2019-11-27 22:48:12 +00:00
|
|
|
doAssert target_slot > attestations_idx
|
|
|
|
var target_slot_attestations =
|
|
|
|
getOrDefault(attestations, target_slot)
|
|
|
|
target_slot_attestations.add attestation
|
|
|
|
attestations[target_slot] = target_slot_attestations
|
2018-12-21 22:37:46 +00:00
|
|
|
|
|
|
|
flushFile(stdout)
|
|
|
|
|
2021-06-11 17:51:46 +00:00
|
|
|
if getStateField(state[], slot).isEpoch:
|
|
|
|
echo &" slot: {shortLog(getStateField(state[], slot))} ",
|
|
|
|
&"epoch: {shortLog(state[].get_current_epoch())}"
|
2019-03-04 13:04:26 +00:00
|
|
|
|
|
|
|
|
2020-01-22 15:36:16 +00:00
|
|
|
maybeWrite(true) # catch that last state as well..
|
|
|
|
|
|
|
|
echo "Done!"
|
2018-12-21 22:37:46 +00:00
|
|
|
|
2021-10-18 16:37:27 +00:00
|
|
|
printTimers(state[].phase0Data.data, attesters, validate, timers)
|