Stable network simulation
This commit is contained in:
parent
1c30214e9a
commit
a586087472
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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].
|
||||||
|
|
|
@ -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?
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue