diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index 3d19ca7..9c2759d 100644 --- a/crates/protocol/src/lib.rs +++ b/crates/protocol/src/lib.rs @@ -264,17 +264,7 @@ impl Protocol { let approved_stake = match Self::validate_signature(approval_message, sig, pk) { Ok(_) => approved_stake + stake, Err(Error::SignatureInvalid) => { - log::debug!( - "invalid signature: pk: {} sig: {:?}", - hex::encode(pk.unwrap_as_ed25519().0), - sig.clone().map(|s| { - if let Signature::ED25519(s) = *s { - hex::encode(s.r_bytes()) - } else { - unreachable!() - } - }) - ); + log::debug!("invalid signature: pk: {} sig: {:?}", pk, sig); approved_stake } Err(Error::ValidatorNotSigned) => approved_stake, diff --git a/nearx/src/builder.rs b/nearx/src/builder.rs index 797488b..07475b2 100644 --- a/nearx/src/builder.rs +++ b/nearx/src/builder.rs @@ -11,9 +11,9 @@ use pretty_assertions::assert_eq; use crate::{ merkle::{MerklePathVariable, NearMerkleTree}, variables::{ - ApprovalMessage, BlockHeightVariable, BlockVariable, BpsApprovals, BpsArr, - BuildEndorsement, CryptoHashVariable, HeaderVariable, ProofVariable, PublicKeyVariable, - StakeInfoVariable, ValidatorStakeVariable, + ApprovalMessage, BlockHeightVariable, BlockVariable, BpsApprovals, BuildEndorsement, + CryptoHashVariable, HeaderVariable, ProofVariable, PublicKeyVariable, StakeInfoVariable, + Validators, }, }; @@ -30,17 +30,17 @@ pub trait Ensure, const D: usize> { epoch_id: &CryptoHashVariable, ) -> BoolVariable; - fn ensure_if_next_epoch_contains_next_bps( + fn ensure_if_next_epoch_contains_next_bps( &mut self, head: &HeaderVariable, epoch_id: &CryptoHashVariable, - next_bps: &BpsArr, + next_bps: &Validators, ) -> BoolVariable; fn validate_signatures( &mut self, approvals: &BpsApprovals, - bps: &BpsArr, + bps: &Validators, approval_message: ApprovalMessage, ) -> StakeInfoVariable; @@ -102,14 +102,14 @@ impl, const D: usize> Ensure for CircuitBuilder( &mut self, head: &HeaderVariable, epoch_id: &CryptoHashVariable, - next_bps: &BpsArr, + next_bps: &Validators, ) -> BoolVariable { let is_next_epoch = self.is_equal(head.inner_lite.next_epoch_id, *epoch_id); - let is_not_empty = self.constant(next_bps.len() > 0); + let is_not_empty = self.constant(next_bps.inner.len() > 0); let ok_anyway = self._true(); self.select(is_next_epoch, is_not_empty, ok_anyway) } @@ -117,12 +117,12 @@ impl, const D: usize> Ensure for CircuitBuilder( &mut self, approvals_after_next: &BpsApprovals, - epoch_bps: &BpsArr, + epoch_bps: &Validators, approval_message: ApprovalMessage, ) -> StakeInfoVariable { assert_eq!(approvals_after_next.is_active.len(), LEN); assert_eq!(approvals_after_next.signatures.len(), LEN); - assert_eq!(epoch_bps.data.len(), LEN); + assert_eq!(epoch_bps.inner.len(), LEN); let messages = [approval_message; LEN]; @@ -135,7 +135,7 @@ impl, const D: usize> Ensure for CircuitBuilder, const D: usize> Ensure for CircuitBuilder, const D: usize> { - fn sync( + fn sync( &mut self, head: &HeaderVariable, - epoch_bps: &BpsArr, - next_block: &BlockVariable, + epoch_bps: &Validators, + next_block: &BlockVariable, ) -> HeaderVariable; - fn reconstruct_approval_message(&mut self, next_block: &BlockVariable) -> ApprovalMessage; + fn reconstruct_approval_message( + &mut self, + next_block: &BlockVariable, + ) -> ApprovalMessage; } impl, const D: usize> Sync for CircuitBuilder { - fn sync( + fn sync( &mut self, head: &HeaderVariable, - epoch_bps: &BpsArr, - next_block: &BlockVariable, + epoch_bps: &Validators, + next_block: &BlockVariable, ) -> HeaderVariable { let not_verified = self.ensure_not_already_verified(head, &next_block.header.inner_lite.height); @@ -300,12 +303,15 @@ impl, const D: usize> Sync for CircuitBuilder &next_block.next_bps_hash, ); self.assertx(bps_valid); - assert!(next_block.next_bps.len() == NUM_BLOCK_PRODUCER_SEATS); + assert!(next_block.next_bps.inner.len() == NUM_BLOCK_PRODUCER_SEATS); next_block.header.to_owned() } - fn reconstruct_approval_message(&mut self, next_block: &BlockVariable) -> ApprovalMessage { + fn reconstruct_approval_message( + &mut self, + next_block: &BlockVariable, + ) -> ApprovalMessage { let next_header_hash = next_block.header.hash(self); let next_block_hash = self.curta_sha256_pair(next_block.next_block_inner_hash, next_header_hash); @@ -482,7 +488,7 @@ mod tests { let define = |builder: &mut B| { let header = builder.read::(); - let next_bps = builder.read::>(); + let next_bps = builder.read::(); let current = builder.ensure_epoch_is_current_or_next(&header, &header.inner_lite.epoch_id); @@ -505,7 +511,7 @@ mod tests { }; let writer = |input: &mut PI| { input.write::(header.clone().into()); - input.write::>(bps_to_variable(Some(bps))); + input.write::(ValidatorsVariableValue::from_iter(bps)); }; let assertions = |mut output: PO| { assert!(output.read::(), "epoch is current"); @@ -520,12 +526,12 @@ mod tests { fn test_reconstruct_approval_msg() { let (_, _, next_block) = test_state(); let define = |builder: &mut B| { - let next_block = builder.read::(); + let next_block = builder.read::>(); let os = builder.reconstruct_approval_message(&next_block); builder.write::(os); }; let writer = |input: &mut PI| { - input.write::(next_block.clone().into()); + input.write::>(next_block.clone().into()); }; let assertions = |mut output: PO| { let created = output.read::(); @@ -539,7 +545,7 @@ mod tests { fn test_raw_le_bytes() { let (_, _, next_block) = test_state(); let define = |builder: &mut B| { - let next_block = builder.read::(); + let next_block = builder.read::>(); let mut bytes = vec![]; for target in next_block.header.inner_lite.height.targets() { @@ -558,7 +564,7 @@ mod tests { builder.write::>(BytesVariable(bytes.try_into().unwrap())); }; let writer = |input: &mut PI| { - input.write::(next_block.clone().into()); + input.write::>(next_block.clone().into()); }; let assertions = |mut output: PO| { let bytes = output.read::>(); @@ -576,6 +582,7 @@ mod tests { /// TODO: CI for only beefy tests #[cfg(test)] mod beefy_tests { + use near_light_client_primitives::NUM_BLOCK_PRODUCER_SEATS; use serial_test::serial; use crate::{ @@ -593,7 +600,7 @@ mod beefy_tests { let define = |builder: &mut B| { let header = builder.read::(); - let next_bps = builder.read::>(); + let next_bps = builder.read::(); let current = builder.ensure_epoch_is_current_or_next(&header, &header.inner_lite.epoch_id); @@ -616,7 +623,7 @@ mod beefy_tests { }; let writer = |input: &mut PI| { input.write::(header.clone().into()); - input.write::>(bps_to_variable(Some(bps))); + input.write::(ValidatorsVariableValue::from_iter(bps)); }; let assertions = |mut output: PO| { assert!(output.read::(), "epoch is current"); @@ -662,15 +669,17 @@ mod beefy_tests { let define = |builder: &mut B| { let head = builder.read::(); - let bps = builder.read::>(); - let next_block = builder.read::(); + let bps = builder.read::(); + let next_block = builder.read::>(); let synced = builder.sync(&head, &bps, &next_block); builder.write::(synced); }; let writer = |input: &mut PI| { input.write::(head.into()); - input.write::>(bps_to_variable(Some(next_bps))); - input.write::(next_block.clone().into()); + input.write::>( + ValidatorsVariableValue::from_iter(next_bps), + ); + input.write::>(next_block.clone().into()); }; let assertions = |mut output: PO| { let header = output.read::(); @@ -686,18 +695,17 @@ mod beefy_tests { fn beefy_builder_test_bounded_signatures() { let (_, bps, next_block) = test_state(); const BPS_AMT: usize = 15; + let r = 0..BPS_AMT; let define = |builder: &mut B| { - let bps = builder.read::>(); - let next_block = builder.read::(); + let bps = builder.read::>(); + let next_block = builder.read::>(); let next_block_approvals = BpsApprovals { - signatures: next_block.approvals_after_next.signatures[0..BPS_AMT] - .to_vec() - .into(), - is_active: next_block.approvals_after_next.is_active[0..BPS_AMT] + signatures: next_block.approvals_after_next.signatures[r.clone()] .to_vec() .into(), + is_active: next_block.approvals_after_next.is_active[r].to_vec().into(), }; let msg = builder.reconstruct_approval_message(&next_block); @@ -705,10 +713,8 @@ mod beefy_tests { builder.validate_signatures(&next_block_approvals, &bps, msg); }; let writer = |input: &mut PI| { - input.write::>( - bps_to_variable(Some(bps.clone()))[0..BPS_AMT].into(), - ); - input.write::(next_block.into()); + input.write::>(bps.into_iter().collect()); + input.write::>(next_block.into()); }; let assertions = |mut _output: PO| {}; builder_suite(define, writer, assertions); diff --git a/nearx/src/hint.rs b/nearx/src/hint.rs index 34b607a..1df5c1e 100644 --- a/nearx/src/hint.rs +++ b/nearx/src/hint.rs @@ -2,6 +2,7 @@ use std::{collections::HashMap, marker::PhantomData}; use async_trait::async_trait; use log::debug; +use near_light_client_primitives::NUM_BLOCK_PRODUCER_SEATS; use near_light_client_protocol::{prelude::CryptoHash, Proof}; use near_light_client_rpc::{prelude::GetProof, LightClientRpc, NearRpcClient, Network}; use plonky2x::{ @@ -11,9 +12,9 @@ use plonky2x::{ use serde::{Deserialize, Serialize}; use crate::variables::{ - bps_to_variable, normalise_account_id, BlockVariable, BlockVariableValue, BpsArr, - CryptoHashVariable, HeaderVariable, ProofVariable, TransactionOrReceiptIdVariable, - ValidatorStakeVariable, + normalise_account_id, BlockVariable, BlockVariableValue, CryptoHashVariable, HeaderVariable, + ProofVariable, TransactionOrReceiptIdVariable, Validators, ValidatorsVariable, + ValidatorsVariableValue, }; #[derive(Debug, Clone, Deserialize, Serialize)] @@ -36,21 +37,21 @@ impl, const D: usize> AsyncHint for FetchNextHeaderI .expect("Failed to fetch header") .expect("Expected a header"); - output_stream.write_value::(next.into()); + output_stream.write_value::>(next.into()); } } impl FetchNextHeaderInputs { - pub fn fetch, const D: usize>( + pub fn fetch, const D: usize, const LEN: usize>( &self, b: &mut CircuitBuilder, hash: &CryptoHashVariable, - ) -> Option { + ) -> Option> { let mut input_stream = VariableStream::new(); input_stream.write::(hash); let output_stream = b.async_hint(input_stream, self.clone()); - Some(output_stream.read::(b)) + Some(output_stream.read::>(b)) } } @@ -101,36 +102,36 @@ pub enum Fetch { }, } -pub enum Inputs { +pub enum Inputs { Sync { header: HeaderVariable, - bps: BpsArr, - next_block: BlockVariable, + bps: Validators, + next_block: BlockVariable, }, } -impl Inputs { +impl Inputs { fn validate, const D: usize>(&self, b: &mut CircuitBuilder) { - // TODO: validate based on inputs + // TODO: validate based on inputs, here is where we might entrust + // untrusted headers and such } } #[derive(Debug, Clone, Deserialize, Serialize)] -pub struct InputFetcher, const D: usize, const NETWORK: usize>( - PhantomData, +pub struct InputFetcher, const D: usize, const N: usize>( + (Network, PhantomData<(L)>), ); #[async_trait] -impl, const D: usize, const NETWORK: usize> AsyncHint - for InputFetcher +impl, const D: usize, const N: usize> AsyncHint + for InputFetcher { async fn hint( &self, input_stream: &mut ValueStream, output_stream: &mut ValueStream, ) { - let network: Network = NETWORK.into(); - let client = NearRpcClient::new(&network.into()); + let client = NearRpcClient::new(&self.0 .0.clone().into()); let marker: L::Field = input_stream.read_value::(); let marker = marker.to_canonical_u64(); @@ -158,13 +159,9 @@ impl, const D: usize, const NETWORK: usize> AsyncHint(header.into()); - output_stream.write_value::>(bps_to_variable(bps)); - output_stream.write_value::(next_block.into()); - let header_var = header.into(); - let bps_var = bps_to_variable(bps); - let next_block_var: BlockVariableValue<_> = next_block.into(); + let bps_var = ValidatorsVariableValue::from_iter(bps.unwrap()); + let next_block_var: BlockVariableValue = next_block.into(); use ethers::utils::hex; next_block_var @@ -176,23 +173,24 @@ impl, const D: usize, const NETWORK: usize> AsyncHint(header_var); - output_stream.write_value::>(bps_var); - output_stream.write_value::(next_block_var); + // TODO: fix the bounding issue here + output_stream.write_value::>(bps_var); + output_stream.write_value::>(next_block_var); } _ => panic!("Invalid marker"), } } } -impl, const D: usize, const NETWORK: usize> InputFetcher { - pub fn fetch(&self, b: &mut CircuitBuilder, args: &Fetch) -> Inputs { +impl, const D: usize, const N: usize> InputFetcher { + pub fn fetch(&self, b: &mut CircuitBuilder, args: &Fetch) -> Inputs { let mut input_stream = VariableStream::new(); match args { Fetch::Sync { @@ -207,8 +205,8 @@ impl, const D: usize, const NETWORK: usize> InputFetcher { let header = output_stream.read::(b); - let bps = output_stream.read::>(b); - let next_block = output_stream.read::(b); + let bps = output_stream.read::>(b); + let next_block = output_stream.read::>(b); Inputs::Sync { header, bps, @@ -221,13 +219,9 @@ impl, const D: usize, const NETWORK: usize> InputFetcher, untrusted_header_hash: &CryptoHashVariable, - ) -> ( - HeaderVariable, - BpsArr, - BlockVariable, - ) { - let fetch_header = FetchHeaderInputs(NETWORK.into()); - let fetch_next_header = FetchNextHeaderInputs(NETWORK.into()); + ) -> (HeaderVariable, Validators, BlockVariable) { + let fetch_header = FetchHeaderInputs(self.0 .0.into()); + let fetch_next_header = FetchNextHeaderInputs(self.0 .0.into()); let header = fetch_header.fetch(b, &untrusted_header_hash); let bps = fetch_next_header @@ -361,24 +355,24 @@ mod tests { let header = b.read::(); let hash = header.hash(b); let next_block = FetchNextHeaderInputs(Network::Mainnet).fetch(b, &hash); - b.write::(next_block.unwrap()); + b.write::>(next_block.unwrap()); let header = b.read::(); let hash = header.hash(b); let next_block = FetchNextHeaderInputs(Network::Testnet).fetch(b, &hash); - b.write::(next_block.unwrap()); + b.write::>(next_block.unwrap()); }; let writer = |input: &mut PI| { input.write::(main_h.into()); input.write::(test_h.into()); }; let assertions = |mut output: PO| { - let inputs = output.read::(); - let nbh: BlockVariableValue = main_nb.into(); + let inputs = output.read::>(); + let nbh: BlockVariableValue = main_nb.into(); pretty_assertions::assert_eq!(format!("{:#?}", inputs), format!("{:#?}", nbh)); - let inputs = output.read::(); - let nbh: BlockVariableValue = test_nb.into(); + let inputs = output.read::>(); + let nbh: BlockVariableValue = test_nb.into(); pretty_assertions::assert_eq!(format!("{:#?}", inputs), format!("{:#?}", nbh)); }; builder_suite(define, writer, assertions); @@ -389,7 +383,8 @@ mod tests { let problem_hash = bytes32!("0xeff7dccf304315aa520ad7e704062a8b8deadc5c0906e7e16d7305067a72a57e"); //let problem_hash = testnet_state().0.hash().0; - let fetcher = InputFetcher::(Default::default()); + const AMT: usize = 50; + let fetcher = InputFetcher::(Default::default()); let define = |b: &mut B| { let trusted_header_hash = b.read::(); @@ -407,8 +402,8 @@ mod tests { // TODO: validate b.write::(header.clone()); - b.write::>(bps.clone()); - b.write::(next_block.clone()); + b.write::>(bps.clone()); + b.write::>(next_block.clone()); let approval = b.reconstruct_approval_message(&next_block); b.validate_signatures(&next_block.approvals_after_next, &bps, approval); @@ -423,21 +418,25 @@ mod tests { }; builder_suite(define, writer, assertions); - let head = serde_json::from_str::( - std::fs::read_to_string("header.json").unwrap().as_str(), - ) - .unwrap(); - let epoch_bps: Vec = serde_json::from_str::>( - std::fs::read_to_string("bps.json").unwrap().as_str(), - ) - .unwrap() - .into_iter() - .map(ValidatorStakeView::into) - .collect(); - let next_block = serde_json::from_str::( - std::fs::read_to_string("next_block.json").unwrap().as_str(), - ) - .unwrap(); - near_light_client_protocol::Protocol::sync(&head, &epoch_bps, next_block).unwrap(); + // TODO: everything doing anything sync wise should also verify with the + // data in the base protocol, if possible + // let head = serde_json::from_str::( + // std::fs::read_to_string("header.json").unwrap().as_str(), + // ) + // .unwrap(); + // let epoch_bps: Vec = + // serde_json::from_str::>( + // std::fs::read_to_string("bps.json").unwrap().as_str(), + // ) + // .unwrap() + // .into_iter() + // .map(ValidatorStakeView::into) + // .collect(); + // let next_block = serde_json::from_str::( + // std::fs::read_to_string("next_block.json").unwrap().as_str(), + // ) + // .unwrap(); + // near_light_client_protocol::Protocol::sync(&head, &epoch_bps, + // next_block).unwrap(); } } diff --git a/nearx/src/variables.rs b/nearx/src/variables.rs index f7a1737..9bf0984 100644 --- a/nearx/src/variables.rs +++ b/nearx/src/variables.rs @@ -30,6 +30,7 @@ use crate::merkle::MerklePathVariable; /// Type for omitting the size across the codebase for arrays that are the same /// size as BPS +pub(crate) type Validators = ValidatorsVariable; pub(crate) type BpsArr = ArrayVariable; pub type CryptoHashVariable = Bytes32Variable; @@ -312,15 +313,15 @@ impl, const D: usize> Hint for EncodeInner { } #[derive(CircuitVariable, Clone, Debug)] -pub struct BlockVariable { +pub struct BlockVariable { pub header: HeaderVariable, pub next_block_inner_hash: CryptoHashVariable, - pub next_bps: BpsArr, - pub approvals_after_next: BpsApprovals, + pub next_bps: Validators, + pub approvals_after_next: BpsApprovals, pub next_bps_hash: CryptoHashVariable, } -impl From for BlockVariableValue { +impl From for BlockVariableValue { fn from(block: LightClientBlockView) -> Self { let next_bps_hash = block .next_bps @@ -333,19 +334,16 @@ impl From for BlockVariableValue { let variable = Self { next_block_inner_hash: block.next_block_inner_hash.0.into(), header: block.clone().into(), - next_bps: bps_to_variable(block.next_bps), + next_bps: block + .next_bps + .map(|bps| bps.into_iter().collect()) + .unwrap_or_default(), approvals_after_next: block.approvals_after_next.into(), next_bps_hash, }; - assert_eq!(variable.next_bps.len(), NUM_BLOCK_PRODUCER_SEATS); - assert_eq!( - variable.approvals_after_next.is_active.len(), - NUM_BLOCK_PRODUCER_SEATS - ); - assert_eq!( - variable.approvals_after_next.signatures.len(), - NUM_BLOCK_PRODUCER_SEATS - ); + assert_eq!(variable.next_bps.inner.len(), LEN); + assert_eq!(variable.approvals_after_next.is_active.len(), LEN); + assert_eq!(variable.approvals_after_next.signatures.len(), LEN); variable } } @@ -365,7 +363,6 @@ impl From>>> .into_iter() .take(AMT) .map(|s| { - // FIXME: heres the problem, active signature, no bps let is_active = s.is_some(); let s: SignatureVariableValue = s.into(); @@ -380,24 +377,6 @@ impl From>>> } } -// TODO: tie this in with the From implementation and newtype it -pub(crate) fn bps_to_variable>( - next_bps: Option>, -) -> Vec> { - next_bps - .map(|next_bps| { - let mut bps = next_bps - .into_iter() - .take(NUM_BLOCK_PRODUCER_SEATS) - .map(Into::::into) - .map(Into::>::into) - .collect_vec(); - bps.resize(NUM_BLOCK_PRODUCER_SEATS, Default::default()); - bps - }) - .unwrap_or_else(|| vec![Default::default(); NUM_BLOCK_PRODUCER_SEATS]) -} - #[derive(CircuitVariable, Clone, Debug)] pub struct ValidatorStakeVariable { // TODO[optimisation]: can optimise this out by witnessing the hash @@ -421,8 +400,8 @@ impl From for ValidatorStakeVariableValue { impl From> for ValidatorStakeView { fn from(val: ValidatorStakeVariableValue) -> Self { - let account_id = normalise_account_id::(&val.account_id); let public_key = PublicKey::ED25519(ED25519PublicKey(val.public_key.0)); + let account_id = normalise_account_id::(&val.account_id); ValidatorStakeView::V1(ValidatorStakeViewV1 { account_id, public_key, @@ -431,6 +410,35 @@ impl From> for ValidatorStakeView { } } +#[derive(CircuitVariable, Clone, Debug)] +pub struct ValidatorsVariable { + pub(crate) inner: BpsArr, +} + +impl, const N: usize> FromIterator + for ValidatorsVariableValue +{ + fn from_iter>(iter: T) -> Self { + let mut bps = iter + .into_iter() + .take(N) + .map(Into::::into) + .map(Into::::into) + .map(Into::>::into) + .collect_vec(); + bps.resize(N, Default::default()); + Self { inner: bps } + } +} + +impl Default for ValidatorsVariableValue { + fn default() -> Self { + Self { + inner: vec![Default::default(); N], + } + } +} + impl Default for ValidatorStakeVariableValue { fn default() -> Self { let account_id: [u8; AccountId::MAX_LEN] = [ACCOUNT_DATA_SEPARATOR; AccountId::MAX_LEN]; @@ -566,12 +574,13 @@ pub struct HashBpsInputs; impl, const D: usize> Hint for HashBpsInputs { fn hint(&self, input_stream: &mut ValueStream, output_stream: &mut ValueStream) { - let bps = input_stream.read_value::>(); + let bps = input_stream.read_value::(); // TODO: if we use a bitmask we wont need default checks let default_validator = ValidatorStakeVariableValue::<>::Field>::default(); let bps = bps + .inner .into_iter() .filter(|x| x.account_id != default_validator.account_id) .map(Into::::into) @@ -590,10 +599,10 @@ impl HashBpsInputs { pub fn hash, const D: usize>( self, b: &mut CircuitBuilder, - bps: &BpsArr, + bps: &Validators, ) -> CryptoHashVariable { let mut input_stream = VariableStream::new(); - input_stream.write::>(bps); + input_stream.write::(bps); let output_stream = b.hint(input_stream, self); output_stream.read::(b)