From 2d9fc34d432698353bfd8599496bef73236984c8 Mon Sep 17 00:00:00 2001 From: ethDreamer <37123614+ethDreamer@users.noreply.github.com> Date: Wed, 4 Jun 2025 01:34:12 -0500 Subject: [PATCH] Fulu EF tests v1.6.0-alpha.0 (#7540) Update to EF tests v1.6.0-alpha.0 --- Cargo.lock | 2 + consensus/types/presets/minimal/electra.yaml | 8 +- consensus/types/src/data_column_sidecar.rs | 59 ++++++--- consensus/types/src/eth_spec.rs | 6 +- consensus/types/src/runtime_var_list.rs | 57 ++++++++ testing/ef_tests/Cargo.toml | 3 + testing/ef_tests/Makefile | 2 +- testing/ef_tests/README.md | 10 ++ testing/ef_tests/check_all_files_accessed.py | 12 +- testing/ef_tests/src/cases.rs | 20 +-- testing/ef_tests/src/cases/common.rs | 83 +++++++++--- testing/ef_tests/src/cases/fork.rs | 6 +- .../ef_tests/src/cases/kzg_compute_cells.rs | 54 ++++++++ testing/ef_tests/src/cases/operations.rs | 22 +++- testing/ef_tests/src/cases/ssz_generic.rs | 38 ++++-- testing/ef_tests/src/cases/ssz_static.rs | 86 +++++++++--- testing/ef_tests/src/decode.rs | 22 ++++ testing/ef_tests/src/handler.rs | 124 ++++++++++-------- testing/ef_tests/src/lib.rs | 10 +- testing/ef_tests/src/type_name.rs | 5 + testing/ef_tests/tests/tests.rs | 32 +++-- 21 files changed, 494 insertions(+), 167 deletions(-) create mode 100644 testing/ef_tests/src/cases/kzg_compute_cells.rs diff --git a/Cargo.lock b/Cargo.lock index 48a39cf304..70c910aadc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2576,6 +2576,8 @@ dependencies = [ "bls", "compare_fields", "compare_fields_derive", + "context_deserialize", + "context_deserialize_derive", "derivative", "eth2_network_config", "ethereum_ssz", diff --git a/consensus/types/presets/minimal/electra.yaml b/consensus/types/presets/minimal/electra.yaml index f99effe0f1..22e26e4025 100644 --- a/consensus/types/presets/minimal/electra.yaml +++ b/consensus/types/presets/minimal/electra.yaml @@ -32,10 +32,10 @@ MAX_ATTESTATIONS_ELECTRA: 8 # Execution # --------------------------------------------------------------- -# [customized] 2**2 (= 4) deposit requests -MAX_DEPOSIT_REQUESTS_PER_PAYLOAD: 4 -# [customized] 2**1 (= 2) withdrawal requests -MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 2 +# 2**13 (= 8,192) deposit requests +MAX_DEPOSIT_REQUESTS_PER_PAYLOAD: 8192 +# 2**4 (= 16) withdrawal requests +MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 16 # 2**1 (= 2) consolidation requests MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 2 diff --git a/consensus/types/src/data_column_sidecar.rs b/consensus/types/src/data_column_sidecar.rs index 5ec2b28b2b..612ddb6eb8 100644 --- a/consensus/types/src/data_column_sidecar.rs +++ b/consensus/types/src/data_column_sidecar.rs @@ -6,12 +6,14 @@ use crate::{ SignedBeaconBlockHeader, Slot, }; use bls::Signature; +use context_deserialize::ContextDeserialize; use derivative::Derivative; use kzg::Error as KzgError; use kzg::{KzgCommitment, KzgProof}; use merkle_proof::verify_merkle_proof; use safe_arith::ArithError; -use serde::{Deserialize, Serialize}; +use serde::de::Error; +use serde::{Deserialize, Deserializer, Serialize}; use ssz::{DecodeError, Encode}; use ssz_derive::{Decode, Encode}; use ssz_types::Error as SszError; @@ -26,12 +28,49 @@ pub type Cell = FixedVector::BytesPerCell>; pub type DataColumn = VariableList, ::MaxBlobCommitmentsPerBlock>; /// Identifies a set of data columns associated with a specific beacon block. -#[derive(Encode, Clone, Debug, PartialEq)] +#[derive(Encode, Clone, Debug, PartialEq, TreeHash)] pub struct DataColumnsByRootIdentifier { pub block_root: Hash256, pub columns: RuntimeVariableList, } +impl<'de> ContextDeserialize<'de, (ForkName, usize)> for DataColumnsByRootIdentifier { + fn context_deserialize(deserializer: D, context: (ForkName, usize)) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + block_root: Hash256, + columns: serde_json::Value, + } + + let helper = Helper::deserialize(deserializer)?; + Ok(Self { + block_root: helper.block_root, + columns: RuntimeVariableList::context_deserialize(helper.columns, context) + .map_err(Error::custom)?, + }) + } +} + +impl DataColumnsByRootIdentifier { + pub fn from_ssz_bytes(bytes: &[u8], num_columns: usize) -> Result { + let mut builder = ssz::SszDecoderBuilder::new(bytes); + builder.register_type::()?; + builder.register_anonymous_variable_length_item()?; + + let mut decoder = builder.build()?; + let block_root = decoder.decode_next()?; + let columns = decoder + .decode_next_with(|bytes| RuntimeVariableList::from_ssz_bytes(bytes, num_columns))?; + Ok(DataColumnsByRootIdentifier { + block_root, + columns, + }) + } +} + impl RuntimeVariableList { pub fn from_ssz_bytes_with_nested( bytes: &[u8], @@ -47,21 +86,7 @@ impl RuntimeVariableList { Some(max_len), )? .into_iter() - .map(|bytes| { - let mut builder = ssz::SszDecoderBuilder::new(&bytes); - builder.register_type::()?; - builder.register_anonymous_variable_length_item()?; - - let mut decoder = builder.build()?; - let block_root = decoder.decode_next()?; - let columns = decoder.decode_next_with(|bytes| { - RuntimeVariableList::from_ssz_bytes(bytes, num_columns) - })?; - Ok(DataColumnsByRootIdentifier { - block_root, - columns, - }) - }) + .map(|bytes| DataColumnsByRootIdentifier::from_ssz_bytes(&bytes, num_columns)) .collect::, _>>()?; Ok(RuntimeVariableList::from_vec(vec, max_len)) diff --git a/consensus/types/src/eth_spec.rs b/consensus/types/src/eth_spec.rs index 6f1b3e6ce6..1cde9c2e48 100644 --- a/consensus/types/src/eth_spec.rs +++ b/consensus/types/src/eth_spec.rs @@ -476,8 +476,6 @@ impl EthSpec for MinimalEthSpec { type KzgCommitmentInclusionProofDepth = U10; type PendingPartialWithdrawalsLimit = U64; type PendingConsolidationsLimit = U64; - type MaxDepositRequestsPerPayload = U4; - type MaxWithdrawalRequestsPerPayload = U2; type FieldElementsPerCell = U64; type FieldElementsPerExtBlob = U8192; type MaxCellsPerBlock = U33554432; @@ -509,7 +507,9 @@ impl EthSpec for MinimalEthSpec { MaxPendingDepositsPerEpoch, MaxConsolidationRequestsPerPayload, MaxAttesterSlashingsElectra, - MaxAttestationsElectra + MaxAttestationsElectra, + MaxDepositRequestsPerPayload, + MaxWithdrawalRequestsPerPayload }); fn default_spec() -> ChainSpec { diff --git a/consensus/types/src/runtime_var_list.rs b/consensus/types/src/runtime_var_list.rs index 454c8b9e18..2a8899e203 100644 --- a/consensus/types/src/runtime_var_list.rs +++ b/consensus/types/src/runtime_var_list.rs @@ -6,6 +6,7 @@ use ssz::Decode; use ssz_types::Error; use std::ops::{Deref, Index, IndexMut}; use std::slice::SliceIndex; +use tree_hash::{Hash256, MerkleHasher, PackedEncoding, TreeHash, TreeHashType}; /// Emulates a SSZ `List`. /// @@ -241,6 +242,62 @@ where } } +impl TreeHash for RuntimeVariableList { + fn tree_hash_type() -> tree_hash::TreeHashType { + tree_hash::TreeHashType::List + } + + fn tree_hash_packed_encoding(&self) -> PackedEncoding { + unreachable!("List should never be packed.") + } + + fn tree_hash_packing_factor() -> usize { + unreachable!("List should never be packed.") + } + + fn tree_hash_root(&self) -> Hash256 { + let root = runtime_vec_tree_hash_root::(&self.vec, self.max_len); + + tree_hash::mix_in_length(&root, self.len()) + } +} + +// We can delete this once the upstream `vec_tree_hash_root` is modified to use a runtime max len. +pub fn runtime_vec_tree_hash_root(vec: &[T], max_len: usize) -> Hash256 +where + T: TreeHash, +{ + match T::tree_hash_type() { + TreeHashType::Basic => { + let mut hasher = + MerkleHasher::with_leaves(max_len.div_ceil(T::tree_hash_packing_factor())); + + for item in vec { + hasher + .write(&item.tree_hash_packed_encoding()) + .expect("ssz_types variable vec should not contain more elements than max"); + } + + hasher + .finish() + .expect("ssz_types variable vec should not have a remaining buffer") + } + TreeHashType::Container | TreeHashType::List | TreeHashType::Vector => { + let mut hasher = MerkleHasher::with_leaves(max_len); + + for item in vec { + hasher + .write(item.tree_hash_root().as_slice()) + .expect("ssz_types vec should not contain more elements than max"); + } + + hasher + .finish() + .expect("ssz_types vec should not have a remaining buffer") + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/testing/ef_tests/Cargo.toml b/testing/ef_tests/Cargo.toml index d93f3a5578..f6d6837f71 100644 --- a/testing/ef_tests/Cargo.toml +++ b/testing/ef_tests/Cargo.toml @@ -7,6 +7,7 @@ edition = { workspace = true } [features] # `ef_tests` feature must be enabled to actually run the tests ef_tests = [] +disable_rayon = [] fake_crypto = ["bls/fake_crypto"] portable = ["beacon_chain/portable"] @@ -16,6 +17,8 @@ beacon_chain = { workspace = true } bls = { workspace = true } compare_fields = { workspace = true } compare_fields_derive = { workspace = true } +context_deserialize = { workspace = true } +context_deserialize_derive = { workspace = true } derivative = { workspace = true } eth2_network_config = { workspace = true } ethereum_ssz = { workspace = true } diff --git a/testing/ef_tests/Makefile b/testing/ef_tests/Makefile index c3a56ec11a..3f72863e3a 100644 --- a/testing/ef_tests/Makefile +++ b/testing/ef_tests/Makefile @@ -1,4 +1,4 @@ -TESTS_TAG := v1.5.0-beta.4 +TESTS_TAG := v1.6.0-alpha.0 TESTS = general minimal mainnet TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS)) diff --git a/testing/ef_tests/README.md b/testing/ef_tests/README.md index 5ffd453d99..b04cd25dc7 100644 --- a/testing/ef_tests/README.md +++ b/testing/ef_tests/README.md @@ -28,6 +28,16 @@ $ cargo test --features ef_tests The tests won't run without the `ef_tests` feature enabled (this is to ensure that a top-level `cargo test --all` won't fail on missing files). +The following is sometimes necessary to avoid stack overflow issues when running on MacOS: +``` +$ export RUST_MIN_STACK=8388608 +``` + +When debugging failing tests, it's often useful to disable parallization and output suppression: +``` +$ cargo test --features ef_tests,disable_rayon -- --nocapture +``` + ## Saving Space When you download the tests, the downloaded archives will be kept in addition to the extracted diff --git a/testing/ef_tests/check_all_files_accessed.py b/testing/ef_tests/check_all_files_accessed.py index 3aeff8ce06..d7568d854f 100755 --- a/testing/ef_tests/check_all_files_accessed.py +++ b/testing/ef_tests/check_all_files_accessed.py @@ -45,13 +45,13 @@ excluded_paths = [ "bls12-381-tests/deserialization_G1", "bls12-381-tests/deserialization_G2", "bls12-381-tests/hash_to_G2", - "tests/.*/eip6110", - "tests/.*/whisk", - # TODO(das): Fulu tests are ignored for now - "tests/.*/fulu", - "tests/.*/fulu/ssz_static/MatrixEntry", - "tests/.*/eip7441", "tests/.*/eip7732", + "tests/.*/eip7805", + # Ignore MatrixEntry SSZ tests for now. + "tests/.*/fulu/ssz_static/MatrixEntry/.*", + # Ignore full epoch tests for now (just test the sub-transitions). + "tests/.*/.*/epoch_processing/.*/pre_epoch.ssz_snappy", + "tests/.*/.*/epoch_processing/.*/post_epoch.ssz_snappy", ] diff --git a/testing/ef_tests/src/cases.rs b/testing/ef_tests/src/cases.rs index 31662e831a..b6f7cb21a1 100644 --- a/testing/ef_tests/src/cases.rs +++ b/testing/ef_tests/src/cases.rs @@ -22,6 +22,7 @@ mod genesis_validity; mod get_custody_groups; mod kzg_blob_to_kzg_commitment; mod kzg_compute_blob_kzg_proof; +mod kzg_compute_cells; mod kzg_compute_cells_and_kzg_proofs; mod kzg_compute_kzg_proof; mod kzg_recover_cells_and_kzg_proofs; @@ -49,7 +50,7 @@ pub use bls_eth_fast_aggregate_verify::*; pub use bls_fast_aggregate_verify::*; pub use bls_sign_msg::*; pub use bls_verify_msg::*; -pub use common::SszStaticType; +pub use common::{DataColumnsByRootIdentifierWrapper, SszStaticType}; pub use compute_columns_for_custody_groups::*; pub use epoch_processing::*; pub use fork::ForkTest; @@ -58,6 +59,7 @@ pub use genesis_validity::*; pub use get_custody_groups::*; pub use kzg_blob_to_kzg_commitment::*; pub use kzg_compute_blob_kzg_proof::*; +pub use kzg_compute_cells::*; pub use kzg_compute_cells_and_kzg_proofs::*; pub use kzg_compute_kzg_proof::*; pub use kzg_recover_cells_and_kzg_proofs::*; @@ -91,29 +93,29 @@ pub use transition::TransitionTest; /// to return `true` for the feature in order for the feature test vector to be tested. #[derive(Debug, PartialEq, Clone, Copy)] pub enum FeatureName { - // TODO(fulu): to be removed once we start using Fulu types for test vectors. - // Existing SSZ types for PeerDAS (Fulu) are the same as Electra, so the test vectors get - // loaded as Electra types (default serde behaviour for untagged enums). - Fulu, + // Placeholder for future feature-gated forks + // Add new feature-gated forks here before they are incorporated into a main fork + #[doc(hidden)] + __Placeholder, } impl FeatureName { pub fn list_all() -> Vec { - vec![FeatureName::Fulu] + vec![] } /// `ForkName` to use when running the feature tests. pub fn fork_name(&self) -> ForkName { match self { - FeatureName::Fulu => ForkName::Electra, + FeatureName::__Placeholder => unreachable!("Placeholder variant should never be used"), } } } impl Display for FeatureName { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { match self { - FeatureName::Fulu => f.write_str("fulu"), + FeatureName::__Placeholder => unreachable!("Placeholder variant should never be used"), } } } diff --git a/testing/ef_tests/src/cases/common.rs b/testing/ef_tests/src/cases/common.rs index 62f834820f..f63380cc33 100644 --- a/testing/ef_tests/src/cases/common.rs +++ b/testing/ef_tests/src/cases/common.rs @@ -1,8 +1,11 @@ -use serde::Deserialize; +use context_deserialize::ContextDeserialize; +use serde::{Deserialize, Deserializer}; use ssz::Encode; use ssz_derive::{Decode, Encode}; use std::fmt::Debug; -use types::ForkName; +use std::marker::PhantomData; +use tree_hash::TreeHash; +use types::{DataColumnsByRootIdentifier, EthSpec, ForkName, Hash256}; /// Macro to wrap U128 and U256 so they deserialize correctly. macro_rules! uint_wrapper { @@ -40,6 +43,15 @@ macro_rules! uint_wrapper { self.x.tree_hash_root() } } + + impl<'de, T> ContextDeserialize<'de, T> for $wrapper_name { + fn context_deserialize(deserializer: D, _context: T) -> Result + where + D: Deserializer<'de>, + { + <$wrapper_name>::deserialize(deserializer) + } + } }; } @@ -47,26 +59,63 @@ uint_wrapper!(DecimalU128, alloy_primitives::U128); uint_wrapper!(DecimalU256, alloy_primitives::U256); /// Trait for types that can be used in SSZ static tests. -pub trait SszStaticType: - serde::de::DeserializeOwned + Encode + Clone + PartialEq + Debug + Sync -{ +pub trait SszStaticType: Encode + Clone + PartialEq + Debug + Sync {} + +impl SszStaticType for T where T: Encode + Clone + PartialEq + Debug + Sync {} + +/// We need the `EthSpec` to implement `LoadCase` for this type, in order to work out the +/// ChainSpec. +/// +/// No other type currently requires this kind of context. +#[derive(Debug, Encode, Clone, PartialEq)] +#[ssz(struct_behaviour = "transparent")] +pub struct DataColumnsByRootIdentifierWrapper { + pub value: DataColumnsByRootIdentifier, + // SSZ derive is a bit buggy and requires skip_deserializing for transparent to work. + #[ssz(skip_serializing, skip_deserializing)] + pub _phantom: PhantomData, } -impl SszStaticType for T where - T: serde::de::DeserializeOwned + Encode + Clone + PartialEq + Debug + Sync +impl<'de, E: EthSpec> ContextDeserialize<'de, (ForkName, usize)> + for DataColumnsByRootIdentifierWrapper { + fn context_deserialize(deserializer: D, context: (ForkName, usize)) -> Result + where + D: Deserializer<'de>, + { + let value = DataColumnsByRootIdentifier::context_deserialize(deserializer, context)?; + Ok(DataColumnsByRootIdentifierWrapper { + value, + _phantom: PhantomData, + }) + } } -/// Return the fork immediately prior to a fork. -pub fn previous_fork(fork_name: ForkName) -> ForkName { - match fork_name { - ForkName::Base => ForkName::Base, - ForkName::Altair => ForkName::Base, - ForkName::Bellatrix => ForkName::Altair, - ForkName::Capella => ForkName::Bellatrix, - ForkName::Deneb => ForkName::Capella, - ForkName::Electra => ForkName::Deneb, - ForkName::Fulu => ForkName::Electra, +// We can delete this if we ever get `tree_hash(struct_behaviour = "transparent")`. +impl TreeHash for DataColumnsByRootIdentifierWrapper { + fn tree_hash_type() -> tree_hash::TreeHashType { + DataColumnsByRootIdentifier::tree_hash_type() + } + + fn tree_hash_packed_encoding(&self) -> tree_hash::PackedEncoding { + self.value.tree_hash_packed_encoding() + } + + fn tree_hash_packing_factor() -> usize { + DataColumnsByRootIdentifier::tree_hash_packing_factor() + } + + fn tree_hash_root(&self) -> Hash256 { + self.value.tree_hash_root() + } +} + +impl From for DataColumnsByRootIdentifierWrapper { + fn from(value: DataColumnsByRootIdentifier) -> Self { + Self { + value, + _phantom: PhantomData, + } } } diff --git a/testing/ef_tests/src/cases/fork.rs b/testing/ef_tests/src/cases/fork.rs index 85301e22f6..cae4fcf405 100644 --- a/testing/ef_tests/src/cases/fork.rs +++ b/testing/ef_tests/src/cases/fork.rs @@ -1,6 +1,5 @@ use super::*; use crate::case_result::compare_beacon_state_results_without_caches; -use crate::cases::common::previous_fork; use crate::decode::{ssz_decode_state, yaml_decode_file}; use serde::Deserialize; use state_processing::upgrade::{ @@ -33,7 +32,10 @@ impl LoadCase for ForkTest { assert_eq!(metadata.fork_name(), fork_name); // Decode pre-state with previous fork. - let pre_spec = &previous_fork(fork_name).make_genesis_spec(E::default_spec()); + let pre_spec = &fork_name + .previous_fork() + .unwrap_or(ForkName::Base) + .make_genesis_spec(E::default_spec()); let pre = ssz_decode_state(&path.join("pre.ssz_snappy"), pre_spec)?; // Decode post-state with target fork. diff --git a/testing/ef_tests/src/cases/kzg_compute_cells.rs b/testing/ef_tests/src/cases/kzg_compute_cells.rs new file mode 100644 index 0000000000..bd7f3649d6 --- /dev/null +++ b/testing/ef_tests/src/cases/kzg_compute_cells.rs @@ -0,0 +1,54 @@ +use super::*; +use crate::case_result::compare_result; +use kzg::Cell; +use serde::Deserialize; +use std::marker::PhantomData; + +#[derive(Debug, Clone, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct KZGComputeCellsInput { + pub blob: String, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(bound = "E: EthSpec", deny_unknown_fields)] +pub struct KZGComputeCells { + pub input: KZGComputeCellsInput, + pub output: Option>, + #[serde(skip)] + _phantom: PhantomData, +} + +impl LoadCase for KZGComputeCells { + fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result { + decode::yaml_decode_file(path.join("data.yaml").as_path()) + } +} + +impl Case for KZGComputeCells { + fn is_enabled_for_fork(fork_name: ForkName) -> bool { + fork_name.fulu_enabled() + } + + fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> { + let cells = parse_blob::(&self.input.blob) + .and_then(|blob| { + let blob = blob.as_ref().try_into().map_err(|e| { + Error::InternalError(format!("Failed to convert blob to kzg blob: {e:?}")) + })?; + let kzg = get_kzg(); + kzg.compute_cells(blob).map_err(|e| { + Error::InternalError(format!("Failed to compute cells and kzg proofs: {e:?}")) + }) + }) + .map(|cells| cells.to_vec()); + + let expected = self.output.as_ref().map(|cells| { + parse_cells_and_proofs(cells, &[]) + .map(|(cells, _)| cells) + .expect("Valid cells") + }); + + compare_result::, _>(&cells, &expected) + } +} diff --git a/testing/ef_tests/src/cases/operations.rs b/testing/ef_tests/src/cases/operations.rs index 7178edb151..0c7c3d087c 100644 --- a/testing/ef_tests/src/cases/operations.rs +++ b/testing/ef_tests/src/cases/operations.rs @@ -22,10 +22,11 @@ use state_processing::{ ConsensusContext, }; use std::fmt::Debug; +use std::path::PathBuf; use types::{ Attestation, AttesterSlashing, BeaconBlock, BeaconBlockBody, BeaconBlockBodyBellatrix, - BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconState, - BlindedPayload, ConsolidationRequest, Deposit, DepositRequest, ExecutionPayload, + BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyFulu, + BeaconState, BlindedPayload, ConsolidationRequest, Deposit, DepositRequest, ExecutionPayload, ForkVersionDecode, FullPayload, ProposerSlashing, SignedBlsToExecutionChange, SignedVoluntaryExit, SyncAggregate, WithdrawalRequest, }; @@ -49,6 +50,7 @@ pub struct WithdrawalsPayload { #[derive(Debug, Clone)] pub struct Operations> { + path: PathBuf, metadata: Metadata, execution_metadata: Option, pub pre: BeaconState, @@ -357,8 +359,8 @@ impl Operation for BeaconBlockBody> { BeaconBlockBody::Electra(inner.clone_as_blinded()) } ForkName::Fulu => { - let inner = >>::from_ssz_bytes(bytes)?; - BeaconBlockBody::Electra(inner.clone_as_blinded()) + let inner = >>::from_ssz_bytes(bytes)?; + BeaconBlockBody::Fulu(inner.clone_as_blinded()) } _ => panic!(), }) @@ -555,6 +557,7 @@ impl> LoadCase for Operations { }; Ok(Self { + path: path.into(), metadata, execution_metadata, pre, @@ -574,6 +577,17 @@ impl> Case for Operations { } fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> { + // FIXME(das): remove this once v1.6.0-alpha.1 is released + // We are ahead of the v1.6.0-alpha.0 spec in our implementation of + // `get_max_blobs_per_block`, so we fail the execution payload test which expects the + // empty blob schedule to generate an error. + if O::handler_name() == "execution_payload" + && fork_name == ForkName::Fulu + && self.path.ends_with("invalid_exceed_max_blobs_per_block") + { + return Err(Error::SkippedKnownFailure); + } + let spec = &testing_spec::(fork_name); let mut pre_state = self.pre.clone(); diff --git a/testing/ef_tests/src/cases/ssz_generic.rs b/testing/ef_tests/src/cases/ssz_generic.rs index 3dc2f17968..96627472ba 100644 --- a/testing/ef_tests/src/cases/ssz_generic.rs +++ b/testing/ef_tests/src/cases/ssz_generic.rs @@ -3,7 +3,9 @@ use super::*; use crate::cases::common::{DecimalU128, DecimalU256, SszStaticType}; use crate::cases::ssz_static::{check_serialization, check_tree_hash}; -use crate::decode::{log_file_access, snappy_decode_file, yaml_decode_file}; +use crate::decode::{context_yaml_decode_file, log_file_access, snappy_decode_file}; +use context_deserialize::ContextDeserialize; +use context_deserialize_derive::context_deserialize; use serde::{de::Error as SerdeError, Deserialize, Deserializer}; use ssz_derive::{Decode, Encode}; use tree_hash::TreeHash; @@ -12,6 +14,7 @@ use types::typenum::*; use types::{BitList, BitVector, FixedVector, ForkName, VariableList, Vector}; #[derive(Debug, Clone, Deserialize)] +#[context_deserialize(ForkName)] struct Metadata { root: String, #[serde(rename(deserialize = "signing_root"))] @@ -118,7 +121,7 @@ macro_rules! type_dispatch { } impl Case for SszGeneric { - fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> { + fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> { let parts = self.case_name.split('_').collect::>(); match self.handler_name.as_str() { @@ -134,7 +137,7 @@ impl Case for SszGeneric { type_dispatch!( ssz_generic_test, - (&self.path), + (&self.path, fork_name), Vector, <>, [elem_ty => primitive_type] @@ -142,7 +145,7 @@ impl Case for SszGeneric { )?; type_dispatch!( ssz_generic_test, - (&self.path), + (&self.path, fork_name), FixedVector, <>, [elem_ty => primitive_type] @@ -159,7 +162,7 @@ impl Case for SszGeneric { type_dispatch!( ssz_generic_test, - (&self.path), + (&self.path, fork_name), BitList, <>, [limit => typenum] @@ -170,21 +173,21 @@ impl Case for SszGeneric { type_dispatch!( ssz_generic_test, - (&self.path), + (&self.path, fork_name), BitVector, <>, [length => typenum] )?; } "boolean" => { - ssz_generic_test::(&self.path)?; + ssz_generic_test::(&self.path, fork_name)?; } "uints" => { let type_name = "uint".to_owned() + parts[1]; type_dispatch!( ssz_generic_test, - (&self.path), + (&self.path, fork_name), _, <>, [type_name.as_str() => primitive_type] @@ -195,7 +198,7 @@ impl Case for SszGeneric { type_dispatch!( ssz_generic_test, - (&self.path), + (&self.path, fork_name), _, <>, [type_name => test_container] @@ -207,10 +210,15 @@ impl Case for SszGeneric { } } -fn ssz_generic_test(path: &Path) -> Result<(), Error> { +fn ssz_generic_test< + T: SszStaticType + for<'de> ContextDeserialize<'de, ForkName> + TreeHash + ssz::Decode, +>( + path: &Path, + fork_name: ForkName, +) -> Result<(), Error> { let meta_path = path.join("meta.yaml"); let meta: Option = if meta_path.is_file() { - Some(yaml_decode_file(&meta_path)?) + Some(context_yaml_decode_file(&meta_path, fork_name)?) } else { None }; @@ -220,7 +228,7 @@ fn ssz_generic_test(path: &Path) -> R let value_path = path.join("value.yaml"); let value: Option = if value_path.is_file() { - Some(yaml_decode_file(&value_path)?) + Some(context_yaml_decode_file(&value_path, fork_name)?) } else { None }; @@ -246,17 +254,20 @@ fn ssz_generic_test(path: &Path) -> R // Containers for SSZ generic tests #[derive(Debug, Clone, Default, PartialEq, Decode, Encode, TreeHash, Deserialize)] +#[context_deserialize(ForkName)] struct SingleFieldTestStruct { A: u8, } #[derive(Debug, Clone, Default, PartialEq, Decode, Encode, TreeHash, Deserialize)] +#[context_deserialize(ForkName)] struct SmallTestStruct { A: u16, B: u16, } #[derive(Debug, Clone, Default, PartialEq, Decode, Encode, TreeHash, Deserialize)] +#[context_deserialize(ForkName)] struct FixedTestStruct { A: u8, B: u64, @@ -264,6 +275,7 @@ struct FixedTestStruct { } #[derive(Debug, Clone, Default, PartialEq, Decode, Encode, TreeHash, Deserialize)] +#[context_deserialize(ForkName)] struct VarTestStruct { A: u16, B: VariableList, @@ -271,6 +283,7 @@ struct VarTestStruct { } #[derive(Debug, Clone, Default, PartialEq, Decode, Encode, TreeHash, Deserialize)] +#[context_deserialize(ForkName)] struct ComplexTestStruct { A: u16, B: VariableList, @@ -283,6 +296,7 @@ struct ComplexTestStruct { } #[derive(Debug, Clone, PartialEq, Decode, Encode, TreeHash, Deserialize)] +#[context_deserialize(ForkName)] struct BitsStruct { A: BitList, B: BitVector, diff --git a/testing/ef_tests/src/cases/ssz_static.rs b/testing/ef_tests/src/cases/ssz_static.rs index c80977a8ac..b02b9597bb 100644 --- a/testing/ef_tests/src/cases/ssz_static.rs +++ b/testing/ef_tests/src/cases/ssz_static.rs @@ -1,10 +1,12 @@ use super::*; use crate::case_result::compare_result; -use crate::decode::{snappy_decode_file, yaml_decode_file}; +use crate::cases::common::DataColumnsByRootIdentifierWrapper; +use crate::decode::{context_yaml_decode_file, snappy_decode_file, yaml_decode_file}; +use context_deserialize::ContextDeserialize; use serde::Deserialize; use ssz::Decode; use tree_hash::TreeHash; -use types::{BeaconBlock, BeaconState, Hash256, SignedBeaconBlock}; +use types::{BeaconBlock, BeaconState, DataColumnsByRootIdentifier, Hash256, SignedBeaconBlock}; #[derive(Debug, Clone, Deserialize)] struct SszStaticRoots { @@ -37,18 +39,28 @@ pub struct SszStaticWithSpec { value: T, } -fn load_from_dir(path: &Path) -> Result<(SszStaticRoots, Vec, T), Error> { +fn load_from_dir ContextDeserialize<'de, ForkName>>( + path: &Path, + fork_name: ForkName, +) -> Result<(SszStaticRoots, Vec, T), Error> { + load_from_dir_with_context(path, fork_name) +} + +fn load_from_dir_with_context ContextDeserialize<'de, C>, C>( + path: &Path, + context: C, +) -> Result<(SszStaticRoots, Vec, T), Error> { let roots = yaml_decode_file(&path.join("roots.yaml"))?; let serialized = snappy_decode_file(&path.join("serialized.ssz_snappy")) .expect("serialized.ssz_snappy exists"); - let value = yaml_decode_file(&path.join("value.yaml"))?; + let value = context_yaml_decode_file(&path.join("value.yaml"), context)?; Ok((roots, serialized, value)) } -impl LoadCase for SszStatic { - fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result { - load_from_dir(path).map(|(roots, serialized, value)| Self { +impl ContextDeserialize<'de, ForkName>> LoadCase for SszStatic { + fn load_from_dir(path: &Path, fork_name: ForkName) -> Result { + load_from_dir(path, fork_name).map(|(roots, serialized, value)| Self { roots, serialized, value, @@ -56,19 +68,9 @@ impl LoadCase for SszStatic { } } -impl LoadCase for SszStaticTHC { - fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result { - load_from_dir(path).map(|(roots, serialized, value)| Self { - roots, - serialized, - value, - }) - } -} - -impl LoadCase for SszStaticWithSpec { - fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result { - load_from_dir(path).map(|(roots, serialized, value)| Self { +impl ContextDeserialize<'de, ForkName>> LoadCase for SszStaticTHC { + fn load_from_dir(path: &Path, fork_name: ForkName) -> Result { + load_from_dir(path, fork_name).map(|(roots, serialized, value)| Self { roots, serialized, value, @@ -124,6 +126,16 @@ impl Case for SszStaticTHC> { } } +impl LoadCase for SszStaticWithSpec> { + fn load_from_dir(path: &Path, fork_name: ForkName) -> Result { + load_from_dir(path, fork_name).map(|(roots, serialized, value)| Self { + roots, + serialized, + value, + }) + } +} + impl Case for SszStaticWithSpec> { fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> { let spec = &testing_spec::(fork_name); @@ -135,6 +147,16 @@ impl Case for SszStaticWithSpec> { } } +impl LoadCase for SszStaticWithSpec> { + fn load_from_dir(path: &Path, fork_name: ForkName) -> Result { + load_from_dir(path, fork_name).map(|(roots, serialized, value)| Self { + roots, + serialized, + value, + }) + } +} + impl Case for SszStaticWithSpec> { fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> { let spec = &testing_spec::(fork_name); @@ -145,3 +167,27 @@ impl Case for SszStaticWithSpec> { Ok(()) } } + +impl LoadCase for SszStaticWithSpec> { + fn load_from_dir(path: &Path, fork_name: ForkName) -> Result { + let spec = &testing_spec::(fork_name); + let context = (fork_name, spec.number_of_columns as usize); + load_from_dir_with_context(path, context).map(|(roots, serialized, value)| Self { + roots, + serialized, + value, + }) + } +} + +impl Case for SszStaticWithSpec> { + fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> { + let spec = &testing_spec::(fork_name); + check_serialization(&self.value, &self.serialized, |bytes| { + DataColumnsByRootIdentifier::from_ssz_bytes(bytes, spec.number_of_columns as usize) + .map(Into::into) + })?; + check_tree_hash(&self.roots.root, self.value.tree_hash_root().as_slice())?; + Ok(()) + } +} diff --git a/testing/ef_tests/src/decode.rs b/testing/ef_tests/src/decode.rs index eb88ac6af1..2074ffce23 100644 --- a/testing/ef_tests/src/decode.rs +++ b/testing/ef_tests/src/decode.rs @@ -1,4 +1,5 @@ use super::*; +use context_deserialize::ContextDeserialize; use fs2::FileExt; use snap::raw::Decoder; use std::fs::{self}; @@ -35,6 +36,27 @@ pub fn yaml_decode(string: &str) -> Result(string: &'de str, context: C) -> Result +where + T: ContextDeserialize<'de, C>, +{ + let deserializer = serde_yaml::Deserializer::from_str(string); + T::context_deserialize(deserializer, context) + .map_err(|e| Error::FailedToParseTest(format!("{:?}", e))) +} + +pub fn context_yaml_decode_file(path: &Path, context: C) -> Result +where + T: for<'de> ContextDeserialize<'de, C>, +{ + log_file_access(path); + fs::read_to_string(path) + .map_err(|e| { + Error::FailedToParseTest(format!("Unable to load {}: {:?}", path.display(), e)) + }) + .and_then(|s| context_yaml_decode(&s, context)) +} + pub fn yaml_decode_file(path: &Path) -> Result { log_file_access(path); fs::read_to_string(path) diff --git a/testing/ef_tests/src/handler.rs b/testing/ef_tests/src/handler.rs index a375498239..fd2bea6e8e 100644 --- a/testing/ef_tests/src/handler.rs +++ b/testing/ef_tests/src/handler.rs @@ -1,6 +1,7 @@ use crate::cases::{self, Case, Cases, EpochTransition, LoadCase, Operation}; use crate::type_name::TypeName; use crate::{type_name, FeatureName}; +use context_deserialize::ContextDeserialize; use derivative::Derivative; use std::fs::{self, DirEntry}; use std::marker::PhantomData; @@ -21,7 +22,7 @@ pub trait Handler { // Add forks here to exclude them from EF spec testing. Helpful for adding future or // unspecified forks. fn disabled_forks(&self) -> Vec { - vec![ForkName::Fulu] + vec![] } fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool { @@ -50,6 +51,19 @@ pub trait Handler { } } + // Do NOT override this function. + // TODO: use default keyword when stable. + fn rayon_enabled() -> bool { + #[cfg(feature = "disable_rayon")] + { + false + } + #[cfg(not(feature = "disable_rayon"))] + { + Self::use_rayon() + } + } + fn use_rayon() -> bool { true } @@ -85,7 +99,7 @@ pub trait Handler { }) .collect(); - let results = Cases { test_cases }.test_results(fork_name, Self::use_rayon()); + let results = Cases { test_cases }.test_results(fork_name, Self::rayon_enabled()); let name = format!( "{}/{}/{}", @@ -127,7 +141,7 @@ pub trait Handler { }) .collect(); - let results = Cases { test_cases }.test_results(fork_name, Self::use_rayon()); + let results = Cases { test_cases }.test_results(fork_name, Self::rayon_enabled()); let name = format!( "{}/{}/{}", @@ -205,7 +219,7 @@ macro_rules! bls_handler { }) .collect(); - let results = Cases { test_cases }.test_results(fork_name, Self::use_rayon()); + let results = Cases { test_cases }.test_results(fork_name, Self::rayon_enabled()); let name = format!( "{}/{}/{}", @@ -327,13 +341,37 @@ impl SszStaticHandler { pub struct SszStaticTHCHandler(PhantomData<(T, E)>); /// Handler for SSZ types that don't implement `ssz::Decode`. -#[derive(Derivative)] -#[derivative(Default(bound = ""))] -pub struct SszStaticWithSpecHandler(PhantomData<(T, E)>); +pub struct SszStaticWithSpecHandler { + supported_forks: Vec, + _phantom: PhantomData<(T, E)>, +} + +impl Default for SszStaticWithSpecHandler { + fn default() -> Self { + Self::for_forks(ForkName::list_all()) + } +} + +impl SszStaticWithSpecHandler { + pub fn for_forks(supported_forks: Vec) -> Self { + SszStaticWithSpecHandler { + supported_forks, + _phantom: PhantomData, + } + } + + pub fn fulu_and_later() -> Self { + Self::for_forks(ForkName::list_all()[6..].to_vec()) + } +} impl Handler for SszStaticHandler where - T: cases::SszStaticType + tree_hash::TreeHash + ssz::Decode + TypeName, + T: cases::SszStaticType + + for<'de> ContextDeserialize<'de, ForkName> + + tree_hash::TreeHash + + ssz::Decode + + TypeName, E: TypeName, { type Case = cases::SszStatic; @@ -353,25 +391,6 @@ where fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool { self.supported_forks.contains(&fork_name) } - - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - // TODO(fulu): to be removed once Fulu types start differing from Electra. We currently run Fulu tests as a - // "feature" - this means we use Electra types for Fulu SSZ tests (except for PeerDAS types, e.g. `DataColumnSidecar`). - // - // This ensures we only run the tests **once** for `Fulu`, using the types matching the - // correct fork, e.g. `Fulu` uses SSZ types from `Electra` as of spec test version - // `v1.5.0-beta.0`, therefore the `Fulu` tests should get included when testing Deneb types. - // - // e.g. Fulu test vectors are executed in the 2nd line below, but excluded in the 1st - // line when testing the type `AttestationElectra`: - // - // ``` - // SszStaticHandler::, MainnetEthSpec>::pre_electra().run(); - // SszStaticHandler::, MainnetEthSpec>::electra_only().run(); - // ``` - feature_name == FeatureName::Fulu - && self.supported_forks.contains(&feature_name.fork_name()) - } } impl Handler for SszStaticTHCHandler, E> @@ -391,10 +410,6 @@ where fn handler_name(&self) -> String { BeaconState::::name().into() } - - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu - } } impl Handler for SszStaticWithSpecHandler @@ -417,8 +432,8 @@ where T::name().into() } - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu + fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool { + self.supported_forks.contains(&fork_name) } } @@ -898,10 +913,6 @@ impl Handler for GetCustodyGroupsHandler { fn handler_name(&self) -> String { "get_custody_groups".into() } - - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu - } } #[derive(Derivative)] @@ -922,9 +933,25 @@ impl Handler for ComputeColumnsForCustodyGroupHandler fn handler_name(&self) -> String { "compute_columns_for_custody_group".into() } +} - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu +#[derive(Derivative)] +#[derivative(Default(bound = ""))] +pub struct KZGComputeCellsHandler(PhantomData); + +impl Handler for KZGComputeCellsHandler { + type Case = cases::KZGComputeCells; + + fn config_name() -> &'static str { + "general" + } + + fn runner_name() -> &'static str { + "kzg" + } + + fn handler_name(&self) -> String { + "compute_cells".into() } } @@ -946,10 +973,6 @@ impl Handler for KZGComputeCellsAndKZGProofHandler { fn handler_name(&self) -> String { "compute_cells_and_kzg_proofs".into() } - - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu - } } #[derive(Derivative)] @@ -970,10 +993,6 @@ impl Handler for KZGVerifyCellKZGProofBatchHandler { fn handler_name(&self) -> String { "verify_cell_kzg_proof_batch".into() } - - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu - } } #[derive(Derivative)] @@ -994,10 +1013,6 @@ impl Handler for KZGRecoverCellsAndKZGProofHandler { fn handler_name(&self) -> String { "recover_cells_and_kzg_proofs".into() } - - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu - } } #[derive(Derivative)] @@ -1022,10 +1037,6 @@ impl Handler for KzgInclusionMerkleProofValidityHandler bool { fork_name.deneb_enabled() } - - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu - } } #[derive(Derivative)] @@ -1073,7 +1084,8 @@ impl Handler for LightClientUpdateHandler { fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool { // Enabled in Altair - fork_name.altair_enabled() + // No test in Fulu yet. + fork_name.altair_enabled() && fork_name != ForkName::Fulu } } diff --git a/testing/ef_tests/src/lib.rs b/testing/ef_tests/src/lib.rs index e7367719d7..de255c2c73 100644 --- a/testing/ef_tests/src/lib.rs +++ b/testing/ef_tests/src/lib.rs @@ -1,11 +1,11 @@ pub use case_result::CaseResult; pub use cases::WithdrawalsPayload; pub use cases::{ - Case, EffectiveBalanceUpdates, Eth1DataReset, FeatureName, HistoricalRootsUpdate, - HistoricalSummariesUpdate, InactivityUpdates, JustificationAndFinalization, - ParticipationFlagUpdates, ParticipationRecordUpdates, PendingBalanceDeposits, - PendingConsolidations, RandaoMixesReset, RegistryUpdates, RewardsAndPenalties, Slashings, - SlashingsReset, SyncCommitteeUpdates, + Case, DataColumnsByRootIdentifierWrapper, EffectiveBalanceUpdates, Eth1DataReset, FeatureName, + HistoricalRootsUpdate, HistoricalSummariesUpdate, InactivityUpdates, + JustificationAndFinalization, ParticipationFlagUpdates, ParticipationRecordUpdates, + PendingBalanceDeposits, PendingConsolidations, RandaoMixesReset, RegistryUpdates, + RewardsAndPenalties, Slashings, SlashingsReset, SyncCommitteeUpdates, }; pub use decode::log_file_access; pub use error::Error; diff --git a/testing/ef_tests/src/type_name.rs b/testing/ef_tests/src/type_name.rs index 387e77310d..b5b2c424d8 100644 --- a/testing/ef_tests/src/type_name.rs +++ b/testing/ef_tests/src/type_name.rs @@ -1,4 +1,5 @@ //! Mapping from types to canonical string identifiers used in testing. +use crate::DataColumnsByRootIdentifierWrapper; use types::historical_summary::HistoricalSummary; use types::*; @@ -59,6 +60,10 @@ type_name!(BeaconBlockHeader); type_name_generic!(BeaconState); type_name!(BlobIdentifier); type_name!(DataColumnsByRootIdentifier); +type_name_generic!( + DataColumnsByRootIdentifierWrapper, + "DataColumnsByRootIdentifier" +); type_name_generic!(BlobSidecar); type_name_generic!(DataColumnSidecar); type_name!(Checkpoint); diff --git a/testing/ef_tests/tests/tests.rs b/testing/ef_tests/tests/tests.rs index d333cdbb11..8842ec2852 100644 --- a/testing/ef_tests/tests/tests.rs +++ b/testing/ef_tests/tests/tests.rs @@ -238,7 +238,8 @@ macro_rules! ssz_static_test_no_run { #[cfg(feature = "fake_crypto")] mod ssz_static { use ef_tests::{ - FeatureName, Handler, SszStaticHandler, SszStaticTHCHandler, SszStaticWithSpecHandler, + DataColumnsByRootIdentifierWrapper, Handler, SszStaticHandler, SszStaticTHCHandler, + SszStaticWithSpecHandler, }; use types::historical_summary::HistoricalSummary; use types::{ @@ -660,20 +661,24 @@ mod ssz_static { #[test] fn data_column_sidecar() { - SszStaticHandler::, MinimalEthSpec>::default() - .run_for_feature(FeatureName::Fulu); - SszStaticHandler::, MainnetEthSpec>::default() - .run_for_feature(FeatureName::Fulu); + SszStaticHandler::, MinimalEthSpec>::fulu_and_later() + .run(); + SszStaticHandler::, MainnetEthSpec>::fulu_and_later() + .run(); } #[test] - #[ignore] - // TODO(das): enable once EF tests are updated to latest release. fn data_column_by_root_identifier() { - // SszStaticHandler::::default() - // .run_for_feature(FeatureName::Fulu); - // SszStaticHandler::::default() - // .run_for_feature(FeatureName::Fulu); + SszStaticWithSpecHandler::< + DataColumnsByRootIdentifierWrapper, + MinimalEthSpec, + >::fulu_and_later() + .run(); + SszStaticWithSpecHandler::< + DataColumnsByRootIdentifierWrapper, + MainnetEthSpec, + >::fulu_and_later() + .run(); } #[test] @@ -941,6 +946,11 @@ fn kzg_verify_kzg_proof() { KZGVerifyKZGProofHandler::::default().run(); } +#[test] +fn kzg_compute_cells() { + KZGComputeCellsHandler::::default().run(); +} + #[test] fn kzg_compute_cells_and_proofs() { KZGComputeCellsAndKZGProofHandler::::default().run();