Merge branch 'master' into simple-cached-tree-hash

This commit is contained in:
Paul Hauner
2019-04-08 19:50:55 +10:00
108 changed files with 6820 additions and 1528 deletions

View File

@@ -1,7 +1,7 @@
use super::PublicKey;
use bls_aggregates::AggregatePublicKey as RawAggregatePublicKey;
/// A single BLS signature.
/// A BLS aggregate public key.
///
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
/// serialization).
@@ -17,7 +17,7 @@ impl AggregatePublicKey {
self.0.add(public_key.as_raw())
}
/// Returns the underlying signature.
/// Returns the underlying public key.
pub fn as_raw(&self) -> &RawAggregatePublicKey {
&self.0
}

View File

@@ -36,6 +36,12 @@ impl AggregateSignature {
}
}
/// Add (aggregate) another `AggregateSignature`.
pub fn add_aggregate(&mut self, agg_signature: &AggregateSignature) {
self.aggregate_signature
.add_aggregate(&agg_signature.aggregate_signature)
}
/// Verify the `AggregateSignature` against an `AggregatePublicKey`.
///
/// Only returns `true` if the set of keys in the `AggregatePublicKey` match the set of keys

View File

@@ -0,0 +1,125 @@
use super::{fake_signature::FakeSignature, AggregatePublicKey, BLS_AGG_SIG_BYTE_SIZE};
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, PrefixedHexVisitor};
use ssz::{hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash};
/// A BLS aggregate signature.
///
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
/// serialization).
#[derive(Debug, PartialEq, Clone, Default, Eq)]
pub struct FakeAggregateSignature {
bytes: Vec<u8>,
}
impl FakeAggregateSignature {
/// Creates a new all-zero's signature
pub fn new() -> Self {
Self::zero()
}
/// Creates a new all-zero's signature
pub fn zero() -> Self {
Self {
bytes: vec![0; BLS_AGG_SIG_BYTE_SIZE],
}
}
/// Does glorious nothing.
pub fn add(&mut self, _signature: &FakeSignature) {
// Do nothing.
}
/// Does glorious nothing.
pub fn add_aggregate(&mut self, _agg_sig: &FakeAggregateSignature) {
// Do nothing.
}
/// _Always_ returns `true`.
pub fn verify(
&self,
_msg: &[u8],
_domain: u64,
_aggregate_public_key: &AggregatePublicKey,
) -> bool {
true
}
/// _Always_ returns `true`.
pub fn verify_multiple(
&self,
_messages: &[&[u8]],
_domain: u64,
_aggregate_public_keys: &[&AggregatePublicKey],
) -> bool {
true
}
}
impl Encodable for FakeAggregateSignature {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(&self.bytes);
}
}
impl Decodable for FakeAggregateSignature {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() - i < BLS_AGG_SIG_BYTE_SIZE {
return Err(DecodeError::TooShort);
}
Ok((
FakeAggregateSignature {
bytes: bytes[i..(i + BLS_AGG_SIG_BYTE_SIZE)].to_vec(),
},
i + BLS_AGG_SIG_BYTE_SIZE,
))
}
}
impl Serialize for FakeAggregateSignature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&hex_encode(ssz_encode(self)))
}
}
impl<'de> Deserialize<'de> for FakeAggregateSignature {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
let (obj, _) = <_>::ssz_decode(&bytes[..], 0)
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(obj)
}
}
impl TreeHash for FakeAggregateSignature {
fn hash_tree_root(&self) -> Vec<u8> {
hash(&self.bytes)
}
}
#[cfg(test)]
mod tests {
use super::super::{Keypair, Signature};
use super::*;
use ssz::ssz_encode;
#[test]
pub fn test_ssz_round_trip() {
let keypair = Keypair::random();
let mut original = FakeAggregateSignature::new();
original.add(&Signature::new(&[42, 42], 0, &keypair.sk));
let bytes = ssz_encode(&original);
let (decoded, _) = FakeAggregateSignature::ssz_decode(&bytes, 0).unwrap();
assert_eq!(original, decoded);
}
}

View File

@@ -0,0 +1,120 @@
use super::{PublicKey, SecretKey, BLS_SIG_BYTE_SIZE};
use hex::encode as hex_encode;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::HexVisitor;
use ssz::{hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash};
/// A single BLS signature.
///
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
/// serialization).
#[derive(Debug, PartialEq, Clone, Eq)]
pub struct FakeSignature {
bytes: Vec<u8>,
}
impl FakeSignature {
/// Creates a new all-zero's signature
pub fn new(_msg: &[u8], _domain: u64, _sk: &SecretKey) -> Self {
FakeSignature::zero()
}
/// Creates a new all-zero's signature
pub fn zero() -> Self {
Self {
bytes: vec![0; BLS_SIG_BYTE_SIZE],
}
}
/// Creates a new all-zero's signature
pub fn new_hashed(_x_real_hashed: &[u8], _x_imaginary_hashed: &[u8], _sk: &SecretKey) -> Self {
FakeSignature::zero()
}
/// _Always_ returns `true`.
pub fn verify(&self, _msg: &[u8], _domain: u64, _pk: &PublicKey) -> bool {
true
}
/// _Always_ returns true.
pub fn verify_hashed(
&self,
_x_real_hashed: &[u8],
_x_imaginary_hashed: &[u8],
_pk: &PublicKey,
) -> bool {
true
}
/// Returns a new empty signature.
pub fn empty_signature() -> Self {
FakeSignature::zero()
}
}
impl Encodable for FakeSignature {
fn ssz_append(&self, s: &mut SszStream) {
s.append_encoded_raw(&self.bytes);
}
}
impl Decodable for FakeSignature {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
if bytes.len() - i < BLS_SIG_BYTE_SIZE {
return Err(DecodeError::TooShort);
}
Ok((
FakeSignature {
bytes: bytes[i..(i + BLS_SIG_BYTE_SIZE)].to_vec(),
},
i + BLS_SIG_BYTE_SIZE,
))
}
}
impl TreeHash for FakeSignature {
fn hash_tree_root(&self) -> Vec<u8> {
hash(&self.bytes)
}
}
impl Serialize for FakeSignature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&hex_encode(ssz_encode(self)))
}
}
impl<'de> Deserialize<'de> for FakeSignature {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let bytes = deserializer.deserialize_str(HexVisitor)?;
let (pubkey, _) = <_>::ssz_decode(&bytes[..], 0)
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
Ok(pubkey)
}
}
#[cfg(test)]
mod tests {
use super::super::Keypair;
use super::*;
use ssz::ssz_encode;
#[test]
pub fn test_ssz_round_trip() {
let keypair = Keypair::random();
let original = FakeSignature::new(&[42, 42], 0, &keypair.sk);
let bytes = ssz_encode(&original);
let (decoded, _) = FakeSignature::ssz_decode(&bytes, 0).unwrap();
assert_eq!(original, decoded);
}
}

View File

@@ -1,7 +1,9 @@
use super::{PublicKey, SecretKey};
use serde_derive::{Deserialize, Serialize};
use std::fmt;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
pub struct Keypair {
pub sk: SecretKey,
pub pk: PublicKey,
@@ -19,3 +21,27 @@ impl Keypair {
self.pk.concatenated_hex_id()
}
}
impl PartialEq for Keypair {
fn eq(&self, other: &Keypair) -> bool {
self == other
}
}
impl Hash for Keypair {
/// Note: this is distinct from consensus serialization, it will produce a different hash.
///
/// This method uses the uncompressed bytes, which are much faster to obtain than the
/// compressed bytes required for consensus serialization.
///
/// Use `ssz::Encode` to obtain the bytes required for consensus hashing.
fn hash<H: Hasher>(&self, state: &mut H) {
self.pk.as_uncompressed_bytes().hash(state)
}
}
impl fmt::Display for Keypair {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.pk)
}
}

View File

@@ -2,18 +2,32 @@ extern crate bls_aggregates;
extern crate ssz;
mod aggregate_public_key;
mod aggregate_signature;
mod keypair;
mod public_key;
mod secret_key;
#[cfg(not(debug_assertions))]
mod aggregate_signature;
#[cfg(not(debug_assertions))]
mod signature;
#[cfg(not(debug_assertions))]
pub use crate::aggregate_signature::AggregateSignature;
#[cfg(not(debug_assertions))]
pub use crate::signature::Signature;
#[cfg(debug_assertions)]
mod fake_aggregate_signature;
#[cfg(debug_assertions)]
mod fake_signature;
#[cfg(debug_assertions)]
pub use crate::fake_aggregate_signature::FakeAggregateSignature as AggregateSignature;
#[cfg(debug_assertions)]
pub use crate::fake_signature::FakeSignature as Signature;
pub use crate::aggregate_public_key::AggregatePublicKey;
pub use crate::aggregate_signature::AggregateSignature;
pub use crate::keypair::Keypair;
pub use crate::public_key::PublicKey;
pub use crate::secret_key::SecretKey;
pub use crate::signature::Signature;
pub const BLS_AGG_SIG_BYTE_SIZE: usize = 96;
pub const BLS_SIG_BYTE_SIZE: usize = 96;

View File

@@ -5,6 +5,7 @@ use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, HexVisitor};
use ssz::{decode, hash, ssz_encode, Decodable, DecodeError, Encodable, SszStream, TreeHash};
use std::default;
use std::fmt;
use std::hash::{Hash, Hasher};
/// A single BLS signature.
@@ -52,6 +53,12 @@ impl PublicKey {
}
}
impl fmt::Display for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.concatenated_hex_id())
}
}
impl default::Default for PublicKey {
fn default() -> Self {
let secret_key = SecretKey::random();

View File

@@ -33,10 +33,21 @@ impl BooleanBitfield {
}
/// Create a new bitfield with the given length `initial_len` and all values set to `bit`.
pub fn from_elem(inital_len: usize, bit: bool) -> Self {
Self {
0: BitVec::from_elem(inital_len, bit),
///
/// Note: if `initial_len` is not a multiple of 8, the remaining bits will be set to `false`
/// regardless of `bit`.
pub fn from_elem(initial_len: usize, bit: bool) -> Self {
// BitVec can panic if we don't set the len to be a multiple of 8.
let full_len = ((initial_len + 7) / 8) * 8;
let mut bitfield = BitVec::from_elem(full_len, false);
if bit {
for i in 0..initial_len {
bitfield.set(i, true);
}
}
Self { 0: bitfield }
}
/// Create a new bitfield using the supplied `bytes` as input
@@ -89,6 +100,11 @@ impl BooleanBitfield {
self.len() == 0
}
/// Returns true if all bits are set to 0.
pub fn is_zero(&self) -> bool {
self.0.none()
}
/// Returns the number of bytes required to represent this bitfield.
pub fn num_bytes(&self) -> usize {
self.to_bytes().len()
@@ -104,6 +120,44 @@ impl BooleanBitfield {
pub fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes()
}
/// Compute the intersection (binary-and) of this bitfield with another. Lengths must match.
pub fn intersection(&self, other: &Self) -> Self {
let mut res = self.clone();
res.intersection_inplace(other);
res
}
/// Like `intersection` but in-place (updates `self`).
pub fn intersection_inplace(&mut self, other: &Self) {
self.0.intersect(&other.0);
}
/// Compute the union (binary-or) of this bitfield with another. Lengths must match.
pub fn union(&self, other: &Self) -> Self {
let mut res = self.clone();
res.union_inplace(other);
res
}
/// Like `union` but in-place (updates `self`).
pub fn union_inplace(&mut self, other: &Self) {
self.0.union(&other.0);
}
/// Compute the difference (binary-minus) of this bitfield with another. Lengths must match.
///
/// Computes `self - other`.
pub fn difference(&self, other: &Self) -> Self {
let mut res = self.clone();
res.difference_inplace(other);
res
}
/// Like `difference` but in-place (updates `self`).
pub fn difference_inplace(&mut self, other: &Self) {
self.0.difference(&other.0);
}
}
impl default::Default for BooleanBitfield {
@@ -125,10 +179,11 @@ impl cmp::PartialEq for BooleanBitfield {
/// Create a new bitfield that is a union of two other bitfields.
///
/// For example `union(0101, 1000) == 1101`
impl std::ops::BitAnd for BooleanBitfield {
// TODO: length-independent intersection for BitAnd
impl std::ops::BitOr for BooleanBitfield {
type Output = Self;
fn bitand(self, other: Self) -> Self {
fn bitor(self, other: Self) -> Self {
let (biggest, smallest) = if self.len() > other.len() {
(&self, &other)
} else {
@@ -419,10 +474,59 @@ mod tests {
}
#[test]
fn test_bitand() {
fn test_bitor() {
let a = BooleanBitfield::from_bytes(&vec![2, 8, 1][..]);
let b = BooleanBitfield::from_bytes(&vec![4, 8, 16][..]);
let c = BooleanBitfield::from_bytes(&vec![6, 8, 17][..]);
assert_eq!(c, a & b);
assert_eq!(c, a | b);
}
#[test]
fn test_is_zero() {
let yes_data: &[&[u8]] = &[&[], &[0], &[0, 0], &[0, 0, 0]];
for bytes in yes_data {
assert!(BooleanBitfield::from_bytes(bytes).is_zero());
}
let no_data: &[&[u8]] = &[&[1], &[6], &[0, 1], &[0, 0, 1], &[0, 0, 255]];
for bytes in no_data {
assert!(!BooleanBitfield::from_bytes(bytes).is_zero());
}
}
#[test]
fn test_intersection() {
let a = BooleanBitfield::from_bytes(&[0b1100, 0b0001]);
let b = BooleanBitfield::from_bytes(&[0b1011, 0b1001]);
let c = BooleanBitfield::from_bytes(&[0b1000, 0b0001]);
assert_eq!(a.intersection(&b), c);
assert_eq!(b.intersection(&a), c);
assert_eq!(a.intersection(&c), c);
assert_eq!(b.intersection(&c), c);
assert_eq!(a.intersection(&a), a);
assert_eq!(b.intersection(&b), b);
assert_eq!(c.intersection(&c), c);
}
#[test]
fn test_union() {
let a = BooleanBitfield::from_bytes(&[0b1100, 0b0001]);
let b = BooleanBitfield::from_bytes(&[0b1011, 0b1001]);
let c = BooleanBitfield::from_bytes(&[0b1111, 0b1001]);
assert_eq!(a.union(&b), c);
assert_eq!(b.union(&a), c);
assert_eq!(a.union(&a), a);
assert_eq!(b.union(&b), b);
assert_eq!(c.union(&c), c);
}
#[test]
fn test_difference() {
let a = BooleanBitfield::from_bytes(&[0b1100, 0b0001]);
let b = BooleanBitfield::from_bytes(&[0b1011, 0b1001]);
let a_b = BooleanBitfield::from_bytes(&[0b0100, 0b0000]);
let b_a = BooleanBitfield::from_bytes(&[0b0011, 0b1000]);
assert_eq!(a.difference(&b), a_b);
assert_eq!(b.difference(&a), b_a);
assert!(a.difference(&a).is_zero());
}
}

View File

@@ -3,10 +3,13 @@ mod testing_slot_clock;
pub use crate::system_time_slot_clock::{Error as SystemTimeSlotClockError, SystemTimeSlotClock};
pub use crate::testing_slot_clock::{Error as TestingSlotClockError, TestingSlotClock};
use std::time::Duration;
pub use types::Slot;
pub trait SlotClock: Send + Sync {
type Error;
fn present_slot(&self) -> Result<Option<Slot>, Self::Error>;
fn duration_to_next_slot(&self) -> Result<Option<Duration>, Self::Error>;
}

View File

@@ -13,6 +13,7 @@ pub enum Error {
/// Determines the present slot based upon the present system time.
#[derive(Clone)]
pub struct SystemTimeSlotClock {
genesis_slot: Slot,
genesis_seconds: u64,
slot_duration_seconds: u64,
}
@@ -22,6 +23,7 @@ impl SystemTimeSlotClock {
///
/// Returns an Error if `slot_duration_seconds == 0`.
pub fn new(
genesis_slot: Slot,
genesis_seconds: u64,
slot_duration_seconds: u64,
) -> Result<SystemTimeSlotClock, Error> {
@@ -29,6 +31,7 @@ impl SystemTimeSlotClock {
Err(Error::SlotDurationIsZero)
} else {
Ok(Self {
genesis_slot,
genesis_seconds,
slot_duration_seconds,
})
@@ -44,11 +47,17 @@ impl SlotClock for SystemTimeSlotClock {
let duration_since_epoch = syslot_time.duration_since(SystemTime::UNIX_EPOCH)?;
let duration_since_genesis =
duration_since_epoch.checked_sub(Duration::from_secs(self.genesis_seconds));
match duration_since_genesis {
None => Ok(None),
Some(d) => Ok(slot_from_duration(self.slot_duration_seconds, d)),
Some(d) => Ok(slot_from_duration(self.slot_duration_seconds, d)
.and_then(|s| Some(s + self.genesis_slot))),
}
}
fn duration_to_next_slot(&self) -> Result<Option<Duration>, Error> {
duration_to_next_slot(self.genesis_seconds, self.slot_duration_seconds)
}
}
impl From<SystemTimeError> for Error {
@@ -62,6 +71,30 @@ fn slot_from_duration(slot_duration_seconds: u64, duration: Duration) -> Option<
duration.as_secs().checked_div(slot_duration_seconds)?,
))
}
// calculate the duration to the next slot
fn duration_to_next_slot(
genesis_time: u64,
seconds_per_slot: u64,
) -> Result<Option<Duration>, Error> {
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
let genesis_time = Duration::from_secs(genesis_time);
if now < genesis_time {
return Ok(None);
}
let since_genesis = now - genesis_time;
let elapsed_slots = since_genesis.as_secs() / seconds_per_slot;
let next_slot_start_seconds = (elapsed_slots + 1)
.checked_mul(seconds_per_slot)
.expect("Next slot time should not overflow u64");
let time_to_next_slot = Duration::from_secs(next_slot_start_seconds) - since_genesis;
Ok(Some(time_to_next_slot))
}
#[cfg(test)]
mod tests {
@@ -74,6 +107,7 @@ mod tests {
#[test]
fn test_slot_now() {
let slot_time = 100;
let genesis_slot = Slot::new(0);
let now = SystemTime::now();
let since_epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();
@@ -81,18 +115,21 @@ mod tests {
let genesis = since_epoch.as_secs() - slot_time * 89;
let clock = SystemTimeSlotClock {
genesis_slot,
genesis_seconds: genesis,
slot_duration_seconds: slot_time,
};
assert_eq!(clock.present_slot().unwrap(), Some(Slot::new(89)));
let clock = SystemTimeSlotClock {
genesis_slot,
genesis_seconds: since_epoch.as_secs(),
slot_duration_seconds: slot_time,
};
assert_eq!(clock.present_slot().unwrap(), Some(Slot::new(0)));
let clock = SystemTimeSlotClock {
genesis_slot,
genesis_seconds: since_epoch.as_secs() - slot_time * 42 - 5,
slot_duration_seconds: slot_time,
};

View File

@@ -1,5 +1,6 @@
use super::SlotClock;
use std::sync::RwLock;
use std::time::Duration;
use types::Slot;
#[derive(Debug, PartialEq)]
@@ -32,6 +33,11 @@ impl SlotClock for TestingSlotClock {
let slot = *self.slot.read().expect("TestingSlotClock poisoned.");
Ok(Some(Slot::new(slot)))
}
/// Always returns a duration of 1 second.
fn duration_to_next_slot(&self) -> Result<Option<Duration>, Error> {
Ok(Some(Duration::from_secs(1)))
}
}
#[cfg(test)]