Stable network simulation

This commit is contained in:
Zahary Karadjov 2019-01-09 03:01:07 +02:00 committed by zah
parent 1c30214e9a
commit a586087472
7 changed files with 66 additions and 43 deletions

View File

@ -15,7 +15,7 @@ type
attachedValidators: ValidatorPool attachedValidators: ValidatorPool
attestationPool: AttestationPool attestationPool: AttestationPool
mainchainMonitor: MainchainMonitor mainchainMonitor: MainchainMonitor
lastScheduledCycle: int lastScheduledEpoch: uint64
headBlock: BeaconBlock headBlock: BeaconBlock
headBlockRoot: Eth2Digest headBlockRoot: Eth2Digest
@ -77,7 +77,7 @@ proc connectToNetwork(node: BeaconNode) {.async.} =
proc sync*(node: BeaconNode): Future[bool] {.async.} = proc sync*(node: BeaconNode): Future[bool] {.async.} =
let persistedState = node.db.lastFinalizedState() let persistedState = node.db.lastFinalizedState()
if persistedState.isNil or if persistedState.isNil or
persistedState[].slotDistanceFromNow() > WEAK_SUBJECTVITY_PERIOD: persistedState[].slotDistanceFromNow() > WEAK_SUBJECTVITY_PERIOD.int64:
if node.config.stateSnapshot.isSome: if node.config.stateSnapshot.isSome:
node.beaconState = node.config.stateSnapshot.get node.beaconState = node.config.stateSnapshot.get
else: else:
@ -86,7 +86,7 @@ proc sync*(node: BeaconNode): Future[bool] {.async.} =
node.beaconState = persistedState[] node.beaconState = persistedState[]
var targetSlot = toSlot timeSinceGenesis(node.beaconState) var targetSlot = toSlot timeSinceGenesis(node.beaconState)
while node.beaconState.finalized_slot.int < targetSlot: while node.beaconState.finalized_slot < targetSlot:
var (peer, changeLog) = await node.network.getValidatorChangeLog( var (peer, changeLog) = await node.network.getValidatorChangeLog(
node.beaconState.validator_registry_delta_chain_tip) node.beaconState.validator_registry_delta_chain_tip)
@ -218,11 +218,11 @@ proc proposeBlock(node: BeaconNode,
validator = validator.idx validator = validator.idx
proc scheduleBlockProposal(node: BeaconNode, proc scheduleBlockProposal(node: BeaconNode,
slot: int, slot: uint64,
validator: AttachedValidator) = validator: AttachedValidator) =
# TODO: # TODO:
# This function exists only to hide a bug with Nim's closures. # This function exists only to hide a bug with Nim's closures.
# If you inline it in `scheduleCycleActions`, you'll see the # If you inline it in `scheduleEpochActions`, you'll see the
# internal `doAssert` starting to fail. # internal `doAssert` starting to fail.
doAssert validator != nil doAssert validator != nil
@ -232,13 +232,13 @@ proc scheduleBlockProposal(node: BeaconNode,
proc scheduleAttestation(node: BeaconNode, proc scheduleAttestation(node: BeaconNode,
validator: AttachedValidator, validator: AttachedValidator,
slot: int, slot: uint64,
shard: uint64, shard: uint64,
committeeLen: int, committeeLen: int,
indexInCommittee: int) = indexInCommittee: int) =
# TODO: # TODO:
# This function exists only to hide a bug with Nim's closures. # This function exists only to hide a bug with Nim's closures.
# If you inline it in `scheduleCycleActions`, you'll see the # If you inline it in `scheduleEpochActions`, you'll see the
# internal `doAssert` starting to fail. # internal `doAssert` starting to fail.
doAssert validator != nil doAssert validator != nil
@ -247,7 +247,7 @@ proc scheduleAttestation(node: BeaconNode,
asyncCheck makeAttestation(node, validator, slot.uint64, asyncCheck makeAttestation(node, validator, slot.uint64,
shard, committeeLen, indexInCommittee) shard, committeeLen, indexInCommittee)
proc scheduleCycleActions(node: BeaconNode, cycleStart: int) = proc scheduleEpochActions(node: BeaconNode, epoch: uint64) =
## This schedules the required block proposals and ## This schedules the required block proposals and
## attestations from our attached validators. ## attestations from our attached validators.
doAssert node != nil doAssert node != nil
@ -256,14 +256,12 @@ proc scheduleCycleActions(node: BeaconNode, cycleStart: int) =
# see the comments in `get_beacon_proposer_index` # see the comments in `get_beacon_proposer_index`
var nextState = node.beaconState var nextState = node.beaconState
for i in 1 ..< EPOCH_LENGTH: for i in 1.uint64 ..< EPOCH_LENGTH:
# Schedule block proposals # Schedule block proposals
nextState.slot = node.beaconState.slot + i.uint64 let slot = epoch * EPOCH_LENGTH + i
nextState.slot = slot
let let proposerIdx = get_beacon_proposer_index(nextState, slot)
slot = cycleStart + i let validator = node.getAttachedValidator(proposerIdx)
proposerIdx = get_beacon_proposer_index(nextState, nextState.slot.uint64)
validator = node.getAttachedValidator(proposerIdx)
if validator != nil: if validator != nil:
# TODO: # TODO:
@ -273,7 +271,7 @@ proc scheduleCycleActions(node: BeaconNode, cycleStart: int) =
# Schedule attestations # Schedule attestations
let let
committeesIdx = get_shard_committees_index(nextState, nextState.slot.uint64) committeesIdx = get_shard_committees_index(nextState, slot)
for shard in node.beaconState.shard_committees_at_slots[committees_idx]: for shard in node.beaconState.shard_committees_at_slots[committees_idx]:
for i, validatorIdx in shard.committee: for i, validatorIdx in shard.committee:
@ -281,12 +279,12 @@ proc scheduleCycleActions(node: BeaconNode, cycleStart: int) =
if validator != nil: if validator != nil:
scheduleAttestation(node, validator, slot, shard.shard, shard.committee.len, i) scheduleAttestation(node, validator, slot, shard.shard, shard.committee.len, i)
node.lastScheduledCycle = cycleStart node.lastScheduledEpoch = epoch
let nextCycle = cycleStart + EPOCH_LENGTH let nextEpoch = epoch + 1
addTimer(node.beaconState.slotMiddle(nextCycle)) do (p: pointer): addTimer(node.beaconState.slotMiddle(nextEpoch * EPOCH_LENGTH)) do (p: pointer):
if node.lastScheduledCycle != nextCycle: if node.lastScheduledEpoch != nextEpoch:
node.scheduleCycleActions(nextCycle) node.scheduleEpochActions(nextEpoch)
proc processBlocks*(node: BeaconNode) = proc processBlocks*(node: BeaconNode) =
node.network.subscribe(topicBeaconBlocks) do (newBlock: BeaconBlock): node.network.subscribe(topicBeaconBlocks) do (newBlock: BeaconBlock):
@ -314,10 +312,9 @@ proc processBlocks*(node: BeaconNode) =
# 3. Peform block processing / state recalculation / etc # 3. Peform block processing / state recalculation / etc
# #
let slot = newBlock.slot.int let epoch = newBlock.slot.epoch
if slot mod EPOCH_LENGTH == 0: if epoch != node.lastScheduledEpoch:
node.scheduleCycleActions(slot) node.scheduleEpochActions(epoch)
node.attestationPool.discardHistoryToSlot(slot)
node.network.subscribe(topicAttestations) do (a: Attestation): node.network.subscribe(topicAttestations) do (a: Attestation):
info "Attestation received", slot = a.data.slot, info "Attestation received", slot = a.data.slot,
@ -327,8 +324,8 @@ proc processBlocks*(node: BeaconNode) =
node.attestationPool.add(a, node.beaconState) node.attestationPool.add(a, node.beaconState)
dynamicLogScope(node = node.config.tcpPort - 50000): dynamicLogScope(node = node.config.tcpPort - 50000):
let cycleStart = node.beaconState.slot.int let epoch = node.beaconState.slot.epoch
node.scheduleCycleActions(cycleStart) node.scheduleEpochActions(epoch)
runForever() runForever()

View File

@ -17,6 +17,14 @@ type
attestations: Deque[array[SHARD_COUNT, Option[Attestation]]] attestations: Deque[array[SHARD_COUNT, Option[Attestation]]]
startingSlot: int startingSlot: int
# TODO:
# The compilicated Deque above is not needed.
#
# In fact, we can use a simple array with length SHARD_COUNT because
# in each epoch, each shard is going to receive attestations exactly once.
# Once the epoch is over, we can discard all attestations and start all
# over again (no need for `discardHistoryToSlot` too).
proc init*(T: type AttestationPool, startingSlot: int): T = proc init*(T: type AttestationPool, startingSlot: int): T =
result.attestations = initDeque[array[SHARD_COUNT, Option[Attestation]]]() result.attestations = initDeque[array[SHARD_COUNT, Option[Attestation]]]()
result.startingSlot = startingSlot result.startingSlot = startingSlot

View File

@ -94,7 +94,7 @@ const
ZERO_HASH* = Eth2Digest() ZERO_HASH* = Eth2Digest()
# Time constants # Time constants
SLOT_DURATION* = 6 ## \ SLOT_DURATION* = 6'u64 ## \
## TODO consistent time unit across projects, similar to C++ chrono? ## TODO consistent time unit across projects, similar to C++ chrono?
MIN_ATTESTATION_INCLUSION_DELAY* = 2'u64^2 ##\ MIN_ATTESTATION_INCLUSION_DELAY* = 2'u64^2 ##\
@ -415,6 +415,9 @@ type
DOMAIN_PROPOSAL = 2 DOMAIN_PROPOSAL = 2
DOMAIN_EXIT = 3 DOMAIN_EXIT = 3
template epoch*(slot: int|uint64): auto =
slot div EPOCH_LENGTH
when true: when true:
# TODO: Remove these once RLP serialization is no longer used # TODO: Remove these once RLP serialization is no longer used
import nimcrypto, rlp, json_serialization import nimcrypto, rlp, json_serialization

View File

@ -15,20 +15,20 @@ proc timeSinceGenesis*(s: BeaconState): Timestamp =
Timestamp(int64(fastEpochTime() - s.genesis_time * 1000) - Timestamp(int64(fastEpochTime() - s.genesis_time * 1000) -
detectedClockDrift) detectedClockDrift)
template toSlot*(t: Timestamp): int = template toSlot*(t: Timestamp): uint64 =
int(t div uint64(SLOT_DURATION * 1000)) t div (SLOT_DURATION * 1000)
template slotStart*(s: BeaconState, slot: int): Timestamp = template slotStart*(s: BeaconState, slot: uint64): Timestamp =
(s.genesis_time + uint64(slot * SLOT_DURATION)) * 1000 (s.genesis_time + (slot * SLOT_DURATION)) * 1000
template slotMiddle*(s: BeaconState, slot: int): Timestamp = template slotMiddle*(s: BeaconState, slot: uint64): Timestamp =
s.slotStart(slot) + SLOT_DURATION * 500 s.slotStart(slot) + SLOT_DURATION * 500
template slotEnd*(s: BeaconState, slot: int): Timestamp = template slotEnd*(s: BeaconState, slot: uint64): Timestamp =
s.slotStart(slot + 1) s.slotStart(slot + 1)
proc randomTimeInSlot*(s: BeaconState, proc randomTimeInSlot*(s: BeaconState,
slot: Natural, slot: uint64,
interval: HSlice[float, float]): Timestamp = interval: HSlice[float, float]): Timestamp =
## Returns a random moment within the slot. ## Returns a random moment within the slot.
## The interval must be a sub-interval of [0..1]. ## The interval must be a sub-interval of [0..1].

View File

@ -3,7 +3,7 @@ import
spec/[datatypes, crypto, digest, beaconstate], beacon_chain_db, conf spec/[datatypes, crypto, digest, beaconstate], beacon_chain_db, conf
const const
WEAK_SUBJECTVITY_PERIOD* = 4 * 30 * 24 * 60 * 60 div SLOT_DURATION WEAK_SUBJECTVITY_PERIOD* = uint64(4 * 30 * 24 * 60 * 60) div SLOT_DURATION
# TODO: This needs revisiting. # TODO: This needs revisiting.
# Why was the validator WITHDRAWAL_PERIOD altered in the spec? # Why was the validator WITHDRAWAL_PERIOD altered in the spec?

View File

@ -50,7 +50,7 @@ proc signBlockProposal*(v: AttachedValidator,
let proposalRoot = hash_tree_root_final(proposal) let proposalRoot = hash_tree_root_final(proposal)
# TODO: Should we use proposalRoot as data, or digest in regards to signature? # TODO: Should we use proposalRoot as data, or digest in regards to signature?
return signMessage(v.privKey, proposalRoot.data) result = signMessage(v.privKey, proposalRoot.data)
else: else:
# TODO: # TODO:
# send RPC # send RPC
@ -64,7 +64,8 @@ proc signAttestation*(v: AttachedValidator,
let attestationRoot = hash_tree_root_final(attestation) let attestationRoot = hash_tree_root_final(attestation)
# TODO: Avoid the allocations belows # TODO: Avoid the allocations belows
return signMessage(v.privKey, @(attestationRoot.data) & @[0'u8]) var dataToSign = @(attestationRoot.data) & @[0'u8]
result = signMessage(v.privKey, dataToSign)
else: else:
# TODO: # TODO:
# send RPC # send RPC

View File

@ -13,15 +13,20 @@ SNAPSHOT_FILE="$SIMULATION_DIR/state_snapshot.json"
cd $(git rev-parse --show-toplevel) cd $(git rev-parse --show-toplevel)
ROOT_DIR=$PWD ROOT_DIR=$PWD
nim c beacon_chain/validator_keygen BEACON_NODE_BIN=$BUILD_OUTPUTS_DIR/beacon_node
nim c beacon_chain/beacon_node VALIDATOR_KEYGEN_BIN=$BUILD_OUTPUTS_DIR/validator_keygen
if [[ ! -z "$SKIP_BUILDS" ]]; then
nim c -o:"$VALIDATOR_KEYGEN_BIN" beacon_chain/validator_keygen
nim c -o:"$BEACON_NODE_BIN" beacon_chain/beacon_node
fi
if [ ! -f $STARTUP_FILE ]; then if [ ! -f $STARTUP_FILE ]; then
beacon_chain/validator_keygen $NUMBER_OF_VALIDATORS "$SIMULATION_DIR" $VALIDATOR_KEYGEN_BIN $NUMBER_OF_VALIDATORS "$SIMULATION_DIR"
fi fi
if [ ! -f $SNAPSHOT_FILE ]; then if [ ! -f $SNAPSHOT_FILE ]; then
beacon_chain/beacon_node createChain \ $BEACON_NODE_BIN createChain \
--chainStartupData:$STARTUP_FILE \ --chainStartupData:$STARTUP_FILE \
--out:$SNAPSHOT_FILE --out:$SNAPSHOT_FILE
fi fi
@ -47,7 +52,7 @@ for i in $(seq 0 9); do
DATA_DIR=$SIMULATION_DIR/data-$i DATA_DIR=$SIMULATION_DIR/data-$i
beacon_chain/beacon_node \ $BEACON_NODE_BIN \
--dataDir:"$DATA_DIR" \ --dataDir:"$DATA_DIR" \
--validator:"$SIMULATION_DIR/validator-${i}1.json" \ --validator:"$SIMULATION_DIR/validator-${i}1.json" \
--validator:"$SIMULATION_DIR/validator-${i}2.json" \ --validator:"$SIMULATION_DIR/validator-${i}2.json" \
@ -64,3 +69,12 @@ for i in $(seq 0 9); do
$BOOTSTRAP_NODES_FLAG & $BOOTSTRAP_NODES_FLAG &
done done
trap ctrl_c INT
function ctrl_c() {
killall beacon_node
exit 0
}
sleep 100000