Skip to content

Commit

Permalink
fix: witness some safety values in headers and add default result to ctx
Browse files Browse the repository at this point in the history
  • Loading branch information
dndll committed Feb 14, 2024
1 parent bfd57bd commit fc54aad
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 27 deletions.
1 change: 1 addition & 0 deletions circuits/plonky2x/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 1 addition & 7 deletions circuits/plonky2x/src/circuits/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,7 @@ impl<const NETWORK: usize> Circuit for SyncCircuit<NETWORK> {
plonky2::plonk::config::AlgebraicHasher<<L as PlonkParameters<D>>::Field>,
{
let trusted_header_hash = b.evm_read::<CryptoHashVariable>();
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
Expand Down
45 changes: 29 additions & 16 deletions circuits/plonky2x/src/circuits/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -34,22 +34,29 @@ impl<const N: usize, const B: usize, const NETWORK: usize> Circuit
<<L as PlonkParameters<D>>::Config as GenericConfig<D>>::Hasher:
AlgebraicHasher<<L as PlonkParameters<D>>::Field>,
{
let trusted_head = b.evm_read::<HeaderVariable>();
let trusted_header_hash = b.evm_read::<CryptoHashVariable>();
let head = FetchHeaderInputs(NETWORK.into()).fetch(b, &trusted_header_hash);

let mut ids = vec![];
for _ in 0..N {
ids.push(b.evm_read::<TransactionOrReceiptIdVariable>());
}

let proofs = FetchProofInputs::<N>(NETWORK.into()).fetch(b, &trusted_head, &ids);
let proofs = FetchProofInputs::<N>(NETWORK.into()).fetch(b, &head, &ids);

// Init a default result for N
let zero = b.constant::<CryptoHashVariable>([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<ProofVerificationResultVariable, N>, Self, B, _, _>(
(),
let output = b.mapreduce_dynamic::<ProofVerificationResultVariable, ProofInputVariable, ArrayVariable<ProofVerificationResultVariable, N>, Self, B, _, _>(
default,
proofs.data,
|_, proofs, b| {
|default, proofs, b| {
let mut results = vec![];

// TODO[Optimisation]: could parallelise these
Expand All @@ -60,19 +67,16 @@ impl<const N: usize, const B: usize, const NETWORK: usize> Circuit

results.resize(
N,
ProofVerificationResultVariable {
id: zero,
result: _false,
},
default,
);

results.into()
},
|_, l, r, b| MergeProofHint::<N>.merge(b, &l, &r),
);
b.watch_slice(&output.data, "output");
for r in output.data {
b.evm_write::<CryptoHashVariable>(r.id);
let _true = b._true();
let passed = byte_from_bool(b, r.result);
b.evm_write::<ByteVariable>(passed);
}
Expand All @@ -84,6 +88,7 @@ impl<const N: usize, const B: usize, const NETWORK: usize> Circuit
{
registry.register_async_hint::<FetchProofInputs<N>>();
registry.register_hint::<MergeProofHint<N>>();
registry.register_async_hint::<FetchHeaderInputs>();

// We hash in verify
registry.register_hint::<EncodeInner>();
Expand All @@ -92,7 +97,7 @@ impl<const N: usize, const B: usize, const NETWORK: usize> Circuit

registry.register_simple::<MapReduceDynamicGenerator<
L,
(),
ProofVerificationResultVariable,
ProofInputVariable,
ArrayVariable<ProofVerificationResultVariable, N>,
Self,
Expand Down Expand Up @@ -130,14 +135,23 @@ pub struct MergeProofHint<const N: usize>;
impl<L: PlonkParameters<D>, const D: usize, const N: usize> Hint<L, D> for MergeProofHint<N> {
fn hint(&self, input_stream: &mut ValueStream<L, D>, output_stream: &mut ValueStream<L, D>) {
let left = input_stream.read_value::<ProofMapReduceVariable<N>>();
log::debug!("Left results: {:?}", left);
let right = input_stream.read_value::<ProofMapReduceVariable<N>>();
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::<L::Field> {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -227,7 +240,7 @@ mod beefy_tests {
VerifyCircuit::<AMT, BATCH, NETWORK>::define(b);
};
let writer = |input: &mut PI| {
input.evm_write::<HeaderVariable>(header.into());
input.evm_write::<CryptoHashVariable>(header.hash().0.into());
for tx in txs {
input.evm_write::<TransactionOrReceiptIdVariable>(tx.into());
}
Expand Down Expand Up @@ -268,7 +281,7 @@ mod beefy_tests {
VerifyCircuit::<AMT, BATCH, NETWORK>::define(b);
};
let writer = |input: &mut PI| {
input.write::<HeaderVariable>(header.into());
input.evm_write::<CryptoHashVariable>(header.hash().0.into());
input.write::<ArrayVariable<TransactionOrReceiptIdVariable, AMT>>(ids.into());
};
let assertions = |mut output: PO| {
Expand Down
17 changes: 13 additions & 4 deletions circuits/plonky2x/src/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,20 @@ impl<L: PlonkParameters<D>, const D: usize> AsyncHint<L, D> for FetchHeaderInput
}

impl FetchHeaderInputs {
/// Fetches a header based on its known hash and witnesses the result.
pub fn fetch<L: PlonkParameters<D>, const D: usize>(
self,
b: &mut CircuitBuilder<L, D>,
hash: &CryptoHashVariable,
trusted_hash: &CryptoHashVariable,
) -> HeaderVariable {
let mut input_stream = VariableStream::new();
input_stream.write::<CryptoHashVariable>(hash);
input_stream.write::<CryptoHashVariable>(trusted_hash);

let output_stream = b.async_hint(input_stream, self);
output_stream.read::<HeaderVariable>(b)
let untrusted = output_stream.read::<HeaderVariable>(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
Expand Down Expand Up @@ -136,7 +140,7 @@ impl<L: PlonkParameters<D>, const D: usize, const B: usize> AsyncHint<L, D>

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::<CryptoHashVariable>(k.0.into());
Expand Down Expand Up @@ -166,6 +170,11 @@ impl<const N: usize> FetchProofInputs<N> {
proof: output_stream.read::<ProofVariable>(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()
}
}
Expand Down

0 comments on commit fc54aad

Please sign in to comment.