diff --git a/circuits/plonky2x/Cargo.toml b/circuits/plonky2x/Cargo.toml index 08be466..126d07f 100644 --- a/circuits/plonky2x/Cargo.toml +++ b/circuits/plonky2x/Cargo.toml @@ -9,6 +9,7 @@ version = "0.1.0" async-trait.workspace = true borsh.workspace = true ethers = "2.0.11" +hex.workspace = true log.workspace = true pretty_assertions = "1.4.0" serde.workspace = true @@ -21,7 +22,6 @@ near-light-client-rpc.workspace = true [dev-dependencies] borsh.workspace = true -hex.workspace = true near-primitives.workspace = true pretty_env_logger.workspace = true serde_json.workspace = true diff --git a/circuits/plonky2x/src/builder.rs b/circuits/plonky2x/src/builder.rs index c770c3b..185afa4 100644 --- a/circuits/plonky2x/src/builder.rs +++ b/circuits/plonky2x/src/builder.rs @@ -266,11 +266,12 @@ impl, const D: usize> Sync for CircuitBuilder let d = self.ensure_stake_is_sufficient(&stake); self.assertx(d); + // TODO: might not need this now, also the logic is wrong because nbps is always >0 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), + Some(&next_block.next_bps_hash), ); self.assertx(e); assert!(next_block.next_bps.len() == NUM_BLOCK_PRODUCER_SEATS); diff --git a/circuits/plonky2x/src/hint.rs b/circuits/plonky2x/src/hint.rs index 21b5c37..5f609dc 100644 --- a/circuits/plonky2x/src/hint.rs +++ b/circuits/plonky2x/src/hint.rs @@ -52,112 +52,6 @@ impl FetchNextHeaderInputs { } } -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct FetchBatchInputs(pub Network); - -#[async_trait] -impl, const D: usize, const AMT: usize> AsyncHint - for FetchBatchInputs -{ - async fn hint( - &self, - input_stream: &mut ValueStream, - output_stream: &mut ValueStream, - ) { - let client = NearRpcClient::new(self.0.clone()); - let trusted_head = input_stream.read_value::(); - let mut head_hash = input_stream.read_value::().0; - - let trusted_bps = client - .fetch_epoch_bps(&CryptoHash(trusted_head.inner_lite.epoch_id.0)) - .await - .unwrap(); - - let mut headers = vec![trusted_head.clone()]; - let mut bps = vec![trusted_bps.clone()]; - let mut blocks = vec![]; - - for i in 0..AMT { - let next = client - .fetch_latest_header(&CryptoHash(head_hash)) - .await - .unwrap() - .ok_or_else(|| anyhow!("No header found")) - .unwrap(); - blocks.push(next.clone()); - - let new_head = LightClientBlockLiteView { - prev_block_hash: next.prev_block_hash, - inner_rest_hash: next.inner_rest_hash, - inner_lite: next.inner_lite, - }; - head_hash = new_head.hash().0; - - if i < AMT - 1 { - headers.push(new_head.into()); - bps.push(next.next_bps.unwrap()); - } - } - - assert_eq!(headers.len(), AMT); - assert_eq!(bps.len(), AMT); - assert_eq!(blocks.len(), AMT); - - for h in headers { - log::debug!("headers: {:#?}", h); - output_stream.write_value::(h); - } - - for b in bps { - log::debug!("bps: {:?}", b.len()); - output_stream - .write_value::>(bps_to_variable(Some(b)).into()); - } - - for b in blocks { - log::debug!("blocks: {:#?}", b.inner_lite); - output_stream.write_value::(b.into()); - } - } -} - -#[derive(CircuitVariable, Debug, Clone)] -pub struct HeaderInput { - pub header: HeaderVariable, - pub bps: BpsArr, - pub block: BlockVariable, -} - -impl FetchBatchInputs { - pub fn write_inputs(h: &HeaderVariable, hash: &CryptoHashVariable) -> VariableStream { - let mut input_stream = VariableStream::new(); - input_stream.write::(h); - input_stream.write::(hash); - input_stream - } - - pub fn read_outputs, const D: usize>( - output_stream: &mut OutputVariableStream, - b: &mut CircuitBuilder, - ) -> ( - BpsArr, - ArrayVariable, - ) { - let headers = output_stream.read_vec::(b, AMT); - let bps = output_stream.read_vec::>(b, AMT); - let blocks = output_stream.read_vec::(b, AMT); - - ( - bps[0].clone(), - izip!(headers, bps, blocks) - .into_iter() - .map(|(header, bps, block)| HeaderInput { header, bps, block }) - .collect_vec() - .into(), - ) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/circuits/plonky2x/src/lib.rs b/circuits/plonky2x/src/lib.rs index c8b9caa..bf3d2f3 100644 --- a/circuits/plonky2x/src/lib.rs +++ b/circuits/plonky2x/src/lib.rs @@ -3,7 +3,9 @@ use builder::Sync; use hint::FetchNextHeaderInputs; pub use plonky2x::{self, backend::circuit::Circuit, prelude::*}; -use variables::{BpsArr, HeaderVariable, ValidatorStakeVariable}; +use variables::{ + BpsArr, CryptoHashVariable, HashBpsInputs, HeaderVariable, ValidatorStakeVariable, +}; use variables::{BuildEndorsement, EncodeInner, SyncedVariable}; /// Building blocks injected into the CircuitBuilder @@ -39,15 +41,24 @@ impl Circuit for SyncCircuit { <>::Config as plonky2::plonk::config::GenericConfig>::Hasher: plonky2::plonk::config::AlgebraicHasher<>::Field>, { - // TODO: evm - let head = b.read::(); - let bps = b.read::>(); - - let head_hash = head.hash(b); - let next_block = - FetchNextHeaderInputs(near_light_client_rpc::Network::Testnet).fetch(b, &head_hash); - let synced = b.sync(&head, &bps, &next_block.unwrap()); - b.write::(synced); + let network = near_light_client_rpc::Network::Testnet; + let trusted_head = b.evm_read::(); + + // This is a very interesting cheat to be able to get the BPS for the next epoch + // without the need to store the BPS, we can verify the hash of the BPS in the circuit + let bps = FetchNextHeaderInputs(near_light_client_rpc::Network::Testnet) + .fetch(b, &trusted_head.inner_lite.next_epoch_id) + .unwrap() + .next_bps; + let bps_hash = HashBpsInputs.hash(b, &bps); + b.assert_is_equal(trusted_head.inner_lite.next_bp_hash, bps_hash); + + let head_hash = trusted_head.hash(b); + let next_block = FetchNextHeaderInputs(network).fetch(b, &head_hash).unwrap(); + b.watch(&bps_hash, "calculate_bps_hash"); + + let synced = b.sync(&trusted_head, &bps, &next_block); + b.evm_write::(synced.new_head); } fn register_generators, const D: usize>(registry: &mut HintRegistry) @@ -58,6 +69,7 @@ impl Circuit for SyncCircuit { registry.register_async_hint::(); registry.register_hint::(); registry.register_hint::(); + registry.register_hint::(); } } @@ -66,31 +78,60 @@ impl Circuit for SyncCircuit { mod beefy_tests { use super::*; use crate::{ - test_utils::{builder_suite, test_state, B, PI, PO}, + test_utils::{builder_suite, testnet_state, B, PI, PO}, variables::bps_to_variable, }; + use ::test_utils::CryptoHash; + use near_light_client_protocol::{prelude::Itertools, ValidatorStake, ValidatorStakeView}; + use near_light_client_rpc::{LightClientRpc, NearRpcClient}; + use near_primitives::types::AccountId; use serial_test::serial; #[test] #[serial] fn beefy_test_sync_e2e() { const SYNC_AMT: usize = 1; - let (header, bps, _nb) = test_state(); + let (header, _, _) = testnet_state(); let define = |b: &mut B| { b.set_debug(); SyncCircuit::::define(b); }; let writer = |input: &mut PI| { - input.write::(header.into()); - input.write::>(bps_to_variable(Some(bps))); + input.evm_write::(header.into()); }; let assertions = |mut output: PO| { - println!("{:#?}", output.read::().new_head); + println!("{:#?}", output.evm_read::()); }; builder_suite(define, writer, assertions); } + fn account_ids>(bps: Vec) -> Vec { + bps.into_iter() + .map(Into::::into) + .map(|x| x.account_id().clone()) + .collect_vec() + } + + #[tokio::test] + async fn test_epoch_madness() { + use pretty_assertions::assert_eq; + let c = NearRpcClient::new(near_light_client_rpc::Network::Testnet); + let (h, bps, n) = testnet_state(); + println!("{:#?}", h); + + assert_eq!(h.inner_lite.next_bp_hash, CryptoHash::hash_borsh(&bps)); + let bps = account_ids(bps); + + let next_epoch = c.fetch_latest_header(&h.inner_lite.next_epoch_id).await; + let ne_nbps = account_ids(next_epoch.unwrap().unwrap().next_bps.unwrap()); + assert_eq!(ne_nbps, bps); + + let nb_epoch = c.fetch_latest_header(&n.inner_lite.epoch_id).await; + let nb_nbps = account_ids(nb_epoch.unwrap().unwrap().next_bps.unwrap()); + assert_eq!(nb_nbps, bps); + } + #[test] fn test_prove() {} diff --git a/circuits/plonky2x/src/test_utils.rs b/circuits/plonky2x/src/test_utils.rs index db619c2..13561a3 100644 --- a/circuits/plonky2x/src/test_utils.rs +++ b/circuits/plonky2x/src/test_utils.rs @@ -28,6 +28,12 @@ pub fn builder_suite( writer(&mut inputs); let (proof, output) = circuit.prove(&inputs); + //proof.public_inputs + std::fs::write( + "input3.bin", + serde_json::to_string(&proof.public_inputs).unwrap(), + ) + .unwrap(); assertions(output.clone()); diff --git a/circuits/plonky2x/src/variables.rs b/circuits/plonky2x/src/variables.rs index 298639f..9408a7a 100644 --- a/circuits/plonky2x/src/variables.rs +++ b/circuits/plonky2x/src/variables.rs @@ -6,13 +6,15 @@ use near_light_client_protocol::{ merkle_util::MerklePath, prelude::AccountId, prelude::CryptoHash, BlockHeaderInnerLiteView, LightClientBlockView, Signature, ValidatorStake, }; -use near_light_client_protocol::{BlockHeaderInnerLite, Proof, StakeInfo, Synced}; +use near_light_client_protocol::{ + ED25519PublicKey, Proof, PublicKey, StakeInfo, Synced, ValidatorStakeView, ValidatorStakeViewV1, +}; use plonky2x::frontend::curta::ec::point::CompressedEdwardsY; +use plonky2x::frontend::curta::ec::point::CompressedEdwardsYVariable; use plonky2x::frontend::ecc::curve25519::ed25519::eddsa::EDDSASignatureVariable; use plonky2x::frontend::ecc::curve25519::ed25519::eddsa::EDDSASignatureVariableValue; use plonky2x::frontend::hint::simple::hint::Hint; use plonky2x::frontend::vars::EvmVariable; -use plonky2x::frontend::{curta::ec::point::CompressedEdwardsYVariable, uint::Uint}; use plonky2x::prelude::*; use pretty_assertions::assert_eq; use serde::{Deserialize, Serialize}; @@ -93,6 +95,61 @@ impl HeaderVariable { hash } } +impl EvmVariable for HeaderVariable { + fn encode, const D: usize>( + &self, + builder: &mut CircuitBuilder, + ) -> Vec { + let mut bytes = vec![]; + bytes.extend_from_slice(&self.prev_block_hash.encode(builder)); + bytes.extend_from_slice(&self.inner_rest_hash.encode(builder)); + bytes.extend_from_slice(&self.inner_lite.encode(builder)); + assert!(bytes.len() == 64 + INNER_ENCODED_LEN); + log::debug!("encoded header {:?}", bytes.len()); + bytes + } + + fn decode, const D: usize>( + builder: &mut CircuitBuilder, + bytes: &[ByteVariable], + ) -> Self { + assert!(bytes.len() == 64 + INNER_ENCODED_LEN); + let prev_block_hash = CryptoHashVariable::decode(builder, &bytes[0..32]); + let inner_rest_hash = CryptoHashVariable::decode(builder, &bytes[32..64]); + let inner_lite = HeaderInnerVariable::decode(builder, &bytes[64..64 + INNER_ENCODED_LEN]); + Self { + prev_block_hash, + inner_rest_hash, + inner_lite, + } + } + + fn encode_value(value: Self::ValueType) -> Vec { + let mut bytes = vec![]; + bytes.extend_from_slice(&CryptoHashVariable::encode_value::( + value.prev_block_hash, + )); + bytes.extend_from_slice(&CryptoHashVariable::encode_value::( + value.inner_rest_hash, + )); + bytes.extend_from_slice(&HeaderInnerVariable::encode_value::(value.inner_lite)); + assert!(bytes.len() == 64 + INNER_ENCODED_LEN); + log::debug!("encoded header value {:?}", bytes.len()); + bytes + } + + fn decode_value(bytes: &[u8]) -> Self::ValueType { + assert!(bytes.len() == 64 + INNER_ENCODED_LEN); + let prev_block_hash = CryptoHashVariable::decode_value::(&bytes[0..32]); + let inner_rest_hash = CryptoHashVariable::decode_value::(&bytes[32..64]); + let inner_lite = HeaderInnerVariable::decode_value::(&bytes[64..]); + Self::ValueType { + prev_block_hash, + inner_rest_hash, + inner_lite, + } + } +} #[derive(CircuitVariable, Clone, Debug)] pub struct HeaderInnerVariable { @@ -125,9 +182,8 @@ impl From for HeaderInnerVariableValue, const D: usize>( + pub(crate) fn encode_borsh, const D: usize>( &self, b: &mut CircuitBuilder, ) -> BytesVariable { @@ -143,17 +199,104 @@ impl HeaderInnerVariable { input_stream.write(&self.block_merkle_root); let output_bytes = b.hint(input_stream, EncodeInner); - let bytes = output_bytes.read::>(b); + let bytes = output_bytes.read::>(b); bytes } pub(crate) fn hash, const D: usize>( &self, b: &mut CircuitBuilder, ) -> CryptoHashVariable { - let bytes = self.encode(b); + let bytes = self.encode_borsh(b); b.curta_sha256(&bytes.0) } } +impl EvmVariable for HeaderInnerVariable { + fn encode, const D: usize>( + &self, + builder: &mut CircuitBuilder, + ) -> Vec { + let mut bytes = vec![]; + bytes.extend_from_slice(&self.height.encode(builder)); + bytes.extend_from_slice(&self.epoch_id.encode(builder)); + bytes.extend_from_slice(&self.next_epoch_id.encode(builder)); + bytes.extend_from_slice(&self.prev_state_root.encode(builder)); + bytes.extend_from_slice(&self.outcome_root.encode(builder)); + bytes.extend_from_slice(&self.timestamp.encode(builder)); + bytes.extend_from_slice(&self.next_bp_hash.encode(builder)); + bytes.extend_from_slice(&self.block_merkle_root.encode(builder)); + log::debug!("encoded inner: {:?}", bytes.len()); + assert_eq!(bytes.len(), INNER_ENCODED_LEN); + bytes + } + + fn decode, const D: usize>( + builder: &mut CircuitBuilder, + bytes: &[ByteVariable], + ) -> Self { + assert_eq!(bytes.len(), INNER_ENCODED_LEN); + let height = U64Variable::decode(builder, &bytes[0..8]); + let epoch_id = CryptoHashVariable::decode(builder, &bytes[8..40]); + let next_epoch_id = CryptoHashVariable::decode(builder, &bytes[40..72]); + let prev_state_root = CryptoHashVariable::decode(builder, &bytes[72..104]); + let outcome_root = CryptoHashVariable::decode(builder, &bytes[104..136]); + let timestamp = U64Variable::decode(builder, &bytes[136..144]); + let next_bp_hash = CryptoHashVariable::decode(builder, &bytes[144..176]); + let block_merkle_root = CryptoHashVariable::decode(builder, &bytes[176..INNER_ENCODED_LEN]); + Self { + height, + epoch_id, + next_epoch_id, + prev_state_root, + outcome_root, + timestamp, + next_bp_hash, + block_merkle_root, + } + } + + fn encode_value(value: Self::ValueType) -> Vec { + let mut bytes = vec![]; + bytes.extend_from_slice(&U64Variable::encode_value::(value.height)); + bytes.extend_from_slice(&CryptoHashVariable::encode_value::(value.epoch_id)); + bytes.extend_from_slice(&CryptoHashVariable::encode_value::(value.next_epoch_id)); + bytes.extend_from_slice(&CryptoHashVariable::encode_value::( + value.prev_state_root, + )); + bytes.extend_from_slice(&CryptoHashVariable::encode_value::(value.outcome_root)); + bytes.extend_from_slice(&U64Variable::encode_value::(value.timestamp)); + bytes.extend_from_slice(&CryptoHashVariable::encode_value::(value.next_bp_hash)); + bytes.extend_from_slice(&CryptoHashVariable::encode_value::( + value.block_merkle_root, + )); + log::debug!("encoded inner value: {:?}", bytes.len()); + assert_eq!(bytes.len(), INNER_ENCODED_LEN); + bytes + } + + fn decode_value(bytes: &[u8]) -> Self::ValueType { + assert_eq!(bytes.len(), INNER_ENCODED_LEN); + let height = U64Variable::decode_value::(&bytes[0..8]); + let epoch_id = CryptoHashVariable::decode_value::(&bytes[8..40]); + let next_epoch_id = CryptoHashVariable::decode_value::(&bytes[40..72]); + let prev_state_root = CryptoHashVariable::decode_value::(&bytes[72..104]); + let outcome_root = CryptoHashVariable::decode_value::(&bytes[104..136]); + let timestamp = U64Variable::decode_value::(&bytes[136..144]); + let next_bp_hash = CryptoHashVariable::decode_value::(&bytes[144..176]); + let block_merkle_root = + CryptoHashVariable::decode_value::(&bytes[176..INNER_ENCODED_LEN]); + + Self::ValueType { + height, + epoch_id, + next_epoch_id, + prev_state_root, + outcome_root, + timestamp, + next_bp_hash, + block_merkle_root, + } + } +} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EncodeInner; @@ -197,12 +340,12 @@ pub struct BlockVariable { pub next_block_inner_hash: CryptoHashVariable, pub next_bps: BpsArr, pub approvals_after_next: BpsApprovals, - pub next_bp_hash: CryptoHashVariable, + pub next_bps_hash: CryptoHashVariable, } impl From for BlockVariableValue { fn from(block: LightClientBlockView) -> Self { - let next_bp_hash = block + let next_bps_hash = block .next_bps .as_ref() .map(|bps| CryptoHash::hash_borsh(bps)) @@ -215,294 +358,10 @@ impl From for BlockVariableValue { header: block.clone().into(), next_bps: bps_to_variable(block.next_bps), approvals_after_next: block.approvals_after_next.into(), - next_bp_hash, + next_bps_hash, } } } -// impl CircuitVariable for BeaconValidatorVariable { -// type ValueType = BeaconValidator; -// -// fn init_unsafe, const D: usize>( -// builder: &mut CircuitBuilder, -// ) -> Self { -// Self { -// pubkey: BLSPubkeyVariable::init_unsafe(builder), -// withdrawal_credentials: Bytes32Variable::init_unsafe(builder), -// effective_balance: U256Variable::init_unsafe(builder), -// slashed: BoolVariable::init_unsafe(builder), -// activation_eligibility_epoch: U256Variable::init_unsafe(builder), -// activation_epoch: U256Variable::init_unsafe(builder), -// exit_epoch: U256Variable::init_unsafe(builder), -// withdrawable_epoch: U256Variable::init_unsafe(builder), -// } -// } -// -// fn nb_elements() -> usize { -// let pubkey = BLSPubkeyVariable::nb_elements(); -// let withdrawal_credentials = Bytes32Variable::nb_elements(); -// let effective_balance = U256Variable::nb_elements(); -// let slashed = BoolVariable::nb_elements(); -// let activation_eligibility_epoch = U256Variable::nb_elements(); -// let activation_epoch = U256Variable::nb_elements(); -// let exit_epoch = U256Variable::nb_elements(); -// let withdrawable_epoch = U256Variable::nb_elements(); -// pubkey -// + withdrawal_credentials -// + effective_balance -// + slashed -// + activation_eligibility_epoch -// + activation_epoch -// + exit_epoch -// + withdrawable_epoch -// } -// -// fn elements(value: Self::ValueType) -> Vec { -// let pubkey = BLSPubkeyVariable::elements(bytes!(value.pubkey)); -// let withdrawal_credentials = -// Bytes32Variable::elements(bytes32!(value.withdrawal_credentials)); -// let effective_balance = U256Variable::elements(value.effective_balance.into()); -// let slashed = BoolVariable::elements(value.slashed); -// let activation_eligibility_epoch = U256Variable::elements( -// value -// .activation_eligibility_epoch -// .parse::() -// .unwrap() -// .into(), -// ); -// let activation_epoch = -// U256Variable::elements(value.activation_epoch.parse::().unwrap().into()); -// let exit_epoch = U256Variable::elements(value.exit_epoch.parse::().unwrap().into()); -// let withdrawable_epoch = -// U256Variable::elements(value.withdrawable_epoch.parse::().unwrap().into()); -// pubkey -// .into_iter() -// .chain(withdrawal_credentials) -// .chain(effective_balance) -// .chain(slashed) -// .chain(activation_eligibility_epoch) -// .chain(activation_epoch) -// .chain(exit_epoch) -// .chain(withdrawable_epoch) -// .collect() -// } -// -// fn from_elements(elements: &[F]) -> Self::ValueType { -// let pubkey = BLSPubkeyVariable::from_elements(&elements[0..384]); -// let withdrawal_credentials = Bytes32Variable::from_elements(&elements[384..640]); -// let effective_balance = U256Variable::from_elements(&elements[640..648]); -// let slashed = BoolVariable::from_elements(&elements[648..649]); -// let activation_eligibility_epoch = U256Variable::from_elements(&elements[649..657]); -// let activation_epoch = U256Variable::from_elements(&elements[657..665]); -// let exit_epoch = U256Variable::from_elements(&elements[665..673]); -// let withdrawable_epoch = U256Variable::from_elements(&elements[673..681]); -// BeaconValidator { -// pubkey: hex!(pubkey), -// withdrawal_credentials: hex!(withdrawal_credentials), -// effective_balance: effective_balance.as_u64(), -// slashed, -// activation_eligibility_epoch: activation_eligibility_epoch.as_u64().to_string(), -// activation_epoch: activation_epoch.as_u64().to_string(), -// exit_epoch: exit_epoch.as_u64().to_string(), -// withdrawable_epoch: withdrawable_epoch.as_u64().to_string(), -// } -// } -// -// fn variables(&self) -> Vec { -// let mut vars = Vec::new(); -// vars.extend(self.pubkey.variables()); -// vars.extend(self.withdrawal_credentials.variables()); -// vars.extend(self.effective_balance.variables()); -// vars.extend(self.slashed.variables()); -// vars.extend(self.activation_eligibility_epoch.variables()); -// vars.extend(self.activation_epoch.variables()); -// vars.extend(self.exit_epoch.variables()); -// vars.extend(self.withdrawable_epoch.variables()); -// vars -// } -// -// fn from_variables_unsafe(variables: &[Variable]) -> Self { -// let pubkey = BLSPubkeyVariable::from_variables_unsafe(&variables[0..384]); -// let withdrawal_credentials = Bytes32Variable::from_variables_unsafe(&variables[384..640]); -// let effective_balance = U256Variable::from_variables_unsafe(&variables[640..648]); -// let slashed = BoolVariable::from_variables_unsafe(&variables[648..649]); -// let activation_eligibility_epoch = -// U256Variable::from_variables_unsafe(&variables[649..657]); -// let activation_epoch = U256Variable::from_variables_unsafe(&variables[657..665]); -// let exit_epoch = U256Variable::from_variables_unsafe(&variables[665..673]); -// let withdrawable_epoch = U256Variable::from_variables_unsafe(&variables[673..681]); -// Self { -// pubkey, -// withdrawal_credentials, -// effective_balance, -// slashed, -// activation_eligibility_epoch, -// activation_epoch, -// exit_epoch, -// withdrawable_epoch, -// } -// } -// -// fn assert_is_valid, const D: usize>( -// &self, -// builder: &mut CircuitBuilder, -// ) { -// self.pubkey.assert_is_valid(builder); -// self.withdrawal_credentials.assert_is_valid(builder); -// self.effective_balance.assert_is_valid(builder); -// self.slashed.assert_is_valid(builder); -// self.activation_eligibility_epoch.assert_is_valid(builder); -// self.activation_epoch.assert_is_valid(builder); -// self.exit_epoch.assert_is_valid(builder); -// self.withdrawable_epoch.assert_is_valid(builder); -// } -// } -// -#[derive(Clone, Debug)] -pub struct BlockVariable2 { - pub header: HeaderVariable, - pub inner_hash: CryptoHashVariable, - pub next_bps: BpsArr, - pub approvals_after_next: BpsApprovals, - pub next_bp_hash: CryptoHashVariable, -} - -impl CircuitVariable for BlockVariable2 { - type ValueType = LightClientBlockView; - - fn init_unsafe, const D: usize>( - builder: &mut CircuitBuilder, - ) -> Self { - Self { - header: HeaderVariable::init_unsafe(builder), - inner_hash: CryptoHashVariable::init_unsafe(builder), - next_bps: BpsArr::::init_unsafe(builder), - approvals_after_next: BpsApprovals::::init_unsafe(builder), - next_bp_hash: CryptoHashVariable::init_unsafe(builder), - } - } - - fn variables(&self) -> Vec { - let mut vars = Vec::new(); - vars.extend(self.header.variables()); - vars.extend(self.inner_hash.variables()); - vars.extend(self.next_bps.variables()); - vars.extend(self.approvals_after_next.variables()); - vars.extend(self.next_bp_hash.variables()); - vars - } - - fn from_variables_unsafe(variables: &[Variable]) -> Self { - todo!() - } - - fn assert_is_valid, const D: usize>( - &self, - builder: &mut CircuitBuilder, - ) { - self.header.assert_is_valid(builder); - self.inner_hash.assert_is_valid(builder); - self.next_bps.assert_is_valid(builder); - self.approvals_after_next.assert_is_valid(builder); - self.next_bp_hash.assert_is_valid(builder); - } - - fn nb_elements() -> usize { - HeaderVariable::nb_elements() - + CryptoHashVariable::nb_elements() - + BpsArr::::nb_elements() - + BpsApprovals::::nb_elements() - + CryptoHashVariable::nb_elements() - } - - fn elements(value: Self::ValueType) -> Vec { - let next_bp_hash = value - .next_bps - .as_ref() - .map(|bps| CryptoHash::hash_borsh(bps)) - .unwrap_or_default() - .0; - - let header = HeaderVariable::elements(value.clone().into()); - let next_block_inner_hash = - CryptoHashVariable::elements(value.next_block_inner_hash.0.into()); - let next_bps = BpsArr::::elements(bps_to_variable(value.next_bps)); - let approvals_after_next = - BpsApprovals::::elements(value.approvals_after_next.into()); - let next_bp_hash = CryptoHashVariable::elements(next_bp_hash.into()); - header - .into_iter() - .chain(next_block_inner_hash) - .chain(next_bps) - .chain(approvals_after_next) - .chain(next_bp_hash) - .collect() - } - - fn from_elements(elements: &[F]) -> Self::ValueType { - let mut start_idx = 0; - let header = HeaderVariable::from_elements( - &elements[start_idx..start_idx + HeaderVariable::nb_elements()], - ); - start_idx += HeaderVariable::nb_elements(); - - let next_block_inner_hash = CryptoHashVariable::from_elements( - &elements[start_idx..start_idx + CryptoHashVariable::nb_elements()], - ); - start_idx += CryptoHashVariable::nb_elements(); - - let next_bps = BpsArr::::from_elements( - &elements[start_idx..start_idx + BpsArr::::nb_elements()], - ); - start_idx += BpsArr::::nb_elements(); - - let approvals_after_next = BpsApprovals::::from_elements( - &elements - [start_idx..start_idx + BpsApprovals::::nb_elements()], - ); - start_idx += BpsApprovals::::nb_elements(); - - let next_bp_hash = CryptoHashVariable::from_elements( - &elements[start_idx..start_idx + CryptoHashVariable::nb_elements()], - ); - - LightClientBlockView { - prev_block_hash: CryptoHash(header.prev_block_hash.into()), - next_block_inner_hash: CryptoHash(next_block_inner_hash.into()), - inner_lite: BlockHeaderInnerLiteView { - height: todo!(), - epoch_id: todo!(), - next_epoch_id: todo!(), - prev_state_root: todo!(), - outcome_root: todo!(), - timestamp: todo!(), - timestamp_nanosec: todo!(), - next_bp_hash: todo!(), - block_merkle_root: todo!(), - }, - inner_rest_hash: todo!(), - next_bps: todo!(), - approvals_after_next: todo!(), - } - } -} - -// impl From> for LightClientBlockView { -// fn from(val: BlockVariableValue) -> Self { -// LightClientBlockView { -// next_block_inner_hash: CryptoHash(val.next_block_inner_hash.0), -// next_bps: if !val.next_bps.is_empty() { -// Some(val.next_bps.into_iter().map(Into::into).collect_vec()) -// } else { -// None -// }, -// approvals_after_next: val.approvals_after_next.into(), -// next_bp_hash: val.next_bp_hash.into(), -// prev_block_hash: todo!(), -// inner_lite: todo!(), -// inner_rest_hash: todo!(), -// } -// } -// } pub(crate) fn bps_to_variable>( next_bps: Option>, @@ -562,12 +421,13 @@ pub struct ValidatorStakeVariable { pub stake: BalanceVariable, } +const ACCOUNT_ID_PADDING_BYTE: u8 = b'#'; impl From for ValidatorStakeVariableValue { fn from(vs: ValidatorStake) -> Self { let public_key = CompressedEdwardsY(vs.public_key().unwrap_as_ed25519().0); let stake = vs.stake(); let mut account_id = vs.take_account_id().as_str().as_bytes().to_vec(); - account_id.resize(AccountId::MAX_LEN, 0); + account_id.resize(AccountId::MAX_LEN, ACCOUNT_ID_PADDING_BYTE); Self { account_id: account_id.try_into().unwrap(), // SAFETY: already checked this above public_key: public_key.into(), @@ -576,6 +436,24 @@ impl From for ValidatorStakeVariableValue { } } +impl Into for ValidatorStakeVariableValue { + fn into(self) -> ValidatorStakeView { + let unpadded_bytes = self + .account_id + .split(|x| *x == ACCOUNT_ID_PADDING_BYTE) + .collect_vec()[0]; + let account_id = String::from_utf8(unpadded_bytes.to_vec()).expect("invalid account bytes"); + println!("account id: {}", account_id); + let account_id = account_id.parse().expect("invalid account id"); + let public_key = PublicKey::ED25519(ED25519PublicKey(self.public_key.0.into())); + ValidatorStakeView::V1(ValidatorStakeViewV1 { + account_id, + public_key, + stake: self.stake.as_u128(), + }) + } +} + impl Default for ValidatorStakeVariableValue { fn default() -> Self { let bytes: [u8; AccountId::MAX_LEN] = [0u8; AccountId::MAX_LEN]; @@ -748,6 +626,44 @@ where } } +#[derive(Debug, Clone, Deserialize, Serialize)] +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 default_validator = + ValidatorStakeVariableValue::<>::Field>::default(); + + let bps = bps + .into_iter() + .filter(|x| x.account_id != default_validator.account_id) + .map(Into::::into) + .collect_vec(); + log::debug!("Bps to hash: {:#?}", bps); + let hash = CryptoHash::hash_borsh(bps); + log::debug!("Hash: {:#?}", hash); + + // TODO: figure out how to hash this in circuit + // It's non trivial because the account id is padded to the max len + output_stream.write_value::(hash.0.into()); + } +} + +impl HashBpsInputs { + pub fn hash, const D: usize>( + self, + b: &mut CircuitBuilder, + bps: &BpsArr, + ) -> CryptoHashVariable { + let mut input_stream = VariableStream::new(); + input_stream.write::>(&bps); + + let output_stream = b.hint(input_stream, self); + output_stream.read::(b) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index 5560286..d7ef216 100644 --- a/crates/protocol/src/lib.rs +++ b/crates/protocol/src/lib.rs @@ -1,6 +1,7 @@ use crate::prelude::*; use error::Error; pub use merkle_util::*; +pub use near_crypto::ED25519PublicKey; pub use near_crypto::{PublicKey, Signature}; pub use near_primitives::{ block_header::ApprovalInner, @@ -8,6 +9,7 @@ pub use near_primitives::{ merkle::MerklePathItem, types::{validator_stake::ValidatorStake, BlockHeight, EpochId}, views::LightClientBlockLiteView, + views::ValidatorStakeViewV1, views::{ validator_stake_view::ValidatorStakeView, BlockHeaderInnerLiteView, LightClientBlockView, }, diff --git a/crates/rpc/src/lib.rs b/crates/rpc/src/lib.rs index 4af6f4e..f36e348 100644 --- a/crates/rpc/src/lib.rs +++ b/crates/rpc/src/lib.rs @@ -10,7 +10,7 @@ use near_jsonrpc_client::{ }; use near_primitives::{ types::{validator_stake::ValidatorStake, ValidatorStakeV1}, - views::{validator_stake_view::ValidatorStakeView, LightClientBlockView}, + views::{validator_stake_view::ValidatorStakeView, LightClientBlockView, ValidatorStakeViewV1}, }; use std::fmt::{Display, Formatter}; @@ -136,6 +136,7 @@ impl LightClientRpc for NearRpcClient { near_primitives::types::EpochId(*epoch_id), ), }; + log::debug!("requesting validators: {:?}", req); self.client .call(&req) .or_else(|e| { @@ -143,7 +144,18 @@ impl LightClientRpc for NearRpcClient { self.archive.call(&req) }) .await - .map(|x| x.current_proposals) + .map(|x| x.current_validators) + .map(|x| { + x.into_iter() + .map(|x| { + ValidatorStakeView::V1(ValidatorStakeViewV1 { + account_id: x.account_id, + public_key: x.public_key, + stake: x.stake, + }) + }) + .collect() + }) .map_err(|e| anyhow::format_err!("{:?}:{}", epoch_id, e)) } }