From 72684b7b7ebfdd6140306797812dc1c7939203a3 Mon Sep 17 00:00:00 2001 From: Eric Tu <6364934+ec2@users.noreply.github.com> Date: Fri, 22 Sep 2023 18:42:55 +0200 Subject: [PATCH] Ec2/spec tests (#9) * Script to download spec tests * rip test-utils from zipline * Update toolchain and alsoimport zipline * can read tests * TODO: Remove the logic w.r.t. execution because altair doesnt have it * Remove eth1 execution checks * Some tests work! * prints * Capella works * cleanup * Point to remote zipline * spec ttest full prover * k=22 for evm * Convert zipline LightClientHeader to ours * fix tests * clippy * fix import * allow pull from sygmaprotocol/zipline private repo in gh actions: * fixes --- .github/workflows/tests.yml | 4 + .gitignore | 5 + Cargo.toml | 8 +- justfile | 21 + lightclient-circuits/Cargo.toml | 6 +- lightclient-circuits/config/sync_step.json | 31 +- .../src/committee_update_circuit.rs | 4 +- .../src/gadget/crypto/hash2curve.rs | 12 +- lightclient-circuits/src/lib.rs | 2 +- lightclient-circuits/src/sync_step_circuit.rs | 31 +- lightclient-circuits/src/util/circuit.rs | 4 +- lightclient-circuits/src/util/common.rs | 2 +- lightclient-circuits/src/witness/sync.rs | 4 +- lightclient-circuits/tests/step.rs | 446 ++++++++++++++++++ rust-toolchain | 2 +- scripts/download_consensus_specs.sh | 13 + 16 files changed, 549 insertions(+), 46 deletions(-) create mode 100644 lightclient-circuits/tests/step.rs create mode 100755 scripts/download_consensus_specs.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 146cc585..69deb496 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,6 +19,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: + - name: Give GitHub Actions access to sygmaprotocol/zipline + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ secrets.SYGMA_REPO_PULL_KEY }} - uses: actions/checkout@v3 - name: Install latest nightly-2022-10-28 uses: dtolnay/rust-toolchain@stable diff --git a/.gitignore b/.gitignore index 73579fc6..10092398 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,11 @@ Cargo.lock # MSVC Windows builds of rustc generate these, which store debugging information *.pdb +# ignore downloaded spec tests +consensus-spec-tests +general.tar.gz +minimal.tar.gz + # Added by cargo diff --git a/Cargo.toml b/Cargo.toml index e5bd2dcd..e61fc3c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,14 +24,16 @@ incremental = true [patch."https://github.com/privacy-scaling-explorations/halo2curves"] halo2curves = { git = "https://github.com/timoftime/halo2curves", branch = "dev/bls12_381" } # halo2curves = { path = "../halo2curves" } +# [patch."https://github.com/timoftime/halo2curves"] +# halo2curves = { path = "../halo2curves" } [patch."https://github.com/axiom-crypto/halo2-lib"] -halo2-base = { git = "https://github.com/timoftime/halo2-lib", rev = "03c7baedeae208b21359e542b8683644b3eb1ac2", default-features = false, features = [ +halo2-base = { git = "https://github.com/timoftime/halo2-lib", rev = "1a2f896a5cd9ed4039a497697b83eafabec62519", default-features = false, features = [ "halo2-pse", "display", ] } -halo2-ecc = { git = "https://github.com/timoftime/halo2-lib", rev = "03c7baedeae208b21359e542b8683644b3eb1ac2", default-features = false } -poseidon = { git = "https://github.com/timoftime/halo2-lib", rev = "03c7baedeae208b21359e542b8683644b3eb1ac2", default-features = false } +halo2-ecc = { git = "https://github.com/timoftime/halo2-lib", rev = "1a2f896a5cd9ed4039a497697b83eafabec62519", default-features = false } +poseidon = { git = "https://github.com/timoftime/halo2-lib", rev = "1a2f896a5cd9ed4039a497697b83eafabec62519", default-features = false } # halo2-base = { path = "../halo2-lib/halo2-base", default-features = false, features = [ # "halo2-pse", diff --git a/justfile b/justfile index 3a12633d..e26b6fc2 100644 --- a/justfile +++ b/justfile @@ -1,15 +1,36 @@ test: cargo test --workspace + fmt: cargo fmt --all + check: cargo check --all + lint: fmt cargo clippy --all-targets --all-features --workspace + setup-circuits: cargo run -r -- sync-step -o artifacts -k 22 cargo run -r -- committee-update -o artifacts -k 18 + gen-step-evm-verifier: cargo run -r -- sync-step -c ./lightclient-circuits/config/sync_step.json -o evm-verifier ./contracts/snark-verifiers/sync_step.yul + gen-rotation-evm-verifier: cargo run -r -- aggregation -c ./lightclient-circuits/config/aggregation.json --app-pk-path ./build/committee_update.pkey --app-config-path ./lightclient-circuits/config/committee_update.json -i ./rotation-snark -o evm-verifier ./contracts/snark-verifiers/committee_update_compressed.yul + +# downloads spec tests and copies them to the right locations. +download-spec-tests: clean-spec-tests + #!/usr/bin/env bash + if [[ ! -d 'consensus-spec-tests' ]]; then + echo "Downloading test data." + scripts/download_consensus_specs.sh + fi + +# deletes all the downloaded spec tests +clean-spec-tests: + echo "Cleaning up downloaded tests" + rm -rf *.profraw + rm -rf *.tar.gz.1 + rm -rf consensus-spec-tests \ No newline at end of file diff --git a/lightclient-circuits/Cargo.toml b/lightclient-circuits/Cargo.toml index c8513fee..46db4e93 100644 --- a/lightclient-circuits/Cargo.toml +++ b/lightclient-circuits/Cargo.toml @@ -60,7 +60,11 @@ rayon = "1.7.0" ark-std = { version = "0.4.0", features = ["print-trace"] } poseidon_native = { git = "https://github.com/axiom-crypto/halo2.git", branch = "axiom/dev", package = "poseidon" } -# [dev-dependencies] +[dev-dependencies] +rstest = "0.18.2" +test-utils = { git = "ssh://git@github.com/sygmaprotocol/Zipline.git", rev = "27e8a01" } +ethereum-consensus-types = { git = "ssh://git@github.com/sygmaprotocol/Zipline.git", rev = "27e8a01" } +light-client-verifier = { git = "ssh://git@github.com/sygmaprotocol/Zipline.git", rev = "27e8a01" } [features] default = [] diff --git a/lightclient-circuits/config/sync_step.json b/lightclient-circuits/config/sync_step.json index 9c6ef2ba..687e7670 100644 --- a/lightclient-circuits/config/sync_step.json +++ b/lightclient-circuits/config/sync_step.json @@ -1,14 +1,14 @@ { "params": { "strategy": "Vertical", - "k": 21, + "k": 20, "num_advice_per_phase": [ - 11, + 16, 0, 0 ], "num_lookup_advice_per_phase": [ - 2, + 3, 0, 0 ], @@ -16,16 +16,21 @@ }, "break_points": [ [ - 2097141, - 2097141, - 2097141, - 2097140, - 2097142, - 2097140, - 2097141, - 2097141, - 2097140, - 2097141 + 1048566, + 1048566, + 1048565, + 1048566, + 1048566, + 1048565, + 1048564, + 1048566, + 1048566, + 1048565, + 1048566, + 1048566, + 1048566, + 1048565, + 1048564 ], [], [] diff --git a/lightclient-circuits/src/committee_update_circuit.rs b/lightclient-circuits/src/committee_update_circuit.rs index 1a84bbea..6a594763 100644 --- a/lightclient-circuits/src/committee_update_circuit.rs +++ b/lightclient-circuits/src/committee_update_circuit.rs @@ -104,9 +104,9 @@ impl CommitteeUpdateCircuit { vec![vec![poseidon_commitment]] } - fn decode_pubkeys_x<'a, I: IntoIterator>>>( + fn decode_pubkeys_x>>>( ctx: &mut Context, - fp_chip: &FpChip<'a, F>, + fp_chip: &FpChip<'_, F>, compressed_encodings: I, ) -> Vec> { let range = fp_chip.range(); diff --git a/lightclient-circuits/src/gadget/crypto/hash2curve.rs b/lightclient-circuits/src/gadget/crypto/hash2curve.rs index d076f06d..27b61efe 100644 --- a/lightclient-circuits/src/gadget/crypto/hash2curve.rs +++ b/lightclient-circuits/src/gadget/crypto/hash2curve.rs @@ -4,12 +4,18 @@ use std::ops::Deref; use std::{cell::RefCell, iter, marker::PhantomData}; +use super::{ + util::{fp2_sgn0, i2osp, strxor}, + Fp2Point, G1Point, G2Point, HashInstructions, +}; +use super::{AssignedHashResult, ShaContexts, ShaThreadBuilder}; use crate::util::{AsBits, ThreadBuilderBase}; use crate::{ util::{bigint_to_le_bytes, decode_into_field, decode_into_field_be}, witness::HashInput, }; use eth_types::{AppCurveExt, Field, HashCurveExt, Spec}; +use ff::Field as _; use halo2_base::{ safe_types::{GateInstructions, RangeInstructions, SafeBytes32, SafeTypeChip}, utils::ScalarField, @@ -29,12 +35,6 @@ use itertools::Itertools; use num_bigint::{BigInt, BigUint}; use pasta_curves::arithmetic::SqrtRatio; -use super::{ - util::{fp2_sgn0, i2osp, strxor}, - Fp2Point, G1Point, G2Point, HashInstructions, -}; -use super::{AssignedHashResult, ShaContexts, ShaThreadBuilder}; - const G2_EXT_DEGREE: usize = 2; // L = ceil((ceil(log2(p)) + k) / 8) (see section 5 of ietf draft link above) diff --git a/lightclient-circuits/src/lib.rs b/lightclient-circuits/src/lib.rs index 7f28bda7..762d4a88 100644 --- a/lightclient-circuits/src/lib.rs +++ b/lightclient-circuits/src/lib.rs @@ -17,7 +17,7 @@ pub mod aggregation; pub mod committee_update_circuit; pub mod sync_step_circuit; -mod builder; +pub mod builder; mod poseidon; mod ssz_merkle; diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 722e61f0..587c8c58 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -64,7 +64,7 @@ use pasta_curves::group::{ff, GroupEncoding}; use poseidon::PoseidonChip; use sha2::{Digest, Sha256}; use snark_verifier_sdk::{evm::gen_evm_verifier_shplonk, CircuitExt}; -use ssz_rs::{GeneralizedIndex, Merkleized, Node}; +use ssz_rs::{Merkleized, Node}; #[allow(type_alias_bounds)] #[derive(Clone, Debug, Default)] @@ -95,12 +95,6 @@ impl SyncStepCircuit { let bls_chip = bls_signature::BlsSignatureChip::new(&fp_chip, pairing_chip); let h2c_chip = HashToCurveChip::::new(&sha256_chip); - let beacon_state_root = args - .beacon_state_root - .iter() - .map(|&b| thread_pool.main().load_witness(F::from(b as u64))) - .collect_vec(); - let execution_payload_root: HashInputChunk> = args.execution_payload_root.clone().into_witness(); @@ -161,6 +155,14 @@ impl SyncStepCircuit { .collect_vec(); let finalized_slot: HashInputChunk<_> = args.finalized_header.slot.into_witness(); + let attested_header_state_root = args + .attested_header + .state_root + .as_ref() + .iter() + .map(|v| thread_pool.main().load_witness(F::from(*v as u64))) + .collect_vec(); + let finalized_header_root = ssz_merkleize_chunks( thread_pool, &sha256_chip, @@ -168,7 +170,7 @@ impl SyncStepCircuit { finalized_slot.clone(), args.finalized_header.proposer_index.into_witness(), args.finalized_header.parent_root.as_ref().into_witness(), - args.finalized_header.state_root.as_ref().into_witness(), + attested_header_state_root.clone().into(), finalized_block_body_root.clone().into(), ], )?; @@ -200,7 +202,7 @@ impl SyncStepCircuit { bls_chip.verify_pairing(thread_pool.main(), signature, msghash, agg_pubkey, g1_neg); fp12_chip.assert_equal(thread_pool.main(), res, fp12_one); - // verify finilized block header against current beacon state merkle proof + // verify finalized block header against current beacon state merkle proof verify_merkle_proof( thread_pool, &sha256_chip, @@ -208,7 +210,7 @@ impl SyncStepCircuit { .iter() .map(|w| w.clone().into_witness()), finalized_header_root.into(), - &beacon_state_root, + &attested_header_state_root, S::FINALIZED_HEADER_INDEX, )?; @@ -317,7 +319,7 @@ impl SyncStepCircuit { Ok(vec![pi_field]) } - fn instances(args: SyncStepArgs) -> Vec> { + pub fn instance(args: SyncStepArgs) -> bn256::Fr { let mut input: [u8; 64] = [0; 64]; let mut attested_slot = args.attested_header.slot.to_le_bytes().to_vec(); @@ -377,8 +379,7 @@ impl SyncStepCircuit { let mut public_input_commitment = sha2::Sha256::digest(input).to_vec(); // Truncate to 253 bits public_input_commitment[31] &= 0b00011111; - let pi_field = bn256::Fr::from_bytes_le(&public_input_commitment); - vec![vec![pi_field]] + bn256::Fr::from_bytes_le(&public_input_commitment) } } @@ -412,9 +413,9 @@ impl SyncStepCircuit { } /// Takes a list of pubkeys and aggregates them. - fn aggregate_pubkeys<'a>( + fn aggregate_pubkeys( ctx: &mut Context, - fp_chip: &FpChip<'a, F>, + fp_chip: &FpChip<'_, F>, pubkey_affines: &[G1Affine], pariticipation_bits: &[bool], assigned_affines: &mut Vec>, diff --git a/lightclient-circuits/src/util/circuit.rs b/lightclient-circuits/src/util/circuit.rs index 0b0d9297..f06de225 100644 --- a/lightclient-circuits/src/util/circuit.rs +++ b/lightclient-circuits/src/util/circuit.rs @@ -9,7 +9,9 @@ use halo2_proofs::plonk::{Circuit, Error, VerifyingKey}; use halo2_proofs::{plonk::ProvingKey, poly::kzg::commitment::ParamsKZG}; use halo2curves::bn256::{Bn256, Fr, G1Affine}; use serde::{Deserialize, Serialize}; -use snark_verifier_sdk::evm::{gen_evm_proof, gen_evm_proof_shplonk, gen_evm_verifier_shplonk, encode_calldata, evm_verify}; +use snark_verifier_sdk::evm::{ + encode_calldata, evm_verify, gen_evm_proof, gen_evm_proof_shplonk, gen_evm_verifier_shplonk, +}; use snark_verifier_sdk::halo2::aggregation::AggregationCircuit; use snark_verifier_sdk::{gen_pk, halo2::gen_snark_shplonk, read_pk}; use snark_verifier_sdk::{CircuitExt, Snark}; diff --git a/lightclient-circuits/src/util/common.rs b/lightclient-circuits/src/util/common.rs index ecb26592..339ed88b 100644 --- a/lightclient-circuits/src/util/common.rs +++ b/lightclient-circuits/src/util/common.rs @@ -114,7 +114,7 @@ impl CellType { } /// Return the storage cell of the advice column - pub(crate) fn storage_for_column(col: &Column) -> CellType { + pub(crate) fn storage_for_column(col: &Column) -> CellType { Self::storage_for_phase(col.column_type().phase()) } } diff --git a/lightclient-circuits/src/witness/sync.rs b/lightclient-circuits/src/witness/sync.rs index fd557818..e855daa4 100644 --- a/lightclient-circuits/src/witness/sync.rs +++ b/lightclient-circuits/src/witness/sync.rs @@ -62,11 +62,11 @@ impl Default for SyncStepArgs { body_root: Node::from_bytes(beacon_block_body_root.try_into().unwrap()), ..Default::default() }; - let finilized_header = finalized_block.hash_tree_root().unwrap().as_ref().to_vec(); + let finalized_header = finalized_block.hash_tree_root().unwrap().as_ref().to_vec(); let finality_merkle_branch = vec![vec![0; 32]; S::FINALIZED_HEADER_DEPTH]; - let beacon_state_root = compute_root(finilized_header, &state_merkle_branch); + let beacon_state_root = compute_root(finalized_header, &state_merkle_branch); Self { signature_compressed: hex::decode("462c5acb68722355eaa568a166e6da4c46702a496586aa94c681e0b03a200394b8f4adc98d6b5a68e3caf9dae31ff7035a402aad93bdd4752e521b3b536b47dee55d129b6374177f2be8c99b6ea6618abae84b389affc5a50ad8d991f763beaa").unwrap(), diff --git a/lightclient-circuits/tests/step.rs b/lightclient-circuits/tests/step.rs new file mode 100644 index 00000000..3ea74112 --- /dev/null +++ b/lightclient-circuits/tests/step.rs @@ -0,0 +1,446 @@ +use ark_std::{end_timer, start_timer}; +use eth_types::Minimal; +use ethereum_consensus_types::presets::minimal::{LightClientBootstrap, LightClientUpdateCapella}; +use ethereum_consensus_types::signing::{compute_domain, DomainType}; +use ethereum_consensus_types::{ForkData, Root}; +use halo2_base::gates::builder::CircuitBuilderStage; +use halo2_proofs::dev::MockProver; +use halo2curves::bn256; +use itertools::Itertools; +use light_client_verifier::ZiplineUpdateWitnessCapella; +use lightclient_circuits::sync_step_circuit::SyncStepCircuit; +use lightclient_circuits::util::gen_srs; +use lightclient_circuits::util::AppCircuit; +use lightclient_circuits::util::Eth2ConfigPinning; +use lightclient_circuits::util::Halo2ConfigPinning; +use lightclient_circuits::util::{full_prover, full_verifier}; +use lightclient_circuits::witness::SyncStepArgs; +use rstest::rstest; +use snark_verifier_sdk::CircuitExt; +use ssz_rs::prelude::*; +use ssz_rs::Merkleized; +use ssz_rs::Node; +use std::path::PathBuf; +use sync_committee_primitives::consensus_types::BeaconBlockHeader; + +use test_utils::{load_snappy_ssz, load_yaml}; +#[derive(Debug, serde::Deserialize)] +struct TestMeta { + genesis_validators_root: String, + trusted_block_root: String, + bootstrap_fork_digest: String, + store_fork_digest: String, +} + +#[derive(Debug, serde::Deserialize)] +#[serde(rename_all = "snake_case")] +enum TestStep { + ProcessUpdate { + update_fork_digest: String, + update: String, + current_slot: u64, + checks: Checks, + }, + ForceUpdate { + current_slot: u64, + checks: Checks, + }, +} + +#[derive(Debug, serde::Deserialize)] +struct Checks { + finalized_header: RootAtSlot, + optimistic_header: RootAtSlot, +} + +#[derive(Debug, serde::Deserialize)] +struct RootAtSlot { + slot: u64, + beacon_root: String, + execution_root: String, +} + +// TODO: remove this once we have a better way to handle the `ssz_rs` dependency +#[derive(Debug, Default, Clone, PartialEq, SimpleSerialize, Eq)] +pub struct ByteVector(pub Vector); +#[derive(Default, Debug, Clone, PartialEq, Eq, SimpleSerialize)] +pub struct ByteList(List); +pub type ExecutionAddress = ByteVector<20>; + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ExecutionPayloadHeader< + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, +> { + pub parent_hash: Node, + pub fee_recipient: ExecutionAddress, + pub state_root: Node, + pub receipts_root: Node, + pub logs_bloom: ByteVector, + pub prev_randao: Node, + pub block_number: u64, + pub gas_limit: u64, + pub gas_used: u64, + pub timestamp: u64, + pub extra_data: ByteList, + pub base_fee_per_gas: U256, + pub block_hash: Node, + pub transactions_root: Node, + pub withdrawals_root: Node, +} + +impl + From< + ethereum_consensus_types::light_client::ExecutionPayloadHeader< + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + >, + > for ExecutionPayloadHeader +{ + fn from( + header: ethereum_consensus_types::light_client::ExecutionPayloadHeader< + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + >, + ) -> Self { + Self { + parent_hash: Node::from_bytes(header.parent_hash.as_ref().try_into().unwrap()), + fee_recipient: ByteVector( + Vector::try_from(header.fee_recipient.0.as_ref().to_vec()).unwrap(), + ), + state_root: Node::from_bytes(header.state_root.as_ref().try_into().unwrap()), + receipts_root: Node::from_bytes(header.receipts_root.as_ref().try_into().unwrap()), + logs_bloom: ByteVector( + Vector::try_from(header.logs_bloom.0.as_ref().to_vec()).unwrap(), + ), + prev_randao: Node::from_bytes(header.prev_randao.as_ref().try_into().unwrap()), + block_number: header.block_number, + gas_limit: header.gas_limit, + gas_used: header.gas_used, + timestamp: header.timestamp, + extra_data: ByteList(List::try_from(header.extra_data.0.as_ref().to_vec()).unwrap()), + base_fee_per_gas: U256::from_bytes_le( + header.base_fee_per_gas.to_bytes_le().try_into().unwrap(), + ), + block_hash: Node::from_bytes(header.block_hash.as_ref().try_into().unwrap()), + transactions_root: Node::from_bytes( + header.transactions_root.as_ref().try_into().unwrap(), + ), + withdrawals_root: Node::from_bytes( + header.withdrawals_root.as_ref().try_into().unwrap(), + ), + } + } +} + +fn to_witness< + const SYNC_COMMITTEE_SIZE: usize, + const NEXT_SYNC_COMMITTEE_GINDEX: usize, + const NEXT_SYNC_COMMITTEE_PROOF_SIZE: usize, + const FINALIZED_ROOT_GINDEX: usize, + const FINALIZED_ROOT_PROOF_SIZE: usize, + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, +>( + zipline_witness: &ZiplineUpdateWitnessCapella< + SYNC_COMMITTEE_SIZE, + NEXT_SYNC_COMMITTEE_GINDEX, + NEXT_SYNC_COMMITTEE_PROOF_SIZE, + FINALIZED_ROOT_GINDEX, + FINALIZED_ROOT_PROOF_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + >, + genesis_validators_root: Root, +) -> SyncStepArgs { + let mut args = SyncStepArgs::::default(); + args.signature_compressed = zipline_witness + .light_client_update + .sync_aggregate + .sync_committee_signature + .to_bytes() + .to_vec(); + args.signature_compressed.reverse(); + let pubkeys_uncompressed = zipline_witness + .committee + .pubkeys + .iter() + .map(|pk| { + let p = pk.decompressed_bytes(); + let mut x = (&p[0..48]).clone().to_vec(); + let mut y = (&p[48..96]).clone().to_vec(); + x.reverse(); + y.reverse(); + let mut res = vec![]; + res.append(&mut x); + res.append(&mut y); + res + }) + .collect_vec(); + args.pubkeys_uncompressed = pubkeys_uncompressed; + args.pariticipation_bits = zipline_witness + .light_client_update + .sync_aggregate + .sync_committee_bits + .iter() + .map(|b| *b) + .collect(); + args.attested_header = BeaconBlockHeader { + slot: zipline_witness + .light_client_update + .attested_header + .beacon + .slot, + proposer_index: zipline_witness + .light_client_update + .attested_header + .beacon + .proposer_index as u64, + parent_root: Node::try_from( + zipline_witness + .light_client_update + .attested_header + .beacon + .parent_root + .as_ref(), + ) + .unwrap(), + state_root: Node::try_from( + zipline_witness + .light_client_update + .attested_header + .beacon + .state_root + .as_ref(), + ) + .unwrap(), + body_root: Node::try_from( + zipline_witness + .light_client_update + .attested_header + .beacon + .body_root + .as_ref(), + ) + .unwrap(), + }; + args.finalized_header = BeaconBlockHeader { + slot: zipline_witness + .light_client_update + .finalized_header + .beacon + .slot, + proposer_index: zipline_witness + .light_client_update + .finalized_header + .beacon + .proposer_index as u64, + parent_root: Node::try_from( + zipline_witness + .light_client_update + .finalized_header + .beacon + .parent_root + .as_ref(), + ) + .unwrap(), + state_root: Node::try_from( + zipline_witness + .light_client_update + .finalized_header + .beacon + .state_root + .as_ref(), + ) + .unwrap(), + body_root: Node::try_from( + zipline_witness + .light_client_update + .finalized_header + .beacon + .body_root + .as_ref(), + ) + .unwrap(), + }; + let fork_data = ForkData { + fork_version: [3, 0, 0, 1], + genesis_validators_root: genesis_validators_root.clone(), + }; + let signing_domain = compute_domain(DomainType::SyncCommittee, &fork_data).unwrap(); + args.domain = signing_domain; + args.execution_payload_branch = zipline_witness + .light_client_update + .finalized_header + .execution_branch + .iter() + .map(|b| b.0.as_ref().to_vec()) + .collect(); + args.execution_payload_root = { + let mut execution_payload_header: ExecutionPayloadHeader< + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + > = zipline_witness + .light_client_update + .finalized_header + .execution + .clone() + .into(); + + execution_payload_header + .hash_tree_root() + .unwrap() + .as_ref() + .to_vec() + }; + args.finality_branch = zipline_witness + .light_client_update + .finality_branch + .iter() + .map(|b| b.as_ref().to_vec()) + .collect(); + // args.beacon_state_root = zipline_witness.light_client_update.finalized_header.state_root.clone().as_ref().to_vec(); + // args.beacon_state_root = args.attested_block.state_root.as_ref().to_vec(); + args +} + +fn read_test_files_and_gen_witness(path: PathBuf) -> SyncStepArgs { + let bootstrap: LightClientBootstrap = + load_snappy_ssz(&path.join("bootstrap.ssz_snappy").to_str().unwrap()).unwrap(); + let meta: TestMeta = load_yaml(&path.join("meta.yaml").to_str().unwrap()); + let steps: Vec = load_yaml(&path.join("steps.yaml").to_str().unwrap()); + + let genesis_validators_root = Root::try_from( + hex::decode(meta.genesis_validators_root.trim_start_matches("0x")) + .unwrap() + .as_slice(), + ) + .unwrap(); + + // let circuit = SyncStepCircuit::::default(); + let updates = steps + .iter() + .filter_map(|step| match step { + TestStep::ProcessUpdate { update, .. } => { + let update: LightClientUpdateCapella = load_snappy_ssz( + &path + .join(format!("{}.ssz_snappy", update)) + .to_str() + .unwrap(), + ) + .unwrap(); + Some(update) + } + _ => None, + }) + .collect::>(); + + let zipline_witness = light_client_verifier::ZiplineUpdateWitnessCapella { + committee: bootstrap.current_sync_committee.clone(), + light_client_update: updates[0].clone(), + }; + to_witness(&zipline_witness, genesis_validators_root.clone()) +} +#[rstest] +fn test_step_mock( + #[files("../consensus-spec-tests/tests/minimal/capella/light_client/sync/pyspec_tests/**")] + #[exclude("deneb*")] + path: PathBuf, +) { + const K: u32 = 21; + let params = gen_srs(K); + + let witness = read_test_files_and_gen_witness(path); + let pinning = Eth2ConfigPinning::from_path("./config/sync_step.json"); + + let circuit = SyncStepCircuit::::create_circuit( + CircuitBuilderStage::Mock, + Some(pinning), + ¶ms, + &witness, + ) + .unwrap(); + + let timer = start_timer!(|| "sync_step mock prover"); + let prover = MockProver::::run(K, &circuit, circuit.instances()).unwrap(); + prover.assert_satisfied_par(); + end_timer!(timer); +} + +#[rstest] +fn test_step_proofgen( + #[files("../consensus-spec-tests/tests/minimal/capella/light_client/sync/pyspec_tests/**")] + #[exclude("deneb*")] + path: PathBuf, +) { + const K: u32 = 20; + let witness = read_test_files_and_gen_witness(path); + + let params = gen_srs(K); + let pk = SyncStepCircuit::::read_or_create_pk( + ¶ms, + "../build/sync_step.pkey", + "./config/sync_step.json", + false, + &SyncStepArgs::::default(), + ); + + let pinning = Eth2ConfigPinning::from_path("./config/sync_step.json"); + + let circuit = SyncStepCircuit::::create_circuit( + CircuitBuilderStage::Prover, + Some(pinning), + ¶ms, + &witness, + ) + .unwrap(); + + let instances = circuit.instances(); + let proof = full_prover(¶ms, &pk, circuit, instances.clone()); + + assert!(full_verifier(¶ms, pk.get_vk(), proof, instances)) +} +#[rstest] +fn test_step_evm_verify( + #[files("../consensus-spec-tests/tests/minimal/capella/light_client/sync/pyspec_tests/**")] + #[exclude("deneb*")] + path: PathBuf, +) { + const K: u32 = 21; + let params = gen_srs(K); + + let pk = SyncStepCircuit::::read_or_create_pk( + ¶ms, + "../build/sync_step.pkey", + "./config/sync_step.json", + false, + &SyncStepArgs::::default(), + ); + + let witness = read_test_files_and_gen_witness(path); + + let pinning = Eth2ConfigPinning::from_path("./config/sync_step.json"); + + let circuit = SyncStepCircuit::::create_circuit( + CircuitBuilderStage::Prover, + Some(pinning), + ¶ms, + &witness, + ) + .unwrap(); + + let instances = circuit.instances(); + let proof = + snark_verifier_sdk::evm::gen_evm_proof_shplonk(¶ms, &pk, circuit, instances.clone()); + println!("proof size: {}", proof.len()); + let deployment_code = SyncStepCircuit::::gen_evm_verifier_shplonk( + ¶ms, + &pk, + None::, + &witness, + ) + .unwrap(); + println!("deployment_code size: {}", deployment_code.len()); + snark_verifier_sdk::evm::evm_verify(deployment_code, instances, proof); +} diff --git a/rust-toolchain b/rust-toolchain index a3c60e9d..0675d073 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2022-11-22 +nightly-2023-04-18 diff --git a/scripts/download_consensus_specs.sh b/scripts/download_consensus_specs.sh new file mode 100755 index 00000000..8f9200b5 --- /dev/null +++ b/scripts/download_consensus_specs.sh @@ -0,0 +1,13 @@ +#!/bin/bash +TESTS_TAG=v1.3.0 +REPO_NAME=consensus-spec-tests +CONFIGS="general minimal" + +set -eou pipefail + +mkdir -p ${REPO_NAME} +for config in ${CONFIGS} +do + wget https://github.com/ethereum/${REPO_NAME}/releases/download/${TESTS_TAG}/${config}.tar.gz + tar -xzf ${config}.tar.gz -C ${REPO_NAME} +done