2019-12-03 16:45:12 +00:00
|
|
|
# beacon_chain
|
2021-01-22 13:29:04 +00:00
|
|
|
# Copyright (c) 2018-2021 Status Research & Development GmbH
|
2019-12-03 16:45:12 +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.
|
|
|
|
|
|
|
|
import
|
2021-05-28 15:25:58 +00:00
|
|
|
chronicles,
|
2020-04-15 07:59:47 +00:00
|
|
|
options, stew/endians2,
|
2021-03-02 10:27:45 +00:00
|
|
|
../beacon_chain/extras,
|
|
|
|
../beacon_chain/validators/validator_pool,
|
2020-07-07 23:02:14 +00:00
|
|
|
../beacon_chain/ssz/merkleization,
|
2021-05-21 09:23:28 +00:00
|
|
|
../beacon_chain/spec/[crypto, datatypes, digest, presets, helpers, validator,
|
|
|
|
signatures, state_transition],
|
|
|
|
../beacon_chain/consensus_object_pools/statedata_helpers
|
2019-12-03 16:45:12 +00:00
|
|
|
|
2020-03-04 21:27:11 +00:00
|
|
|
func makeFakeValidatorPrivKey(i: int): ValidatorPrivKey =
|
|
|
|
# 0 is not a valid BLS private key - 1000 helps interop with rust BLS library,
|
|
|
|
# lighthouse.
|
|
|
|
# TODO: switch to https://github.com/ethereum/eth2.0-pm/issues/60
|
|
|
|
var bytes = uint64(i + 1000).toBytesLE()
|
|
|
|
copyMem(addr result, addr bytes[0], sizeof(bytes))
|
2019-12-03 16:45:12 +00:00
|
|
|
|
2020-12-03 04:30:35 +00:00
|
|
|
func makeFakeHash*(i: int): Eth2Digest =
|
2019-12-03 16:45:12 +00:00
|
|
|
var bytes = uint64(i).toBytesLE()
|
|
|
|
static: doAssert sizeof(bytes) <= sizeof(result.data)
|
|
|
|
copyMem(addr result.data[0], addr bytes[0], sizeof(bytes))
|
|
|
|
|
2020-05-01 15:51:24 +00:00
|
|
|
func hackPrivKey*(v: Validator): ValidatorPrivKey =
|
2019-12-03 16:45:12 +00:00
|
|
|
## Extract private key, per above hack
|
|
|
|
var bytes: array[8, byte]
|
|
|
|
static: doAssert sizeof(bytes) <= sizeof(v.withdrawal_credentials.data)
|
|
|
|
|
|
|
|
copyMem(
|
|
|
|
addr bytes, unsafeAddr v.withdrawal_credentials.data[0], sizeof(bytes))
|
|
|
|
let i = int(uint64.fromBytesLE(bytes))
|
|
|
|
makeFakeValidatorPrivKey(i)
|
|
|
|
|
2020-12-03 04:30:35 +00:00
|
|
|
func makeDeposit*(i: int, flags: UpdateFlags = {}): DepositData =
|
2019-12-03 16:45:12 +00:00
|
|
|
## Ugly hack for now: we stick the private key in withdrawal_credentials
|
|
|
|
## which means we can repro private key and randao reveal from this data,
|
|
|
|
## for testing :)
|
|
|
|
let
|
|
|
|
privkey = makeFakeValidatorPrivKey(i)
|
2020-04-11 08:51:07 +00:00
|
|
|
pubkey = privkey.toPubKey()
|
2019-12-03 16:45:12 +00:00
|
|
|
withdrawal_credentials = makeFakeHash(i)
|
|
|
|
|
2020-11-14 21:43:27 +00:00
|
|
|
result = DepositData(
|
2021-06-01 11:13:40 +00:00
|
|
|
pubkey: pubkey.toPubKey(),
|
2020-11-14 21:43:27 +00:00
|
|
|
withdrawal_credentials: withdrawal_credentials,
|
|
|
|
amount: MAX_EFFECTIVE_BALANCE)
|
2019-12-03 16:45:12 +00:00
|
|
|
|
2020-03-04 21:27:11 +00:00
|
|
|
if skipBLSValidation notin flags:
|
2020-11-14 21:43:27 +00:00
|
|
|
result.signature = get_deposit_signature(
|
2021-04-26 20:39:44 +00:00
|
|
|
defaultRuntimePreset, result, privkey).toValidatorSig()
|
2019-12-03 16:45:12 +00:00
|
|
|
|
2021-05-21 09:23:28 +00:00
|
|
|
func makeInitialDeposits*(
|
2020-11-14 21:43:27 +00:00
|
|
|
n = SLOTS_PER_EPOCH, flags: UpdateFlags = {}): seq[DepositData] =
|
2019-12-03 16:45:12 +00:00
|
|
|
for i in 0..<n.int:
|
|
|
|
result.add makeDeposit(i, flags)
|
|
|
|
|
2021-01-22 13:29:04 +00:00
|
|
|
func signBlock(
|
2020-04-09 09:41:02 +00:00
|
|
|
fork: Fork, genesis_validators_root: Eth2Digest, blck: BeaconBlock,
|
|
|
|
privKey: ValidatorPrivKey, flags: UpdateFlags = {}): SignedBeaconBlock =
|
2021-01-22 13:29:04 +00:00
|
|
|
let root = hash_tree_root(blck)
|
2020-04-09 09:41:02 +00:00
|
|
|
SignedBeaconBlock(
|
|
|
|
message: blck,
|
2020-07-16 13:16:51 +00:00
|
|
|
root: root,
|
2020-04-09 09:41:02 +00:00
|
|
|
signature:
|
|
|
|
if skipBlsValidation notin flags:
|
|
|
|
get_block_signature(
|
2021-04-26 20:39:44 +00:00
|
|
|
fork, genesis_validators_root, blck.slot, root, privKey).toValidatorSig()
|
2020-04-09 09:41:02 +00:00
|
|
|
else:
|
|
|
|
ValidatorSig()
|
|
|
|
)
|
|
|
|
|
2020-03-19 23:48:03 +00:00
|
|
|
proc addTestBlock*(
|
2020-05-22 14:21:22 +00:00
|
|
|
state: var HashedBeaconState,
|
2020-03-19 23:48:03 +00:00
|
|
|
parent_root: Eth2Digest,
|
2020-06-04 12:03:16 +00:00
|
|
|
cache: var StateCache,
|
2020-03-19 23:48:03 +00:00
|
|
|
eth1_data = Eth1Data(),
|
|
|
|
attestations = newSeq[Attestation](),
|
|
|
|
deposits = newSeq[Deposit](),
|
2020-06-29 17:30:19 +00:00
|
|
|
graffiti = default(GraffitiBytes),
|
2021-04-12 20:25:09 +00:00
|
|
|
flags: set[UpdateFlag] = {},
|
|
|
|
nextSlot = true): SignedBeaconBlock =
|
2019-12-03 16:45:12 +00:00
|
|
|
# Create and add a block to state - state will advance by one slot!
|
2021-04-12 20:25:09 +00:00
|
|
|
if nextSlot:
|
2021-05-07 11:36:21 +00:00
|
|
|
var rewards: RewardInfo
|
|
|
|
doAssert process_slots(state, state.data.slot + 1, cache, rewards, flags)
|
2020-06-01 07:44:50 +00:00
|
|
|
|
2019-12-03 16:45:12 +00:00
|
|
|
let
|
2020-05-22 14:21:22 +00:00
|
|
|
proposer_index = get_beacon_proposer_index(state.data, cache)
|
|
|
|
privKey = hackPrivKey(state.data.validators[proposer_index.get])
|
2020-03-19 23:48:03 +00:00
|
|
|
randao_reveal =
|
|
|
|
if skipBlsValidation notin flags:
|
2020-03-30 11:31:44 +00:00
|
|
|
privKey.genRandaoReveal(
|
2021-04-26 20:39:44 +00:00
|
|
|
state.data.fork, state.data.genesis_validators_root, state.data.slot).
|
|
|
|
toValidatorSig()
|
2020-03-19 23:48:03 +00:00
|
|
|
else:
|
|
|
|
ValidatorSig()
|
2019-12-03 16:45:12 +00:00
|
|
|
|
2020-03-19 23:48:03 +00:00
|
|
|
let
|
|
|
|
message = makeBeaconBlock(
|
2020-07-07 23:02:14 +00:00
|
|
|
defaultRuntimePreset,
|
2020-03-19 23:48:03 +00:00
|
|
|
state,
|
2020-05-22 14:21:22 +00:00
|
|
|
proposer_index.get(),
|
2020-03-19 23:48:03 +00:00
|
|
|
parent_root,
|
|
|
|
randao_reveal,
|
2020-04-17 22:09:34 +00:00
|
|
|
# Keep deposit counts internally consistent.
|
|
|
|
Eth1Data(
|
|
|
|
deposit_root: eth1_data.deposit_root,
|
2020-07-26 18:55:48 +00:00
|
|
|
deposit_count: state.data.eth1_deposit_index + deposits.lenu64,
|
2020-04-17 22:09:34 +00:00
|
|
|
block_hash: eth1_data.block_hash),
|
2020-03-19 23:48:03 +00:00
|
|
|
graffiti,
|
|
|
|
attestations,
|
2020-05-22 14:21:22 +00:00
|
|
|
deposits,
|
2020-10-07 16:57:21 +00:00
|
|
|
@[],
|
|
|
|
@[],
|
|
|
|
@[],
|
2021-05-20 10:44:13 +00:00
|
|
|
default(ExecutionPayload),
|
2020-06-04 12:03:16 +00:00
|
|
|
noRollback,
|
|
|
|
cache)
|
2020-01-21 09:22:13 +00:00
|
|
|
|
2020-03-19 23:48:03 +00:00
|
|
|
doAssert message.isSome(), "Should have created a valid block!"
|
2019-12-03 16:45:12 +00:00
|
|
|
|
2020-04-09 09:41:02 +00:00
|
|
|
let
|
|
|
|
new_block = signBlock(
|
2020-05-22 14:21:22 +00:00
|
|
|
state.data.fork,
|
|
|
|
state.data.genesis_validators_root, message.get(), privKey, flags)
|
2019-12-03 16:45:12 +00:00
|
|
|
|
|
|
|
new_block
|
|
|
|
|
2020-03-19 23:48:03 +00:00
|
|
|
proc makeTestBlock*(
|
2020-05-22 14:21:22 +00:00
|
|
|
state: HashedBeaconState,
|
2020-03-19 23:48:03 +00:00
|
|
|
parent_root: Eth2Digest,
|
2020-06-04 12:03:16 +00:00
|
|
|
cache: var StateCache,
|
2020-03-19 23:48:03 +00:00
|
|
|
eth1_data = Eth1Data(),
|
|
|
|
attestations = newSeq[Attestation](),
|
|
|
|
deposits = newSeq[Deposit](),
|
2021-01-22 13:29:04 +00:00
|
|
|
graffiti = default(GraffitiBytes)): SignedBeaconBlock =
|
2019-12-03 16:45:12 +00:00
|
|
|
# Create a block for `state.slot + 1` - like a block proposer would do!
|
|
|
|
# It's a bit awkward - in order to produce a block for N+1, we need to
|
|
|
|
# calculate what the state will look like after that block has been applied,
|
|
|
|
# because the block includes the state root.
|
2020-07-01 17:00:14 +00:00
|
|
|
var tmpState = assignClone(state)
|
2020-03-19 23:48:03 +00:00
|
|
|
addTestBlock(
|
2020-06-04 12:03:16 +00:00
|
|
|
tmpState[], parent_root, cache, eth1_data, attestations, deposits,
|
2021-01-22 13:29:04 +00:00
|
|
|
graffiti)
|
2019-12-03 16:45:12 +00:00
|
|
|
|
2021-05-21 09:23:28 +00:00
|
|
|
func makeAttestationData*(
|
|
|
|
state: StateData, slot: Slot, committee_index: CommitteeIndex,
|
|
|
|
beacon_block_root: Eth2Digest): AttestationData =
|
|
|
|
## Create an attestation / vote for the block `beacon_block_root` using the
|
|
|
|
## data in `state` to fill in the rest of the fields.
|
|
|
|
## `state` is the state corresponding to the `beacon_block_root` advanced to
|
|
|
|
## the slot we're attesting to.
|
|
|
|
|
|
|
|
let
|
|
|
|
current_epoch = get_current_epoch(state)
|
|
|
|
start_slot = compute_start_slot_at_epoch(current_epoch)
|
|
|
|
epoch_boundary_block_root =
|
|
|
|
if start_slot == getStateField(state, slot): beacon_block_root
|
|
|
|
else: get_block_root_at_slot(state, start_slot)
|
|
|
|
|
|
|
|
doAssert slot.compute_epoch_at_slot == current_epoch,
|
|
|
|
"Computed epoch was " & $slot.compute_epoch_at_slot &
|
|
|
|
" while the state current_epoch was " & $current_epoch
|
|
|
|
|
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/validator.md#attestation-data
|
|
|
|
AttestationData(
|
|
|
|
slot: slot,
|
|
|
|
index: committee_index.uint64,
|
|
|
|
beacon_block_root: beacon_block_root,
|
|
|
|
source: getStateField(state, current_justified_checkpoint),
|
|
|
|
target: Checkpoint(
|
|
|
|
epoch: current_epoch,
|
|
|
|
root: epoch_boundary_block_root
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
func makeAttestation*(
|
|
|
|
state: StateData, beacon_block_root: Eth2Digest,
|
2020-11-04 21:52:47 +00:00
|
|
|
committee: seq[ValidatorIndex], slot: Slot, index: CommitteeIndex,
|
|
|
|
validator_index: ValidatorIndex, cache: var StateCache,
|
2019-12-03 16:45:12 +00:00
|
|
|
flags: UpdateFlags = {}): Attestation =
|
|
|
|
# Avoids state_sim silliness; as it's responsible for all validators,
|
|
|
|
# transforming, from monotonic enumerable index -> committee index ->
|
|
|
|
# montonoic enumerable index, is wasteful and slow. Most test callers
|
|
|
|
# want ValidatorIndex, so that's supported too.
|
|
|
|
let
|
2021-05-21 09:23:28 +00:00
|
|
|
validator = getStateField(state, validators)[validator_index]
|
2019-12-03 16:45:12 +00:00
|
|
|
sac_index = committee.find(validator_index)
|
|
|
|
data = makeAttestationData(state, slot, index, beacon_block_root)
|
|
|
|
|
|
|
|
doAssert sac_index != -1, "find_beacon_committee should guarantee this"
|
|
|
|
|
|
|
|
var aggregation_bits = CommitteeValidatorsBits.init(committee.len)
|
2019-12-20 13:25:33 +00:00
|
|
|
aggregation_bits.setBit sac_index
|
2019-12-03 16:45:12 +00:00
|
|
|
|
|
|
|
let
|
|
|
|
sig =
|
2020-03-04 21:27:11 +00:00
|
|
|
if skipBLSValidation notin flags:
|
2021-05-21 09:23:28 +00:00
|
|
|
get_attestation_signature(
|
|
|
|
getStateField(state, fork),
|
|
|
|
getStateField(state, genesis_validators_root),
|
2021-04-26 20:39:44 +00:00
|
|
|
data, hackPrivKey(validator)).toValidatorSig()
|
2019-12-03 16:45:12 +00:00
|
|
|
else:
|
|
|
|
ValidatorSig()
|
|
|
|
|
|
|
|
Attestation(
|
|
|
|
data: data,
|
|
|
|
aggregation_bits: aggregation_bits,
|
|
|
|
signature: sig
|
|
|
|
)
|
|
|
|
|
2021-05-21 09:23:28 +00:00
|
|
|
func find_beacon_committee*(
|
|
|
|
state: StateData, validator_index: ValidatorIndex,
|
2019-12-03 16:45:12 +00:00
|
|
|
cache: var StateCache): auto =
|
2021-05-21 09:23:28 +00:00
|
|
|
let epoch = compute_epoch_at_slot(getStateField(state, slot))
|
2020-07-23 17:01:07 +00:00
|
|
|
for epoch_committee_index in 0'u64 ..< get_committee_count_per_slot(
|
|
|
|
state, epoch, cache) * SLOTS_PER_EPOCH:
|
2019-12-03 16:45:12 +00:00
|
|
|
let
|
|
|
|
slot = ((epoch_committee_index mod SLOTS_PER_EPOCH) +
|
|
|
|
epoch.compute_start_slot_at_epoch.uint64).Slot
|
2020-11-04 21:52:47 +00:00
|
|
|
index = CommitteeIndex(epoch_committee_index div SLOTS_PER_EPOCH)
|
|
|
|
committee = get_beacon_committee(state, slot, index, cache)
|
2019-12-03 16:45:12 +00:00
|
|
|
if validator_index in committee:
|
|
|
|
return (committee, slot, index)
|
|
|
|
doAssert false
|
|
|
|
|
2021-05-21 09:23:28 +00:00
|
|
|
func makeAttestation*(
|
|
|
|
state: StateData, beacon_block_root: Eth2Digest,
|
2021-01-22 13:29:04 +00:00
|
|
|
validator_index: ValidatorIndex, cache: var StateCache): Attestation =
|
2019-12-03 16:45:12 +00:00
|
|
|
let (committee, slot, index) =
|
|
|
|
find_beacon_committee(state, validator_index, cache)
|
|
|
|
makeAttestation(state, beacon_block_root, committee, slot, index,
|
2021-01-22 13:29:04 +00:00
|
|
|
validator_index, cache)
|
2020-01-21 09:22:13 +00:00
|
|
|
|
2021-05-21 09:23:28 +00:00
|
|
|
func makeFullAttestations*(
|
|
|
|
state: StateData, beacon_block_root: Eth2Digest, slot: Slot,
|
2020-01-21 09:22:13 +00:00
|
|
|
cache: var StateCache,
|
|
|
|
flags: UpdateFlags = {}): seq[Attestation] =
|
|
|
|
# Create attestations in which the full committee participates for each shard
|
|
|
|
# that should be attested to during a particular slot
|
2020-07-27 10:59:57 +00:00
|
|
|
let committees_per_slot =
|
|
|
|
get_committee_count_per_slot(state, slot.epoch, cache)
|
2020-01-21 09:22:13 +00:00
|
|
|
|
2020-07-23 17:01:07 +00:00
|
|
|
for index in 0'u64..<committees_per_slot:
|
2020-01-21 09:22:13 +00:00
|
|
|
let
|
2020-04-15 09:01:36 +00:00
|
|
|
committee = get_beacon_committee(
|
|
|
|
state, slot, index.CommitteeIndex, cache)
|
2021-05-21 09:23:28 +00:00
|
|
|
data = makeAttestationData(
|
|
|
|
state, slot, index.CommitteeIndex, beacon_block_root)
|
2020-03-04 21:27:11 +00:00
|
|
|
|
|
|
|
doAssert committee.len() >= 1
|
|
|
|
# Initial attestation
|
|
|
|
var attestation = Attestation(
|
|
|
|
aggregation_bits: CommitteeValidatorsBits.init(committee.len),
|
2021-04-26 20:39:44 +00:00
|
|
|
data: data)
|
|
|
|
|
2020-08-15 17:33:58 +00:00
|
|
|
var agg {.noInit.}: AggregateSignature
|
2021-04-26 20:39:44 +00:00
|
|
|
agg.init(get_attestation_signature(
|
2021-05-21 09:23:28 +00:00
|
|
|
getStateField(state, fork),
|
|
|
|
getStateField(state, genesis_validators_root), data,
|
|
|
|
hackPrivKey(getStateField(state, validators)[committee[0]])))
|
2020-08-15 17:33:58 +00:00
|
|
|
|
2020-03-04 21:27:11 +00:00
|
|
|
# Aggregate the remainder
|
|
|
|
attestation.aggregation_bits.setBit 0
|
|
|
|
for j in 1 ..< committee.len():
|
2020-01-21 09:22:13 +00:00
|
|
|
attestation.aggregation_bits.setBit j
|
2020-03-04 21:27:11 +00:00
|
|
|
if skipBLSValidation notin flags:
|
2020-08-15 17:33:58 +00:00
|
|
|
agg.aggregate(get_attestation_signature(
|
2021-05-21 09:23:28 +00:00
|
|
|
getStateField(state, fork),
|
|
|
|
getStateField(state, genesis_validators_root), data,
|
|
|
|
hackPrivKey(getStateField(state, validators)[committee[j]])
|
2020-03-04 21:27:11 +00:00
|
|
|
))
|
2020-01-21 09:22:13 +00:00
|
|
|
|
2021-04-26 20:39:44 +00:00
|
|
|
attestation.signature = agg.finish().toValidatorSig()
|
2020-01-21 09:22:13 +00:00
|
|
|
result.add attestation
|
2020-08-27 07:34:12 +00:00
|
|
|
|
2021-05-21 09:23:28 +00:00
|
|
|
func makeFullAttestations*(
|
|
|
|
state: HashedBeaconState, beacon_block_root: Eth2Digest, slot: Slot,
|
|
|
|
cache: var StateCache,
|
|
|
|
flags: UpdateFlags = {}): seq[Attestation] =
|
|
|
|
makeFullAttestations(
|
|
|
|
(ref StateData)(data: state, blck: BlockRef(
|
|
|
|
root: beacon_block_root, slot: slot))[], beacon_block_root, slot, cache,
|
|
|
|
flags)
|
|
|
|
|
2020-08-27 07:34:12 +00:00
|
|
|
iterator makeTestBlocks*(
|
|
|
|
state: HashedBeaconState,
|
|
|
|
parent_root: Eth2Digest,
|
|
|
|
cache: var StateCache,
|
|
|
|
blocks: int,
|
2021-01-22 13:29:04 +00:00
|
|
|
attested: bool): SignedBeaconBlock =
|
2020-08-27 07:34:12 +00:00
|
|
|
var
|
|
|
|
state = assignClone(state)
|
|
|
|
parent_root = parent_root
|
|
|
|
for _ in 0..<blocks:
|
|
|
|
let attestations = if attested:
|
2021-05-21 09:23:28 +00:00
|
|
|
makeFullAttestations(state[], parent_root, state[].data.slot, cache)
|
2020-08-27 07:34:12 +00:00
|
|
|
else:
|
|
|
|
@[]
|
|
|
|
|
|
|
|
let blck = addTestBlock(
|
2021-01-22 13:29:04 +00:00
|
|
|
state[], parent_root, cache, attestations = attestations)
|
2020-08-27 07:34:12 +00:00
|
|
|
yield blck
|
|
|
|
parent_root = blck.root
|