Merge branch 'ef-tests' into v0.6.1
This commit is contained in:
commit
1eeaaaa92b
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "tests/ef_tests/eth2.0-spec-tests"]
|
||||
path = tests/ef_tests/eth2.0-spec-tests
|
||||
url = https://github.com/ethereum/eth2.0-spec-tests
|
@ -29,6 +29,7 @@ members = [
|
||||
"beacon_node/rpc",
|
||||
"beacon_node/version",
|
||||
"beacon_node/beacon_chain",
|
||||
"tests/ef_tests",
|
||||
"protos",
|
||||
"validator_client",
|
||||
"account_manager",
|
||||
|
34
Jenkinsfile
vendored
34
Jenkinsfile
vendored
@ -1,34 +0,0 @@
|
||||
pipeline {
|
||||
agent {
|
||||
dockerfile {
|
||||
filename 'Dockerfile'
|
||||
args '-v cargo-cache:/cache/cargocache:rw -e "CARGO_HOME=/cache/cargocache"'
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'cargo build --verbose --all'
|
||||
sh 'cargo build --verbose --all --release'
|
||||
}
|
||||
}
|
||||
stage('Check') {
|
||||
steps {
|
||||
sh 'cargo fmt --all -- --check'
|
||||
// No clippy until later...
|
||||
//sh 'cargo clippy'
|
||||
}
|
||||
}
|
||||
stage('Test') {
|
||||
steps {
|
||||
sh 'cargo test --verbose --all'
|
||||
sh 'cargo test --verbose --all --release'
|
||||
sh 'cargo test --manifest-path eth2/state_processing/Cargo.toml --verbose \
|
||||
--release --features fake_crypto'
|
||||
sh 'cargo test --manifest-path eth2/state_processing/Cargo.toml --verbose \
|
||||
--release --features fake_crypto -- --ignored'
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::test_utils::{graffiti_from_hex_str, TestRandom};
|
||||
use crate::*;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@ -24,6 +24,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
pub struct BeaconBlockBody {
|
||||
pub randao_reveal: Signature,
|
||||
pub eth1_data: Eth1Data,
|
||||
#[serde(deserialize_with = "graffiti_from_hex_str")]
|
||||
pub graffiti: [u8; 32],
|
||||
pub proposer_slashings: Vec<ProposerSlashing>,
|
||||
pub attester_slashings: Vec<AttesterSlashing>,
|
||||
|
@ -81,7 +81,7 @@ pub type AttesterMap = HashMap<(u64, u64), Vec<usize>>;
|
||||
pub type ProposerMap = HashMap<u64, usize>;
|
||||
|
||||
pub use bls::{AggregatePublicKey, AggregateSignature, Keypair, PublicKey, SecretKey, Signature};
|
||||
pub use fixed_len_vec::{typenum::Unsigned, FixedLenVec};
|
||||
pub use fixed_len_vec::{typenum, typenum::Unsigned, FixedLenVec};
|
||||
pub use libp2p::floodsub::{Topic, TopicBuilder, TopicHash};
|
||||
pub use libp2p::multiaddr;
|
||||
pub use libp2p::Multiaddr;
|
||||
|
@ -14,5 +14,5 @@ pub use rand::{
|
||||
RngCore,
|
||||
{prng::XorShiftRng, SeedableRng},
|
||||
};
|
||||
pub use serde_utils::{fork_from_hex_str, u8_from_hex_str};
|
||||
pub use serde_utils::{fork_from_hex_str, graffiti_from_hex_str, u8_from_hex_str};
|
||||
pub use test_random::TestRandom;
|
||||
|
@ -2,6 +2,7 @@ use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
pub const FORK_BYTES_LEN: usize = 4;
|
||||
pub const GRAFFITI_BYTES_LEN: usize = 32;
|
||||
|
||||
pub fn u8_from_hex_str<'de, D>(deserializer: D) -> Result<u8, D::Error>
|
||||
where
|
||||
@ -32,3 +33,24 @@ where
|
||||
}
|
||||
Ok(array)
|
||||
}
|
||||
|
||||
pub fn graffiti_from_hex_str<'de, D>(deserializer: D) -> Result<[u8; GRAFFITI_BYTES_LEN], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
let mut array = [0 as u8; GRAFFITI_BYTES_LEN];
|
||||
let decoded: Vec<u8> = hex::decode(&s.as_str()[2..]).map_err(D::Error::custom)?;
|
||||
|
||||
if decoded.len() > GRAFFITI_BYTES_LEN {
|
||||
return Err(D::Error::custom("Fork length too long"));
|
||||
}
|
||||
|
||||
for (i, item) in array.iter_mut().enumerate() {
|
||||
if i > decoded.len() {
|
||||
break;
|
||||
}
|
||||
*item = decoded[i];
|
||||
}
|
||||
Ok(array)
|
||||
}
|
||||
|
@ -5,10 +5,11 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bls-aggregates = { git = "https://github.com/sigp/signature-schemes", tag = "0.6.1" }
|
||||
bls-aggregates = { git = "https://github.com/sigp/signature-schemes", branch = "secret-key-serialization" }
|
||||
cached_tree_hash = { path = "../cached_tree_hash" }
|
||||
hashing = { path = "../hashing" }
|
||||
hex = "0.3"
|
||||
rand = "0.5"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_hex = { path = "../serde_hex" }
|
||||
|
36
eth2/utils/bls/src/fake_aggregate_public_key.rs
Normal file
36
eth2/utils/bls/src/fake_aggregate_public_key.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use super::{PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE};
|
||||
use bls_aggregates::AggregatePublicKey as RawAggregatePublicKey;
|
||||
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
impl FakeAggregatePublicKey {
|
||||
pub fn new() -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
|
||||
/// Creates a new all-zero's aggregate public key
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
bytes: vec![0; BLS_PUBLIC_KEY_BYTE_SIZE],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, _public_key: &PublicKey) {
|
||||
// No nothing.
|
||||
}
|
||||
|
||||
pub fn as_raw(&self) -> &FakeAggregatePublicKey {
|
||||
&self
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
self.bytes.clone()
|
||||
}
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
use super::{fake_signature::FakeSignature, AggregatePublicKey, BLS_AGG_SIG_BYTE_SIZE};
|
||||
use super::{
|
||||
fake_aggregate_public_key::FakeAggregatePublicKey, fake_signature::FakeSignature,
|
||||
BLS_AGG_SIG_BYTE_SIZE,
|
||||
};
|
||||
use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
@ -43,7 +46,7 @@ impl FakeAggregateSignature {
|
||||
&self,
|
||||
_msg: &[u8],
|
||||
_domain: u64,
|
||||
_aggregate_public_key: &AggregatePublicKey,
|
||||
_aggregate_public_key: &FakeAggregatePublicKey,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
@ -53,7 +56,7 @@ impl FakeAggregateSignature {
|
||||
&self,
|
||||
_messages: &[&[u8]],
|
||||
_domain: u64,
|
||||
_aggregate_public_keys: &[&AggregatePublicKey],
|
||||
_aggregate_public_keys: &[&FakeAggregatePublicKey],
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
|
169
eth2/utils/bls/src/fake_public_key.rs
Normal file
169
eth2/utils/bls/src/fake_public_key.rs
Normal file
@ -0,0 +1,169 @@
|
||||
use super::{SecretKey, BLS_PUBLIC_KEY_BYTE_SIZE};
|
||||
use bls_aggregates::PublicKey as RawPublicKey;
|
||||
use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_hex::{encode as hex_encode, HexVisitor};
|
||||
use ssz::{ssz_encode, Decode, DecodeError};
|
||||
use std::default;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use tree_hash::tree_hash_ssz_encoding_as_vector;
|
||||
|
||||
/// A single BLS signature.
|
||||
///
|
||||
/// This struct is a wrapper upon a base type and provides helper functions (e.g., SSZ
|
||||
/// serialization).
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub struct FakePublicKey {
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl FakePublicKey {
|
||||
pub fn from_secret_key(_secret_key: &SecretKey) -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
|
||||
/// Creates a new all-zero's public key
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
bytes: vec![0; BLS_PUBLIC_KEY_BYTE_SIZE],
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying point as compressed bytes.
|
||||
///
|
||||
/// Identical to `self.as_uncompressed_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(),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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 itself
|
||||
pub fn as_raw(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FakePublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.concatenated_hex_id())
|
||||
}
|
||||
}
|
||||
|
||||
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 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(HexVisitor)?;
|
||||
let pubkey = Self::from_ssz_bytes(&bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid pubkey ({:?})", e)))?;
|
||||
Ok(pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
tree_hash_ssz_encoding_as_vector!(FakePublicKey);
|
||||
cached_tree_hash_ssz_encoding_as_vector!(FakePublicKey, 48);
|
||||
|
||||
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(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ssz::ssz_encode;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_cached_tree_hash() {
|
||||
let sk = SecretKey::random();
|
||||
let original = FakePublicKey::from_secret_key(&sk);
|
||||
|
||||
let mut cache = cached_tree_hash::TreeHashCache::new(&original).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
cache.tree_hash_root().unwrap().to_vec(),
|
||||
original.tree_hash_root()
|
||||
);
|
||||
|
||||
let sk = SecretKey::random();
|
||||
let modified = FakePublicKey::from_secret_key(&sk);
|
||||
|
||||
cache.update(&modified).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
cache.tree_hash_root().unwrap().to_vec(),
|
||||
modified.tree_hash_root()
|
||||
);
|
||||
}
|
||||
}
|
@ -3,33 +3,50 @@ extern crate ssz;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod aggregate_public_key;
|
||||
mod keypair;
|
||||
mod public_key;
|
||||
mod secret_key;
|
||||
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
mod aggregate_signature;
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
mod signature;
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
pub use crate::aggregate_signature::AggregateSignature;
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
pub use crate::signature::Signature;
|
||||
pub use crate::keypair::Keypair;
|
||||
pub use crate::secret_key::SecretKey;
|
||||
pub use bls_aggregates::{compress_g2, hash_on_g2};
|
||||
|
||||
#[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(feature = "fake_crypto")]
|
||||
pub use crate::fake_aggregate_signature::FakeAggregateSignature as AggregateSignature;
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
pub use crate::fake_signature::FakeSignature as Signature;
|
||||
|
||||
pub use crate::aggregate_public_key::AggregatePublicKey;
|
||||
pub use crate::keypair::Keypair;
|
||||
pub use crate::public_key::PublicKey;
|
||||
pub use crate::secret_key::SecretKey;
|
||||
#[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;
|
||||
|
@ -1,3 +1,5 @@
|
||||
extern crate rand;
|
||||
|
||||
use super::BLS_SECRET_KEY_BYTE_SIZE;
|
||||
use bls_aggregates::SecretKey as RawSecretKey;
|
||||
use hex::encode as hex_encode;
|
||||
@ -16,7 +18,7 @@ pub struct SecretKey(RawSecretKey);
|
||||
|
||||
impl SecretKey {
|
||||
pub fn random() -> Self {
|
||||
SecretKey(RawSecretKey::random())
|
||||
SecretKey(RawSecretKey::random(&mut rand::thread_rng()))
|
||||
}
|
||||
|
||||
/// Returns the underlying point as compressed bytes.
|
||||
|
@ -9,6 +9,7 @@ pub use typenum;
|
||||
mod impls;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct FixedLenVec<T, N> {
|
||||
vec: Vec<T>,
|
||||
_phantom: PhantomData<N>,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::*;
|
||||
use ethereum_types::H256;
|
||||
use ethereum_types::{H256, U128, U256};
|
||||
|
||||
macro_rules! impl_decodable_for_uint {
|
||||
($type: ident, $bit_size: expr) => {
|
||||
@ -85,6 +85,48 @@ impl Decode for H256 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for U256 {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ssz_fixed_len() -> usize {
|
||||
32
|
||||
}
|
||||
|
||||
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
let len = bytes.len();
|
||||
let expected = <Self as Decode>::ssz_fixed_len();
|
||||
|
||||
if len != expected {
|
||||
Err(DecodeError::InvalidByteLength { len, expected })
|
||||
} else {
|
||||
Ok(U256::from_little_endian(bytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for U128 {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ssz_fixed_len() -> usize {
|
||||
16
|
||||
}
|
||||
|
||||
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||
let len = bytes.len();
|
||||
let expected = <Self as Decode>::ssz_fixed_len();
|
||||
|
||||
if len != expected {
|
||||
Err(DecodeError::InvalidByteLength { len, expected })
|
||||
} else {
|
||||
Ok(U128::from_little_endian(bytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_decodable_for_u8_array {
|
||||
($len: expr) => {
|
||||
impl Decode for [u8; $len] {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::*;
|
||||
use ethereum_types::H256;
|
||||
use ethereum_types::{H256, U128, U256};
|
||||
|
||||
macro_rules! impl_encodable_for_uint {
|
||||
($type: ident, $bit_size: expr) => {
|
||||
@ -77,6 +77,42 @@ impl Encode for H256 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for U256 {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ssz_fixed_len() -> usize {
|
||||
32
|
||||
}
|
||||
|
||||
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
||||
let n = <Self as Encode>::ssz_fixed_len();
|
||||
let s = buf.len();
|
||||
|
||||
buf.resize(s + n, 0);
|
||||
self.to_little_endian(&mut buf[s..]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for U128 {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ssz_fixed_len() -> usize {
|
||||
16
|
||||
}
|
||||
|
||||
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
||||
let n = <Self as Encode>::ssz_fixed_len();
|
||||
let s = buf.len();
|
||||
|
||||
buf.resize(s + n, 0);
|
||||
self.to_little_endian(&mut buf[s..]);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_encodable_for_u8_array {
|
||||
($len: expr) => {
|
||||
impl Encode for [u8; $len] {
|
||||
|
23
tests/ef_tests/Cargo.toml
Normal file
23
tests/ef_tests/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "ef_tests"
|
||||
version = "0.1.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
fake_crypto = ["bls/fake_crypto"]
|
||||
|
||||
[dependencies]
|
||||
bls = { path = "../../eth2/utils/bls" }
|
||||
ethereum-types = "0.5"
|
||||
hex = "0.3"
|
||||
rayon = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_yaml = "0.8"
|
||||
ssz = { path = "../../eth2/utils/ssz" }
|
||||
tree_hash = { path = "../../eth2/utils/tree_hash" }
|
||||
cached_tree_hash = { path = "../../eth2/utils/cached_tree_hash" }
|
||||
types = { path = "../../eth2/types" }
|
||||
walkdir = "2"
|
||||
yaml-rust = { git = "https://github.com/sigp/yaml-rust", branch = "escape_all_str"}
|
1
tests/ef_tests/eth2.0-spec-tests
Submodule
1
tests/ef_tests/eth2.0-spec-tests
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 161a36ee6232d8d251d798c8262638ed0c34c9c6
|
52
tests/ef_tests/src/case_result.rs
Normal file
52
tests/ef_tests/src/case_result.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use super::*;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct CaseResult {
|
||||
pub case_index: usize,
|
||||
pub desc: String,
|
||||
pub result: Result<(), Error>,
|
||||
}
|
||||
|
||||
impl CaseResult {
|
||||
pub fn new<T: Debug>(case_index: usize, case: &T, result: Result<(), Error>) -> Self {
|
||||
CaseResult {
|
||||
case_index,
|
||||
desc: format!("{:?}", case),
|
||||
result,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compares `result` with `expected`.
|
||||
///
|
||||
/// If `expected.is_none()` then `result` is expected to be `Err`. Otherwise, `T` in `result` and
|
||||
/// `expected` must be equal.
|
||||
pub fn compare_result<T, E>(result: &Result<T, E>, expected: &Option<T>) -> Result<(), Error>
|
||||
where
|
||||
T: PartialEq<T> + Debug,
|
||||
E: Debug,
|
||||
{
|
||||
match (result, expected) {
|
||||
// Pass: The should have failed and did fail.
|
||||
(Err(_), None) => Ok(()),
|
||||
// Fail: The test failed when it should have produced a result (fail).
|
||||
(Err(e), Some(expected)) => Err(Error::NotEqual(format!(
|
||||
"Got {:?} Expected {:?}",
|
||||
e, expected
|
||||
))),
|
||||
// Fail: The test produced a result when it should have failed (fail).
|
||||
(Ok(result), None) => Err(Error::DidntFail(format!("Got {:?}", result))),
|
||||
// Potential Pass: The test should have produced a result, and it did.
|
||||
(Ok(result), Some(expected)) => {
|
||||
if result == expected {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotEqual(format!(
|
||||
"Got {:?} expected {:?}",
|
||||
result, expected
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
tests/ef_tests/src/cases.rs
Normal file
59
tests/ef_tests/src/cases.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use super::*;
|
||||
use crate::yaml_decode::*;
|
||||
use yaml_rust::YamlLoader;
|
||||
|
||||
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 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<T> {
|
||||
pub test_cases: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T: YamlDecode> YamlDecode for Cases<T> {
|
||||
/// Decodes a YAML list of test cases
|
||||
fn yaml_decode(yaml: &String) -> Result<Self, Error> {
|
||||
let mut p = 0;
|
||||
let mut elems: Vec<&str> = yaml
|
||||
.match_indices("\n- ")
|
||||
// Skip the `\n` used for matching a new line
|
||||
.map(|(i, _)| i + 1)
|
||||
.map(|i| {
|
||||
let yaml_element = &yaml[p..i];
|
||||
p = i;
|
||||
|
||||
yaml_element
|
||||
})
|
||||
.collect();
|
||||
|
||||
elems.push(&yaml[p..]);
|
||||
|
||||
let test_cases = elems
|
||||
.iter()
|
||||
.map(|s| {
|
||||
// Remove the `- ` prefix.
|
||||
let s = &s[2..];
|
||||
// Remove a single level of indenting.
|
||||
s.replace("\n ", "\n")
|
||||
})
|
||||
.map(|s| T::yaml_decode(&s.to_string()).unwrap())
|
||||
.collect();
|
||||
|
||||
Ok(Self { test_cases })
|
||||
}
|
||||
}
|
51
tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs
Normal file
51
tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use super::*;
|
||||
use crate::case_result::compare_result;
|
||||
use bls::{AggregatePublicKey, PublicKey};
|
||||
use serde_derive::Deserialize;
|
||||
use types::EthSpec;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct BlsAggregatePubkeys {
|
||||
pub input: Vec<String>,
|
||||
pub output: String,
|
||||
}
|
||||
|
||||
impl YamlDecode for BlsAggregatePubkeys {
|
||||
fn yaml_decode(yaml: &String) -> Result<Self, Error> {
|
||||
Ok(serde_yaml::from_str(&yaml.as_str()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl EfTest for Cases<BlsAggregatePubkeys> {
|
||||
fn test_results<E: EthSpec>(&self) -> Vec<CaseResult> {
|
||||
self.test_cases
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, tc)| {
|
||||
let result = bls_add_pubkeys(&tc.input, &tc.output);
|
||||
|
||||
CaseResult::new(i, tc, result)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a `aggregate_pubkeys` test case.
|
||||
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)))?;
|
||||
|
||||
aggregate_pubkey.add(&key);
|
||||
}
|
||||
|
||||
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<u8>, Vec<u8>>(&aggregate_pubkey, &output_bytes)
|
||||
}
|
51
tests/ef_tests/src/cases/bls_aggregate_sigs.rs
Normal file
51
tests/ef_tests/src/cases/bls_aggregate_sigs.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use super::*;
|
||||
use crate::case_result::compare_result;
|
||||
use bls::{AggregateSignature, Signature};
|
||||
use serde_derive::Deserialize;
|
||||
use types::EthSpec;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct BlsAggregateSigs {
|
||||
pub input: Vec<String>,
|
||||
pub output: String,
|
||||
}
|
||||
|
||||
impl YamlDecode for BlsAggregateSigs {
|
||||
fn yaml_decode(yaml: &String) -> Result<Self, Error> {
|
||||
Ok(serde_yaml::from_str(&yaml.as_str()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl EfTest for Cases<BlsAggregateSigs> {
|
||||
fn test_results<E: EthSpec>(&self) -> Vec<CaseResult> {
|
||||
self.test_cases
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, tc)| {
|
||||
let result = bls_add_signatures(&tc.input, &tc.output);
|
||||
|
||||
CaseResult::new(i, tc, result)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a `aggregate_sigs` test case.
|
||||
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)))?;
|
||||
|
||||
aggregate_signature.add(&sig);
|
||||
}
|
||||
|
||||
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<u8>, Vec<u8>>(&aggregate_signature, &output_bytes)
|
||||
}
|
71
tests/ef_tests/src/cases/bls_g2_compressed.rs
Normal file
71
tests/ef_tests/src/cases/bls_g2_compressed.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use super::*;
|
||||
use crate::case_result::compare_result;
|
||||
use bls::{compress_g2, hash_on_g2};
|
||||
use serde_derive::Deserialize;
|
||||
use types::EthSpec;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct BlsG2CompressedInput {
|
||||
pub message: String,
|
||||
pub domain: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct BlsG2Compressed {
|
||||
pub input: BlsG2CompressedInput,
|
||||
pub output: Vec<String>,
|
||||
}
|
||||
|
||||
impl YamlDecode for BlsG2Compressed {
|
||||
fn yaml_decode(yaml: &String) -> Result<Self, Error> {
|
||||
Ok(serde_yaml::from_str(&yaml.as_str()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl EfTest for Cases<BlsG2Compressed> {
|
||||
fn test_results<E: EthSpec>(&self) -> Vec<CaseResult> {
|
||||
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<String>) -> 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 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);
|
||||
|
||||
compare_result::<Vec<u8>, Vec<u8>>(&Ok(point), &Some(decoded))
|
||||
}
|
||||
|
||||
// Converts a vector to u64 (from big endian)
|
||||
fn bytes_to_u64(array: &Vec<u8>) -> 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
|
||||
}
|
85
tests/ef_tests/src/cases/bls_g2_uncompressed.rs
Normal file
85
tests/ef_tests/src/cases/bls_g2_uncompressed.rs
Normal file
@ -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<Vec<String>>,
|
||||
}
|
||||
|
||||
impl YamlDecode for BlsG2Uncompressed {
|
||||
fn yaml_decode(yaml: &String) -> Result<Self, Error> {
|
||||
Ok(serde_yaml::from_str(&yaml.as_str()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl EfTest for Cases<BlsG2Uncompressed> {
|
||||
fn test_results<E: EthSpec>(&self) -> Vec<CaseResult> {
|
||||
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<Vec<String>>,
|
||||
) -> 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<u8> = 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<u8>, Vec<u8>>(&Ok(point_bytes.to_vec()), &Some(decoded))
|
||||
}
|
||||
|
||||
// Converts a vector to u64 (from big endian)
|
||||
fn bytes_to_u64(array: &Vec<u8>) -> 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
|
||||
}
|
53
tests/ef_tests/src/cases/bls_priv_to_pub.rs
Normal file
53
tests/ef_tests/src/cases/bls_priv_to_pub.rs
Normal file
@ -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<Self, Error> {
|
||||
Ok(serde_yaml::from_str(&yaml.as_str()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl EfTest for Cases<BlsPrivToPub> {
|
||||
fn test_results<E: EthSpec>(&self) -> Vec<CaseResult> {
|
||||
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<u8>, Vec<u8>>(&Ok(pk.as_raw().as_bytes()), &Some(decoded))
|
||||
}
|
||||
|
||||
// Increase the size of an array to 48 bytes
|
||||
fn pad_to_48(array: &mut Vec<u8>) {
|
||||
while array.len() < 48 {
|
||||
array.insert(0, 0);
|
||||
}
|
||||
}
|
88
tests/ef_tests/src/cases/bls_sign_msg.rs
Normal file
88
tests/ef_tests/src/cases/bls_sign_msg.rs
Normal file
@ -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<Self, Error> {
|
||||
Ok(serde_yaml::from_str(&yaml.as_str()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl EfTest for Cases<BlsSign> {
|
||||
fn test_results<E: EthSpec>(&self) -> Vec<CaseResult> {
|
||||
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<u8>, Vec<u8>>(&Ok(signature.as_bytes()), &Some(decoded))
|
||||
}
|
||||
|
||||
// Converts a vector to u64 (from big endian)
|
||||
fn bytes_to_u64(array: &Vec<u8>) -> 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<u8>) {
|
||||
while array.len() < 48 {
|
||||
array.insert(0, 0);
|
||||
}
|
||||
}
|
81
tests/ef_tests/src/cases/ssz_generic.rs
Normal file
81
tests/ef_tests/src/cases/ssz_generic.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use super::*;
|
||||
use crate::case_result::compare_result;
|
||||
use ethereum_types::{U128, U256};
|
||||
use serde_derive::Deserialize;
|
||||
use ssz::Decode;
|
||||
use std::fmt::Debug;
|
||||
use types::EthSpec;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct SszGeneric {
|
||||
#[serde(alias = "type")]
|
||||
pub type_name: String,
|
||||
pub valid: bool,
|
||||
pub value: Option<String>,
|
||||
pub ssz: Option<String>,
|
||||
}
|
||||
|
||||
impl YamlDecode for SszGeneric {
|
||||
fn yaml_decode(yaml: &String) -> Result<Self, Error> {
|
||||
Ok(serde_yaml::from_str(&yaml.as_str()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl EfTest for Cases<SszGeneric> {
|
||||
fn test_results<E: EthSpec>(&self) -> Vec<CaseResult> {
|
||||
self.test_cases
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, tc)| {
|
||||
let result = if let Some(ssz) = &tc.ssz {
|
||||
match tc.type_name.as_ref() {
|
||||
"uint8" => ssz_generic_test::<u8>(tc.valid, ssz, &tc.value),
|
||||
"uint16" => ssz_generic_test::<u16>(tc.valid, ssz, &tc.value),
|
||||
"uint32" => ssz_generic_test::<u32>(tc.valid, ssz, &tc.value),
|
||||
"uint64" => ssz_generic_test::<u64>(tc.valid, ssz, &tc.value),
|
||||
"uint128" => ssz_generic_test::<U128>(tc.valid, ssz, &tc.value),
|
||||
"uint256" => ssz_generic_test::<U256>(tc.valid, ssz, &tc.value),
|
||||
_ => Err(Error::FailedToParseTest(format!(
|
||||
"Unknown type: {}",
|
||||
tc.type_name
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
// Skip tests that do not have an ssz field.
|
||||
//
|
||||
// See: https://github.com/ethereum/eth2.0-specs/issues/1079
|
||||
Ok(())
|
||||
};
|
||||
|
||||
CaseResult::new(i, tc, result)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a `ssz_generic` test case.
|
||||
fn ssz_generic_test<T>(
|
||||
should_be_ok: bool,
|
||||
ssz: &String,
|
||||
value: &Option<String>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: Decode + YamlDecode + Debug + PartialEq<T>,
|
||||
{
|
||||
let ssz = hex::decode(&ssz[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?;
|
||||
|
||||
// We do not cater for the scenario where the test is valid but we are not passed any SSZ.
|
||||
if should_be_ok && value.is_none() {
|
||||
panic!("Unexpected test input. Cannot pass without value.")
|
||||
}
|
||||
|
||||
let expected = if let Some(string) = value {
|
||||
Some(T::yaml_decode(string)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let decoded = T::from_ssz_bytes(&ssz);
|
||||
|
||||
compare_result(&decoded, &expected)
|
||||
}
|
137
tests/ef_tests/src/cases/ssz_static.rs
Normal file
137
tests/ef_tests/src/cases/ssz_static.rs
Normal file
@ -0,0 +1,137 @@
|
||||
use super::*;
|
||||
use crate::case_result::compare_result;
|
||||
use cached_tree_hash::{CachedTreeHash, TreeHashCache};
|
||||
use rayon::prelude::*;
|
||||
use serde_derive::Deserialize;
|
||||
use ssz::{Decode, Encode, ssz_encode};
|
||||
use std::fmt::Debug;
|
||||
use tree_hash::TreeHash;
|
||||
use types::{
|
||||
test_utils::{SeedableRng, TestRandom, XorShiftRng},
|
||||
Attestation, AttestationData, AttestationDataAndCustodyBit, AttesterSlashing, BeaconBlock,
|
||||
BeaconBlockBody, BeaconBlockHeader, BeaconState, Crosslink, Deposit, DepositData, Eth1Data,
|
||||
EthSpec, Fork, Hash256, HistoricalBatch, IndexedAttestation, PendingAttestation,
|
||||
ProposerSlashing, Transfer, Validator, VoluntaryExit,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct SszStatic {
|
||||
pub type_name: String,
|
||||
pub serialized: String,
|
||||
pub root: String,
|
||||
#[serde(skip)]
|
||||
pub raw_yaml: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Value<T> {
|
||||
value: T,
|
||||
}
|
||||
|
||||
impl YamlDecode for SszStatic {
|
||||
fn yaml_decode(yaml: &String) -> Result<Self, Error> {
|
||||
let mut ssz_static: SszStatic = serde_yaml::from_str(&yaml.as_str()).unwrap();
|
||||
|
||||
ssz_static.raw_yaml = yaml.clone();
|
||||
|
||||
Ok(ssz_static)
|
||||
}
|
||||
}
|
||||
|
||||
impl SszStatic {
|
||||
fn value<T: serde::de::DeserializeOwned>(&self) -> Result<T, Error> {
|
||||
let wrapper: Value<T> = serde_yaml::from_str(&self.raw_yaml.as_str()).map_err(|e| {
|
||||
Error::FailedToParseTest(format!("Unable to parse {} YAML: {:?}", self.type_name, e))
|
||||
})?;
|
||||
|
||||
Ok(wrapper.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl EfTest for Cases<SszStatic> {
|
||||
fn test_results<E: EthSpec>(&self) -> Vec<CaseResult> {
|
||||
self.test_cases
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
.map(|(i, tc)| {
|
||||
let result = match tc.type_name.as_ref() {
|
||||
"Fork" => ssz_static_test::<Fork>(tc),
|
||||
"Crosslink" => ssz_static_test::<Crosslink>(tc),
|
||||
"Eth1Data" => ssz_static_test::<Eth1Data>(tc),
|
||||
"AttestationData" => ssz_static_test::<AttestationData>(tc),
|
||||
"AttestationDataAndCustodyBit" => {
|
||||
ssz_static_test::<AttestationDataAndCustodyBit>(tc)
|
||||
}
|
||||
"IndexedAttestation" => ssz_static_test::<IndexedAttestation>(tc),
|
||||
"DepositData" => ssz_static_test::<DepositData>(tc),
|
||||
"BeaconBlockHeader" => ssz_static_test::<BeaconBlockHeader>(tc),
|
||||
"Validator" => ssz_static_test::<Validator>(tc),
|
||||
"PendingAttestation" => ssz_static_test::<PendingAttestation>(tc),
|
||||
"HistoricalBatch" => ssz_static_test::<HistoricalBatch<E>>(tc),
|
||||
"ProposerSlashing" => ssz_static_test::<ProposerSlashing>(tc),
|
||||
"AttesterSlashing" => ssz_static_test::<AttesterSlashing>(tc),
|
||||
"Attestation" => ssz_static_test::<Attestation>(tc),
|
||||
"Deposit" => ssz_static_test::<Deposit>(tc),
|
||||
"VoluntaryExit" => ssz_static_test::<VoluntaryExit>(tc),
|
||||
"Transfer" => ssz_static_test::<Transfer>(tc),
|
||||
"BeaconBlockBody" => ssz_static_test::<BeaconBlockBody>(tc),
|
||||
"BeaconBlock" => ssz_static_test::<BeaconBlock>(tc),
|
||||
"BeaconState" => ssz_static_test::<BeaconState<E>>(tc),
|
||||
_ => Err(Error::FailedToParseTest(format!(
|
||||
"Unknown type: {}",
|
||||
tc.type_name
|
||||
))),
|
||||
};
|
||||
|
||||
CaseResult::new(i, tc, result)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn ssz_static_test<T>(tc: &SszStatic) -> Result<(), Error>
|
||||
where
|
||||
T: Clone
|
||||
+ Decode
|
||||
+ Debug
|
||||
+ Encode
|
||||
+ PartialEq<T>
|
||||
+ serde::de::DeserializeOwned
|
||||
+ TreeHash
|
||||
+ CachedTreeHash
|
||||
+ TestRandom,
|
||||
{
|
||||
// Verify we can decode SSZ in the same way we can decode YAML.
|
||||
let ssz = hex::decode(&tc.serialized[2..])
|
||||
.map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?;
|
||||
let expected = tc.value::<T>()?;
|
||||
let decode_result = T::from_ssz_bytes(&ssz);
|
||||
compare_result(&decode_result, &Some(expected))?;
|
||||
|
||||
// Verify we can encode the result back into original ssz bytes
|
||||
let decoded = decode_result.unwrap();
|
||||
let encoded_result = decoded.as_ssz_bytes();
|
||||
compare_result::<Vec<u8>, Error>(&Ok(encoded_result), &Some(ssz));
|
||||
|
||||
// Verify the TreeHash root of the decoded struct matches the test.
|
||||
let expected_root =
|
||||
&hex::decode(&tc.root[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?;
|
||||
let expected_root = Hash256::from_slice(&expected_root);
|
||||
let tree_hash_root = Hash256::from_slice(&decoded.tree_hash_root());
|
||||
compare_result::<Hash256, Error>(&Ok(tree_hash_root), &Some(expected_root))?;
|
||||
|
||||
// Verify a _new_ CachedTreeHash root of the decoded struct matches the test.
|
||||
let cache = TreeHashCache::new(&decoded).unwrap();
|
||||
let cached_tree_hash_root = Hash256::from_slice(cache.tree_hash_root().unwrap());
|
||||
compare_result::<Hash256, Error>(&Ok(cached_tree_hash_root), &Some(expected_root))?;
|
||||
|
||||
// Verify the root after an update from a random CachedTreeHash to the decoded struct.
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
let random_instance = T::random_for_test(&mut rng);
|
||||
let mut cache = TreeHashCache::new(&random_instance).unwrap();
|
||||
cache.update(&decoded).unwrap();
|
||||
let updated_root = Hash256::from_slice(cache.tree_hash_root().unwrap());
|
||||
compare_result::<Hash256, Error>(&Ok(updated_root), &Some(expected_root))?;
|
||||
|
||||
Ok(())
|
||||
}
|
117
tests/ef_tests/src/doc.rs
Normal file
117
tests/ef_tests/src/doc.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use crate::case_result::CaseResult;
|
||||
use crate::cases::*;
|
||||
use crate::doc_header::DocHeader;
|
||||
use crate::eth_specs::{MainnetEthSpec, MinimalEthSpec};
|
||||
use crate::yaml_decode::{extract_yaml_by_key, yaml_split_header_and_cases, YamlDecode};
|
||||
use crate::EfTest;
|
||||
use serde_derive::Deserialize;
|
||||
use std::{fs::File, io::prelude::*, path::PathBuf};
|
||||
use types::EthSpec;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Doc {
|
||||
pub header_yaml: String,
|
||||
pub cases_yaml: String,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
impl Doc {
|
||||
fn from_path(path: PathBuf) -> Self {
|
||||
let mut file = File::open(path.clone()).unwrap();
|
||||
|
||||
let mut yaml = String::new();
|
||||
file.read_to_string(&mut yaml).unwrap();
|
||||
|
||||
let (header_yaml, cases_yaml) = yaml_split_header_and_cases(yaml.clone());
|
||||
|
||||
Self {
|
||||
header_yaml,
|
||||
cases_yaml,
|
||||
path,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_results(&self) -> Vec<CaseResult> {
|
||||
let header: DocHeader = serde_yaml::from_str(&self.header_yaml.as_str()).unwrap();
|
||||
|
||||
match (
|
||||
header.runner.as_ref(),
|
||||
header.handler.as_ref(),
|
||||
header.config.as_ref(),
|
||||
) {
|
||||
("ssz", "uint", _) => run_test::<SszGeneric, MainnetEthSpec>(self),
|
||||
("ssz", "static", "minimal") => run_test::<SszStatic, MinimalEthSpec>(self),
|
||||
("ssz", "static", "mainnet") => run_test::<SszStatic, MainnetEthSpec>(self),
|
||||
("bls", "aggregate_pubkeys", "mainnet") => {
|
||||
run_test::<BlsAggregatePubkeys, MainnetEthSpec>(self)
|
||||
}
|
||||
("bls", "aggregate_sigs", "mainnet") => {
|
||||
run_test::<BlsAggregateSigs, MainnetEthSpec>(self)
|
||||
}
|
||||
("bls", "msg_hash_compressed", "mainnet") => {
|
||||
run_test::<BlsG2Compressed, MainnetEthSpec>(self)
|
||||
}
|
||||
// Note this test fails due to a difference in our internal representations. It does
|
||||
// not effect verification or external representation.
|
||||
//
|
||||
// It is skipped.
|
||||
("bls", "msg_hash_uncompressed", "mainnet") => vec![],
|
||||
("bls", "priv_to_pub", "mainnet") => run_test::<BlsPrivToPub, MainnetEthSpec>(self),
|
||||
("bls", "sign_msg", "mainnet") => run_test::<BlsSign, MainnetEthSpec>(self),
|
||||
(runner, handler, config) => panic!(
|
||||
"No implementation for runner: \"{}\", handler: \"{}\", config: \"{}\"",
|
||||
runner, handler, config
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_tests_pass(path: PathBuf) {
|
||||
let doc = Self::from_path(path);
|
||||
let results = doc.test_results();
|
||||
|
||||
if results.iter().any(|r| r.result.is_err()) {
|
||||
print_failures(&doc, &results);
|
||||
panic!("Tests failed (see above)");
|
||||
} else {
|
||||
println!("Passed {} tests in {:?}", results.len(), doc.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_test<T, E: EthSpec>(doc: &Doc) -> Vec<CaseResult>
|
||||
where
|
||||
Cases<T>: EfTest + YamlDecode,
|
||||
{
|
||||
// Extract only the "test_cases" YAML as a stand-alone string.
|
||||
//let test_cases_yaml = extract_yaml_by_key(self., "test_cases");
|
||||
|
||||
// Pass only the "test_cases" YAML string to `yaml_decode`.
|
||||
let test_cases: Cases<T> = Cases::yaml_decode(&doc.cases_yaml).unwrap();
|
||||
|
||||
test_cases.test_results::<E>()
|
||||
}
|
||||
|
||||
pub fn print_failures(doc: &Doc, results: &[CaseResult]) {
|
||||
let header: DocHeader = serde_yaml::from_str(&doc.header_yaml).unwrap();
|
||||
let failures: Vec<&CaseResult> = results.iter().filter(|r| r.result.is_err()).collect();
|
||||
|
||||
println!("--------------------------------------------------");
|
||||
println!("Test Failure");
|
||||
println!("Title: {}", header.title);
|
||||
println!("File: {:?}", doc.path);
|
||||
println!("");
|
||||
println!(
|
||||
"{} tests, {} failures, {} passes.",
|
||||
results.len(),
|
||||
failures.len(),
|
||||
results.len() - failures.len()
|
||||
);
|
||||
println!("");
|
||||
|
||||
for failure in failures {
|
||||
println!("-------");
|
||||
println!("case[{}].result:", failure.case_index);
|
||||
println!("{:#?}", failure.result);
|
||||
}
|
||||
println!("");
|
||||
}
|
12
tests/ef_tests/src/doc_header.rs
Normal file
12
tests/ef_tests/src/doc_header.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct DocHeader {
|
||||
pub title: String,
|
||||
pub summary: String,
|
||||
pub forks_timeline: String,
|
||||
pub forks: Vec<String>,
|
||||
pub config: String,
|
||||
pub runner: String,
|
||||
pub handler: String,
|
||||
}
|
9
tests/ef_tests/src/error.rs
Normal file
9
tests/ef_tests/src/error.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Error {
|
||||
/// The value in the test didn't match our value.
|
||||
NotEqual(String),
|
||||
/// The test specified a failure and we did not experience one.
|
||||
DidntFail(String),
|
||||
/// Failed to parse the test (internal error).
|
||||
FailedToParseTest(String),
|
||||
}
|
28
tests/ef_tests/src/eth_specs.rs
Normal file
28
tests/ef_tests/src/eth_specs.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use types::{
|
||||
typenum::{U64, U8},
|
||||
ChainSpec, EthSpec, FewValidatorsEthSpec, FoundationEthSpec,
|
||||
};
|
||||
|
||||
/// "Minimal" testing specification, as defined here:
|
||||
///
|
||||
/// https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/configs/constant_presets/minimal.yaml
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct MinimalEthSpec;
|
||||
|
||||
impl EthSpec for MinimalEthSpec {
|
||||
type ShardCount = U8;
|
||||
type SlotsPerHistoricalRoot = U64;
|
||||
type LatestRandaoMixesLength = U64;
|
||||
type LatestActiveIndexRootsLength = U64;
|
||||
type LatestSlashedExitLength = U64;
|
||||
|
||||
fn spec() -> ChainSpec {
|
||||
// TODO: this spec is likely incorrect!
|
||||
FewValidatorsEthSpec::spec()
|
||||
}
|
||||
}
|
||||
|
||||
pub type MainnetEthSpec = FoundationEthSpec;
|
21
tests/ef_tests/src/lib.rs
Normal file
21
tests/ef_tests/src/lib.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use types::EthSpec;
|
||||
|
||||
pub use case_result::CaseResult;
|
||||
pub use doc::Doc;
|
||||
pub use error::Error;
|
||||
pub use yaml_decode::YamlDecode;
|
||||
|
||||
mod case_result;
|
||||
mod cases;
|
||||
mod doc;
|
||||
mod doc_header;
|
||||
mod error;
|
||||
mod eth_specs;
|
||||
mod yaml_decode;
|
||||
|
||||
/// Defined where an object can return the results of some test(s) adhering to the Ethereum
|
||||
/// Foundation testing format.
|
||||
pub trait EfTest {
|
||||
/// Returns the results of executing one or more tests.
|
||||
fn test_results<E: EthSpec>(&self) -> Vec<CaseResult>;
|
||||
}
|
59
tests/ef_tests/src/yaml_decode.rs
Normal file
59
tests/ef_tests/src/yaml_decode.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use super::*;
|
||||
use ethereum_types::{U128, U256};
|
||||
use types::Fork;
|
||||
|
||||
mod utils;
|
||||
|
||||
pub use utils::*;
|
||||
|
||||
pub trait YamlDecode: Sized {
|
||||
/// Decode an object from the test specification YAML.
|
||||
fn yaml_decode(string: &String) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
/// Basic types can general be decoded with the `parse` fn if they implement `str::FromStr`.
|
||||
macro_rules! impl_via_parse {
|
||||
($ty: ty) => {
|
||||
impl YamlDecode for $ty {
|
||||
fn yaml_decode(string: &String) -> Result<Self, Error> {
|
||||
string
|
||||
.parse::<Self>()
|
||||
.map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_via_parse!(u8);
|
||||
impl_via_parse!(u16);
|
||||
impl_via_parse!(u32);
|
||||
impl_via_parse!(u64);
|
||||
|
||||
/// Some `ethereum-types` methods have a `str::FromStr` implementation that expects `0x`-prefixed:
|
||||
/// hex, so we use `from_dec_str` instead.
|
||||
macro_rules! impl_via_from_dec_str {
|
||||
($ty: ty) => {
|
||||
impl YamlDecode for $ty {
|
||||
fn yaml_decode(string: &String) -> Result<Self, Error> {
|
||||
Self::from_dec_str(string).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_via_from_dec_str!(U128);
|
||||
impl_via_from_dec_str!(U256);
|
||||
|
||||
/// Types that already implement `serde::Deserialize` can be decoded using `serde_yaml`.
|
||||
macro_rules! impl_via_serde_yaml {
|
||||
($ty: ty) => {
|
||||
impl YamlDecode for $ty {
|
||||
fn yaml_decode(string: &String) -> Result<Self, Error> {
|
||||
serde_yaml::from_str(string)
|
||||
.map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_via_serde_yaml!(Fork);
|
35
tests/ef_tests/src/yaml_decode/utils.rs
Normal file
35
tests/ef_tests/src/yaml_decode/utils.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use yaml_rust::{Yaml, YamlEmitter, YamlLoader};
|
||||
|
||||
pub fn extract_yaml_by_key(yaml: &str, key: &str) -> String {
|
||||
let doc = &YamlLoader::load_from_str(yaml).unwrap()[0];
|
||||
let subsection = &doc[key];
|
||||
|
||||
yaml_to_string(subsection)
|
||||
}
|
||||
|
||||
pub fn extract_yaml_by_index(yaml: &str, index: usize) -> String {
|
||||
let doc = &YamlLoader::load_from_str(yaml).unwrap()[0];
|
||||
let subsection = &doc[index];
|
||||
|
||||
yaml_to_string(subsection)
|
||||
}
|
||||
|
||||
pub fn yaml_to_string(yaml: &Yaml) -> String {
|
||||
let mut out_str = String::new();
|
||||
let mut emitter = YamlEmitter::new(&mut out_str);
|
||||
emitter.escape_all_strings(true);
|
||||
emitter.dump(yaml).unwrap();
|
||||
|
||||
out_str
|
||||
}
|
||||
|
||||
pub fn yaml_split_header_and_cases(mut yaml: String) -> (String, String) {
|
||||
let test_cases_start = yaml.find("\ntest_cases:\n").unwrap();
|
||||
// + 1 to skip the \n we used for matching.
|
||||
let mut test_cases = yaml.split_off(test_cases_start + 1);
|
||||
|
||||
let end_of_first_line = test_cases.find("\n").unwrap();
|
||||
let test_cases = test_cases.split_off(end_of_first_line + 1);
|
||||
|
||||
(yaml, test_cases)
|
||||
}
|
62
tests/ef_tests/tests/tests.rs
Normal file
62
tests/ef_tests/tests/tests.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use ef_tests::*;
|
||||
use rayon::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
fn yaml_files_in_test_dir(dir: &str) -> Vec<PathBuf> {
|
||||
let mut base_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
base_path.push("eth2.0-spec-tests");
|
||||
base_path.push("tests");
|
||||
base_path.push(dir);
|
||||
|
||||
assert!(
|
||||
base_path.exists(),
|
||||
"Unable to locate test files. Did you init git submoules?"
|
||||
);
|
||||
|
||||
WalkDir::new(base_path)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter_map(|entry| {
|
||||
if entry.file_type().is_file() {
|
||||
match entry.file_name().to_str() {
|
||||
Some(f) if f.ends_with(".yaml") => Some(entry.path().to_path_buf()),
|
||||
Some(f) if f.ends_with(".yml") => Some(entry.path().to_path_buf()),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
fn ssz_generic() {
|
||||
yaml_files_in_test_dir("ssz_generic")
|
||||
.into_par_iter()
|
||||
.for_each(|file| {
|
||||
Doc::assert_tests_pass(file);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
fn ssz_static() {
|
||||
yaml_files_in_test_dir("ssz_static")
|
||||
.into_par_iter()
|
||||
.for_each(|file| {
|
||||
Doc::assert_tests_pass(file);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
fn bls() {
|
||||
yaml_files_in_test_dir("bls")
|
||||
.into_par_iter()
|
||||
.for_each(|file| {
|
||||
Doc::assert_tests_pass(file);
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user