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/hyperspace/cosmos/src/utils.rs b/hyperspace/cosmos/src/utils.rs new file mode 100644 index 000000000..007167a7a --- /dev/null +++ b/hyperspace/cosmos/src/utils.rs @@ -0,0 +1,127 @@ +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, signature::Verifier as _}, + signature::Signature, + PublicKey, +}; + +/// 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 { commit: Commit { signatures, .. }, .. }, + .. + } = header; + + let total_voting_power = validator_set.total_voting_power().value(); + + // 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 + } +} + +// 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/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..db4864ecc 100644 --- a/light-clients/ics07-tendermint/src/lib.rs +++ b/light-clients/ics07-tendermint/src/lib.rs @@ -46,7 +46,6 @@ pub mod merkle; pub mod mock; #[cfg(any(test, feature = "mocks"))] mod query; - /// Host functions that allow the light client verify cryptographic proofs in native. pub trait HostFunctionsProvider: ics23::HostFunctionsProvider