use axiom_codec::{
constants::USER_MAX_OUTPUTS,
decoder::native::decode_compute_snark,
encoder::native::{get_query_hash_v2, get_query_schema_hash},
types::native::{AxiomV2ComputeQuery, AxiomV2ComputeSnark, AxiomV2DataQuery},
utils::native::{decode_hilo_to_h256, encode_h256_to_hilo},
HiLo,
};
use axiom_eth::{
halo2_base::{
gates::circuit::CircuitBuilderStage,
halo2_proofs::{dev::MockProver, poly::commitment::ParamsProver},
utils::fs::gen_srs,
},
halo2curves::bn256::{Fr, G1Affine},
snark_verifier::pcs::{
kzg::{KzgAccumulator, LimbsEncoding},
AccumulatorEncoding,
},
snark_verifier_sdk::{
gen_pk,
halo2::{
aggregation::{AggregationCircuit, VerifierUniversality},
gen_snark_shplonk,
},
NativeLoader, Snark, BITS, LIMBS, SHPLONK,
},
utils::{
build_utils::pinning::CircuitPinningInstructions,
component::ComponentCircuit,
snark_verifier::{AggregationCircuitParams, NUM_FE_ACCUMULATOR},
},
};
use ethers_core::{types::H256, utils::keccak256};
use itertools::Itertools;
#[cfg(test)]
use test_log::test;
use crate::{
components::results::types::{CircuitOutputResultsRoot, LogicOutputResultsRoot},
utils::client_circuit::metadata::AxiomV2CircuitMetadata,
verify_compute::{
tests::utils::dummy_compute_snark,
types::{
CircuitInputVerifyCompute, CoreParamsVerifyCompute, LogicalPublicInstanceVerifyCompute,
},
utils::{
get_onchain_vk_from_protocol, verify_snark, write_onchain_vkey, UserCircuitParams,
DEFAULT_CLIENT_METADATA, DEFAULT_USER_PARAMS,
},
},
};
use super::{get_test_result, prepare_mock_circuit, prepare_prover_circuit, test_compute_circuit};
fn test_compute_app_snark(
k: u32,
user_params: UserCircuitParams,
subquery_results: LogicOutputResultsRoot,
result_len: u16,
) -> Snark {
let app_params = gen_srs(k);
let compute_app = test_compute_circuit(k, user_params, subquery_results, result_len as usize);
let pk = gen_pk(&app_params, &compute_app, None);
gen_snark_shplonk(&app_params, &pk, compute_app, None::<&str>)
}
pub fn test_aggregation_circuit(
agg_circuit_params: AggregationCircuitParams,
snark: Snark,
) -> AggregationCircuit {
let params = gen_srs(agg_circuit_params.degree);
let mut circuit = AggregationCircuit::new::<SHPLONK>(
CircuitBuilderStage::Mock,
agg_circuit_params,
¶ms,
[snark],
VerifierUniversality::None,
);
circuit.expose_previous_instances(false);
circuit
}
fn get_metadata(agg_circuit_params: AggregationCircuitParams) -> AxiomV2CircuitMetadata {
AxiomV2CircuitMetadata {
version: 0,
num_instance: vec![DEFAULT_CLIENT_METADATA.num_instance[0] + NUM_FE_ACCUMULATOR as u32],
num_challenge: vec![0],
is_aggregation: true,
num_advice_per_phase: vec![agg_circuit_params.num_advice as u16],
num_lookup_advice_per_phase: vec![agg_circuit_params.num_lookup_advice as u8],
num_rlc_columns: 0,
num_fixed: agg_circuit_params.num_fixed as u8,
max_outputs: USER_MAX_OUTPUTS as u16,
}
}
fn get_test_input(
app_snark: Snark,
source_chain_id: u64,
subquery_results: LogicOutputResultsRoot,
result_len: u16,
agg_k: u32,
) -> (CoreParamsVerifyCompute, CircuitInputVerifyCompute) {
let agg_circuit_params = AggregationCircuitParams {
degree: agg_k,
num_advice: 20,
num_lookup_advice: 2,
num_fixed: 1,
lookup_bits: agg_k as usize - 1,
};
let agg_params = gen_srs(agg_k);
let subquery_results = CircuitOutputResultsRoot::<Fr>::try_from(subquery_results).unwrap();
let client_metadata = get_metadata(agg_circuit_params);
let agg_compute_snark = {
let agg_circuit = test_aggregation_circuit(agg_circuit_params, app_snark);
let pk = gen_pk(&agg_params, &agg_circuit, None);
gen_snark_shplonk(&agg_params, &pk, agg_circuit, None::<&str>)
};
dbg!(&client_metadata);
let core_params = CoreParamsVerifyCompute::new(
subquery_results.results.len(),
agg_params.get_g()[0],
client_metadata,
agg_compute_snark.protocol.preprocessed.len(),
);
println!("agg_compute_snark.protocol.preprocessed.len(): {}", core_params.preprocessed_len());
(
core_params,
CircuitInputVerifyCompute::new(
source_chain_id,
subquery_results,
true,
result_len,
agg_compute_snark,
),
)
}
#[test]
fn test_verify_compute_agg_mock() -> anyhow::Result<()> {
let (_input_results, data_results) = get_test_result::<Fr>();
let num_subqueries = data_results.num_subqueries;
let result_len = num_subqueries as u16;
let compute_app_snark =
test_compute_app_snark(14, DEFAULT_USER_PARAMS, data_results.clone(), result_len);
let source_chain_id = 1;
let agg_k = 20;
let (core_params, input) =
get_test_input(compute_app_snark, source_chain_id, data_results.clone(), result_len, agg_k);
let subqueries =
data_results.results[..num_subqueries].iter().map(|r| r.subquery.clone()).collect();
let agg_compute_snark = input.compute_snark();
let data_query = AxiomV2DataQuery { source_chain_id, subqueries };
let compute_vkey = get_onchain_vk_from_protocol(
&agg_compute_snark.protocol,
core_params.client_metadata().clone(),
);
let agg_instances = &agg_compute_snark.instances[0];
let KzgAccumulator { lhs, rhs } =
<LimbsEncoding<LIMBS, BITS> as AccumulatorEncoding<G1Affine, NativeLoader>>::from_repr(
&agg_instances[..NUM_FE_ACCUMULATOR].iter().collect_vec(),
)
.unwrap();
let compute_results = agg_instances[NUM_FE_ACCUMULATOR..]
.chunks(2)
.take(result_len as usize)
.map(|c| decode_hilo_to_h256(HiLo::from_hi_lo([c[0], c[1]])))
.collect_vec();
let compute_snark = AxiomV2ComputeSnark {
kzg_accumulator: Some((lhs, rhs)),
compute_results,
proof_transcript: agg_compute_snark.proof.clone(),
};
let compute_proof = compute_snark.encode()?.into();
let compute_query = AxiomV2ComputeQuery {
k: agg_k as u8,
result_len,
vkey: write_onchain_vkey(&compute_vkey).unwrap(),
compute_proof,
};
let circuit_k = 19u32;
let keccak_f_capacity = 200;
let circuit = prepare_mock_circuit(core_params, circuit_k as usize, keccak_f_capacity, input);
let instances = circuit.get_public_instances();
let logic_pis = instances.other.clone();
let LogicalPublicInstanceVerifyCompute {
query_hash, query_schema, compute_results_hash, ..
} = logic_pis.try_into()?;
let native_query_schema =
get_query_schema_hash(compute_query.k, compute_query.result_len, &compute_query.vkey)?;
assert_eq!(&query_schema, &encode_h256_to_hilo(&native_query_schema));
let native_query_hash = get_query_hash_v2(source_chain_id, &data_query, &compute_query)?;
assert_eq!(&query_hash, &encode_h256_to_hilo(&native_query_hash));
let compute_snark =
decode_compute_snark(&mut &compute_query.compute_proof[..], result_len, true)?;
let encode_results = compute_snark.compute_results.iter().map(|r| r.0.to_vec()).concat();
let native_results_hash = H256(keccak256(encode_results));
assert_eq!(&compute_results_hash, &encode_h256_to_hilo(&native_results_hash));
MockProver::run(circuit_k, &circuit, vec![instances.into()]).unwrap().assert_satisfied();
Ok(())
}
#[test]
fn test_verify_compute_agg_prover() -> anyhow::Result<()> {
let (_input_results, data_results) = get_test_result::<Fr>();
let num_subqueries = data_results.num_subqueries;
let result_len = num_subqueries as u16;
let mut user_params = DEFAULT_USER_PARAMS;
user_params.num_advice_cols += 1;
let app_k = 12;
let agg_k = 20;
let compute_app_snark = dummy_compute_snark(&gen_srs(app_k), user_params, "./data");
let (core_params, input) = get_test_input(compute_app_snark, 0, data_results.clone(), 0, agg_k);
let circuit_k = 19u32;
let circuit = prepare_mock_circuit(core_params, circuit_k as usize, 200, input);
let kzg_params = gen_srs(circuit_k);
let pk = gen_pk(&kzg_params, &circuit, None);
let rlc_pinning = circuit.pinning();
let compute_app_snark =
test_compute_app_snark(14, DEFAULT_USER_PARAMS, data_results.clone(), result_len);
let (core_params, input) =
get_test_input(compute_app_snark, 1, data_results, result_len, agg_k);
let circuit = prepare_prover_circuit(core_params, rlc_pinning, 200, input);
let snark = gen_snark_shplonk(&kzg_params, &pk, circuit, None::<&str>);
let dk = (kzg_params.get_g()[0], kzg_params.g2(), kzg_params.s_g2());
verify_snark(&dk.into(), &snark)?;
Ok(())
}