lighthouse/crypto/bls/tests/tests.rs
Michael Sproul 36bd4d87f0 Update to spec v1.0.0-rc.0 and BLSv4 (#1765)
## Issue Addressed

Closes #1504 
Closes #1505
Replaces #1703
Closes #1707

## Proposed Changes

* Update BLST and Milagro to versions compatible with BLSv4 spec
* Update Lighthouse to spec v1.0.0-rc.0, and update EF test vectors
* Use the v1.0.0 constants for `MainnetEthSpec`.
* Rename `InteropEthSpec` -> `V012LegacyEthSpec`
    * Change all constants to suit the mainnet `v0.12.3` specification (i.e., Medalla).
* Deprecate the `--spec` flag for the `lighthouse` binary
    * This value is now obtained from the `config_name` field of the `YamlConfig`.
        * Built in testnet YAML files have been updated.
    * Ignore the `--spec` value, if supplied, log a warning that it will be deprecated
    * `lcli` still has the spec flag, that's fine because it's dev tooling.
* Remove the `E: EthSpec` from `YamlConfig`
    * This means we need to deser the genesis `BeaconState` on-demand, but this is fine.
* Swap the old "minimal", "mainnet" strings over to the new `EthSpecId` enum.
* Always require a `CONFIG_NAME` field in `YamlConfig` (it used to have a default).

## Additional Info

Lots of breaking changes, do not merge! ~~We will likely need a Lighthouse v0.4.0 branch, and possibly a long-term v0.3.0 branch to keep Medalla alive~~.

Co-authored-by: Kirk Baird <baird.k@outlook.com>
Co-authored-by: Paul Hauner <paul@paulhauner.com>
2020-10-28 22:19:38 +00:00

505 lines
16 KiB
Rust

use bls::{Hash256, INFINITY_SIGNATURE, SECRET_KEY_BYTES_LEN};
use ssz::{Decode, Encode};
use std::borrow::Cow;
use std::fmt::Debug;
fn ssz_round_trip<T: Encode + Decode + PartialEq + Debug>(item: T) {
assert_eq!(item, T::from_ssz_bytes(&item.as_ssz_bytes()).unwrap());
}
macro_rules! test_suite {
($impls: ident) => {
use super::*;
use bls::$impls::*;
fn secret_from_u64(i: u64) -> SecretKey {
let mut secret_bytes = [0; 32];
// Use i + 1 to avoid the all-zeros secret key.
secret_bytes[32 - 8..].copy_from_slice(&(i + 1).to_be_bytes());
SecretKey::deserialize(&secret_bytes).unwrap()
}
#[test]
fn invalid_zero_secret_key() {
assert!(SecretKey::deserialize(&[0; SECRET_KEY_BYTES_LEN]).is_err());
}
#[test]
fn infinity_agg_sig() {
assert_eq!(
&AggregateSignature::infinity().serialize()[..],
&INFINITY_SIGNATURE[..]
);
assert_eq!(
AggregateSignature::deserialize(&INFINITY_SIGNATURE).unwrap(),
AggregateSignature::infinity(),
);
}
#[test]
fn ssz_round_trip_multiple_types() {
let mut agg_sig = AggregateSignature::infinity();
ssz_round_trip(agg_sig.clone());
let msg = Hash256::from_low_u64_be(42);
let secret = secret_from_u64(42);
let sig = secret.sign(msg);
ssz_round_trip(sig.clone());
agg_sig.add_assign(&sig);
ssz_round_trip(agg_sig);
}
#[test]
fn ssz_round_trip_sig_empty() {
ssz_round_trip(Signature::empty())
}
#[test]
fn ssz_round_trip_agg_sig_empty() {
ssz_round_trip(AggregateSignature::empty())
}
#[test]
fn ssz_round_trip_agg_sig_infinity() {
ssz_round_trip(AggregateSignature::infinity())
}
#[test]
fn partial_eq_empty_sig() {
assert_eq!(Signature::empty(), Signature::empty())
}
#[test]
fn partial_eq_empty_sig_and_non_empty_sig() {
assert!(Signature::empty() != SignatureTester::default().sig)
}
#[test]
fn partial_eq_empty_agg_sig() {
assert_eq!(AggregateSignature::empty(), AggregateSignature::empty())
}
#[test]
fn partial_eq_empty_agg_sig_and_real_agg_sig() {
assert!(
AggregateSignature::empty() != AggregateSignatureTester::new_with_single_msg(1).sig
)
}
#[test]
fn partial_eq_infinity_agg_sig() {
assert_eq!(
AggregateSignature::infinity(),
AggregateSignature::infinity()
)
}
#[test]
fn partial_eq_infinity_agg_sig_and_real_agg_sig() {
assert!(
AggregateSignature::infinity()
!= AggregateSignatureTester::new_with_single_msg(1).sig
)
}
#[test]
fn partial_eq_infinity_agg_sig_and_empty_agg_sig() {
assert!(AggregateSignature::infinity() != AggregateSignature::empty())
}
/// A helper struct for composing tests via the builder pattern.
struct SignatureTester {
sig: Signature,
pubkey: PublicKey,
msg: Hash256,
}
impl Default for SignatureTester {
fn default() -> Self {
let secret = SecretKey::deserialize(&[42; 32]).unwrap();
let pubkey = secret.public_key();
let msg = Hash256::from_low_u64_be(42);
Self {
sig: secret.sign(msg),
pubkey,
msg,
}
}
}
impl SignatureTester {
pub fn infinity_sig(mut self) -> Self {
self.sig = Signature::deserialize(&INFINITY_SIGNATURE[..]).unwrap();
self
}
pub fn assert_verify(self, is_valid: bool) {
assert_eq!(self.sig.verify(&self.pubkey, self.msg), is_valid);
// Check a single-signature signature set.
assert_eq!(
SignatureSet::single_pubkey(&self.sig, Cow::Borrowed(&self.pubkey), self.msg,)
.verify(),
is_valid
)
}
}
#[test]
fn standard_signature_is_valid_with_standard_pubkey() {
SignatureTester::default().assert_verify(true)
}
#[test]
fn infinity_signature_is_invalid_with_standard_pubkey() {
SignatureTester::default()
.infinity_sig()
.assert_verify(false)
}
/// A helper struct for composing tests via the builder pattern.
struct AggregateSignatureTester {
sig: AggregateSignature,
pubkeys: Vec<PublicKey>,
msgs: Vec<Hash256>,
}
impl AggregateSignatureTester {
fn new_with_single_msg(num_pubkeys: u64) -> Self {
let mut pubkeys = Vec::with_capacity(num_pubkeys as usize);
let mut sig = AggregateSignature::infinity();
let msg = Hash256::from_low_u64_be(42);
for i in 0..num_pubkeys {
let secret = secret_from_u64(i);
pubkeys.push(secret.public_key());
sig.add_assign(&secret.sign(msg));
}
Self {
sig,
pubkeys,
msgs: vec![msg],
}
}
pub fn empty_sig(mut self) -> Self {
self.sig = AggregateSignature::empty();
self
}
pub fn wrong_sig(mut self) -> Self {
let sk = SecretKey::deserialize(&[1; 32]).unwrap();
self.sig = AggregateSignature::infinity();
self.sig.add_assign(&sk.sign(Hash256::from_low_u64_be(1)));
self
}
pub fn infinity_sig(mut self) -> Self {
self.sig = AggregateSignature::deserialize(&INFINITY_SIGNATURE[..]).unwrap();
self
}
pub fn aggregate_empty_sig(mut self) -> Self {
self.sig.add_assign(&Signature::empty());
self
}
pub fn aggregate_empty_agg_sig(mut self) -> Self {
self.sig.add_assign_aggregate(&AggregateSignature::empty());
self
}
pub fn aggregate_infinity_sig(mut self) -> Self {
self.sig
.add_assign(&Signature::deserialize(&INFINITY_SIGNATURE[..]).unwrap());
self
}
pub fn assert_single_message_verify(self, is_valid: bool) {
assert!(self.msgs.len() == 1);
let msg = self.msgs.first().unwrap();
let pubkeys = self.pubkeys.iter().collect::<Vec<_>>();
assert_eq!(
self.sig.fast_aggregate_verify(*msg, &pubkeys),
is_valid,
"fast_aggregate_verify expected {} but got {}",
is_valid,
!is_valid
);
let msgs = pubkeys.iter().map(|_| msg.clone()).collect::<Vec<_>>();
assert_eq!(
self.sig.aggregate_verify(&msgs, &pubkeys),
is_valid,
"aggregate_verify expected {} but got {}",
is_valid,
!is_valid
);
}
}
/// An aggregate without any signatures should not verify.
#[test]
fn fast_aggregate_verify_0_pubkeys() {
AggregateSignatureTester::new_with_single_msg(0).assert_single_message_verify(false)
}
/// An aggregate of size 1 should verify.
#[test]
fn fast_aggregate_verify_1_pubkey() {
AggregateSignatureTester::new_with_single_msg(1).assert_single_message_verify(true)
}
/// An aggregate of size 128 should verify.
#[test]
fn fast_aggregate_verify_128_pubkeys() {
AggregateSignatureTester::new_with_single_msg(128).assert_single_message_verify(true)
}
/// The infinity signature should not verify against 1 non-infinity pubkey.
#[test]
fn fast_aggregate_verify_infinity_signature_with_1_regular_public_key() {
AggregateSignatureTester::new_with_single_msg(1)
.infinity_sig()
.assert_single_message_verify(false)
}
/// The infinity signature should not verify against 128 non-infinity pubkeys.
#[test]
fn fast_aggregate_verify_infinity_signature_with_128_regular_public_keys() {
AggregateSignatureTester::new_with_single_msg(128)
.infinity_sig()
.assert_single_message_verify(false)
}
/// Adding a infinity signature (without an infinity pubkey) should verify.
#[test]
fn fast_aggregate_verify_with_one_aggregated_infinity_sig() {
AggregateSignatureTester::new_with_single_msg(1)
.aggregate_infinity_sig()
.assert_single_message_verify(true)
}
/// Adding four infinity signatures (without any infinity pubkeys) should verify.
#[test]
fn fast_aggregate_verify_with_four_aggregated_infinity_sig() {
AggregateSignatureTester::new_with_single_msg(1)
.aggregate_infinity_sig()
.aggregate_infinity_sig()
.aggregate_infinity_sig()
.aggregate_infinity_sig()
.assert_single_message_verify(true)
}
/// The wrong signature should not verify.
#[test]
fn fast_aggregate_verify_wrong_signature() {
AggregateSignatureTester::new_with_single_msg(1)
.wrong_sig()
.assert_single_message_verify(false)
}
/// An "empty" signature should not verify.
#[test]
fn fast_aggregate_verify_empty_signature() {
AggregateSignatureTester::new_with_single_msg(1)
.empty_sig()
.assert_single_message_verify(false)
}
/// Aggregating an "empty" signature should have no effect.
#[test]
fn fast_aggregate_verify_with_aggregated_empty_sig() {
AggregateSignatureTester::new_with_single_msg(1)
.aggregate_empty_sig()
.assert_single_message_verify(true)
}
/// Aggregating an "empty" aggregate signature should have no effect.
#[test]
fn fast_aggregate_verify_with_aggregated_empty_agg_sig() {
AggregateSignatureTester::new_with_single_msg(1)
.aggregate_empty_agg_sig()
.assert_single_message_verify(true)
}
/// A helper struct to make it easer to deal with `SignatureSet` lifetimes.
struct OwnedSignatureSet {
signature: AggregateSignature,
signing_keys: Vec<PublicKey>,
message: Hash256,
should_be_valid: bool,
}
impl OwnedSignatureSet {
pub fn multiple_pubkeys(&self) -> SignatureSet {
let signing_keys = self.signing_keys.iter().map(Cow::Borrowed).collect();
SignatureSet::multiple_pubkeys(&self.signature, signing_keys, self.message)
}
pub fn run_checks(&self) {
assert_eq!(
self.multiple_pubkeys().verify(),
self.should_be_valid,
"multiple pubkey expected {} but got {}",
self.should_be_valid,
!self.should_be_valid
)
}
}
/// A helper struct for composing tests via the builder pattern.
#[derive(Default)]
struct SignatureSetTester {
owned_sets: Vec<OwnedSignatureSet>,
}
impl SignatureSetTester {
pub fn push_valid_set(mut self, num_signers: usize) -> Self {
let mut signature = AggregateSignature::infinity();
let message = Hash256::from_low_u64_be(42);
let signing_keys = (0..num_signers)
.map(|i| {
let secret = secret_from_u64(i as u64);
signature.add_assign(&secret.sign(message));
secret.public_key()
})
.collect();
self.owned_sets.push(OwnedSignatureSet {
signature,
signing_keys,
message,
should_be_valid: true,
});
self
}
pub fn push_invalid_set(mut self) -> Self {
let mut signature = AggregateSignature::infinity();
let message = Hash256::from_low_u64_be(42);
signature.add_assign(&secret_from_u64(0).sign(message));
self.owned_sets.push(OwnedSignatureSet {
signature,
signing_keys: vec![secret_from_u64(42).public_key()],
message,
should_be_valid: false,
});
self
}
pub fn push_invalid_pubkey_infinity_set(mut self) -> Self {
self.owned_sets.push(OwnedSignatureSet {
signature: AggregateSignature::deserialize(&INFINITY_SIGNATURE).unwrap(),
signing_keys: vec![secret_from_u64(42).public_key()],
message: Hash256::zero(),
should_be_valid: false,
});
self
}
pub fn run_checks(self) {
assert!(!self.owned_sets.is_empty(), "empty test is meaningless");
for owned_set in &self.owned_sets {
owned_set.run_checks()
}
let should_be_valid = self
.owned_sets
.iter()
.all(|owned_set| owned_set.should_be_valid);
let signature_sets = self
.owned_sets
.iter()
.map(|owned_set| owned_set.multiple_pubkeys())
.collect::<Vec<_>>();
assert_eq!(
verify_signature_sets(signature_sets.iter()),
should_be_valid
);
}
}
#[test]
fn signature_set_1_valid_set_with_1_signer() {
SignatureSetTester::default().push_valid_set(1).run_checks()
}
#[test]
fn signature_set_1_invalid_set() {
SignatureSetTester::default()
.push_invalid_set()
.run_checks()
}
#[test]
fn signature_set_1_valid_set_with_2_signers() {
SignatureSetTester::default().push_valid_set(2).run_checks()
}
#[test]
fn signature_set_1_valid_set_with_128_signers() {
SignatureSetTester::default()
.push_valid_set(128)
.run_checks()
}
#[test]
fn signature_set_2_valid_set_with_one_signer_each() {
SignatureSetTester::default()
.push_valid_set(1)
.push_valid_set(1)
.run_checks()
}
#[test]
fn signature_set_2_valid_set_with_2_signers_each() {
SignatureSetTester::default()
.push_valid_set(2)
.push_valid_set(2)
.run_checks()
}
#[test]
fn signature_set_2_valid_set_with_1_invalid_set() {
SignatureSetTester::default()
.push_valid_set(2)
.push_invalid_set()
.run_checks()
}
#[test]
fn signature_set_3_sets_with_one_invalid_pubkey_infinity_set() {
SignatureSetTester::default()
.push_valid_set(2)
.push_invalid_pubkey_infinity_set()
.push_valid_set(2)
.run_checks()
}
};
}
mod blst {
test_suite!(blst_implementations);
}
#[cfg(not(debug_assertions))]
mod milagro {
test_suite!(milagro_implementations);
}