Update to BLS v0.10.1
This commit is contained in:
parent
c418bf2b24
commit
bfd4df912e
|
@ -31,7 +31,7 @@ proc combine*(tgt: var Attestation, src: Attestation, flags: UpdateFlags) =
|
||||||
tgt.aggregation_bits.combine(src.aggregation_bits)
|
tgt.aggregation_bits.combine(src.aggregation_bits)
|
||||||
|
|
||||||
if skipBlsValidation notin flags:
|
if skipBlsValidation notin flags:
|
||||||
tgt.signature.combine(src.signature)
|
tgt.signature.aggregate(src.signature)
|
||||||
else:
|
else:
|
||||||
trace "Ignoring overlapping attestations"
|
trace "Ignoring overlapping attestations"
|
||||||
|
|
||||||
|
@ -341,7 +341,7 @@ proc getAttestationsForBlock*(
|
||||||
# one new attestation in there
|
# one new attestation in there
|
||||||
if not attestation.aggregation_bits.overlaps(v.aggregation_bits):
|
if not attestation.aggregation_bits.overlaps(v.aggregation_bits):
|
||||||
attestation.aggregation_bits.combine(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)
|
result.add(attestation)
|
||||||
|
|
||||||
|
|
|
@ -293,4 +293,3 @@ iterator validatorKeys*(conf: BeaconNodeConf): ValidatorPrivKey =
|
||||||
template writeValue*(writer: var JsonWriter,
|
template writeValue*(writer: var JsonWriter,
|
||||||
value: TypedInputFile|InputFile|InputDir|OutPath|OutDir|OutFile) =
|
value: TypedInputFile|InputFile|InputDir|OutPath|OutDir|OutFile) =
|
||||||
writer.writeValue(string value)
|
writer.writeValue(string value)
|
||||||
|
|
||||||
|
|
|
@ -15,24 +15,19 @@ func get_eth1data_stub*(deposit_count: uint64, current_epoch: Epoch): Eth1Data =
|
||||||
block_hash: hash_tree_root(hash_tree_root(voting_period).data),
|
block_hash: hash_tree_root(hash_tree_root(voting_period).data),
|
||||||
)
|
)
|
||||||
|
|
||||||
when ValidatorPrivKey is BlsValue:
|
func makeInteropPrivKey*(i: int): ValidatorPrivKey =
|
||||||
func makeInteropPrivKey*(i: int): ValidatorPrivKey =
|
var bytes: array[32, byte]
|
||||||
discard
|
bytes[0..7] = uint64(i).toBytesLE()
|
||||||
{.fatal: "todo/unused?".}
|
|
||||||
else:
|
|
||||||
func makeInteropPrivKey*(i: int): ValidatorPrivKey =
|
|
||||||
var bytes: array[32, byte]
|
|
||||||
bytes[0..7] = uint64(i).toBytesLE()
|
|
||||||
|
|
||||||
let
|
let
|
||||||
# BLS381-12 curve order - same as milagro but formatted different
|
# BLS381-12 curve order - same as milagro but formatted different
|
||||||
curveOrder =
|
curveOrder =
|
||||||
"52435875175126190479447740508185965837690552500527637822603658699938581184513".parse(UInt256)
|
"52435875175126190479447740508185965837690552500527637822603658699938581184513".parse(UInt256)
|
||||||
|
|
||||||
privkeyBytes = eth2hash(bytes)
|
privkeyBytes = eth2hash(bytes)
|
||||||
key = (UInt256.fromBytesLE(privkeyBytes.data) mod curveOrder).toBytesBE()
|
key = (UInt256.fromBytesLE(privkeyBytes.data) mod curveOrder).toBytesBE()
|
||||||
|
|
||||||
ValidatorPrivKey.init(key)
|
result.initFromBytes(key)
|
||||||
|
|
||||||
const eth1BlockHash* = block:
|
const eth1BlockHash* = block:
|
||||||
var x: Eth2Digest
|
var x: Eth2Digest
|
||||||
|
@ -56,10 +51,10 @@ func makeDeposit*(
|
||||||
pubkey: pubkey,
|
pubkey: pubkey,
|
||||||
withdrawal_credentials: makeWithdrawalCredentials(pubkey)))
|
withdrawal_credentials: makeWithdrawalCredentials(pubkey)))
|
||||||
|
|
||||||
if skipBlsValidation notin flags:
|
if skipBLSValidation notin flags:
|
||||||
ret.data.signature =
|
let domain = compute_domain(DOMAIN_DEPOSIT)
|
||||||
bls_sign(
|
let signing_root = compute_signing_root(ret.getDepositMessage, domain)
|
||||||
privkey, hash_tree_root(ret.getDepositMessage).data,
|
|
||||||
compute_domain(DOMAIN_DEPOSIT))
|
ret.data.signature = bls_sign(privkey, signing_root.data)
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
|
|
@ -73,9 +73,11 @@ proc process_deposit*(
|
||||||
|
|
||||||
if index == -1:
|
if index == -1:
|
||||||
# Verify the deposit signature (proof of possession)
|
# Verify the deposit signature (proof of possession)
|
||||||
if skipBlsValidation notin flags and not bls_verify(
|
let domain = compute_domain(DOMAIN_DEPOSIT)
|
||||||
pubkey, hash_tree_root(deposit.getDepositMessage).data,
|
let signing_root = compute_signing_root(deposit.getDepositMessage, domain)
|
||||||
deposit.data.signature, compute_domain(DOMAIN_DEPOSIT)):
|
if skipBLSValidation notin flags and not bls_verify(
|
||||||
|
pubkey, signing_root.data,
|
||||||
|
deposit.data.signature):
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Add validator and balance entries
|
# Add validator and balance entries
|
||||||
|
@ -214,7 +216,7 @@ func initialize_beacon_state_from_eth1*(
|
||||||
latest_block_header:
|
latest_block_header:
|
||||||
BeaconBlockHeader(
|
BeaconBlockHeader(
|
||||||
body_root: hash_tree_root(BeaconBlockBody(
|
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),
|
state_root: hash_tree_root(state),
|
||||||
body: BeaconBlockBody(
|
body: BeaconBlockBody(
|
||||||
# TODO: This shouldn't be necessary if OpaqueBlob is the default
|
# 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
|
# parent_root, randao_reveal, eth1_data, signature, and body automatically
|
||||||
# initialized to default values.
|
# initialized to default values.
|
||||||
|
|
||||||
|
@ -381,13 +383,13 @@ proc is_valid_indexed_attestation*(
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Verify aggregate signature
|
# Verify aggregate signature
|
||||||
if skipBlsValidation notin flags and not bls_verify(
|
let pubkeys = mapIt(indices, state.validators[it.int].pubkey) # TODO: fuse loops with blsFastAggregateVerify
|
||||||
bls_aggregate_pubkeys(mapIt(indices, state.validators[it.int].pubkey)),
|
let domain = state.get_domain(DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
|
||||||
hash_tree_root(indexed_attestation.data).data,
|
let signing_root = compute_signing_root(indexed_attestation.data, domain)
|
||||||
indexed_attestation.signature,
|
if skipBLSValidation notin flags and
|
||||||
get_domain(
|
not blsFastAggregateVerify(
|
||||||
state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
|
pubkeys, signing_root.data, indexed_attestation.signature
|
||||||
):
|
):
|
||||||
notice "indexed attestation: signature verification failure"
|
notice "indexed attestation: signature verification failure"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
|
@ -22,18 +22,26 @@
|
||||||
# even if in theory it is possible to allow this in BLS.
|
# even if in theory it is possible to allow this in BLS.
|
||||||
|
|
||||||
import
|
import
|
||||||
stew/[endians2, objects, byteutils], hashes, nimcrypto/utils,
|
# Internal
|
||||||
|
./digest,
|
||||||
|
# Status
|
||||||
|
stew/[endians2, objects, byteutils],
|
||||||
|
nimcrypto/[utils, sysrand],
|
||||||
blscurve, json_serialization,
|
blscurve, json_serialization,
|
||||||
digest,
|
chronicles,
|
||||||
chronicles
|
# Standard library
|
||||||
|
hashes
|
||||||
|
|
||||||
export
|
export
|
||||||
json_serialization
|
json_serialization
|
||||||
|
|
||||||
export
|
# export
|
||||||
blscurve.init, blscurve.getBytes, blscurve.combine,
|
# blscurve.init, blscurve.getBytes, blscurve.combine,
|
||||||
blscurve.`$`, blscurve.`==`,
|
# blscurve.`$`, blscurve.`==`,
|
||||||
blscurve.Signature
|
# blscurve.Signature
|
||||||
|
|
||||||
|
# Type definitions
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
BlsValueType* = enum
|
BlsValueType* = enum
|
||||||
|
@ -52,34 +60,22 @@ type
|
||||||
else:
|
else:
|
||||||
blob*: array[48, byte]
|
blob*: array[48, byte]
|
||||||
|
|
||||||
ValidatorPubKey* = BlsValue[blscurve.VerKey]
|
ValidatorPubKey* = BlsValue[blscurve.PublicKey]
|
||||||
# ValidatorPubKey* = blscurve.VerKey
|
# 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.
|
||||||
|
|
||||||
# ValidatorPubKey* = array[48, byte]
|
ValidatorPrivKey* = blscurve.SecretKey
|
||||||
# The use of byte arrays proved to be a dead end pretty quickly.
|
# ValidatorPrivKey* = BlsValue[blscurve.SecretKey]
|
||||||
# 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
|
|
||||||
|
|
||||||
ValidatorSig* = BlsValue[blscurve.Signature]
|
ValidatorSig* = BlsValue[blscurve.Signature]
|
||||||
|
|
||||||
BlsCurveType* = VerKey|SigKey|Signature
|
BlsCurveType* = PublicKey|SecretKey|Signature
|
||||||
ValidatorPKI* = ValidatorPrivKey|ValidatorPubKey|ValidatorSig
|
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 =
|
func `==`*(a, b: BlsValue): bool =
|
||||||
if a.kind != b.kind: return false
|
if a.kind != b.kind: return false
|
||||||
if a.kind == Real:
|
if a.kind == Real:
|
||||||
|
@ -87,11 +83,123 @@ func `==`*(a, b: BlsValue): bool =
|
||||||
else:
|
else:
|
||||||
return a.blob == b.blob
|
return a.blob == b.blob
|
||||||
|
|
||||||
func getBytes*(x: BlsValue): auto =
|
template `==`*[T](a: BlsValue[T], b: T): bool =
|
||||||
if x.kind == Real:
|
a.blsValue == b
|
||||||
getBytes x.blsValue
|
|
||||||
|
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:
|
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 =
|
func shortLog*(x: BlsValue): string =
|
||||||
($x)[0..7]
|
($x)[0..7]
|
||||||
|
@ -99,93 +207,30 @@ func shortLog*(x: BlsValue): string =
|
||||||
func shortLog*(x: BlsCurveType): string =
|
func shortLog*(x: BlsCurveType): string =
|
||||||
($x)[0..7]
|
($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:
|
if x.kind == Real:
|
||||||
hash x.blsValue.getBytes()
|
"r: 0x" & x.blsValue.toHex()
|
||||||
else:
|
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 =
|
func getBytes*(x: BlsValue): auto =
|
||||||
hash(getBytes(x))
|
if x.kind == Real:
|
||||||
|
x.blsValue.exportRaw()
|
||||||
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
|
|
||||||
else:
|
else:
|
||||||
pk.getKey
|
x.blob
|
||||||
|
|
||||||
func init*(T: type VerKey): VerKey =
|
func initFromBytes[T](val: var BlsValue[T], bytes: openarray[byte]) =
|
||||||
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 =
|
|
||||||
# This is a workaround, so that we can deserialize the serialization of a
|
# This is a workaround, so that we can deserialize the serialization of a
|
||||||
# default-initialized BlsValue without raising an exception
|
# default-initialized BlsValue without raising an exception
|
||||||
when defined(ssz_testing):
|
when defined(ssz_testing):
|
||||||
|
@ -194,22 +239,40 @@ func fromBytes*[T](R: type BlsValue[T], bytes: openarray[byte]): R =
|
||||||
else:
|
else:
|
||||||
# Try if valid BLS value
|
# Try if valid BLS value
|
||||||
# TODO: address the side-effects in nim-blscurve
|
# TODO: address the side-effects in nim-blscurve
|
||||||
{.noSideEffect.}:
|
val = BlsValue[T](kind: Real)
|
||||||
let success = init(result.blsValue, bytes)
|
let success = val.blsValue.fromBytes(bytes)
|
||||||
if not success:
|
if not success:
|
||||||
# TODO: chronicles trace
|
# TODO: chronicles trace
|
||||||
result = R(kind: OpaqueBlob)
|
val = BlsValue[T](kind: OpaqueBlob)
|
||||||
doAssert result.blob.len == bytes.len
|
val.blob[val.blob.low .. val.blob.high] = bytes
|
||||||
result.blob[result.blob.low .. result.blob.high] = bytes
|
|
||||||
|
|
||||||
func fromHex*[T](R: type BlsValue[T], hexStr: string): R =
|
func initFromBytes*(val: var ValidatorPrivKey, bytes: openarray[byte]) {.inline.} =
|
||||||
fromBytes(R, hexToSeqByte(hexStr))
|
discard val.fromBytes(bytes)
|
||||||
|
|
||||||
func initFromBytes*[T](val: var BlsValue[T], bytes: openarray[byte]) =
|
func fromBytes[T](R: type BlsValue[T], bytes: openarray[byte]): R {.inline.}=
|
||||||
val = fromBytes(BlsValue[T], bytes)
|
result.initFromBytes(bytes)
|
||||||
|
|
||||||
func initFromBytes*(val: var BlsCurveType, bytes: openarray[byte]) =
|
func fromHex*[T](R: var BlsValue[T], hexStr: string) {.inline.} =
|
||||||
val = init(type(val), bytes)
|
## 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.} =
|
proc writeValue*(writer: var JsonWriter, value: ValidatorPubKey) {.inline.} =
|
||||||
when value is BlsValue:
|
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.} =
|
proc readValue*(reader: var JsonReader, value: var ValidatorPrivKey) {.inline.} =
|
||||||
value.initFromBytes(fromHex reader.readValue(string))
|
value.initFromBytes(fromHex reader.readValue(string))
|
||||||
|
|
||||||
when ValidatorPrivKey is BlsValue:
|
proc writeValue*(writer: var JsonWriter, value: PublicKey) {.inline.} =
|
||||||
proc newPrivKey*(): ValidatorPrivKey =
|
|
||||||
ValidatorPrivKey(kind: Real, blsValue: SigKey.random())
|
|
||||||
else:
|
|
||||||
proc newPrivKey*(): ValidatorPrivKey =
|
|
||||||
SigKey.random()
|
|
||||||
|
|
||||||
proc writeValue*(writer: var JsonWriter, value: VerKey) {.inline.} =
|
|
||||||
writer.writeValue($value)
|
writer.writeValue($value)
|
||||||
|
|
||||||
proc readValue*(reader: var JsonReader, value: var VerKey) {.inline.} =
|
proc readValue*(reader: var JsonReader, value: var PublicKey) {.inline.} =
|
||||||
value = VerKey.init(reader.readValue(string))
|
let hex = reader.readValue(string)
|
||||||
|
let ok = value.fromHex(hex)
|
||||||
|
doAssert ok, "Invalid public key: " & hex
|
||||||
|
|
||||||
proc writeValue*(writer: var JsonWriter, value: Signature) {.inline.} =
|
proc writeValue*(writer: var JsonWriter, value: Signature) {.inline.} =
|
||||||
writer.writeValue($value)
|
writer.writeValue($value)
|
||||||
|
|
||||||
proc readValue*(reader: var JsonReader, value: var Signature) {.inline.} =
|
proc readValue*(reader: var JsonReader, value: var Signature) {.inline.} =
|
||||||
value = Signature.init(reader.readValue(string))
|
let hex = reader.readValue(string)
|
||||||
|
let ok = value.fromHex(hex)
|
||||||
proc toGaugeValue*(hash: Eth2Digest): int64 =
|
doAssert ok, "Invalid signature: " & hex
|
||||||
# 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]))
|
|
||||||
|
|
||||||
template fromSszBytes*(T: type BlsValue, bytes: openarray[byte]): auto =
|
template fromSszBytes*(T: type BlsValue, bytes: openarray[byte]): auto =
|
||||||
fromBytes(T, bytes)
|
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
|
||||||
|
|
|
@ -96,6 +96,9 @@ type
|
||||||
DOMAIN_SHARD_PROPOSER = 128
|
DOMAIN_SHARD_PROPOSER = 128
|
||||||
DOMAIN_SHARD_ATTESTER = 129
|
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
|
# https://github.com/nim-lang/Nim/issues/574 and be consistent across
|
||||||
# 32-bit and 64-bit word platforms.
|
# 32-bit and 64-bit word platforms.
|
||||||
# TODO VALIDATOR_REGISTRY_LIMIT is 1 shl 40 in 0.8.3, and
|
# 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
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#signingroot
|
||||||
SigningRoot* = object
|
SigningRoot* = object
|
||||||
object_root*: Eth2Digest
|
object_root*: Eth2Digest
|
||||||
domain*: uint64
|
domain*: Domain
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#signedvoluntaryexit
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#signedvoluntaryexit
|
||||||
SignedVoluntaryExit* = object
|
SignedVoluntaryExit* = object
|
||||||
|
|
|
@ -12,7 +12,6 @@ import
|
||||||
math,
|
math,
|
||||||
# Third-party
|
# Third-party
|
||||||
stew/endians2,
|
stew/endians2,
|
||||||
blscurve, # defines Domain
|
|
||||||
# Internal
|
# Internal
|
||||||
./datatypes, ./digest, ../ssz
|
./datatypes, ./digest, ../ssz
|
||||||
|
|
||||||
|
|
|
@ -100,12 +100,9 @@ proc process_randao(
|
||||||
let proposer = addr state.validators[proposer_index.get]
|
let proposer = addr state.validators[proposer_index.get]
|
||||||
|
|
||||||
# Verify that the provided randao value is valid
|
# Verify that the provided randao value is valid
|
||||||
if skipBlsValidation notin flags and not bls_verify(
|
let signing_root = compute_signing_root(epoch, get_domain(state, DOMAIN_RANDAO))
|
||||||
proposer.pubkey,
|
if skipBLSValidation notin flags:
|
||||||
hash_tree_root(epoch.uint64).data,
|
if not blsVerify(proposer.pubkey, signing_root.data, body.randao_reveal):
|
||||||
body.randao_reveal,
|
|
||||||
get_domain(state, DOMAIN_RANDAO)):
|
|
||||||
|
|
||||||
notice "Randao mismatch", proposer_pubkey = proposer.pubkey,
|
notice "Randao mismatch", proposer_pubkey = proposer.pubkey,
|
||||||
message = epoch,
|
message = epoch,
|
||||||
signature = body.randao_reveal,
|
signature = body.randao_reveal,
|
||||||
|
@ -167,13 +164,12 @@ proc process_proposer_slashing*(
|
||||||
if skipBlsValidation notin flags:
|
if skipBlsValidation notin flags:
|
||||||
for i, signed_header in [proposer_slashing.signed_header_1,
|
for i, signed_header in [proposer_slashing.signed_header_1,
|
||||||
proposer_slashing.signed_header_2]:
|
proposer_slashing.signed_header_2]:
|
||||||
if not bls_verify(
|
let domain = get_domain(
|
||||||
proposer.pubkey,
|
|
||||||
hash_tree_root(signed_header.message).data,
|
|
||||||
signed_header.signature,
|
|
||||||
get_domain(
|
|
||||||
state, DOMAIN_BEACON_PROPOSER,
|
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",
|
notice "Proposer slashing: invalid signature",
|
||||||
signature_index = i
|
signature_index = i
|
||||||
return false
|
return false
|
||||||
|
@ -339,11 +335,8 @@ proc process_voluntary_exit*(
|
||||||
# Verify signature
|
# Verify signature
|
||||||
if skipBlsValidation notin flags:
|
if skipBlsValidation notin flags:
|
||||||
let domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
|
let domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
|
||||||
if not bls_verify(
|
let signing_root = compute_signing_root(voluntary_exit, domain)
|
||||||
validator.pubkey,
|
if not bls_verify(validator.pubkey, signing_root.data, signed_voluntary_exit.signature):
|
||||||
hash_tree_root(voluntary_exit).data,
|
|
||||||
signed_voluntary_exit.signature,
|
|
||||||
domain):
|
|
||||||
notice "Exit: invalid signature"
|
notice "Exit: invalid signature"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ func getValidator*(pool: ValidatorPool,
|
||||||
validatorKey: ValidatorPubKey): AttachedValidator =
|
validatorKey: ValidatorPubKey): AttachedValidator =
|
||||||
pool.validators.getOrDefault(validatorKey)
|
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,
|
proc signBlockProposal*(v: AttachedValidator, fork: Fork, slot: Slot,
|
||||||
blockRoot: Eth2Digest): Future[ValidatorSig] {.async.} =
|
blockRoot: Eth2Digest): Future[ValidatorSig] {.async.} =
|
||||||
|
|
||||||
|
@ -38,7 +39,8 @@ proc signBlockProposal*(v: AttachedValidator, fork: Fork, slot: Slot,
|
||||||
# replaced by something more sensible
|
# replaced by something more sensible
|
||||||
await sleepAsync(chronos.milliseconds(1))
|
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:
|
else:
|
||||||
error "Unimplemented"
|
error "Unimplemented"
|
||||||
quit 1
|
quit 1
|
||||||
|
@ -56,18 +58,20 @@ proc signAttestation*(v: AttachedValidator,
|
||||||
# replaced by something more sensible
|
# replaced by something more sensible
|
||||||
await sleepAsync(chronos.milliseconds(1))
|
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:
|
else:
|
||||||
error "Unimplemented"
|
error "Unimplemented"
|
||||||
quit 1
|
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):
|
func genRandaoReveal*(k: ValidatorPrivKey, fork: Fork, slot: Slot):
|
||||||
ValidatorSig =
|
ValidatorSig =
|
||||||
let
|
let
|
||||||
domain = get_domain(fork, DOMAIN_RANDAO, compute_epoch_at_slot(slot))
|
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):
|
func genRandaoReveal*(v: AttachedValidator, fork: Fork, slot: Slot):
|
||||||
ValidatorSig =
|
ValidatorSig =
|
||||||
|
|
|
@ -138,18 +138,25 @@ proc main(nb_samples: Natural) =
|
||||||
for i in 0 ..< proof_of_possessions.len:
|
for i in 0 ..< proof_of_possessions.len:
|
||||||
pop_valid = pop_valid and proof_of_possessions[i].verifyPoP(pubkeys[i])
|
pop_valid = pop_valid and proof_of_possessions[i].verifyPoP(pubkeys[i])
|
||||||
|
|
||||||
var agg_pubkey: VerKey
|
# TODO: update with IETF API (Eth2 v0.10.1)
|
||||||
bench &"Benchmarking {num_validators} public keys aggregation", agg_pubkey:
|
# func fastAggregateVerify*[T: byte|char](
|
||||||
agg_pubkey = combine(pubkeys)
|
# publicKeys: openarray[PublicKey],
|
||||||
|
# message: openarray[T],
|
||||||
|
# signature: Signature # Aggregated signature
|
||||||
|
# ): bool
|
||||||
|
|
||||||
var agg_sig: Signature
|
# var agg_pubkey: VerKey
|
||||||
bench &"Benchmarking {num_validators} signatures aggregation", agg_sig:
|
# bench &"Benchmarking {num_validators} public keys aggregation", agg_pubkey:
|
||||||
agg_sig = combine(signatures)
|
# agg_pubkey = combine(pubkeys)
|
||||||
|
|
||||||
var msg_verif: bool
|
# var agg_sig: Signature
|
||||||
bench "Benchmarking message verification", msg_verif:
|
# bench &"Benchmarking {num_validators} signatures aggregation", agg_sig:
|
||||||
let domain = 0'u64
|
# agg_sig = combine(signatures)
|
||||||
msg_verif = agg_sig.verify(msg.data, domain, agg_pubkey)
|
|
||||||
|
# var msg_verif: bool
|
||||||
|
# bench "Benchmarking message verification", msg_verif:
|
||||||
|
# let domain = 0'u64
|
||||||
|
# msg_verif = agg_sig.verify(msg.data, domain, agg_pubkey)
|
||||||
|
|
||||||
#####################
|
#####################
|
||||||
#
|
#
|
||||||
|
|
|
@ -18,7 +18,6 @@ import # Unit test
|
||||||
./test_block_pool,
|
./test_block_pool,
|
||||||
./test_discovery_helpers,
|
./test_discovery_helpers,
|
||||||
./test_helpers,
|
./test_helpers,
|
||||||
./test_interop,
|
|
||||||
./test_kvstore,
|
./test_kvstore,
|
||||||
./test_kvstore_lmdb,
|
./test_kvstore_lmdb,
|
||||||
./test_kvstore_sqlite3,
|
./test_kvstore_sqlite3,
|
||||||
|
@ -31,6 +30,11 @@ import # Unit test
|
||||||
./test_sync_manager,
|
./test_sync_manager,
|
||||||
./test_honest_validator
|
./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
|
import # Refactor state transition unit tests
|
||||||
# TODO re-enable when useful
|
# TODO re-enable when useful
|
||||||
# ./spec_block_processing/test_genesis,
|
# ./spec_block_processing/test_genesis,
|
||||||
|
|
|
@ -60,16 +60,14 @@ proc get_attestation_signature(
|
||||||
): ValidatorSig =
|
): ValidatorSig =
|
||||||
|
|
||||||
let msg = attestation_data.hash_tree_root()
|
let msg = attestation_data.hash_tree_root()
|
||||||
|
let domain = get_domain(
|
||||||
return bls_sign(
|
|
||||||
key = privkey,
|
|
||||||
msg = msg.data,
|
|
||||||
domain = get_domain(
|
|
||||||
state = state,
|
state = state,
|
||||||
domain_type = DOMAIN_BEACON_ATTESTER,
|
domain_type = DOMAIN_BEACON_ATTESTER,
|
||||||
message_epoch = attestation_data.target.epoch
|
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) =
|
proc signMockAttestation*(state: BeaconState, attestation: var Attestation) =
|
||||||
var cache = get_empty_per_epoch_cache()
|
var cache = get_empty_per_epoch_cache()
|
||||||
|
@ -89,7 +87,7 @@ proc signMockAttestation*(state: BeaconState, attestation: var Attestation) =
|
||||||
attestation.signature = sig
|
attestation.signature = sig
|
||||||
first_iter = false
|
first_iter = false
|
||||||
else:
|
else:
|
||||||
combine(attestation.signature, sig)
|
aggregate(attestation.signature, sig)
|
||||||
|
|
||||||
proc mockAttestationImpl(
|
proc mockAttestationImpl(
|
||||||
state: BeaconState,
|
state: BeaconState,
|
||||||
|
|
|
@ -27,28 +27,27 @@ proc signMockBlockImpl(
|
||||||
|
|
||||||
let privkey = MockPrivKeys[proposer_index]
|
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(
|
|
||||||
state,
|
|
||||||
DOMAIN_RANDAO,
|
|
||||||
message_epoch = block_slot.compute_epoch_at_slot(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
signedBlock.signature = bls_sign(
|
block:
|
||||||
key = privkey,
|
let domain = get_domain(
|
||||||
msg = signedBlock.message.hash_tree_root().data,
|
state,
|
||||||
domain = get_domain(
|
DOMAIN_RANDAO,
|
||||||
state,
|
message_epoch = block_slot.compute_epoch_at_slot(),
|
||||||
DOMAIN_BEACON_PROPOSER,
|
)
|
||||||
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)
|
||||||
|
|
||||||
|
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*(
|
proc signMockBlock*(
|
||||||
state: BeaconState,
|
state: BeaconState,
|
||||||
|
|
|
@ -23,13 +23,17 @@ func signMockDepositData(
|
||||||
privkey: ValidatorPrivKey
|
privkey: ValidatorPrivKey
|
||||||
) =
|
) =
|
||||||
# No state --> Genesis
|
# No state --> Genesis
|
||||||
deposit_data.signature = bls_sign(
|
let domain = compute_domain(
|
||||||
key = privkey,
|
|
||||||
msg = deposit_data.getDepositMessage().hash_tree_root().data,
|
|
||||||
domain = compute_domain(
|
|
||||||
DOMAIN_DEPOSIT,
|
DOMAIN_DEPOSIT,
|
||||||
default(array[4, byte]) # Genesis is fork_version 0
|
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(
|
func signMockDepositData(
|
||||||
|
@ -37,13 +41,17 @@ func signMockDepositData(
|
||||||
privkey: ValidatorPrivKey,
|
privkey: ValidatorPrivKey,
|
||||||
state: BeaconState
|
state: BeaconState
|
||||||
) =
|
) =
|
||||||
deposit_data.signature = bls_sign(
|
let domain = compute_domain(
|
||||||
key = privkey,
|
DOMAIN_DEPOSIT,
|
||||||
msg = deposit_data.getDepositMessage().hash_tree_root().data,
|
default(array[4, byte]) # Genesis is fork_version 0
|
||||||
domain = get_domain(
|
|
||||||
state,
|
|
||||||
DOMAIN_DEPOSIT
|
|
||||||
)
|
)
|
||||||
|
let signing_root = compute_signing_root(
|
||||||
|
deposit_data.getDepositMessage(),
|
||||||
|
domain
|
||||||
|
)
|
||||||
|
deposit_data.signature = blsSign(
|
||||||
|
privkey,
|
||||||
|
signing_root.data
|
||||||
)
|
)
|
||||||
|
|
||||||
func mockDepositData(
|
func mockDepositData(
|
||||||
|
|
|
@ -17,7 +17,8 @@ import
|
||||||
let MockPrivKeys* = block:
|
let MockPrivKeys* = block:
|
||||||
var privkeys: array[MIN_GENESIS_ACTIVE_VALIDATOR_COUNT, ValidatorPrivKey]
|
var privkeys: array[MIN_GENESIS_ACTIVE_VALIDATOR_COUNT, ValidatorPrivKey]
|
||||||
for pk in privkeys.mitems():
|
for pk in privkeys.mitems():
|
||||||
pk = newPrivKey()
|
let pair = newKeyPair()
|
||||||
|
pk = pair.priv
|
||||||
privkeys
|
privkeys
|
||||||
|
|
||||||
let MockPubKeys* = block:
|
let MockPubKeys* = block:
|
||||||
|
|
|
@ -60,11 +60,6 @@ suite "Beacon chain DB" & preset():
|
||||||
timedTest "find ancestors" & preset():
|
timedTest "find ancestors" & preset():
|
||||||
var
|
var
|
||||||
db = init(BeaconChainDB, kvStore MemoryStoreRef.init())
|
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
|
let
|
||||||
a0 = SignedBeaconBlock(message: BeaconBlock(slot: GENESIS_SLOT + 0))
|
a0 = SignedBeaconBlock(message: BeaconBlock(slot: GENESIS_SLOT + 0))
|
||||||
|
|
|
@ -5,6 +5,10 @@ import
|
||||||
../beacon_chain/[extras, interop, ssz],
|
../beacon_chain/[extras, interop, ssz],
|
||||||
../beacon_chain/spec/[beaconstate, crypto, helpers, datatypes]
|
../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:
|
# Interop test yaml, found here:
|
||||||
# https://github.com/ethereum/eth2.0-pm/blob/a0b9d22fad424574b1307828f867b30237758468/interop/mocked_start/keygen_10_validators.yaml
|
# https://github.com/ethereum/eth2.0-pm/blob/a0b9d22fad424574b1307828f867b30237758468/interop/mocked_start/keygen_10_validators.yaml
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ suite "Zero signature sanity checks":
|
||||||
timedTest "SSZ serialization roundtrip of SignedBeaconBlockHeader":
|
timedTest "SSZ serialization roundtrip of SignedBeaconBlockHeader":
|
||||||
|
|
||||||
let defaultBlockHeader = SignedBeaconBlockHeader(
|
let defaultBlockHeader = SignedBeaconBlockHeader(
|
||||||
signature: BlsValue[Signature](kind: OpaqueBlob)
|
signature: ValidatorSig(kind: OpaqueBlob)
|
||||||
)
|
)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
|
|
|
@ -13,21 +13,12 @@ import
|
||||||
../beacon_chain/spec/[beaconstate, crypto, datatypes, digest,
|
../beacon_chain/spec/[beaconstate, crypto, datatypes, digest,
|
||||||
helpers, validator]
|
helpers, validator]
|
||||||
|
|
||||||
when ValidatorPrivKey is BlsValue:
|
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,
|
||||||
# 0 is not a valid BLS private key - 1000 helps interop with rust BLS library,
|
# lighthouse.
|
||||||
# lighthouse.
|
# TODO: switch to https://github.com/ethereum/eth2.0-pm/issues/60
|
||||||
# TODO: switch to https://github.com/ethereum/eth2.0-pm/issues/60
|
var bytes = uint64(i + 1000).toBytesLE()
|
||||||
result.kind = BlsValueType.Real
|
copyMem(addr result, addr bytes[0], sizeof(bytes))
|
||||||
var bytes = uint64(i + 1000).toBytesLE()
|
|
||||||
copyMem(addr result.blsValue.x[0], addr bytes[0], sizeof(bytes))
|
|
||||||
else:
|
|
||||||
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))
|
|
||||||
|
|
||||||
func makeFakeHash(i: int): Eth2Digest =
|
func makeFakeHash(i: int): Eth2Digest =
|
||||||
var bytes = uint64(i).toBytesLE()
|
var bytes = uint64(i).toBytesLE()
|
||||||
|
@ -62,10 +53,9 @@ func makeDeposit(i: int, flags: UpdateFlags): Deposit =
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if skipBlsValidation notin flags:
|
if skipBLSValidation notin flags:
|
||||||
result.data.signature =
|
let signing_root = compute_signing_root(result.getDepositMessage, domain)
|
||||||
bls_sign(privkey, hash_tree_root(result.getDepositMessage).data,
|
result.data.signature = bls_sign(privkey, signing_root.data)
|
||||||
domain)
|
|
||||||
|
|
||||||
func makeInitialDeposits*(
|
func makeInitialDeposits*(
|
||||||
n = SLOTS_PER_EPOCH, flags: UpdateFlags = {}): seq[Deposit] =
|
n = SLOTS_PER_EPOCH, flags: UpdateFlags = {}): seq[Deposit] =
|
||||||
|
@ -123,20 +113,16 @@ proc addBlock*(
|
||||||
doAssert privKey.pubKey() == proposer.pubkey,
|
doAssert privKey.pubKey() == proposer.pubkey,
|
||||||
"signature key should be derived from private key! - wrong privkey?"
|
"signature key should be derived from private key! - wrong privkey?"
|
||||||
|
|
||||||
if skipBlsValidation notin flags:
|
if skipBLSValidation notin flags:
|
||||||
let block_root = hash_tree_root(new_block.message)
|
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!
|
# We have a signature - put it in the block and we should be done!
|
||||||
new_block.signature =
|
new_block.signature = bls_sign(privKey, signing_root.data)
|
||||||
bls_sign(privKey, block_root.data,
|
|
||||||
get_domain(state, DOMAIN_BEACON_PROPOSER,
|
|
||||||
compute_epoch_at_slot(new_block.message.slot)))
|
|
||||||
|
|
||||||
doAssert bls_verify(
|
doAssert bls_verify(
|
||||||
proposer.pubkey,
|
proposer.pubkey,
|
||||||
block_root.data, new_block.signature,
|
signing_root.data, new_block.signature),
|
||||||
get_domain(
|
|
||||||
state, DOMAIN_BEACON_PROPOSER,
|
|
||||||
compute_epoch_at_slot(new_block.message.slot))),
|
|
||||||
"we just signed this message - it should pass verification!"
|
"we just signed this message - it should pass verification!"
|
||||||
|
|
||||||
new_block
|
new_block
|
||||||
|
@ -171,12 +157,11 @@ proc makeAttestation*(
|
||||||
aggregation_bits.setBit sac_index
|
aggregation_bits.setBit sac_index
|
||||||
|
|
||||||
let
|
let
|
||||||
msg = hash_tree_root(data)
|
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, data.target.epoch)
|
||||||
|
signing_root = compute_signing_root(data, domain)
|
||||||
sig =
|
sig =
|
||||||
if skipBlsValidation notin flags:
|
if skipBLSValidation notin flags:
|
||||||
bls_sign(
|
bls_sign(hackPrivKey(validator), signing_root.data)
|
||||||
hackPrivKey(validator), msg.data,
|
|
||||||
get_domain(state, DOMAIN_BEACON_ATTESTER, data.target.epoch))
|
|
||||||
else:
|
else:
|
||||||
ValidatorSig()
|
ValidatorSig()
|
||||||
|
|
||||||
|
@ -223,19 +208,27 @@ proc makeFullAttestations*(
|
||||||
let
|
let
|
||||||
committee = get_beacon_committee(state, slot, index, cache)
|
committee = get_beacon_committee(state, slot, index, cache)
|
||||||
data = makeAttestationData(state, slot, index, beacon_block_root)
|
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
|
doAssert committee.len() >= 1
|
||||||
attestation = Attestation(
|
# Initial attestation
|
||||||
aggregation_bits: CommitteeValidatorsBits.init(committee.len),
|
var attestation = Attestation(
|
||||||
data: data,
|
aggregation_bits: CommitteeValidatorsBits.init(committee.len),
|
||||||
signature: ValidatorSig(kind: Real, blsValue: Signature.init())
|
data: data,
|
||||||
|
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
|
attestation.aggregation_bits.setBit j
|
||||||
if skipBlsValidation notin flags:
|
if skipBLSValidation notin flags:
|
||||||
attestation.signature.combine(bls_sign(
|
attestation.signature.aggregate(bls_sign(
|
||||||
hackPrivKey(state.validators[committee[j]]), msg.data,
|
hackPrivKey(state.validators[committee[j]]),
|
||||||
get_domain(state, DOMAIN_BEACON_ATTESTER, data.target.epoch)))
|
signing_root.data
|
||||||
|
))
|
||||||
|
|
||||||
result.add attestation
|
result.add attestation
|
||||||
|
|
Loading…
Reference in New Issue