mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-22 07:18:25 +00:00
Directory Restructure (#1163)
* Move tests -> testing * Directory restructure * Update Cargo.toml during restructure * Update Makefile during restructure * Fix arbitrary path
This commit is contained in:
99
crypto/bls/src/aggregate_public_key.rs
Normal file
99
crypto/bls/src/aggregate_public_key.rs
Normal file
@@ -0,0 +1,99 @@
|
||||
use super::{PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE};
|
||||
use milagro_bls::AggregatePublicKey as RawAggregatePublicKey;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_hex::{encode as hex_encode, PrefixedHexVisitor};
|
||||
use ssz::{Decode, DecodeError, Encode};
|
||||
|
||||
/// A BLS aggregate public key.
|
||||
///
|
||||
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
|
||||
/// serialization).
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct AggregatePublicKey(RawAggregatePublicKey);
|
||||
|
||||
impl AggregatePublicKey {
|
||||
pub fn new() -> Self {
|
||||
AggregatePublicKey(RawAggregatePublicKey::new())
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
let pubkey = RawAggregatePublicKey::from_bytes(&bytes).map_err(|_| {
|
||||
DecodeError::BytesInvalid(format!("Invalid AggregatePublicKey bytes: {:?}", bytes))
|
||||
})?;
|
||||
|
||||
Ok(AggregatePublicKey(pubkey))
|
||||
}
|
||||
|
||||
pub fn add_without_affine(&mut self, public_key: &PublicKey) {
|
||||
self.0.point.add(&public_key.as_raw().point)
|
||||
}
|
||||
|
||||
pub fn affine(&mut self) {
|
||||
self.0.point.affine()
|
||||
}
|
||||
|
||||
pub fn add(&mut self, public_key: &PublicKey) {
|
||||
self.0.add(public_key.as_raw())
|
||||
}
|
||||
|
||||
/// Returns the underlying public key.
|
||||
pub fn as_raw(&self) -> &RawAggregatePublicKey {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns the underlying point as compressed bytes.
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
self.as_raw().as_bytes()
|
||||
}
|
||||
|
||||
pub fn into_raw(self) -> RawAggregatePublicKey {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Return a hex string representation of this key's bytes.
|
||||
#[cfg(test)]
|
||||
pub fn as_hex_string(&self) -> String {
|
||||
serde_hex::encode(self.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl_ssz!(
|
||||
AggregatePublicKey,
|
||||
BLS_PUBLIC_KEY_BYTE_SIZE,
|
||||
"AggregatePublicKey"
|
||||
);
|
||||
impl_tree_hash!(AggregatePublicKey, BLS_PUBLIC_KEY_BYTE_SIZE);
|
||||
|
||||
impl Serialize for AggregatePublicKey {
|
||||
/// Serde serialization is compliant the Ethereum YAML test format.
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&hex_encode(self.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for AggregatePublicKey {
|
||||
/// Serde serialization is compliant the Ethereum YAML test format.
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
|
||||
let agg_sig = AggregatePublicKey::from_ssz_bytes(&bytes)
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
|
||||
|
||||
Ok(agg_sig)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl arbitrary::Arbitrary for AggregatePublicKey {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
let mut bytes = [0u8; BLS_PUBLIC_KEY_BYTE_SIZE];
|
||||
u.fill_buffer(&mut bytes)?;
|
||||
Self::from_bytes(&bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
|
||||
}
|
||||
}
|
||||
209
crypto/bls/src/aggregate_signature.rs
Normal file
209
crypto/bls/src/aggregate_signature.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
use super::*;
|
||||
use milagro_bls::{AggregateSignature as RawAggregateSignature, G2Point};
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_hex::{encode as hex_encode, PrefixedHexVisitor};
|
||||
use ssz::{Decode, DecodeError, Encode};
|
||||
|
||||
/// 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 AggregateSignature {
|
||||
aggregate_signature: RawAggregateSignature,
|
||||
is_empty: bool,
|
||||
}
|
||||
|
||||
impl AggregateSignature {
|
||||
/// Instantiate a new AggregateSignature.
|
||||
///
|
||||
/// is_empty is false
|
||||
/// AggregateSignature is point at infinity
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
aggregate_signature: RawAggregateSignature::new(),
|
||||
is_empty: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add (aggregate) a signature to the `AggregateSignature`.
|
||||
pub fn add(&mut self, signature: &Signature) {
|
||||
// Only empty if both are empty
|
||||
self.is_empty = self.is_empty && signature.is_empty();
|
||||
|
||||
// Note: empty signatures will have point at infinity which is equivalent of adding 0.
|
||||
self.aggregate_signature.add(signature.as_raw())
|
||||
}
|
||||
|
||||
/// Add (aggregate) another `AggregateSignature`.
|
||||
pub fn add_aggregate(&mut self, agg_signature: &AggregateSignature) {
|
||||
// Only empty if both are empty
|
||||
self.is_empty = self.is_empty && agg_signature.is_empty();
|
||||
|
||||
// Note: empty signatures will have point at infinity which is equivalent of adding 0.
|
||||
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
|
||||
/// that signed the `AggregateSignature`.
|
||||
pub fn verify(&self, msg: &[u8], aggregate_public_key: &AggregatePublicKey) -> bool {
|
||||
if self.is_empty {
|
||||
return false;
|
||||
}
|
||||
self.aggregate_signature
|
||||
.fast_aggregate_verify_pre_aggregated(msg, aggregate_public_key.as_raw())
|
||||
}
|
||||
|
||||
/// Verify the `AggregateSignature` against an `AggregatePublicKey`.
|
||||
///
|
||||
/// Only returns `true` if the set of keys in the `AggregatePublicKey` match the set of keys
|
||||
/// that signed the `AggregateSignature`.
|
||||
pub fn verify_unaggregated(&self, msg: &[u8], public_keys: &[&PublicKey]) -> bool {
|
||||
if self.is_empty {
|
||||
return false;
|
||||
}
|
||||
let public_key_refs: Vec<_> = public_keys.iter().map(|pk| pk.as_raw()).collect();
|
||||
self.aggregate_signature
|
||||
.fast_aggregate_verify(msg, &public_key_refs)
|
||||
}
|
||||
|
||||
/// Verify this AggregateSignature against multiple AggregatePublickeys and Messages.
|
||||
///
|
||||
/// Each AggregatePublicKey has a 1:1 ratio with a 32 byte Message.
|
||||
pub fn verify_multiple(&self, messages: &[&[u8]], public_keys: &[&PublicKey]) -> bool {
|
||||
if self.is_empty {
|
||||
return false;
|
||||
}
|
||||
let public_keys_refs: Vec<_> = public_keys.iter().map(|pk| pk.as_raw()).collect();
|
||||
self.aggregate_signature
|
||||
.aggregate_verify(&messages, &public_keys_refs)
|
||||
}
|
||||
|
||||
/// Return AggregateSignature as bytes
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
if self.is_empty {
|
||||
return vec![0; BLS_AGG_SIG_BYTE_SIZE];
|
||||
}
|
||||
self.aggregate_signature.as_bytes()
|
||||
}
|
||||
|
||||
/// Convert bytes to AggregateSignature
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
for byte in bytes {
|
||||
if *byte != 0 {
|
||||
let sig = RawAggregateSignature::from_bytes(&bytes).map_err(|_| {
|
||||
DecodeError::BytesInvalid(format!(
|
||||
"Invalid AggregateSignature bytes: {:?}",
|
||||
bytes
|
||||
))
|
||||
})?;
|
||||
|
||||
return Ok(Self {
|
||||
aggregate_signature: sig,
|
||||
is_empty: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(Self::empty_signature())
|
||||
}
|
||||
|
||||
/// Returns the underlying signature.
|
||||
pub fn as_raw(&self) -> &RawAggregateSignature {
|
||||
&self.aggregate_signature
|
||||
}
|
||||
|
||||
/// Returns the underlying signature.
|
||||
pub fn from_point(point: G2Point) -> Self {
|
||||
Self {
|
||||
aggregate_signature: RawAggregateSignature { point },
|
||||
is_empty: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns if the AggregateSignature `is_empty`
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.is_empty
|
||||
}
|
||||
|
||||
/// Creates a new AggregateSignature
|
||||
///
|
||||
/// aggregate_signature set to the point infinity
|
||||
/// is_empty set to true
|
||||
pub fn empty_signature() -> Self {
|
||||
Self {
|
||||
aggregate_signature: RawAggregateSignature::new(),
|
||||
is_empty: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a hex string representation of the bytes of this signature.
|
||||
#[cfg(test)]
|
||||
pub fn as_hex_string(&self) -> String {
|
||||
hex_encode(self.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl_ssz!(
|
||||
AggregateSignature,
|
||||
BLS_AGG_SIG_BYTE_SIZE,
|
||||
"AggregateSignature"
|
||||
);
|
||||
|
||||
impl_tree_hash!(AggregateSignature, BLS_AGG_SIG_BYTE_SIZE);
|
||||
|
||||
impl Serialize for AggregateSignature {
|
||||
/// Serde serialization is compliant the Ethereum YAML test format.
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&hex_encode(self.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for AggregateSignature {
|
||||
/// Serde serialization is compliant the Ethereum YAML test format.
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
|
||||
let agg_sig = AggregateSignature::from_ssz_bytes(&bytes)
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
|
||||
|
||||
Ok(agg_sig)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl arbitrary::Arbitrary for AggregateSignature {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
let mut bytes = [0u8; BLS_AGG_SIG_BYTE_SIZE];
|
||||
u.fill_buffer(&mut bytes)?;
|
||||
Self::from_bytes(&bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{Keypair, Signature};
|
||||
use super::*;
|
||||
use ssz::Encode;
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
let keypair = Keypair::random();
|
||||
|
||||
let mut original = AggregateSignature::new();
|
||||
original.add(&Signature::new(&[42, 42], &keypair.sk));
|
||||
|
||||
let bytes = original.as_ssz_bytes();
|
||||
let decoded = AggregateSignature::from_ssz_bytes(&bytes).unwrap();
|
||||
|
||||
assert_eq!(original, decoded);
|
||||
}
|
||||
}
|
||||
130
crypto/bls/src/fake_aggregate_public_key.rs
Normal file
130
crypto/bls/src/fake_aggregate_public_key.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
use super::{PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE};
|
||||
use hex::encode as hex_encode;
|
||||
use milagro_bls::G1Point;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_hex::PrefixedHexVisitor;
|
||||
use ssz::{ssz_encode, Decode, DecodeError, Encode};
|
||||
|
||||
/// A BLS aggregate public key.
|
||||
///
|
||||
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
|
||||
/// serialization).
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct FakeAggregatePublicKey {
|
||||
bytes: Vec<u8>,
|
||||
/// Never used, only use for compatibility with "real" `AggregatePublicKey`.
|
||||
pub point: G1Point,
|
||||
}
|
||||
|
||||
impl FakeAggregatePublicKey {
|
||||
pub fn new() -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
|
||||
pub fn empty_signature() -> Self {
|
||||
Self {
|
||||
bytes: vec![0; BLS_PUBLIC_KEY_BYTE_SIZE],
|
||||
point: G1Point::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
if bytes.len() != BLS_PUBLIC_KEY_BYTE_SIZE {
|
||||
Err(DecodeError::InvalidByteLength {
|
||||
len: bytes.len(),
|
||||
expected: BLS_PUBLIC_KEY_BYTE_SIZE,
|
||||
})
|
||||
} else {
|
||||
Ok(Self {
|
||||
bytes: bytes.to_vec(),
|
||||
point: G1Point::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_without_affine(&mut self, _public_key: &PublicKey) {
|
||||
// No nothing.
|
||||
}
|
||||
|
||||
pub fn affine(&mut self) {
|
||||
// No nothing.
|
||||
}
|
||||
|
||||
/// Creates a new all-zero's aggregate public key
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
bytes: vec![0; BLS_PUBLIC_KEY_BYTE_SIZE],
|
||||
point: G1Point::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, _public_key: &PublicKey) {
|
||||
// No nothing.
|
||||
}
|
||||
|
||||
pub fn add_point(&mut self, _point: &G1Point) {
|
||||
// No nothing.
|
||||
}
|
||||
|
||||
pub fn aggregate(_pks: &[&PublicKey]) -> Self {
|
||||
Self::new()
|
||||
}
|
||||
|
||||
pub fn from_public_key(public_key: &PublicKey) -> Self {
|
||||
Self {
|
||||
bytes: public_key.as_bytes(),
|
||||
point: public_key.point.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_raw(&self) -> &Self {
|
||||
&self
|
||||
}
|
||||
|
||||
pub fn into_raw(self) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
self.bytes.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl_ssz!(
|
||||
FakeAggregatePublicKey,
|
||||
BLS_PUBLIC_KEY_BYTE_SIZE,
|
||||
"FakeAggregatePublicKey"
|
||||
);
|
||||
|
||||
impl_tree_hash!(FakeAggregatePublicKey, BLS_PUBLIC_KEY_BYTE_SIZE);
|
||||
|
||||
impl Serialize for FakeAggregatePublicKey {
|
||||
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 FakeAggregatePublicKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
|
||||
let pubkey = <_>::from_ssz_bytes(&bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
|
||||
Ok(pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl arbitrary::Arbitrary for FakeAggregatePublicKey {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
let mut bytes = [0u8; BLS_PUBLIC_KEY_BYTE_SIZE];
|
||||
u.fill_buffer(&mut bytes)?;
|
||||
Self::from_bytes(&bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
|
||||
}
|
||||
}
|
||||
162
crypto/bls/src/fake_aggregate_signature.rs
Normal file
162
crypto/bls/src/fake_aggregate_signature.rs
Normal file
@@ -0,0 +1,162 @@
|
||||
use super::{
|
||||
fake_aggregate_public_key::FakeAggregatePublicKey, fake_public_key::FakePublicKey,
|
||||
fake_signature::FakeSignature, BLS_AGG_SIG_BYTE_SIZE,
|
||||
};
|
||||
use milagro_bls::G2Point;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_hex::{encode as hex_encode, PrefixedHexVisitor};
|
||||
use ssz::{ssz_encode, Decode, DecodeError, Encode};
|
||||
|
||||
/// 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>,
|
||||
/// Never used, only use for compatibility with "real" `AggregateSignature`.
|
||||
pub point: G2Point,
|
||||
}
|
||||
|
||||
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],
|
||||
point: G2Point::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_raw(&self) -> &Self {
|
||||
&self
|
||||
}
|
||||
|
||||
/// 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.
|
||||
}
|
||||
|
||||
/// Does glorious nothing.
|
||||
pub fn aggregate(&mut self, _agg_sig: &FakeAggregateSignature) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/// _Always_ returns `true`.
|
||||
pub fn verify(&self, _msg: &[u8], _aggregate_public_key: &FakeAggregatePublicKey) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// _Always_ returns `true`.
|
||||
pub fn verify_multiple(
|
||||
&self,
|
||||
_messages: &[&[u8]],
|
||||
_aggregate_public_keys: &[&FakePublicKey],
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// _Always_ returns `true`.
|
||||
pub fn fast_aggregate_verify_pre_aggregated(
|
||||
&self,
|
||||
_messages: &[u8],
|
||||
_aggregate_public_keys: &FakeAggregatePublicKey,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// _Always_ returns `true`.
|
||||
pub fn from_signature(signature: &FakeSignature) -> Self {
|
||||
Self {
|
||||
bytes: signature.as_bytes(),
|
||||
point: signature.point.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert bytes to fake BLS aggregate signature
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
if bytes.len() != BLS_AGG_SIG_BYTE_SIZE {
|
||||
Err(DecodeError::InvalidByteLength {
|
||||
len: bytes.len(),
|
||||
expected: BLS_AGG_SIG_BYTE_SIZE,
|
||||
})
|
||||
} else {
|
||||
Ok(Self {
|
||||
bytes: bytes.to_vec(),
|
||||
point: G2Point::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
self.bytes.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl_ssz!(
|
||||
FakeAggregateSignature,
|
||||
BLS_AGG_SIG_BYTE_SIZE,
|
||||
"FakeAggregateSignature"
|
||||
);
|
||||
|
||||
impl_tree_hash!(FakeAggregateSignature, 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 = <_>::from_ssz_bytes(&bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
|
||||
Ok(obj)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl arbitrary::Arbitrary for FakeAggregateSignature {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
let mut bytes = [0u8; BLS_AGG_SIG_BYTE_SIZE];
|
||||
u.fill_buffer(&mut bytes)?;
|
||||
Self::from_bytes(&bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
|
||||
}
|
||||
}
|
||||
|
||||
#[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], &keypair.sk));
|
||||
|
||||
let bytes = ssz_encode(&original);
|
||||
let decoded = FakeAggregateSignature::from_ssz_bytes(&bytes).unwrap();
|
||||
|
||||
assert_eq!(original, decoded);
|
||||
}
|
||||
}
|
||||
182
crypto/bls/src/fake_public_key.rs
Normal file
182
crypto/bls/src/fake_public_key.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
use super::{SecretKey, BLS_PUBLIC_KEY_BYTE_SIZE};
|
||||
use milagro_bls::G1Point;
|
||||
use milagro_bls::PublicKey as RawPublicKey;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_hex::{encode as hex_encode, PrefixedHexVisitor};
|
||||
use ssz::{ssz_encode, Decode, DecodeError, Encode};
|
||||
use std::default;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// A single BLS signature.
|
||||
///
|
||||
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
|
||||
/// serialization).
|
||||
#[derive(Clone, Eq)]
|
||||
pub struct FakePublicKey {
|
||||
bytes: Vec<u8>,
|
||||
/// Never used, only use for compatibility with "real" `PublicKey`.
|
||||
pub point: G1Point,
|
||||
}
|
||||
|
||||
impl FakePublicKey {
|
||||
pub fn from_secret_key(_secret_key: &SecretKey) -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
|
||||
pub fn from_raw(raw: RawPublicKey) -> Self {
|
||||
Self {
|
||||
bytes: raw.clone().as_bytes(),
|
||||
point: G1Point::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new all-zero's public key
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
bytes: vec![0; BLS_PUBLIC_KEY_BYTE_SIZE],
|
||||
point: G1Point::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying point as compressed bytes.
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
self.bytes.clone()
|
||||
}
|
||||
|
||||
/// Converts compressed bytes to FakePublicKey
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
Ok(Self {
|
||||
bytes: bytes.to_vec(),
|
||||
point: G1Point::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the FakePublicKey as (x, y) bytes
|
||||
pub fn as_uncompressed_bytes(&self) -> Vec<u8> {
|
||||
self.as_bytes()
|
||||
}
|
||||
|
||||
/// Converts (x, y) bytes to FakePublicKey
|
||||
pub fn from_uncompressed_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
Self::from_bytes(bytes)
|
||||
}
|
||||
|
||||
/// Returns the last 6 bytes of the SSZ encoding of the public key, as a hex string.
|
||||
///
|
||||
/// Useful for providing a short identifier to the user.
|
||||
pub fn concatenated_hex_id(&self) -> String {
|
||||
let bytes = ssz_encode(self);
|
||||
let end_bytes = &bytes[bytes.len().saturating_sub(6)..bytes.len()];
|
||||
hex_encode(end_bytes)
|
||||
}
|
||||
|
||||
/// Returns the point as a hex string of the SSZ encoding.
|
||||
///
|
||||
/// Note: the string is prefixed with `0x`.
|
||||
pub fn as_hex_string(&self) -> String {
|
||||
hex_encode(self.as_ssz_bytes())
|
||||
}
|
||||
|
||||
// Returns itself
|
||||
pub fn as_raw(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn as_point(&self) -> &G1Point {
|
||||
&self.point
|
||||
}
|
||||
|
||||
pub fn into_point(self) -> G1Point {
|
||||
self.point
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FakePublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.concatenated_hex_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FakePublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "0x{}", self.as_hex_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl default::Default for FakePublicKey {
|
||||
fn default() -> Self {
|
||||
let secret_key = SecretKey::random();
|
||||
FakePublicKey::from_secret_key(&secret_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl_ssz!(FakePublicKey, BLS_PUBLIC_KEY_BYTE_SIZE, "FakePublicKey");
|
||||
|
||||
impl_tree_hash!(FakePublicKey, BLS_PUBLIC_KEY_BYTE_SIZE);
|
||||
|
||||
impl Serialize for FakePublicKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&hex_encode(self.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for FakePublicKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
|
||||
let pubkey = Self::from_ssz_bytes(&bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid pubkey ({:?})", e)))?;
|
||||
Ok(pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for FakePublicKey {
|
||||
fn eq(&self, other: &FakePublicKey) -> bool {
|
||||
ssz_encode(self) == ssz_encode(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for FakePublicKey {
|
||||
/// 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.as_uncompressed_bytes().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl arbitrary::Arbitrary for FakePublicKey {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
let mut bytes = [0u8; BLS_PUBLIC_KEY_BYTE_SIZE];
|
||||
u.fill_buffer(&mut bytes)?;
|
||||
Self::from_bytes(&bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ssz::ssz_encode;
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
let sk = SecretKey::random();
|
||||
let original = FakePublicKey::from_secret_key(&sk);
|
||||
|
||||
let bytes = ssz_encode(&original);
|
||||
let decoded = FakePublicKey::from_ssz_bytes(&bytes).unwrap();
|
||||
|
||||
assert_eq!(original, decoded);
|
||||
}
|
||||
}
|
||||
143
crypto/bls/src/fake_signature.rs
Normal file
143
crypto/bls/src/fake_signature.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use super::{PublicKey, SecretKey, BLS_SIG_BYTE_SIZE};
|
||||
use hex::encode as hex_encode;
|
||||
use milagro_bls::G2Point;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_hex::PrefixedHexVisitor;
|
||||
use ssz::{ssz_encode, Decode, DecodeError, Encode};
|
||||
|
||||
/// 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>,
|
||||
is_empty: bool,
|
||||
/// Never used, only use for compatibility with "real" `Signature`.
|
||||
pub point: G2Point,
|
||||
}
|
||||
|
||||
impl FakeSignature {
|
||||
/// Creates a new all-zero's signature
|
||||
pub fn new(_msg: &[u8], _sk: &SecretKey) -> Self {
|
||||
FakeSignature::zero()
|
||||
}
|
||||
|
||||
/// Creates a new all-zero's signature
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
bytes: vec![0; BLS_SIG_BYTE_SIZE],
|
||||
is_empty: true,
|
||||
point: G2Point::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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], _pk: &PublicKey) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn as_raw(&self) -> &Self {
|
||||
&self
|
||||
}
|
||||
|
||||
/// _Always_ returns true.
|
||||
pub fn verify_hashed(
|
||||
&self,
|
||||
_x_real_hashed: &[u8],
|
||||
_x_imaginary_hashed: &[u8],
|
||||
_pk: &PublicKey,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Convert bytes to fake BLS Signature
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
if bytes.len() != BLS_SIG_BYTE_SIZE {
|
||||
Err(DecodeError::InvalidByteLength {
|
||||
len: bytes.len(),
|
||||
expected: BLS_SIG_BYTE_SIZE,
|
||||
})
|
||||
} else {
|
||||
let is_empty = bytes.iter().all(|x| *x == 0);
|
||||
Ok(Self {
|
||||
bytes: bytes.to_vec(),
|
||||
is_empty,
|
||||
point: G2Point::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
self.bytes.clone()
|
||||
}
|
||||
|
||||
/// Returns a new empty signature.
|
||||
pub fn empty_signature() -> Self {
|
||||
FakeSignature::zero()
|
||||
}
|
||||
|
||||
// Check for empty Signature
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.is_empty
|
||||
}
|
||||
}
|
||||
|
||||
impl_ssz!(FakeSignature, BLS_SIG_BYTE_SIZE, "FakeSignature");
|
||||
|
||||
impl_tree_hash!(FakeSignature, BLS_SIG_BYTE_SIZE);
|
||||
|
||||
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(PrefixedHexVisitor)?;
|
||||
let pubkey = <_>::from_ssz_bytes(&bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
|
||||
Ok(pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl arbitrary::Arbitrary for FakeSignature {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
let mut bytes = [0u8; BLS_SIG_BYTE_SIZE];
|
||||
u.fill_buffer(&mut bytes)?;
|
||||
Self::from_bytes(&bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
|
||||
}
|
||||
}
|
||||
|
||||
#[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], &keypair.sk);
|
||||
|
||||
let bytes = ssz_encode(&original);
|
||||
let decoded = FakeSignature::from_ssz_bytes(&bytes).unwrap();
|
||||
|
||||
assert_eq!(original, decoded);
|
||||
}
|
||||
}
|
||||
42
crypto/bls/src/keypair.rs
Normal file
42
crypto/bls/src/keypair.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use super::{PublicKey, SecretKey};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Keypair {
|
||||
pub sk: SecretKey,
|
||||
pub pk: PublicKey,
|
||||
}
|
||||
|
||||
impl Keypair {
|
||||
/// Instantiate a Keypair using SecretKey::random().
|
||||
pub fn random() -> Self {
|
||||
let sk = SecretKey::random();
|
||||
let pk = PublicKey::from_secret_key(&sk);
|
||||
Keypair { sk, pk }
|
||||
}
|
||||
|
||||
pub fn identifier(&self) -> String {
|
||||
self.pk.concatenated_hex_id()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
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)
|
||||
}
|
||||
}
|
||||
83
crypto/bls/src/lib.rs
Normal file
83
crypto/bls/src/lib.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
extern crate milagro_bls;
|
||||
extern crate ssz;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod keypair;
|
||||
mod public_key_bytes;
|
||||
mod secret_key;
|
||||
mod signature_bytes;
|
||||
mod signature_set;
|
||||
|
||||
pub use crate::keypair::Keypair;
|
||||
pub use crate::public_key_bytes::PublicKeyBytes;
|
||||
pub use crate::secret_key::SecretKey;
|
||||
pub use crate::signature_bytes::SignatureBytes;
|
||||
pub use milagro_bls::{compress_g2, hash_to_curve_g2};
|
||||
pub use signature_set::{verify_signature_sets, SignatureSet};
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
pub use arbitrary;
|
||||
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
mod fake_aggregate_public_key;
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
mod fake_aggregate_signature;
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
mod fake_public_key;
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
mod fake_signature;
|
||||
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
mod aggregate_public_key;
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
mod aggregate_signature;
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
mod public_key;
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
mod signature;
|
||||
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
pub use fakes::*;
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
mod fakes {
|
||||
pub use crate::fake_aggregate_public_key::FakeAggregatePublicKey as AggregatePublicKey;
|
||||
pub use crate::fake_aggregate_signature::FakeAggregateSignature as AggregateSignature;
|
||||
pub use crate::fake_public_key::FakePublicKey as PublicKey;
|
||||
pub use crate::fake_signature::FakeSignature as Signature;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
pub use reals::*;
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
mod reals {
|
||||
pub use crate::aggregate_public_key::AggregatePublicKey;
|
||||
pub use crate::aggregate_signature::AggregateSignature;
|
||||
pub use crate::public_key::PublicKey;
|
||||
pub use crate::signature::Signature;
|
||||
}
|
||||
|
||||
pub const BLS_AGG_SIG_BYTE_SIZE: usize = 96;
|
||||
pub const BLS_SIG_BYTE_SIZE: usize = 96;
|
||||
pub const BLS_SECRET_KEY_BYTE_SIZE: usize = 32;
|
||||
pub const BLS_PUBLIC_KEY_BYTE_SIZE: usize = 48;
|
||||
|
||||
use eth2_hashing::hash;
|
||||
use ssz::ssz_encode;
|
||||
|
||||
/// Returns the withdrawal credentials for a given public key.
|
||||
pub fn get_withdrawal_credentials(pubkey: &PublicKey, prefix_byte: u8) -> Vec<u8> {
|
||||
let hashed = hash(&ssz_encode(pubkey));
|
||||
let mut prefixed = vec![prefix_byte];
|
||||
prefixed.extend_from_slice(&hashed[1..]);
|
||||
|
||||
prefixed
|
||||
}
|
||||
|
||||
pub fn bls_verify_aggregate(
|
||||
pubkey: &AggregatePublicKey,
|
||||
message: &[u8],
|
||||
signature: &AggregateSignature,
|
||||
) -> bool {
|
||||
signature.verify(message, pubkey)
|
||||
}
|
||||
265
crypto/bls/src/macros.rs
Normal file
265
crypto/bls/src/macros.rs
Normal file
@@ -0,0 +1,265 @@
|
||||
macro_rules! impl_ssz {
|
||||
($type: ident, $byte_size: expr, $item_str: expr) => {
|
||||
impl ssz::Encode for $type {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ssz_fixed_len() -> usize {
|
||||
$byte_size
|
||||
}
|
||||
|
||||
fn ssz_bytes_len(&self) -> usize {
|
||||
$byte_size
|
||||
}
|
||||
|
||||
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
||||
buf.append(&mut self.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl ssz::Decode for $type {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ssz_fixed_len() -> usize {
|
||||
$byte_size
|
||||
}
|
||||
|
||||
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
let len = bytes.len();
|
||||
let expected = <Self as ssz::Decode>::ssz_fixed_len();
|
||||
|
||||
if len != expected {
|
||||
Err(ssz::DecodeError::InvalidByteLength { len, expected })
|
||||
} else {
|
||||
$type::from_bytes(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_tree_hash {
|
||||
($type: ty, $byte_size: expr) => {
|
||||
impl tree_hash::TreeHash for $type {
|
||||
fn tree_hash_type() -> tree_hash::TreeHashType {
|
||||
tree_hash::TreeHashType::Vector
|
||||
}
|
||||
|
||||
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
|
||||
unreachable!("Vector should never be packed.")
|
||||
}
|
||||
|
||||
fn tree_hash_packing_factor() -> usize {
|
||||
unreachable!("Vector should never be packed.")
|
||||
}
|
||||
|
||||
fn tree_hash_root(&self) -> tree_hash::Hash256 {
|
||||
// We could use the tree hash implementation for `FixedVec<u8, $byte_size>`,
|
||||
// but benchmarks have show that to be at least 15% slower because of the
|
||||
// unnecessary copying and allocation (one Vec per byte)
|
||||
let values_per_chunk = tree_hash::BYTES_PER_CHUNK;
|
||||
let minimum_chunk_count = ($byte_size + values_per_chunk - 1) / values_per_chunk;
|
||||
|
||||
let mut hasher = tree_hash::MerkleHasher::with_leaves(minimum_chunk_count);
|
||||
hasher
|
||||
.write(&self.as_ssz_bytes())
|
||||
.expect("bls should not exceed leaf count");
|
||||
hasher
|
||||
.finish()
|
||||
.expect("bls should not exceed leaf count from buffer")
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! bytes_struct {
|
||||
($name: ident, $type: ty, $byte_size: expr, $small_name: expr,
|
||||
$type_str: expr, $byte_size_str: expr) => {
|
||||
#[doc = "Stores `"]
|
||||
#[doc = $byte_size_str]
|
||||
#[doc = "` bytes which may or may not represent a valid BLS "]
|
||||
#[doc = $small_name]
|
||||
#[doc = ".\n\nThe `"]
|
||||
#[doc = $type_str]
|
||||
#[doc = "` struct performs validation when it is instantiated, where as this struct does \
|
||||
not. This struct is suitable where we may wish to store bytes that are \
|
||||
potentially not a valid "]
|
||||
#[doc = $small_name]
|
||||
#[doc = " (e.g., from the deposit contract)."]
|
||||
#[derive(Clone)]
|
||||
pub struct $name {
|
||||
bytes: [u8; $byte_size],
|
||||
}
|
||||
};
|
||||
($name: ident, $type: ty, $byte_size: expr, $small_name: expr) => {
|
||||
bytes_struct!($name, $type, $byte_size, $small_name, stringify!($type),
|
||||
stringify!($byte_size));
|
||||
|
||||
impl $name {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
|
||||
Ok(Self {
|
||||
bytes: Self::get_bytes(bytes)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
bytes: [0; $byte_size],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
self.bytes.to_vec()
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
fn get_bytes(bytes: &[u8]) -> Result<[u8; $byte_size], ssz::DecodeError> {
|
||||
let mut result = [0; $byte_size];
|
||||
if bytes.len() != $byte_size {
|
||||
Err(ssz::DecodeError::InvalidByteLength {
|
||||
len: bytes.len(),
|
||||
expected: $byte_size,
|
||||
})
|
||||
} else {
|
||||
result[..].copy_from_slice(bytes);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for $name {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
self.bytes[..].fmt(formatter)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for $name {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
&self.bytes[..] == &other.bytes[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for $name {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.bytes.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for $name {}
|
||||
|
||||
impl std::convert::TryInto<$type> for &$name {
|
||||
type Error = ssz::DecodeError;
|
||||
|
||||
fn try_into(self) -> Result<$type, Self::Error> {
|
||||
<$type>::from_bytes(&self.bytes[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<$type> for $name {
|
||||
fn from(obj: $type) -> Self {
|
||||
// We know that obj.as_bytes() always has exactly $byte_size many bytes.
|
||||
Self::from_bytes(obj.as_ssz_bytes().as_slice()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl ssz::Encode for $name {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ssz_fixed_len() -> usize {
|
||||
$byte_size
|
||||
}
|
||||
|
||||
fn ssz_bytes_len(&self) -> usize {
|
||||
$byte_size
|
||||
}
|
||||
|
||||
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
||||
buf.extend_from_slice(&self.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl ssz::Decode for $name {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ssz_fixed_len() -> usize {
|
||||
$byte_size
|
||||
}
|
||||
|
||||
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
let len = bytes.len();
|
||||
let expected = <Self as ssz::Decode>::ssz_fixed_len();
|
||||
|
||||
if len != expected {
|
||||
Err(ssz::DecodeError::InvalidByteLength { len, expected })
|
||||
} else {
|
||||
Self::from_bytes(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl tree_hash::TreeHash for $name {
|
||||
fn tree_hash_type() -> tree_hash::TreeHashType {
|
||||
tree_hash::TreeHashType::Vector
|
||||
}
|
||||
|
||||
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
|
||||
unreachable!("Vector should never be packed.")
|
||||
}
|
||||
|
||||
fn tree_hash_packing_factor() -> usize {
|
||||
unreachable!("Vector should never be packed.")
|
||||
}
|
||||
|
||||
fn tree_hash_root(&self) -> tree_hash::Hash256 {
|
||||
let values_per_chunk = tree_hash::BYTES_PER_CHUNK;
|
||||
let minimum_chunk_count = ($byte_size + values_per_chunk - 1) / values_per_chunk;
|
||||
|
||||
let mut hasher = tree_hash::MerkleHasher::with_leaves(minimum_chunk_count);
|
||||
hasher.write(&self.bytes).expect("bls should not exceed leaf count");
|
||||
hasher.finish().expect("bls should not exceed leaf count from buffer")
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::Serialize for $name {
|
||||
/// Serde serialization is compliant the Ethereum YAML test format.
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::ser::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&serde_hex::encode(ssz::ssz_encode(self)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::de::Deserialize<'de> for $name {
|
||||
/// Serde serialization is compliant the Ethereum YAML test format.
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
let bytes = deserializer.deserialize_str(serde_hex::PrefixedHexVisitor)?;
|
||||
let signature = Self::from_ssz_bytes(&bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
|
||||
Ok(signature)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl $crate::arbitrary::Arbitrary for $name {
|
||||
fn arbitrary(u: &mut $crate::arbitrary::Unstructured<'_>) -> $crate::arbitrary::Result<Self> {
|
||||
let mut bytes = [0u8; $byte_size];
|
||||
u.fill_buffer(&mut bytes)?;
|
||||
Self::from_bytes(&bytes).map_err(|_| $crate::arbitrary::Error::IncorrectFormat)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
179
crypto/bls/src/public_key.rs
Normal file
179
crypto/bls/src/public_key.rs
Normal file
@@ -0,0 +1,179 @@
|
||||
use super::{SecretKey, BLS_PUBLIC_KEY_BYTE_SIZE};
|
||||
use milagro_bls::{G1Point, PublicKey as RawPublicKey};
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_hex::{encode as hex_encode, PrefixedHexVisitor};
|
||||
use ssz::{Decode, DecodeError, Encode};
|
||||
use std::default;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// A single BLS signature.
|
||||
///
|
||||
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
|
||||
/// serialization).
|
||||
#[derive(Clone, Eq)]
|
||||
pub struct PublicKey(RawPublicKey);
|
||||
|
||||
impl PublicKey {
|
||||
pub fn from_secret_key(secret_key: &SecretKey) -> Self {
|
||||
PublicKey(RawPublicKey::from_secret_key(secret_key.as_raw()))
|
||||
}
|
||||
|
||||
pub fn from_raw(raw: RawPublicKey) -> Self {
|
||||
Self(raw)
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying signature.
|
||||
pub fn as_raw(&self) -> &RawPublicKey {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Consumes self and returns the underlying signature.
|
||||
pub fn as_point(&self) -> &G1Point {
|
||||
&self.0.point
|
||||
}
|
||||
|
||||
/// Consumes self and returns the underlying signature.
|
||||
pub fn into_point(self) -> G1Point {
|
||||
self.0.point
|
||||
}
|
||||
|
||||
/// Returns the underlying point as compressed bytes.
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
self.as_raw().as_bytes()
|
||||
}
|
||||
|
||||
/// Converts compressed bytes to PublicKey
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
let pubkey = RawPublicKey::from_bytes(&bytes).map_err(|_| {
|
||||
DecodeError::BytesInvalid(format!("Invalid PublicKey bytes: {:?}", bytes))
|
||||
})?;
|
||||
|
||||
Ok(PublicKey(pubkey))
|
||||
}
|
||||
|
||||
/// Returns the PublicKey as (x, y) bytes
|
||||
pub fn as_uncompressed_bytes(&self) -> Vec<u8> {
|
||||
RawPublicKey::as_uncompressed_bytes(&mut self.0.clone())
|
||||
}
|
||||
|
||||
/// Converts (x, y) bytes to PublicKey
|
||||
pub fn from_uncompressed_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
let pubkey = RawPublicKey::from_uncompressed_bytes(&bytes).map_err(|_| {
|
||||
DecodeError::BytesInvalid("Invalid PublicKey uncompressed bytes.".to_string())
|
||||
})?;
|
||||
Ok(PublicKey(pubkey))
|
||||
}
|
||||
|
||||
/// Returns the last 6 bytes of the SSZ encoding of the public key, as a hex string.
|
||||
///
|
||||
/// Useful for providing a short identifier to the user.
|
||||
pub fn concatenated_hex_id(&self) -> String {
|
||||
self.as_hex_string()[0..6].to_string()
|
||||
}
|
||||
|
||||
/// Returns the point as a hex string of the SSZ encoding.
|
||||
///
|
||||
/// Note: the string is prefixed with `0x`.
|
||||
pub fn as_hex_string(&self) -> String {
|
||||
hex_encode(self.as_ssz_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.concatenated_hex_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.as_hex_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl default::Default for PublicKey {
|
||||
fn default() -> Self {
|
||||
let secret_key = SecretKey::random();
|
||||
PublicKey::from_secret_key(&secret_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl_ssz!(PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE, "PublicKey");
|
||||
|
||||
impl_tree_hash!(PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE);
|
||||
|
||||
impl Serialize for PublicKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&hex_encode(self.as_raw().as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for PublicKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
|
||||
let pubkey = Self::from_ssz_bytes(&bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid pubkey ({:?})", e)))?;
|
||||
Ok(pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for PublicKey {
|
||||
fn eq(&self, other: &PublicKey) -> bool {
|
||||
self.as_ssz_bytes() == other.as_ssz_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for PublicKey {
|
||||
/// 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.as_uncompressed_bytes().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl arbitrary::Arbitrary for PublicKey {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
let mut bytes = [0u8; BLS_PUBLIC_KEY_BYTE_SIZE];
|
||||
u.fill_buffer(&mut bytes)?;
|
||||
Self::from_bytes(&bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ssz::ssz_encode;
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
let sk = SecretKey::random();
|
||||
let original = PublicKey::from_secret_key(&sk);
|
||||
|
||||
let bytes = ssz_encode(&original);
|
||||
let decoded = PublicKey::from_ssz_bytes(&bytes).unwrap();
|
||||
|
||||
assert_eq!(original, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_byte_size() {
|
||||
let sk = SecretKey::random();
|
||||
let original = PublicKey::from_secret_key(&sk);
|
||||
|
||||
let bytes = ssz_encode(&original);
|
||||
assert_eq!(bytes.len(), BLS_PUBLIC_KEY_BYTE_SIZE);
|
||||
}
|
||||
}
|
||||
43
crypto/bls/src/public_key_bytes.rs
Normal file
43
crypto/bls/src/public_key_bytes.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use ssz::{Decode, DecodeError, Encode};
|
||||
|
||||
use super::{PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE};
|
||||
|
||||
bytes_struct!(
|
||||
PublicKeyBytes,
|
||||
PublicKey,
|
||||
BLS_PUBLIC_KEY_BYTE_SIZE,
|
||||
"public key"
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::TryInto;
|
||||
|
||||
use ssz::ssz_encode;
|
||||
|
||||
use super::super::Keypair;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn test_valid_public_key() {
|
||||
let keypair = Keypair::random();
|
||||
|
||||
let bytes = ssz_encode(&keypair.pk);
|
||||
let public_key_bytes = PublicKeyBytes::from_bytes(&bytes).unwrap();
|
||||
let public_key: Result<PublicKey, _> = (&public_key_bytes).try_into();
|
||||
assert!(public_key.is_ok());
|
||||
assert_eq!(keypair.pk, public_key.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
pub fn test_invalid_public_key() {
|
||||
let mut public_key_bytes = [0; BLS_PUBLIC_KEY_BYTE_SIZE];
|
||||
public_key_bytes[0] = 255; //a_flag1 == b_flag1 == c_flag1 == 1 and x1 = 0 shouldn't be allowed
|
||||
let public_key_bytes = PublicKeyBytes::from_bytes(&public_key_bytes[..]);
|
||||
assert!(public_key_bytes.is_ok());
|
||||
|
||||
let public_key: Result<PublicKey, _> = public_key_bytes.as_ref().unwrap().try_into();
|
||||
assert!(public_key.is_err());
|
||||
}
|
||||
}
|
||||
93
crypto/bls/src/secret_key.rs
Normal file
93
crypto/bls/src/secret_key.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
extern crate rand;
|
||||
|
||||
use super::BLS_SECRET_KEY_BYTE_SIZE;
|
||||
use hex::encode as hex_encode;
|
||||
use milagro_bls::SecretKey as RawSecretKey;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_hex::PrefixedHexVisitor;
|
||||
use ssz::{ssz_encode, Decode, DecodeError, Encode};
|
||||
|
||||
/// 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 SecretKey(RawSecretKey);
|
||||
|
||||
impl SecretKey {
|
||||
pub fn random() -> Self {
|
||||
SecretKey(RawSecretKey::random(&mut rand::thread_rng()))
|
||||
}
|
||||
|
||||
pub fn from_raw(raw: RawSecretKey) -> Self {
|
||||
Self(raw)
|
||||
}
|
||||
|
||||
/// Returns the underlying point as compressed bytes.
|
||||
fn as_bytes(&self) -> Vec<u8> {
|
||||
self.as_raw().as_bytes()
|
||||
}
|
||||
|
||||
/// Instantiate a SecretKey from existing bytes.
|
||||
///
|
||||
/// Note: this is _not_ SSZ decoding.
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<SecretKey, DecodeError> {
|
||||
Ok(SecretKey(RawSecretKey::from_bytes(bytes).map_err(|e| {
|
||||
DecodeError::BytesInvalid(format!(
|
||||
"Invalid SecretKey bytes: {:?} Error: {:?}",
|
||||
bytes, e
|
||||
))
|
||||
})?))
|
||||
}
|
||||
|
||||
/// Returns the underlying secret key.
|
||||
pub fn as_raw(&self) -> &RawSecretKey {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl_ssz!(SecretKey, BLS_SECRET_KEY_BYTE_SIZE, "SecretKey");
|
||||
|
||||
impl_tree_hash!(SecretKey, BLS_SECRET_KEY_BYTE_SIZE);
|
||||
|
||||
impl Serialize for SecretKey {
|
||||
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 SecretKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
|
||||
let secret_key = SecretKey::from_ssz_bytes(&bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
|
||||
Ok(secret_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ssz::ssz_encode;
|
||||
|
||||
#[test]
|
||||
pub fn test_ssz_round_trip() {
|
||||
let byte_key = [
|
||||
3, 211, 210, 129, 231, 69, 162, 234, 16, 15, 244, 214, 126, 201, 0, 85, 28, 239, 82,
|
||||
121, 208, 190, 223, 6, 169, 202, 86, 236, 197, 218, 3, 69,
|
||||
];
|
||||
let original = SecretKey::from_bytes(&byte_key).unwrap();
|
||||
|
||||
let bytes = ssz_encode(&original);
|
||||
let decoded = SecretKey::from_ssz_bytes(&bytes).unwrap();
|
||||
|
||||
assert_eq!(original, decoded);
|
||||
}
|
||||
}
|
||||
165
crypto/bls/src/signature.rs
Normal file
165
crypto/bls/src/signature.rs
Normal file
@@ -0,0 +1,165 @@
|
||||
use super::{PublicKey, SecretKey, BLS_SIG_BYTE_SIZE};
|
||||
use milagro_bls::Signature as RawSignature;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_hex::{encode as hex_encode, PrefixedHexVisitor};
|
||||
use ssz::{ssz_encode, Decode, DecodeError, Encode};
|
||||
|
||||
/// 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 Signature {
|
||||
signature: RawSignature,
|
||||
is_empty: bool,
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
/// Instantiate a new Signature from a message and a SecretKey.
|
||||
pub fn new(msg: &[u8], sk: &SecretKey) -> Self {
|
||||
Signature {
|
||||
signature: RawSignature::new(msg, sk.as_raw()),
|
||||
is_empty: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify the Signature against a PublicKey.
|
||||
pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> bool {
|
||||
if self.is_empty {
|
||||
return false;
|
||||
}
|
||||
self.signature.verify(msg, pk.as_raw())
|
||||
}
|
||||
|
||||
/// Returns the underlying signature.
|
||||
pub fn as_raw(&self) -> &RawSignature {
|
||||
&self.signature
|
||||
}
|
||||
|
||||
/// Returns a new empty signature.
|
||||
pub fn empty_signature() -> Self {
|
||||
// Set RawSignature = infinity
|
||||
let mut empty: Vec<u8> = vec![0; BLS_SIG_BYTE_SIZE];
|
||||
empty[0] += u8::pow(2, 6) + u8::pow(2, 7);
|
||||
Signature {
|
||||
signature: RawSignature::from_bytes(&empty).unwrap(),
|
||||
is_empty: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a BLS Signature to bytes
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
if self.is_empty {
|
||||
return vec![0; 96];
|
||||
}
|
||||
self.signature.as_bytes()
|
||||
}
|
||||
|
||||
// Convert bytes to BLS Signature
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
for byte in bytes {
|
||||
if *byte != 0 {
|
||||
let raw_signature = RawSignature::from_bytes(&bytes).map_err(|_| {
|
||||
DecodeError::BytesInvalid(format!("Invalid Signature bytes: {:?}", bytes))
|
||||
})?;
|
||||
return Ok(Signature {
|
||||
signature: raw_signature,
|
||||
is_empty: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(Signature::empty_signature())
|
||||
}
|
||||
|
||||
// Check for empty Signature
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.is_empty
|
||||
}
|
||||
|
||||
/// Display a signature as a hex string of its bytes.
|
||||
#[cfg(test)]
|
||||
pub fn as_hex_string(&self) -> String {
|
||||
hex_encode(self.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl_ssz!(Signature, BLS_SIG_BYTE_SIZE, "Signature");
|
||||
|
||||
impl_tree_hash!(Signature, BLS_SIG_BYTE_SIZE);
|
||||
|
||||
impl Serialize for Signature {
|
||||
/// Serde serialization is compliant the Ethereum YAML test format.
|
||||
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 Signature {
|
||||
/// Serde serialization is compliant the Ethereum YAML test format.
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
|
||||
let signature = Self::from_ssz_bytes(&bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid ssz ({:?})", e)))?;
|
||||
Ok(signature)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl arbitrary::Arbitrary for Signature {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
let mut bytes = [0u8; BLS_SIG_BYTE_SIZE];
|
||||
u.fill_buffer(&mut bytes)?;
|
||||
Self::from_bytes(&bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 = Signature::new(&[42, 42], &keypair.sk);
|
||||
|
||||
let bytes = ssz_encode(&original);
|
||||
let decoded = Signature::from_ssz_bytes(&bytes).unwrap();
|
||||
|
||||
assert_eq!(original, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_byte_size() {
|
||||
let keypair = Keypair::random();
|
||||
|
||||
let signature = Signature::new(&[42, 42], &keypair.sk);
|
||||
let bytes = ssz_encode(&signature);
|
||||
assert_eq!(bytes.len(), BLS_SIG_BYTE_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_empty_signature() {
|
||||
let sig = Signature::empty_signature();
|
||||
|
||||
let sig_as_bytes: Vec<u8> = sig.as_raw().as_bytes();
|
||||
|
||||
assert_eq!(sig_as_bytes.len(), BLS_SIG_BYTE_SIZE);
|
||||
for (i, one_byte) in sig_as_bytes.iter().enumerate() {
|
||||
if i == 0 {
|
||||
assert_eq!(*one_byte, u8::pow(2, 6) + u8::pow(2, 7));
|
||||
} else {
|
||||
assert_eq!(*one_byte, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
crypto/bls/src/signature_bytes.rs
Normal file
39
crypto/bls/src/signature_bytes.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use ssz::{Decode, DecodeError, Encode};
|
||||
|
||||
use super::{Signature, BLS_SIG_BYTE_SIZE};
|
||||
|
||||
bytes_struct!(SignatureBytes, Signature, BLS_SIG_BYTE_SIZE, "signature");
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::TryInto;
|
||||
|
||||
use ssz::ssz_encode;
|
||||
|
||||
use super::super::Keypair;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn test_valid_signature() {
|
||||
let keypair = Keypair::random();
|
||||
let original = Signature::new(&[42, 42], &keypair.sk);
|
||||
|
||||
let bytes = ssz_encode(&original);
|
||||
let signature_bytes = SignatureBytes::from_bytes(&bytes).unwrap();
|
||||
let signature: Result<Signature, _> = (&signature_bytes).try_into();
|
||||
assert!(signature.is_ok());
|
||||
assert_eq!(original, signature.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
pub fn test_invalid_signature() {
|
||||
let mut signature_bytes = [0; BLS_SIG_BYTE_SIZE];
|
||||
signature_bytes[0] = 255; //a_flag1 == b_flag1 == c_flag1 == 1 and x1 = 0 shouldn't be allowed
|
||||
let signature_bytes = SignatureBytes::from_bytes(&signature_bytes[..]);
|
||||
assert!(signature_bytes.is_ok());
|
||||
|
||||
let signature: Result<Signature, _> = signature_bytes.as_ref().unwrap().try_into();
|
||||
assert!(signature.is_err());
|
||||
}
|
||||
}
|
||||
75
crypto/bls/src/signature_set.rs
Normal file
75
crypto/bls/src/signature_set.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use crate::{AggregateSignature, PublicKey, Signature};
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
use milagro_bls::{
|
||||
AggregatePublicKey as RawAggregatePublicKey, AggregateSignature as RawAggregateSignature,
|
||||
PublicKey as RawPublicKey,
|
||||
};
|
||||
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
use crate::fakes::{
|
||||
AggregatePublicKey as RawAggregatePublicKey, AggregateSignature as RawAggregateSignature,
|
||||
PublicKey as RawPublicKey,
|
||||
};
|
||||
|
||||
type Message = Vec<u8>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SignatureSet {
|
||||
pub signature: RawAggregateSignature,
|
||||
signing_keys: RawAggregatePublicKey,
|
||||
message: Message,
|
||||
}
|
||||
|
||||
impl SignatureSet {
|
||||
pub fn single(signature: &Signature, signing_key: Cow<PublicKey>, message: Message) -> Self {
|
||||
Self {
|
||||
signature: RawAggregateSignature::from_signature(signature.as_raw()),
|
||||
signing_keys: RawAggregatePublicKey::from_public_key(signing_key.as_raw()),
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
signature: &AggregateSignature,
|
||||
signing_keys: Vec<Cow<PublicKey>>,
|
||||
message: Message,
|
||||
) -> Self
|
||||
where {
|
||||
let signing_keys_refs: Vec<&RawPublicKey> =
|
||||
signing_keys.iter().map(|pk| pk.as_raw()).collect();
|
||||
Self {
|
||||
signature: signature.as_raw().clone(),
|
||||
signing_keys: RawAggregatePublicKey::aggregate(&signing_keys_refs),
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.signature
|
||||
.fast_aggregate_verify_pre_aggregated(&self.message, &self.signing_keys)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
type VerifySet<'a> = (
|
||||
&'a RawAggregateSignature,
|
||||
&'a RawAggregatePublicKey,
|
||||
&'a [u8],
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
pub fn verify_signature_sets<'a>(sets: Vec<SignatureSet>) -> bool {
|
||||
let rng = &mut rand::thread_rng();
|
||||
let verify_set: Vec<VerifySet> = sets
|
||||
.iter()
|
||||
.map(|ss| (&ss.signature, &ss.signing_keys, ss.message.as_slice()))
|
||||
.collect();
|
||||
RawAggregateSignature::verify_multiple_aggregate_signatures(rng, verify_set.into_iter())
|
||||
}
|
||||
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
pub fn verify_signature_sets<'a>(_: Vec<SignatureSet>) -> bool {
|
||||
true
|
||||
}
|
||||
Reference in New Issue
Block a user