From 8401044a6a48b141b29b0b7a33c0108d8406fead Mon Sep 17 00:00:00 2001 From: aeryz Date: Thu, 25 Jul 2024 20:40:26 +0300 Subject: [PATCH] feat(cometbls-near): groth16 verifier with host functions Signed-off-by: aeryz --- Cargo.lock | 31 ++ Cargo.toml | 2 +- lib/cometbls-groth16-verifier/src/lib.rs | 28 ++ .../ibc/lightclients/cometbls/client_state.rs | 1 + lib/unionlabs/src/uint.rs | 1 + light-clients/cometbls/near/Cargo.toml | 2 + light-clients/cometbls/near/src/contract.rs | 26 +- light-clients/cometbls/near/src/error.rs | 9 + light-clients/cometbls/near/src/lib.rs | 1 + light-clients/cometbls/near/src/verifier.rs | 409 ++++++++++++++++++ near/near.nix | 27 +- near/test-circuit/Cargo.toml | 35 ++ near/test-circuit/src/main.rs | 112 +++++ poc-relayer/src/main.rs | 1 - 14 files changed, 674 insertions(+), 11 deletions(-) create mode 100644 light-clients/cometbls/near/src/verifier.rs create mode 100644 near/test-circuit/Cargo.toml create mode 100644 near/test-circuit/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index b131c988d8..a74d5c0757 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1623,6 +1623,8 @@ version = "0.1.0" dependencies = [ "borsh 1.5.1", "cometbls-groth16-verifier", + "hex", + "hex-literal", "ibc-vm-rs", "ics23", "near-contract-standards", @@ -10053,6 +10055,35 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test-circuit" +version = "0.1.0" +dependencies = [ + "base64 0.21.7", + "borsh 1.5.1", + "env_logger", + "hex", + "hex-literal", + "ibc-vm-rs", + "near-contract-standards", + "near-crypto 0.20.1", + "near-jsonrpc-client 0.8.0", + "near-jsonrpc-primitives 0.20.1", + "near-primitives 0.20.1", + "near-primitives-core 0.21.2", + "near-sdk", + "near-sdk-contract-tools", + "near-store", + "near-units", + "near-workspaces", + "serde", + "serde_json", + "sha2 0.10.8", + "time", + "tokio", + "unionlabs", +] + [[package]] name = "textwrap" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index 0c258f572f..06e66e9911 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,7 +73,7 @@ members = [ "light-clients/near/near", "near/dummy-ibc-app", "near/near-ibc-tests", - "light-clients/near/ics08-near", "lib/near-verifier", "light-clients/cometbls/near", "poc-relayer", + "light-clients/near/ics08-near", "lib/near-verifier", "light-clients/cometbls/near", "poc-relayer", "near/test-circuit", ] [workspace.package] diff --git a/lib/cometbls-groth16-verifier/src/lib.rs b/lib/cometbls-groth16-verifier/src/lib.rs index f5d1b24048..34d254d2b9 100644 --- a/lib/cometbls-groth16-verifier/src/lib.rs +++ b/lib/cometbls-groth16-verifier/src/lib.rs @@ -282,6 +282,7 @@ fn verify_generic_zkp_2( .chain_update(trusted_validators_hash) .finalize(), ); + // println!("commitment: {:?}", commitment_hash.to_be_bytes()); // drop the most significant byte to fit in bn254 F_r inputs_hash[0] = 0; let public_inputs: [substrate_bn::Fr; NB_PUBLIC_INPUTS] = [ @@ -460,4 +461,31 @@ mod tests { Err(Error::InvalidProof) ); } + + #[test] + fn print_consts() { + let mut buffer = [0u8; 64]; + GAMMA_ABC_G1[2] + .x() + .to_big_endian(&mut buffer[0..32]) + .unwrap(); + GAMMA_ABC_G1[2] + .y() + .to_big_endian(&mut buffer[32..64]) + .unwrap(); + buffer.reverse(); + println!( + "{}", + format!( + r#" + G1Affine {{ + x: Fq({:?}), + y: Fq({:?}), + }} + "#, + &buffer[32..64], + &buffer[0..32], + ) + ); + } } diff --git a/lib/unionlabs/src/ibc/lightclients/cometbls/client_state.rs b/lib/unionlabs/src/ibc/lightclients/cometbls/client_state.rs index 458a57f0b2..9c492b1fdc 100644 --- a/lib/unionlabs/src/ibc/lightclients/cometbls/client_state.rs +++ b/lib/unionlabs/src/ibc/lightclients/cometbls/client_state.rs @@ -18,6 +18,7 @@ use crate::{ from ) )] +#[derive(Default)] pub struct ClientState { pub chain_id: String, pub trusting_period: u64, diff --git a/lib/unionlabs/src/uint.rs b/lib/unionlabs/src/uint.rs index 4c7252911e..12ef83a2f6 100644 --- a/lib/unionlabs/src/uint.rs +++ b/lib/unionlabs/src/uint.rs @@ -8,6 +8,7 @@ use core::{ str::FromStr, }; +use borsh::BorshSerialize; use serde::{Deserialize, Serialize}; use serde_utils::HEX_ENCODING_PREFIX; diff --git a/light-clients/cometbls/near/Cargo.toml b/light-clients/cometbls/near/Cargo.toml index 545b9b663c..ed718d3999 100644 --- a/light-clients/cometbls/near/Cargo.toml +++ b/light-clients/cometbls/near/Cargo.toml @@ -19,6 +19,8 @@ borsh = { workspace = true, features = [ "derive"] } thiserror = { workspace = true } ics23 = { workspace = true } cometbls-groth16-verifier = { workspace = true } +hex-literal = {workspace = true } +hex.workspace = true [lints] workspace = true diff --git a/light-clients/cometbls/near/src/contract.rs b/light-clients/cometbls/near/src/contract.rs index e4a8810060..e31796da1d 100644 --- a/light-clients/cometbls/near/src/contract.rs +++ b/light-clients/cometbls/near/src/contract.rs @@ -23,7 +23,7 @@ use unionlabs::{ id::ClientId, }; -use crate::error::Error; +use crate::{error::Error, verifier::verify_zkp}; #[near_bindgen] #[derive(PanicOnDefault, BorshDeserialize, BorshSerialize)] @@ -205,13 +205,13 @@ impl Contract { return false; } - // cometbls_groth16_verifier::verify_zkp( - // &self.client_state.chain_id, - // trusted_validators_hash, - // &header.signed_header, - // header.zero_knowledge_proof, - // ) - // .unwrap(); + verify_zkp( + &self.client_state.chain_id, + trusted_validators_hash, + &header.signed_header, + header.zero_knowledge_proof, + ) + .unwrap(); true } @@ -221,6 +221,16 @@ impl Contract { false } + pub fn test_circuit( + &self, + chain_id: String, + trusted_validators_hash: unionlabs::hash::H256, + header: unionlabs::ibc::lightclients::cometbls::light_header::LightHeader, + zkp: Vec, + ) { + verify_zkp(&chain_id, trusted_validators_hash, &header, zkp).unwrap() + } + pub fn update_client(&mut self, client_msg: Vec) -> (Vec, Vec<(Height, Vec)>) { let header = Header::decode_as::(&client_msg).unwrap(); diff --git a/light-clients/cometbls/near/src/error.rs b/light-clients/cometbls/near/src/error.rs index d0ebc42923..614df75cc5 100644 --- a/light-clients/cometbls/near/src/error.rs +++ b/light-clients/cometbls/near/src/error.rs @@ -40,4 +40,13 @@ pub enum Error { #[error("the chain id cannot be more than 31 bytes long to fit in the bn254 scalar field")] InvalidChainId, + + #[error("invalid zkp length")] + InvalidZKPLength, + + #[error("invalid height")] + InvalidHeight, + + #[error("invalid timestamp")] + InvalidTimestamp, } diff --git a/light-clients/cometbls/near/src/lib.rs b/light-clients/cometbls/near/src/lib.rs index ed72987554..0a0ced5d53 100644 --- a/light-clients/cometbls/near/src/lib.rs +++ b/light-clients/cometbls/near/src/lib.rs @@ -1,2 +1,3 @@ pub mod contract; pub mod error; +pub mod verifier; diff --git a/light-clients/cometbls/near/src/verifier.rs b/light-clients/cometbls/near/src/verifier.rs new file mode 100644 index 0000000000..d524fc20d8 --- /dev/null +++ b/light-clients/cometbls/near/src/verifier.rs @@ -0,0 +1,409 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use hex_literal::hex; +use near_sdk::env; +use unionlabs::{ + hash::H256, ibc::lightclients::cometbls::light_header::LightHeader, uint::U256, ByteArrayExt, +}; + +use crate::error::Error; + +#[derive(Copy, Debug, Default, Clone, BorshSerialize, BorshDeserialize)] +pub struct Fq(pub [u8; 32]); + +impl From for Fq { + fn from(value: u64) -> Self { + let mut buffer = [0u8; 32]; + buffer[0..8].copy_from_slice(&value.to_le_bytes()); + Fq(buffer) + } +} + +#[derive(Debug, Default, Clone, BorshSerialize, BorshDeserialize)] +pub struct Fq2(pub Fq, pub Fq); + +pub const FQ_SIZE: usize = 32; +pub const G1_SIZE: usize = 2 * FQ_SIZE; +pub const G2_SIZE: usize = 2 * G1_SIZE; +pub const EXPECTED_PROOF_SIZE: usize = G1_SIZE + G2_SIZE + G1_SIZE + G1_SIZE + G1_SIZE; + +pub const HMAC_O: &[u8] = &hex!("1F333139281E100F5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C5C"); +pub const HMAC_I: &[u8] = &hex!("75595B5342747A653636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636"); +pub const PRIME_R_MINUS_ONE: U256 = U256::from_limbs([ + 4891460686036598784, + 2896914383306846353, + 13281191951274694749, + 3486998266802970665, +]); + +pub const ALPHA_G1: G1Affine = G1Affine { + x: Fq([ + 153, 168, 24, 193, 103, 1, 111, 127, 109, 2, 216, 64, 5, 165, 237, 31, 124, 108, 25, 196, + 221, 241, 87, 51, 182, 122, 204, 1, 41, 7, 103, 9, + ]), + y: Fq([ + 255, 129, 13, 157, 51, 116, 128, 128, 105, 193, 234, 30, 93, 38, 58, 144, 207, 129, 129, + 185, 139, 65, 88, 5, 121, 113, 118, 53, 122, 206, 199, 8, + ]), +}; +pub const BETA_G2: G2Affine = G2Affine { + x: Fq2( + Fq([ + 116, 40, 132, 234, 24, 160, 14, 243, 24, 116, 213, 252, 85, 17, 177, 143, 169, 57, 29, + 198, 155, 151, 27, 137, 138, 45, 191, 198, 68, 3, 63, 21, + ]), + Fq([ + 101, 109, 201, 47, 31, 148, 220, 23, 0, 38, 205, 128, 33, 46, 81, 96, 210, 83, 158, + 126, 139, 64, 136, 93, 29, 96, 183, 112, 210, 95, 53, 25, + ]), + ), + y: Fq2( + Fq([ + 161, 102, 168, 244, 129, 112, 104, 183, 220, 39, 2, 48, 133, 154, 161, 86, 245, 174, + 18, 32, 215, 225, 172, 145, 34, 57, 191, 2, 102, 143, 210, 39, + ]), + Fq([ + 118, 162, 9, 196, 98, 30, 188, 69, 94, 17, 6, 29, 53, 168, 161, 189, 86, 130, 108, 217, + 134, 186, 224, 104, 244, 98, 252, 218, 60, 228, 213, 34, + ]), + ), +}; +pub const GAMMA_G2: G2Affine = G2Affine { + x: Fq2( + Fq([ + 25, 182, 113, 158, 66, 196, 46, 209, 223, 70, 250, 8, 200, 112, 197, 36, 26, 82, 145, + 59, 101, 217, 180, 54, 121, 224, 137, 194, 224, 187, 22, 34, + ]), + Fq([ + 207, 58, 72, 156, 167, 146, 127, 79, 129, 64, 10, 46, 189, 115, 154, 147, 91, 206, 179, + 34, 66, 100, 239, 248, 226, 72, 49, 26, 233, 107, 231, 32, + ]), + ), + y: Fq2( + Fq([ + 58, 175, 246, 108, 37, 153, 187, 92, 145, 127, 238, 195, 150, 148, 55, 234, 53, 232, + 223, 200, 25, 159, 60, 223, 127, 58, 237, 187, 117, 107, 46, 39, + ]), + Fq([ + 113, 5, 38, 216, 83, 140, 105, 78, 143, 175, 204, 198, 146, 97, 207, 62, 194, 205, 84, + 90, 225, 218, 199, 37, 240, 196, 96, 232, 96, 77, 239, 47, + ]), + ), +}; +pub const DELTA_G2: G2Affine = G2Affine { + x: Fq2( + Fq([ + 235, 4, 77, 219, 149, 30, 155, 40, 237, 167, 218, 147, 171, 163, 65, 239, 44, 150, 164, + 214, 24, 44, 167, 133, 163, 32, 24, 201, 200, 3, 212, 5, + ]), + Fq([ + 252, 185, 240, 74, 49, 201, 136, 162, 245, 166, 71, 16, 255, 175, 225, 1, 131, 29, 97, + 71, 37, 155, 84, 228, 93, 71, 224, 209, 24, 76, 94, 41, + ]), + ), + y: Fq2( + Fq([ + 7, 27, 211, 147, 97, 116, 151, 45, 70, 133, 59, 213, 58, 234, 80, 223, 14, 241, 63, + 128, 113, 63, 238, 42, 105, 160, 139, 179, 163, 166, 218, 5, + ]), + Fq([ + 66, 178, 169, 55, 218, 119, 194, 159, 243, 241, 194, 245, 213, 52, 254, 226, 230, 16, + 152, 197, 183, 237, 181, 35, 116, 35, 185, 83, 114, 104, 79, 21, + ]), + ), +}; +pub const PEDERSEN_G: G2Affine = G2Affine { + x: Fq2( + Fq([ + 90, 229, 109, 192, 20, 168, 19, 119, 18, 244, 88, 70, 88, 186, 111, 126, 57, 12, 195, + 152, 146, 249, 126, 86, 202, 133, 152, 135, 216, 216, 240, 19, + ]), + Fq([ + 135, 25, 189, 159, 250, 43, 186, 150, 57, 81, 218, 46, 8, 186, 146, 255, 193, 4, 155, + 162, 241, 253, 125, 127, 3, 176, 44, 19, 248, 246, 125, 37, + ]), + ), + y: Fq2( + Fq([ + 52, 127, 186, 120, 64, 214, 31, 105, 58, 120, 83, 131, 39, 49, 242, 98, 233, 101, 60, + 206, 146, 127, 168, 224, 219, 180, 141, 197, 66, 6, 232, 21, + ]), + Fq([ + 138, 121, 13, 200, 103, 254, 116, 205, 74, 105, 57, 58, 93, 104, 19, 238, 40, 245, 147, + 89, 234, 252, 14, 86, 172, 163, 199, 96, 204, 235, 96, 22, + ]), + ), +}; + +pub const PEDERSEN_G_ROOT_SIGMA_NEG: G2Affine = G2Affine { + x: Fq2( + Fq([ + 175, 91, 78, 48, 18, 58, 52, 67, 57, 50, 29, 214, 33, 181, 253, 249, 205, 152, 112, 98, + 89, 40, 250, 7, 35, 95, 1, 28, 223, 4, 161, 2, + ]), + Fq([ + 104, 99, 202, 226, 242, 176, 192, 206, 69, 126, 129, 173, 37, 160, 104, 251, 28, 184, + 96, 38, 9, 107, 232, 227, 247, 92, 85, 167, 65, 225, 191, 47, + ]), + ), + y: Fq2( + Fq([ + 141, 219, 123, 186, 89, 3, 145, 235, 166, 53, 241, 135, 102, 127, 128, 151, 187, 80, + 34, 126, 222, 87, 219, 103, 207, 229, 185, 28, 85, 56, 8, 44, + ]), + Fq([ + 34, 89, 106, 0, 43, 110, 130, 192, 130, 159, 109, 155, 83, 14, 12, 35, 108, 42, 22, + 226, 74, 53, 163, 179, 208, 191, 243, 236, 147, 63, 218, 39, + ]), + ), +}; +pub const GAMMA_ABC_G1: [G1Affine; 3] = [ + G1Affine { + x: Fq([ + 129, 146, 83, 48, 148, 29, 83, 216, 206, 193, 196, 66, 16, 246, 200, 130, 254, 232, 44, + 74, 233, 124, 182, 75, 79, 134, 67, 39, 229, 67, 24, 39, + ]), + y: Fq([ + 6, 36, 203, 115, 37, 168, 159, 234, 122, 210, 203, 222, 71, 138, 123, 163, 142, 202, + 24, 187, 161, 240, 36, 246, 114, 177, 248, 156, 198, 66, 51, 37, + ]), + }, + G1Affine { + x: Fq([ + 202, 75, 18, 93, 94, 26, 46, 192, 226, 38, 114, 67, 79, 190, 156, 160, 227, 202, 21, + 176, 194, 14, 22, 233, 2, 14, 214, 244, 113, 190, 13, 11, + ]), + y: Fq([ + 12, 224, 112, 182, 168, 185, 95, 104, 112, 20, 216, 61, 224, 159, 158, 254, 51, 202, + 175, 22, 170, 146, 229, 236, 136, 131, 118, 211, 235, 154, 11, 19, + ]), + }, + G1Affine { + x: Fq([ + 199, 144, 196, 161, 145, 138, 177, 46, 126, 60, 54, 0, 91, 47, 92, 188, 245, 64, 140, + 237, 152, 3, 53, 113, 118, 12, 124, 244, 213, 147, 158, 2, + ]), + y: Fq([ + 217, 241, 238, 106, 156, 19, 182, 235, 190, 46, 17, 218, 178, 63, 86, 0, 4, 15, 203, + 131, 59, 181, 121, 143, 174, 207, 157, 69, 16, 5, 241, 44, + ]), + }, +]; + +#[derive(Copy, Debug, Default, Clone, BorshSerialize, BorshDeserialize)] +pub struct G1Affine { + pub x: Fq, + pub y: Fq, +} + +impl G1Affine { + pub const fn zero() -> Self { + Self { + x: Fq([0; 32]), + y: Fq([0; 32]), + } + } +} + +#[derive(Debug, Default, Clone, BorshSerialize, BorshDeserialize)] +pub struct G2Affine { + pub x: Fq2, + pub y: Fq2, +} + +impl G2Affine { + pub const fn zero() -> Self { + Self { + x: Fq2(Fq([0; 32]), Fq([0; 32])), + y: Fq2(Fq([0; 32]), Fq([0; 32])), + } + } +} + +pub struct Proof { + pub a: G1Affine, + pub b: G2Affine, + pub c: G1Affine, +} + +pub struct ZKP { + pub proof: Proof, + pub proof_commitment: G1Affine, + pub proof_commitment_pok: G1Affine, +} + +pub type RawZKP = [u8; EXPECTED_PROOF_SIZE]; + +impl TryFrom<&[u8]> for ZKP { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + let value = RawZKP::try_from(value).map_err(|_| Error::InvalidZKPLength)?; + + Ok(Self { + proof: Proof { + a: G1Affine::from(value.array_slice::<0, G1_SIZE>()), + b: G2Affine::from(value.array_slice::()), + c: G1Affine::from(value.array_slice::<{ G1_SIZE + G2_SIZE }, G1_SIZE>()), + }, + proof_commitment: G1Affine::from( + value.array_slice::<{ G1_SIZE + G2_SIZE + G1_SIZE }, G1_SIZE>(), + ), + proof_commitment_pok: G1Affine::from( + value.array_slice::<{ G1_SIZE + G2_SIZE + G1_SIZE + G1_SIZE }, G1_SIZE>(), + ), + }) + } +} + +impl From<[u8; G1_SIZE]> for G1Affine { + fn from(value: [u8; G1_SIZE]) -> Self { + let mut x = value.array_slice::<0, FQ_SIZE>(); + x.reverse(); + let mut y = value.array_slice::(); + y.reverse(); + G1Affine { x: Fq(x), y: Fq(y) } + } +} + +impl From<[u8; G2_SIZE]> for G2Affine { + fn from(value: [u8; G2_SIZE]) -> Self { + let mut x_0 = value.array_slice::(); + x_0.reverse(); + let mut x_1 = value.array_slice::<0, FQ_SIZE>(); + x_1.reverse(); + let mut y_0 = value.array_slice::<{ G1_SIZE + FQ_SIZE }, FQ_SIZE>(); + y_0.reverse(); + let mut y_1 = value.array_slice::(); + y_1.reverse(); + G2Affine { + x: Fq2(Fq(x_0), Fq(x_1)), + y: Fq2(Fq(y_0), Fq(y_1)), + } + } +} + +pub fn verify_zkp( + chain_id: &str, + trusted_validators_hash: H256, + header: &LightHeader, + zkp: impl Into>, +) -> Result<(), Error> { + let zkp = ZKP::try_from(zkp.into().as_ref())?; + + let mut commitment_hash = hash_commitment(&zkp.proof_commitment)?; + + let mut inputs_hash = env::sha256_array( + &vec![0u8; 32 - chain_id.len()] + .into_iter() + .chain(chain_id.bytes()) + .chain( + U256::from( + u64::try_from(i64::from(header.height)).map_err(|_| Error::InvalidHeight)?, + ) + .to_be_bytes(), + ) + .chain( + U256::from( + u64::try_from(i64::from(header.time.seconds)) + .map_err(|_| Error::InvalidTimestamp)?, + ) + .to_be_bytes(), + ) + .chain( + U256::from( + u64::try_from(i32::from(header.time.nanos)) + .map_err(|_| Error::InvalidTimestamp)?, + ) + .to_be_bytes(), + ) + .chain(header.validators_hash) + .chain(header.next_validators_hash) + .chain(header.app_hash) + .chain(trusted_validators_hash) + .collect::>(), + ); + + // drop the most significant byte to fit in bn254 F_r + inputs_hash[0] = 0; + inputs_hash.reverse(); + + // env::panic_str(&format!("commhash: {:?}", commitment_hash.0)); + let public_inputs = [Fq(inputs_hash), commitment_hash]; + + let initial_point: G1Affine = borsh::from_slice(&env::alt_bn128_g1_sum( + &borsh::to_vec(&[ + (false, GAMMA_ABC_G1[0].clone()), + (false, zkp.proof_commitment.clone()), + ]) + .unwrap(), + )) + .unwrap(); + + let public_inputs_msm = public_inputs + .into_iter() + .zip(GAMMA_ABC_G1.into_iter().skip(1)) + .fold(initial_point, |s, (w_i, gamma_l_i)| { + let mul: G1Affine = borsh::from_slice(&env::alt_bn128_g1_multiexp( + &borsh::to_vec(&[(gamma_l_i, w_i)]).unwrap(), + )) + .unwrap(); + + borsh::from_slice::(&env::alt_bn128_g1_sum( + &borsh::to_vec(&[(false, s), (false, mul)]).unwrap(), + )) + .unwrap() + }); + + // TODO(aeryz): either split the pairing or apply fiat-shamir transformation + if env::alt_bn128_pairing_check( + &borsh::to_vec(&[ + (zkp.proof.a, zkp.proof.b), + (public_inputs_msm, GAMMA_G2), + (zkp.proof.c, DELTA_G2), + (ALPHA_G1, BETA_G2), + (zkp.proof_commitment, PEDERSEN_G), + (zkp.proof_commitment_pok, PEDERSEN_G_ROOT_SIGMA_NEG), + ]) + .unwrap(), + ) { + Ok(()) + } else { + env::panic_str("pairing check failed"); + } +} + +fn hmac_keccak(message: &[u8]) -> [u8; 32] { + env::keccak256_array( + &HMAC_O + .iter() + .copied() + .chain(env::keccak256( + &HMAC_I + .iter() + .copied() + .chain(message.iter().copied()) + .collect::>(), + )) + .collect::>(), + ) +} + +fn hash_commitment(proof_commitment: &G1Affine) -> Result { + let mut buffer = [0u8; 64]; + buffer[0..32].copy_from_slice(&proof_commitment.x.0); + buffer[0..32].reverse(); + buffer[32..64].copy_from_slice(&proof_commitment.y.0); + buffer[32..64].reverse(); + let hmac = hmac_keccak(&buffer); + let mut out = ((U256::from_be_bytes(hmac) % PRIME_R_MINUS_ONE) + U256::from(1)).to_be_bytes(); + out.reverse(); + Ok(Fq(out)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn convert() {} +} diff --git a/near/near.nix b/near/near.nix index 87c208574d..9626671bef 100644 --- a/near/near.nix +++ b/near/near.nix @@ -28,6 +28,31 @@ meta.mainProgram = "near-ibc-tests"; }; + + test-circuit = pkgs.stdenv.mkDerivation { + name = "test-circuit"; + buildInputs = [ pkgs.makeWrapper ]; + src = + (crane.buildWorkspaceMember { + crateDirFromRoot = "near/test-circuit"; + extraEnv = { + PROTOC = "${pkgs.protobuf}/bin/protoc"; + LIBCLANG_PATH = "${pkgs.llvmPackages_14.libclang.lib}/lib"; + }; + extraBuildInputs = [ pkgs.pkg-config pkgs.openssl pkgs.perl pkgs.gnumake ]; + extraNativeBuildInputs = [ pkgs.clang ]; + extraEnv = { }; + }).packages.test-circuit; + installPhase = '' + mkdir -p $out/bin + cp -r $src/bin/test-circuit $out/bin/test-circuit + wrapProgram $out/bin/test-circuit \ + --set NEAR_SANDBOX_BIN_PATH "${near-sandbox}/bin/neard" \ + --set VERIFIER "${self'.packages.cometbls-near}/lib/cometbls_near.wasm" + ''; + meta.mainProgram = "test-circuit"; + }; + cargo-near = craneLib.buildPackage rec { pname = "cargo-near"; version = "v0.6.2"; @@ -201,7 +226,7 @@ in { packages = near-light-client.packages // dummy-ibc-app.packages // near-ibc.packages // cometbls-near.packages // near-ics08.packages // { - inherit near-ibc-tests near-sandbox cargo-near nearcore near-localnet; + inherit near-ibc-tests near-sandbox cargo-near nearcore near-localnet test-circuit; }; checks = near-light-client.checks // near-ibc.checks; diff --git a/near/test-circuit/Cargo.toml b/near/test-circuit/Cargo.toml new file mode 100644 index 0000000000..197608fa06 --- /dev/null +++ b/near/test-circuit/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "test-circuit" +version = "0.1.0" +edition.workspace = true +license-file.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +borsh = { workspace = true, features = ["borsh-derive"] } +ibc-vm-rs.workspace = true +near-contract-standards.workspace = true +near-sdk = { workspace = true } +near-sdk-contract-tools = { workspace = true } +near-units = "0.2.0" +near-workspaces.workspace = true +serde.workspace = true +serde_json.workspace = true +tokio = { workspace = true, features = ["full"] } +unionlabs = { workspace = true, features = ["near"] } +# near-primitives = { git = "https://github.com/near/nearcore" } +base64 = { workspace = true } +env_logger = "0.9" +hex.workspace = true +hex-literal.workspace = true +near-crypto = "0.20" +near-jsonrpc-client = "0.8" +near-jsonrpc-primitives = "0.20" +near-primitives = "0.20" +near-primitives-core = { version = "0.21" } +near-store = { git = "https://github.com/near/nearcore" } +sha2 = { workspace = true } +time = { workspace = true, features = ["parsing", "serde"] } diff --git a/near/test-circuit/src/main.rs b/near/test-circuit/src/main.rs new file mode 100644 index 0000000000..ee01c70a45 --- /dev/null +++ b/near/test-circuit/src/main.rs @@ -0,0 +1,112 @@ +use std::{env, str::FromStr, thread::sleep, time::Duration}; + +use hex_literal::hex; +use near_primitives_core::hash::CryptoHash; +use near_workspaces::{ + network::Sandbox, + sandbox, testnet, + types::{Gas, KeyType, NearToken, SecretKey}, + Account, AccountId, Contract, Worker, +}; +use unionlabs::{ + encoding::{EncodeAs, Proto}, + google::protobuf::timestamp::Timestamp, + hash::H256, + ibc::lightclients::cometbls::{client_state::ClientState, light_header::LightHeader}, +}; + +const VERIFIER_ENV: &str = "VERIFIER"; + +mod alice { + pub const CLIENT_TYPE: &str = "near-alice"; +} +mod bob { + pub const CLIENT_TYPE: &str = "near-bob"; +} + +#[derive(serde::Serialize)] +pub struct CreateClient { + pub client_id: String, + pub client_state: Vec, + pub consensus_state: Vec, +} + +#[derive(serde::Serialize)] +pub struct TestCircuit { + chain_id: String, + trusted_validators_hash: H256, + header: LightHeader, + zkp: Vec, +} + +pub async fn deploy_contract( + sandbox: &Worker, + account_id: &str, + env_key: &'static str, +) -> Contract { + let wasm_path = env::var(env_key).unwrap(); + let wasm_blob = std::fs::read(wasm_path).unwrap(); + let account_id = account_id.to_string().try_into().unwrap(); + let secret_key = SecretKey::from_seed(KeyType::ED25519, "testificate"); + sandbox + .create_tla_and_deploy(account_id, secret_key, &wasm_blob) + .await + .unwrap() + .unwrap() +} + +#[tokio::main] +async fn main() { + let sandbox = sandbox().await.unwrap(); + let verifier_contract = deploy_contract(&sandbox, "verifier.test.near", VERIFIER_ENV).await; + let owner = sandbox.root_account().unwrap(); + let user = owner + .create_subaccount("user") + .initial_balance(NearToken::from_near(30)) + .transact() + .await + .unwrap() + .into_result() + .unwrap(); + let res = user + .call(verifier_contract.id(), "initialize") + .args_json(CreateClient { + client_id: String::from("08-wasm-0"), + client_state: ClientState::default().encode_as::(), + consensus_state: Vec::new(), + }) + .transact() + .await + .unwrap(); + println!("res1: {res:?}"); + + let res = user + .call(verifier_contract.id(), "test_circuit") + .gas(Gas::from_gas(300000000000000)) + .args_json(TestCircuit { + chain_id: "union-testnet-8".into(), + trusted_validators_hash: hex!( + "1deda64b1cc1319718f168b5aa8ed904b7d5b0ab932acdf6deae0ad9bd565a53" + ) + .into(), + header: LightHeader { + height: 969001.try_into().unwrap(), + time: Timestamp::from_str("2024-06-18T13:20:56.784169335Z").unwrap(), + validators_hash: hex!( + "1deda64b1cc1319718f168b5aa8ed904b7d5b0ab932acdf6deae0ad9bd565a53" + ) + .into(), + next_validators_hash: hex!( + "01a84dca649aa2df8de2f65a84c9092bbd5296b4bc54d818f844b28573d8e0be" + ) + .into(), + app_hash: hex!("1818da4a8b1c430557a3018adc2bf9a06e56c3b530e5cce7709232e0f03bd9ab") + .into(), + }, + zkp: hex!("086541c22b53d509d8369492d32683188f0b379950ea3c5da84aca2b331d911c163bc6e30c7610b6903832184d284399d140b316134202cfa53b695ed17db64e271a8ab10b015cc4562730180cc7af7d7509b64de00b5864ccef3ab6b5c187da1511c4af3392d5e4465cebeb3c92cad546ab6b5b7de08923ae756d4a49d972920ed4f1b33bde26016e753fe00e9ee8b37873e4df4696cce84baa34e444d6f9dc0021b25644dc22fd9414197dd9e094180eac33a5e6fc6d2e04e12df5baaae92815173080dedcafeb2789245e75f1c38ddaa4611273fa5eed1cb77f75aabace770186385a3a373190a9091147de95b3f11050152bc4376573ed454cfd703f1e7106edb33921b12717708fe03861534c812a5ea6c7e0ec428c02292f1e7dafb45901e8b29e0b18ba7cbfad2a7aef7db558f3eb49a943a379a03b1b976df912a0c329b66224da89f94e29c49b3c5070b86b23d9d23424246235088ea858a21340cc2d1120ac3dc25febd188abf16774ea49564f34bc769b6abd9295128c391dad18").to_vec(), + }) + .transact() + .await + .unwrap(); + println!("res: {res:?}"); +} diff --git a/poc-relayer/src/main.rs b/poc-relayer/src/main.rs index 8f36aca4b6..e7a05a0757 100644 --- a/poc-relayer/src/main.rs +++ b/poc-relayer/src/main.rs @@ -941,7 +941,6 @@ async fn main() { let client_state = ClientState::decode_as::(&client_state) .unwrap() .into_any(); - println!("uinon latest height: {}", union_client_state.latest_height); let (proof_consensus, _) = near .state_proof( header.new_state.inner_lite.height - 1,