2018-11-29 22:11:05 +00:00
|
|
|
# beacon_chain
|
|
|
|
# Copyright (c) 2018 Status Research & Development GmbH
|
|
|
|
# Licensed and distributed under either of
|
|
|
|
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at http://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
|
2018-12-21 23:47:55 +00:00
|
|
|
options, milagro_crypto, sequtils,
|
2018-12-19 04:36:10 +00:00
|
|
|
../beacon_chain/[extras, ssz, state_transition],
|
2019-01-26 19:32:10 +00:00
|
|
|
../beacon_chain/spec/[beaconstate, crypto, datatypes, digest, helpers, validator]
|
2018-11-29 22:11:05 +00:00
|
|
|
|
2018-12-13 16:00:55 +00:00
|
|
|
const
|
|
|
|
randaoRounds = 100
|
2018-11-29 22:11:05 +00:00
|
|
|
|
2018-12-13 16:00:55 +00:00
|
|
|
func makeValidatorPrivKey(i: int): ValidatorPrivKey =
|
|
|
|
var i = i + 1 # 0 does not work, as private key...
|
|
|
|
copyMem(result.x[0].addr, i.addr, min(sizeof(result.x), sizeof(i)))
|
|
|
|
|
2018-12-19 12:58:53 +00:00
|
|
|
func makeFakeHash*(i: int): Eth2Digest =
|
2018-12-13 16:00:55 +00:00
|
|
|
copyMem(result.data[0].addr, i.unsafeAddr, min(sizeof(result.data), sizeof(i)))
|
|
|
|
|
2019-01-17 18:27:11 +00:00
|
|
|
func hackPrivKey(v: Validator): ValidatorPrivKey =
|
2018-12-13 16:00:55 +00:00
|
|
|
## Extract private key, per above hack
|
|
|
|
var i: int
|
|
|
|
copyMem(
|
|
|
|
i.addr, v.withdrawal_credentials.data[0].unsafeAddr,
|
|
|
|
min(sizeof(v.withdrawal_credentials.data), sizeof(i)))
|
|
|
|
makeValidatorPrivKey(i)
|
|
|
|
|
2019-01-17 18:27:11 +00:00
|
|
|
func hackReveal(v: Validator): Eth2Digest =
|
2018-12-13 16:00:55 +00:00
|
|
|
result = v.withdrawal_credentials
|
|
|
|
for i in 0..randaoRounds:
|
|
|
|
let tmp = repeat_hash(result, 1)
|
|
|
|
if tmp == v.randao_commitment:
|
|
|
|
return
|
|
|
|
result = tmp
|
|
|
|
raise newException(Exception, "can't find randao hack value")
|
|
|
|
|
2018-12-27 23:40:22 +00:00
|
|
|
func makeDeposit(i: int, flags: UpdateFlags): Deposit =
|
2018-12-13 16:00:55 +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 = makeValidatorPrivKey(i)
|
|
|
|
pubkey = privkey.fromSigKey()
|
|
|
|
withdrawal_credentials = makeFakeHash(i)
|
|
|
|
randao_commitment = repeat_hash(withdrawal_credentials, randaoRounds)
|
2018-12-27 23:40:22 +00:00
|
|
|
|
|
|
|
let pop =
|
|
|
|
if skipValidation in flags:
|
|
|
|
ValidatorSig()
|
|
|
|
else:
|
|
|
|
let proof_of_possession_data = DepositInput(
|
|
|
|
pubkey: pubkey,
|
|
|
|
withdrawal_credentials: withdrawal_credentials,
|
|
|
|
randao_commitment: randao_commitment
|
|
|
|
)
|
|
|
|
signMessage(
|
|
|
|
privkey, hash_tree_root_final(proof_of_possession_data).data)
|
2018-12-13 16:00:55 +00:00
|
|
|
|
|
|
|
Deposit(
|
|
|
|
deposit_data: DepositData(
|
2018-12-27 20:14:37 +00:00
|
|
|
deposit_input: DepositInput(
|
2018-12-13 16:00:55 +00:00
|
|
|
pubkey: pubkey,
|
|
|
|
proof_of_possession: pop,
|
|
|
|
withdrawal_credentials: withdrawal_credentials,
|
|
|
|
randao_commitment: randao_commitment
|
|
|
|
),
|
2019-01-21 18:26:58 +00:00
|
|
|
amount: MAX_DEPOSIT_AMOUNT,
|
2018-12-13 16:00:55 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2018-12-27 23:40:22 +00:00
|
|
|
func makeInitialDeposits*(
|
|
|
|
n = EPOCH_LENGTH, flags: UpdateFlags = {}): seq[Deposit] =
|
2018-12-11 17:55:45 +00:00
|
|
|
for i in 0..<n.int:
|
2018-12-27 23:40:22 +00:00
|
|
|
result.add makeDeposit(i + 1, flags)
|
2018-12-13 16:00:55 +00:00
|
|
|
|
|
|
|
func makeGenesisBlock*(state: BeaconState): BeaconBlock =
|
|
|
|
BeaconBlock(
|
2019-01-16 10:21:06 +00:00
|
|
|
slot: GENESIS_SLOT,
|
2018-12-13 16:00:55 +00:00
|
|
|
state_root: Eth2Digest(data: hash_tree_root(state))
|
|
|
|
)
|
|
|
|
|
2018-12-19 04:36:10 +00:00
|
|
|
func getNextBeaconProposerIndex*(state: BeaconState): Uint24 =
|
2018-12-27 20:14:37 +00:00
|
|
|
# TODO: This is a special version of get_beacon_proposer_index that takes into
|
|
|
|
# account the partial update done at the start of slot processing -
|
|
|
|
# see get_shard_committees_index
|
2018-12-14 16:12:39 +00:00
|
|
|
var next_state = state
|
|
|
|
next_state.slot += 1
|
2018-12-19 04:36:10 +00:00
|
|
|
get_beacon_proposer_index(next_state, next_state.slot)
|
|
|
|
|
2018-12-21 22:37:46 +00:00
|
|
|
proc addBlock*(
|
|
|
|
state: var BeaconState, previous_block_root: Eth2Digest,
|
2018-12-27 23:40:22 +00:00
|
|
|
body: BeaconBlockBody, flags: UpdateFlags = {}): BeaconBlock =
|
2018-12-21 22:37:46 +00:00
|
|
|
# Create and add a block to state - state will advance by one slot!
|
|
|
|
# This is the equivalent of running
|
|
|
|
# updateState(state, prev_block, makeBlock(...), {skipValidation})
|
|
|
|
# but avoids some slow block copies
|
|
|
|
|
|
|
|
state.slot += 1
|
|
|
|
let proposer_index = get_beacon_proposer_index(state, state.slot)
|
|
|
|
state.slot -= 1
|
2018-12-13 16:00:55 +00:00
|
|
|
|
2018-12-19 04:36:10 +00:00
|
|
|
let
|
|
|
|
# Index from the new state, but registry from the old state.. hmm...
|
2018-12-27 20:14:37 +00:00
|
|
|
proposer = state.validator_registry[proposer_index]
|
2018-12-19 04:36:10 +00:00
|
|
|
|
|
|
|
var
|
|
|
|
# In order to reuse the state transition function, we first create a dummy
|
|
|
|
# block that has some fields set, and use that to generate the state as it
|
|
|
|
# would look with the new block applied.
|
|
|
|
new_block = BeaconBlock(
|
|
|
|
slot: state.slot + 1,
|
2018-12-21 22:37:46 +00:00
|
|
|
parent_root: previous_block_root,
|
2018-12-19 04:36:10 +00:00
|
|
|
state_root: Eth2Digest(), # we need the new state first
|
|
|
|
randao_reveal: hackReveal(proposer),
|
2019-01-18 00:14:22 +00:00
|
|
|
eth1_data: Eth1Data(), # TODO
|
2018-12-19 04:36:10 +00:00
|
|
|
signature: ValidatorSig(), # we need the rest of the block first!
|
2018-12-21 23:47:55 +00:00
|
|
|
body: body
|
2018-12-13 16:00:55 +00:00
|
|
|
)
|
|
|
|
|
2018-12-27 20:14:37 +00:00
|
|
|
let block_ok = updateState(
|
2018-12-21 22:37:46 +00:00
|
|
|
state, previous_block_root, some(new_block), {skipValidation})
|
|
|
|
assert block_ok
|
2018-12-19 04:36:10 +00:00
|
|
|
|
|
|
|
# Ok, we have the new state as it would look with the block applied - now we
|
|
|
|
# can set the state root in order to be able to create a valid signature
|
2018-12-21 22:37:46 +00:00
|
|
|
new_block.state_root = Eth2Digest(data: hash_tree_root(state))
|
2018-12-19 04:36:10 +00:00
|
|
|
|
|
|
|
let
|
|
|
|
proposerPrivkey = hackPrivKey(proposer)
|
|
|
|
|
|
|
|
# Once we've collected all the state data, we sign the block data along with
|
|
|
|
# some book-keeping values
|
2018-12-13 16:00:55 +00:00
|
|
|
signed_data = ProposalSignedData(
|
|
|
|
slot: new_block.slot,
|
|
|
|
shard: BEACON_CHAIN_SHARD_NUMBER,
|
|
|
|
block_root: Eth2Digest(data: hash_tree_root(new_block))
|
2018-11-29 22:11:05 +00:00
|
|
|
)
|
2018-12-13 16:00:55 +00:00
|
|
|
proposal_hash = hash_tree_root(signed_data)
|
|
|
|
|
2018-12-19 04:36:10 +00:00
|
|
|
assert proposerPrivkey.fromSigKey() == proposer.pubkey,
|
|
|
|
"signature key should be derived from private key! - wrong privkey?"
|
2018-12-13 16:00:55 +00:00
|
|
|
|
2018-12-27 23:40:22 +00:00
|
|
|
if skipValidation notin flags:
|
|
|
|
# We have a signature - put it in the block and we should be done!
|
|
|
|
new_block.signature =
|
|
|
|
# TODO domain missing!
|
|
|
|
signMessage(proposerPrivkey, proposal_hash)
|
2018-12-13 16:00:55 +00:00
|
|
|
|
2018-12-27 23:40:22 +00:00
|
|
|
assert bls_verify(
|
|
|
|
proposer.pubkey,
|
|
|
|
proposal_hash, new_block.signature,
|
|
|
|
get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL)),
|
|
|
|
"we just signed this message - it should pass verification!"
|
2018-12-13 16:00:55 +00:00
|
|
|
|
|
|
|
new_block
|
2018-12-21 22:37:46 +00:00
|
|
|
|
|
|
|
proc makeBlock*(
|
|
|
|
state: BeaconState, previous_block_root: Eth2Digest,
|
|
|
|
body: BeaconBlockBody): BeaconBlock =
|
|
|
|
# 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.
|
|
|
|
var next_state = state
|
|
|
|
addBlock(next_state, previous_block_root, body)
|
2018-12-21 23:47:55 +00:00
|
|
|
|
|
|
|
proc find_shard_committee(
|
|
|
|
sacs: openArray[ShardCommittee], validator_index: Uint24): ShardCommittee =
|
|
|
|
for sac in sacs:
|
|
|
|
if validator_index in sac.committee: return sac
|
|
|
|
doAssert false
|
|
|
|
|
|
|
|
proc makeAttestation*(
|
|
|
|
state: BeaconState, beacon_block_root: Eth2Digest,
|
2018-12-27 23:40:22 +00:00
|
|
|
validator_index: Uint24, flags: UpdateFlags = {}): Attestation =
|
2018-12-21 23:47:55 +00:00
|
|
|
let
|
|
|
|
sac = find_shard_committee(
|
|
|
|
get_shard_committees_at_slot(state, state.slot), validator_index)
|
|
|
|
validator = state.validator_registry[validator_index]
|
|
|
|
sac_index = sac.committee.find(validator_index)
|
|
|
|
|
|
|
|
data = AttestationData(
|
|
|
|
slot: state.slot,
|
|
|
|
shard: sac.shard,
|
|
|
|
beacon_block_root: beacon_block_root,
|
|
|
|
epoch_boundary_root: Eth2Digest(), # TODO
|
|
|
|
shard_block_root: Eth2Digest(), # TODO
|
|
|
|
latest_crosslink_root: Eth2Digest(), # TODO
|
|
|
|
justified_slot: state.justified_slot,
|
2018-12-27 23:40:22 +00:00
|
|
|
justified_block_root: get_block_root(state, state.justified_slot),
|
2018-12-21 23:47:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
assert sac_index != -1, "find_shard_committe should guarantee this"
|
|
|
|
|
|
|
|
var
|
|
|
|
participation_bitfield = repeat(0'u8, ceil_div8(sac.committee.len))
|
|
|
|
bitSet(participation_bitfield, sac_index)
|
|
|
|
|
|
|
|
let
|
|
|
|
msg = hash_tree_root_final(data)
|
2018-12-27 23:40:22 +00:00
|
|
|
sig =
|
|
|
|
if skipValidation notin flags:
|
|
|
|
signMessage(hackPrivKey(validator), @(msg.data) & @[0'u8])
|
|
|
|
else:
|
|
|
|
ValidatorSig()
|
2018-12-21 23:47:55 +00:00
|
|
|
|
|
|
|
Attestation(
|
|
|
|
data: data,
|
|
|
|
participation_bitfield: participation_bitfield,
|
2018-12-27 23:40:22 +00:00
|
|
|
aggregate_signature: sig
|
2018-12-21 23:47:55 +00:00
|
|
|
)
|