complete switching to v0.2.0 RANDAO scheme, with verification enabled

This commit is contained in:
Dustin Brody 2019-02-14 13:41:04 -08:00
parent c2a52d7fc5
commit 8fbf87aa8c
7 changed files with 38 additions and 131 deletions

View File

@ -131,7 +131,6 @@ proc addLocalValidators*(node: BeaconNode) =
let
privKey = validator.privKey
pubKey = privKey.pubKey()
randao = validator.randao
let idx = node.beaconState.validator_registry.findIt(it.pubKey == pubKey)
if idx == -1:
@ -139,7 +138,7 @@ proc addLocalValidators*(node: BeaconNode) =
else:
debug "Attaching validator", validator = shortValidatorKey(node, idx),
idx, pubKey
node.attachedValidators.addLocalValidator(idx, pubKey, privKey, randao)
node.attachedValidators.addLocalValidator(idx, pubKey, privKey)
info "Local validators attached ", count = node.attachedValidators.count
@ -216,7 +215,7 @@ proc proposeBlock(node: BeaconNode,
var newBlock = BeaconBlock(
slot: slot,
parent_root: node.headBlockRoot,
randao_reveal: validator.genRandaoReveal(state),
randao_reveal: validator.genRandaoReveal(state, state.slot),
eth1_data: node.mainchainMonitor.getBeaconBlockRef(),
signature: ValidatorSig(), # we need the rest of the block first!
body: blockBody)

View File

@ -2,7 +2,7 @@ import
os, options,
confutils/defs, chronicles/options as chroniclesOptions,
json_serialization,
spec/[crypto, datatypes], randao, time
spec/[crypto, datatypes], time
export
json_serialization
@ -20,7 +20,6 @@ type
PrivateValidatorData* = object
privKey*: ValidatorPrivKey
randao*: Randao
BeaconNodeConf* = object
logLevel* {.
@ -81,13 +80,9 @@ proc readFileBytes(path: string): seq[byte] =
proc loadPrivKey*(p: ValidatorKeyPath): ValidatorPrivKey =
ValidatorPrivKey.init(readFileBytes(string(p) & ".privkey"))
proc loadRandao*(p: ValidatorKeyPath): Randao =
initRandao(readFileBytes(string(p) & ".randao"))
proc parseCmdArg*(T: type ValidatorKeyPath, input: TaintedString): T =
result = T(input)
discard loadPrivKey(result)
discard loadRandao(result)
template mustBeFilePath(input: TaintedString) =
if not fileExists(string input):

View File

@ -1,49 +0,0 @@
import spec/[digest, helpers]
type Randao* = object
seed*: Eth2Digest
const MaxRandaoLevels = 10000 # TODO: This number is arbitrary
proc initRandao*(seed: Eth2Digest): Randao =
result.seed = seed
proc initRandao*(bytes: openarray[byte]): Randao =
if bytes.len != sizeof(Eth2Digest):
raise newException(Exception, "Wrong randao size")
var s: Eth2Digest
s.data[0 .. ^1] = bytes
initRandao(s)
proc initialCommitment*(r: Randao): Eth2Digest =
repeatHash(r.seed, MaxRandaoLevels)
proc reveal*(r: Randao, commitment: Eth2Digest): Eth2Digest =
if commitment == r.seed:
raise newException(Exception, "Randao: cannot reveal for seed")
result = r.seed
for i in 0 .. MaxRandaoLevels:
let h = eth2hash(result.data)
if h == commitment:
return
result = h
raise newException(Exception, "Randao: commitment not found")
when isMainModule:
import times, nimcrypto
var seed: Eth2Digest
let r = initRandao(seed)
var s = epochTime()
var ic = r.initialCommitment()
var e = epochTime()
echo "initialCommitment: ", ic
echo "Took time: ", e - s
s = epochTime()
let rev = r.reveal(ic)
e = epochTime()
echo "reveal: ", rev
echo "Took time: ", e - s
echo r.reveal(eth2hash([1.byte, 2, 3])) # Should raise

View File

@ -54,18 +54,6 @@ func get_new_recent_block_roots*(old_block_roots: seq[Eth2Digest],
for _ in 0 ..< min(d, old_block_roots.len):
result.add parent_hash
# TODO remove; cascades through randao.nim, validator_pool.nim, etc
func repeat_hash*(v: Eth2Digest, n: SomeInteger): Eth2Digest =
# Spec version:
# if n == 0: v
# else: repeat_hash(eth2hash(v.data), n - 1)
# Nim is pretty bad at recursion though (max 2k levels / no tco), so:
result = v
var n = n
while n != 0:
result = eth2hash(result.data)
dec n
func ceil_div8*(v: int): int = (v + 7) div 8 # TODO use a proper bitarray!
# https://github.com/ethereum/eth2.0-specs/blob/v0.2.0/specs/core/0_beacon-chain.md#integer_squareroot

View File

@ -39,60 +39,49 @@ func flatten[T](v: openArray[seq[T]]): seq[T] =
# TODO not in nim - doh.
for x in v: result.add x
proc verifyProposerSignature(state: BeaconState, blck: BeaconBlock): bool =
# https://github.com/ethereum/eth2.0-specs/blob/v0.2.0/specs/core/0_beacon-chain.md#proposer-signature
func verifyProposerSignature(state: BeaconState, blck: BeaconBlock): bool =
## 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
## block. Here, we check that the signature is correct by repeating the same
## process.
##
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposer-signature
var blck_without_sig = blck
blck_without_sig.signature = ValidatorSig()
var blck_without_signature = blck
blck_without_signature.signature = ValidatorSig()
let
signed_data = ProposalSignedData(
slot: state.slot,
shard: BEACON_CHAIN_SHARD_NUMBER,
block_root: hash_tree_root_final(blck_without_sig)
block_root: hash_tree_root_final(blck_without_signature)
)
proposal_hash = hash_tree_root_final(signed_data)
proposal_root = hash_tree_root_final(signed_data)
proposer_index = get_beacon_proposer_index(state, state.slot)
bls_verify(
state.validator_registry[proposer_index].pubkey,
proposal_hash.data, blck.signature,
get_domain(state.fork, slot_to_epoch(state.slot), DOMAIN_PROPOSAL))
proposal_root.data, blck.signature,
get_domain(state.fork, get_current_epoch(state), DOMAIN_PROPOSAL))
# https://github.com/ethereum/eth2.0-specs/blob/v0.2.0/specs/core/0_beacon-chain.md#randao
proc processRandao(
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
## When a validator signs up, they will include a hash number together with
## the deposit - the randao_commitment. The commitment is formed by hashing
## a secret value N times.
## The first time the proposer proposes a block, they will hash their secret
## value N-1 times, and provide the reuslt as "reveal" - now everyone else can
## verify that the reveal matches the commitment by hashing it once.
## The next time the proposer proposes, they will reveal the secret value
## hashed N-2 times and so on, and everyone will verify that it matches N-1.
## The previous reveal has now become the commitment!
##
## Effectively, the block proposer can only reveal N-1 times, so better pick
## a large N!
##
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#randao
let
proposer_index = get_beacon_proposer_index(state, state.slot)
proposer = addr state.validator_registry[proposer_index]
if skipValidation notin flags:
# Check that proposer commit and reveal match
# TODO re-enable if appropriate
#if expected != proposer.randao_commitment:
# notice "Randao reveal mismatch", reveal = blck.randao_reveal,
# layers = proposer.randao_layers,
# commitment = proposer.randao_commitment,
# expected
# return false
discard
if not bls_verify(
proposer.pubkey,
int_to_bytes32(get_current_epoch(state)),
blck.randao_reveal,
get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO)):
notice "Randao mismatch", proposer_pubkey = proposer.pubkey,
message = get_current_epoch(state),
signature = blck.randao_reveal,
slot = state.slot,
blck_slot = blck.slot
return false
# Update state and proposer now that we're alright
let
@ -102,7 +91,7 @@ proc processRandao(
for i, b in state.latest_randao_mixes[mix].data:
state.latest_randao_mixes[mix].data[i] = b xor rr[i]
return true
true
func processDepositRoot(state: var BeaconState, blck: BeaconBlock) =
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#eth1-data
@ -359,13 +348,12 @@ proc process_ejections(state: var BeaconState) =
state.validator_balances[index] < EJECTION_BALANCE:
exit_validator(state, index.ValidatorIndex)
# https://github.com/ethereum/eth2.0-specs/blob/v0.2.0/specs/core/0_beacon-chain.md#per-slot-processing
func processSlot(state: var BeaconState, previous_block_root: Eth2Digest) =
## Time on the beacon chain moves in slots. Every time we make it to a new
## slot, a proposer creates a block to represent the state of the beacon
## chain at that time. In case the proposer is missing, it may happen that
## the no block is produced during the slot.
##
## https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#per-slot-processing
state.slot += 1
state.latest_block_roots[(state.slot - 1) mod LATEST_BLOCK_ROOTS_LENGTH] =
previous_block_root
@ -380,6 +368,7 @@ proc processBlock(
# TODO when there's a failure, we should reset the state!
# TODO probably better to do all verification first, then apply state changes
# https://github.com/ethereum/eth2.0-specs/blob/v0.2.0/specs/core/0_beacon-chain.md#slot-1
if not (blck.slot == state.slot):
notice "Unexpected block slot number",
blockSlot = blck.slot,

View File

@ -1,7 +1,7 @@
import
tables, random,
chronos,
spec/[datatypes, crypto, digest, helpers], randao, ssz
spec/[datatypes, crypto, digest, helpers], ssz
type
ValidatorKind = enum
@ -15,7 +15,6 @@ type
case kind: ValidatorKind
of inProcess:
privKey: ValidatorPrivKey
randaoSecret: Randao
else:
connection: ValidatorConnection
@ -31,12 +30,10 @@ template count*(pool: ValidatorPool): int =
proc addLocalValidator*(pool: var ValidatorPool,
idx: int,
pubKey: ValidatorPubKey,
privKey: ValidatorPrivKey,
randaoSecret: Randao) =
privKey: ValidatorPrivKey) =
let v = AttachedValidator(idx: idx,
kind: inProcess,
privKey: privKey,
randaoSecret: randaoSecret)
privKey: privKey)
pool.validators[pubKey] = v
proc getValidator*(pool: ValidatorPool,
@ -74,25 +71,13 @@ proc signAttestation*(v: AttachedValidator,
# send RPC
discard
proc randaoReveal*(v: AttachedValidator, commitment: Eth2Digest): Future[Eth2Digest] {.async.} =
if v.kind == inProcess:
result = v.randaoSecret.reveal(commitment)
else:
# TODO:
# send RPC
discard
# TODO move elsewhere when something else wants this utility function
func int_to_bytes32(x: uint64) : array[32, byte] =
for i in 0 ..< 8:
result[31 - i] = byte((x shr i*8) and 0xff)
func genRandaoReveal*(k: ValidatorPrivKey, state: BeaconState):
func genRandaoReveal*(k: ValidatorPrivKey, state: BeaconState, slot: SlotNumber):
ValidatorSig =
# https://github.com/ethereum/eth2.0-specs/blob/v0.1/specs/core/0_beacon-chain.md#randao
bls_sign(k, int_to_bytes32(get_current_epoch(state)),
get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))
# https://github.com/ethereum/eth2.0-specs/blob/v0.2.0/specs/core/0_beacon-chain.md#randao
assert slot > state.slot
bls_sign(k, int_to_bytes32(slot_to_epoch(slot)),
get_domain(state.fork, slot_to_epoch(slot), DOMAIN_RANDAO))
func genRandaoReveal*(v: AttachedValidator, state: BeaconState):
func genRandaoReveal*(v: AttachedValidator, state: BeaconState, slot: SlotNumber):
ValidatorSig =
genRandaoReveal(v.privKey, state)
genRandaoReveal(v.privKey, state, slot)

View File

@ -100,7 +100,7 @@ proc addBlock*(
slot: state.slot + 1,
parent_root: previous_block_root,
state_root: Eth2Digest(), # we need the new state first
randao_reveal: privKey.genRandaoReveal(state),
randao_reveal: privKey.genRandaoReveal(state, state.slot + 1),
eth1_data: Eth1Data(), # TODO
signature: ValidatorSig(), # we need the rest of the block first!
body: body