diff --git a/circuits/plonky2x/src/builder.rs b/circuits/plonky2x/src/builder.rs index 79f96e4..5cf9835 100644 --- a/circuits/plonky2x/src/builder.rs +++ b/circuits/plonky2x/src/builder.rs @@ -3,6 +3,7 @@ use plonky2x::prelude::*; use pretty_assertions::assert_eq; use crate::{ + hint::FetchHeaderInputs, merkle::{MerklePathVariable, NearMerkleTree}, variables::{ ApprovalMessage, BlockHeightVariable, BlockVariable, BpsApprovals, BpsArr, diff --git a/circuits/plonky2x/src/circuits/sync.rs b/circuits/plonky2x/src/circuits/sync.rs index 206946c..0cd61a5 100644 --- a/circuits/plonky2x/src/circuits/sync.rs +++ b/circuits/plonky2x/src/circuits/sync.rs @@ -23,13 +23,7 @@ impl Circuit for SyncCircuit { plonky2::plonk::config::AlgebraicHasher<>::Field>, { let trusted_header_hash = b.evm_read::(); - b.watch(&trusted_header_hash, "trusted_header_hash"); - - let untrusted = FetchHeaderInputs(NETWORK.into()).fetch(b, &trusted_header_hash); - let untrusted_hash = untrusted.hash(b); - b.watch(&untrusted_hash, "untrusted_hash"); - b.assert_is_equal(trusted_header_hash, untrusted_hash); - let head = untrusted; + let head = FetchHeaderInputs(NETWORK.into()).fetch(b, &trusted_header_hash); // This is a very interesting trick to be able to get the BPS for the next epoch // without the need to store the BPS, we verify the hash of the BPS in the diff --git a/circuits/plonky2x/src/circuits/verify.rs b/circuits/plonky2x/src/circuits/verify.rs index cd5d103..deebb03 100644 --- a/circuits/plonky2x/src/circuits/verify.rs +++ b/circuits/plonky2x/src/circuits/verify.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use crate::{ builder::Verify, - hint::{FetchProofInputs, ProofInputVariable}, + hint::{FetchHeaderInputs, FetchProofInputs, ProofInputVariable}, variables::{ byte_from_bool, CryptoHashVariable, EncodeInner, HeaderVariable, TransactionOrReceiptIdVariable, @@ -34,22 +34,29 @@ impl Circuit <>::Config as GenericConfig>::Hasher: AlgebraicHasher<>::Field>, { - let trusted_head = b.evm_read::(); + let trusted_header_hash = b.evm_read::(); + let head = FetchHeaderInputs(NETWORK.into()).fetch(b, &trusted_header_hash); + let mut ids = vec![]; for _ in 0..N { ids.push(b.evm_read::()); } - let proofs = FetchProofInputs::(NETWORK.into()).fetch(b, &trusted_head, &ids); + let proofs = FetchProofInputs::(NETWORK.into()).fetch(b, &head, &ids); + // Init a default result for N let zero = b.constant::([0u8; 32].into()); let _false = b._false(); + let default = ProofVerificationResultVariable { + id: zero, + result: _false, + }; // TODO: write some outputs here for each ID - let output = b.mapreduce_dynamic::<(), ProofInputVariable, ArrayVariable, Self, B, _, _>( - (), + let output = b.mapreduce_dynamic::, Self, B, _, _>( + default, proofs.data, - |_, proofs, b| { + |default, proofs, b| { let mut results = vec![]; // TODO[Optimisation]: could parallelise these @@ -60,19 +67,16 @@ impl Circuit results.resize( N, - ProofVerificationResultVariable { - id: zero, - result: _false, - }, + default, ); results.into() }, |_, l, r, b| MergeProofHint::.merge(b, &l, &r), ); + b.watch_slice(&output.data, "output"); for r in output.data { b.evm_write::(r.id); - let _true = b._true(); let passed = byte_from_bool(b, r.result); b.evm_write::(passed); } @@ -84,6 +88,7 @@ impl Circuit { registry.register_async_hint::>(); registry.register_hint::>(); + registry.register_async_hint::(); // We hash in verify registry.register_hint::(); @@ -92,7 +97,7 @@ impl Circuit registry.register_simple::, Self, @@ -130,14 +135,23 @@ pub struct MergeProofHint; impl, const D: usize, const N: usize> Hint for MergeProofHint { fn hint(&self, input_stream: &mut ValueStream, output_stream: &mut ValueStream) { let left = input_stream.read_value::>(); + log::debug!("Left results: {:?}", left); let right = input_stream.read_value::>(); + log::debug!("Right results: {:?}", right); let mut results = left .into_iter() .chain(right.into_iter()) - .filter_map(|r| if r.id.0 != [0u8; 32] { Some(r) } else { None }) + .filter_map(|r| { + if r.id.0 != [0u8; 32] && r.id.0 != [255u8; 32] { + Some(r) + } else { + None + } + }) .collect_vec(); + log::debug!("Merged results: {:?}", results); results.resize( N, ProofVerificationResultVariableValue:: { @@ -174,7 +188,6 @@ mod beefy_tests { use ::test_utils::CryptoHash; use near_light_client_protocol::prelude::Itertools; use near_primitives::types::TransactionOrReceiptId; - use plonky2x::frontend::vars::EvmVariable; use serial_test::serial; use test_utils::fixture; @@ -227,7 +240,7 @@ mod beefy_tests { VerifyCircuit::::define(b); }; let writer = |input: &mut PI| { - input.evm_write::(header.into()); + input.evm_write::(header.hash().0.into()); for tx in txs { input.evm_write::(tx.into()); } @@ -268,7 +281,7 @@ mod beefy_tests { VerifyCircuit::::define(b); }; let writer = |input: &mut PI| { - input.write::(header.into()); + input.evm_write::(header.hash().0.into()); input.write::>(ids.into()); }; let assertions = |mut output: PO| { diff --git a/circuits/plonky2x/src/hint.rs b/circuits/plonky2x/src/hint.rs index a9166e1..b7d7d65 100644 --- a/circuits/plonky2x/src/hint.rs +++ b/circuits/plonky2x/src/hint.rs @@ -73,16 +73,20 @@ impl, const D: usize> AsyncHint for FetchHeaderInput } impl FetchHeaderInputs { + /// Fetches a header based on its known hash and witnesses the result. pub fn fetch, const D: usize>( self, b: &mut CircuitBuilder, - hash: &CryptoHashVariable, + trusted_hash: &CryptoHashVariable, ) -> HeaderVariable { let mut input_stream = VariableStream::new(); - input_stream.write::(hash); + input_stream.write::(trusted_hash); let output_stream = b.async_hint(input_stream, self); - output_stream.read::(b) + let untrusted = output_stream.read::(b); + let untrusted_hash = untrusted.hash(b); + b.assert_is_equal(*trusted_hash, untrusted_hash); + untrusted } } // TODO: refactor into some client-like carrier for all hints that is serdeable @@ -136,7 +140,7 @@ impl, const D: usize, const B: usize> AsyncHint assert_eq!(proofs.len(), B, "Invalid number of proofs"); - log::info!("Fetched {} proofs", proofs.len()); + log::debug!("Fetched {} proofs", proofs.len()); for (k, p) in proofs.into_iter() { output_stream.write_value::(k.0.into()); @@ -166,6 +170,11 @@ impl FetchProofInputs { proof: output_stream.read::(b), }); } + // Witness that each head block root in each proof is the same as the trusted + // head + inputs.iter().for_each(|x| { + b.assert_is_equal(x.proof.head_block_root, head.inner_lite.block_merkle_root) + }); inputs.into() } }