2018-11-28 19:49:03 +00:00
|
|
|
# beacon_chain
|
2019-02-28 21:24:43 +00:00
|
|
|
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
2018-11-28 19:49:03 +00:00
|
|
|
# Licensed and distributed under either of
|
|
|
|
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
|
|
|
# At the time of writing, the exact definitions of what should be used for
|
|
|
|
# cryptography in the spec is in flux, with sizes and test vectors still being
|
|
|
|
# hashed out. This layer helps isolate those chagnes.
|
|
|
|
|
2018-12-27 20:14:37 +00:00
|
|
|
# Useful conversation about BLS signatures (TODO: condense this)
|
|
|
|
#
|
|
|
|
# I can probably google this somehow, but bls signatures, anyone knows off the
|
|
|
|
# top of their head if they have to be combined one by one, or can two group
|
|
|
|
# signatures be combined? what happens to overlap then?
|
|
|
|
# Danny Ryan
|
|
|
|
# @djrtwo
|
|
|
|
# Dec 21 12:00
|
|
|
|
# Yeah, you can do any linear combination of signatures. but you have to
|
|
|
|
# remember the linear combination of pubkeys that constructed
|
|
|
|
# if you have two instances of a signature from pubkey p, then you need 2*p in
|
|
|
|
# the group pubkey
|
|
|
|
# because the attestation bitfield is only 1 bit per pubkey right now,
|
|
|
|
# attestations do not support this
|
|
|
|
# it could be extended to support N overlaps up to N times per pubkey if we
|
|
|
|
# had N bits per validator instead of 1
|
|
|
|
# We are shying away from this for the time being. If there end up being
|
|
|
|
# substantial difficulties in network layer aggregation, then adding bits
|
|
|
|
# to aid in supporting overlaps is one potential solution
|
|
|
|
# Jacek Sieka
|
|
|
|
# @arnetheduck
|
|
|
|
# Dec 21 12:02
|
|
|
|
# ah nice, you anticipated my followup question there :) so it's not a
|
|
|
|
# straight-off set union operation
|
|
|
|
# Danny Ryan
|
|
|
|
# @djrtwo
|
|
|
|
# Dec 21 12:02
|
|
|
|
# depending on the particular network level troubles we run into
|
|
|
|
# right
|
|
|
|
# aggregatng sigs and pubkeys are both just ec adds
|
|
|
|
# https://github.com/ethereum/py-evm/blob/d82b10ae361cde6abbac62f171fcea7809c4e3cf/eth/_utils/bls.py#L191-L202
|
|
|
|
# subtractions work too (i suppose this is obvious). You can linearly combine
|
|
|
|
# sigs or pubs in any way
|
|
|
|
|
|
|
|
|
2018-11-28 19:49:03 +00:00
|
|
|
import
|
2019-02-28 21:24:43 +00:00
|
|
|
sequtils,
|
2019-03-01 16:51:37 +00:00
|
|
|
hashes, eth/rlp,
|
2019-03-01 13:34:37 +00:00
|
|
|
blscurve, json_serialization
|
2018-12-19 12:58:53 +00:00
|
|
|
|
|
|
|
export
|
2019-03-01 13:34:37 +00:00
|
|
|
json_serialization
|
2019-02-05 16:13:29 +00:00
|
|
|
|
2019-02-07 10:51:21 +00:00
|
|
|
export blscurve.init, blscurve.getBytes, blscurve.combine, blscurve.`$`, blscurve.`==`
|
2018-11-28 19:49:03 +00:00
|
|
|
|
|
|
|
type
|
2019-02-05 16:13:29 +00:00
|
|
|
ValidatorPubKey* = blscurve.VerKey
|
|
|
|
ValidatorPrivKey* = blscurve.SigKey
|
|
|
|
ValidatorSig* = blscurve.Signature
|
|
|
|
ValidatorPKI* = ValidatorPrivKey|ValidatorPubKey|ValidatorSig
|
2018-11-29 01:08:34 +00:00
|
|
|
|
2019-02-28 21:21:29 +00:00
|
|
|
func shortLog*(x: ValidatorPKI): string =
|
|
|
|
($x)[0..7]
|
|
|
|
|
2018-11-29 01:08:34 +00:00
|
|
|
template hash*(k: ValidatorPubKey|ValidatorPrivKey): Hash =
|
2019-02-05 16:13:29 +00:00
|
|
|
hash(k.getBytes())
|
2018-11-29 01:08:34 +00:00
|
|
|
|
2019-02-05 16:13:29 +00:00
|
|
|
func pubKey*(pk: ValidatorPrivKey): ValidatorPubKey = pk.getKey()
|
2018-12-05 13:58:41 +00:00
|
|
|
|
2019-02-20 20:35:27 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/bls_signature.md#bls_aggregate_pubkeys
|
2018-12-17 19:36:17 +00:00
|
|
|
func bls_aggregate_pubkeys*(keys: openArray[ValidatorPubKey]): ValidatorPubKey =
|
2018-12-21 23:47:55 +00:00
|
|
|
var empty = true
|
2018-12-11 17:55:45 +00:00
|
|
|
for key in keys:
|
|
|
|
if empty:
|
|
|
|
result = key
|
|
|
|
empty = false
|
|
|
|
else:
|
|
|
|
result.combine(key)
|
|
|
|
|
2019-02-20 20:35:27 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/bls_signature.md#bls_verify
|
2018-12-17 19:36:17 +00:00
|
|
|
func bls_verify*(
|
2018-12-11 17:55:45 +00:00
|
|
|
pubkey: ValidatorPubKey, msg: openArray[byte], sig: ValidatorSig,
|
|
|
|
domain: uint64): bool =
|
|
|
|
# name from spec!
|
2019-02-05 16:13:29 +00:00
|
|
|
sig.verify(msg, domain, pubkey)
|
|
|
|
|
2019-02-28 21:24:43 +00:00
|
|
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/bls_signature.md#bls_verify_multiple
|
2019-02-07 23:07:15 +00:00
|
|
|
func bls_verify_multiple*(
|
2019-02-28 21:24:43 +00:00
|
|
|
pubkeys: seq[ValidatorPubKey], message_hashes: seq[array[0..31, byte]],
|
2019-02-07 23:07:15 +00:00
|
|
|
sig: ValidatorSig, domain: uint64): bool =
|
|
|
|
let L = len(pubkeys)
|
2019-02-28 21:24:43 +00:00
|
|
|
doAssert L == len(message_hashes)
|
|
|
|
|
|
|
|
# TODO optimize using multiPairing
|
|
|
|
for pubkey_message_hash in zip(pubkeys, message_hashes):
|
|
|
|
let (pubkey, message_hash) = pubkey_message_hash
|
|
|
|
# TODO spec doesn't say to handle this specially, but it's silly to
|
|
|
|
# validate without any actual public keys.
|
|
|
|
if pubkey != ValidatorPubKey() and
|
|
|
|
not sig.verify(message_hash, domain, pubkey):
|
|
|
|
return false
|
2019-02-07 23:07:15 +00:00
|
|
|
|
|
|
|
true
|
|
|
|
|
|
|
|
func bls_sign*(key: ValidatorPrivKey, msg: openarray[byte],
|
|
|
|
domain: uint64): ValidatorSig =
|
2019-02-05 16:13:29 +00:00
|
|
|
# name from spec!
|
|
|
|
key.sign(domain, msg)
|
2018-12-19 12:58:53 +00:00
|
|
|
|
|
|
|
proc writeValue*(writer: var JsonWriter, value: ValidatorPubKey) {.inline.} =
|
2019-02-05 16:13:29 +00:00
|
|
|
writer.writeValue($value)
|
2018-12-19 12:58:53 +00:00
|
|
|
|
|
|
|
proc readValue*(reader: var JsonReader, value: var ValidatorPubKey) {.inline.} =
|
2019-02-05 16:13:29 +00:00
|
|
|
value = VerKey.init(reader.readValue(string))
|
2018-12-19 12:58:53 +00:00
|
|
|
|
|
|
|
proc writeValue*(writer: var JsonWriter, value: ValidatorSig) {.inline.} =
|
2019-02-05 16:13:29 +00:00
|
|
|
writer.writeValue($value)
|
2018-12-19 12:58:53 +00:00
|
|
|
|
|
|
|
proc readValue*(reader: var JsonReader, value: var ValidatorSig) {.inline.} =
|
2019-02-05 16:13:29 +00:00
|
|
|
value = Signature.init(reader.readValue(string))
|
2018-12-19 12:58:53 +00:00
|
|
|
|
|
|
|
proc writeValue*(writer: var JsonWriter, value: ValidatorPrivKey) {.inline.} =
|
2019-02-05 16:13:29 +00:00
|
|
|
writer.writeValue($value)
|
2018-12-19 12:58:53 +00:00
|
|
|
|
|
|
|
proc readValue*(reader: var JsonReader, value: var ValidatorPrivKey) {.inline.} =
|
2019-02-05 16:13:29 +00:00
|
|
|
value = SigKey.init(reader.readValue(string))
|
2018-12-19 12:58:53 +00:00
|
|
|
|
2019-02-06 19:58:18 +00:00
|
|
|
proc newPrivKey*(): ValidatorPrivKey = SigKey.random()
|
2019-03-01 16:51:37 +00:00
|
|
|
|
2019-02-18 10:34:39 +00:00
|
|
|
# RLP serialization (TODO: remove if no longer necessary)
|
2019-03-01 16:51:37 +00:00
|
|
|
proc append*(writer: var RlpWriter, value: ValidatorPubKey) =
|
|
|
|
writer.append value.getBytes()
|
|
|
|
|
|
|
|
proc read*(rlp: var Rlp, T: type ValidatorPubKey): T {.inline.} =
|
2019-03-04 11:50:26 +00:00
|
|
|
result = ValidatorPubKey.init(rlp.toBytes.toOpenArray)
|
|
|
|
rlp.skipElem()
|
2019-03-01 16:51:37 +00:00
|
|
|
|
|
|
|
proc append*(writer: var RlpWriter, value: ValidatorSig) =
|
|
|
|
writer.append value.getBytes()
|
|
|
|
|
|
|
|
proc read*(rlp: var Rlp, T: type ValidatorSig): T {.inline.} =
|
2019-03-04 11:50:26 +00:00
|
|
|
result = ValidatorSig.init(rlp.toBytes.toOpenArray)
|
|
|
|
rlp.skipElem()
|
2019-03-01 16:51:37 +00:00
|
|
|
|