Update to BLS v0.10.1

This commit is contained in:
Mamy André-Ratsimbazafy 2020-03-04 22:27:11 +01:00 committed by tersec
parent c418bf2b24
commit bfd4df912e
19 changed files with 365 additions and 302 deletions

View File

@ -31,7 +31,7 @@ proc combine*(tgt: var Attestation, src: Attestation, flags: UpdateFlags) =
tgt.aggregation_bits.combine(src.aggregation_bits)
if skipBlsValidation notin flags:
tgt.signature.combine(src.signature)
tgt.signature.aggregate(src.signature)
else:
trace "Ignoring overlapping attestations"
@ -341,7 +341,7 @@ proc getAttestationsForBlock*(
# one new attestation in there
if not attestation.aggregation_bits.overlaps(v.aggregation_bits):
attestation.aggregation_bits.combine(v.aggregation_bits)
attestation.signature.combine(v.aggregate_signature)
attestation.signature.aggregate(v.aggregate_signature)
result.add(attestation)

View File

@ -293,4 +293,3 @@ iterator validatorKeys*(conf: BeaconNodeConf): ValidatorPrivKey =
template writeValue*(writer: var JsonWriter,
value: TypedInputFile|InputFile|InputDir|OutPath|OutDir|OutFile) =
writer.writeValue(string value)

View File

@ -15,12 +15,7 @@ func get_eth1data_stub*(deposit_count: uint64, current_epoch: Epoch): Eth1Data =
block_hash: hash_tree_root(hash_tree_root(voting_period).data),
)
when ValidatorPrivKey is BlsValue:
func makeInteropPrivKey*(i: int): ValidatorPrivKey =
discard
{.fatal: "todo/unused?".}
else:
func makeInteropPrivKey*(i: int): ValidatorPrivKey =
func makeInteropPrivKey*(i: int): ValidatorPrivKey =
var bytes: array[32, byte]
bytes[0..7] = uint64(i).toBytesLE()
@ -32,7 +27,7 @@ else:
privkeyBytes = eth2hash(bytes)
key = (UInt256.fromBytesLE(privkeyBytes.data) mod curveOrder).toBytesBE()
ValidatorPrivKey.init(key)
result.initFromBytes(key)
const eth1BlockHash* = block:
var x: Eth2Digest
@ -56,10 +51,10 @@ func makeDeposit*(
pubkey: pubkey,
withdrawal_credentials: makeWithdrawalCredentials(pubkey)))
if skipBlsValidation notin flags:
ret.data.signature =
bls_sign(
privkey, hash_tree_root(ret.getDepositMessage).data,
compute_domain(DOMAIN_DEPOSIT))
if skipBLSValidation notin flags:
let domain = compute_domain(DOMAIN_DEPOSIT)
let signing_root = compute_signing_root(ret.getDepositMessage, domain)
ret.data.signature = bls_sign(privkey, signing_root.data)
ret

View File

@ -73,9 +73,11 @@ proc process_deposit*(
if index == -1:
# Verify the deposit signature (proof of possession)
if skipBlsValidation notin flags and not bls_verify(
pubkey, hash_tree_root(deposit.getDepositMessage).data,
deposit.data.signature, compute_domain(DOMAIN_DEPOSIT)):
let domain = compute_domain(DOMAIN_DEPOSIT)
let signing_root = compute_signing_root(deposit.getDepositMessage, domain)
if skipBLSValidation notin flags and not bls_verify(
pubkey, signing_root.data,
deposit.data.signature):
return false
# Add validator and balance entries
@ -214,7 +216,7 @@ func initialize_beacon_state_from_eth1*(
latest_block_header:
BeaconBlockHeader(
body_root: hash_tree_root(BeaconBlockBody(
randao_reveal: BlsValue[Signature](kind: OpaqueBlob)
randao_reveal: ValidatorSig(kind: OpaqueBlob)
))
)
)
@ -263,7 +265,7 @@ func get_initial_beacon_block*(state: BeaconState): SignedBeaconBlock =
state_root: hash_tree_root(state),
body: BeaconBlockBody(
# TODO: This shouldn't be necessary if OpaqueBlob is the default
randao_reveal: BlsValue[Signature](kind: OpaqueBlob))))
randao_reveal: ValidatorSig(kind: OpaqueBlob))))
# parent_root, randao_reveal, eth1_data, signature, and body automatically
# initialized to default values.
@ -381,12 +383,12 @@ proc is_valid_indexed_attestation*(
return false
# Verify aggregate signature
if skipBlsValidation notin flags and not bls_verify(
bls_aggregate_pubkeys(mapIt(indices, state.validators[it.int].pubkey)),
hash_tree_root(indexed_attestation.data).data,
indexed_attestation.signature,
get_domain(
state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
let pubkeys = mapIt(indices, state.validators[it.int].pubkey) # TODO: fuse loops with blsFastAggregateVerify
let domain = state.get_domain(DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
let signing_root = compute_signing_root(indexed_attestation.data, domain)
if skipBLSValidation notin flags and
not blsFastAggregateVerify(
pubkeys, signing_root.data, indexed_attestation.signature
):
notice "indexed attestation: signature verification failure"
return false

View File

@ -22,18 +22,26 @@
# even if in theory it is possible to allow this in BLS.
import
stew/[endians2, objects, byteutils], hashes, nimcrypto/utils,
# Internal
./digest,
# Status
stew/[endians2, objects, byteutils],
nimcrypto/[utils, sysrand],
blscurve, json_serialization,
digest,
chronicles
chronicles,
# Standard library
hashes
export
json_serialization
export
blscurve.init, blscurve.getBytes, blscurve.combine,
blscurve.`$`, blscurve.`==`,
blscurve.Signature
# export
# blscurve.init, blscurve.getBytes, blscurve.combine,
# blscurve.`$`, blscurve.`==`,
# blscurve.Signature
# Type definitions
# ----------------------------------------------------------------------
type
BlsValueType* = enum
@ -52,34 +60,22 @@ type
else:
blob*: array[48, byte]
ValidatorPubKey* = BlsValue[blscurve.VerKey]
# ValidatorPubKey* = blscurve.VerKey
ValidatorPubKey* = BlsValue[blscurve.PublicKey]
# Alternatives
# ValidatorPubKey* = blscurve.PublicKey
# ValidatorPubKey* = array[48, byte]
# The use of byte arrays proved to be a dead end pretty quickly.
# Plenty of code needs to be modified for a successful build and
# the changes will negatively affect the performance.
# ValidatorPrivKey* = BlsValue[blscurve.SigKey]
ValidatorPrivKey* = blscurve.SigKey
ValidatorPrivKey* = blscurve.SecretKey
# ValidatorPrivKey* = BlsValue[blscurve.SecretKey]
ValidatorSig* = BlsValue[blscurve.Signature]
BlsCurveType* = VerKey|SigKey|Signature
BlsCurveType* = PublicKey|SecretKey|Signature
ValidatorPKI* = ValidatorPrivKey|ValidatorPubKey|ValidatorSig
proc init*[T](BLS: type BlsValue[T], val: auto): BLS =
result.kind = BlsValueType.Real
result.blsValue = init(T, val)
func `$`*(x: BlsValue): string =
if x.kind == Real:
$x.blsValue
else:
# r: is short for random. The prefix must be short
# due to the mechanics of the `shortLog` function.
"r:" & toHex(x.blob, true)
func `==`*(a, b: BlsValue): bool =
if a.kind != b.kind: return false
if a.kind == Real:
@ -87,11 +83,123 @@ func `==`*(a, b: BlsValue): bool =
else:
return a.blob == b.blob
func getBytes*(x: BlsValue): auto =
if x.kind == Real:
getBytes x.blsValue
template `==`*[T](a: BlsValue[T], b: T): bool =
a.blsValue == b
template `==`*[T](a: T, b: BlsValue[T]): bool =
a == b.blsValue
# API
# ----------------------------------------------------------------------
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#bls-signatures
func pubKey*(privkey: ValidatorPrivKey): ValidatorPubKey =
## Create a private key from a public key
# Un-specced in either hash-to-curve or Eth2
# TODO: Test suite should use `keyGen` instead
when ValidatorPubKey is BlsValue:
ValidatorPubKey(kind: Real, blsValue: privkey.privToPub())
elif ValidatorPubKey is array:
privkey.getKey.getBytes
else:
x.blob
privkey.getKey
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#bls-signatures
func aggregate*[T](values: openarray[ValidatorSig]): ValidatorSig =
## Aggregate arrays of sequences of Validator Signatures
## This assumes that they are real signatures
result = BlsValue[T](kind: Real, blsValue: values[0].BlsValue)
for i in 1 ..< values.len:
result.blsValue.aggregate(values[i].blsValue)
func aggregate*(x: var ValidatorSig, other: ValidatorSig) =
## Aggregate 2 Validator Signatures
## This assumes that they are real signatures
x.blsValue.aggregate(other.blsValue)
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#bls-signatures
func blsVerify*(
pubkey: ValidatorPubKey, message: openArray[byte],
signature: ValidatorSig): bool =
## Check that a signature is valid for a message
## under the provided public key.
## returns `true` if the signature is valid, `false` otherwise.
##
## The proof-of-possession MUST be verified before calling this function.
## It is recommended to use the overload that accepts a proof-of-possession
## to enforce correct usage.
if signature.kind != Real:
# Invalid signatures are possible in deposits (discussed with Danny)
return false
if pubkey.kind != Real:
# TODO: chronicles warning
return false
# TODO: remove fully if the comment below is not true anymore and
# and we don't need this workaround
# # TODO bls_verify_multiple(...) used to have this workaround, and now it
# # lives here. No matter the signature, there's also no meaningful way to
# # verify it -- it's a kind of vacuous truth. No pubkey/sig pairs. Sans a
# # getBytes() or similar mechanism, pubKey == default(ValidatorPubKey) is
# # a way to create many false positive matches. This seems odd.
# if pubkey.getBytes() == default(ValidatorPubKey).getBytes():
# return true
pubkey.blsValue.verify(message, signature.blsValue)
func blsSign*(privkey: ValidatorPrivKey, message: openarray[byte]): ValidatorSig =
## Computes a signature from a secret key and a message
ValidatorSig(kind: Real, blsValue: privkey.sign(message))
func blsFastAggregateVerify*[T: byte|char](
publicKeys: openarray[ValidatorPubKey],
message: openarray[T],
signature: ValidatorSig
): bool =
## Verify the aggregate of multiple signatures on the same message
## This function is faster than AggregateVerify
##
## The proof-of-possession MUST be verified before calling this function.
## It is recommended to use the overload that accepts a proof-of-possession
## to enforce correct usage.
# TODO: Note: `invalid` in the following paragraph means invalid by construction
# The keys/signatures are not even points on the elliptic curves.
# To respect both the IETF API and the fact that
# we can have invalid public keys (as in not point on the elliptic curve),
# requiring a wrapper indirection,
# we need a first pass to extract keys from the wrapper
# and then call fastAggregateVerify.
# Instead:
# - either we expose a new API: context + init-update-finish
# in blscurve which already exists internally
# - or at network/databases/serialization boundaries we do not
# allow invalid BLS objects to pollute consensus routines
if signature.kind != Real:
return false
var unwrapped: seq[PublicKey]
for pubkey in publicKeys:
if pubkey.kind != Real:
return false
unwrapped.add pubkey.blsValue
return fastAggregateVerify(unwrapped, message, signature.blsValue)
proc newKeyPair*(): tuple[pub: ValidatorPubKey, priv: ValidatorPrivKey] {.noInit.}=
## Generates a new public-private keypair
## This requires entropy on the system
# The input-keying-material requires 32 bytes at least for security
# The generation is deterministic and the input-keying-material
# must be protected against side-channel attacks
var ikm: array[32, byte]
let written = randomBytes(ikm)
doAssert written >= 32, "Key generation failure"
result.pub.kind = Real
doAssert keyGen(ikm, result.pub.blsValue, result.priv), "Key generation failure"
# Logging
# ----------------------------------------------------------------------
func shortLog*(x: BlsValue): string =
($x)[0..7]
@ -99,93 +207,30 @@ func shortLog*(x: BlsValue): string =
func shortLog*(x: BlsCurveType): string =
($x)[0..7]
func hash*(x: BlsValue): Hash {.inline.} =
proc toGaugeValue*(hash: Eth2Digest): int64 =
# Only the last 8 bytes are taken into consideration in accordance
# to the ETH2 metrics spec:
# https://github.com/ethereum/eth2.0-metrics/blob/6a79914cb31f7d54858c7dd57eee75b6162ec737/metrics.md#interop-metrics
cast[int64](uint64.fromBytesLE(hash.data[24..31]))
# Codecs
# ----------------------------------------------------------------------
func `$`*(x: BlsValue): string =
if x.kind == Real:
hash x.blsValue.getBytes()
"r: 0x" & x.blsValue.toHex()
else:
hash x.blob
# r: is short for random. The prefix must be short
# due to the mechanics of the `shortLog` function.
"r: 0x" & x.blob.toHex(lowercase = true)
template hash*(x: BlsCurveType): Hash =
hash(getBytes(x))
template `==`*[T](a: BlsValue[T], b: T): bool =
a.blsValue == b
template `==`*[T](a: T, b: BlsValue[T]): bool =
a == b.blsValue
func pubKey*(pk: ValidatorPrivKey): ValidatorPubKey =
when ValidatorPubKey is BlsValue:
ValidatorPubKey(kind: Real, blsValue: pk.getKey())
elif ValidatorPubKey is array:
pk.getKey.getBytes
func getBytes*(x: BlsValue): auto =
if x.kind == Real:
x.blsValue.exportRaw()
else:
pk.getKey
x.blob
func init*(T: type VerKey): VerKey =
result.point.inf()
func init*(T: type Signature): Signature =
result.point.inf()
func combine*[T](values: openarray[BlsValue[T]]): BlsValue[T] =
result = BlsValue[T](kind: Real, blsValue: T.init())
for value in values:
result.blsValue.combine(value.blsValue)
func combine*[T](x: var BlsValue[T], other: BlsValue[T]) =
doAssert x.kind == Real and other.kind == Real
x.blsValue.combine(other.blsValue)
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.4/specs/bls_signature.md#bls_aggregate_pubkeys
func bls_aggregate_pubkeys*(keys: openArray[ValidatorPubKey]): ValidatorPubKey =
keys.combine()
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.4/specs/bls_signature.md#bls_aggregate_signatures
func bls_aggregate_signatures*(keys: openArray[ValidatorSig]): ValidatorSig =
keys.combine()
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.4/specs/bls_signature.md#bls_verify
func bls_verify*(
pubkey: ValidatorPubKey, msg: openArray[byte], sig: ValidatorSig,
domain: Domain
): bool =
# name from spec!
if sig.kind != Real:
# Invalid signatures are possible in deposits (discussed with Danny)
return false
when ValidatorPubKey is BlsValue:
if sig.kind != Real or pubkey.kind != Real:
# TODO: chronicles warning
return false
# TODO bls_verify_multiple(...) used to have this workaround, and now it
# lives here. No matter the signature, there's also no meaningful way to
# verify it -- it's a kind of vacuous truth. No pubkey/sig pairs. Sans a
# getBytes() or similar mechanism, pubKey == default(ValidatorPubKey) is
# a way to create many false positive matches. This seems odd.
if pubkey.getBytes() == default(ValidatorPubKey).getBytes():
return true
sig.blsValue.verify(msg, domain, pubkey.blsValue)
else:
sig.verify(msg, domain, pubkey)
when ValidatorPrivKey is BlsValue:
func bls_sign*(key: ValidatorPrivKey, msg: openarray[byte],
domain: Domain): ValidatorSig =
# name from spec!
if key.kind == Real:
ValidatorSig(kind: Real, blsValue: key.blsValue.sign(domain, msg))
else:
ValidatorSig(kind: OpaqueBlob)
else:
func bls_sign*(key: ValidatorPrivKey, msg: openarray[byte],
domain: Domain): ValidatorSig =
# name from spec!
ValidatorSig(kind: Real, blsValue: key.sign(domain, msg))
func fromBytes*[T](R: type BlsValue[T], bytes: openarray[byte]): R =
func initFromBytes[T](val: var BlsValue[T], bytes: openarray[byte]) =
# This is a workaround, so that we can deserialize the serialization of a
# default-initialized BlsValue without raising an exception
when defined(ssz_testing):
@ -194,22 +239,40 @@ func fromBytes*[T](R: type BlsValue[T], bytes: openarray[byte]): R =
else:
# Try if valid BLS value
# TODO: address the side-effects in nim-blscurve
{.noSideEffect.}:
let success = init(result.blsValue, bytes)
val = BlsValue[T](kind: Real)
let success = val.blsValue.fromBytes(bytes)
if not success:
# TODO: chronicles trace
result = R(kind: OpaqueBlob)
doAssert result.blob.len == bytes.len
result.blob[result.blob.low .. result.blob.high] = bytes
val = BlsValue[T](kind: OpaqueBlob)
val.blob[val.blob.low .. val.blob.high] = bytes
func fromHex*[T](R: type BlsValue[T], hexStr: string): R =
fromBytes(R, hexToSeqByte(hexStr))
func initFromBytes*(val: var ValidatorPrivKey, bytes: openarray[byte]) {.inline.} =
discard val.fromBytes(bytes)
func initFromBytes*[T](val: var BlsValue[T], bytes: openarray[byte]) =
val = fromBytes(BlsValue[T], bytes)
func fromBytes[T](R: type BlsValue[T], bytes: openarray[byte]): R {.inline.}=
result.initFromBytes(bytes)
func initFromBytes*(val: var BlsCurveType, bytes: openarray[byte]) =
val = init(type(val), bytes)
func fromHex*[T](R: var BlsValue[T], hexStr: string) {.inline.} =
## Initialize a BLSValue from its hex representation
R.fromBytes(hexStr.hexToSeqByte())
# Hashing
# ----------------------------------------------------------------------
func hash*(x: BlsValue): Hash {.inline.} =
# TODO: we can probably just slice the BlsValue
if x.kind == Real:
hash x.blsValue.exportRaw()
else:
hash x.blob
template hash*(x: BlsCurveType): Hash =
# TODO: prevent using secret keys
bind getBytes
hash(getBytes(x))
# Serialization
# ----------------------------------------------------------------------
proc writeValue*(writer: var JsonWriter, value: ValidatorPubKey) {.inline.} =
when value is BlsValue:
@ -246,30 +309,26 @@ proc writeValue*(writer: var JsonWriter, value: ValidatorPrivKey) {.inline.} =
proc readValue*(reader: var JsonReader, value: var ValidatorPrivKey) {.inline.} =
value.initFromBytes(fromHex reader.readValue(string))
when ValidatorPrivKey is BlsValue:
proc newPrivKey*(): ValidatorPrivKey =
ValidatorPrivKey(kind: Real, blsValue: SigKey.random())
else:
proc newPrivKey*(): ValidatorPrivKey =
SigKey.random()
proc writeValue*(writer: var JsonWriter, value: VerKey) {.inline.} =
proc writeValue*(writer: var JsonWriter, value: PublicKey) {.inline.} =
writer.writeValue($value)
proc readValue*(reader: var JsonReader, value: var VerKey) {.inline.} =
value = VerKey.init(reader.readValue(string))
proc readValue*(reader: var JsonReader, value: var PublicKey) {.inline.} =
let hex = reader.readValue(string)
let ok = value.fromHex(hex)
doAssert ok, "Invalid public key: " & hex
proc writeValue*(writer: var JsonWriter, value: Signature) {.inline.} =
writer.writeValue($value)
proc readValue*(reader: var JsonReader, value: var Signature) {.inline.} =
value = Signature.init(reader.readValue(string))
proc toGaugeValue*(hash: Eth2Digest): int64 =
# Only the last 8 bytes are taken into consideration in accordance
# to the ETH2 metrics spec:
# https://github.com/ethereum/eth2.0-metrics/blob/6a79914cb31f7d54858c7dd57eee75b6162ec737/metrics.md#interop-metrics
cast[int64](uint64.fromBytesLE(hash.data[24..31]))
let hex = reader.readValue(string)
let ok = value.fromHex(hex)
doAssert ok, "Invalid signature: " & hex
template fromSszBytes*(T: type BlsValue, bytes: openarray[byte]): auto =
fromBytes(T, bytes)
# For confutils
func init*(T: typedesc[ValidatorPrivKey], hex: string): T {.inline.} =
let success = result.fromHex(hex)
doAssert success, "Private key is invalid" # Don't display private keys even if invalid

View File

@ -96,6 +96,9 @@ type
DOMAIN_SHARD_PROPOSER = 128
DOMAIN_SHARD_ATTESTER = 129
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#custom-types
Domain* = array[8, byte]
# https://github.com/nim-lang/Nim/issues/574 and be consistent across
# 32-bit and 64-bit word platforms.
# TODO VALIDATOR_REGISTRY_LIMIT is 1 shl 40 in 0.8.3, and
@ -324,7 +327,7 @@ type
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#signingroot
SigningRoot* = object
object_root*: Eth2Digest
domain*: uint64
domain*: Domain
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#signedvoluntaryexit
SignedVoluntaryExit* = object

View File

@ -12,7 +12,6 @@ import
math,
# Third-party
stew/endians2,
blscurve, # defines Domain
# Internal
./datatypes, ./digest, ../ssz

View File

@ -100,12 +100,9 @@ proc process_randao(
let proposer = addr state.validators[proposer_index.get]
# Verify that the provided randao value is valid
if skipBlsValidation notin flags and not bls_verify(
proposer.pubkey,
hash_tree_root(epoch.uint64).data,
body.randao_reveal,
get_domain(state, DOMAIN_RANDAO)):
let signing_root = compute_signing_root(epoch, get_domain(state, DOMAIN_RANDAO))
if skipBLSValidation notin flags:
if not blsVerify(proposer.pubkey, signing_root.data, body.randao_reveal):
notice "Randao mismatch", proposer_pubkey = proposer.pubkey,
message = epoch,
signature = body.randao_reveal,
@ -167,13 +164,12 @@ proc process_proposer_slashing*(
if skipBlsValidation notin flags:
for i, signed_header in [proposer_slashing.signed_header_1,
proposer_slashing.signed_header_2]:
if not bls_verify(
proposer.pubkey,
hash_tree_root(signed_header.message).data,
signed_header.signature,
get_domain(
let domain = get_domain(
state, DOMAIN_BEACON_PROPOSER,
compute_epoch_at_slot(signed_header.message.slot))):
compute_epoch_at_slot(signed_header.message.slot)
)
let signing_root = compute_signing_root(signed_header.message, domain)
if not blsVerify(proposer.pubkey, signing_root.data, signed_header.signature):
notice "Proposer slashing: invalid signature",
signature_index = i
return false
@ -339,11 +335,8 @@ proc process_voluntary_exit*(
# Verify signature
if skipBlsValidation notin flags:
let domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
if not bls_verify(
validator.pubkey,
hash_tree_root(voluntary_exit).data,
signed_voluntary_exit.signature,
domain):
let signing_root = compute_signing_root(voluntary_exit, domain)
if not bls_verify(validator.pubkey, signing_root.data, signed_voluntary_exit.signature):
notice "Exit: invalid signature"
return false

View File

@ -24,6 +24,7 @@ func getValidator*(pool: ValidatorPool,
validatorKey: ValidatorPubKey): AttachedValidator =
pool.validators.getOrDefault(validatorKey)
# TODO: Honest validator - https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md
proc signBlockProposal*(v: AttachedValidator, fork: Fork, slot: Slot,
blockRoot: Eth2Digest): Future[ValidatorSig] {.async.} =
@ -38,7 +39,8 @@ proc signBlockProposal*(v: AttachedValidator, fork: Fork, slot: Slot,
# replaced by something more sensible
await sleepAsync(chronos.milliseconds(1))
result = bls_sign(v.privKey, blockRoot.data, domain)
let signing_root = compute_signing_root(blockRoot, domain)
result = blsSign(v.privKey, signing_root.data)
else:
error "Unimplemented"
quit 1
@ -56,18 +58,20 @@ proc signAttestation*(v: AttachedValidator,
# replaced by something more sensible
await sleepAsync(chronos.milliseconds(1))
result = bls_sign(v.privKey, attestationRoot.data, domain)
let signing_root = compute_signing_root(attestationRoot, domain)
result = blsSign(v.privKey, signing_root.data)
else:
error "Unimplemented"
quit 1
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#randao-reveal
func genRandaoReveal*(k: ValidatorPrivKey, fork: Fork, slot: Slot):
ValidatorSig =
let
domain = get_domain(fork, DOMAIN_RANDAO, compute_epoch_at_slot(slot))
root = hash_tree_root(compute_epoch_at_slot(slot).uint64).data
signing_root = compute_signing_root(compute_epoch_at_slot(slot).uint64, domain)
bls_sign(k, root, domain)
bls_sign(k, signing_root.data)
func genRandaoReveal*(v: AttachedValidator, fork: Fork, slot: Slot):
ValidatorSig =

View File

@ -138,18 +138,25 @@ proc main(nb_samples: Natural) =
for i in 0 ..< proof_of_possessions.len:
pop_valid = pop_valid and proof_of_possessions[i].verifyPoP(pubkeys[i])
var agg_pubkey: VerKey
bench &"Benchmarking {num_validators} public keys aggregation", agg_pubkey:
agg_pubkey = combine(pubkeys)
# TODO: update with IETF API (Eth2 v0.10.1)
# func fastAggregateVerify*[T: byte|char](
# publicKeys: openarray[PublicKey],
# message: openarray[T],
# signature: Signature # Aggregated signature
# ): bool
var agg_sig: Signature
bench &"Benchmarking {num_validators} signatures aggregation", agg_sig:
agg_sig = combine(signatures)
# var agg_pubkey: VerKey
# bench &"Benchmarking {num_validators} public keys aggregation", agg_pubkey:
# agg_pubkey = combine(pubkeys)
var msg_verif: bool
bench "Benchmarking message verification", msg_verif:
let domain = 0'u64
msg_verif = agg_sig.verify(msg.data, domain, agg_pubkey)
# var agg_sig: Signature
# bench &"Benchmarking {num_validators} signatures aggregation", agg_sig:
# agg_sig = combine(signatures)
# var msg_verif: bool
# bench "Benchmarking message verification", msg_verif:
# let domain = 0'u64
# msg_verif = agg_sig.verify(msg.data, domain, agg_pubkey)
#####################
#

View File

@ -18,7 +18,6 @@ import # Unit test
./test_block_pool,
./test_discovery_helpers,
./test_helpers,
./test_interop,
./test_kvstore,
./test_kvstore_lmdb,
./test_kvstore_sqlite3,
@ -31,6 +30,11 @@ import # Unit test
./test_sync_manager,
./test_honest_validator
# ./test_interop
# TODO: BLS changes in v0.10.1 will generate different interop signatures
# Requires an update of the interop mocked start: https://github.com/ethereum/eth2.0-pm/tree/master/interop/mocked_start
# or of ZRNT / ZCLI to v0.10.1
import # Refactor state transition unit tests
# TODO re-enable when useful
# ./spec_block_processing/test_genesis,

View File

@ -60,16 +60,14 @@ proc get_attestation_signature(
): ValidatorSig =
let msg = attestation_data.hash_tree_root()
return bls_sign(
key = privkey,
msg = msg.data,
domain = get_domain(
let domain = get_domain(
state = state,
domain_type = DOMAIN_BEACON_ATTESTER,
message_epoch = attestation_data.target.epoch
)
)
let signing_root = compute_signing_root(msg, domain)
return bls_sign(privkey, signing_root.data)
proc signMockAttestation*(state: BeaconState, attestation: var Attestation) =
var cache = get_empty_per_epoch_cache()
@ -89,7 +87,7 @@ proc signMockAttestation*(state: BeaconState, attestation: var Attestation) =
attestation.signature = sig
first_iter = false
else:
combine(attestation.signature, sig)
aggregate(attestation.signature, sig)
proc mockAttestationImpl(
state: BeaconState,

View File

@ -27,28 +27,27 @@ proc signMockBlockImpl(
let privkey = MockPrivKeys[proposer_index]
signedBlock.message.body.randao_reveal = bls_sign(
key = privkey,
msg = block_slot
.compute_epoch_at_slot()
.hash_tree_root()
.data,
domain = get_domain(
block:
let domain = get_domain(
state,
DOMAIN_RANDAO,
message_epoch = block_slot.compute_epoch_at_slot(),
)
let signing_root = compute_signing_root(
block_slot.compute_epoch_at_slot(),
domain
)
signedBlock.message.body.randao_reveal = bls_sign(privkey, signing_root.data)
signedBlock.signature = bls_sign(
key = privkey,
msg = signedBlock.message.hash_tree_root().data,
domain = get_domain(
block:
let domain = get_domain(
state,
DOMAIN_BEACON_PROPOSER,
message_epoch = block_slot.compute_epoch_at_slot(),
)
)
let signing_root = compute_signing_root(signedBlock.message, domain)
signedBlock.signature = bls_sign(privkey, signing_root.data)
proc signMockBlock*(
state: BeaconState,

View File

@ -23,13 +23,17 @@ func signMockDepositData(
privkey: ValidatorPrivKey
) =
# No state --> Genesis
deposit_data.signature = bls_sign(
key = privkey,
msg = deposit_data.getDepositMessage().hash_tree_root().data,
domain = compute_domain(
let domain = compute_domain(
DOMAIN_DEPOSIT,
default(array[4, byte]) # Genesis is fork_version 0
)
let signing_root = compute_signing_root(
deposit_data.getDepositMessage(),
domain
)
deposit_data.signature = blsSign(
privkey,
signing_root.data
)
func signMockDepositData(
@ -37,13 +41,17 @@ func signMockDepositData(
privkey: ValidatorPrivKey,
state: BeaconState
) =
deposit_data.signature = bls_sign(
key = privkey,
msg = deposit_data.getDepositMessage().hash_tree_root().data,
domain = get_domain(
state,
DOMAIN_DEPOSIT
let domain = compute_domain(
DOMAIN_DEPOSIT,
default(array[4, byte]) # Genesis is fork_version 0
)
let signing_root = compute_signing_root(
deposit_data.getDepositMessage(),
domain
)
deposit_data.signature = blsSign(
privkey,
signing_root.data
)
func mockDepositData(

View File

@ -17,7 +17,8 @@ import
let MockPrivKeys* = block:
var privkeys: array[MIN_GENESIS_ACTIVE_VALIDATOR_COUNT, ValidatorPrivKey]
for pk in privkeys.mitems():
pk = newPrivKey()
let pair = newKeyPair()
pk = pair.priv
privkeys
let MockPubKeys* = block:

View File

@ -60,11 +60,6 @@ suite "Beacon chain DB" & preset():
timedTest "find ancestors" & preset():
var
db = init(BeaconChainDB, kvStore MemoryStoreRef.init())
x: ValidatorSig
y = init(ValidatorSig, x.getBytes())
# Silly serialization check that fails without the right import
check: x == y
let
a0 = SignedBeaconBlock(message: BeaconBlock(slot: GENESIS_SLOT + 0))

View File

@ -5,6 +5,10 @@ import
../beacon_chain/[extras, interop, ssz],
../beacon_chain/spec/[beaconstate, crypto, helpers, datatypes]
# TODO: BLS changes in v0.10.1 will generate different interop signatures
# Requires an update of the interop mocked start
# or of ZRNT / ZCLI to v0.10.1
# Interop test yaml, found here:
# https://github.com/ethereum/eth2.0-pm/blob/a0b9d22fad424574b1307828f867b30237758468/interop/mocked_start/keygen_10_validators.yaml

View File

@ -39,7 +39,7 @@ suite "Zero signature sanity checks":
timedTest "SSZ serialization roundtrip of SignedBeaconBlockHeader":
let defaultBlockHeader = SignedBeaconBlockHeader(
signature: BlsValue[Signature](kind: OpaqueBlob)
signature: ValidatorSig(kind: OpaqueBlob)
)
check:

View File

@ -13,21 +13,12 @@ import
../beacon_chain/spec/[beaconstate, crypto, datatypes, digest,
helpers, validator]
when ValidatorPrivKey is BlsValue:
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
result.kind = BlsValueType.Real
var bytes = uint64(i + 1000).toBytesLE()
copyMem(addr result.blsValue.x[0], addr bytes[0], sizeof(bytes))
else:
func makeFakeValidatorPrivKey(i: int): ValidatorPrivKey =
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.x[0], addr bytes[0], sizeof(bytes))
copyMem(addr result, addr bytes[0], sizeof(bytes))
func makeFakeHash(i: int): Eth2Digest =
var bytes = uint64(i).toBytesLE()
@ -62,10 +53,9 @@ func makeDeposit(i: int, flags: UpdateFlags): Deposit =
)
)
if skipBlsValidation notin flags:
result.data.signature =
bls_sign(privkey, hash_tree_root(result.getDepositMessage).data,
domain)
if skipBLSValidation notin flags:
let signing_root = compute_signing_root(result.getDepositMessage, domain)
result.data.signature = bls_sign(privkey, signing_root.data)
func makeInitialDeposits*(
n = SLOTS_PER_EPOCH, flags: UpdateFlags = {}): seq[Deposit] =
@ -123,20 +113,16 @@ proc addBlock*(
doAssert privKey.pubKey() == proposer.pubkey,
"signature key should be derived from private key! - wrong privkey?"
if skipBlsValidation notin flags:
let block_root = hash_tree_root(new_block.message)
if skipBLSValidation notin flags:
let domain = get_domain(state, DOMAIN_BEACON_PROPOSER,
compute_epoch_at_slot(new_block.message.slot))
let signing_root = compute_signing_root(new_block.message, domain)
# We have a signature - put it in the block and we should be done!
new_block.signature =
bls_sign(privKey, block_root.data,
get_domain(state, DOMAIN_BEACON_PROPOSER,
compute_epoch_at_slot(new_block.message.slot)))
new_block.signature = bls_sign(privKey, signing_root.data)
doAssert bls_verify(
proposer.pubkey,
block_root.data, new_block.signature,
get_domain(
state, DOMAIN_BEACON_PROPOSER,
compute_epoch_at_slot(new_block.message.slot))),
signing_root.data, new_block.signature),
"we just signed this message - it should pass verification!"
new_block
@ -171,12 +157,11 @@ proc makeAttestation*(
aggregation_bits.setBit sac_index
let
msg = hash_tree_root(data)
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, data.target.epoch)
signing_root = compute_signing_root(data, domain)
sig =
if skipBlsValidation notin flags:
bls_sign(
hackPrivKey(validator), msg.data,
get_domain(state, DOMAIN_BEACON_ATTESTER, data.target.epoch))
if skipBLSValidation notin flags:
bls_sign(hackPrivKey(validator), signing_root.data)
else:
ValidatorSig()
@ -223,19 +208,27 @@ proc makeFullAttestations*(
let
committee = get_beacon_committee(state, slot, index, cache)
data = makeAttestationData(state, slot, index, beacon_block_root)
msg = hash_tree_root(data)
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, data.target.epoch)
signing_root = compute_signing_root(data, domain)
var
attestation = Attestation(
doAssert committee.len() >= 1
# Initial attestation
var attestation = Attestation(
aggregation_bits: CommitteeValidatorsBits.init(committee.len),
data: data,
signature: ValidatorSig(kind: Real, blsValue: Signature.init())
signature: bls_sign(
hackPrivKey(state.validators[committee[0]]),
signing_root.data
)
for j in 0..<committee.len():
)
# Aggregate the remainder
attestation.aggregation_bits.setBit 0
for j in 1 ..< committee.len():
attestation.aggregation_bits.setBit j
if skipBlsValidation notin flags:
attestation.signature.combine(bls_sign(
hackPrivKey(state.validators[committee[j]]), msg.data,
get_domain(state, DOMAIN_BEACON_ATTESTER, data.target.epoch)))
if skipBLSValidation notin flags:
attestation.signature.aggregate(bls_sign(
hackPrivKey(state.validators[committee[j]]),
signing_root.data
))
result.add attestation