From dc7b740a40808aa98a9e5e8efa3575ec3a9f70c8 Mon Sep 17 00:00:00 2001 From: dndll Date: Wed, 17 Jan 2024 09:30:47 +0000 Subject: [PATCH] wip: return a value from syncing as per other lib --- circuits/plonky2x/src/builder.rs | 111 ++++++++++------------------- circuits/plonky2x/src/codec.rs | 105 --------------------------- circuits/plonky2x/src/lib.rs | 28 +++++--- circuits/plonky2x/src/merkle.rs | 2 + circuits/plonky2x/src/variables.rs | 40 ++++++++++- 5 files changed, 97 insertions(+), 189 deletions(-) delete mode 100644 circuits/plonky2x/src/codec.rs diff --git a/circuits/plonky2x/src/builder.rs b/circuits/plonky2x/src/builder.rs index 376a2be..84573ef 100644 --- a/circuits/plonky2x/src/builder.rs +++ b/circuits/plonky2x/src/builder.rs @@ -2,10 +2,9 @@ use crate::merkle::NearMerkleTree; use crate::variables::{ ApprovalMessage, BlockHeightVariable, BlockVariable, BpsApprovals, BpsArr, BuildEndorsement, CryptoHashVariable, HeaderVariable, MerklePathVariable, ProofVariable, StakeInfoVariable, - ValidatorStakeVariable, + SyncedVariable, ValidatorStakeVariable, }; use near_light_client_protocol::prelude::Itertools; -use plonky2x::prelude::plonky2::plonk::config::GenericHashOut; use plonky2x::prelude::*; use pretty_assertions::assert_eq; @@ -132,6 +131,7 @@ impl, const D: usize> Verify for CircuitBuilder, const D: usize> Verify for CircuitBuilder BoolVariable { let hash = head.hash(self); + self.watch(&hash, "header hash"); self.is_equal(hash, *outcome_proof_block_hash) } @@ -200,17 +201,17 @@ impl, const D: usize> Verify for CircuitBuilder, const D: usize> Verify for CircuitBuilder, const D: usize> { head: HeaderVariable, epoch_bps: BpsArr, next_block: BlockVariable, - ); + ) -> SyncedVariable; fn reconstruct_approval_message(&mut self, next_block: &BlockVariable) -> ApprovalMessage; } @@ -251,7 +253,7 @@ impl, const D: usize> SyncCircuit for CircuitBuilder head: HeaderVariable, epoch_bps: BpsArr, next_block: BlockVariable, - ) { + ) -> SyncedVariable { let a = self.ensure_not_already_verified(&head, &next_block.header.inner_lite.height); self.assertx(a); @@ -272,22 +274,30 @@ impl, const D: usize> SyncCircuit for CircuitBuilder let d = self.ensure_stake_is_sufficient(&stake); self.assertx(d); - // TODO: hashing bps - if next_block.next_bps.len() > 0 { - let c = self.ensure_next_bps_is_valid( + let (next_bps_epoch, next_bps) = if next_block.next_bps.len() > 0 { + // TODO: hashing bps in circut + let e = self.ensure_next_bps_is_valid( &next_block.header.inner_lite.next_bp_hash, Some(&next_block.next_bp_hash), ); - self.assertx(c); - self.write::>(next_block.next_bps); - } + self.assertx(e); + ( + next_block.header.inner_lite.next_epoch_id, + next_block.next_bps, + ) + } else { + let eid = self.constant::([0u8; 32].into()); + let bps = self.constant::>(Default::default()); + (eid, bps) + }; self.watch(&next_block.header, "new_head"); - self.write::(next_block.header); - // TODO: decide what to write here for evm - // self.evm_write(next_block.inner_lite.block_merkle_root); + SyncedVariable { + new_head: next_block.header, + next_bps_epoch, + next_bps, + } } - // TODO: make const fn here to test len with real approval fn reconstruct_approval_message(&mut self, next_block: &BlockVariable) -> ApprovalMessage { let next_header_hash = next_block.header.hash(self); let next_block_hash = @@ -328,9 +338,9 @@ impl, const D: usize> VerifyCircuit for CircuitBuild proof: ProofVariable, ) -> BoolVariable { let block_hash = proof.block_header.hash(self); + self.watch(&block_hash, "proof header hash"); let block_hash_matches = self.is_equal(block_hash, proof.outcome_proof_block_hash); - self.watch(&block_hash_matches, "block_hash_matches"); let outcome_matches = self.verify_outcome( &proof.block_header.inner_lite.outcome_root, @@ -338,19 +348,19 @@ impl, const D: usize> VerifyCircuit for CircuitBuild &proof.outcome_hash, &proof.outcome_root_proof, ); - self.watch(&outcome_matches, "outcome_matches"); let block_matches = self.verify_block(&proof.head_block_root, &proof.block_proof, &block_hash); - self.watch(&block_matches, "block_matches"); let comp = self.and(block_matches, outcome_matches); let verified = self.and(comp, block_hash_matches); + self.watch(&verified, "proof verified"); self.assertx(verified); verified } } +// TODO: test this and reuse for block header inner fn to_le_bytes, V: CircuitVariable, const D: usize, const N: usize>( b: &mut CircuitBuilder, v: &V, @@ -376,20 +386,13 @@ fn to_le_bytes, V: CircuitVariable, const D: usize, const mod tests { use super::*; use crate::variables::*; - use ethers::types::U64; use near_light_client_protocol::{ prelude::{BasicProof, Header, Itertools}, - LightClientBlockView, Proof, Protocol, StakeInfo, ValidatorStake, + LightClientBlockView, Protocol, StakeInfo, ValidatorStake, }; use near_primitives::hash::CryptoHash; - use plonky2x::frontend::{ - ecc::curve25519::ed25519::eddsa::EDDSASignatureVariable, vars::EvmVariable, - }; - use plonky2x::{ - backend::circuit::{PublicInput, PublicOutput}, - frontend::ecc::curve25519::ed25519::eddsa::EDDSASignatureVariableValue, - }; - use pretty_assertions::{assert_eq, assert_ne}; + use plonky2x::backend::circuit::{PublicInput, PublicOutput}; + use pretty_assertions::assert_eq; use serde::de::DeserializeOwned; use std::str::FromStr; @@ -434,6 +437,7 @@ mod tests { next, ) } + fn to_header(bv: LightClientBlockView) -> Header { Header { prev_block_hash: bv.prev_block_hash, @@ -694,14 +698,15 @@ mod tests { } #[test] - fn test_sync_across_boundaries_blackbox() { + fn test_sync_across_epoch_boundaries() { let (head, next_bps, next_block) = test_state(); let define = |builder: &mut B| { - let header = builder.read::(); + let head = builder.read::(); let bps = builder.read::>(); let next_block = builder.read::(); - builder.sync(header, bps, next_block); + let synced = builder.sync(head, bps, next_block); + builder.write::(synced); }; let writer = |input: &mut PI| { input.write::(head.into()); @@ -709,48 +714,10 @@ mod tests { input.write::(next_block.clone().into()); }; let assertions = |mut output: PO| { - let header = output.read::(); + let header = output.read::(); println!("header: {:?}", header); }; builder_suite(define, writer, assertions); - - // TODO: let mut sync_and_update = |next_block: LightClientBlockView| { - // let next_bps = builder - // .constant::>( - // bps_var - // .clone() - // .into_iter() - // .map(|s| Into::::into(s)) - // .map(Into::into) - // .collect(), - // ); - // let next_block = builder.constant::(next_block.clone().into()); - // // // Assert we matched the epoch id for the new BPS - // // assert_eq!( - // // head.inner_lite.next_epoch_id, - // // sync_next.next_bps.as_ref().unwrap().0 .0 - // // ); - // // - // // head = sync_next.new_head; - // // next_bps = sync_next.next_bps.unwrap().1; - // // - // // // Assert new head is the new block - // // assert_eq!(head.inner_lite, next_block.inner_lite); - // // // Assert new BPS is from the next block producers because we're - // // // in an epoch boundary - // // assert_eq!( - // // &next_bps, - // // &next_block - // // .next_bps - // // .unwrap() - // // .into_iter() - // // .map(Into::into) - // // .collect_vec() - // // ); - // // next_epoch_id.0 = head.inner_lite.next_epoch_id; - // }; - // sync_and_update(next_block_var.clone()); - // } #[test] diff --git a/circuits/plonky2x/src/codec.rs b/circuits/plonky2x/src/codec.rs deleted file mode 100644 index 643ee11..0000000 --- a/circuits/plonky2x/src/codec.rs +++ /dev/null @@ -1,105 +0,0 @@ -use borsh::BorshSerialize; -use plonky2x::{ - frontend::{hint::simple::hint::Hint, uint::Uint}, - prelude::{ - ByteVariable, CircuitBuilder, CircuitVariable, PlonkParameters, U32Variable, U64Variable, - ValueStream, - }, -}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct BorshCodec; - -impl, const D: usize> Hint for BorshCodec { - fn hint(&self, input_stream: &mut ValueStream, output_stream: &mut ValueStream) { - todo!() - } -} - -// THis isnt quite right, we should probably implement borsh for generic types here but its -// annoying -pub trait Borsh: Sized { - fn encodeb, const D: usize>( - &self, - builder: &mut CircuitBuilder, - ) -> Vec; - fn decodeb, const D: usize>( - builder: &mut CircuitBuilder, - bytes: &[ByteVariable], - ) -> Self; -} - -// impl Borsh for U64Variable { -// fn encodeb, const D: usize>( -// &self, -// builder: &mut CircuitBuilder, -// ) -> Vec { -// let x = self.variables()[0]; -// builder.beacon_get_block_header(block_root) -// >::to_little_endian(&self, &mut vec![]); -// U64Target::from(self).encode(builder); -// self.limbs -// .iter() -// .rev() -// .flat_map(|x| x.encode(builder)) -// .collect::>() -//} - -// fn decodeb, const D: usize>( -// builder: &mut CircuitBuilder, -// bytes: &[ByteVariable], -// ) -> Self { -// assert_eq!(bytes.len(), 2 * 4); -// let mut limbs = [U32Variable::init_unsafe(builder); 2]; -// limbs[0].to_little_endian(&mut bytes[0..4]); -// U32Variable::from_variables(builder, variables) -// for i in 0..2 { -// limbs[i] = U32Variable::decodeb(builder, &bytes[i * 4..(i + 1) * 4]); -// } -// limbs.reverse(); -// Self { limbs } -// } -//} - -// fn serialize(&self, writer: &mut W) -> borsh::io::Result<()> { -// let mut bytes = vec![]; -// self.0.to_little_endian(&mut bytes); -// writer.write_all(&bytes) -// } -// -// fn deserialize(reader: &mut R) -> borsh::io::Result { -// let mut bytes = vec![]; -// reader.read_to_end(&mut bytes)?; -// Ok(Borshable(::from_little_endian(&bytes))) -// } -// -macro_rules! borsh_integer { - ($a:ident, $b:ty, $c:expr) => { - impl Borsh for $a { - fn encodeb, const D: usize>( - &self, - builder: &mut CircuitBuilder, - ) -> Vec { - self.limbs - .iter() - .rev() - .flat_map(|x| x.encode(builder)) - .collect::>() - } - - fn decodeb, const D: usize>( - builder: &mut CircuitBuilder, - bytes: &[ByteVariable], - ) -> Self { - assert_eq!(bytes.len(), $c * 4); - let mut limbs = [U32Variable::init_unsafe(builder); $c]; - for i in 0..$c { - limbs[i] = U32Variable::decodeb(builder, &bytes[i * 4..(i + 1) * 4]); - } - limbs.reverse(); - Self { limbs } - } - } - }; -} diff --git a/circuits/plonky2x/src/lib.rs b/circuits/plonky2x/src/lib.rs index 4c3e0d6..dc0e003 100644 --- a/circuits/plonky2x/src/lib.rs +++ b/circuits/plonky2x/src/lib.rs @@ -1,24 +1,29 @@ use plonky2x::prelude::*; use variables::{BlockVariable, BpsArr, HeaderVariable, ProofVariable, ValidatorStakeVariable}; +/// Building blocks injected into the CircuitBuilder mod builder; -mod codec; -mod input; +/// Unprefixed merkle tree without collision resistance mod merkle; mod variables; -// TODO: -// hint/generator for any queries for things that are offchain/expensive to do in a circuit -// -// +// TODO: epoch sync, store head per epoch +// TODO: read bps from rainbow bridge +// TODO: determine how much we can bootstrap from RB +// TODO: evm reads/writes +// TODO: sync & prove for txs later than sync head +// TODO: async proof requests, based on a receipt/txs id (should be able to use light client rpc lib +// TODO: batch proof requests for a set of receipts/txs, must be bounded +// TODO: proof relay +// TODO: proof relay batches and batching factor +// TODO: batching/experimental proofs +// TODO[Style]: Shared trait for protocol functionality between crate <> circuit +// TODO[Style]: macro to share all the same implementation with semantic type differences between +// TODO: determine fees, allows integrators to charge +// protocol crate #[derive(Debug)] pub struct Circuit; -// TODO: here they use a hint to go and get all the inputs offchain -// - -// TODO: decide if to make validator set and other generics at trait level -// or stay in types pub trait SyncCircuit, const D: usize> { fn sync( &mut self, @@ -29,6 +34,7 @@ pub trait SyncCircuit, const D: usize> { } pub trait VerifyCircuit, const D: usize> { + // TODO: read head to determine if need to sync & prove fn verify( &mut self, proof: ProofVariable, diff --git a/circuits/plonky2x/src/merkle.rs b/circuits/plonky2x/src/merkle.rs index 90f47c1..f7cb1fd 100644 --- a/circuits/plonky2x/src/merkle.rs +++ b/circuits/plonky2x/src/merkle.rs @@ -1,5 +1,7 @@ use plonky2x::prelude::*; +/// This is an unprefixed merkle tree without collision resistance, this should probably adapt the +/// tendermint tree or introduce this functionality to succintx's simple tree pub trait NearMerkleTree { fn get_root_from_merkle_proof_hashed_leaf_unindex( &mut self, diff --git a/circuits/plonky2x/src/variables.rs b/circuits/plonky2x/src/variables.rs index 7461737..d49fb5f 100644 --- a/circuits/plonky2x/src/variables.rs +++ b/circuits/plonky2x/src/variables.rs @@ -6,7 +6,7 @@ use near_light_client_protocol::{ merkle_util::MerklePath, prelude::AccountId, prelude::CryptoHash, BlockHeaderInnerLiteView, LightClientBlockView, Signature, ValidatorStake, }; -use near_light_client_protocol::{Proof, StakeInfo}; +use near_light_client_protocol::{Proof, StakeInfo, Synced}; use plonky2x::frontend::curta::ec::point::CompressedEdwardsY; use plonky2x::frontend::ecc::curve25519::ed25519::eddsa::EDDSASignatureVariable; use plonky2x::frontend::ecc::curve25519::ed25519::eddsa::EDDSASignatureVariableValue; @@ -464,3 +464,41 @@ impl, const D: usize> Hint for BuildEndorsement { // Self // } // } + +#[derive(CircuitVariable, Clone, Debug)] +pub struct SyncedVariable { + pub new_head: HeaderVariable, + pub next_bps_epoch: CryptoHashVariable, + pub next_bps: BpsArr, +} + +impl From for SyncedVariableValue +where + F: RichField, +{ + fn from(value: Synced) -> Self { + let default_bps = vec![ValidatorStakeVariableValue::default(); NUM_BLOCK_PRODUCER_SEATS]; + Self { + new_head: value.new_head.into(), + next_bps_epoch: value + .next_bps + .as_ref() + .map(|v| v.0 .0 .0.into()) + .unwrap_or_default(), + next_bps: value + .next_bps + .map(|v| v.1.into_iter().map(Into::into).collect_vec()) + .unwrap_or(default_bps), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tests() { + todo!() + } +}