From 7a96ad130e4924befadb27e51159d8491e12a45d Mon Sep 17 00:00:00 2001 From: Kirk Baird Date: Tue, 21 May 2019 12:46:22 +1000 Subject: [PATCH] Finalise bls spec tests --- eth2/utils/bls/src/fake_public_key.rs | 11 ++- tests/ef_tests/src/cases.rs | 16 +++- .../src/cases/bls_aggregate_pubkeys.rs | 19 ++-- .../ef_tests/src/cases/bls_aggregate_sigs.rs | 19 ++-- ...{g2_compressed.rs => bls_g2_compressed.rs} | 38 ++++---- .../ef_tests/src/cases/bls_g2_uncompressed.rs | 85 ++++++++++++++++++ tests/ef_tests/src/cases/bls_priv_to_pub.rs | 53 +++++++++++ tests/ef_tests/src/cases/bls_sign_msg.rs | 88 +++++++++++++++++++ tests/ef_tests/src/doc.rs | 19 +++- tests/ef_tests/tests/tests.rs | 2 - 10 files changed, 295 insertions(+), 55 deletions(-) rename tests/ef_tests/src/cases/{g2_compressed.rs => bls_g2_compressed.rs} (57%) create mode 100644 tests/ef_tests/src/cases/bls_g2_uncompressed.rs create mode 100644 tests/ef_tests/src/cases/bls_priv_to_pub.rs create mode 100644 tests/ef_tests/src/cases/bls_sign_msg.rs diff --git a/eth2/utils/bls/src/fake_public_key.rs b/eth2/utils/bls/src/fake_public_key.rs index 16cdba53e..2c14191c0 100644 --- a/eth2/utils/bls/src/fake_public_key.rs +++ b/eth2/utils/bls/src/fake_public_key.rs @@ -16,7 +16,7 @@ use tree_hash::tree_hash_ssz_encoding_as_vector; /// serialization). #[derive(Debug, Clone, Eq)] pub struct FakePublicKey { - bytes: Vec + bytes: Vec, } impl FakePublicKey { @@ -34,14 +34,14 @@ impl FakePublicKey { /// Returns the underlying point as compressed bytes. /// /// Identical to `self.as_uncompressed_bytes()`. - fn as_bytes(&self) -> Vec { + pub fn as_bytes(&self) -> Vec { self.bytes.clone() } /// Converts compressed bytes to FakePublicKey pub fn from_bytes(bytes: &[u8]) -> Result { Ok(Self { - bytes: bytes.to_vec() + bytes: bytes.to_vec(), }) } @@ -63,6 +63,11 @@ impl FakePublicKey { let end_bytes = &bytes[bytes.len().saturating_sub(6)..bytes.len()]; hex_encode(end_bytes) } + + // Returns itself + pub fn as_raw(&self) -> &Self { + self + } } impl fmt::Display for FakePublicKey { diff --git a/tests/ef_tests/src/cases.rs b/tests/ef_tests/src/cases.rs index fe6b22432..3097517de 100644 --- a/tests/ef_tests/src/cases.rs +++ b/tests/ef_tests/src/cases.rs @@ -2,15 +2,23 @@ use super::*; use crate::yaml_decode::*; use yaml_rust::YamlLoader; -mod ssz_generic; -mod ssz_static; mod bls_aggregate_pubkeys; mod bls_aggregate_sigs; +mod bls_g2_compressed; +mod bls_g2_uncompressed; +mod bls_priv_to_pub; +mod bls_sign_msg; +mod ssz_generic; +mod ssz_static; -pub use ssz_generic::*; -pub use ssz_static::*; pub use bls_aggregate_pubkeys::*; pub use bls_aggregate_sigs::*; +pub use bls_g2_compressed::*; +pub use bls_g2_uncompressed::*; +pub use bls_priv_to_pub::*; +pub use bls_sign_msg::*; +pub use ssz_generic::*; +pub use ssz_static::*; #[derive(Debug)] pub struct Cases { diff --git a/tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs b/tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs index 7a2a62207..8bbf1fc5a 100644 --- a/tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs +++ b/tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs @@ -1,10 +1,7 @@ use super::*; use crate::case_result::compare_result; use bls::{AggregatePublicKey, PublicKey}; -use ethereum_types::{U128, U256}; use serde_derive::Deserialize; -use ssz::Decode; -use std::fmt::Debug; use types::EthSpec; #[derive(Debug, Clone, Deserialize)] @@ -25,7 +22,7 @@ impl EfTest for Cases { .iter() .enumerate() .map(|(i, tc)| { - let result = bls_add_aggregates::(&tc.input, &tc.output); + let result = bls_add_pubkeys(&tc.input, &tc.output); CaseResult::new(i, tc, result) }) @@ -34,20 +31,20 @@ impl EfTest for Cases { } /// Execute a `aggregate_pubkeys` test case. -fn bls_add_aggregates( - inputs: &[String], - output: &String, -) -> Result<(), Error> { +fn bls_add_pubkeys(inputs: &[String], output: &String) -> Result<(), Error> { let mut aggregate_pubkey = AggregatePublicKey::new(); for key_str in inputs { - let key = hex::decode(&key_str[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let key = PublicKey::from_bytes(&key).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + let key = + hex::decode(&key_str[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + let key = PublicKey::from_bytes(&key) + .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; aggregate_pubkey.add(&key); } - let output_bytes = Some(hex::decode(&output[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?); + let output_bytes = + Some(hex::decode(&output[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?); let aggregate_pubkey = Ok(aggregate_pubkey.as_raw().as_bytes()); compare_result::, Vec>(&aggregate_pubkey, &output_bytes) diff --git a/tests/ef_tests/src/cases/bls_aggregate_sigs.rs b/tests/ef_tests/src/cases/bls_aggregate_sigs.rs index 8d337a1ee..1b8bede33 100644 --- a/tests/ef_tests/src/cases/bls_aggregate_sigs.rs +++ b/tests/ef_tests/src/cases/bls_aggregate_sigs.rs @@ -1,10 +1,7 @@ use super::*; use crate::case_result::compare_result; use bls::{AggregateSignature, Signature}; -use ethereum_types::{U128, U256}; use serde_derive::Deserialize; -use ssz::Decode; -use std::fmt::Debug; use types::EthSpec; #[derive(Debug, Clone, Deserialize)] @@ -25,7 +22,7 @@ impl EfTest for Cases { .iter() .enumerate() .map(|(i, tc)| { - let result = bls_add_aggregates::(&tc.input, &tc.output); + let result = bls_add_signatures(&tc.input, &tc.output); CaseResult::new(i, tc, result) }) @@ -34,20 +31,20 @@ impl EfTest for Cases { } /// Execute a `aggregate_sigs` test case. -fn bls_add_aggregates( - inputs: &[String], - output: &String, -) -> Result<(), Error> { +fn bls_add_signatures(inputs: &[String], output: &String) -> Result<(), Error> { let mut aggregate_signature = AggregateSignature::new(); for key_str in inputs { - let sig = hex::decode(&key_str[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let sig = Signature::from_bytes(&sig).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + let sig = + hex::decode(&key_str[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + let sig = Signature::from_bytes(&sig) + .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; aggregate_signature.add(&sig); } - let output_bytes = Some(hex::decode(&output[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?); + let output_bytes = + Some(hex::decode(&output[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?); let aggregate_signature = Ok(aggregate_signature.as_bytes()); compare_result::, Vec>(&aggregate_signature, &output_bytes) diff --git a/tests/ef_tests/src/cases/g2_compressed.rs b/tests/ef_tests/src/cases/bls_g2_compressed.rs similarity index 57% rename from tests/ef_tests/src/cases/g2_compressed.rs rename to tests/ef_tests/src/cases/bls_g2_compressed.rs index 306d34604..95d36028f 100644 --- a/tests/ef_tests/src/cases/g2_compressed.rs +++ b/tests/ef_tests/src/cases/bls_g2_compressed.rs @@ -1,10 +1,7 @@ use super::*; use crate::case_result::compare_result; use bls::{compress_g2, hash_on_g2}; -use ethereum_types::{U128, U256}; use serde_derive::Deserialize; -use ssz::Decode; -use std::fmt::Debug; use types::EthSpec; #[derive(Debug, Clone, Deserialize)] @@ -40,36 +37,35 @@ impl EfTest for Cases { } /// Execute a `compressed hash to g2` test case. -fn compressed_hash( - message: &String, - domain: &String, - output: &Vec, -) -> Result<(), Error> { - let msg = hex::decode(&message[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; +fn compressed_hash(message: &String, domain: &String, output: &Vec) -> Result<(), Error> { + // Convert message and domain to required types + let msg = + hex::decode(&message[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; let d = hex::decode(&domain[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; let d = bytes_to_u64(&d); - let point = hash_on_g2 + // Calculate the point and convert it to compressed bytes + let mut point = hash_on_g2(&msg, d); + let point = compress_g2(&mut point); + // Convert the output to one set of bytes + let mut decoded = + hex::decode(&output[0][2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + let mut decoded_y = + hex::decode(&output[1][2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + decoded.append(&mut decoded_y); - let mut output = hex::decode(&output[0][2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let output_y = hex::decode(&output[1][2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - output.append(&output_y); - - let point = hash_on_g2(&msg, d); - let point = compress_g2(&point); - - compare_result::, Vec>(Ok(point), Some(output)) + compare_result::, Vec>(&Ok(point), &Some(decoded)) } -// Converts a vector to u64 (from little endian) +// Converts a vector to u64 (from big endian) fn bytes_to_u64(array: &Vec) -> u64 { let mut result: u64 = 0; - for (i, value) in array.iter().enumerate() { + for (i, value) in array.iter().rev().enumerate() { if i == 8 { break; } - result += u64::pow(2, i * 8) * *value; + result += u64::pow(2, i as u32 * 8) * (*value as u64); } result } diff --git a/tests/ef_tests/src/cases/bls_g2_uncompressed.rs b/tests/ef_tests/src/cases/bls_g2_uncompressed.rs new file mode 100644 index 000000000..49c9c734f --- /dev/null +++ b/tests/ef_tests/src/cases/bls_g2_uncompressed.rs @@ -0,0 +1,85 @@ +use super::*; +use crate::case_result::compare_result; +use bls::hash_on_g2; +use serde_derive::Deserialize; +use types::EthSpec; + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsG2UncompressedInput { + pub message: String, + pub domain: String, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsG2Uncompressed { + pub input: BlsG2UncompressedInput, + pub output: Vec>, +} + +impl YamlDecode for BlsG2Uncompressed { + fn yaml_decode(yaml: &String) -> Result { + Ok(serde_yaml::from_str(&yaml.as_str()).unwrap()) + } +} + +impl EfTest for Cases { + fn test_results(&self) -> Vec { + self.test_cases + .iter() + .enumerate() + .map(|(i, tc)| { + let result = compressed_hash(&tc.input.message, &tc.input.domain, &tc.output); + + CaseResult::new(i, tc, result) + }) + .collect() + } +} + +/// Execute a `compressed hash to g2` test case. +fn compressed_hash( + message: &String, + domain: &String, + output: &Vec>, +) -> Result<(), Error> { + // Convert message and domain to required types + let msg = + hex::decode(&message[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + let d = hex::decode(&domain[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + let d = bytes_to_u64(&d); + + // Calculate the point and convert it to compressed bytes + let point = hash_on_g2(&msg, d); + let mut point_bytes = [0 as u8; 288]; + point.getpx().geta().tobytearray(&mut point_bytes, 0); + point.getpx().getb().tobytearray(&mut point_bytes, 48); + point.getpy().geta().tobytearray(&mut point_bytes, 96); + point.getpy().getb().tobytearray(&mut point_bytes, 144); + point.getpz().geta().tobytearray(&mut point_bytes, 192); + point.getpz().getb().tobytearray(&mut point_bytes, 240); + + // Convert the output to one set of bytes (x.a, x.b, y.a, y.b, z.a, z.b) + let mut decoded: Vec = vec![]; + for coordinate in output { + let mut decoded_part = hex::decode(&coordinate[0][2..]) + .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + decoded.append(&mut decoded_part); + decoded_part = hex::decode(&coordinate[1][2..]) + .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + decoded.append(&mut decoded_part); + } + + compare_result::, Vec>(&Ok(point_bytes.to_vec()), &Some(decoded)) +} + +// Converts a vector to u64 (from big endian) +fn bytes_to_u64(array: &Vec) -> u64 { + let mut result: u64 = 0; + for (i, value) in array.iter().rev().enumerate() { + if i == 8 { + break; + } + result += u64::pow(2, i as u32 * 8) * (*value as u64); + } + result +} diff --git a/tests/ef_tests/src/cases/bls_priv_to_pub.rs b/tests/ef_tests/src/cases/bls_priv_to_pub.rs new file mode 100644 index 000000000..b5b4fc997 --- /dev/null +++ b/tests/ef_tests/src/cases/bls_priv_to_pub.rs @@ -0,0 +1,53 @@ +use super::*; +use crate::case_result::compare_result; +use bls::{PublicKey, SecretKey}; +use serde_derive::Deserialize; +use types::EthSpec; + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsPrivToPub { + pub input: String, + pub output: String, +} + +impl YamlDecode for BlsPrivToPub { + fn yaml_decode(yaml: &String) -> Result { + Ok(serde_yaml::from_str(&yaml.as_str()).unwrap()) + } +} + +impl EfTest for Cases { + fn test_results(&self) -> Vec { + self.test_cases + .iter() + .enumerate() + .map(|(i, tc)| { + let result = secret_to_public(&tc.input, &tc.output); + + CaseResult::new(i, tc, result) + }) + .collect() + } +} + +/// Execute a `Private key to public key` test case. +fn secret_to_public(secret: &String, output: &String) -> Result<(), Error> { + // Convert message and domain to required types + let mut sk = + hex::decode(&secret[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + pad_to_48(&mut sk); + let sk = SecretKey::from_bytes(&sk).unwrap(); + let pk = PublicKey::from_secret_key(&sk); + + let decoded = + hex::decode(&output[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + + compare_result::, Vec>(&Ok(pk.as_raw().as_bytes()), &Some(decoded)) +} + +// Increase the size of an array to 48 bytes +fn pad_to_48(array: &mut Vec) { + while array.len() < 48 { + array.insert(0, 0); + } +} diff --git a/tests/ef_tests/src/cases/bls_sign_msg.rs b/tests/ef_tests/src/cases/bls_sign_msg.rs new file mode 100644 index 000000000..c62431365 --- /dev/null +++ b/tests/ef_tests/src/cases/bls_sign_msg.rs @@ -0,0 +1,88 @@ +use super::*; +use crate::case_result::compare_result; +use bls::{SecretKey, Signature}; +use serde_derive::Deserialize; +use types::EthSpec; + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsSignInput { + pub privkey: String, + pub message: String, + pub domain: String, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct BlsSign { + pub input: BlsSignInput, + pub output: String, +} + +impl YamlDecode for BlsSign { + fn yaml_decode(yaml: &String) -> Result { + Ok(serde_yaml::from_str(&yaml.as_str()).unwrap()) + } +} + +impl EfTest for Cases { + fn test_results(&self) -> Vec { + self.test_cases + .iter() + .enumerate() + .map(|(i, tc)| { + let result = sign_msg( + &tc.input.privkey, + &tc.input.message, + &tc.input.domain, + &tc.output, + ); + + CaseResult::new(i, tc, result) + }) + .collect() + } +} + +/// Execute a `compressed hash to g2` test case. +fn sign_msg( + private_key: &String, + message: &String, + domain: &String, + output: &String, +) -> Result<(), Error> { + // Convert private_key, message and domain to required types + let mut sk = + hex::decode(&private_key[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + pad_to_48(&mut sk); + let sk = SecretKey::from_bytes(&sk).unwrap(); + let msg = + hex::decode(&message[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + let d = hex::decode(&domain[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + let d = bytes_to_u64(&d); + + let signature = Signature::new(&msg, d, &sk); + + // Convert the output to one set of bytes + let decoded = + hex::decode(&output[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + + compare_result::, Vec>(&Ok(signature.as_bytes()), &Some(decoded)) +} + +// Converts a vector to u64 (from big endian) +fn bytes_to_u64(array: &Vec) -> u64 { + let mut result: u64 = 0; + for (i, value) in array.iter().rev().enumerate() { + if i == 8 { + break; + } + result += u64::pow(2, i as u32 * 8) * (*value as u64); + } + result +} + +// Increase the size of an array to 48 bytes +fn pad_to_48(array: &mut Vec) { + while array.len() < 48 { + array.insert(0, 0); + } +} diff --git a/tests/ef_tests/src/doc.rs b/tests/ef_tests/src/doc.rs index 1d7d3994c..a3d55ad01 100644 --- a/tests/ef_tests/src/doc.rs +++ b/tests/ef_tests/src/doc.rs @@ -35,9 +35,22 @@ impl Doc { ("ssz", "uint", _) => run_test::(&self.yaml), ("ssz", "static", "minimal") => run_test::(&self.yaml), ("ssz", "static", "mainnet") => run_test::(&self.yaml), - ("bls", "aggregate_pubkeys", "mainnet") => run_test::(&self.yaml), - ("bls", "aggregate_sigs", "mainnet") => run_test::(&self.yaml), - + ("bls", "aggregate_pubkeys", "mainnet") => { + run_test::(&self.yaml) + } + ("bls", "aggregate_sigs", "mainnet") => { + run_test::(&self.yaml) + } + ("bls", "msg_hash_compressed", "mainnet") => { + run_test::(&self.yaml) + } + ("bls", "msg_hash_uncompressed", "mainnet") => { + run_test::(&self.yaml) + } + ("bls", "priv_to_pub", "mainnet") => { + run_test::(&self.yaml) + } + ("bls", "sign_msg", "mainnet") => run_test::(&self.yaml), (runner, handler, config) => panic!( "No implementation for runner: \"{}\", handler: \"{}\", config: \"{}\"", runner, handler, config diff --git a/tests/ef_tests/tests/tests.rs b/tests/ef_tests/tests/tests.rs index 88059eaba..a52e3757a 100644 --- a/tests/ef_tests/tests/tests.rs +++ b/tests/ef_tests/tests/tests.rs @@ -26,7 +26,6 @@ fn yaml_files_in_test_dir(dir: &str) -> Vec { .collect() } -/* #[test] fn ssz_generic() { yaml_files_in_test_dir("ssz_generic") @@ -44,7 +43,6 @@ fn ssz_static() { Doc::assert_tests_pass(file); }); } -*/ #[test] fn bls() {