Merge pull request #115 from status-im/nus
complete switching to v0.2.0 RANDAO scheme, with verification enabled
This commit is contained in:
commit
d3f97c1d5c
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import
|
||||
os, ospaths, strutils, strformat,
|
||||
chronos, nimcrypto, json_serialization, confutils,
|
||||
spec/[datatypes, digest, crypto], conf, randao, time, ssz,
|
||||
spec/[datatypes, digest, crypto], conf, time, ssz,
|
||||
../tests/testutil
|
||||
|
||||
proc writeFile(filename: string, value: auto) =
|
||||
|
@ -9,17 +9,13 @@ proc writeFile(filename: string, value: auto) =
|
|||
echo "Wrote ", filename
|
||||
|
||||
proc genSingleValidator(path: string): (ValidatorPubKey,
|
||||
ValidatorPrivKey,
|
||||
Eth2Digest) =
|
||||
ValidatorPrivKey) =
|
||||
var v: PrivateValidatorData
|
||||
v.privKey = newPrivKey()
|
||||
if randomBytes(v.randao.seed.data) != sizeof(v.randao.seed.data):
|
||||
raise newException(Exception, "Could not generate randao seed")
|
||||
|
||||
writeFile(path, v)
|
||||
|
||||
assert v.privKey != ValidatorPrivKey(), "Private key shouldn't be zero"
|
||||
return (v.privKey.pubKey(), v.privKey, v.randao.initialCommitment)
|
||||
return (v.privKey.pubKey(), v.privKey)
|
||||
|
||||
# TODO: Make these more comprehensive and find them a new home
|
||||
type
|
||||
|
@ -41,7 +37,7 @@ cli do (validators: int,
|
|||
var startupData: ChainStartupData
|
||||
|
||||
for i in 1 .. validators:
|
||||
let (pubKey, privKey, randaoCommitment) =
|
||||
let (pubKey, privKey) =
|
||||
genSingleValidator(outputDir / &"validator-{i:02}.json")
|
||||
|
||||
let
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue