diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3d4d9d77..3c24f7d8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,10 +14,15 @@ concurrency: env: CARGO_TERM_COLOR: always - RUSTFLAGS: "-C link-args=-Wl,--allow-multiple-definition" +# RUSTFLAGS: "-C link-args=-Wl,--allow-multiple-definition" FOUNDRY_PROFILE: ci BNB_RPC: ${{ secrets.BNB_RPC }} + BSC_URL: ${{ secrets.BSC_URL }} + OP_URL: ${{ secrets.OP_URL }} + SEPOLIA_URL: ${{ secrets.SEPOLIA_URL }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} POLYGON_RPC: ${{ secrets.POLYGON_RPC }} + WASM_BINDGEN_TEST_TIMEOUT: 3600 jobs: check-wasm: @@ -260,3 +265,23 @@ jobs: forge test -vvv id: test + hyperclient-tests: + name: Hyperclient Integration Tests + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4.1.1 + + - name: Install wasm-pack + run: | + curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + rustup target add wasm32-unknown-unknown + + - name: Install chrome + uses: browser-actions/setup-chrome@latest + + - name: Run WASM tests + run: | + wasm-pack test --headless --chrome --no-default-features --features=wasm + working-directory: modules/client + diff --git a/Cargo.lock b/Cargo.lock index d076ecf03..9c73830cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,17 @@ dependencies = [ "subtle 2.5.0", ] +[[package]] +name = "again" +version = "0.1.2" +source = "git+https://github.com/polytope-labs/again?branch=develop#39dc8f18462ff6f2da6205076bd34a967ee1f010" +dependencies = [ + "fluvio-wasm-timer", + "getrandom 0.2.12", + "log", + "rand 0.8.5", +] + [[package]] name = "ahash" version = "0.7.7" @@ -2596,7 +2607,7 @@ dependencies = [ "async-trait", "cumulus-primitives-core", "futures", - "jsonrpsee-core", + "jsonrpsee-core 0.16.3", "parity-scale-codec", "polkadot-overseer", "sc-client-api", @@ -2660,7 +2671,7 @@ dependencies = [ "either", "futures", "futures-timer", - "jsonrpsee", + "jsonrpsee 0.16.3", "parity-scale-codec", "pin-project", "polkadot-overseer", @@ -4019,6 +4030,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "fluvio-wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b768c170dc045fa587a8f948c91f9bcfb87f774930477c6215addf54317f137f" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "fnv" version = "1.0.7" @@ -4447,7 +4473,7 @@ checksum = "26ac8b505de5aa10e9c9548a3642fc708fc47fe3843b840992e6e6ab139f39d0" dependencies = [ "futures", "indicatif", - "jsonrpsee", + "jsonrpsee 0.16.3", "log", "parity-scale-codec", "serde", @@ -5003,7 +5029,28 @@ dependencies = [ "futures-channel", "futures-core", "futures-sink", - "gloo-utils", + "gloo-utils 0.1.7", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.2.0", + "http", "js-sys", "pin-project", "serde", @@ -5039,6 +5086,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "group" version = "0.13.0" @@ -5329,9 +5389,9 @@ dependencies = [ "hyper", "log", "rustls 0.21.10", - "rustls-native-certs", + "rustls-native-certs 0.6.3", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "webpki-roots 0.25.3", ] @@ -5370,7 +5430,7 @@ dependencies = [ "gargantua-runtime", "ismp-rpc", "ismp-runtime-api", - "jsonrpsee", + "jsonrpsee 0.16.3", "log", "messier-runtime", "nexus-runtime", @@ -5423,9 +5483,9 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "dotenv", + "console_error_panic_hook", "ethers", - "frame-support", + "fluvio-wasm-timer", "futures", "getrandom 0.2.12", "hashbrown 0.14.3", @@ -5435,17 +5495,21 @@ dependencies = [ "ismp-solidity-abi", "js-sys", "parity-scale-codec", + "primitive-types", + "reconnecting-jsonrpsee-ws-client", "reqwest", "serde", "serde-wasm-bindgen", "sp-core 28.0.0", "subxt", + "tiny-keccak", "tokio", + "tracing", + "tracing-wasm", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test", "wasm-streams 0.4.0", - "wasm-timer", "web-sys", ] @@ -5824,7 +5888,7 @@ dependencies = [ "hex-literal 0.3.4", "ismp", "ismp-runtime-api", - "jsonrpsee", + "jsonrpsee 0.16.3", "pallet-ismp", "parity-scale-codec", "sc-client-api", @@ -6004,16 +6068,28 @@ version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "367a292944c07385839818bb71c8d76611138e2dedb0677d035b8da21d29c78b" dependencies = [ - "jsonrpsee-client-transport", - "jsonrpsee-core", + "jsonrpsee-client-transport 0.16.3", + "jsonrpsee-core 0.16.3", "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", - "jsonrpsee-types", - "jsonrpsee-ws-client", + "jsonrpsee-types 0.16.3", + "jsonrpsee-ws-client 0.16.3", "tracing", ] +[[package]] +name = "jsonrpsee" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3ae45a64cfc0882934f963be9431b2a165d667f53140358181f262aca0702" +dependencies = [ + "jsonrpsee-client-transport 0.22.2", + "jsonrpsee-core 0.22.2", + "jsonrpsee-types 0.22.2", + "jsonrpsee-ws-client 0.22.2", +] + [[package]] name = "jsonrpsee-client-transport" version = "0.16.3" @@ -6024,21 +6100,44 @@ dependencies = [ "futures-channel", "futures-timer", "futures-util", - "gloo-net", + "gloo-net 0.2.6", "http", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.3", + "jsonrpsee-types 0.16.3", "pin-project", - "rustls-native-certs", + "rustls-native-certs 0.6.3", "soketto", "thiserror", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tokio-util", "tracing", "webpki-roots 0.25.3", ] +[[package]] +name = "jsonrpsee-client-transport" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455fc882e56f58228df2aee36b88a1340eafd707c76af2fa68cf94b37d461131" +dependencies = [ + "futures-channel", + "futures-util", + "gloo-net 0.5.0", + "http", + "jsonrpsee-core 0.22.2", + "pin-project", + "rustls-native-certs 0.7.0", + "rustls-pki-types", + "soketto", + "thiserror", + "tokio", + "tokio-rustls 0.25.0", + "tokio-util", + "tracing", + "url", +] + [[package]] name = "jsonrpsee-core" version = "0.16.3" @@ -6055,7 +6154,7 @@ dependencies = [ "futures-util", "globset", "hyper", - "jsonrpsee-types", + "jsonrpsee-types 0.16.3", "parking_lot 0.12.1", "rand 0.8.5", "rustc-hash", @@ -6068,6 +6167,30 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "jsonrpsee-core" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75568f4f9696e3a47426e1985b548e1a9fcb13372a5e320372acaf04aca30d1" +dependencies = [ + "anyhow", + "async-lock 3.3.0", + "async-trait", + "beef", + "futures-timer", + "futures-util", + "jsonrpsee-types 0.22.2", + "pin-project", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "wasm-bindgen-futures", +] + [[package]] name = "jsonrpsee-http-client" version = "0.16.3" @@ -6077,8 +6200,8 @@ dependencies = [ "async-trait", "hyper", "hyper-rustls", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.3", + "jsonrpsee-types 0.16.3", "rustc-hash", "serde", "serde_json", @@ -6110,8 +6233,8 @@ dependencies = [ "futures-util", "http", "hyper", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.3", + "jsonrpsee-types 0.16.3", "serde", "serde_json", "soketto", @@ -6136,6 +6259,19 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-types" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467fd35feeee179f71ab294516bdf3a81139e7aeebdd860e46897c12e1a3368" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "jsonrpsee-ws-client" version = "0.16.3" @@ -6143,9 +6279,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e1b3975ed5d73f456478681a417128597acd6a2487855fdb7b4a3d4d195bf5e" dependencies = [ "http", - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-client-transport 0.16.3", + "jsonrpsee-core 0.16.3", + "jsonrpsee-types 0.16.3", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ca71e74983f624c0cb67828e480a981586074da8ad3a2f214c6a3f884edab9" +dependencies = [ + "http", + "jsonrpsee-client-transport 0.22.2", + "jsonrpsee-core 0.22.2", + "jsonrpsee-types 0.22.2", + "url", ] [[package]] @@ -7257,7 +7406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19066d17147f6819ec25f5f6fc3b9fca2008ae745ac7fa2d55ddb1d207119eae" dependencies = [ "anyhow", - "jsonrpsee", + "jsonrpsee 0.16.3", "parity-scale-codec", "serde", "sp-api", @@ -9103,7 +9252,7 @@ version = "30.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba1486d58f38892df779a7812f28ff962d0b0632b955ea3c348f605caa01ba6d" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.3", "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "sp-api", @@ -10493,7 +10642,7 @@ version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "382eada9005c73375778e1dca58116e0660431cf90989fe0dde54ebe1f621a1e" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.3", "mmr-rpc", "pallet-transaction-payment-rpc", "polkadot-primitives", @@ -11396,6 +11545,23 @@ dependencies = [ "yasna", ] +[[package]] +name = "reconnecting-jsonrpsee-ws-client" +version = "0.3.0" +source = "git+https://github.com/niklasad1/reconnecting-jsonrpsee-ws-client?rev=84fd8400cac12babd1be7c337dc1a8d14ce0c101#84fd8400cac12babd1be7c337dc1a8d14ce0c101" +dependencies = [ + "again", + "futures", + "getrandom 0.2.12", + "jsonrpsee 0.22.2", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "wasm-bindgen-futures", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -11547,14 +11713,14 @@ dependencies = [ "percent-encoding", "pin-project-lite 0.2.13", "rustls 0.21.10", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.24.1", "tokio-util", "tower-service", "url", @@ -12060,6 +12226,20 @@ dependencies = [ "sct", ] +[[package]] +name = "rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +dependencies = [ + "log", + "ring 0.17.7", + "rustls-pki-types", + "rustls-webpki 0.102.2", + "subtle 2.5.0", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -12067,7 +12247,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.1", + "rustls-pki-types", "schannel", "security-framework", ] @@ -12081,6 +12274,22 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" +dependencies = [ + "base64 0.21.7", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" + [[package]] name = "rustls-webpki" version = "0.100.3" @@ -12101,6 +12310,17 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring 0.17.7", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -12489,7 +12709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f55379d0ce6f87026f896f03bce33862b57f77a46fbb4fb7e974182d20d458" dependencies = [ "futures", - "jsonrpsee", + "jsonrpsee 0.16.3", "sc-consensus-babe", "sc-consensus-epochs", "sc-rpc-api", @@ -12548,7 +12768,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4e3602efe1e206032164772c24365642e3dab807c5f8db0af166f6ef63af3e" dependencies = [ "futures", - "jsonrpsee", + "jsonrpsee 0.16.3", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -12626,7 +12846,7 @@ checksum = "694328880bf11fce67f5e066d7884dbdf1f2a41c42a7dfce9b0a6bc6b90448a1" dependencies = [ "finality-grandpa", "futures", - "jsonrpsee", + "jsonrpsee 0.16.3", "log", "parity-scale-codec", "sc-client-api", @@ -13015,7 +13235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ac0d83b9117c9c004e58331a593a28044eaf6414e5b3c605d1571b4a6981966" dependencies = [ "futures", - "jsonrpsee", + "jsonrpsee 0.16.3", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -13047,7 +13267,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630e81a436f32487452ae1a57ad0ba31f320ddf864bb7faefd7490fe16b3e139" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.3", "parity-scale-codec", "sc-chain-spec", "sc-mixnet", @@ -13069,7 +13289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b623908525a2f22aafa079080aa7fbce8b58ca8de53b9f31e3cc8547e0ad8b2" dependencies = [ "http", - "jsonrpsee", + "jsonrpsee 0.16.3", "log", "serde_json", "substrate-prometheus-endpoint", @@ -13088,7 +13308,7 @@ dependencies = [ "futures", "futures-util", "hex", - "jsonrpsee", + "jsonrpsee 0.16.3", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -13119,7 +13339,7 @@ dependencies = [ "exit-future", "futures", "futures-timer", - "jsonrpsee", + "jsonrpsee 0.16.3", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -13204,7 +13424,7 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb310dce9d2474a74152605dc0ca0f81a46f61ba4e7a39752f1203121f47c0c6" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.3", "parity-scale-codec", "sc-chain-spec", "sc-client-api", @@ -15910,7 +16130,7 @@ checksum = "fcee7812a1e1cec85e3095c5d1c1627ceb084c0c81e66c2f9df7cb7b3a5938f3" dependencies = [ "frame-system-rpc-runtime-api", "futures", - "jsonrpsee", + "jsonrpsee 0.16.3", "log", "parity-scale-codec", "sc-rpc-api", @@ -15942,7 +16162,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9076480cc6f480429b081bf93607d32183bdac4d6f0d2969d5e08de08bea1701" dependencies = [ "async-trait", - "jsonrpsee", + "jsonrpsee 0.16.3", "log", "sc-rpc-api", "serde", @@ -15974,7 +16194,7 @@ version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c92c25dcb3e4aee5559c2bd9b4d105786220cad116719d7ebb39e4f359865d44" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.3", "parity-scale-codec", "sc-client-api", "sc-rpc-api", @@ -16038,7 +16258,7 @@ dependencies = [ "getrandom 0.2.12", "hex", "impl-serde", - "jsonrpsee", + "jsonrpsee 0.16.3", "parity-scale-codec", "primitive-types", "scale-bits", @@ -16067,7 +16287,7 @@ dependencies = [ "frame-metadata 16.0.0", "heck", "hex", - "jsonrpsee", + "jsonrpsee 0.16.3", "parity-scale-codec", "proc-macro2", "quote", @@ -16580,6 +16800,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.2", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -16602,7 +16833,7 @@ dependencies = [ "log", "rustls 0.21.10", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tungstenite", "webpki-roots 0.23.1", ] @@ -16880,6 +17111,17 @@ dependencies = [ "tracing-log 0.2.0", ] +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber 0.3.18", + "wasm-bindgen", +] + [[package]] name = "trie-db" version = "0.27.1" diff --git a/evm/abi/build.rs b/evm/abi/build.rs index e4b0fa673..d50f3ea07 100644 --- a/evm/abi/build.rs +++ b/evm/abi/build.rs @@ -1,12 +1,13 @@ -use ethers_contract_abigen::MultiAbigen; -use forge_testsuite::Runner; -use std::{env, path::PathBuf}; +use std::env; fn main() -> anyhow::Result<()> { let base_dir = env::current_dir()?.parent().unwrap().display().to_string(); #[cfg(feature = "build-abi")] { + use ethers_contract_abigen::MultiAbigen; + use forge_testsuite::Runner; + use std::path::PathBuf; // first compile the project. let _ = Runner::new(PathBuf::from(&base_dir)); diff --git a/evm/abi/src/conversions.rs b/evm/abi/src/conversions.rs index 487f69d84..f0a87009d 100644 --- a/evm/abi/src/conversions.rs +++ b/evm/abi/src/conversions.rs @@ -24,6 +24,7 @@ use crate::{ use anyhow::anyhow; use ismp::{host::StateMachine, router}; +use crate::evm_host::PostRequestEventFilter; #[cfg(feature = "beefy")] pub use beefy::*; use primitive_types::H256; @@ -282,18 +283,7 @@ impl TryFrom for ismp::events::Event { gas_limit: get.gaslimit.low_u64(), })), EvmHostEvents::PostRequestEventFilter(post) => - Ok(ismp::events::Event::PostRequest(router::Post { - source: StateMachine::from_str(&String::from_utf8(post.source.0.into())?) - .map_err(|e| anyhow!("{}", e))?, - dest: StateMachine::from_str(&String::from_utf8(post.dest.0.into())?) - .map_err(|e| anyhow!("{}", e))?, - nonce: post.nonce.low_u64(), - from: post.from.0.into(), - to: post.to.0.into(), - timeout_timestamp: post.timeout_timestamp.low_u64(), - data: post.data.0.into(), - gas_limit: post.gaslimit.low_u64(), - })), + Ok(ismp::events::Event::PostRequest(post.try_into()?)), EvmHostEvents::PostResponseEventFilter(resp) => Ok(ismp::events::Event::PostResponse(router::PostResponse { post: router::Post { @@ -317,6 +307,25 @@ impl TryFrom for ismp::events::Event { } } +impl TryFrom for router::Post { + type Error = anyhow::Error; + + fn try_from(post: PostRequestEventFilter) -> Result { + Ok(router::Post { + source: StateMachine::from_str(&String::from_utf8(post.source.0.into())?) + .map_err(|e| anyhow!("{}", e))?, + dest: StateMachine::from_str(&String::from_utf8(post.dest.0.into())?) + .map_err(|e| anyhow!("{}", e))?, + nonce: post.nonce.low_u64(), + from: post.from.0.into(), + to: post.to.0.into(), + timeout_timestamp: post.timeout_timestamp.low_u64(), + data: post.data.0.into(), + gas_limit: post.gaslimit.low_u64(), + }) + } +} + pub mod local { use super::H256; diff --git a/evm/script/DeployZkBeefy.s.sol b/evm/script/DeployZkBeefy.s.sol index 2a1fdde27..44707ac8f 100644 --- a/evm/script/DeployZkBeefy.s.sol +++ b/evm/script/DeployZkBeefy.s.sol @@ -14,11 +14,12 @@ contract DeployScript is Script { bytes32 public salt = keccak256(bytes(vm.envString("VERSION"))); - address public SEPOLIA_HOST = 0x9DF353352b469782AB1B0F2CbBFEC41bF1FDbDb3; - address public ARB_SEPOLIA_HOST = 0x424e6971EB1C693cf4296d4bdb42aa0F32a0dd9e; - address public OP_SEPOLIA_HOST = 0x1B58A47e61Ca7604b634CBB00b4e275cCd7c9E95; - address public BASE_SEPOLIA_HOST = 0x4c876500A13cc3825D343b5Ac791d3A4913bF14f; - address public BSC_TESTNET_HOST = 0x022DDE07A21d8c553978b006D93CDe68ac83e677; + address public SEPOLIA_HOST = 0xe4226c474A6f4BF285eA80c2f01c0942B04323e5; + address public ARB_SEPOLIA_HOST = 0x56101AD00677488B3576C85e9e75d4F0a08BD627; + address public OP_SEPOLIA_HOST = 0x39f3D7a7783653a04e2970e35e5f32F0e720daeB; + address public BASE_SEPOLIA_HOST = 0x1D14e30e440B8DBA9765108eC291B7b66F98Fd09; + address public BSC_TESTNET_HOST = 0x4e5bbdd9fE89F54157DDb64b21eD4D1CA1CDf9a6; + uint256 public paraId = vm.envUint("PARA_ID"); string private host = vm.envString("HOST"); bytes32 private privateKey = vm.envBytes32("PRIVATE_KEY"); diff --git a/modules/client/Cargo.toml b/modules/client/Cargo.toml index 1f003bb14..41d02c6a1 100644 --- a/modules/client/Cargo.toml +++ b/modules/client/Cargo.toml @@ -3,25 +3,21 @@ name = "hyperclient" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib", "rlib"] - [dependencies] -wasm-bindgen = { version = "0.2.90", features = [] } -ismp = { path = "../ismp/core", default-features = false } +wasm-bindgen = { version = "0.2.90" } subxt = { version = "0.30.1", default-features = false } -getrandom = { version = "0.2", features = ["js"] } -ethers = { workspace = true, features = ["ws"] } +getrandom = { version = "0.2", default-features = false, features = ["js"] } anyhow = "1.0.75" hex-literal = { version = "0.3.3" } serde-wasm-bindgen = { version = "0.6.3", default-features = false } serde = { version = "1.0.196", features = ["derive"], default-features = false } wasm-bindgen-futures = "0.4.40" -sp-core = { workspace = true, default-features = false, features = ["full_crypto", "serde"] } +tiny-keccak = { version = "2.0.2", features = ["keccak"] } codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } -async-trait = { version = "0.1.53", features = [] } +async-trait = { version = "0.1.53", default-features = false } futures = "0.3.30" wasm-streams = "0.4.0" tokio = { version = "1.35.1", features = ["macros"] } @@ -29,19 +25,29 @@ reqwest = { version = "0.11.23", features = ["json"] } wasm-bindgen-test = "0.3.41" js-sys = "0.3.68" web-sys = "0.3.68" -wasm-timer = "0.2.5" -ismp-solidity-abi = { path = "../../evm/abi", default-features = false } +wasm-timer = { package = "fluvio-wasm-timer", version = "0.2.5" } hashbrown = { version = "0.14.3", features = ["serde"] } +primitive-types = { version = "0.12.2", default-features = false, features = ["serde"] } + +ethers = { workspace = true, features = ["ws"] } +sp-core = { workspace = true } +ismp = { path = "../ismp/core", default-features = false } +ismp-solidity-abi = { path = "../../evm/abi", default-features = false } + +[dependencies.reconnecting-jsonrpsee-ws-client] +git = "https://github.com/niklasad1/reconnecting-jsonrpsee-ws-client" +rev = "84fd8400cac12babd1be7c337dc1a8d14ce0c101" +default-features = false [features] default = ["std"] -wasm = ["subxt/web", "subxt/jsonrpsee"] -std = ["subxt/native", "subxt/jsonrpsee"] - +wasm = ["subxt/web", "subxt/jsonrpsee", "reconnecting-jsonrpsee-ws-client/web"] +std = ["subxt/native", "subxt/jsonrpsee", "reconnecting-jsonrpsee-ws-client/native"] [dev-dependencies] -tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] } -frame-support = { workspace = true, features = ["default"]} -dotenv = "0.15.0" -hex = "0.4.3" \ No newline at end of file +wasm-bindgen-test = "0.3.24" +tracing-wasm = "0.2.1" +console_error_panic_hook = "0.1.7" +tracing = { version = "0.1.40", default-features = false } +hex = { version = "0.4.3", default-features = false } \ No newline at end of file diff --git a/modules/client/pkg/hyperclient.d.ts b/modules/client/pkg/hyperclient.d.ts index 58205b746..a086d4ff9 100644 --- a/modules/client/pkg/hyperclient.d.ts +++ b/modules/client/pkg/hyperclient.d.ts @@ -105,10 +105,6 @@ export class JsChainConfig { */ handler_address: Uint8Array; /** -* Keccak = 1, Blake2 = 2 -*/ - hash_algo: number; -/** */ host_address: Uint8Array; /** @@ -127,13 +123,21 @@ export class JsClientConfig { dest: JsChainConfig; /** */ - hyperbridge: JsChainConfig; + hyperbridge: JsHyperbridgeConfig; /** */ source: JsChainConfig; } /** */ +export class JsHyperbridgeConfig { + free(): void; +/** +*/ + rpc_url: string; +} +/** +*/ export class JsPost { free(): void; /** diff --git a/modules/client/pkg/hyperclient_bg.js b/modules/client/pkg/hyperclient_bg.js index 1bbadbe68..0972d1c0d 100644 --- a/modules/client/pkg/hyperclient_bg.js +++ b/modules/client/pkg/hyperclient_bg.js @@ -24,6 +24,15 @@ function takeObject(idx) { return ret; } +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); @@ -44,15 +53,6 @@ function getStringFromWasm0(ptr, len) { return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); } -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - let WASM_VECTOR_LEN = 0; const lTextEncoder = typeof TextEncoder === 'undefined' ? (0, module.require)('util').TextEncoder : TextEncoder; @@ -220,23 +220,35 @@ function makeMutClosure(arg0, arg1, dtor, f) { return real; } function __wbg_adapter_32(arg0, arg1) { - wasm.wasm_bindgen__convert__closures__invoke0_mut__h55df6f4dbbec5483(arg0, arg1); + wasm.wasm_bindgen__convert__closures__invoke0_mut__hdc28361670d72cce(arg0, arg1); } function __wbg_adapter_35(arg0, arg1, arg2) { - wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h5e9cc6c4a192068d(arg0, arg1, addHeapObject(arg2)); + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h367ff8bffb6f7b2e(arg0, arg1, addHeapObject(arg2)); } -function __wbg_adapter_38(arg0, arg1) { - wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hf936b6f64a75e0db(arg0, arg1); +function __wbg_adapter_40(arg0, arg1) { + wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h9aae2ac53c63e19e(arg0, arg1); } function __wbg_adapter_45(arg0, arg1) { - wasm.wasm_bindgen__convert__closures__invoke0_mut__hedfac42a0d077e67(arg0, arg1); + wasm.wasm_bindgen__convert__closures__invoke0_mut__h3c8b3f369d6af9ec(arg0, arg1); } function __wbg_adapter_48(arg0, arg1, arg2) { - wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hf3f3671bbd9bc1e7(arg0, arg1, addHeapObject(arg2)); + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb494be3815513233(arg0, arg1, addHeapObject(arg2)); +} + +function getArrayU8FromWasm0(ptr, len) { + ptr = ptr >>> 0; + return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); +} + +function passArray8ToWasm0(arg, malloc) { + const ptr = malloc(arg.length * 1, 1) >>> 0; + getUint8Memory0().set(arg, ptr / 1); + WASM_VECTOR_LEN = arg.length; + return ptr; } function _assertClass(instance, klass) { @@ -316,18 +328,6 @@ export function subscribe_to_request_status(request, config_js, post_request_hei return takeObject(ret); } -function getArrayU8FromWasm0(ptr, len) { - ptr = ptr >>> 0; - return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); -} - -function passArray8ToWasm0(arg, malloc) { - const ptr = malloc(arg.length * 1, 1) >>> 0; - getUint8Memory0().set(arg, ptr / 1); - WASM_VECTOR_LEN = arg.length; - return ptr; -} - function handleError(f, args) { try { return f.apply(this, args); @@ -336,7 +336,7 @@ function handleError(f, args) { } } function __wbg_adapter_229(arg0, arg1, arg2, arg3) { - wasm.wasm_bindgen__convert__closures__invoke2_mut__h5f0b1403bdde1a12(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); + wasm.wasm_bindgen__convert__closures__invoke2_mut__h124eb194c086a246(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); } const IntoUnderlyingByteSourceFinalization = (typeof FinalizationRegistry === 'undefined') @@ -644,21 +644,6 @@ export class JsChainConfig { const len0 = WASM_VECTOR_LEN; wasm.__wbg_set_jschainconfig_consensus_state_id(this.__wbg_ptr, ptr0, len0); } - /** - * Keccak = 1, Blake2 = 2 - * @returns {number} - */ - get hash_algo() { - const ret = wasm.__wbg_get_jschainconfig_hash_algo(this.__wbg_ptr); - return ret; - } - /** - * Keccak = 1, Blake2 = 2 - * @param {number} arg0 - */ - set hash_algo(arg0) { - wasm.__wbg_set_jschainconfig_hash_algo(this.__wbg_ptr, arg0); - } } const JsClientConfigFinalization = (typeof FinalizationRegistry === 'undefined') @@ -710,22 +695,77 @@ export class JsClientConfig { wasm.__wbg_set_jsclientconfig_dest(this.__wbg_ptr, ptr0); } /** - * @returns {JsChainConfig} + * @returns {JsHyperbridgeConfig} */ get hyperbridge() { const ret = wasm.__wbg_get_jsclientconfig_hyperbridge(this.__wbg_ptr); - return JsChainConfig.__wrap(ret); + return JsHyperbridgeConfig.__wrap(ret); } /** - * @param {JsChainConfig} arg0 + * @param {JsHyperbridgeConfig} arg0 */ set hyperbridge(arg0) { - _assertClass(arg0, JsChainConfig); + _assertClass(arg0, JsHyperbridgeConfig); var ptr0 = arg0.__destroy_into_raw(); wasm.__wbg_set_jsclientconfig_hyperbridge(this.__wbg_ptr, ptr0); } } +const JsHyperbridgeConfigFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_jshyperbridgeconfig_free(ptr >>> 0)); +/** +*/ +export class JsHyperbridgeConfig { + + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(JsHyperbridgeConfig.prototype); + obj.__wbg_ptr = ptr; + JsHyperbridgeConfigFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + JsHyperbridgeConfigFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_jshyperbridgeconfig_free(ptr); + } + /** + * @returns {string} + */ + get rpc_url() { + let deferred1_0; + let deferred1_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.__wbg_get_jschainconfig_rpc_url(retptr, this.__wbg_ptr); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred1_0 = r0; + deferred1_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } + } + /** + * @param {string} arg0 + */ + set rpc_url(arg0) { + const ptr0 = passStringToWasm0(arg0, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.__wbg_set_jschainconfig_rpc_url(this.__wbg_ptr, ptr0, len0); + } +} + const JsPostFinalization = (typeof FinalizationRegistry === 'undefined') ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry(ptr => wasm.__wbg_jspost_free(ptr >>> 0)); @@ -1034,6 +1074,16 @@ export function __wbindgen_object_drop_ref(arg0) { takeObject(arg0); }; +export function __wbindgen_as_number(arg0) { + const ret = +getObject(arg0); + return ret; +}; + +export function __wbindgen_object_clone_ref(arg0) { + const ret = getObject(arg0); + return addHeapObject(ret); +}; + export function __wbindgen_cb_drop(arg0) { const obj = takeObject(arg0).original; if (obj.cnt-- == 1) { @@ -1054,16 +1104,6 @@ export function __wbindgen_error_new(arg0, arg1) { return addHeapObject(ret); }; -export function __wbindgen_as_number(arg0) { - const ret = +getObject(arg0); - return ret; -}; - -export function __wbindgen_object_clone_ref(arg0) { - const ret = getObject(arg0); - return addHeapObject(ret); -}; - export function __wbindgen_is_object(arg0) { const val = getObject(arg0); const ret = typeof(val) === 'object' && val !== null; @@ -1157,42 +1197,6 @@ export function __wbg_fetch_10edd7d7da150227(arg0, arg1) { return addHeapObject(ret); }; -export function __wbg_data_bbdd2d77ab2f7e78(arg0) { - const ret = getObject(arg0).data; - return addHeapObject(ret); -}; - -export function __wbg_view_38a0bacb59ad00ee(arg0) { - const ret = getObject(arg0).view; - return isLikeNone(ret) ? 0 : addHeapObject(ret); -}; - -export function __wbg_respond_fee44bba73c2fc8a() { return handleError(function (arg0, arg1) { - getObject(arg0).respond(arg1 >>> 0); -}, arguments) }; - -export function __wbg_now_ef71656beb948bc8(arg0) { - const ret = getObject(arg0).now(); - return ret; -}; - -export function __wbg_addEventListener_f984e99465a6a7f4() { return handleError(function (arg0, arg1, arg2, arg3) { - getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3)); -}, arguments) }; - -export function __wbg_addEventListener_bc4a7ad4cc72c6bf() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4)); -}, arguments) }; - -export function __wbg_dispatchEvent_1dc222127c2ec453() { return handleError(function (arg0, arg1) { - const ret = getObject(arg0).dispatchEvent(getObject(arg1)); - return ret; -}, arguments) }; - -export function __wbg_removeEventListener_acfc154b998d806b() { return handleError(function (arg0, arg1, arg2, arg3) { - getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3)); -}, arguments) }; - export function __wbg_byobRequest_643426f0037311f0(arg0) { const ret = getObject(arg0).byobRequest; return isLikeNone(ret) ? 0 : addHeapObject(ret); @@ -1202,43 +1206,6 @@ export function __wbg_close_0b618a762cdb578b() { return handleError(function (ar getObject(arg0).close(); }, arguments) }; -export function __wbg_signal_8fbb4942ce477464(arg0) { - const ret = getObject(arg0).signal; - return addHeapObject(ret); -}; - -export function __wbg_new_92cc7d259297256c() { return handleError(function () { - const ret = new AbortController(); - return addHeapObject(ret); -}, arguments) }; - -export function __wbg_abort_510372063dd66b29(arg0) { - getObject(arg0).abort(); -}; - -export function __wbg_wasClean_06aba8a282b21973(arg0) { - const ret = getObject(arg0).wasClean; - return ret; -}; - -export function __wbg_code_c25ac89aa8108189(arg0) { - const ret = getObject(arg0).code; - return ret; -}; - -export function __wbg_reason_ab96417c470b0f79(arg0, arg1) { - const ret = getObject(arg1).reason; - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len1; - getInt32Memory0()[arg0 / 4 + 0] = ptr1; -}; - -export function __wbg_newwitheventinitdict_ff303f34f1b980fa() { return handleError(function (arg0, arg1, arg2) { - const ret = new CloseEvent(getStringFromWasm0(arg0, arg1), getObject(arg2)); - return addHeapObject(ret); -}, arguments) }; - export function __wbg_instanceof_Response_b5451a06784a2404(arg0) { let result; try { @@ -1282,6 +1249,11 @@ export function __wbg_append_b2e8ed692fc5eb6e() { return handleError(function (a getObject(arg0).append(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); }, arguments) }; +export function __wbg_now_ef71656beb948bc8(arg0) { + const ret = getObject(arg0).now(); + return ret; +}; + export function __wbg_close_23aa806471e38253() { return handleError(function (arg0) { getObject(arg0).close(); }, arguments) }; @@ -1290,11 +1262,6 @@ export function __wbg_enqueue_fe9e340e2bc8714b() { return handleError(function ( getObject(arg0).enqueue(getObject(arg1)); }, arguments) }; -export function __wbg_newwithstrandinit_11fbc38beb4c26b0() { return handleError(function (arg0, arg1, arg2) { - const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2)); - return addHeapObject(ret); -}, arguments) }; - export function __wbg_readyState_2599ffe07703eeea(arg0) { const ret = getObject(arg0).readyState; return ret; @@ -1321,6 +1288,79 @@ export function __wbg_send_8e8f1c88be375fc1() { return handleError(function (arg getObject(arg0).send(getArrayU8FromWasm0(arg1, arg2)); }, arguments) }; +export function __wbg_wasClean_06aba8a282b21973(arg0) { + const ret = getObject(arg0).wasClean; + return ret; +}; + +export function __wbg_code_c25ac89aa8108189(arg0) { + const ret = getObject(arg0).code; + return ret; +}; + +export function __wbg_reason_ab96417c470b0f79(arg0, arg1) { + const ret = getObject(arg1).reason; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; +}; + +export function __wbg_newwitheventinitdict_ff303f34f1b980fa() { return handleError(function (arg0, arg1, arg2) { + const ret = new CloseEvent(getStringFromWasm0(arg0, arg1), getObject(arg2)); + return addHeapObject(ret); +}, arguments) }; + +export function __wbg_data_bbdd2d77ab2f7e78(arg0) { + const ret = getObject(arg0).data; + return addHeapObject(ret); +}; + +export function __wbg_view_38a0bacb59ad00ee(arg0) { + const ret = getObject(arg0).view; + return isLikeNone(ret) ? 0 : addHeapObject(ret); +}; + +export function __wbg_respond_fee44bba73c2fc8a() { return handleError(function (arg0, arg1) { + getObject(arg0).respond(arg1 >>> 0); +}, arguments) }; + +export function __wbg_addEventListener_f984e99465a6a7f4() { return handleError(function (arg0, arg1, arg2, arg3) { + getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3)); +}, arguments) }; + +export function __wbg_addEventListener_bc4a7ad4cc72c6bf() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { + getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4)); +}, arguments) }; + +export function __wbg_dispatchEvent_1dc222127c2ec453() { return handleError(function (arg0, arg1) { + const ret = getObject(arg0).dispatchEvent(getObject(arg1)); + return ret; +}, arguments) }; + +export function __wbg_removeEventListener_acfc154b998d806b() { return handleError(function (arg0, arg1, arg2, arg3) { + getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3)); +}, arguments) }; + +export function __wbg_signal_8fbb4942ce477464(arg0) { + const ret = getObject(arg0).signal; + return addHeapObject(ret); +}; + +export function __wbg_new_92cc7d259297256c() { return handleError(function () { + const ret = new AbortController(); + return addHeapObject(ret); +}, arguments) }; + +export function __wbg_abort_510372063dd66b29(arg0) { + getObject(arg0).abort(); +}; + +export function __wbg_newwithstrandinit_11fbc38beb4c26b0() { return handleError(function (arg0, arg1, arg2) { + const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2)); + return addHeapObject(ret); +}, arguments) }; + export function __wbg_new_75208e29bddfd88c() { const ret = new Array(); return addHeapObject(ret); @@ -1552,38 +1592,38 @@ export function __wbindgen_memory() { return addHeapObject(ret); }; -export function __wbindgen_closure_wrapper1628(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 668, __wbg_adapter_32); +export function __wbindgen_closure_wrapper2392(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1028, __wbg_adapter_32); return addHeapObject(ret); }; -export function __wbindgen_closure_wrapper2508(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1070, __wbg_adapter_35); +export function __wbindgen_closure_wrapper2921(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1190, __wbg_adapter_35); return addHeapObject(ret); }; -export function __wbindgen_closure_wrapper2510(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1070, __wbg_adapter_38); +export function __wbindgen_closure_wrapper2923(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1190, __wbg_adapter_35); return addHeapObject(ret); }; -export function __wbindgen_closure_wrapper2512(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1070, __wbg_adapter_35); +export function __wbindgen_closure_wrapper2925(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1190, __wbg_adapter_40); return addHeapObject(ret); }; -export function __wbindgen_closure_wrapper2514(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1070, __wbg_adapter_35); +export function __wbindgen_closure_wrapper2927(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1190, __wbg_adapter_35); return addHeapObject(ret); }; -export function __wbindgen_closure_wrapper3205(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1404, __wbg_adapter_45); +export function __wbindgen_closure_wrapper3396(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1396, __wbg_adapter_45); return addHeapObject(ret); }; -export function __wbindgen_closure_wrapper3300(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1459, __wbg_adapter_48); +export function __wbindgen_closure_wrapper3492(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1450, __wbg_adapter_48); return addHeapObject(ret); }; diff --git a/modules/client/pkg/hyperclient_bg.wasm b/modules/client/pkg/hyperclient_bg.wasm index 26493730e..146f14821 100644 Binary files a/modules/client/pkg/hyperclient_bg.wasm and b/modules/client/pkg/hyperclient_bg.wasm differ diff --git a/modules/client/pkg/hyperclient_bg.wasm.d.ts b/modules/client/pkg/hyperclient_bg.wasm.d.ts index fbf712d8a..4b6ec9116 100644 --- a/modules/client/pkg/hyperclient_bg.wasm.d.ts +++ b/modules/client/pkg/hyperclient_bg.wasm.d.ts @@ -1,10 +1,6 @@ /* tslint:disable */ /* eslint-disable */ export const memory: WebAssembly.Memory; -export function query_request_status(a: number, b: number): number; -export function query_response_status(a: number, b: number): number; -export function timeout_post_request(a: number, b: number): number; -export function subscribe_to_request_status(a: number, b: number, c: number): number; export function __wbg_jschainconfig_free(a: number): void; export function __wbg_get_jschainconfig_rpc_url(a: number, b: number): void; export function __wbg_set_jschainconfig_rpc_url(a: number, b: number, c: number): void; @@ -16,8 +12,7 @@ export function __wbg_get_jschainconfig_handler_address(a: number, b: number): v export function __wbg_set_jschainconfig_handler_address(a: number, b: number, c: number): void; export function __wbg_get_jschainconfig_consensus_state_id(a: number, b: number): void; export function __wbg_set_jschainconfig_consensus_state_id(a: number, b: number, c: number): void; -export function __wbg_get_jschainconfig_hash_algo(a: number): number; -export function __wbg_set_jschainconfig_hash_algo(a: number, b: number): void; +export function __wbg_jshyperbridgeconfig_free(a: number): void; export function __wbg_jsclientconfig_free(a: number): void; export function __wbg_get_jsclientconfig_source(a: number): number; export function __wbg_set_jsclientconfig_source(a: number, b: number): void; @@ -51,32 +46,34 @@ export function __wbg_set_jsresponse_timeout_timestamp(a: number, b: number): vo export function __wbg_set_jsresponse_gas_limit(a: number, b: number): void; export function __wbg_get_jsresponse_timeout_timestamp(a: number): number; export function __wbg_get_jsresponse_gas_limit(a: number): number; -export function __wbg_intounderlyingsource_free(a: number): void; -export function intounderlyingsource_pull(a: number, b: number): number; -export function intounderlyingsource_cancel(a: number): void; +export function __wbg_get_jshyperbridgeconfig_rpc_url(a: number, b: number): void; +export function __wbg_set_jshyperbridgeconfig_rpc_url(a: number, b: number, c: number): void; +export function query_request_status(a: number, b: number): number; +export function query_response_status(a: number, b: number): number; +export function timeout_post_request(a: number, b: number): number; +export function subscribe_to_request_status(a: number, b: number, c: number): number; +export function __wbg_intounderlyingsink_free(a: number): void; +export function intounderlyingsink_write(a: number, b: number): number; +export function intounderlyingsink_close(a: number): number; +export function intounderlyingsink_abort(a: number, b: number): number; export function __wbg_intounderlyingbytesource_free(a: number): void; export function intounderlyingbytesource_type(a: number, b: number): void; export function intounderlyingbytesource_autoAllocateChunkSize(a: number): number; export function intounderlyingbytesource_start(a: number, b: number): void; export function intounderlyingbytesource_pull(a: number, b: number): number; export function intounderlyingbytesource_cancel(a: number): void; -export function __wbg_intounderlyingsink_free(a: number): void; -export function intounderlyingsink_write(a: number, b: number): number; -export function intounderlyingsink_close(a: number): number; -export function intounderlyingsink_abort(a: number, b: number): number; -export function rustsecp256k1_v0_9_2_context_create(a: number): number; -export function rustsecp256k1_v0_9_2_context_destroy(a: number): void; -export function rustsecp256k1_v0_9_2_default_illegal_callback_fn(a: number, b: number): void; -export function rustsecp256k1_v0_9_2_default_error_callback_fn(a: number, b: number): void; +export function __wbg_intounderlyingsource_free(a: number): void; +export function intounderlyingsource_pull(a: number, b: number): number; +export function intounderlyingsource_cancel(a: number): void; export function __wbindgen_malloc(a: number, b: number): number; export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number; export const __wbindgen_export_2: WebAssembly.Table; -export function wasm_bindgen__convert__closures__invoke0_mut__h55df6f4dbbec5483(a: number, b: number): void; -export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h5e9cc6c4a192068d(a: number, b: number, c: number): void; -export function _dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hf936b6f64a75e0db(a: number, b: number): void; -export function wasm_bindgen__convert__closures__invoke0_mut__hedfac42a0d077e67(a: number, b: number): void; -export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hf3f3671bbd9bc1e7(a: number, b: number, c: number): void; +export function wasm_bindgen__convert__closures__invoke0_mut__hdc28361670d72cce(a: number, b: number): void; +export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h367ff8bffb6f7b2e(a: number, b: number, c: number): void; +export function _dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h9aae2ac53c63e19e(a: number, b: number): void; +export function wasm_bindgen__convert__closures__invoke0_mut__h3c8b3f369d6af9ec(a: number, b: number): void; +export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb494be3815513233(a: number, b: number, c: number): void; export function __wbindgen_add_to_stack_pointer(a: number): number; export function __wbindgen_free(a: number, b: number, c: number): void; export function __wbindgen_exn_store(a: number): void; -export function wasm_bindgen__convert__closures__invoke2_mut__h5f0b1403bdde1a12(a: number, b: number, c: number, d: number): void; +export function wasm_bindgen__convert__closures__invoke2_mut__h124eb194c086a246(a: number, b: number, c: number, d: number): void; diff --git a/modules/client/pkg/package.json b/modules/client/pkg/package.json index 9d72ff303..b1b209ed6 100644 --- a/modules/client/pkg/package.json +++ b/modules/client/pkg/package.json @@ -1,5 +1,5 @@ { - "name": "@polytope-labs/hyperclient", + "name": "hyperclient", "version": "0.1.0", "files": [ "hyperclient_bg.wasm", diff --git a/modules/client/src/interfaces.rs b/modules/client/src/interfaces.rs index 4bee19a5d..2c151fe1f 100644 --- a/modules/client/src/interfaces.rs +++ b/modules/client/src/interfaces.rs @@ -5,7 +5,7 @@ use ismp::{ host::StateMachine, router::{Post, PostResponse}, }; -use sp_core::H160; +use primitive_types::H160; use wasm_bindgen::prelude::wasm_bindgen; #[wasm_bindgen(getter_with_clone)] @@ -16,8 +16,12 @@ pub struct JsChainConfig { pub host_address: Vec, pub handler_address: Vec, pub consensus_state_id: Vec, - /// Keccak = 1, Blake2 = 2 - pub hash_algo: i32, +} + +#[wasm_bindgen(getter_with_clone)] +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct JsHyperbridgeConfig { + pub rpc_url: String, } #[wasm_bindgen(getter_with_clone)] @@ -25,7 +29,7 @@ pub struct JsChainConfig { pub struct JsClientConfig { pub source: JsChainConfig, pub dest: JsChainConfig, - pub hyperbridge: JsChainConfig, + pub hyperbridge: JsHyperbridgeConfig, } impl TryFrom for ClientConfig { @@ -64,8 +68,6 @@ impl TryFrom for ClientConfig { } else { let conf = SubstrateConfig { rpc_url: val.rpc_url.clone(), - state_machine: StateMachine::from_str(&val.state_machine) - .map_err(|e| anyhow!("{e:?}"))?, consensus_state_id: { if val.consensus_state_id.len() != 4 { Err(anyhow!("Invalid consensus state id"))? @@ -74,22 +76,26 @@ impl TryFrom for ClientConfig { dest.copy_from_slice(&val.consensus_state_id); dest }, - hash_algo: { - if val.hash_algo == 1 { - HashAlgorithm::Keccak - } else { - HashAlgorithm::Blake2 - } - }, + hash_algo: HashAlgorithm::Keccak, }; Ok(ChainConfig::Substrate(conf)) } }; + let to_hyperbridge_config = |val: &JsHyperbridgeConfig| { + let conf = SubstrateConfig { + rpc_url: val.rpc_url.clone(), + consensus_state_id: [0u8; 4], + hash_algo: HashAlgorithm::Keccak, + }; + + Ok::(ChainConfig::Substrate(conf)) + }; + let source_config = to_config(&value.source)?; let dest_config = to_config(&value.dest)?; - let hyperbridge = to_config(&value.hyperbridge)?; + let hyperbridge = to_hyperbridge_config(&value.hyperbridge)?; Ok(ClientConfig { source: source_config, dest: dest_config, hyperbridge }) } @@ -166,7 +172,7 @@ impl TryFrom for PostResponse { #[cfg(test)] mod tests { use crate::{ - interfaces::{JsChainConfig, JsClientConfig, JsPost, JsResponse}, + interfaces::{JsChainConfig, JsClientConfig, JsHyperbridgeConfig, JsPost, JsResponse}, types::{ChainConfig, ClientConfig, EvmConfig, HashAlgorithm, SubstrateConfig}, }; use ethers::prelude::H160; @@ -199,8 +205,7 @@ mod tests { let hyperbrige_config = SubstrateConfig { rpc_url: "ws://127.0.0.1:9990".to_string(), - state_machine: StateMachine::Kusama(2000), - consensus_state_id: *b"PARA", + consensus_state_id: [0u8; 4], hash_algo: HashAlgorithm::Keccak, }; let config = ClientConfig { @@ -215,7 +220,6 @@ mod tests { host_address: BSC_HOST.0.to_vec(), handler_address: BSC_HANDLER.0.to_vec(), consensus_state_id: b"BSC0".to_vec(), - hash_algo: 0, }; let js_dest = JsChainConfig { @@ -224,17 +228,9 @@ mod tests { host_address: OP_HOST.0.to_vec(), handler_address: OP_HANDLER.0.to_vec(), consensus_state_id: b"ETH0".to_vec(), - hash_algo: 0, }; - let js_hyperbridge = JsChainConfig { - rpc_url: "ws://127.0.0.1:9990".to_string(), - state_machine: "KUSAMA-2000".to_string(), - host_address: Default::default(), - handler_address: Default::default(), - consensus_state_id: b"PARA".to_vec(), - hash_algo: 1, - }; + let js_hyperbridge = JsHyperbridgeConfig { rpc_url: "ws://127.0.0.1:9990".to_string() }; let js_client_conf = JsClientConfig { source: js_source, dest: js_dest, hyperbridge: js_hyperbridge }; diff --git a/modules/client/src/providers/evm_chain.rs b/modules/client/src/providers/evm_chain.rs index 7e5be5920..d40b0913b 100644 --- a/modules/client/src/providers/evm_chain.rs +++ b/modules/client/src/providers/evm_chain.rs @@ -4,6 +4,7 @@ use crate::{ }; use ethers::prelude::Middleware; +use super::StreamItem; use crate::types::{EvmStateProof, SubstrateStateProof}; use anyhow::{anyhow, Context, Error}; use core::time::Duration; @@ -178,64 +179,70 @@ impl Client for EvmClient { let interval = wasm_timer::Interval::new(Duration::from_secs(30)); let stream = stream::unfold( (initial_height, interval, client), - move |(mut latest_height, mut interval, client)| async move { + move |(latest_height, mut interval, client)| async move { let state_machine = client.state_machine; - loop { - interval.next().await; - let block_number = match client.client.get_block_number().await { - Ok(number) => number.low_u64(), - Err(err) => - return Some(( - Err(err).context(format!( - "Error encountered fetching latest block number for {state_machine:?}" - )), - (latest_height, interval, client), - )), - }; - - // in case we get old heights, best to ignore them - if block_number < latest_height { - continue; - } - - let contract = EvmHost::new(client.host_address, client.client.clone()); - let results = match contract - .events() - .address(client.host_address.into()) - .from_block(latest_height) - .to_block(block_number) - .query() - .await - { - Ok(events) => events, - Err(err) => - return Some(( - Err(err).context(format!( - "Failed to query events on {state_machine:?}" - )), - (latest_height, interval, client), - )), - }; + interval.next().await; + let block_number = match client.client.get_block_number().await { + Ok(number) => number.low_u64(), + Err(err) => + return Some(( + Err(err).context(format!( + "Error encountered fetching latest block number for {state_machine:?}" + )), + (latest_height, interval, client), + )), + }; - let events = results - .into_iter() - .filter_map(|ev| match ev { - EvmHostEvents::PostRequestHandledFilter(filter) - if filter.commitment == commitment.0 => - Some(filter), - _ => None, - }) - .collect::>(); - - // we only want the highest event - if let Some(event) = events.last() { - return Some((Ok(event.clone()), (block_number + 1, interval, client))) - } else { - latest_height = block_number + 1; - } + // in case we get old heights, best to ignore them + if block_number < latest_height { + return Some((Ok(StreamItem::NoOp), (block_number, interval, client))) } + + let contract = EvmHost::new(client.host_address, client.client.clone()); + let results = match contract + .events() + .address(client.host_address.into()) + .from_block(latest_height) + .to_block(block_number) + .query() + .await + { + Ok(events) => events, + Err(err) => + return Some(( + Err(err) + .context(format!("Failed to query events on {state_machine:?}")), + (latest_height, interval, client), + )), + }; + + let events = results + .into_iter() + .filter_map(|ev| match ev { + EvmHostEvents::PostRequestHandledFilter(filter) + if filter.commitment == commitment.0 => + Some(filter), + _ => None, + }) + .collect::>(); + + // we only want the highest event + let value = if let Some(event) = events.last() { + StreamItem::Item(event.clone()) + } else { + StreamItem::NoOp + }; + + Some((Ok(value), (block_number + 1, interval, client))) }, - ); + ) + .filter_map(|item| async move { + match item { + Ok(StreamItem::NoOp) => None, + Ok(StreamItem::Item(event)) => Some(Ok(event)), + Err(err) => Some(Err(err)), + } + }); Ok(Box::pin(stream)) } @@ -248,58 +255,63 @@ impl Client for EvmClient { let interval = wasm_timer::Interval::new(Duration::from_secs(30)); let stream = stream::unfold( (initial_height, interval, self.clone()), - move |(mut latest_height, mut interval, client)| async move { + move |(latest_height, mut interval, client)| async move { let state_machine = client.state_machine; - loop { - interval.next().await; - let block_number = match client.client.get_block_number().await { - Ok(number) => number.low_u64(), - Err(err) => - return Some(( - Err(err).context(format!( - "Error encountered fetching latest block number for {state_machine:?}" - )), - (latest_height,interval, client), - )), - }; + interval.next().await; + let block_number = match client.client.get_block_number().await { + Ok(number) => number.low_u64(), + Err(err) => + return Some(( + Err(err).context(format!( + "Error encountered fetching latest block number for {state_machine:?}" + )), + (latest_height, interval, client), + )), + }; - // in case we get old heights, best to ignore them - if block_number < latest_height { - continue; - } - - let contract = Handler::new(client.ismp_handler, client.client.clone()); - let results = match contract - .events() - .address(client.ismp_handler.into()) - .from_block(latest_height) - .to_block(block_number) - .query() - .await - { - Ok(events) => events, - Err(err) => - return Some(( - Err(err).context(format!( - "Failed to query events on {state_machine:?}" - )), - (latest_height, interval, client), - )), - }; - let mut events = results - .into_iter() - .map(|ev| ev.into()) - .collect::>(); - // we only want the highest event - events.sort_by(|a, b| a.latest_height.cmp(&b.latest_height)); - if let Some(event) = events.last() { - return Some((Ok(event.clone()), (block_number + 1, interval, client))) - } else { - latest_height = block_number + 1; - } + // in case we get old heights, best to ignore them + if block_number < latest_height { + return Some((Ok(StreamItem::NoOp), (block_number, interval, client))) } + + let contract = Handler::new(client.ismp_handler, client.client.clone()); + let results = match contract + .events() + .address(client.ismp_handler.into()) + .from_block(latest_height) + .to_block(block_number) + .query() + .await + { + Ok(events) => events, + Err(err) => + return Some(( + Err(err) + .context(format!("Failed to query events on {state_machine:?}")), + (latest_height, interval, client), + )), + }; + let mut events = + results.into_iter().map(|ev| ev.into()).collect::>(); + // we only want the highest event + events.sort_by(|a, b| a.latest_height.cmp(&b.latest_height)); + // we only want the highest event + let value = if let Some(event) = events.last() { + StreamItem::Item(event.clone()) + } else { + StreamItem::NoOp + }; + + Some((Ok(value), (block_number + 1, interval, client))) }, - ); + ) + .filter_map(|item| async move { + match item { + Ok(StreamItem::NoOp) => None, + Ok(StreamItem::Item(event)) => Some(Ok(event)), + Err(err) => Some(Err(err)), + } + }); Ok(Box::pin(stream)) } diff --git a/modules/client/src/providers/mod.rs b/modules/client/src/providers/mod.rs index b8343d593..1ad59c371 100644 --- a/modules/client/src/providers/mod.rs +++ b/modules/client/src/providers/mod.rs @@ -1,3 +1,9 @@ pub mod evm_chain; pub mod global; +pub mod rpc_wrapper; pub mod substrate; + +pub enum StreamItem { + NoOp, + Item(T), +} diff --git a/modules/client/src/providers/rpc_wrapper.rs b/modules/client/src/providers/rpc_wrapper.rs new file mode 100644 index 000000000..ebd7ea6f0 --- /dev/null +++ b/modules/client/src/providers/rpc_wrapper.rs @@ -0,0 +1,57 @@ +use futures::{StreamExt, TryStreamExt}; +use reconnecting_jsonrpsee_ws_client::{Client, SubscriptionId}; +use std::ops::Deref; +use subxt::{ + error::RpcError, + rpc::{RawValue, RpcClientT, RpcFuture, RpcSubscription}, +}; + +pub struct ClientWrapper(pub Client); + +impl Deref for ClientWrapper { + type Target = Client; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl RpcClientT for ClientWrapper { + fn request_raw<'a>( + &'a self, + method: &'a str, + params: Option>, + ) -> RpcFuture<'a, Box> { + Box::pin(async move { + let res = self + .0 + .request_raw(method.to_string(), params) + .await + .map_err(|e| RpcError::ClientError(Box::new(e)))?; + Ok(res) + }) + } + + fn subscribe_raw<'a>( + &'a self, + sub: &'a str, + params: Option>, + unsub: &'a str, + ) -> RpcFuture<'a, RpcSubscription> { + Box::pin(async move { + let stream = self + .0 + .subscribe_raw(sub.to_string(), params, unsub.to_string()) + .await + .map_err(|e| RpcError::ClientError(Box::new(e)))?; + + let id = match stream.id() { + SubscriptionId::Str(id) => Some(id.clone().into_owned()), + SubscriptionId::Num(id) => Some(id.to_string()), + }; + + let stream = stream.map_err(|e| RpcError::ClientError(Box::new(e))).boxed(); + Ok(RpcSubscription { stream, id }) + }) + } +} diff --git a/modules/client/src/providers/substrate.rs b/modules/client/src/providers/substrate.rs index 7d2b1a71b..40d147bbb 100644 --- a/modules/client/src/providers/substrate.rs +++ b/modules/client/src/providers/substrate.rs @@ -1,13 +1,14 @@ +use super::StreamItem; use crate::{ providers::global::{Client, RequestOrResponse}, - runtime, + runtime::{self}, types::{BoxStream, Extrinsic, HashAlgorithm, SubstrateStateProof}, }; use anyhow::{anyhow, Error}; use codec::{Decode, Encode}; use core::time::Duration; use ethers::prelude::{H160, H256}; -use futures::stream; +use futures::{stream, StreamExt}; use hashbrown::HashMap; use hex_literal::hex; use ismp::{ @@ -17,9 +18,13 @@ use ismp::{ messaging::Message, }; use ismp_solidity_abi::evm_host::PostRequestHandledFilter; +use reconnecting_jsonrpsee_ws_client::Client as ReconnectClient; use serde::{Deserialize, Serialize}; +use std::sync::Arc; use subxt::{config::Header, rpc_params, OnlineClient}; +use super::rpc_wrapper::ClientWrapper; + #[derive(Debug, Clone)] pub struct SubstrateClient { /// RPC url of a hyperbridge node @@ -34,10 +39,23 @@ pub struct SubstrateClient { impl SubstrateClient { pub async fn new( rpc_url: String, - state_machine: StateMachineId, hashing: HashAlgorithm, + consensus_state_id: [u8; 4], ) -> Result { - let client = OnlineClient::::from_url(rpc_url.clone()).await?; + let rpc = ReconnectClient::builder().build(rpc_url.clone()).await?; + + let client = OnlineClient::::from_rpc_client(Arc::new(ClientWrapper(rpc))).await?; + let state_machine_address = runtime::api::storage().parachain_info().parachain_id(); + let state_id = client + .storage() + .at_latest() + .await? + .fetch(&state_machine_address) + .await? + .ok_or(anyhow!("Couldn't get para chain id"))?; + + let state_machine = + StateMachineId { state_id: StateMachine::Kusama(state_id.0), consensus_state_id }; Ok(Self { rpc_url, client, state_machine, hashing }) } @@ -102,6 +120,7 @@ impl Client for SubstrateClient { async fn query_request_receipt(&self, request_hash: H256) -> Result { let addr = runtime::api::storage().ismp().request_receipts(&request_hash); let receipt = self.client.storage().at_latest().await?.fetch(&addr).await?; + if let Some(receipt) = receipt { Ok(H160::from_slice(&receipt[..20])) } else { @@ -141,7 +160,7 @@ impl Client for SubstrateClient { let initial_height: u64 = self.client.blocks().at_latest().await?.number().into(); let stream = stream::unfold( (initial_height, subscription, self.clone()), - move |(mut latest_height, mut subscription, client)| { + move |(latest_height, mut subscription, client)| { let item = item.clone(); async move { loop { @@ -151,7 +170,10 @@ impl Client for SubstrateClient { // log::error!( // "Error encountered while watching finalized heads: {err:?}" // ); - continue; + return Some(( + Ok(StreamItem::NoOp), + (latest_height, subscription, client), + )) }, None => return None, }; @@ -164,7 +186,10 @@ impl Client for SubstrateClient { Err(_err) => { // log::error!("Error encountered while querying ismp events // {err:?}"); - continue; + return Some(( + Ok(StreamItem::NoOp), + (latest_height, subscription, client), + )) }, }; @@ -184,19 +209,28 @@ impl Client for SubstrateClient { }); let value = match event { - Some(event) => - Some((Ok(event), (header.number().into(), subscription, client))), - None => { - latest_height = header.number().into(); - continue; - }, + Some(event) => Some(( + Ok(StreamItem::Item(event)), + (header.number().into(), subscription, client), + )), + None => Some(( + Ok(StreamItem::NoOp), + (header.number().into(), subscription, client), + )), }; return value; } } }, - ); + ) + .filter_map(|item| async move { + match item { + Ok(StreamItem::NoOp) => None, + Ok(StreamItem::Item(event)) => Some(Ok(event)), + Err(err) => Some(Err(err)), + } + }); Ok(Box::pin(stream)) } diff --git a/modules/client/src/streams.rs b/modules/client/src/streams.rs index e90b57b8c..3301d0626 100644 --- a/modules/client/src/streams.rs +++ b/modules/client/src/streams.rs @@ -1,5 +1,8 @@ use crate::{ - providers::global::{Client, RequestOrResponse}, + providers::{ + global::{Client, RequestOrResponse}, + StreamItem, + }, types::{BoxStream, MessageStatus, PostStreamState}, Keccak256, }; @@ -248,19 +251,23 @@ pub async fn timeout_stream(timeout: u64, client: impl Client + Clone) -> BoxStr }; }; - loop { - let response = lambda().await; + let response = lambda().await; - let value = match response { - Ok(val) if val => Some((Ok(MessageStatus::Timeout), ())), - Ok(val) if !val => continue, - Err(e) => - Some((Err(anyhow!("Encountered an error in timeout stream: {:?}", e)), ())), - _ => None, - }; + let value = match response { + Ok(true) => Some((Ok(StreamItem::Item(MessageStatus::Timeout)), ())), + Ok(false) => Some((Ok(StreamItem::NoOp), ())), + Err(e) => + Some((Err(anyhow!("Encountered an error in timeout stream: {:?}", e)), ())), + }; - return value - } + return value + } + }) + .filter_map(|item| async move { + match item { + Ok(StreamItem::NoOp) => None, + Ok(StreamItem::Item(event)) => Some(Ok(event)), + Err(err) => Some(Err(err)), } }); diff --git a/modules/client/src/tests/streams.rs b/modules/client/src/tests/streams.rs index 952dcef04..b2c3a4034 100644 --- a/modules/client/src/tests/streams.rs +++ b/modules/client/src/tests/streams.rs @@ -1,3 +1,6 @@ +#![cfg(target_arch = "wasm32")] +use wasm_bindgen_test::*; + use crate::{ internals::timeout_request_stream, mock::erc_20::Erc20, streams::query_request_status_stream, types::ClientConfig, @@ -12,35 +15,55 @@ use ethers::{ use crate::{ internals::query_request_status_internal, + providers::global::Client, types::{ChainConfig, EvmConfig, HashAlgorithm, MessageStatus, SubstrateConfig, TimeoutStatus}, }; use ethers::{ + contract::parse_log, prelude::{transaction::eip2718::TypedTransaction, NameOrAddress, TransactionRequest}, utils::hex, }; -use frame_support::crypto::ecdsa::ECDSAExt; use futures::StreamExt; use hex_literal::hex; -use ismp::host::{Ethereum, StateMachine}; +use ismp::{ + consensus::StateMachineId, + host::{Ethereum, StateMachine}, + router, +}; use ismp_solidity_abi::{ - evm_host::EvmHost, + evm_host::{EvmHost, PostRequestEventFilter}, ping_module::{PingMessage, PingModule}, }; -use sp_core::Pair; use std::{sync::Arc, time::Duration}; -const OP_HOST: H160 = H160(hex!("1B58A47e61Ca7604b634CBB00b4e275cCd7c9E95")); -const BSC_HOST: H160 = H160(hex!("022DDE07A21d8c553978b006D93CDe68ac83e677")); -const OP_HANDLER: H160 = H160(hex!("a25151598Dc180fc03635858f37bDF8427f47845")); -const BSC_HANDLER: H160 = H160(hex!("43a0BcC347894303f93905cE137CB3b804bE990d")); +const OP_HOST: H160 = H160(hex!("39f3D7a7783653a04e2970e35e5f32F0e720daeB")); +const OP_HANDLER: H160 = H160(hex!("8738b27E29Af7c92ba2AF72B2fcF01C8934e3Db0")); + +const SEPOLIA_HOST: H160 = H160(hex!("e4226c474A6f4BF285eA80c2f01c0942B04323e5")); +const SEPOLIA_HANDLER: H160 = H160(hex!("F763D969aDC8281b1A8459Bde4CE86BA811b0Aaf")); + +const BSC_HOST: H160 = H160(hex!("4e5bbdd9fE89F54157DDb64b21eD4D1CA1CDf9a6")); +const BSC_HANDLER: H160 = H160(hex!("3aBA86C71C86353e5a96E98e1E08411063B5e2DB")); + +const PING_MODULE: H160 = H160(hex!("d4812d6A3b9fB46feA314260Cbb61D57EBc71D7F")); -#[tokio::test] -#[ignore] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +/// Run the tests by `$ wasm-pack test --firefox --headless` + +fn init_tracing() { + console_error_panic_hook::set_once(); + let _ = tracing_wasm::try_set_as_global_default(); +} + +#[wasm_bindgen_test] async fn subscribe_to_request_status() -> Result<(), anyhow::Error> { - dotenv::dotenv().ok(); - let signing_key = std::env::var("SIGNING_KEY").unwrap(); - let bsc_url = std::env::var("BSC_URL").unwrap(); - let op_url = std::env::var("OP_URL").unwrap(); + init_tracing(); + + let signing_key = env!("SIGNING_KEY").to_string(); + let bsc_url = env!("BSC_URL").to_string(); + let op_url = env!("OP_URL").to_string(); + let source_chain = EvmConfig { rpc_url: bsc_url.clone(), state_machine: StateMachine::Bsc, @@ -58,8 +81,7 @@ async fn subscribe_to_request_status() -> Result<(), anyhow::Error> { }; let hyperbrige_config = SubstrateConfig { - rpc_url: "ws://127.0.0.1:9990".to_string(), - state_machine: StateMachine::Kusama(2000), + rpc_url: "wss://hyperbridge-gargantua-rpc.blockops.network:443".to_string(), consensus_state_id: *b"PARA", hash_algo: HashAlgorithm::Keccak, }; @@ -70,16 +92,15 @@ async fn subscribe_to_request_status() -> Result<(), anyhow::Error> { }; // Send Ping Message - let signer = sp_core::ecdsa::Pair::from_seed_slice(&hex::decode(signing_key).unwrap()).unwrap(); + let signer = hex::decode(signing_key).unwrap(); let provider = Arc::new(Provider::::try_connect(&bsc_url).await?); - let signer = LocalWallet::from(SecretKey::from_slice(signer.seed().as_slice())?) + let signer = LocalWallet::from(SecretKey::from_slice(signer.as_slice())?) .with_chain_id(provider.get_chainid().await?.low_u64()); let client = Arc::new(provider.with_signer(signer)); - let ping_addr = H160(hex!("4c1b6031d5BB8A52EF7A13b32852fbE070733FCA")); - let ping = PingModule::new(ping_addr, client.clone()); + let ping_addr = H160(hex!("d4812d6A3b9fB46feA314260Cbb61D57EBc71D7F")); + let ping = PingModule::new(PING_MODULE, client.clone()); let chain = StateMachine::Bsc; let host_addr = ping.host().await.context(format!("Error in {chain:?}"))?; - dbg!(&host_addr); let host = EvmHost::new(host_addr, client.clone()); let erc_20 = Erc20::new(host.dai().await.context(format!("Error in {chain:?}"))?, client.clone()); @@ -106,31 +127,19 @@ async fn subscribe_to_request_status() -> Result<(), anyhow::Error> { .await .context(format!("Error in {chain:?}"))? .await - .context(format!("Error in {chain:?}"))?; + .context(format!("Error in {chain:?}"))? + .unwrap(); - assert!(receipt.is_some()); - let block = receipt.unwrap().block_number.unwrap(); - let events = host - .events() - .address(source_chain.host_address.into()) - .from_block(block) - .to_block(block) - .query() - .await?; - let mut event = events.into_iter().filter_map(|ev| ev.try_into().ok()); - - let event = event.find_map(|ev| match ev { - ismp::events::Event::PostRequest(post) => - if post.dest == StateMachine::Ethereum(Ethereum::Optimism) { - Some(post) - } else { - None - }, - _ => None, - }); + let post: router::Post = receipt + .logs + .into_iter() + .find_map(|log| parse_log::(log).ok()) + .expect("Tx should emit post request") + .try_into()?; + tracing::info!("Got PostRequest {post:#?}"); + let block = receipt.block_number.unwrap(); + tracing::info!("\n\nTx block: {block}\n\n"); - let post = event.expect("Post request event should be available"); - dbg!(&post.timeout_timestamp); let source_client = config.source_chain().await?; let dest_client = config.dest_chain().await?; let hyperbridge_client = config.hyperbridge_client().await?; @@ -146,23 +155,23 @@ async fn subscribe_to_request_status() -> Result<(), anyhow::Error> { while let Some(res) = stream.next().await { match res { Ok(status) => { - println!("Got Status {:?}", status); + tracing::info!("Got Status {:?}", status); }, Err(e) => { - println!("{e:?}") + tracing::info!("Error: {e:?}") }, } } Ok(()) } -#[tokio::test] -#[ignore] +#[wasm_bindgen_test] async fn test_timeout_request() -> Result<(), anyhow::Error> { - dotenv::dotenv().ok(); - let signing_key = std::env::var("SIGNING_KEY").unwrap(); - let bsc_url = std::env::var("BSC_URL").unwrap(); - let op_url = std::env::var("OP_URL").unwrap(); + init_tracing(); + + let signing_key = env!("SIGNING_KEY").to_string(); + let bsc_url = env!("BSC_URL").to_string(); + let sepolia_url = env!("SEPOLIA_URL").to_string(); let source_chain = EvmConfig { rpc_url: bsc_url.clone(), state_machine: StateMachine::Bsc, @@ -172,16 +181,15 @@ async fn test_timeout_request() -> Result<(), anyhow::Error> { }; let dest_chain = EvmConfig { - rpc_url: op_url, - state_machine: StateMachine::Ethereum(Ethereum::Optimism), - host_address: OP_HOST, - handler_address: OP_HANDLER, + rpc_url: sepolia_url, + state_machine: StateMachine::Ethereum(Ethereum::ExecutionLayer), + host_address: SEPOLIA_HOST, + handler_address: SEPOLIA_HANDLER, consensus_state_id: *b"ETH0", }; let hyperbrige_config = SubstrateConfig { - rpc_url: "ws://127.0.0.1:9990".to_string(), - state_machine: StateMachine::Kusama(2000), + rpc_url: "wss://hyperbridge-gargantua-rpc.blockops.network:443".to_string(), consensus_state_id: *b"PARA", hash_algo: HashAlgorithm::Keccak, }; @@ -192,19 +200,17 @@ async fn test_timeout_request() -> Result<(), anyhow::Error> { }; // Send Ping Message - let pair = sp_core::ecdsa::Pair::from_seed_slice(&hex::decode(signing_key).unwrap()).unwrap(); - + let pair = hex::decode(signing_key).unwrap(); let provider = Arc::new(Provider::::try_connect(&bsc_url).await?); let chain_id = provider.get_chainid().await?.low_u64(); - let signer = - LocalWallet::from(SecretKey::from_slice(pair.seed().as_slice())?).with_chain_id(chain_id); + let signer = LocalWallet::from(SecretKey::from_slice(pair.as_slice())?).with_chain_id(chain_id); let client = Arc::new(provider.with_signer(signer)); - let ping_addr = H160(hex!("4c1b6031d5BB8A52EF7A13b32852fbE070733FCA")); - let ping = PingModule::new(ping_addr, client.clone()); + let ping = PingModule::new(PING_MODULE, client.clone()); let chain = StateMachine::Bsc; let host_addr = ping.host().await.context(format!("Error in {chain:?}"))?; - dbg!(&host_addr); let host = EvmHost::new(host_addr, client.clone()); + tracing::info!("{:#?}", host.host_params().await?); + let erc_20 = Erc20::new(host.dai().await.context(format!("Error in {chain:?}"))?, client.clone()); let call = erc_20.approve(host_addr, U256::max_value()); @@ -216,9 +222,28 @@ async fn test_timeout_request() -> Result<(), anyhow::Error> { .context(format!("Error in {chain:?}"))? .await .context(format!("Error in {chain:?}"))?; + + let hyperbridge_client = config.hyperbridge_client().await?; + let mut stream = hyperbridge_client + .state_machine_update_notification(StateMachineId { + state_id: StateMachine::Bsc, + consensus_state_id: *b"BSC0", + }) + .await?; + // wait for a bsc update, before sending request + while let Some(res) = stream.next().await { + match res { + Ok(_) => { + tracing::info!("\n\nGot State Machine update for BSC\n\n"); + break + }, + _ => {}, + } + } + let call = ping.ping(PingMessage { dest: dest_chain.state_machine.to_string().as_bytes().to_vec().into(), - module: ping_addr.clone().into(), + module: PING_MODULE.clone().into(), timeout: 5 * 60, fee: U256::from(0u128), count: U256::from(1), @@ -230,38 +255,31 @@ async fn test_timeout_request() -> Result<(), anyhow::Error> { .await .context(format!("Error in {chain:?}"))? .await - .context(format!("Error in {chain:?}"))?; + .context(format!("Error in {chain:?}"))? + .unwrap(); - assert!(receipt.is_some()); - let block = receipt.unwrap().block_number.unwrap(); - let events = host - .events() - .address(source_chain.host_address.into()) - .from_block(block) - .to_block(block) - .query() - .await?; - let mut event = events.into_iter().filter_map(|ev| ev.try_into().ok()); - - let event = event.find_map(|ev| match ev { - ismp::events::Event::PostRequest(post) => - if post.dest == StateMachine::Ethereum(Ethereum::Optimism) { - Some(post) - } else { - None - }, - _ => None, - }); + let block = receipt.block_number.unwrap(); + tracing::info!("\n\nTx block: {block}\n\n"); + let post: router::Post = receipt + .logs + .into_iter() + .find_map(|log| parse_log::(log).ok()) + .expect("Tx should emit post request") + .try_into()?; + tracing::info!("Got PostRequest {post:#?}"); + + let block = receipt.block_number.unwrap(); + tracing::info!("\n\nTx block: {block}\n\n"); - let post = event.expect("Post request event should be available"); - dbg!(&post.timeout_timestamp); loop { let status = query_request_status_internal(post.clone(), config.clone()).await?; + tracing::info!("Got Status {status:?}"); if status == MessageStatus::Timeout { break } else { - println!("{status:?}"); - tokio::time::sleep(Duration::from_secs(2 * 60)).await; + let delay = Duration::from_secs(60); + tracing::info!("Waiting for {delay:?}"); + wasm_timer::Delay::new(delay).await?; } } @@ -270,16 +288,15 @@ async fn test_timeout_request() -> Result<(), anyhow::Error> { while let Some(res) = stream.next().await { match res { Ok(status) => { - println!("Got Status {:?}", status); + tracing::info!("Got Status {:?}", status); match status { TimeoutStatus::TimeoutMessage(call_data) => { let gas_price = client.get_gas_price().await?; - println!("Sending timeout to BSC"); + tracing::info!("Sending timeout to BSC"); let receipt = client .clone() .send_transaction( TypedTransaction::Legacy(TransactionRequest { - from: Some(H160::from(pair.public().to_eth_address().unwrap())), to: Some(NameOrAddress::Address(source_chain.handler_address)), value: Some(Default::default()), gas_price: Some(gas_price * 5), // experiment with higher? @@ -296,7 +313,7 @@ async fn test_timeout_request() -> Result<(), anyhow::Error> { } }, Err(e) => { - println!("{e:?}") + tracing::info!("{e:?}") }, } } diff --git a/modules/client/src/types.rs b/modules/client/src/types.rs index 2bfa9ab73..3c643862b 100644 --- a/modules/client/src/types.rs +++ b/modules/client/src/types.rs @@ -5,10 +5,7 @@ use codec::Encode; use core::pin::Pin; use ethers::types::H160; use futures::Stream; -use ismp::{ - consensus::{ConsensusStateId, StateMachineId}, - host::StateMachine, -}; +use ismp::{consensus::ConsensusStateId, host::StateMachine}; use serde::{Deserialize, Serialize}; use subxt::{ config::{polkadot::PolkadotExtrinsicParams, substrate::SubstrateHeader, Hasher}, @@ -33,7 +30,13 @@ pub struct KeccakHasher; impl Hasher for KeccakHasher { type Output = H256; fn hash(s: &[u8]) -> Self::Output { - sp_core::keccak_256(s).into() + use tiny_keccak::Hasher; + + let mut keccak = tiny_keccak::Keccak::v256(); + let mut output = H256::default(); + keccak.update(s); + keccak.finalize(&mut output[..]); + output } } @@ -76,7 +79,6 @@ impl EvmConfig { #[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)] pub struct SubstrateConfig { pub rpc_url: String, - pub state_machine: StateMachine, pub consensus_state_id: ConsensusStateId, pub hash_algo: HashAlgorithm, } @@ -85,11 +87,8 @@ impl SubstrateConfig { async fn into_client(&self) -> Result, anyhow::Error> { let client = SubstrateClient::::new( self.rpc_url.clone(), - StateMachineId { - state_id: self.state_machine, - consensus_state_id: self.consensus_state_id, - }, self.hash_algo, + self.consensus_state_id, ) .await?; Ok(client)