From 1587e12504a6354b93fc51244c77274a948deaad Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Tue, 7 Nov 2023 20:30:24 +0100 Subject: [PATCH 1/3] hyperspace-cosmos: integrate zk prover - assemble input for prover over a finalized block - get the prover result - send the packet to the other chain with the proof --- light-clients/ics07-tendermint/Cargo.toml | 2 +- light-clients/ics07-tendermint/src/lib.rs | 1 + light-clients/ics07-tendermint/src/utils.rs | 70 +++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 light-clients/ics07-tendermint/src/utils.rs diff --git a/light-clients/ics07-tendermint/Cargo.toml b/light-clients/ics07-tendermint/Cargo.toml index 9b81bbccc..158462848 100644 --- a/light-clients/ics07-tendermint/Cargo.toml +++ b/light-clients/ics07-tendermint/Cargo.toml @@ -40,7 +40,7 @@ prost = { version = "0.11", default-features = false } bytes = { version = "1.1.0", default-features = false } subtle-encoding = { version = "0.5", default-features = false } flex-error = { version = "0.4.4", default-features = false } -tendermint = { git = "https://github.com/informalsystems/tendermint-rs", rev = "e81f7bf23d63ffbcd242381d1ce5e35da3515ff1", default-features = false } +tendermint = { git = "https://github.com/informalsystems/tendermint-rs", rev = "e81f7bf23d63ffbcd242381d1ce5e35da3515ff1", default-features = false, features = ["rust-crypto"] } tendermint-proto = { git = "https://github.com/informalsystems/tendermint-rs", rev = "e81f7bf23d63ffbcd242381d1ce5e35da3515ff1", default-features = false } tendermint-light-client-verifier = { git = "https://github.com/informalsystems/tendermint-rs", rev = "e81f7bf23d63ffbcd242381d1ce5e35da3515ff1", default-features = false } sha2 = { version = "0.10", optional = true, default-features = false } diff --git a/light-clients/ics07-tendermint/src/lib.rs b/light-clients/ics07-tendermint/src/lib.rs index ea868b686..941431d98 100644 --- a/light-clients/ics07-tendermint/src/lib.rs +++ b/light-clients/ics07-tendermint/src/lib.rs @@ -42,6 +42,7 @@ pub mod client_state; pub mod consensus_state; pub mod error; pub mod merkle; +pub mod utils; #[cfg(any(test, feature = "mocks"))] pub mod mock; #[cfg(any(test, feature = "mocks"))] diff --git a/light-clients/ics07-tendermint/src/utils.rs b/light-clients/ics07-tendermint/src/utils.rs new file mode 100644 index 000000000..77f7fc0f4 --- /dev/null +++ b/light-clients/ics07-tendermint/src/utils.rs @@ -0,0 +1,70 @@ +use tendermint::{block::{signed_header::SignedHeader, Commit, CommitSig}, signature::Signature, PublicKey, crypto::default::signature::Verifier}; + +use crate::client_message::Header; + +use tendermint::crypto::signature::Verifier as _; + + + +/// Utilty function that: +/// - collects {signature, pubkey, msg} from every validator that successfuly signed a block so that +/// - there's consensus (voting power > 2/3) and +/// - size of the validators that succesfully voted equals the circuit size + +pub fn collect_signatures_for_finalized_block(header: Header, circuit_size: usize) -> Option)>> { + + let Header {validator_set, + signed_header: SignedHeader{ + header, commit: Commit{signatures, ..} + ,..}, .. + } = header; + + let total_voting_power = validator_set.total_voting_power().value(); + + // 1. order by voting power + + // filter by valid signatures + let mut validator_info_signed_block = signatures.iter().filter(|commit| matches!(commit, CommitSig::BlockIdFlagCommit{..})) + .flat_map(|commit| match commit { + CommitSig::BlockIdFlagCommit { validator_address, signature, .. } if signature.is_some() => { + match validator_set.validators().iter().find(|info| *validator_address == info.address ) { + None => None, + Some(info) => Some((info.pub_key, signature.clone().unwrap(), commit, info.power)) + } + + } + _ => unreachable!() + } + ) + .collect::>(); + +// order by power DESC +validator_info_signed_block.sort_by(|(_, _, _, power_a), (_, _, _, power_b)| power_b.value().cmp(&power_a.value())); + +let result = validator_info_signed_block.into_iter().fold((0u64, vec![]), |mut acc, (pubkey, signature, _commit, power)| { + if acc.0 * 3 > 2*total_voting_power && acc.1.len() == circuit_size { + acc + } else { + // TOOD: commit has to become bytes, I guess? How to do so? + // let msg = commit; + let msg = vec![0u8]; + + if Verifier::verify(pubkey, msg.as_ref(), &signature).is_ok() { + acc.1.push((pubkey, signature, msg.to_vec())); + acc.0 += power.value(); + } + acc + } + + }); + +if result.0 * 3 > 2*total_voting_power && result.1.len() == circuit_size { + Some(result.1) +} else { + None +} +} + +// fn check_block_is_final() { + +// } \ No newline at end of file From 7519168c8e74e9dca4bd27687808e16644bd5f1c Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Tue, 7 Nov 2023 20:32:41 +0100 Subject: [PATCH 2/3] fmt code --- light-clients/ics07-tendermint/src/lib.rs | 2 +- light-clients/ics07-tendermint/src/utils.rs | 115 +++++++++++--------- 2 files changed, 66 insertions(+), 51 deletions(-) diff --git a/light-clients/ics07-tendermint/src/lib.rs b/light-clients/ics07-tendermint/src/lib.rs index 941431d98..4fc17c38d 100644 --- a/light-clients/ics07-tendermint/src/lib.rs +++ b/light-clients/ics07-tendermint/src/lib.rs @@ -42,11 +42,11 @@ pub mod client_state; pub mod consensus_state; pub mod error; pub mod merkle; -pub mod utils; #[cfg(any(test, feature = "mocks"))] pub mod mock; #[cfg(any(test, feature = "mocks"))] mod query; +pub mod utils; /// Host functions that allow the light client verify cryptographic proofs in native. pub trait HostFunctionsProvider: diff --git a/light-clients/ics07-tendermint/src/utils.rs b/light-clients/ics07-tendermint/src/utils.rs index 77f7fc0f4..e22e2a80e 100644 --- a/light-clients/ics07-tendermint/src/utils.rs +++ b/light-clients/ics07-tendermint/src/utils.rs @@ -1,70 +1,85 @@ -use tendermint::{block::{signed_header::SignedHeader, Commit, CommitSig}, signature::Signature, PublicKey, crypto::default::signature::Verifier}; +use tendermint::{ + block::{signed_header::SignedHeader, Commit, CommitSig}, + crypto::default::signature::Verifier, + signature::Signature, + PublicKey, +}; use crate::client_message::Header; use tendermint::crypto::signature::Verifier as _; - - /// Utilty function that: /// - collects {signature, pubkey, msg} from every validator that successfuly signed a block so that /// - there's consensus (voting power > 2/3) and -/// - size of the validators that succesfully voted equals the circuit size +/// - size of the validators that succesfully voted equals the circuit size -pub fn collect_signatures_for_finalized_block(header: Header, circuit_size: usize) -> Option)>> { +pub fn collect_signatures_for_finalized_block( + header: Header, + circuit_size: usize, +) -> Option)>> { + let Header { + validator_set, + signed_header: SignedHeader { header, commit: Commit { signatures, .. }, .. }, + .. + } = header; - let Header {validator_set, - signed_header: SignedHeader{ - header, commit: Commit{signatures, ..} - ,..}, .. - } = header; + let total_voting_power = validator_set.total_voting_power().value(); - let total_voting_power = validator_set.total_voting_power().value(); + // 1. order by voting power - // 1. order by voting power - - // filter by valid signatures - let mut validator_info_signed_block = signatures.iter().filter(|commit| matches!(commit, CommitSig::BlockIdFlagCommit{..})) - .flat_map(|commit| match commit { - CommitSig::BlockIdFlagCommit { validator_address, signature, .. } if signature.is_some() => { - match validator_set.validators().iter().find(|info| *validator_address == info.address ) { - None => None, - Some(info) => Some((info.pub_key, signature.clone().unwrap(), commit, info.power)) - } - - } - _ => unreachable!() - } - ) - .collect::>(); + // filter by valid signatures + let mut validator_info_signed_block = signatures + .iter() + .filter(|commit| matches!(commit, CommitSig::BlockIdFlagCommit { .. })) + .flat_map(|commit| match commit { + CommitSig::BlockIdFlagCommit { validator_address, signature, .. } + if signature.is_some() => + { + match validator_set + .validators() + .iter() + .find(|info| *validator_address == info.address) + { + None => None, + Some(info) => + Some((info.pub_key, signature.clone().unwrap(), commit, info.power)), + } + }, + _ => unreachable!(), + }) + .collect::>(); -// order by power DESC -validator_info_signed_block.sort_by(|(_, _, _, power_a), (_, _, _, power_b)| power_b.value().cmp(&power_a.value())); + // order by power DESC + validator_info_signed_block + .sort_by(|(_, _, _, power_a), (_, _, _, power_b)| power_b.value().cmp(&power_a.value())); -let result = validator_info_signed_block.into_iter().fold((0u64, vec![]), |mut acc, (pubkey, signature, _commit, power)| { - if acc.0 * 3 > 2*total_voting_power && acc.1.len() == circuit_size { - acc - } else { - // TOOD: commit has to become bytes, I guess? How to do so? - // let msg = commit; - let msg = vec![0u8]; + let result = validator_info_signed_block.into_iter().fold( + (0u64, vec![]), + |mut acc, (pubkey, signature, _commit, power)| { + if acc.0 * 3 > 2 * total_voting_power && acc.1.len() == circuit_size { + acc + } else { + // TOOD: commit has to become bytes, I guess? How to do so? + // let msg = commit; + let msg = vec![0u8]; - if Verifier::verify(pubkey, msg.as_ref(), &signature).is_ok() { - acc.1.push((pubkey, signature, msg.to_vec())); - acc.0 += power.value(); - } - acc - } - - }); + if Verifier::verify(pubkey, msg.as_ref(), &signature).is_ok() { + acc.1.push((pubkey, signature, msg.to_vec())); + acc.0 += power.value(); + } + acc + } + }, + ); -if result.0 * 3 > 2*total_voting_power && result.1.len() == circuit_size { - Some(result.1) -} else { - None -} + if result.0 * 3 > 2 * total_voting_power && result.1.len() == circuit_size { + Some(result.1) + } else { + None + } } // fn check_block_is_final() { -// } \ No newline at end of file +// } From 2ff863d32b529b659c20d2899ecfc685755b6059 Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Wed, 8 Nov 2023 15:51:41 +0100 Subject: [PATCH 3/3] draft of function calling the zk-prover http server --- Cargo.lock | 26 ++++---- hyperspace/cosmos/Cargo.toml | 3 + hyperspace/cosmos/src/lib.rs | 1 + .../cosmos}/src/utils.rs | 62 ++++++++++++++++--- light-clients/ics07-tendermint/src/lib.rs | 2 - 5 files changed, 70 insertions(+), 24 deletions(-) rename {light-clients/ics07-tendermint => hyperspace/cosmos}/src/utils.rs (55%) diff --git a/Cargo.lock b/Cargo.lock index b3209a3d0..e5398aa35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -641,9 +641,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -4643,6 +4643,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "base64 0.21.5", "bech32", "bip32", "derive_more", @@ -4681,6 +4682,7 @@ dependencies = [ "tokio-stream", "tonic", "tracing", + "ureq", ] [[package]] @@ -11199,7 +11201,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.21.0", + "base64 0.21.5", ] [[package]] @@ -12863,9 +12865,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -15780,20 +15782,20 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "ureq" -version = "2.6.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d" +checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3" dependencies = [ - "base64 0.13.1", + "base64 0.21.5", "flate2", "log", "once_cell", - "rustls 0.20.8", + "rustls 0.21.7", + "rustls-webpki 0.101.6", "serde", "serde_json", "url", - "webpki 0.22.0", - "webpki-roots 0.22.6", + "webpki-roots 0.25.2", ] [[package]] @@ -16180,7 +16182,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c86437fa68626fe896e5afc69234bb2b5894949083586535f200385adfd71213" dependencies = [ "anyhow", - "base64 0.21.0", + "base64 0.21.5", "bincode", "directories-next", "file-per-thread-logger", diff --git a/hyperspace/cosmos/Cargo.toml b/hyperspace/cosmos/Cargo.toml index 43d3a941b..d5e630dd1 100644 --- a/hyperspace/cosmos/Cargo.toml +++ b/hyperspace/cosmos/Cargo.toml @@ -34,6 +34,9 @@ ripemd = "0.1.3" digest = "0.10.6" quick_cache = "0.3.0" rand = "0.8.5" +ureq = {version = "2.8.0", features = ["json"] } +base64 = "0.21.5" + # composable ibc = { path = "../../ibc/modules", features = [] } diff --git a/hyperspace/cosmos/src/lib.rs b/hyperspace/cosmos/src/lib.rs index 5b0cac5d8..7ffbe38f9 100644 --- a/hyperspace/cosmos/src/lib.rs +++ b/hyperspace/cosmos/src/lib.rs @@ -25,5 +25,6 @@ pub mod provider; #[cfg(any(test, feature = "testing"))] pub mod test_provider; pub mod tx; +pub mod utils; pub type TimeoutHeight = Option; diff --git a/light-clients/ics07-tendermint/src/utils.rs b/hyperspace/cosmos/src/utils.rs similarity index 55% rename from light-clients/ics07-tendermint/src/utils.rs rename to hyperspace/cosmos/src/utils.rs index e22e2a80e..007167a7a 100644 --- a/light-clients/ics07-tendermint/src/utils.rs +++ b/hyperspace/cosmos/src/utils.rs @@ -1,14 +1,14 @@ +use base64::{engine::general_purpose, Engine as _}; + +use crate::error::Error; +use ics07_tendermint::client_message::Header; use tendermint::{ block::{signed_header::SignedHeader, Commit, CommitSig}, - crypto::default::signature::Verifier, + crypto::{default::signature::Verifier, signature::Verifier as _}, signature::Signature, PublicKey, }; -use crate::client_message::Header; - -use tendermint::crypto::signature::Verifier as _; - /// Utilty function that: /// - collects {signature, pubkey, msg} from every validator that successfuly signed a block so that /// - there's consensus (voting power > 2/3) and @@ -20,14 +20,12 @@ pub fn collect_signatures_for_finalized_block( ) -> Option)>> { let Header { validator_set, - signed_header: SignedHeader { header, commit: Commit { signatures, .. }, .. }, + signed_header: SignedHeader { commit: Commit { signatures, .. }, .. }, .. } = header; let total_voting_power = validator_set.total_voting_power().value(); - // 1. order by voting power - // filter by valid signatures let mut validator_info_signed_block = signatures .iter() @@ -80,6 +78,50 @@ pub fn collect_signatures_for_finalized_block( } } -// fn check_block_is_final() { +// TODO: this should be async +pub fn call_zk_prover( + zk_prover_url: String, + signatures: Vec, + public_keys: Vec, + messages: Vec, +) -> Result, Error> { + let body: ProverResponse = ureq::get(zk_prover_url.as_ref()) + .call() + .map_err(|e| Error::Custom(e.to_string()))? + .into_json() + .map_err(|e| Error::Custom(e.to_string()))?; + + if body.message.as_str() != "prover is busy" { + let resp: ProverResponse = ureq::post(zk_prover_url.as_ref()) + .send_json(ureq::json!({ + "public_keys": public_keys, + "signatures": signatures, + "messages": messages, + })) + .map_err(|e| Error::Custom(e.to_string()))? + .into_json() + .map_err(|e| Error::Custom(e.to_string()))?; -// } + if &resp.message == "proof submitted" { + let body: ProverResponse = ureq::get(zk_prover_url.as_ref()) + .call() + .map_err(|e| Error::Custom(e.to_string()))? + .into_json() + .map_err(|e| Error::Custom(e.to_string()))?; + + // assume here that we got the proof + // TODO: do proper re-check as the proof takes 5 minutes to be build + return Ok(general_purpose::STANDARD_NO_PAD + .decode(body.proof) + .map_err(|e| Error::Custom(e.to_string()))?) + } + } + Err(Error::Custom("could not get a proof".to_string())) +} + +// note that we know the circuit size +#[derive(Debug, serde::Deserialize)] +struct ProverResponse { + message: String, + proof: String, +} diff --git a/light-clients/ics07-tendermint/src/lib.rs b/light-clients/ics07-tendermint/src/lib.rs index 4fc17c38d..db4864ecc 100644 --- a/light-clients/ics07-tendermint/src/lib.rs +++ b/light-clients/ics07-tendermint/src/lib.rs @@ -46,8 +46,6 @@ pub mod merkle; pub mod mock; #[cfg(any(test, feature = "mocks"))] mod query; -pub mod utils; - /// Host functions that allow the light client verify cryptographic proofs in native. pub trait HostFunctionsProvider: ics23::HostFunctionsProvider