Fixed epoch actions (#105)
* Fixed epoch actions
* debugging asserts
* Revert "debugging asserts"
This reverts commit 9a11f5a7d8
.
This commit is contained in:
parent
433e2f78c2
commit
56a48cc60f
|
@ -30,8 +30,6 @@ const
|
||||||
|
|
||||||
stateStoragePeriod = EPOCH_LENGTH * 10 # Save states once per this number of slots. TODO: Find a good number.
|
stateStoragePeriod = EPOCH_LENGTH * 10 # Save states once per this number of slots. TODO: Find a good number.
|
||||||
|
|
||||||
func humaneSlotNum(s: SlotNumber): SlotNumber =
|
|
||||||
s - GENESIS_SLOT
|
|
||||||
|
|
||||||
func shortHash(x: auto): string =
|
func shortHash(x: auto): string =
|
||||||
($x)[0..7]
|
($x)[0..7]
|
||||||
|
@ -51,7 +49,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): T =
|
||||||
result.config = conf
|
result.config = conf
|
||||||
|
|
||||||
result.attachedValidators = ValidatorPool.init
|
result.attachedValidators = ValidatorPool.init
|
||||||
init result.attestationPool, 0
|
init result.attestationPool, GENESIS_SLOT
|
||||||
init result.mainchainMonitor, "", Port(0) # TODO: specify geth address and port
|
init result.mainchainMonitor, "", Port(0) # TODO: specify geth address and port
|
||||||
|
|
||||||
let trieDB = trieDB newChainDb(string conf.dataDir)
|
let trieDB = trieDB newChainDb(string conf.dataDir)
|
||||||
|
@ -236,7 +234,8 @@ proc proposeBlock(node: BeaconNode,
|
||||||
|
|
||||||
info "Block proposed", slot = humaneSlotNum(slot),
|
info "Block proposed", slot = humaneSlotNum(slot),
|
||||||
stateRoot = shortHash(newBlock.state_root),
|
stateRoot = shortHash(newBlock.state_root),
|
||||||
validator = shortValidatorKey(node, validator.idx)
|
validator = shortValidatorKey(node, validator.idx),
|
||||||
|
idx = validator.idx
|
||||||
|
|
||||||
proc scheduleBlockProposal(node: BeaconNode,
|
proc scheduleBlockProposal(node: BeaconNode,
|
||||||
slot: uint64,
|
slot: uint64,
|
||||||
|
@ -251,6 +250,7 @@ proc scheduleBlockProposal(node: BeaconNode,
|
||||||
|
|
||||||
info "Scheduling block proposal",
|
info "Scheduling block proposal",
|
||||||
validator = shortValidatorKey(node, validator.idx),
|
validator = shortValidatorKey(node, validator.idx),
|
||||||
|
idx = validator.idx,
|
||||||
slot = humaneSlotNum(slot),
|
slot = humaneSlotNum(slot),
|
||||||
fromNow = (at - fastEpochTime()) div 1000
|
fromNow = (at - fastEpochTime()) div 1000
|
||||||
|
|
||||||
|
@ -288,9 +288,11 @@ proc scheduleEpochActions(node: BeaconNode, epoch: uint64) =
|
||||||
# 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 0.uint64 ..< EPOCH_LENGTH:
|
let start = if epoch == GENESIS_EPOCH: 1.uint64 else: 0.uint64
|
||||||
|
|
||||||
|
for i in start ..< EPOCH_LENGTH:
|
||||||
# Schedule block proposals
|
# Schedule block proposals
|
||||||
let slot = epoch * EPOCH_LENGTH + i + 1
|
let slot = epoch * EPOCH_LENGTH + i
|
||||||
nextState.slot = slot
|
nextState.slot = slot
|
||||||
let proposerIdx = get_beacon_proposer_index(nextState, slot)
|
let proposerIdx = get_beacon_proposer_index(nextState, slot)
|
||||||
let validator = node.getAttachedValidator(proposerIdx)
|
let validator = node.getAttachedValidator(proposerIdx)
|
||||||
|
|
|
@ -15,7 +15,7 @@ type
|
||||||
# shard number. When we haven't received an attestation for a particular
|
# shard number. When we haven't received an attestation for a particular
|
||||||
# shard yet, the Option value will be `none`
|
# shard yet, the Option value will be `none`
|
||||||
attestations: Deque[array[SHARD_COUNT, Option[Attestation]]]
|
attestations: Deque[array[SHARD_COUNT, Option[Attestation]]]
|
||||||
startingSlot: int
|
startingSlot: uint64
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# The compilicated Deque above is not needed.
|
# The compilicated Deque above is not needed.
|
||||||
|
@ -37,7 +37,7 @@ type
|
||||||
# substantial difficulties in network layer aggregation, then adding bits to
|
# substantial difficulties in network layer aggregation, then adding bits to
|
||||||
# aid in supporting overlaps is one potential solution
|
# aid in supporting overlaps is one potential solution
|
||||||
|
|
||||||
proc init*(T: type AttestationPool, startingSlot: int): T =
|
proc init*(T: type AttestationPool, startingSlot: uint64): T =
|
||||||
result.attestations = initDeque[array[SHARD_COUNT, Option[Attestation]]]()
|
result.attestations = initDeque[array[SHARD_COUNT, Option[Attestation]]]()
|
||||||
result.startingSlot = startingSlot
|
result.startingSlot = startingSlot
|
||||||
|
|
||||||
|
@ -74,14 +74,14 @@ proc add*(pool: var AttestationPool,
|
||||||
beaconState: BeaconState) =
|
beaconState: BeaconState) =
|
||||||
# The caller of this function is responsible for ensuring that
|
# The caller of this function is responsible for ensuring that
|
||||||
# the attestations will be given in a strictly slot increasing order:
|
# the attestations will be given in a strictly slot increasing order:
|
||||||
doAssert attestation.data.slot.int >= pool.startingSlot
|
doAssert attestation.data.slot >= pool.startingSlot
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# Validate that the attestation is authentic (it's properly signed)
|
# Validate that the attestation is authentic (it's properly signed)
|
||||||
# and make sure that the validator is supposed to make an attestation
|
# and make sure that the validator is supposed to make an attestation
|
||||||
# for the specific shard/slot
|
# for the specific shard/slot
|
||||||
|
|
||||||
let slotIdxInPool = attestation.data.slot.int - pool.startingSlot
|
let slotIdxInPool = int(attestation.data.slot - pool.startingSlot)
|
||||||
if slotIdxInPool >= pool.attestations.len:
|
if slotIdxInPool >= pool.attestations.len:
|
||||||
pool.attestations.setLen(slotIdxInPool + 1)
|
pool.attestations.setLen(slotIdxInPool + 1)
|
||||||
|
|
||||||
|
@ -104,20 +104,20 @@ proc getAttestationsForBlock*(pool: AttestationPool,
|
||||||
firstSlot = 0.uint64
|
firstSlot = 0.uint64
|
||||||
lastSlot = newBlockSlot - MIN_ATTESTATION_INCLUSION_DELAY
|
lastSlot = newBlockSlot - MIN_ATTESTATION_INCLUSION_DELAY
|
||||||
|
|
||||||
if pool.startingSlot.uint64 + MIN_ATTESTATION_INCLUSION_DELAY <= lastState.slot:
|
if pool.startingSlot + MIN_ATTESTATION_INCLUSION_DELAY <= lastState.slot:
|
||||||
firstSlot = lastState.slot - MIN_ATTESTATION_INCLUSION_DELAY
|
firstSlot = lastState.slot - MIN_ATTESTATION_INCLUSION_DELAY
|
||||||
|
|
||||||
for slot in firstSlot .. lastSlot:
|
for slot in firstSlot .. lastSlot:
|
||||||
let slotDequeIdx = slot.int - pool.startingSlot
|
let slotDequeIdx = int(slot - pool.startingSlot)
|
||||||
if slotDequeIdx >= pool.attestations.len: return
|
if slotDequeIdx >= pool.attestations.len: return
|
||||||
let shardAndComittees = get_crosslink_committees_at_slot(lastState, slot)
|
let shardAndComittees = get_crosslink_committees_at_slot(lastState, slot)
|
||||||
for s in shardAndComittees:
|
for s in shardAndComittees:
|
||||||
if pool.attestations[slotDequeIdx][s.shard].isSome:
|
if pool.attestations[slotDequeIdx][s.shard].isSome:
|
||||||
result.add pool.attestations[slotDequeIdx][s.shard].get
|
result.add pool.attestations[slotDequeIdx][s.shard].get
|
||||||
|
|
||||||
proc discardHistoryToSlot*(pool: var AttestationPool, slot: int) =
|
proc discardHistoryToSlot*(pool: var AttestationPool, slot: uint64) =
|
||||||
## The index is treated inclusively
|
## The index is treated inclusively
|
||||||
let slot = slot - MIN_ATTESTATION_INCLUSION_DELAY.int
|
let slot = slot - MIN_ATTESTATION_INCLUSION_DELAY
|
||||||
if slot < pool.startingSlot:
|
if slot < pool.startingSlot:
|
||||||
return
|
return
|
||||||
let slotIdx = int(slot - pool.startingSlot)
|
let slotIdx = int(slot - pool.startingSlot)
|
||||||
|
@ -187,7 +187,7 @@ func getVoteCount(aggregation_bitfield: openarray[byte]): int =
|
||||||
for validatorIdx in 0 ..< aggregation_bitfield.len * 8:
|
for validatorIdx in 0 ..< aggregation_bitfield.len * 8:
|
||||||
result += int aggregation_bitfield.get_bitfield_bit(validatorIdx)
|
result += int aggregation_bitfield.get_bitfield_bit(validatorIdx)
|
||||||
|
|
||||||
func getAttestationVoteCount(pool: AttestationPool, current_slot: int): CountTable[Eth2Digest] =
|
func getAttestationVoteCount(pool: AttestationPool, current_slot: uint64): CountTable[Eth2Digest] =
|
||||||
## Returns all blocks more recent that the current slot
|
## Returns all blocks more recent that the current slot
|
||||||
## that were attested and their vote count
|
## that were attested and their vote count
|
||||||
# This replaces:
|
# This replaces:
|
||||||
|
@ -202,7 +202,7 @@ func getAttestationVoteCount(pool: AttestationPool, current_slot: int): CountTab
|
||||||
# while the following implementation will count such blockhash multiple times instead.
|
# while the following implementation will count such blockhash multiple times instead.
|
||||||
result = initCountTable[Eth2Digest]()
|
result = initCountTable[Eth2Digest]()
|
||||||
|
|
||||||
for slot in current_slot - pool.startingSlot ..< pool.attestations.len:
|
for slot in current_slot - pool.startingSlot ..< pool.attestations.len.uint64:
|
||||||
for attestation in pool.attestations[slot]:
|
for attestation in pool.attestations[slot]:
|
||||||
if attestation.isSome:
|
if attestation.isSome:
|
||||||
# Increase the block attestation counts by the number of validators aggregated
|
# Increase the block attestation counts by the number of validators aggregated
|
||||||
|
@ -218,7 +218,7 @@ proc lmdGhost*(
|
||||||
# LMD GHOST (Latest Message Driven - Greediest Heaviest Observed SubTree)
|
# LMD GHOST (Latest Message Driven - Greediest Heaviest Observed SubTree)
|
||||||
|
|
||||||
# Raw vote count from all attestations
|
# Raw vote count from all attestations
|
||||||
let rawVoteCount = pool.getAttestationVoteCount(state.slot.int)
|
let rawVoteCount = pool.getAttestationVoteCount(state.slot)
|
||||||
|
|
||||||
# The real vote count for a block also takes into account votes for its children
|
# The real vote count for a block also takes into account votes for its children
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ func validate_proof_of_possession(state: BeaconState,
|
||||||
proof_of_possession,
|
proof_of_possession,
|
||||||
get_domain(
|
get_domain(
|
||||||
state.fork,
|
state.fork,
|
||||||
state.slot,
|
slot_to_epoch(state.slot),
|
||||||
DOMAIN_DEPOSIT,
|
DOMAIN_DEPOSIT,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -469,6 +469,12 @@ when true:
|
||||||
proc read*(rlp: var Rlp, T: type ValidatorSig): T {.inline.} =
|
proc read*(rlp: var Rlp, T: type ValidatorSig): T {.inline.} =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
func shortValidatorKey*(state: BeaconState, validatorIdx: int): string =
|
||||||
|
($state.validator_registry[validatorIdx].pubkey)[0..7]
|
||||||
|
|
||||||
|
func humaneSlotNum*(s: SlotNumber): SlotNumber =
|
||||||
|
s - GENESIS_SLOT
|
||||||
|
|
||||||
export
|
export
|
||||||
writeValue, readValue
|
writeValue, readValue
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ func flatten[T](v: openArray[seq[T]]): seq[T] =
|
||||||
# TODO not in nim - doh.
|
# TODO not in nim - doh.
|
||||||
for x in v: result.add x
|
for x in v: result.add x
|
||||||
|
|
||||||
func verifyProposerSignature(state: BeaconState, blck: BeaconBlock): bool =
|
proc verifyProposerSignature(state: BeaconState, blck: BeaconBlock): bool =
|
||||||
## When creating a block, the proposer will sign a version of the block that
|
## When creating a block, the proposer will sign a version of the block that
|
||||||
## doesn't contain the data (chicken and egg), then add the signature to that
|
## doesn't contain the data (chicken and egg), then add the signature to that
|
||||||
## block. Here, we check that the signature is correct by repeating the same
|
## block. Here, we check that the signature is correct by repeating the same
|
||||||
|
@ -61,7 +61,7 @@ func verifyProposerSignature(state: BeaconState, blck: BeaconBlock): bool =
|
||||||
bls_verify(
|
bls_verify(
|
||||||
state.validator_registry[proposer_index].pubkey,
|
state.validator_registry[proposer_index].pubkey,
|
||||||
proposal_hash.data, blck.signature,
|
proposal_hash.data, blck.signature,
|
||||||
get_domain(state.fork, state.slot, DOMAIN_PROPOSAL))
|
get_domain(state.fork, slot_to_epoch(state.slot), DOMAIN_PROPOSAL))
|
||||||
|
|
||||||
proc processRandao(
|
proc processRandao(
|
||||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||||
|
@ -147,7 +147,7 @@ proc processProposerSlashings(
|
||||||
hash_tree_root_final(proposer_slashing.proposal_data_1).data,
|
hash_tree_root_final(proposer_slashing.proposal_data_1).data,
|
||||||
proposer_slashing.proposal_signature_1,
|
proposer_slashing.proposal_signature_1,
|
||||||
get_domain(
|
get_domain(
|
||||||
state.fork, proposer_slashing.proposal_data_1.slot,
|
state.fork, slot_to_epoch(proposer_slashing.proposal_data_1.slot),
|
||||||
DOMAIN_PROPOSAL)):
|
DOMAIN_PROPOSAL)):
|
||||||
notice "PropSlash: invalid signature 1"
|
notice "PropSlash: invalid signature 1"
|
||||||
return false
|
return false
|
||||||
|
@ -156,7 +156,7 @@ proc processProposerSlashings(
|
||||||
hash_tree_root_final(proposer_slashing.proposal_data_2).data,
|
hash_tree_root_final(proposer_slashing.proposal_data_2).data,
|
||||||
proposer_slashing.proposal_signature_2,
|
proposer_slashing.proposal_signature_2,
|
||||||
get_domain(
|
get_domain(
|
||||||
state.fork, proposer_slashing.proposal_data_2.slot,
|
state.fork, slot_to_epoch(proposer_slashing.proposal_data_2.slot),
|
||||||
DOMAIN_PROPOSAL)):
|
DOMAIN_PROPOSAL)):
|
||||||
notice "PropSlash: invalid signature 2"
|
notice "PropSlash: invalid signature 2"
|
||||||
return false
|
return false
|
||||||
|
@ -401,7 +401,7 @@ proc processBlock(
|
||||||
# type that omits some fields - this way, the compiler would guarantee
|
# type that omits some fields - this way, the compiler would guarantee
|
||||||
# that we don't try to access fields that don't have a value yet
|
# that we don't try to access fields that don't have a value yet
|
||||||
if not verifyProposerSignature(state, blck):
|
if not verifyProposerSignature(state, blck):
|
||||||
notice "Proposer signature not valid"
|
notice "Proposer signature not valid", slot = humaneSlotNum(state.slot)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if not processRandao(state, blck, flags):
|
if not processRandao(state, blck, flags):
|
||||||
|
|
|
@ -51,7 +51,7 @@ proc signBlockProposal*(v: AttachedValidator, fork: Fork,
|
||||||
|
|
||||||
# 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?
|
||||||
result = bls_sign(v.privKey, proposalRoot.data,
|
result = bls_sign(v.privKey, proposalRoot.data,
|
||||||
get_domain(fork, proposal.slot, DOMAIN_PROPOSAL))
|
get_domain(fork, slot_to_epoch(proposal.slot), DOMAIN_PROPOSAL))
|
||||||
else:
|
else:
|
||||||
# TODO:
|
# TODO:
|
||||||
# send RPC
|
# send RPC
|
||||||
|
|
|
@ -134,12 +134,12 @@ proc addBlock*(
|
||||||
# TODO domain present do something!
|
# TODO domain present do something!
|
||||||
new_block.signature =
|
new_block.signature =
|
||||||
bls_sign(proposerPrivkey, proposal_hash,
|
bls_sign(proposerPrivkey, proposal_hash,
|
||||||
get_domain(state.fork, state.slot, DOMAIN_PROPOSAL))
|
get_domain(state.fork, slot_to_epoch(state.slot), DOMAIN_PROPOSAL))
|
||||||
|
|
||||||
assert bls_verify(
|
assert bls_verify(
|
||||||
proposer.pubkey,
|
proposer.pubkey,
|
||||||
proposal_hash, new_block.signature,
|
proposal_hash, new_block.signature,
|
||||||
get_domain(state.fork, state.slot, DOMAIN_PROPOSAL)),
|
get_domain(state.fork, slot_to_epoch(state.slot), DOMAIN_PROPOSAL)),
|
||||||
"we just signed this message - it should pass verification!"
|
"we just signed this message - it should pass verification!"
|
||||||
|
|
||||||
new_block
|
new_block
|
||||||
|
|
Loading…
Reference in New Issue