diff --git a/Cargo.lock b/Cargo.lock index 2e7d17489..bd8102fa3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,7 +178,7 @@ checksum = "4436e0292ab1bb631b42973c61205e704475fe8126af845c8d923c0996328127" [[package]] name = "anchor-attribute-access-control" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "anchor-syn", "proc-macro2", @@ -189,7 +189,7 @@ dependencies = [ [[package]] name = "anchor-attribute-account" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "anchor-syn", "bs58 0.5.1", @@ -201,7 +201,7 @@ dependencies = [ [[package]] name = "anchor-attribute-constant" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "anchor-syn", "quote", @@ -211,7 +211,7 @@ dependencies = [ [[package]] name = "anchor-attribute-error" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "anchor-syn", "quote", @@ -221,7 +221,7 @@ dependencies = [ [[package]] name = "anchor-attribute-event" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "anchor-syn", "proc-macro2", @@ -232,7 +232,7 @@ dependencies = [ [[package]] name = "anchor-attribute-program" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "anchor-syn", "quote", @@ -242,7 +242,7 @@ dependencies = [ [[package]] name = "anchor-client" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "anchor-lang", "anyhow", @@ -261,7 +261,7 @@ dependencies = [ [[package]] name = "anchor-derive-accounts" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "anchor-syn", "quote", @@ -271,7 +271,7 @@ dependencies = [ [[package]] name = "anchor-derive-serde" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "anchor-syn", "borsh-derive-internal 0.10.3", @@ -283,7 +283,7 @@ dependencies = [ [[package]] name = "anchor-derive-space" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "proc-macro2", "quote", @@ -293,7 +293,7 @@ dependencies = [ [[package]] name = "anchor-lang" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "anchor-attribute-access-control", "anchor-attribute-account", @@ -305,9 +305,9 @@ dependencies = [ "anchor-derive-serde", "anchor-derive-space", "arrayref", - "base64 0.21.7", + "base64 0.13.1", "bincode", - "borsh 0.10.3", + "borsh 0.10.4", "bytemuck", "getrandom 0.2.15", "solana-program", @@ -331,7 +331,7 @@ dependencies = [ [[package]] name = "anchor-syn" version = "0.29.0" -source = "git+https://github.com/dhruvja/anchor#90a3008fcbbc5bcbc704cd6cccf61ef130c5f9eb" +source = "git+https://github.com/mina86/anchor?branch=send-sync#db3660c5627a9c4449e9f331c3c3e523992a1178" dependencies = [ "anyhow", "bs58 0.5.1", @@ -1271,12 +1271,12 @@ dependencies = [ [[package]] name = "borsh" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +checksum = "115e54d64eb62cdebad391c19efc9dce4981c690c85a33a12199d99bb9546fee" dependencies = [ "borsh-derive 0.10.3", - "hashbrown 0.13.2", + "hashbrown 0.12.3", ] [[package]] @@ -1567,8 +1567,9 @@ dependencies = [ [[package]] name = "cf-guest" version = "0.0.0" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "bytemuck", "derive_more", "guestchain", @@ -1591,7 +1592,7 @@ dependencies = [ name = "cf-guest" version = "0.0.1" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "bytemuck", "cf-guest 0.0.0", "derive_more", @@ -1623,7 +1624,7 @@ dependencies = [ name = "cf-guest-cw" version = "0.1.0" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "byteorder", "cf-guest 0.0.1", "cosmwasm-schema", @@ -1652,6 +1653,66 @@ dependencies = [ "trie-ids", ] +[[package]] +name = "cf-solana" +version = "0.0.0" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" +dependencies = [ + "arrayvec 0.7.4", + "base64 0.21.7", + "blake3", + "bs58 0.5.1", + "bytemuck", + "cf-guest 0.0.0", + "derive_more", + "ibc-client-tendermint-types", + "ibc-core-client-context", + "ibc-core-commitment-types", + "ibc-core-host", + "ibc-primitives 0.50.0", + "ibc-proto 0.41.0", + "lib", + "prost 0.12.3", + "prost-build 0.12.3", + "proto-utils", + "serde", + "solana-program", + "stdx", + "trie-ids", +] + +[[package]] +name = "cf-solana" +version = "0.1.0" +dependencies = [ + "borsh 0.10.4", + "bytemuck", + "cf-solana 0.0.0", + "derive_more", + "ed25519-consensus", + "guestchain", + "ibc 0.15.0", + "ibc-core-client-context", + "ibc-core-client-types", + "ibc-core-handler-types", + "ibc-core-host-types", + "ibc-derive 0.1.0", + "ibc-primitives 0.50.0", + "ibc-proto 0.18.0", + "insta", + "lib", + "memory", + "prost 0.11.9", + "prost 0.12.3", + "prost-build 0.11.9", + "rand 0.8.5", + "sealable-trie", + "serde", + "stdx", + "tendermint-proto 0.34.1", + "trie-ids", +] + [[package]] name = "cfg-expr" version = "0.15.8" @@ -4841,8 +4902,9 @@ dependencies = [ [[package]] name = "guestchain" version = "0.0.0" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "bytemuck", "derive_more", "ibc-core-client-context", @@ -5195,7 +5257,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite 0.2.14", - "socket2 0.5.7", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -5313,7 +5375,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "borsh 0.10.3", + "borsh 0.10.4", "clap 3.2.25", "codegen", "derive_more", @@ -5327,6 +5389,7 @@ dependencies = [ "hyperspace-metrics", "hyperspace-parachain", "hyperspace-primitives", + "hyperspace-rollup", "hyperspace-solana", "ibc 0.15.0", "ibc-app-transfer-types", @@ -5526,6 +5589,96 @@ dependencies = [ "tokio", ] +[[package]] +name = "hyperspace-rollup" +version = "0.1.0" +dependencies = [ + "anchor-client", + "anchor-lang", + "anchor-spl", + "anyhow", + "async-trait", + "base64 0.21.7", + "bech32", + "bincode", + "bip32", + "borsh 0.10.4", + "bs58 0.5.1", + "bytemuck", + "cf-guest 0.0.0", + "cf-guest 0.0.1", + "cf-solana 0.0.0", + "cf-solana 0.1.0", + "derive_more", + "digest 0.10.7", + "ed25519-zebra", + "futures", + "futures-util", + "guestchain", + "hex", + "hyperspace-primitives", + "ibc 0.15.0", + "ibc-app-transfer-types", + "ibc-client-tendermint-types", + "ibc-client-wasm-types", + "ibc-core-channel-types", + "ibc-core-client-types", + "ibc-core-commitment-types", + "ibc-core-connection-types", + "ibc-core-handler-types", + "ibc-core-host-types", + "ibc-primitives 0.1.0", + "ibc-primitives 0.50.0", + "ibc-proto 0.18.0", + "ibc-proto 0.41.0", + "ibc-rpc", + "ics07-tendermint", + "ics08-wasm", + "ics23 0.11.1", + "itertools 0.10.5", + "jito-protos", + "jito-searcher-client", + "k256 0.11.6", + "lib", + "log", + "memory", + "pallet-ibc", + "parity-scale-codec", + "prost 0.11.9", + "prost-types 0.12.3", + "quick_cache", + "rand 0.8.5", + "reqwest", + "ripemd", + "rs_merkle", + "rusqlite", + "sealable-trie", + "serde", + "serde_json", + "sha2 0.10.8", + "solana-ibc", + "solana-metrics", + "solana-signature-verifier", + "solana-transaction-status", + "solana-trie", + "solana-write-account", + "stdx", + "tendermint 0.33.2", + "tendermint 0.34.1", + "tendermint-light-client", + "tendermint-light-client-verifier 0.33.2", + "tendermint-light-client-verifier 0.34.1", + "tendermint-proto 0.33.2", + "tendermint-rpc", + "thiserror", + "tiny-bip39 1.0.0", + "tokio", + "tokio-stream", + "tonic 0.8.3", + "tracing", + "trie-ids", +] + [[package]] name = "hyperspace-solana" version = "0.1.0" @@ -5539,11 +5692,13 @@ dependencies = [ "bech32", "bincode", "bip32", - "borsh 0.10.3", + "borsh 0.10.4", "bs58 0.5.1", "bytemuck", "cf-guest 0.0.0", "cf-guest 0.0.1", + "cf-solana 0.0.0", + "cf-solana 0.1.0", "derive_more", "digest 0.10.7", "ed25519-zebra", @@ -5596,6 +5751,7 @@ dependencies = [ "solana-signature-verifier", "solana-transaction-status", "solana-trie", + "solana-witnessed-trie", "solana-write-account", "stdx", "tendermint 0.33.2", @@ -5630,6 +5786,7 @@ dependencies = [ "hyperspace-cosmos", "hyperspace-parachain", "hyperspace-primitives", + "hyperspace-rollup", "hyperspace-solana", "ibc 0.15.0", "ibc-proto 0.18.0", @@ -5720,7 +5877,7 @@ dependencies = [ [[package]] name = "ibc" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "ibc-apps", "ibc-clients", @@ -5733,7 +5890,7 @@ dependencies = [ [[package]] name = "ibc-app-transfer" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "ibc-app-transfer-types", "ibc-core", @@ -5743,9 +5900,9 @@ dependencies = [ [[package]] name = "ibc-app-transfer-types" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "derive_more", "displaydoc", "ibc-core", @@ -5758,7 +5915,7 @@ dependencies = [ [[package]] name = "ibc-apps" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "ibc-app-transfer", ] @@ -5766,7 +5923,7 @@ dependencies = [ [[package]] name = "ibc-client-tendermint" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "derive_more", "ibc-client-tendermint-types", @@ -5783,9 +5940,9 @@ dependencies = [ [[package]] name = "ibc-client-tendermint-types" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "displaydoc", "ibc-core-client-types", "ibc-core-commitment-types", @@ -5802,7 +5959,7 @@ dependencies = [ [[package]] name = "ibc-client-wasm-types" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "base64 0.21.7", "displaydoc", @@ -5816,7 +5973,7 @@ dependencies = [ [[package]] name = "ibc-clients" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "ibc-client-tendermint", "ibc-client-wasm-types", @@ -5825,7 +5982,7 @@ dependencies = [ [[package]] name = "ibc-core" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "ibc-core-channel", "ibc-core-client", @@ -5841,7 +5998,7 @@ dependencies = [ [[package]] name = "ibc-core-channel" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "ibc-core-channel-types", "ibc-core-client", @@ -5856,9 +6013,9 @@ dependencies = [ [[package]] name = "ibc-core-channel-types" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "derive_more", "displaydoc", "ibc-core-client-types", @@ -5876,7 +6033,7 @@ dependencies = [ [[package]] name = "ibc-core-client" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "ibc-client-tendermint-types", "ibc-core-client-context", @@ -5890,7 +6047,7 @@ dependencies = [ [[package]] name = "ibc-core-client-context" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "derive_more", "displaydoc", @@ -5907,9 +6064,9 @@ dependencies = [ [[package]] name = "ibc-core-client-types" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "derive_more", "displaydoc", "ibc-core-commitment-types", @@ -5924,9 +6081,9 @@ dependencies = [ [[package]] name = "ibc-core-commitment-types" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "derive_more", "displaydoc", "ibc-primitives 0.50.0", @@ -5939,7 +6096,7 @@ dependencies = [ [[package]] name = "ibc-core-connection" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "ibc-core-client", "ibc-core-connection-types", @@ -5951,9 +6108,9 @@ dependencies = [ [[package]] name = "ibc-core-connection-types" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "derive_more", "displaydoc", "ibc-core-client-types", @@ -5969,7 +6126,7 @@ dependencies = [ [[package]] name = "ibc-core-handler" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "ibc-client-tendermint-types", "ibc-core-channel", @@ -5985,9 +6142,9 @@ dependencies = [ [[package]] name = "ibc-core-handler-types" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "derive_more", "displaydoc", "ibc-core-channel-types", @@ -6006,7 +6163,7 @@ dependencies = [ [[package]] name = "ibc-core-host" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "derive_more", "displaydoc", @@ -6024,9 +6181,9 @@ dependencies = [ [[package]] name = "ibc-core-host-cosmos" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "derive_more", "displaydoc", "ibc-app-transfer-types", @@ -6048,9 +6205,9 @@ dependencies = [ [[package]] name = "ibc-core-host-types" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "derive_more", "displaydoc", "ibc-primitives 0.50.0", @@ -6060,7 +6217,7 @@ dependencies = [ [[package]] name = "ibc-core-router" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "derive_more", "displaydoc", @@ -6074,9 +6231,9 @@ dependencies = [ [[package]] name = "ibc-core-router-types" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "derive_more", "displaydoc", "ibc-core-host-types", @@ -6101,7 +6258,7 @@ dependencies = [ [[package]] name = "ibc-derive" version = "0.6.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ "proc-macro2", "quote", @@ -6134,9 +6291,9 @@ dependencies = [ [[package]] name = "ibc-primitives" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "derive_more", "displaydoc", "ibc-proto 0.41.0", @@ -6166,7 +6323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd4ee32b22d3b06f31529b956f4928e5c9a068d71e46cf6abfa19c31ca550553" dependencies = [ "base64 0.21.7", - "borsh 0.10.3", + "borsh 0.10.4", "bytes", "flex-error", "ics23 0.11.1", @@ -6426,7 +6583,7 @@ dependencies = [ name = "ics13-near" version = "0.1.0" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "bytes", "derive_more", "env_logger 0.9.3", @@ -7182,12 +7339,14 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "lib" version = "0.0.0" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ "base64 0.21.7", - "borsh 0.10.3", + "borsh 0.10.4", "bs58 0.5.1", "bytemuck", "derive_more", + "serde", "sha2 0.10.8", "solana-program", "stdx", @@ -8055,6 +8214,7 @@ dependencies = [ [[package]] name = "memory" version = "0.0.0" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ "derive_more", "stdx", @@ -8235,7 +8395,7 @@ version = "3.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba8ee05284d79b367ae8966d558e1a305a781fc80c9df51f37775169117ba64f" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "num-derive 0.3.3", "num-traits", "solana-program", @@ -9321,8 +9481,9 @@ name = "pallet-ibc" version = "0.0.1" dependencies = [ "beefy-light-client-primitives", - "borsh 0.10.3", + "borsh 0.10.4", "cf-guest 0.0.1", + "cf-solana 0.1.0", "chrono", "cumulus-primitives-core", "derive_more", @@ -12143,6 +12304,7 @@ dependencies = [ [[package]] name = "proto-utils" version = "0.0.0" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ "const_format", "derive_more", @@ -14555,10 +14717,11 @@ dependencies = [ [[package]] name = "sealable-trie" version = "0.0.0" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ "ascii 1.1.0", "base64 0.21.7", - "borsh 0.10.3", + "borsh 0.10.4", "bytemuck", "derive_more", "lib", @@ -14977,7 +15140,7 @@ dependencies = [ "bytes", "ics23 0.10.0", "proptest", - "rand 0.8.5", + "rand 0.4.6", "sha2 0.10.8", "tendermint 0.33.2", ] @@ -15128,6 +15291,7 @@ dependencies = [ [[package]] name = "solana-allocator" version = "0.0.3" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ "bytemuck", "solana-program", @@ -15264,12 +15428,14 @@ dependencies = [ [[package]] name = "solana-ibc" version = "0.0.3" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ "anchor-lang", "anchor-spl", "base64 0.21.7", "bytemuck", "cf-guest 0.0.0", + "cf-solana 0.0.0", "derive_more", "guestchain", "hex-literal 0.4.1", @@ -15399,7 +15565,7 @@ dependencies = [ "bincode", "bitflags 2.5.0", "blake3", - "borsh 0.10.3", + "borsh 0.10.4", "borsh 0.9.3", "bs58 0.4.0", "bv", @@ -15619,7 +15785,7 @@ dependencies = [ "base64 0.21.7", "bincode", "bitflags 2.5.0", - "borsh 0.10.3", + "borsh 0.10.4", "bs58 0.4.0", "bytemuck", "byteorder", @@ -15685,9 +15851,10 @@ checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" [[package]] name = "solana-signature-verifier" version = "0.0.3" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ "base64 0.21.7", - "borsh 0.10.3", + "borsh 0.10.4", "bytemuck", "derive_more", "guestchain", @@ -15777,7 +15944,7 @@ dependencies = [ "Inflector", "base64 0.21.7", "bincode", - "borsh 0.10.3", + "borsh 0.10.4", "bs58 0.4.0", "lazy_static", "log", @@ -15796,6 +15963,7 @@ dependencies = [ [[package]] name = "solana-trie" version = "0.0.3" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ "bytemuck", "lib", @@ -15858,9 +16026,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "solana-witnessed-trie" +version = "0.0.3" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" +dependencies = [ + "arrayvec 0.7.4", + "bytemuck", + "cf-solana 0.0.0", + "derive_more", + "lib", + "solana-program", + "solana-trie", + "stdx", + "strum 0.25.0", +] + [[package]] name = "solana-write-account" version = "0.0.3" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ "solana-program", "stdx", @@ -17053,7 +17238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "992d9c64c2564cc8f63a4b508bf3ebcdf2254b0429b13cd1d31adb6162432a5f" dependencies = [ "assert_matches", - "borsh 0.10.3", + "borsh 0.10.4", "num-derive 0.4.2", "num-traits", "solana-program", @@ -17112,7 +17297,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85a5db7e4efb1107b0b8e52a13f035437cdcb36ef99c58f6d467f089d9b2915a" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "bytemuck", "solana-program", "solana-zk-token-sdk", @@ -17252,7 +17437,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c16ce3ba6979645fb7627aa1e435576172dd63088dc7848cb09aa331fa1fe4f" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "solana-program", "spl-discriminator", "spl-pod", @@ -17363,6 +17548,7 @@ dependencies = [ [[package]] name = "stdx" version = "0.0.0" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" [[package]] name = "strsim" @@ -18647,10 +18833,11 @@ dependencies = [ [[package]] name = "trie-ids" version = "0.0.0" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ "ascii 1.1.0", "base64 0.21.7", - "borsh 0.10.3", + "borsh 0.10.4", "bytemuck", "derive_more", "ibc-core-channel-types", @@ -18812,9 +18999,9 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "digest 0.10.7", - "rand 0.8.5", + "rand 0.4.6", "static_assertions", ] @@ -19079,6 +19266,7 @@ dependencies = [ [[package]] name = "wasm" version = "0.0.0" +source = "git+https://github.com/ComposableFi/emulated-light-client/#ba1046ead4232ee07a1db9b120f3035c983e0052" dependencies = [ "const_format", "derive_more", @@ -20200,7 +20388,7 @@ dependencies = [ [[patch.unused]] name = "ibc-testkit" version = "0.50.0" -source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" +source = "git+https://github.com/mina86/ibc-rs?rev=e1be8c9292c82c1e7c158067f0014fb292ee652d#e1be8c9292c82c1e7c158067f0014fb292ee652d" [[patch.unused]] name = "tendermint-light-client" diff --git a/Cargo.toml b/Cargo.toml index bf29a0006..72f5ec7b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,13 +41,14 @@ members = [ "hyperspace/parachain", "hyperspace/cosmos", "hyperspace/solana", + "hyperspace/rollup", "hyperspace/testsuite", "hyperspace/metrics", # utilities "utils/subxt/codegen", "utils/subxt/generated", "utils/parachain-node", - "utils/parachain-node/runtime", + "utils/parachain-node/runtime", "light-clients/cf-solana", # "utils/simnode" ] @@ -58,21 +59,21 @@ jsonrpsee = { version = "0.16.3" } aes-gcm-siv = { git = "https://github.com/RustCrypto/AEADs", rev = "6105d7a5591aefa646a95d12b5e8d3f55a9214ef" } curve25519-dalek-new = { git = "https://github.com/dalek-cryptography/curve25519-dalek", rev = "0cd099a9fb8ff9f6fedc8723d44dbb1c743e9d35", package = "curve25519-dalek" } curve25519-dalek = { git = "https://github.com/solana-labs/curve25519-dalek.git", rev = "b500cdc2a920cd5bff9e2dd974d7b97349d61464" } -anchor-client = { git = "https://github.com/dhruvja/anchor" } -anchor-lang = { git = "https://github.com/dhruvja/anchor" } +anchor-client = { git = "https://github.com/mina86/anchor", branch = "send-sync" } +anchor-lang = { git = "https://github.com/mina86/anchor", branch = "send-sync" } -ibc = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false, features = ["borsh", "serde"] } -ibc-client-tendermint-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-channel-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-client-context = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-client-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-commitment-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-connection-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-host = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-host-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-primitives = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-testkit = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -# ibc-testkit = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054" } +ibc = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false, features = ["borsh", "serde"] } +ibc-client-tendermint-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-channel-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-client-context = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-client-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-commitment-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-connection-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-host = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-host-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-primitives = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-testkit = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +# ibc-testkit = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d" } #ibc = { path = "../ibc-rs-mina/ibc" } #ibc-app-nft-transfer = { path = "../ibc-rs-mina/ibc-apps/ics721-nft-transfer" } #ibc-app-nft-transfer-types = { path = "../ibc-rs-mina/ibc-apps/ics721-nft-transfer/types" } diff --git a/config/mantis-testnet.toml b/config/mantis-testnet.toml new file mode 100644 index 000000000..378ba0795 --- /dev/null +++ b/config/mantis-testnet.toml @@ -0,0 +1,33 @@ +type = "rollup" +name = "mantis" +rpc_url = "https://mantis-rollup.composable-shared-artifacts.composablenodes.tech/rpc" +ws_url = "wss://mantis-rollup.composable-shared-artifacts.composablenodes.tech/ws" +chain_id = "mantis-1" +client_id = "cf-solana-13" +connection_id = "connection-0" +account_prefix = "ibc" +fee_denom = "stake" +fee_amount = "4000" +gas_limit = 100000000 +store_prefix = "ibc" +max_tx_size = 320000 +commitment_prefix = [105, 98, 99] +channel_whitelist = [["channel-0", "transfer"]] +commitment_level = "confirmed" +private_key = [ + 48, 123, 8, 80, 248, 0, 217, 142, 124, 193, 95, 24, 168, 139, 214, 136, 147, 210, 168, + 135, 26, 36, 162, 89, 150, 185, 99, 191, 247, 135, 78, 111, 12, 8, 4, 81, 129, 165, + 153, 230, 192, 225, 51, 119, 216, 14, 69, 225, 73, 7, 204, 144, 39, 213, 91, 255, 136, + 38, 95, 131, 197, 4, 101, 186, + ] +solana_ibc_program_id = "2HLLVco5HvwWriNbUhmVwA2pCetRkpgrqwnjcsZdyTKT" +write_program_id = "FufGpHqMQgGVjtMH9AV8YMrJYq8zaK6USRsJkZP4yDjo" +signature_verifier_program_id = "C6r1VEbn3mSpecgrZ7NdBvWUtYVJWrDPv4uU9Xs956gc" +trie_db_path = "../solana-ibc-indexer/indexer.db3" +transaction_sender = "RPC" +trie_rpc_url = "http://34.140.169.227:42069" + +[common_state_config] +skip_optional_client_updates = true +max_packets_to_process = 10 +handshake_completed = true \ No newline at end of file diff --git a/config/solana-devnet.toml b/config/solana-devnet.toml index f152f9e48..8f174d7e1 100644 --- a/config/solana-devnet.toml +++ b/config/solana-devnet.toml @@ -1,10 +1,10 @@ type = "solana" name = "solana" -rpc_url = "https://devnet.helius-rpc.com/?api-key=bc5c0cfc-46df-4781-978f-af6ca7a202c2" -ws_url = "wss://devnet.helius-rpc.com/?api-key=bc5c0cfc-46df-4781-978f-af6ca7a202c2" +rpc_url = "https://devnet.helius-rpc.com/?api-key=5ae782d8-6bf6-489c-b6df-ef7e6289e193" +ws_url = "wss://devnet.helius-rpc.com/?api-key=5ae782d8-6bf6-489c-b6df-ef7e6289e193" chain_id = "solana-1" -client_id = "08-wasm-201" -connection_id = "connection-1" +client_id = "cf-guest-1" +connection_id = "connection-15" account_prefix = "ibc" fee_denom = "stake" fee_amount = "4000" @@ -12,15 +12,21 @@ gas_limit = 100000000 store_prefix = "ibc" max_tx_size = 320000 commitment_prefix = [105, 98, 99] -channel_whitelist = [["channel-1", "transfer"]] +channel_whitelist = [["channel-2", "transfer"]] commitment_level = "confirmed" -private_key = [48, 123, 8, 80, 248, 0, 217, 142, 124, 193, 95, 24, 168, 139, 214, 136, 147, 210, 168, 135, 26, 36, 162, 89, 150, 185, 99, 191, 247, 135, 78, 111, 12, 8, 4, 81, 129, 165, 153, 230, 192, 225, 51, 119, 216, 14, 69, 225, 73, 7, 204, 144, 39, 213, 91, 255, 136, 38, 95, 131, 197, 4, 101, 186] -solana_ibc_program_id = "FeFjYj2YuMsk87Cp48ubzQPtW4MWDaKJrCs1TcdgosZJ" -write_program_id = "FufGpHqMQgGVjtMH9AV8YMrJYq8zaK6USRsJkZP4yDjo" +private_key = [ + 48, 123, 8, 80, 248, 0, 217, 142, 124, 193, 95, 24, 168, 139, 214, 136, 147, 210, 168, + 135, 26, 36, 162, 89, 150, 185, 99, 191, 247, 135, 78, 111, 12, 8, 4, 81, 129, 165, + 153, 230, 192, 225, 51, 119, 216, 14, 69, 225, 73, 7, 204, 144, 39, 213, 91, 255, 136, + 38, 95, 131, 197, 4, 101, 186, + ] +solana_ibc_program_id = "8o54PU8EcKtf4AqkFfqNhCWBqwpvFki64WEVNWAUP84c" +write_program_id = "9tjGWBMR6u6mBzEiW7j6A72KyxzEmcEsW3oV7ijBLmFb" signature_verifier_program_id = "C6r1VEbn3mSpecgrZ7NdBvWUtYVJWrDPv4uU9Xs956gc" trie_db_path = "../solana-ibc-indexer/indexer.db3" -transaction_sender = "JITO" +transaction_sender = "RPC" [common_state_config] skip_optional_client_updates = true -max_packets_to_process = 10 \ No newline at end of file +max_packets_to_process = 10 +handshake_completed = true \ No newline at end of file diff --git a/contracts/pallet-ibc/Cargo.toml b/contracts/pallet-ibc/Cargo.toml index 1deda570c..b463b3362 100644 --- a/contracts/pallet-ibc/Cargo.toml +++ b/contracts/pallet-ibc/Cargo.toml @@ -52,9 +52,12 @@ ics23 = { git = "https://github.com/cosmos/ics23", rev = "74ce807b7be39a7e0afb4e #guest-chain cf-guest = { path = "../../light-clients/cf-guest" } -guestchain = { path = "../../../emulated-light-client/common/guestchain" , default-features = false } +guestchain = { git = "https://github.com/ComposableFi/emulated-light-client/" , default-features = false } ed25519-dalek = { version = "2.1.1", default-features = false, features = ["pkcs8"] } +#rollup +cf-solana = { path = "../../light-clients/cf-solana" } + grandpa-client-primitives = { package = "grandpa-light-client-primitives", path = "../../algorithms/grandpa/primitives", default-features = false } beefy-client-primitives = { package = "beefy-light-client-primitives", path = "../../algorithms/beefy/primitives", default-features = false } light-client-common = { path = "../../light-clients/common", default-features = false } diff --git a/contracts/pallet-ibc/src/lib.rs b/contracts/pallet-ibc/src/lib.rs index 3a56c43e2..6fe5044a6 100644 --- a/contracts/pallet-ibc/src/lib.rs +++ b/contracts/pallet-ibc/src/lib.rs @@ -1118,6 +1118,10 @@ pub mod pallet { let latest_height = guest.latest_height(); AnyClientState::wrap(&guest) }, + AnyClientState::Rollup(rollup) => { + let latest_height = rollup.latest_height(); + AnyClientState::wrap(&rollup) + }, AnyClientState::Wasm(_) => return Err(Error::::ClientFreezeFailed.into()), #[cfg(any(test, feature = "testing"))] AnyClientState::Mock(mut ms) => { diff --git a/contracts/pallet-ibc/src/light_clients.rs b/contracts/pallet-ibc/src/light_clients.rs index 011e9a355..c3f7f3194 100644 --- a/contracts/pallet-ibc/src/light_clients.rs +++ b/contracts/pallet-ibc/src/light_clients.rs @@ -66,13 +66,32 @@ const GUEST_CONSENSUS_STATE_TYPE_URL: &'static str = cf_guest::proto::ConsensusS const GUEST_HEADER_TYPE_URL: &'static str = cf_guest::proto::Header::IBC_TYPE_URL; const GUEST_MISBEHAVIOUR_TYPE_URL: &'static str = cf_guest::proto::Misbehaviour::IBC_TYPE_URL; +const ROLLUP_CLIENT_MESSAGE_TYPE_URL: &'static str = cf_solana::proto::ClientMessage::IBC_TYPE_URL; +const ROLLUP_CLIENT_STATE_TYPE_URL: &'static str = cf_solana::proto::ClientState::IBC_TYPE_URL; +const ROLLUP_CONSENSUS_STATE_TYPE_URL: &'static str = + cf_solana::proto::ConsensusState::IBC_TYPE_URL; +const ROLLUP_HEADER_TYPE_URL: &'static str = cf_solana::proto::Header::IBC_TYPE_URL; +const ROLLUP_MISBEHAVIOUR_TYPE_URL: &'static str = cf_solana::proto::Misbehaviour::IBC_TYPE_URL; + #[derive(Clone, Default, PartialEq, Debug, Eq)] pub struct HostFunctionsManager; /// Ed25519 public key (a.k.a. verifying key). #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[repr(transparent)] -pub struct PubKey(ed25519_dalek::VerifyingKey); +pub struct PubKey(pub ed25519_dalek::VerifyingKey); + +impl PubKey { + pub fn to_bytes(&self) -> [u8; 32] { + self.0.as_bytes().clone() + } +} + +impl Signature { + pub fn to_bytes(&self) -> [u8; 64] { + self.0.to_bytes() + } +} impl guestchain::PubKey for PubKey { type Signature = Signature; @@ -120,7 +139,7 @@ impl Ord for PubKey { /// Ed25519 signature. #[derive(Clone, PartialEq, Eq, Debug)] #[repr(transparent)] -pub struct Signature(ed25519_dalek::Signature); +pub struct Signature(pub ed25519_dalek::Signature); impl guestchain::Signature for Signature { fn to_vec(&self) -> Vec { @@ -278,7 +297,7 @@ impl grandpa_client_primitives::HostFunctions for HostFunctionsManager { fn insert_relay_header_hashes(new_hashes: &[::Hash]) { if new_hashes.is_empty() { - return + return; } GrandpaHeaderHashesSetStorage::mutate(|hashes_set| { @@ -333,6 +352,7 @@ pub enum AnyClient { Tendermint(ics07_tendermint::client_def::TendermintClient), Wasm(ics08_wasm::client_def::WasmClient), Guest(cf_guest::client_def::GuestClient), + Rollup(cf_solana::client_def::SolanaClient), #[cfg(any(test, feature = "testing"))] Mock(ibc::mock::client_def::MockClient), } @@ -344,6 +364,7 @@ pub enum AnyUpgradeOptions { Tendermint(ics07_tendermint::client_state::UpgradeOptions), Wasm(Box), Guest(cf_guest::client::UpgradeOptions), + Rollup(cf_solana::client::UpgradeOptions), #[cfg(any(test, feature = "testing"))] Mock(()), } @@ -360,6 +381,8 @@ pub enum AnyClientState { Wasm(ics08_wasm::client_state::ClientState), #[ibc(proto_url = "GUEST_CLIENT_STATE_TYPE_URL")] Guest(cf_guest::ClientState), + #[ibc(proto_url = "ROLLUP_CLIENT_STATE_TYPE_URL")] + Rollup(cf_solana::ClientState), #[cfg(any(test, feature = "testing"))] #[ibc(proto_url = "MOCK_CLIENT_STATE_TYPE_URL")] Mock(ibc::mock::client_state::MockClientState), @@ -376,14 +399,16 @@ impl AnyClientState { let client_state = AnyClientState::try_from(any).ok()?; match client_state { - AnyClientState::Wasm(wasm_client_state) => - any = Any::decode(&*wasm_client_state.data).ok()?, - c => + AnyClientState::Wasm(wasm_client_state) => { + any = Any::decode(&*wasm_client_state.data).ok()? + }, + c => { if f(&c) { - break Some(c) + break Some(c); } else { - return None - }, + return None; + } + }, }; } } @@ -417,6 +442,7 @@ impl AnyClientState { AnyClientState::Tendermint(client_state) => client_state.latest_height(), AnyClientState::Wasm(client_state) => client_state.latest_height(), AnyClientState::Guest(client_state) => client_state.latest_height(), + AnyClientState::Rollup(client_state) => client_state.latest_height(), #[cfg(any(test, feature = "testing"))] AnyClientState::Mock(client_state) => client_state.latest_height(), } @@ -435,6 +461,8 @@ pub enum AnyConsensusState { Wasm(ics08_wasm::consensus_state::ConsensusState), #[ibc(proto_url = "GUEST_CONSENSUS_STATE_TYPE_URL")] Guest(cf_guest::ConsensusState), + #[ibc(proto_url = "ROLLUP_CONSENSUS_STATE_TYPE_URL")] + Rollup(cf_solana::ConsensusState), #[cfg(any(test, feature = "testing"))] #[ibc(proto_url = "MOCK_CONSENSUS_STATE_TYPE_URL")] Mock(ibc::mock::client_state::MockConsensusState), @@ -462,6 +490,8 @@ pub enum AnyClientMessage { Wasm(ics08_wasm::client_message::ClientMessage), #[ibc(proto_url = "GUEST_CLIENT_MESSAGE_TYPE_URL")] Guest(cf_guest::ClientMessage), + #[ibc(proto_url = "ROLLUP_CLIENT_MESSAGE_TYPE_URL")] + Rollup(cf_solana::ClientMessage), #[cfg(any(test, feature = "testing"))] #[ibc(proto_url = "MOCK_CLIENT_MESSAGE_TYPE_URL")] Mock(ibc::mock::header::MockClientMessage), @@ -494,16 +524,18 @@ impl TryFrom for AnyClientMessage { ics10_grandpa::client_message::ClientMessage::decode_vec(&value.value) .map_err(ics02_client::error::Error::decode_raw_header)?, )), - GRANDPA_HEADER_TYPE_URL => + GRANDPA_HEADER_TYPE_URL => { Ok(Self::Grandpa(ics10_grandpa::client_message::ClientMessage::Header( ics10_grandpa::client_message::Header::decode_vec(&value.value) .map_err(ics02_client::error::Error::decode_raw_header)?, - ))), - GRANDPA_MISBEHAVIOUR_TYPE_URL => + ))) + }, + GRANDPA_MISBEHAVIOUR_TYPE_URL => { Ok(Self::Grandpa(ics10_grandpa::client_message::ClientMessage::Misbehaviour( ics10_grandpa::client_message::Misbehaviour::decode_vec(&value.value) .map_err(ics02_client::error::Error::decode_raw_header)?, - ))), + ))) + }, // TODO: beefy header, misbehaviour impl From BEEFY_CLIENT_MESSAGE_TYPE_URL => Ok(Self::Beefy( ics11_beefy::client_message::ClientMessage::decode_vec(&value.value) @@ -513,16 +545,18 @@ impl TryFrom for AnyClientMessage { ics07_tendermint::client_message::ClientMessage::decode_vec(&value.value) .map_err(ics02_client::error::Error::decode_raw_header)?, )), - TENDERMINT_HEADER_TYPE_URL => + TENDERMINT_HEADER_TYPE_URL => { Ok(Self::Tendermint(ics07_tendermint::client_message::ClientMessage::Header( ics07_tendermint::client_message::Header::decode_vec(&value.value) .map_err(ics02_client::error::Error::decode_raw_header)?, - ))), - TENDERMINT_MISBEHAVIOUR_TYPE_URL => + ))) + }, + TENDERMINT_MISBEHAVIOUR_TYPE_URL => { Ok(Self::Tendermint(ics07_tendermint::client_message::ClientMessage::Misbehaviour( ics07_tendermint::client_message::Misbehaviour::decode_vec(&value.value) .map_err(ics02_client::error::Error::decode_raw_header)?, - ))), + ))) + }, GUEST_CLIENT_MESSAGE_TYPE_URL => Ok(Self::Guest( cf_guest::ClientMessage::decode_vec(&value.value) .map_err(ics02_client::error::Error::decode_raw_header)?, @@ -535,6 +569,18 @@ impl TryFrom for AnyClientMessage { cf_guest::Misbehaviour::decode_vec(&value.value) .map_err(ics02_client::error::Error::decode_raw_header)?, ))), + ROLLUP_CLIENT_MESSAGE_TYPE_URL => Ok(Self::Rollup( + cf_solana::ClientMessage::decode_vec(&value.value) + .map_err(ics02_client::error::Error::decode_raw_header)?, + )), + ROLLUP_HEADER_TYPE_URL => Ok(Self::Rollup(cf_solana::ClientMessage::from( + cf_solana::Header::decode_vec(&value.value) + .map_err(ics02_client::error::Error::decode_raw_header)?, + ))), + ROLLUP_MISBEHAVIOUR_TYPE_URL => Ok(Self::Rollup(cf_solana::ClientMessage::from( + cf_solana::Misbehaviour::decode_vec(&value.value) + .map_err(ics02_client::error::Error::decode_raw_header)?, + ))), WASM_CLIENT_MESSAGE_TYPE_URL => Ok(Self::Wasm( ics08_wasm::client_message::ClientMessage::decode_vec(&value.value) .map_err(ics02_client::error::Error::decode_raw_header)?, @@ -547,24 +593,32 @@ impl TryFrom for AnyClientMessage { impl From for Any { fn from(client_msg: AnyClientMessage) -> Self { match client_msg { - AnyClientMessage::Wasm(msg) => - Any { type_url: WASM_CLIENT_MESSAGE_TYPE_URL.to_string(), value: msg.encode_vec() }, + AnyClientMessage::Wasm(msg) => { + Any { type_url: WASM_CLIENT_MESSAGE_TYPE_URL.to_string(), value: msg.encode_vec() } + }, AnyClientMessage::Grandpa(msg) => match msg { - ics10_grandpa::client_message::ClientMessage::Header(h) => - Any { type_url: GRANDPA_HEADER_TYPE_URL.to_string(), value: h.encode_vec() }, + ics10_grandpa::client_message::ClientMessage::Header(h) => { + Any { type_url: GRANDPA_HEADER_TYPE_URL.to_string(), value: h.encode_vec() } + }, ics10_grandpa::client_message::ClientMessage::Misbehaviour(m) => Any { type_url: GRANDPA_MISBEHAVIOUR_TYPE_URL.to_string(), value: m.encode_vec(), }, }, - AnyClientMessage::Beefy(msg) => - Any { type_url: BEEFY_CLIENT_MESSAGE_TYPE_URL.to_string(), value: msg.encode_vec() }, + AnyClientMessage::Beefy(msg) => { + Any { type_url: BEEFY_CLIENT_MESSAGE_TYPE_URL.to_string(), value: msg.encode_vec() } + }, AnyClientMessage::Tendermint(msg) => Any { type_url: TENDERMINT_CLIENT_MESSAGE_TYPE_URL.to_string(), value: msg.encode_vec(), }, - AnyClientMessage::Guest(msg) => - Any { type_url: GUEST_CLIENT_MESSAGE_TYPE_URL.to_string(), value: msg.encode_vec() }, + AnyClientMessage::Guest(msg) => { + Any { type_url: GUEST_CLIENT_MESSAGE_TYPE_URL.to_string(), value: msg.encode_vec() } + }, + AnyClientMessage::Rollup(msg) => Any { + type_url: ROLLUP_CLIENT_MESSAGE_TYPE_URL.to_string(), + value: msg.encode_vec(), + }, #[cfg(any(test, feature = "testing"))] AnyClientMessage::Mock(_msg) => panic!("MockHeader can't be serialized"), } diff --git a/hyperspace/core/Cargo.toml b/hyperspace/core/Cargo.toml index 6183cbbd8..8cdc7a337 100644 --- a/hyperspace/core/Cargo.toml +++ b/hyperspace/core/Cargo.toml @@ -22,7 +22,8 @@ normal = ["scale-encode"] primitives = { path = "../primitives", package = "hyperspace-primitives" } parachain = { path = "../parachain", package = "hyperspace-parachain" } cosmos = { path = "../cosmos", package = "hyperspace-cosmos", optional = true } -solana = { path = "../solana", package = "hyperspace-solana", optional = true } +solana = { path = "../solana", package = "hyperspace-solana"} +rollup = { path = "../rollup", package = "hyperspace-rollup"} #near = { path = "near", package = "hyperspace-near", optional = true } metrics = { path = "../metrics", package = "hyperspace-metrics" } @@ -71,9 +72,9 @@ frame-system = { git = "https://github.com/paritytech/substrate", branch = "polk frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } prost = { version = "0.11", default-features = false } serde_json = "1.0.74" -guestchain = { path = "../../../emulated-light-client/common/guestchain" ,default-features = false } -sealable-trie = { path = "../../../emulated-light-client/common/sealable-trie" ,default-features = false } -lib = { path = "../../../emulated-light-client/common/lib" ,default-features = false } +guestchain = { git = "https://github.com/ComposableFi/emulated-light-client/" ,default-features = false } +sealable-trie = { git = "https://github.com/ComposableFi/emulated-light-client/" ,default-features = false } +lib = { git = "https://github.com/ComposableFi/emulated-light-client/" ,default-features = false } [dev-dependencies] derive_more = "0.99.17" @@ -85,18 +86,19 @@ cosmos = { path = "../cosmos", package = "hyperspace-cosmos", features = [ "testing", ] } solana = { path = "../solana", package = "hyperspace-solana" } -trie-ids = { path = "../../../emulated-light-client/common/trie-ids" ,features = ["borsh"] } +rollup = { path = "../rollup", package = "hyperspace-rollup" } +trie-ids = { git = "https://github.com/ComposableFi/emulated-light-client/" ,features = ["borsh"] } -ibc-new-primitives = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false, features = ["borsh", "serde"] , package="ibc-primitives" } -ibc-core-host-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false, features = ["borsh", "serde"]} -ibc-core-handler-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-client-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-connection-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-channel-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-app-transfer-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-commitment-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-client-tendermint-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -# ibc-testkit = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } +ibc-new-primitives = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false, features = ["borsh", "serde"] , package="ibc-primitives" } +ibc-core-host-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false, features = ["borsh", "serde"]} +ibc-core-handler-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-client-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-connection-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-channel-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-app-transfer-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-commitment-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-client-tendermint-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +# ibc-testkit = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } ibc-proto-new = { version = "0.41.0", default-features = false, package="ibc-proto" } @@ -113,7 +115,8 @@ sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "polkad build-metadata-from-ws = [] #near = ["dep:near"] cosmos = ["dep:cosmos"] -solana = ["dep:solana"] -testing = ["primitives/testing", "parachain/testing", "cosmos/testing", "solana/testing"] -default = ["cosmos", "solana"] +# solana = ["dep:solana"] +# rollup = ["dep:rollup"] +testing = ["primitives/testing", "parachain/testing", "cosmos/testing", "solana/testing", "rollup/testing"] +default = ["cosmos"] composable-beefy = [] diff --git a/hyperspace/core/src/chain.rs b/hyperspace/core/src/chain.rs index 10084a657..0d47fdec4 100644 --- a/hyperspace/core/src/chain.rs +++ b/hyperspace/core/src/chain.rs @@ -70,8 +70,10 @@ use primitives::{ MisbehaviourHandler, UpdateType, }; use serde::{Deserialize, Serialize}; -#[cfg(feature = "solana")] +// #[cfg(feature = "solana")] use solana::{SolanaClient, SolanaClientConfig}; +// #[cfg(feature = "rollup")] +use rollup::{RollupClient, RollupClientConfig}; use std::{pin::Pin, time::Duration}; use tendermint_proto::Protobuf; use thiserror::Error; @@ -102,8 +104,10 @@ chains! { PicassoKusama(ParachainClientConfig, ParachainClient), #[cfg(feature = "cosmos")] Cosmos(CosmosClientConfig, CosmosClient), - #[cfg(feature = "solana")] + // #[cfg(feature = "solana")] Solana(SolanaClientConfig, SolanaClient), + // #[cfg(feature = "rollup")] + Rollup(RollupClientConfig, RollupClient), } fn wrap_any_msg_into_wasm(msg: Any, checksum: Bytes) -> Result { diff --git a/hyperspace/core/src/events.rs b/hyperspace/core/src/events.rs index 03f60f54a..f8c329224 100644 --- a/hyperspace/core/src/events.rs +++ b/hyperspace/core/src/events.rs @@ -329,6 +329,7 @@ pub async fn parse_events( } }, IbcEvent::OpenInitChannel(open_init) => { + log::info!("Came inside open init channel {:?}", open_init); if let Some(channel_id) = open_init.channel_id { let channel_response = source .query_channel_end( @@ -390,6 +391,8 @@ pub async fn parse_events( signer: sink.account_id(), }; + log::info!("This is channel open try {:?}", msg); + let value = msg.clone().encode_vec(); let msg = Any { value, type_url: msg.type_url() }; messages.push(msg) @@ -397,9 +400,11 @@ pub async fn parse_events( }, IbcEvent::OpenTryChannel(open_try) => if let Some(channel_id) = open_try.channel_id { + log::info!("This is open try channel open try {:?}", open_try); let channel_response = source .query_channel_end(open_try.height(), channel_id, open_try.port_id.clone()) .await?; + log::info!("After query channel end"); let channel_end = ChannelEnd::try_from(channel_response.channel.ok_or_else(|| { Error::Custom(format!( diff --git a/hyperspace/core/src/lib.rs b/hyperspace/core/src/lib.rs index 22a0f7af6..36437b394 100644 --- a/hyperspace/core/src/lib.rs +++ b/hyperspace/core/src/lib.rs @@ -218,7 +218,7 @@ async fn process_some_finality_event( .query_latest_ibc_events(finality_event, &*sink) .await .map_err(|e| anyhow!("Failed to fetch IBC events for finality event {e}"))?; - log::trace!(target: "hyperspace", "Received updates count: {}", updates.len()); + log::info!(target: "hyperspace", "Received updates count: {} for chain {}", updates.len(), source.name()); // query packets that can now be sent, at this sink height because of connection // delay. let (ready_packets, mut timeout_msgs) = @@ -228,7 +228,7 @@ async fn process_some_finality_event( let mut msgs = Vec::new(); - log::trace!( + log::info!( target: "hyperspace", "{} has undelivered seqs: [{:?}:{}, {:?}:{}, {:?}:{}]", source.name(), UndeliveredType::Acks, source.has_undelivered_sequences(UndeliveredType::Acks), @@ -236,7 +236,7 @@ async fn process_some_finality_event( UndeliveredType::Recvs, source.has_undelivered_sequences(UndeliveredType::Recvs), ); - log::trace!( + log::info!( target: "hyperspace", "{} has undelivered seqs: [{:?}:{}, {:?}:{}, {:?}:{}]", sink.name(), UndeliveredType::Acks, sink.has_undelivered_sequences(UndeliveredType::Acks), @@ -251,6 +251,10 @@ async fn process_some_finality_event( ); process_updates(source, sink, metrics, mode, updates, &mut msgs, ready_packets.clone()).await?; + // if msgs.len() < 2 && !source.common_state().handshake_completed { + // log::info!("-------------------------------------------Skipping the update--------------------------------------------"); + // msgs = vec![]; + // } msgs.extend(ready_packets); diff --git a/hyperspace/primitives/src/utils.rs b/hyperspace/primitives/src/utils.rs index 05f134805..f74c7aa67 100644 --- a/hyperspace/primitives/src/utils.rs +++ b/hyperspace/primitives/src/utils.rs @@ -137,7 +137,7 @@ pub async fn create_connection( }; let msg = Any { type_url: msg.type_url(), value: msg.encode_vec() }; - + log::info!("This is msg {:?}", msg); let tx_id = chain_a.submit(vec![msg]).await?; let connection_id_a = chain_a.query_connection_id_from_tx_hash(tx_id).await?; chain_a.set_connection_id(connection_id_a.clone()); @@ -195,8 +195,11 @@ pub async fn create_channel( let msg = Any { type_url: msg.type_url(), value: msg.encode_vec() }; + log::info!("This is message sent {:?}", msg); let tx_id = chain_a.submit(vec![msg]).await?; + log::info!("This is tx id {:?}", tx_id); let channel_id_a = chain_a.query_channel_id_from_tx_hash(tx_id).await?; + log::info!("This is channel id on a {:?}", channel_id_a); chain_a.add_channel_to_whitelist(channel_id_a); log::info!(target: "hyperspace", "============= Wait till both chains have completed channel handshake ============="); diff --git a/hyperspace/rollup/Cargo.toml b/hyperspace/rollup/Cargo.toml new file mode 100644 index 000000000..7757b3fdd --- /dev/null +++ b/hyperspace/rollup/Cargo.toml @@ -0,0 +1,126 @@ +[package] +name = "hyperspace-rollup" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +primitives = { path = "../primitives", package = "hyperspace-primitives" } + +# crates.io +anyhow = "1.0.65" +futures = "0.3.21" +async-trait = "0.1.53" +log = "0.4.17" +hex = "0.4.3" +tokio = { version = "1.32.0", features = ["macros", "sync"] } +rs_merkle = "1.2.0" +codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +serde_json = "1.0.74" +derive_more = { version = "0.99", features = ["from"] } +serde = {version="1.0.137", features = ["derive"]} +tokio-stream = { version = "0.1.14", features = ["sync"]} +thiserror = "1.0.31" +itertools = "0.10.3" +prost = { version = "0.11" } +k256 = { version = "0.11.6", features = ["ecdsa-core", "ecdsa", "sha256"] } +tonic = { version = "0.8", features = ["tls", "tls-roots"] } +bech32 = "0.9.1" +bip32 = "0.4.0" +ed25519-zebra = { version = "3.1.0" } +tiny-bip39 = "1.0.0" +sha2 = "0.10.6" +ripemd = "0.1.3" +digest = "0.10.6" +quick_cache = "0.3.0" +rand = "0.8.5" +anchor-client = {version = "0.29.0", features = ["async"] } +anchor-lang = "0.29.0" +anchor-spl = "0.29.0" +solana-transaction-status = "=1.17.31" +base64 = { version = "0.21.4", default-features = false, features = ["alloc"] } +borsh = { version = "0.10.3", default-features = false } +bytemuck = { version = "1.14", default-features = false } +reqwest = "0.11.24" +futures-util = "0.3.28" +jito-searcher-client = { git = "https://github.com/dhruvja/searcher-examples" } +jito-protos = { git = "https://github.com/dhruvja/searcher-examples" } +prost-types = "0.12" +solana-metrics = "=1.17.31" +rusqlite = { version = "0.29.0", features = ["bundled"] } + +bs58 = { version = "0.5.0", features = ["alloc"] } +bincode = { version = "1.3.3" } + +# New IBC +ibc-new-primitives = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false, features = ["borsh", "serde"] , package="ibc-primitives" } +ibc-core-host-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false, features = ["borsh", "serde"]} +ibc-core-handler-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-client-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-connection-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-channel-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-app-transfer-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-commitment-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-client-tendermint-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-client-wasm-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false} +# ibc-testkit = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-proto-new = { version = "0.41.0", default-features = false, package="ibc-proto" } + +ics23 = { version = "0.11.1" } + + + +# Old IBC +ibc = { path = "../../ibc/modules", features = [] } +ibc-proto = { path = "../../ibc/proto", package="ibc-proto" } +ibc-primitives = { path = "../../contracts/pallet-ibc/primitives" } +ics07-tendermint = { path = "../../light-clients/ics07-tendermint", features = ["dummy"] } +ics08-wasm = { path = "../../light-clients/ics08-wasm" } +ibc-rpc = { path = "../../contracts/pallet-ibc/rpc" } +pallet-ibc = { path = "../../contracts/pallet-ibc", features = ["testing"]} + +# Trie +lib = { git = "https://github.com/ComposableFi/emulated-light-client/" } +memory = { git = "https://github.com/ComposableFi/emulated-light-client/" } +sealable-trie = { git = "https://github.com/ComposableFi/emulated-light-client/" ,features = ["borsh"] } +stdx = { git = "https://github.com/ComposableFi/emulated-light-client/" } +solana-trie = { git = "https://github.com/ComposableFi/emulated-light-client/" } +trie-ids = { git = "https://github.com/ComposableFi/emulated-light-client/" ,features = ["borsh"] } +cf-guest = { path = "../../light-clients/cf-guest", default-features = false } +cf-guest-og = { git = "https://github.com/ComposableFi/emulated-light-client/" ,package = "cf-guest" } +guestchain = { git = "https://github.com/ComposableFi/emulated-light-client/" ,default-features = false } + +cf-solana-og = { git = "https://github.com/ComposableFi/emulated-light-client/", package = "cf-solana", features = ["serde"] } +cf-solana = { path = "../../light-clients/cf-solana", default-features = false } + +#Contract +solana-ibc = { git = "https://github.com/ComposableFi/emulated-light-client/", features = ["no-entrypoint", "witness"]} +solana-write-account = { git = "https://github.com/ComposableFi/emulated-light-client/" ,features = ["library"] } +solana-signature-verifier = { git = "https://github.com/ComposableFi/emulated-light-client/" } + +tracing = "0.1.36" + +# tendermint +tendermint = { git = "https://github.com/informalsystems/tendermint-rs", rev = "4d81b67c28510db7d2d99ed62ebfa9fdf0e02141", default-features = false, features = [ + "secp256k1", +] } +tendermint_new = { git = "https://github.com/mina86/tendermint-rs", rev = "45fbd500d731effb95a98257630feb46f6c41d06", default-features = false, package = "tendermint" } +tendermint-proto = { git = "https://github.com/informalsystems/tendermint-rs", rev = "4d81b67c28510db7d2d99ed62ebfa9fdf0e02141", default-features = false } +tendermint-rpc = { git = "https://github.com/informalsystems/tendermint-rs", rev = "4d81b67c28510db7d2d99ed62ebfa9fdf0e02141", default-features = false, features = [ + "http-client", + "websocket-client", +] } +tendermint-light-client = { git = "https://github.com/informalsystems/tendermint-rs", rev = "4d81b67c28510db7d2d99ed62ebfa9fdf0e02141", default-features = false, features = [ + "rpc-client", + "secp256k1", + "unstable", +# "testing" +] } +tendermint-light-client-verifier = { git = "https://github.com/informalsystems/tendermint-rs", rev = "4d81b67c28510db7d2d99ed62ebfa9fdf0e02141", default-features = false } +tendermint-light-client-verifier_new = { git = "https://github.com/mina86/tendermint-rs", rev = "45fbd500d731effb95a98257630feb46f6c41d06", default-features = false, package = "tendermint-light-client-verifier" } + +[features] +testing = [ + "primitives/testing" +] diff --git a/hyperspace/rollup/src/client.rs b/hyperspace/rollup/src/client.rs new file mode 100644 index 000000000..1386a5da2 --- /dev/null +++ b/hyperspace/rollup/src/client.rs @@ -0,0 +1,1288 @@ +use anchor_client::{ + solana_client::{ + nonblocking::rpc_client::RpcClient as AsyncRpcClient, rpc_config::RpcSendTransactionConfig, + }, + solana_sdk::{ + account::ReadableAccount, + address_lookup_table::program, + commitment_config::{CommitmentConfig, CommitmentLevel}, + compute_budget::ComputeBudgetInstruction, + instruction::Instruction, + pubkey::ParsePubkeyError, + signature::{Keypair, Signature}, + signer::Signer as AnchorSigner, + transaction::Transaction, + }, + Client as AnchorClient, Cluster, Program, +}; +use anchor_lang::{prelude::*, system_program}; +use anchor_spl::associated_token::get_associated_token_address; +use core::{str::FromStr, time::Duration}; +use futures::future::join_all; +use ibc::{ + applications::transfer::{msgs::transfer::MsgTransfer, PrefixedCoin}, + core::{ + ics02_client::client_state::ClientType, + ics23_commitment::commitment::CommitmentPrefix, + ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}, + }, +}; +use ibc_app_transfer_types::{ + is_receiver_chain_source, is_sender_chain_source, Coin, PrefixedDenom, TracePrefix, +}; +use ibc_core_handler_types::msgs::MsgEnvelope; +use ibc_core_host_types::identifiers::ClientId as ClientIdNew; +use itertools::izip; +use lib::hash::CryptoHash; +use primitives::{CommonClientConfig, CommonClientState, IbcProvider}; +use serde::{Deserialize, Serialize}; +use sigverify::ed25519_program::{new_instruction, Entry}; +use solana_transaction_status::UiTransactionEncoding; +use std::{ + collections::HashSet, + ops::Deref, + rc::Rc, + result::Result, + sync::{Arc, Mutex}, + thread::sleep, +}; +use tendermint_light_client_verifier_new::types::{TrustedBlockState, UntrustedBlockState}; +use tendermint_rpc::Url; + +use super::{solana_ibc_STORAGE_SEED, CHAIN_SEED, TRIE_SEED}; +use crate::{ + error::Error, + events, + utils::{new_ed25519_instruction_with_signature, non_absent_vote}, +}; +use guestchain::{PubKey, Signature as _}; +use solana_ibc::{ + chain::ChainData, events::BlockFinalised, ix_data_account, storage::PrivateStorage, +}; +use tendermint_new::{ + chain, + vote::{SignedVote, ValidatorIndex}, +}; + +#[derive(Debug)] +pub enum DeliverIxType { + UpdateClient { + client_message: ibc_proto_new::google::protobuf::Any, + client_id: ClientIdNew, + }, + Recv { + token: Coin, + port_id: ibc_core_host_types::identifiers::PortId, + channel_id: ibc_core_host_types::identifiers::ChannelId, + receiver: String, + }, + Timeout { + token: Coin, + port_id: ibc_core_host_types::identifiers::PortId, + channel_id: ibc_core_host_types::identifiers::ChannelId, + sender_account: String, + }, + Acknowledgement { + sender: Pubkey, + }, + Normal, +} + +/// Implements the [`crate::Chain`] trait for solana +#[derive(Clone)] +pub struct RollupClient { + /// Chain name + pub name: String, + /// rpc url for solana + pub rpc_url: String, + /// rpc url for trie proofs + pub trie_rpc_url: String, + /// websocket url for solana + pub ws_url: String, + /// Solana chain Id + pub chain_id: String, + /// Light client id on counterparty chain + pub client_id: Arc>>, + /// Connection Id + pub connection_id: Arc>>, + /// Account prefix + pub account_prefix: String, + pub fee_denom: String, + /// The key that signs transactions + pub keybase: KeyEntry, + /// Maximun transaction size + pub max_tx_size: usize, + pub commitment_level: CommitmentLevel, + pub solana_ibc_program_id: Pubkey, + pub write_program_id: Pubkey, + pub signature_verifier_program_id: Pubkey, + pub common_state: CommonClientState, + pub client_type: ClientType, + pub last_searched_sig_for_send_packets: Arc>, + pub last_searched_sig_for_recv_packets: Arc>, + /// Reference to commitment + pub commitment_prefix: CommitmentPrefix, + /// Channels cleared for packet relay + pub channel_whitelist: Arc>>, + // Trie db path + pub trie_db_path: String, + // Sets whether to use JITO or RPC for submitting transactions + pub transaction_sender: TransactionSender, +} + +#[derive(std::fmt::Debug, Serialize, Deserialize, Clone)] +pub struct RollupClientConfig { + /// Chain name + pub name: String, + /// rpc url for solana + pub rpc_url: Url, + /// rpc url for trie proofs + pub trie_rpc_url: Url, + /// websocket url for solana + pub ws_url: Url, + /// Solana chain Id + pub chain_id: String, + /// Light client id on counterparty chain + pub client_id: Option, + /// Connection Id + pub connection_id: Option, + /// Account prefix + pub account_prefix: String, + /// Fee denom + pub fee_denom: String, + /// Fee amount + pub fee_amount: String, + /// Fee amount + pub gas_limit: u64, + /// Store prefix + pub store_prefix: String, + /// Maximun transaction size + pub max_tx_size: usize, + /// All the client states and headers will be wrapped in WASM ones using the WASM code ID. + pub wasm_checksum: Option, + pub common_state_config: CommonClientConfig, + /// Reference to commitment + pub commitment_prefix: Vec, + /// Channels cleared for packet relay + pub channel_whitelist: Vec<(ChannelId, PortId)>, + pub commitment_level: String, + pub private_key: Vec, + pub solana_ibc_program_id: String, + pub write_program_id: String, + pub signature_verifier_program_id: String, + pub trie_db_path: String, + pub transaction_sender: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum TransactionSender { + JITO, + RPC, +} + +#[derive(Debug, Clone)] +pub enum FinalityEvent { + Guest { blockhash: CryptoHash, block_height: u64 }, + Rollup { slot: u64, previous_blockhash: String, blockhash: String }, +} + +#[derive(Debug)] +struct Trie { + #[allow(dead_code)] + id: i32, + height: i32, + data: Vec, + state_root: Vec, + match_block_state_root: bool, +} + +#[derive(Clone)] +pub struct KeyEntry { + pub public_key: Pubkey, + pub private_key: Vec, +} + +impl KeyEntry { + pub fn keypair(&self) -> Keypair { + Keypair::from_bytes(&self.private_key).unwrap() + } +} + +impl From> for KeyEntry { + fn from(value: Vec) -> Self { + let keypair = Keypair::from_bytes(&value).unwrap(); + Self { public_key: keypair.pubkey(), private_key: value } + } +} + +impl RollupClient { + pub fn get_trie_key(&self) -> Pubkey { + let trie_seeds = &[TRIE_SEED]; + let trie = Pubkey::find_program_address(trie_seeds, &self.solana_ibc_program_id).0; + trie + } + + pub fn get_ibc_storage_key(&self) -> Pubkey { + let storage_seeds = &[solana_ibc_STORAGE_SEED]; + let ibc_storage = + Pubkey::find_program_address(storage_seeds, &self.solana_ibc_program_id).0; + ibc_storage + } + + pub fn get_witness_key(&self) -> Pubkey { + let trie_key = self.get_trie_key(); + let witness_seeds = &[b"witness", trie_key.as_ref()]; + let witness = Pubkey::find_program_address(witness_seeds, &self.solana_ibc_program_id).0; + log::info!("Witness key {}", witness); + witness + } + + pub fn get_chain_key(&self) -> Pubkey { + let chain_seeds = &[CHAIN_SEED]; + let chain = Pubkey::find_program_address(chain_seeds, &self.solana_ibc_program_id).0; + chain + } + + pub fn get_mint_auth_key(&self) -> Pubkey { + let mint_auth_seeds = &[solana_ibc::MINT_ESCROW_SEED]; + let mint_auth = + Pubkey::find_program_address(mint_auth_seeds, &self.solana_ibc_program_id).0; + mint_auth + } + + pub fn get_fee_collector_key(&self) -> Pubkey { + let fee_collector_seeds = &[solana_ibc::FEE_SEED]; + let fee_collector = + Pubkey::find_program_address(fee_collector_seeds, &self.solana_ibc_program_id).0; + fee_collector + } + + /// Returns trie at a particular height or the latest one if not available + pub async fn get_trie( + &self, + at: u64, + require_proof: bool, + ) -> (solana_trie::TrieAccount, ()>, bool) { + let connection = self.get_db(); + if require_proof { + let body = events::PayloadWithSingleParam::> { + jsonrpc: "2.0".to_string(), + id: 10, + method: "getSlotData".to_string(), + params: vec![at], + }; + let url = self.trie_rpc_url.clone(); + let response = tokio::task::spawn_blocking(move || { + for _ in 0..5 { + let response = + reqwest::blocking::Client::new().post(url.clone()).json(&body).send(); + let response = crate::utils::skip_fail!(response); + let response: std::result::Result = + response.json(); + let transactions = crate::utils::skip_fail!(response); + return transactions; + } + log::error!("Couldnt get transactions after 5 retries"); + panic!("WTF"); + }) + .await + .unwrap(); + let trie = solana_trie::TrieAccount::new(response.result.root_account.data().to_vec()) + .unwrap(); + return (trie, false); + } + let trie_key = self.get_trie_key(); + let rpc_client = self.rpc_client(); + let trie_account = rpc_client + .get_account_with_commitment(&trie_key, CommitmentConfig::processed()) + .await + .unwrap() + .value + .unwrap(); + let trie = solana_trie::TrieAccount::new(trie_account.data).unwrap(); + (trie, false) + } + + pub async fn get_ibc_storage(&self) -> PrivateStorageWithWitness { + let program = self.program(); + let ibc_storage_key = self.get_ibc_storage_key(); + let mut account_data = self.rpc_client().get_account_data(&ibc_storage_key).await.unwrap(); + let storage: PrivateStorageWithWitness = PrivateStorageWithWitness::deserialize(&mut &account_data[8..]).unwrap(); + // let storage = tokio::task::spawn_blocking(move || { + // program.account(ibc_storage_key).unwrap() + // }).await.unwrap(); + storage + } + + pub async fn get_chain_storage(&self) -> ChainData { + let program = self.program(); + let chain_storage_key = self.get_chain_key(); + let storage = program.account(chain_storage_key).await.unwrap(); + storage + } + + pub fn rpc_client(&self) -> AsyncRpcClient { + let program = self.program(); + program.async_rpc() + } + + pub fn client(&self) -> AnchorClient> { + let cluster = Cluster::from_str(&self.rpc_url).unwrap(); + let signer = self.keybase.keypair(); + let authority = Arc::new(signer); + let client = + AnchorClient::new_with_options(cluster, authority, CommitmentConfig::processed()); + client + } + + pub fn get_db(&self) -> rusqlite::Connection { + let db_url = self.trie_db_path.as_str(); + rusqlite::Connection::open(db_url).unwrap() + } + + pub fn program(&self) -> Program> { + let anchor_client = self.client(); + anchor_client.program(self.solana_ibc_program_id).unwrap() + } + + #[allow(dead_code)] + pub async fn new(config: RollupClientConfig) -> Result { + let db_url = config.trie_db_path.as_str(); + let conn = rusqlite::Connection::open(db_url).unwrap(); + let count = conn.query_row("SELECT COUNT(*) FROM Trie", [], |row| { + log::info!("This is row"); + Ok(()) + }); + log::info!("This is count {:?}", count); + let transaction_sender_str = config.transaction_sender.to_ascii_uppercase(); + let transaction_sender = match transaction_sender_str.as_str() { + "JITO" => TransactionSender::JITO, + "RPC" => TransactionSender::RPC, + _ => panic!("Invalid param transaction sender: Expected JITO/RPC"), + }; + Ok(Self { + name: config.name, + rpc_url: config.rpc_url.to_string(), + trie_rpc_url: config.trie_rpc_url.to_string(), + ws_url: config.ws_url.to_string(), + chain_id: config.chain_id, + client_id: Arc::new(Mutex::new(config.client_id)), + connection_id: Arc::new(Mutex::new(config.connection_id)), + account_prefix: config.account_prefix, + fee_denom: config.fee_denom, + keybase: config.private_key.into(), + max_tx_size: config.max_tx_size, + commitment_level: CommitmentLevel::from_str(&config.commitment_level).unwrap(), + solana_ibc_program_id: Pubkey::from_str(&config.solana_ibc_program_id).unwrap(), + write_program_id: Pubkey::from_str(&config.write_program_id).unwrap(), + signature_verifier_program_id: Pubkey::from_str(&config.signature_verifier_program_id) + .unwrap(), + common_state: CommonClientState { + handshake_completed: config.common_state_config.handshake_completed, + ..Default::default() + }, + client_type: "07-tendermint".to_string(), + last_searched_sig_for_send_packets: Arc::new( + tokio::sync::Mutex::new(String::default()), + ), + last_searched_sig_for_recv_packets: Arc::new( + tokio::sync::Mutex::new(String::default()), + ), + commitment_prefix: CommitmentPrefix::try_from(config.commitment_prefix).unwrap(), + channel_whitelist: Arc::new(Mutex::new(config.channel_whitelist.into_iter().collect())), + trie_db_path: config.trie_db_path, + transaction_sender, + }) + } + + pub async fn send_deliver( + &self, + instruction_type: DeliverIxType, + + chunk_account: Pubkey, + max_tries: u8, + ) -> Result<(Vec, Vec), Error> { + // log::info!("This is ix type {:?}", instruction_type); + let program = self.program(); + let signer = self.keybase.keypair(); + let authority = Arc::new(signer); + let rpc = self.rpc_client(); + let mut tries = 0; + let mut signature = String::new(); + let solana_ibc_storage_key = self.get_ibc_storage_key(); + let trie_key = self.get_trie_key(); + let chain_key = self.get_chain_key(); + let witness_key = self.get_witness_key(); + // while tries < max_tries { + // println!("Try For Tx: {}", tries); + // let mut status = true; + // let sig = + match instruction_type { + DeliverIxType::UpdateClient { ref client_message, ref client_id } => { + let header = cf_guest_og::Header::::try_from( + client_message.clone(), + ) + .unwrap(); + + let mut pubkeys = vec![]; + let mut final_signatures = vec![]; + let mut messages = vec![]; + + let fp = guestchain::block::Fingerprint::from_hash( + &header.genesis_hash, + header.block_header.block_height, + &header.block_hash, + ); + let mut validators = + header.epoch.validators().iter().map(Some).collect::>>(); + for (idx, sig) in header.signatures { + let validator = validators.get_mut(usize::from(idx)).unwrap().take().unwrap(); + pubkeys.push(validator.pubkey().clone()); + final_signatures.push(sig.to_vec()); + messages.push(fp.as_slice()); + } + + // log::info!("Pubkeys {:?}", pubkeys); + // log::info!("final_signatures {:?}", final_signatures); + // log::info!("messages {:?}", messages); + // Chunk the signatures + let total_signatures = final_signatures.len(); + let chunk_size = 3; + let chunks = total_signatures / chunk_size + 1; + let authority_bytes = authority.pubkey().to_bytes(); + let signature_seeds = &[authority_bytes.as_ref()]; + let (signatures_account_pda, bump) = Pubkey::find_program_address( + signature_seeds, + &self.signature_verifier_program_id, + ); + let mut signature_chunking_txs = Vec::new(); + for chunk in 0..chunks { + let start = chunk * chunk_size; + let end = (start + chunk_size).min(total_signatures); + println!("Start {} end {}", start, end); + + let accounts = vec![ + AccountMeta { + pubkey: authority.pubkey(), + is_signer: true, + is_writable: true, + }, + AccountMeta { + pubkey: signatures_account_pda, + is_signer: false, + is_writable: true, + }, + AccountMeta { + pubkey: anchor_lang::solana_program::sysvar::instructions::ID, + is_signer: false, + is_writable: true, + }, + AccountMeta { + pubkey: system_program::ID, + is_signer: false, + is_writable: true, + }, + ]; + let mut data = vec![0, 0]; + data.extend(&bump.to_le_bytes()); + let instruction = Instruction::new_with_bytes( + self.signature_verifier_program_id, + &data, + accounts, + ); + let mut entries = Vec::new(); + let temp_pubkeys = pubkeys[start..end].to_vec(); + let temp_signatures = final_signatures[start..end].to_vec(); + let temp_messages = messages[start..end].to_vec(); + for (pubkey, signature, message) in + izip!(&temp_pubkeys, &temp_signatures, &temp_messages,) + { + let pubkey = pubkey.0.as_bytes(); + let sig = signature.as_slice().try_into().unwrap(); + let message = message.clone(); + let entry: Entry = Entry { pubkey, signature: sig, message }; + entries.push(entry); + } + let ix = program + .request() + .instruction(ComputeBudgetInstruction::set_compute_unit_limit(300_000)) + // .instruction(ComputeBudgetInstruction::set_compute_unit_price(50_000)) + .instruction(new_instruction(entries.clone().as_slice()).unwrap()) + .instruction(instruction) + .instructions() + .unwrap(); + let blockhash = rpc.get_latest_blockhash().await.unwrap(); + let transactions = + Transaction::new_with_payer(ix.as_slice(), Some(&authority.pubkey())); + signature_chunking_txs.push(transactions); + // .send() + // .await + // .or_else(|e| { + // println!("This is error for signature {:?}", e); + // status = false; + // ibc::prelude::Err("Error".to_owned()) + // }); + // log::info!("This is signature for sending signature {:?}", sig); + } + // let futures = + // instructions.iter().map(|tx| rpc.send_and_confirm_transaction(tx)); + // let signatures = join_all(futures).await; + // for sig in signatures { + // println!(" Signature Chunking Signature {:?}", sig); + // } + let ix = program + .request() + .instruction(ComputeBudgetInstruction::set_compute_unit_limit(2_000_000u32)) + .instruction(ComputeBudgetInstruction::request_heap_frame(256 * 1024)) + // .instruction(ComputeBudgetInstruction::set_compute_unit_price(50_000)) + .accounts(vec![ + AccountMeta { + is_signer: true, + is_writable: true, + pubkey: authority.pubkey(), + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: solana_ibc_storage_key, + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: trie_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: witness_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: chain_key }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: system_program::ID, + }, + AccountMeta { + pubkey: signatures_account_pda, + is_signer: false, + is_writable: true, + }, + AccountMeta { pubkey: chunk_account, is_signer: false, is_writable: true }, + ]) + .args(ix_data_account::Instruction) + .signer(&*authority) + .instructions() + .unwrap(); + let blockhash = rpc.get_latest_blockhash().await.unwrap(); + let transactions = + Transaction::new_with_payer(ix.as_slice(), Some(&authority.pubkey())); + // .send() + // .await + // .or_else(|e| { + // println!("This is error {:?}", e); + // status = false; + // ibc::prelude::Err("Error".to_owned()) + // }); + let accounts = vec![ + AccountMeta { pubkey: authority.pubkey(), is_signer: true, is_writable: true }, + AccountMeta { + pubkey: signatures_account_pda, + is_signer: false, + is_writable: true, + }, + ]; + let mut data = vec![1, 0]; + data.extend(&bump.to_le_bytes()); + let instruction = Instruction::new_with_bytes( + self.signature_verifier_program_id, + &data, + accounts, + ); + let tx = program + .request() + .instruction(ComputeBudgetInstruction::set_compute_unit_limit(100_000)) + // .instruction(ComputeBudgetInstruction::set_compute_unit_price(50_000)) + .instruction(instruction) + .payer(authority) + .transaction() + .unwrap(); + // .send() + // .await.or_else(|e| { + // println!("This is error {:?}", e); + // status = false; + // ibc::prelude::Err("Error".to_owned()) + // }); + // log::info!("This is signature for freeing signature {:?}", sig); + // signature + Ok((signature_chunking_txs, vec![transactions, tx])) + }, + DeliverIxType::Recv { ref token, ref port_id, ref channel_id, ref receiver } => { + log::info!( + "PortId: {:?} and channel {:?} and token {:?}", + port_id, + channel_id, + token + ); + let (escrow_account, token_mint, receiver_account, receiver_address) = + get_accounts( + &token.denom, + self.solana_ibc_program_id, + receiver, + port_id, + channel_id, + &self.rpc_client(), + false, + ) + .await + .map_or( + ( + self.solana_ibc_program_id, + self.solana_ibc_program_id, + self.solana_ibc_program_id, + self.solana_ibc_program_id, + ), + |v| v, + ); + log::info!("This is token mint while sending transfer {:?}", token_mint); + let mint_authority = self.get_mint_auth_key(); + let ix = program + .request() + .instruction(ComputeBudgetInstruction::set_compute_unit_limit(2_000_000u32)) + .instruction(ComputeBudgetInstruction::request_heap_frame(256 * 1024)) + // .instruction(ComputeBudgetInstruction::set_compute_unit_price(500000)) + .accounts(vec![ + AccountMeta { + is_signer: true, + is_writable: true, + pubkey: authority.pubkey(), + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: receiver_account, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: solana_ibc_storage_key, + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: trie_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: witness_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: chain_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: mint_authority }, + AccountMeta { is_signer: false, is_writable: true, pubkey: token_mint }, + AccountMeta { is_signer: false, is_writable: true, pubkey: escrow_account }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: receiver_address, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.get_fee_collector_key(), + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: anchor_spl::associated_token::ID, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: anchor_spl::token::ID, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: system_program::ID, + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: chunk_account }, + ]) + .args(ix_data_account::Instruction) + .signer(&*authority) + .instructions() + .unwrap(); + let blockhash = rpc.get_latest_blockhash().await.unwrap(); + let transactions = + Transaction::new_with_payer(ix.as_slice(), Some(&authority.pubkey())); + Ok((vec![], vec![transactions])) + // .send() + // .await + // .or_else(|e| { + // println!("This is error {:?}", e); + // status = false; + // ibc::prelude::Err("Error".to_owned()) + // }) + }, + DeliverIxType::Timeout { + ref token, + ref port_id, + ref channel_id, + ref sender_account, + } => { + log::info!( + "PortId: {:?} and channel {:?} and token {:?}", + port_id, + channel_id, + token + ); + let (escrow_account, token_mint, sender_account, sender_address) = get_accounts( + &token.denom, + self.solana_ibc_program_id, + &sender_account, + port_id, + channel_id, + &self.rpc_client(), + true, + ) + .await + .map_or( + ( + self.solana_ibc_program_id, + self.solana_ibc_program_id, + self.solana_ibc_program_id, + self.solana_ibc_program_id, + ), + |v| v, + ); + log::info!("This is token mint while sending transfer {:?}", token_mint); + let mint_authority = self.get_mint_auth_key(); + let ix = program + .request() + .instruction(ComputeBudgetInstruction::set_compute_unit_limit(2_000_000u32)) + .instruction(ComputeBudgetInstruction::request_heap_frame(256 * 1024)) + // .instruction(ComputeBudgetInstruction::set_compute_unit_price(50_000)) + .accounts(vec![ + AccountMeta { + is_signer: true, + is_writable: true, + pubkey: authority.pubkey(), + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: sender_account }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: solana_ibc_storage_key, + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: trie_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: witness_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: chain_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: mint_authority }, + AccountMeta { is_signer: false, is_writable: true, pubkey: token_mint }, + AccountMeta { is_signer: false, is_writable: true, pubkey: escrow_account }, + AccountMeta { is_signer: false, is_writable: true, pubkey: sender_address }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.get_fee_collector_key(), + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: anchor_spl::associated_token::ID, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: anchor_spl::token::ID, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: system_program::ID, + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: chunk_account }, + ]) + .args(ix_data_account::Instruction) + .signer(&*authority) + .instructions() + .unwrap(); + let blockhash = rpc.get_latest_blockhash().await.unwrap(); + let transactions = + Transaction::new_with_payer(ix.as_slice(), Some(&authority.pubkey())); + Ok((vec![], vec![transactions])) + // .send() + // .await + // .or_else(|e| { + // println!("This is error {:?}", e); + // status = false; + // ibc::prelude::Err("Error".to_owned()) + // }) + }, + DeliverIxType::Acknowledgement { sender } => { + let ix = program + .request() + .instruction(ComputeBudgetInstruction::set_compute_unit_limit(2_000_000u32)) + .instruction(ComputeBudgetInstruction::request_heap_frame(256 * 1024)) + // .instruction(ComputeBudgetInstruction::set_compute_unit_price(50_000)) + .accounts(vec![ + AccountMeta { + is_signer: true, + is_writable: true, + pubkey: authority.pubkey(), + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: sender }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: solana_ibc_storage_key, + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: trie_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: witness_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: chain_key }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.get_fee_collector_key(), + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: system_program::ID, + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: chunk_account }, + ]) + // .args(solana_ibc::instruction::Deliver { message: message.clone() }) + .args(ix_data_account::Instruction) + .signer(&*authority) + .instructions() + .unwrap(); + let blockhash = rpc.get_latest_blockhash().await.unwrap(); + let transactions = + Transaction::new_with_payer(ix.as_slice(), Some(&authority.pubkey())); + let mut cloned_tx = transactions.clone(); + cloned_tx.sign(&[&*authority], blockhash); + // let serialized_tx = bincode::serialize(&cloned_tx).unwrap(); + // // encode in base 58 + // let encoded_tx = bs58::encode(serialized_tx).into_string(); + // log::info!("Encoded tx {:?}", encoded_tx); + // while i < 10 { + // let result = rpc.simulate_transaction(&transactions).await; + // log::info!("result {:?}", result); + // sleep(Duration::from_secs(2)); + // } + Ok((vec![], vec![transactions])) + // .send() + // .await + // .or_else(|e| { + // println!("This is error {:?}", e); + // status = false; + // ibc::prelude::Err("Error".to_owned()) + // }) + }, + DeliverIxType::Normal => { + let jito_address = + Pubkey::from_str("96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5").unwrap(); + let ix = program + .request() + // .instruction(anchor_lang::solana_program::system_program::transfer( + // &authority.pubkey(), + // &jito_address, + // 400000, + // )) + .instruction(ComputeBudgetInstruction::set_compute_unit_limit(2_000_000u32)) + .instruction(ComputeBudgetInstruction::request_heap_frame(128 * 1024)) + // .instruction(ComputeBudgetInstruction::set_compute_unit_price(50_000)) + .accounts(vec![ + AccountMeta { + is_signer: true, + is_writable: true, + pubkey: authority.pubkey(), + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: solana_ibc_storage_key, + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: trie_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: witness_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: chain_key }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: system_program::ID, + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: chunk_account }, + ]) + .args(ix_data_account::Instruction) + .payer(authority.clone()) + .signer(&*authority) + .instructions() + .unwrap(); + + let blockhash = rpc.get_latest_blockhash().await.unwrap(); + let transactions = + Transaction::new_with_payer(ix.as_slice(), Some(&authority.pubkey())); + Ok((vec![], vec![transactions])) + // .send_with_spinner_and_config(RpcSendTransactionConfig { + // skip_preflight: true, + // ..Default::default() + // }) + // .await + // .or_else(|e| { + // println!("This is error {:?}", e); + // status = false; + // ibc::prelude::Err("Error".to_owned()) + // }), + }, + } + + // if status { + // let blockhash = rpc.get_latest_blockhash().await.unwrap(); + // // Wait for finalizing the transaction + // let _ = rpc + // .confirm_transaction_with_spinner( + // &sig.clone().unwrap(), + // &blockhash, + // CommitmentConfig::finalized(), + // ) + // .await + // .unwrap(); + // signature = sig.unwrap().to_string(); + // break + // } + // sleep(Duration::from_millis(500)); + // tries += 1; + // } + // if tries == max_tries { + // log::info!("Max retries reached for normal tx in solana"); + // } + // Ok(signature) + } + + pub async fn send_transfer_inner( + &self, + msg: MsgTransfer, + ) -> Result<::TransactionId, Error> { + let keypair = self.keybase.keypair(); + println!("submitting tx now, {}", keypair.pubkey()); + let authority = Arc::new(keypair); + let program = self.program(); + + // Build, sign, and send program instruction + let solana_ibc_storage_key = self.get_ibc_storage_key(); + let trie_key = self.get_trie_key(); + let chain_key = self.get_chain_key(); + let witness_key = self.get_witness_key(); + + let mint_authority = self.get_mint_auth_key(); + + let channel_id = + ibc_core_host_types::identifiers::ChannelId::new(msg.source_channel.sequence()); + let port_id = + ibc_core_host_types::identifiers::PortId::from_str(msg.source_port.as_str()).unwrap(); + // let trace_path = TracePrefix::new(port_id.clone(), channel_id.clone()); + let prefixed_denom = ibc_app_transfer_types::PrefixedDenom { + // TODO(dhruv): implement conversion + trace_path: ibc_app_transfer_types::TracePath::default(), + base_denom: ibc_app_transfer_types::BaseDenom::from_str( + msg.token.denom.base_denom.as_str(), + ) + .unwrap(), + }; + let token = ibc_app_transfer_types::PrefixedCoin { + denom: prefixed_denom, + amount: ibc_app_transfer_types::Amount::from(msg.token.amount.as_u256().0), + }; + let hashed_denom = CryptoHash::digest(&token.denom.base_denom.as_str().as_bytes()); + let (escrow_account, token_mint) = + if is_sender_chain_source(port_id.clone(), channel_id.clone(), &token.denom) { + let escrow_seeds = ["escrow".as_bytes(), hashed_denom.as_ref()]; + let escrow_account = + Pubkey::find_program_address(&escrow_seeds, &self.solana_ibc_program_id).0; + // let prefix = TracePrefix::new(port_id.clone(), channel_id.clone()); + let base_denom = token.denom.base_denom.clone(); + // trace_path.remove_prefix(&prefix); + log::info!( + "This is base denom {:?} and trace path {:?}", + base_denom, + token.denom.trace_path + ); + let token_mint = Pubkey::from_str(&base_denom.to_string()).unwrap(); + (escrow_account, token_mint) + } else { + let token_mint_seeds = ["mint".as_bytes(), hashed_denom.as_ref()]; + let token_mint = + Pubkey::find_program_address(&token_mint_seeds, &self.solana_ibc_program_id).0; + (self.solana_ibc_program_id, token_mint) + }; + + let sender_token_address = get_associated_token_address( + &Pubkey::from_str(msg.sender.as_ref()).unwrap(), + &token_mint, + ); + let packet_data = ibc_app_transfer_types::packet::PacketData { + token, + sender: ibc_new_primitives::Signer::from(msg.sender.as_ref().to_string()), + receiver: ibc_new_primitives::Signer::from(msg.receiver.as_ref().to_string()), + memo: ibc_app_transfer_types::Memo::from(msg.memo), + }; + + let new_msg_transfer = ibc_app_transfer_types::msgs::transfer::MsgTransfer { + port_id_on_a: port_id.clone(), + chan_id_on_a: channel_id.clone(), + packet_data, + timeout_height_on_b: ibc_core_channel_types::timeout::TimeoutHeight::At( + ibc_core_client_types::Height::new( + msg.timeout_height.revision_number, + msg.timeout_height.revision_height, + ) + .unwrap(), + ), + timeout_timestamp_on_b: ibc_new_primitives::Timestamp::from_nanoseconds( + msg.timeout_timestamp.nanoseconds(), + ) + .unwrap(), + }; + + let sig = program + .request() + .instruction(ComputeBudgetInstruction::set_compute_unit_limit(2_000_000u32)) + .accounts(vec![ + AccountMeta { is_signer: true, is_writable: true, pubkey: authority.pubkey() }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.solana_ibc_program_id, + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: solana_ibc_storage_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: trie_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: witness_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: chain_key }, + AccountMeta { is_signer: false, is_writable: true, pubkey: mint_authority }, + AccountMeta { is_signer: false, is_writable: true, pubkey: token_mint }, + AccountMeta { is_signer: false, is_writable: true, pubkey: escrow_account }, + AccountMeta { is_signer: false, is_writable: true, pubkey: sender_token_address }, + AccountMeta { + is_signer: false, + is_writable: true, + pubkey: self.get_fee_collector_key(), + }, + AccountMeta { is_signer: false, is_writable: true, pubkey: anchor_spl::token::ID }, + AccountMeta { is_signer: false, is_writable: true, pubkey: system_program::ID }, + ]) + .args(solana_ibc::instruction::SendTransfer { + hashed_full_denom: hashed_denom, + msg: new_msg_transfer, + }) + // .payer(Arc::new(keypair)) + .signer(&*authority) + .send_with_spinner_and_config(RpcSendTransactionConfig { + skip_preflight: true, + ..Default::default() + }) + .await + .unwrap(); + let rpc = program.async_rpc(); + let blockhash = rpc.get_latest_blockhash().await.unwrap(); + // Wait for finalizing the transaction + let _ = rpc + .confirm_transaction_with_spinner(&sig, &blockhash, CommitmentConfig::finalized()) + .await + .unwrap(); + let signature = sig.to_string(); + Ok(signature) + } +} + +pub async fn get_accounts( + denom: &PrefixedDenom, + program_id: Pubkey, + receiver: &String, + port_id: &ibc_core_host_types::identifiers::PortId, + channel_id: &ibc_core_host_types::identifiers::ChannelId, + rpc: &AsyncRpcClient, + refund: bool, +) -> Result<(Pubkey, Pubkey, Pubkey, Pubkey), ParsePubkeyError> { + if (!refund && is_receiver_chain_source(port_id.clone(), channel_id.clone(), denom) + || (refund && is_sender_chain_source(port_id.clone(), channel_id.clone(), denom))) + { + log::info!("Receiver chain source"); + let hashed_denom = CryptoHash::digest(denom.base_denom.as_str().as_bytes()); + let escrow_seeds = ["escrow".as_bytes(), hashed_denom.as_ref()]; + let escrow_account = Pubkey::find_program_address(&escrow_seeds, &program_id).0; + let token_mint = Pubkey::from_str(&denom.base_denom.to_string())?; + let receiver_account = Pubkey::from_str(&receiver)?; + let receiver_address = get_associated_token_address(&receiver_account, &token_mint); + Ok((escrow_account, token_mint, receiver_account, receiver_address)) + } else { + log::info!("Not receiver chain source"); + let mut full_token = denom.clone(); + if !refund { + full_token.add_trace_prefix(TracePrefix::new(port_id.clone(), channel_id.clone())); + } + let hashed_denom = CryptoHash::digest(full_token.to_string().as_bytes()); + let token_mint_seeds = ["mint".as_bytes(), hashed_denom.as_ref()]; + let token_mint = Pubkey::find_program_address(&token_mint_seeds, &program_id).0; + let receiver_account = Pubkey::from_str(&receiver)?; + let receiver_address = get_associated_token_address(&receiver_account, &token_mint); + let token_mint_info = rpc.get_token_supply(&token_mint).await; + if token_mint_info.is_err() { + return Err(ParsePubkeyError::Invalid); + } + Ok((program_id, token_mint, receiver_account, receiver_address)) + } +} + +// #[test] +// fn test_fetch() { +// let tx_id = +// "33s9BBJyp5jy9dnuwTa4uEiNvzygCrroVr1z8ZNRSG25LDcqAbdKMHXC9emx1Q1ktfgKUbKqFMiqfKioWFx8JesD" +// .to_string(); +// let authority = Rc::new( +// Keypair::from_bytes(&vec![ +// 48, 123, 8, 80, 248, 0, 217, 142, 124, 193, 95, 24, 168, 139, 214, 136, 147, 210, 168, +// 135, 26, 36, 162, 89, 150, 185, 99, 191, 247, 135, 78, 111, 12, 8, 4, 81, 129, 165, +// 153, 230, 192, 225, 51, 119, 216, 14, 69, 225, 73, 7, 204, 144, 39, 213, 91, 255, 136, +// 38, 95, 131, 197, 4, 101, 186, +// ]) +// .unwrap(), +// ); +// let client = AnchorClient::new_with_options( +// Cluster::Devnet, +// authority.clone(), +// CommitmentConfig::processed(), +// ); +// let program = client +// .program(Pubkey::from_str("9FeHRJLHJSEw4dYZrABHWTRKruFjxDmkLtPmhM5WFYL7").unwrap()) +// .unwrap(); +// let signature = Signature::from_str(&tx_id).unwrap(); +// let sol_rpc_client = program.rpc(); +// let tx = sol_rpc_client.get_transaction(&signature, UiTransactionEncoding::Json).unwrap(); +// println!("This is tx {:?}", tx); +// } + +#[derive(Debug, borsh::BorshSerialize, borsh::BorshDeserialize)] +/// The private IBC storage, i.e. data which doesn’t require proofs. +pub struct PrivateStorageWithWitness { + /// Per-client information. + /// + /// Entry at index `N` corresponds to the client with IBC identifier + /// `client-`. + pub clients: Vec, + + /// Information about the counterparty on given connection. + /// + /// Entry at index `N` corresponds to the connection with IBC identifier + /// `connection-`. + pub connections: Vec>, + + /// Information about a each `(port, channel)` endpoint. + pub port_channel: solana_ibc::storage::map::Map, + + pub channel_counter: u32, + + pub fee_collector: Pubkey, + + pub new_fee_collector_proposal: Option, + + pub assets: solana_ibc::storage::map::Map, + + // Fee to be charged for each transfer + pub fee_in_lamports: u64, + + pub local_consensus_state: std::collections::VecDeque<(u64, u64, CryptoHash)>, +} \ No newline at end of file diff --git a/hyperspace/rollup/src/client_state.rs b/hyperspace/rollup/src/client_state.rs new file mode 100644 index 000000000..1e53b409f --- /dev/null +++ b/hyperspace/rollup/src/client_state.rs @@ -0,0 +1,213 @@ +use std::str::FromStr; + +use ibc::{ + core::{ + ics02_client::trust_threshold::TrustThreshold, ics23_commitment::specs::ProofSpecs, + ics24_host::identifier::ChainId, + }, + mock::header::MockHeader, + Height, +}; +use ibc_proto_new::ibc::lightclients::tendermint::v1::{ClientState, Fraction}; +use pallet_ibc::light_clients::AnyClientState; +use tendermint_proto::Protobuf; + +pub fn convert_new_client_state_to_old( + client_state: solana_ibc::client_state::AnyClientState, +) -> AnyClientState { + match client_state { + solana_ibc::client_state::AnyClientState::Tendermint(client) => { + let inner_client = client.inner(); + log::info!("This is latest height on solana {:?}", inner_client.latest_height); + AnyClientState::Tendermint(ics07_tendermint::client_state::ClientState { + chain_id: ChainId::from_str(inner_client.chain_id.as_str()).unwrap(), + trust_level: TrustThreshold::new( + inner_client.trust_level.numerator(), + inner_client.trust_level.denominator(), + ) + .unwrap(), + trusting_period: inner_client.trusting_period, + unbonding_period: inner_client.unbonding_period, + max_clock_drift: inner_client.max_clock_drift, + latest_height: Height::new( + inner_client.latest_height.revision_number(), + inner_client.latest_height.revision_height(), + ), + proof_specs: ProofSpecs::cosmos(), // Not sure about this + upgrade_path: inner_client.upgrade_path.clone(), + frozen_height: inner_client.frozen_height.and_then(|height| { + Some(Height::new(height.revision_number(), height.revision_height())) + }), + _phantom: std::marker::PhantomData, + }) + }, + // solana_ibc::client_state::AnyClientState::Mock(client) => + // AnyClientState::Mock(ibc::mock::client_state::MockClientState { + // header: MockHeader { + // height: Height::new( + // client.header.height.revision_number(), + // client.header.height.revision_height(), + // ), + // timestamp: ibc::timestamp::Timestamp::from_nanoseconds( + // client.header.timestamp.nanoseconds(), + // ) + // .unwrap(), + // }, + // frozen_height: client.frozen_height.and_then(|height| { + // Some(Height::new(height.revision_number(), height.revision_height())) + // }), + // }), + // solana_ibc::client_state::AnyClientState::Mock(_) => unimplemented!(), + solana_ibc::client_state::AnyClientState::Wasm(_) => unimplemented!(), + solana_ibc::client_state::AnyClientState::Rollup(cs) => { + AnyClientState::Rollup(cf_solana::ClientState(cf_solana_og::ClientState { + latest_slot: cs.latest_slot, + witness_account: cs.witness_account, + trusting_period_ns: cs.trusting_period_ns, + is_frozen: cs.is_frozen, + })) + }, + solana_ibc::client_state::AnyClientState::Guest(cs) => { + AnyClientState::Guest(cf_guest::ClientState(cf_guest_og::ClientState::new( + cs.genesis_hash, + cs.latest_height, + cs.trusting_period_ns, + cs.epoch_commitment, + Some(cs.prev_epoch_commitment), + cs.is_frozen, + ))) + }, + _ => unimplemented!(), + } +} + +pub fn convert_old_client_state_to_new( + client_state: AnyClientState, +) -> solana_ibc::client_state::AnyClientState { + match client_state { + #[allow(deprecated)] + AnyClientState::Tendermint(cs) => solana_ibc::client_state::AnyClientState::Tendermint( + ClientState { + chain_id: cs.chain_id.to_string(), + trust_level: Some(Fraction { + numerator: cs.trust_level.numerator(), + denominator: cs.trust_level.denominator(), + }), + trusting_period: Some(cs.trusting_period.into()), + unbonding_period: Some(cs.unbonding_period.into()), + max_clock_drift: Some(cs.max_clock_drift.into()), + frozen_height: cs.frozen_height.map_or( + Some(ibc_proto_new::ibc::core::client::v1::Height { + revision_height: 0, + revision_number: 0, + }), + |height| { + Some(ibc_proto_new::ibc::core::client::v1::Height { + revision_number: height.revision_number, + revision_height: height.revision_height, + }) + }, + ), + latest_height: Some(ibc_proto_new::ibc::core::client::v1::Height { + revision_number: cs.latest_height.revision_number, + revision_height: cs.latest_height.revision_height, + }), + proof_specs: ibc_core_commitment_types::specs::ProofSpecs::cosmos().into(), + upgrade_path: cs.upgrade_path, + allow_update_after_expiry: false, + allow_update_after_misbehaviour: false, + } + .try_into() + .unwrap(), + ), + #[allow(deprecated)] + AnyClientState::Rollup(cs) => { + let cs = cs.0; + solana_ibc::client_state::AnyClientState::Rollup(cf_solana_og::ClientState { + latest_slot: cs.latest_slot, + witness_account: cs.witness_account, + trusting_period_ns: cs.trusting_period_ns, + is_frozen: cs.is_frozen, + }) + }, + AnyClientState::Guest(cs) => { + let cs = cs.0; + solana_ibc::client_state::AnyClientState::Guest(cf_guest_og::ClientState::new( + cs.genesis_hash, + cs.latest_height, + cs.trusting_period_ns, + cs.epoch_commitment, + Some(cs.prev_epoch_commitment), + cs.is_frozen, + )) + }, + // AnyClientState::Mock(cs) => + // solana_ibc::client_state::AnyClientState::Mock(MockClientState { + // header: ibc_testkit::testapp::ibc::clients::mock::header::MockHeader { + // height: ibc_core_client_types::Height::new( + // cs.header.height().revision_number, + // cs.header.height().revision_height, + // ) + // .unwrap(), + // timestamp: ibc_new_primitives::Timestamp::from_nanoseconds( + // cs.header.timestamp.nanoseconds(), + // ) + // .unwrap(), + // }, + // frozen_height: cs.frozen_height.and_then(|height| { + // Some( + // ibc_core_client_types::Height::new( + // height.revision_number, + // height.revision_height, + // ) + // .unwrap(), + // ) + // }), + // }), + #[allow(deprecated)] + AnyClientState::Wasm(cs) => { + let cs = AnyClientState::decode_vec(&cs.data).unwrap(); + println!("This is tendermint\n {:?}", cs); + match cs { + AnyClientState::Tendermint(e) => { + log::info!( + "This is default {:?}", + ibc_core_commitment_types::specs::ProofSpecs::cosmos() + ); + log::info!("This is from client state {:?}", e.proof_specs); + solana_ibc::client_state::AnyClientState::Tendermint( + ClientState { + chain_id: e.chain_id.to_string(), + trust_level: Some(Fraction { + numerator: e.trust_level.numerator(), + denominator: e.trust_level.denominator(), + }), + trusting_period: Some(e.trusting_period.into()), + unbonding_period: Some(e.unbonding_period.into()), + max_clock_drift: Some(e.max_clock_drift.into()), + frozen_height: e.frozen_height.and_then(|height| { + Some(ibc_proto_new::ibc::core::client::v1::Height { + revision_number: height.revision_number, + revision_height: height.revision_height, + }) + }), + latest_height: Some(ibc_proto_new::ibc::core::client::v1::Height { + revision_number: e.latest_height.revision_number, + revision_height: e.latest_height.revision_height, + }), + proof_specs: ibc_core_commitment_types::specs::ProofSpecs::cosmos() + .into(), + upgrade_path: e.upgrade_path, + allow_update_after_expiry: false, + allow_update_after_misbehaviour: false, + } + .try_into() + .unwrap(), + ) + }, + _ => panic!("Invalid state {:?}", cs), + } + }, + _ => panic!("Client state not supported"), + } +} diff --git a/hyperspace/rollup/src/consensus_state.rs b/hyperspace/rollup/src/consensus_state.rs new file mode 100644 index 000000000..da0ffadc8 --- /dev/null +++ b/hyperspace/rollup/src/consensus_state.rs @@ -0,0 +1,118 @@ +use ibc::core::ics23_commitment::commitment::CommitmentRoot; +use ibc_proto_new::{ + google::protobuf::Timestamp, ibc::lightclients::tendermint::v1::ConsensusState, +}; +use pallet_ibc::light_clients::AnyConsensusState; +use tendermint::Hash; + +pub fn convert_new_consensus_state_to_old( + consensus_state: solana_ibc::consensus_state::AnyConsensusState, +) -> AnyConsensusState { + match consensus_state { + solana_ibc::consensus_state::AnyConsensusState::Tendermint(cs) => { + let timestamp_in_secs = cs.timestamp().unix_timestamp(); + let remaining_timestamp_in_nano = + (cs.timestamp().unix_timestamp_nanos() % 1_000_000_000) as u32; + AnyConsensusState::Tendermint(ics07_tendermint::consensus_state::ConsensusState { + timestamp: tendermint::time::Time::from_unix_timestamp( + timestamp_in_secs, + remaining_timestamp_in_nano, + ) + .unwrap(), + root: CommitmentRoot { bytes: cs.inner().root.as_bytes().to_vec() }, + next_validators_hash: Hash::try_from(cs.next_validators_hash().as_bytes().to_vec()) + .unwrap(), + }) + }, + // solana_ibc::consensus_state::AnyConsensusState::Mock(cs) => + // AnyConsensusState::Mock(ibc::mock::client_state::MockConsensusState { + // header: MockHeader { + // height: Height::new( + // cs.header.height.revision_number(), + // cs.header.height.revision_height(), + // ), + // timestamp: ibc::timestamp::Timestamp::from_nanoseconds( + // cs.header.timestamp.nanoseconds(), + // ) + // .unwrap(), + // }, + // root: CommitmentRoot { bytes: cs.root.into_vec() }, + // }), + // solana_ibc::consensus_state::AnyConsensusState::Mock(_) => unimplemented!(), + solana_ibc::consensus_state::AnyConsensusState::Wasm(_) => { + panic!("Guest consensus not supported") + }, + solana_ibc::consensus_state::AnyConsensusState::Rollup(cs) => { + AnyConsensusState::Rollup(cf_solana::ConsensusState(cf_solana_og::ConsensusState { + trie_root: cs.trie_root, + timestamp_sec: cs.timestamp_sec, + })) + }, + solana_ibc::consensus_state::AnyConsensusState::Guest(cs) => { + AnyConsensusState::Guest(cf_guest::ConsensusState(cf_guest_og::ConsensusState { + block_hash: cs.block_hash, + timestamp_ns: cs.timestamp_ns, + })) + }, + _ => unimplemented!(), + } +} + +pub fn convert_old_consensus_state_to_new( + consensus_state: AnyConsensusState, +) -> solana_ibc::consensus_state::AnyConsensusState { + match consensus_state { + AnyConsensusState::Tendermint(cs) => { + let timestamp_in_secs = cs.timestamp.unix_timestamp(); + let remaining_timestamp_in_nano = + (cs.timestamp.unix_timestamp_nanos() % 1_000_000_000) as i32; + solana_ibc::consensus_state::AnyConsensusState::Tendermint( + ConsensusState { + timestamp: Some(Timestamp { + seconds: timestamp_in_secs, + nanos: remaining_timestamp_in_nano, + }), + root: Some(ibc_proto_new::ibc::core::commitment::v1::MerkleRoot { + hash: cs.root.bytes, + }), + next_validators_hash: cs.next_validators_hash.as_bytes().to_vec(), + } + .try_into() + .unwrap(), + ) + }, + AnyConsensusState::Rollup(cs) => { + let cs = cs.0; + solana_ibc::consensus_state::AnyConsensusState::Rollup(cf_solana_og::ConsensusState { + trie_root: cs.trie_root, + timestamp_sec: cs.timestamp_sec, + }) + }, + AnyConsensusState::Guest(cs) => { + let cs = cs.0; + solana_ibc::consensus_state::AnyConsensusState::Guest(cf_guest_og::ConsensusState { + block_hash: cs.block_hash, + timestamp_ns: cs.timestamp_ns, + }) + }, + // AnyConsensusState::Mock(cs) => solana_ibc::consensus_state::AnyConsensusState::Mock( + // ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState { + // header: ibc_testkit::testapp::ibc::clients::mock::header::MockHeader { + // height: ibc_core_client_types::Height::new( + // cs.header.height().revision_number, + // cs.header.height().revision_height, + // ) + // .unwrap(), + // timestamp: ibc_new_primitives::Timestamp::from_nanoseconds( + // cs.header.timestamp.nanoseconds(), + // ) + // .unwrap(), + // }, + // root: ibc_core_commitment_types::commitment::CommitmentRoot::from_bytes( + // cs.root.as_bytes(), + // ), + // }, + // ), + _ => panic!("Client state not supported"), + } +} diff --git a/hyperspace/rollup/src/error.rs b/hyperspace/rollup/src/error.rs new file mode 100644 index 000000000..770ff56d7 --- /dev/null +++ b/hyperspace/rollup/src/error.rs @@ -0,0 +1,34 @@ +use ibc::timestamp::ParseTimestampError; +use prost::DecodeError; + +/// Error definitions for the cosmos client in accordance with the parachain's Error type. +#[derive(thiserror::Error, Debug)] +pub enum Error { + /// An error from the rpc interface + #[error("Rpc client error: {0}")] + RpcError(String), + /// Custom error + #[error("{0}")] + Custom(String), + /// Decode error + #[error("Decode error: {0}")] + DecodeError(#[from] DecodeError), + /// Encode error + #[error("Encode error: {0}")] + EncodeError(#[from] prost::EncodeError), + /// Parse timestamp error + #[error("Parse timestamp error: {0}")] + ParseTimestampError(#[from] ParseTimestampError), + /// Transfer error + #[error("IBC transfer error: {0}")] + TransferError(#[from] ibc::applications::transfer::error::Error), + /// Tendermint error + #[error("Tendermint error: {0}")] + TendermintError(#[from] tendermint::Error), +} + +impl From for Error { + fn from(error: String) -> Self { + Self::Custom(error) + } +} diff --git a/hyperspace/rollup/src/events.rs b/hyperspace/rollup/src/events.rs new file mode 100644 index 000000000..5aa281b46 --- /dev/null +++ b/hyperspace/rollup/src/events.rs @@ -0,0 +1,965 @@ +use anchor_client::{ + solana_client::{ + nonblocking::rpc_client::RpcClient, rpc_client::GetConfirmedSignaturesForAddress2Config, + }, + solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}, +}; +use guestchain::{BlockHeader, Signature as SignatureTrait}; +use lib::hash::CryptoHash; +use serde::{Deserialize, Serialize}; +use solana_ibc::events::Epoch; +use solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta; +use std::str::FromStr; +use tokio::runtime::Runtime; + +use base64::Engine; +use ibc::{ + core::{ + ics02_client::{ + client_state::ClientType, + events::{ + Attributes as ClientAttributes, ClientMisbehaviour, CreateClient, UpdateClient, + UpgradeClient, + }, + }, + ics03_connection::events::{ + Attributes as ConnAttributes, OpenAck as ConnOpenAck, OpenConfirm as ConnOpenConfirm, + OpenInit as ConnOpenInit, OpenTry as ConnOpenTry, + }, + ics04_channel::{ + events::{ + AcknowledgePacket, CloseConfirm as ChanCloseConfirm, CloseInit as ChanCloseInit, + OpenAck as ChanOpenAck, OpenConfirm as ChanOpenConfirm, OpenInit as ChanOpenInit, + OpenTry as ChanOpenTry, ReceivePacket, SendPacket, TimeoutPacket, + WriteAcknowledgement, + }, + packet::{Packet, Sequence}, + }, + ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}, + ics26_routing::context::ModuleId, + }, + events::{IbcEvent, ModuleEvent, ModuleEventAttribute}, + timestamp::Timestamp, + Height, +}; +use pallet_ibc::light_clients::Signature; + +use crate::utils::skip_fail; + +pub fn convert_new_event_to_old( + event: ibc_core_handler_types::events::IbcEvent, + height: Height, +) -> Option { + match event { + ibc_core_handler_types::events::IbcEvent::CreateClient(e) => { + let eve = CreateClient(ClientAttributes { + height: Height { + revision_number: e.consensus_height().revision_number(), + revision_height: e.consensus_height().revision_height(), + }, + client_id: ClientId::from_str(e.client_id().as_str()).unwrap(), + client_type: ClientType::from_str(e.client_type().as_str()).unwrap(), + consensus_height: Height { + revision_number: e.consensus_height().revision_number(), + revision_height: e.consensus_height().revision_height(), + }, + }); + Some(IbcEvent::CreateClient(eve)) + }, + ibc_core_handler_types::events::IbcEvent::UpdateClient(e) => { + let eve = UpdateClient { + common: ClientAttributes { + height: Height { + revision_number: e.consensus_height().revision_number(), + revision_height: e.consensus_height().revision_height(), + }, + client_id: ClientId::from_str(e.client_id().as_str()).unwrap(), + client_type: ClientType::from_str(e.client_type().as_str()).unwrap(), + consensus_height: Height { + revision_number: e.consensus_height().revision_number(), + revision_height: e.consensus_height().revision_height(), + }, + }, + header: Some(e.header().clone()), + }; + Some(IbcEvent::UpdateClient(eve)) + }, + ibc_core_handler_types::events::IbcEvent::UpgradeClient(e) => { + let eve = UpgradeClient(ClientAttributes { + height: Height { + revision_number: e.consensus_height().revision_number(), + revision_height: e.consensus_height().revision_height(), + }, + client_id: ClientId::from_str(e.client_id().as_str()).unwrap(), + client_type: ClientType::from_str(e.client_type().as_str()).unwrap(), + consensus_height: Height { + revision_number: e.consensus_height().revision_number(), + revision_height: e.consensus_height().revision_height(), + }, + }); + Some(IbcEvent::UpgradeClient(eve)) + }, + ibc_core_handler_types::events::IbcEvent::ClientMisbehaviour(e) => { + let eve = ClientMisbehaviour(ClientAttributes { + height, + client_id: ClientId::from_str(e.client_id().as_str()).unwrap(), + client_type: ClientType::from_str(e.client_type().as_str()).unwrap(), + consensus_height: height, + }); + Some(IbcEvent::ClientMisbehaviour(eve)) + }, + ibc_core_handler_types::events::IbcEvent::OpenInitConnection(e) => { + let eve = ConnOpenInit(ConnAttributes { + height, + client_id: ClientId::from_str(e.client_id_on_a().as_str()).unwrap(), + counterparty_client_id: ClientId::from_str(e.client_id_on_b().as_str()).unwrap(), + counterparty_connection_id: e + .conn_id_on_b() + .and_then(|conn| Some(ConnectionId::from_str(conn.as_str()).unwrap())), + connection_id: Some(ConnectionId::from_str(e.conn_id_on_a().as_str()).unwrap()), + }); + Some(IbcEvent::OpenInitConnection(eve)) + }, + ibc_core_handler_types::events::IbcEvent::OpenTryConnection(e) => { + let eve = ConnOpenTry(ConnAttributes { + height, + client_id: ClientId::from_str(e.client_id_on_b().as_str()).unwrap(), + counterparty_client_id: ClientId::from_str(e.client_id_on_b().as_str()).unwrap(), + counterparty_connection_id: e.conn_id_on_a().and_then(|conn_id| { + Some(ConnectionId::from_str(conn_id.clone().as_str()).unwrap()) + }), + connection_id: Some( + ConnectionId::from_str(e.conn_id_on_b().clone().as_str()).unwrap(), + ), + }); + Some(IbcEvent::OpenTryConnection(eve)) + }, + ibc_core_handler_types::events::IbcEvent::OpenAckConnection(e) => { + let eve = ConnOpenAck(ConnAttributes { + height, + client_id: ClientId::from_str(e.client_id_on_a().as_str()).unwrap(), + counterparty_client_id: ClientId::from_str(e.client_id_on_b().as_str()).unwrap(), + counterparty_connection_id: e + .conn_id_on_b() + .and_then(|conn| Some(ConnectionId::from_str(conn.as_str()).unwrap())), + connection_id: Some(ConnectionId::from_str(e.conn_id_on_a().as_str()).unwrap()), + }); + Some(IbcEvent::OpenAckConnection(eve)) + }, + ibc_core_handler_types::events::IbcEvent::OpenConfirmConnection(e) => { + let eve = ConnOpenConfirm(ConnAttributes { + height, + client_id: ClientId::from_str(e.client_id_on_a().as_str()).unwrap(), + counterparty_client_id: ClientId::from_str(e.client_id_on_b().as_str()).unwrap(), + counterparty_connection_id: e + .conn_id_on_a() + .and_then(|conn| Some(ConnectionId::from_str(conn.as_str()).unwrap())), + connection_id: Some(ConnectionId::from_str(e.conn_id_on_b().as_str()).unwrap()), + }); + Some(IbcEvent::OpenConfirmConnection(eve)) + }, + ibc_core_handler_types::events::IbcEvent::OpenInitChannel(e) => { + let eve = ChanOpenInit { + height, + port_id: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + channel_id: Some(ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap()), + connection_id: ConnectionId::from_str(e.conn_id_on_a().as_str()).unwrap(), + counterparty_port_id: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + counterparty_channel_id: None, + }; + Some(IbcEvent::OpenInitChannel(eve)) + }, + ibc_core_handler_types::events::IbcEvent::OpenTryChannel(e) => { + let eve = ChanOpenTry { + height, + port_id: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + channel_id: Some(ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap()), + connection_id: ConnectionId::from_str(e.conn_id_on_b().as_str()).unwrap(), + counterparty_port_id: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + counterparty_channel_id: Some( + ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap(), + ), + }; + Some(IbcEvent::OpenTryChannel(eve)) + }, + ibc_core_handler_types::events::IbcEvent::OpenAckChannel(e) => { + let eve = ChanOpenAck { + height, + port_id: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + channel_id: Some(ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap()), + connection_id: ConnectionId::from_str(e.conn_id_on_a().as_str()).unwrap(), + counterparty_port_id: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + counterparty_channel_id: Some( + ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), + ), + }; + Some(IbcEvent::OpenAckChannel(eve)) + }, + ibc_core_handler_types::events::IbcEvent::OpenConfirmChannel(e) => { + let eve = ChanOpenConfirm { + height, + port_id: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + channel_id: Some(ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap()), + connection_id: ConnectionId::from_str(e.conn_id_on_b().as_str()).unwrap(), + counterparty_port_id: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + counterparty_channel_id: Some( + ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap(), + ), + }; + Some(IbcEvent::OpenConfirmChannel(eve)) + }, + ibc_core_handler_types::events::IbcEvent::CloseInitChannel(e) => { + let eve = ChanCloseInit { + height, + port_id: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + channel_id: ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap(), + connection_id: ConnectionId::from_str(e.conn_id_on_a().as_str()).unwrap(), + counterparty_port_id: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + counterparty_channel_id: None, + }; + Some(IbcEvent::CloseInitChannel(eve)) + }, + ibc_core_handler_types::events::IbcEvent::CloseConfirmChannel(e) => { + let eve = ChanCloseConfirm { + height, + port_id: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + channel_id: Some(ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap()), + connection_id: ConnectionId::from_str(e.conn_id_on_b().as_str()).unwrap(), + counterparty_port_id: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + counterparty_channel_id: None, + }; + Some(IbcEvent::CloseConfirmChannel(eve)) + }, + ibc_core_handler_types::events::IbcEvent::SendPacket(e) => { + let eve = SendPacket { + height, + packet: Packet { + sequence: Sequence(e.seq_on_a().value()), + source_port: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + source_channel: ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap(), + destination_port: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + destination_channel: ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), + data: e.packet_data().to_vec(), + timeout_height: match e.timeout_height_on_b() { + ibc_core_channel_types::timeout::TimeoutHeight::Never => { + Height { revision_height: 0, revision_number: 0 } + }, + ibc_core_channel_types::timeout::TimeoutHeight::At(h) => Height { + revision_height: h.revision_height(), + revision_number: h.revision_number(), + }, + }, + timeout_timestamp: Timestamp::from_nanoseconds( + e.timeout_timestamp_on_b().nanoseconds(), + ) + .unwrap(), + }, + }; + Some(IbcEvent::SendPacket(eve)) + }, + ibc_core_handler_types::events::IbcEvent::ReceivePacket(e) => { + let eve = ReceivePacket { + height, + packet: Packet { + sequence: Sequence(e.seq_on_b().value()), + source_port: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + source_channel: ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap(), + destination_port: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + destination_channel: ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), + data: e.packet_data().to_vec(), + timeout_height: match e.timeout_height_on_b() { + ibc_core_channel_types::timeout::TimeoutHeight::Never => { + Height { revision_height: 0, revision_number: 0 } + }, + ibc_core_channel_types::timeout::TimeoutHeight::At(h) => Height { + revision_height: h.revision_height(), + revision_number: h.revision_number(), + }, + }, + timeout_timestamp: Timestamp::from_nanoseconds( + e.timeout_timestamp_on_b().nanoseconds(), + ) + .unwrap(), + }, + }; + Some(IbcEvent::ReceivePacket(eve)) + }, + ibc_core_handler_types::events::IbcEvent::WriteAcknowledgement(e) => { + let eve = WriteAcknowledgement { + height, + packet: Packet { + sequence: Sequence(e.seq_on_a().value()), + source_port: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + source_channel: ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap(), + destination_port: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + destination_channel: ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), + data: e.packet_data().to_vec(), + timeout_height: match e.timeout_height_on_b() { + ibc_core_channel_types::timeout::TimeoutHeight::Never => { + Height { revision_height: 0, revision_number: 0 } + }, + ibc_core_channel_types::timeout::TimeoutHeight::At(h) => Height { + revision_height: h.revision_height(), + revision_number: h.revision_number(), + }, + }, + timeout_timestamp: Timestamp::from_nanoseconds( + e.timeout_timestamp_on_b().nanoseconds(), + ) + .unwrap(), + }, + ack: e.acknowledgement().as_bytes().to_vec(), + }; + Some(IbcEvent::WriteAcknowledgement(eve)) + }, + ibc_core_handler_types::events::IbcEvent::AcknowledgePacket(e) => { + let eve = AcknowledgePacket { + height, + packet: Packet { + sequence: Sequence(e.seq_on_a().value()), + source_port: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + source_channel: ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap(), + destination_port: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + destination_channel: ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), + data: Vec::new(), + timeout_height: match e.timeout_height_on_b() { + ibc_core_channel_types::timeout::TimeoutHeight::Never => { + Height { revision_height: 0, revision_number: 0 } + }, + ibc_core_channel_types::timeout::TimeoutHeight::At(h) => Height { + revision_height: h.revision_height(), + revision_number: h.revision_number(), + }, + }, + timeout_timestamp: Timestamp::from_nanoseconds( + e.timeout_timestamp_on_b().nanoseconds(), + ) + .unwrap(), + }, + }; + Some(IbcEvent::AcknowledgePacket(eve)) + }, + ibc_core_handler_types::events::IbcEvent::TimeoutPacket(e) => { + let eve = TimeoutPacket { + height, + packet: Packet { + sequence: Sequence(e.seq_on_a().value()), + source_port: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + source_channel: ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap(), + destination_port: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + destination_channel: ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), + data: Vec::new(), // Not sure about this + timeout_height: match e.timeout_height_on_b() { + ibc_core_channel_types::timeout::TimeoutHeight::Never => { + Height { revision_height: 0, revision_number: 0 } + }, + ibc_core_channel_types::timeout::TimeoutHeight::At(h) => Height { + revision_height: h.revision_height(), + revision_number: h.revision_number(), + }, + }, + timeout_timestamp: Timestamp::from_nanoseconds( + e.timeout_timestamp_on_b().nanoseconds(), + ) + .unwrap(), + }, + }; + Some(IbcEvent::TimeoutPacket(eve)) + }, + ibc_core_handler_types::events::IbcEvent::ChannelClosed(_) => None, + ibc_core_handler_types::events::IbcEvent::Module(e) => { + let attributes: Vec = e + .attributes + .iter() + .map(|attr| ModuleEventAttribute { + key: attr.clone().key, + value: attr.clone().value, + }) + .collect(); + let eve = ModuleEvent { + kind: e.kind, + module_name: ModuleId::from_str("transfer").unwrap(), + attributes, + }; + Some(IbcEvent::AppModule(eve)) + }, + ibc_core_handler_types::events::IbcEvent::Message(_) => None, + } +} + +pub fn get_ibc_events_from_logs( + logs: Vec, +) -> (Vec, u64) { + let (events, proof_height) = get_events_from_logs(logs); + let events: Vec = events + .iter() + .filter_map(|event| match event { + solana_ibc::events::Event::IbcEvent(e) => Some(e.clone()), + _ => None, + }) + .collect(); + (events, proof_height) +} + +pub async fn get_client_state_at_height( + rpc: RpcClient, + program_id: Pubkey, + upto_height: u64, +) -> Option { + log::info!("Getting client states at height {:?}", upto_height); + let mut before_hash = None; + let mut current_height = upto_height; + while current_height >= upto_height { + let (transactions, last_searched_hash) = + get_previous_transactions(&rpc, program_id, before_hash, SearchIn::IBC).await; + if transactions.is_empty() { + break; + } + before_hash = Some( + anchor_client::solana_sdk::signature::Signature::from_str(&last_searched_hash).unwrap(), + ); + for tx in transactions { + let logs = match tx.result.transaction.meta.clone().unwrap().log_messages { + solana_transaction_status::option_serializer::OptionSerializer::Some(e) => e, + _ => Vec::new(), + }; + let (events, height) = get_events_from_logs(logs); + let client_state_events: Vec = events + .iter() + .filter_map(|event| match event { + solana_ibc::events::Event::ClientStateUpdate(e) => Some(e.clone()), + _ => None, + }) + .collect(); + current_height = height; + if height == 0 || client_state_events.is_empty() { + continue; + } + log::info!("Found height {:?}", height); + if height < upto_height { + break; + } + // There can be only one client state event in a tx + let current_client_state = &client_state_events[0]; + let any_client_state: solana_ibc::client_state::AnyClientState = + borsh::BorshDeserialize::try_from_slice(current_client_state.state.as_ref()) + .unwrap(); + log::info!("This is any client state {:?}", any_client_state); + return Some(any_client_state); + } + } + None +} + +pub fn get_events_from_logs(logs: Vec) -> (Vec>, u64) { + let serialized_events: Vec<&str> = logs + .iter() + .filter_map(|log| { + if log.starts_with("Program data: ") { + Some(log.strip_prefix("Program data: ").unwrap()) + } else { + None + } + }) + .collect(); + let height_str = logs + .iter() + .find_map(|log| { + if log.starts_with("Program log: Current Block height ") { + Some(log.strip_prefix("Program log: Current Block height ").unwrap()) + } else { + None + } + }) + .map_or("0", |height| height); + let height = height_str.parse::().unwrap(); + let events: Vec = serialized_events + .iter() + .map(|event| { + let decoded_event = base64::prelude::BASE64_STANDARD.decode(event).unwrap(); + let decoded_event: solana_ibc::events::Event = + borsh::BorshDeserialize::try_from_slice(&decoded_event).unwrap(); + decoded_event + }) + .collect(); + (events, height) +} + +pub async fn _get_signatures_for_blockhash( + rpc: RpcClient, + program_id: Pubkey, + blockhash: CryptoHash, +) -> Result<(Vec<(Pubkey, Signature)>, BlockHeader), String> { + let (transactions, _) = + get_previous_transactions(&rpc, program_id, None, SearchIn::GuestChain).await; + + let mut signatures = Vec::new(); + // let mut index = 0; + for tx in transactions { + let logs = match tx.result.transaction.meta.clone().unwrap().log_messages { + solana_transaction_status::option_serializer::OptionSerializer::Some(e) => e, + _ => Vec::new(), + }; + let (events, _proof_height) = get_events_from_logs(logs); + // Find block signed events with blockhash + let block_header: Vec> = events + .iter() + .map(|event| match event { + solana_ibc::events::Event::NewBlock(e) => { + println!("This is new block event {:?}", e.block_header.0.block_height); + let new_blockhash = e.block_header.0.calc_hash(); + if blockhash == new_blockhash { + println!("New block event where it is true"); + return Some(e.block_header.0.clone()); + } + None + }, + solana_ibc::events::Event::BlockSigned(e) => { + println!("This is block signed event {:?}", e.block_height); + if e.block_hash == blockhash { + println!("This is block signed in side blockhash"); + signatures.push(( + Pubkey::new_from_array(e.pubkey.clone().into()), + Signature::from_bytes(&e.signature.to_vec()).unwrap(), + )) + }; + None + }, + _ => None, + }) + .collect(); + if let Some(header) = block_header.iter().find(|b| b.is_some()) { + return Ok((signatures, header.clone().unwrap())); + } + } + Err("Couldnt find blocks".to_string()) +} + +pub async fn get_header_from_height( + rpc: RpcClient, + program_id: Pubkey, + height: u64, +) -> Option { + log::info!("Getting header for height {}", height); + let mut before_hash = None; + let mut block_header = None; + while block_header.is_none() { + let (transactions, last_searched_hash) = + get_previous_transactions(&rpc, program_id, before_hash, SearchIn::GuestChain).await; + if transactions.is_empty() { + break; + } + before_hash = Some( + anchor_client::solana_sdk::signature::Signature::from_str(&last_searched_hash).unwrap(), + ); + log::info!("THis is before hash {:?} {:?}", before_hash, last_searched_hash); + for tx in transactions { + let logs = match tx.result.transaction.meta.clone().unwrap().log_messages { + solana_transaction_status::option_serializer::OptionSerializer::Some(e) => e, + _ => Vec::new(), + }; + let (events, _proof_height) = get_events_from_logs(logs); + // Find block signed events with blockhash + block_header = events.iter().find_map(|event| match event { + solana_ibc::events::Event::NewBlock(e) => { + println!( + "This is new block event when fetching for height {:?}", + e.block_header.0.block_height + ); + let block_height = u64::from(e.block_header.0.block_height); + if block_height == height { + println!("New block event where it is true for height {:?}", height); + return Some(e.block_header.0.clone()); + } + None + }, + _ => None, + }); + if block_header.is_some() { + return block_header; + } + } + } + block_header +} + +pub async fn get_signatures_upto_height( + rpc: RpcClient, + program_id: Pubkey, + upto_height: u64, +) -> ( + Vec<( + Vec<(Pubkey, Signature)>, + BlockHeader, + Option>, + )>, + Vec, +) { + let mut current_height = upto_height; + let mut before_hash = None; + let mut all_signatures = Vec::new(); + let mut all_block_headers = Vec::new(); + let mut all_ibc_events = Vec::new(); + // let mut finalized_heights = Vec::new(); + log::info!("This is upto height {:?}", upto_height); + while current_height >= upto_height { + let (transactions, last_searched_hash) = + get_previous_transactions(&rpc, program_id, before_hash, SearchIn::GuestChain).await; + if transactions.is_empty() { + break; + } + before_hash = Some( + anchor_client::solana_sdk::signature::Signature::from_str(&last_searched_hash).unwrap(), + ); + for tx in transactions { + let transaction_err = tx.result.transaction.meta.clone().unwrap().err; + if transaction_err.is_some() { + // match tx.result.transaction.transaction { + // solana_transaction_status::EncodedTransaction::Json(e) => { + // println!("Error in transaction {:?}", e.signatures); + // }, + // _ => panic!("WTF") + // } + continue; + } + let logs = match tx.result.transaction.meta.clone().unwrap().log_messages { + solana_transaction_status::option_serializer::OptionSerializer::Some(e) => e, + _ => Vec::new(), + }; + let (events, _proof_height) = get_events_from_logs(logs.clone()); + let (ibc_events, _) = get_ibc_events_from_logs(logs); + all_ibc_events.extend(ibc_events); + for event in events { + match event { + solana_ibc::events::Event::NewBlock(e) => { + println!( + "This is new block event when fetching for height {:?}", + e.block_header.0.block_height + ); + let block_height = u64::from(e.block_header.0.block_height); + current_height = block_height; + if block_height >= upto_height { + all_block_headers.push((e.block_header.0.clone(), e.epoch)); + } else { + log::info!("breaking out of upto height"); + } + }, + solana_ibc::events::Event::BlockSigned(e) => { + all_signatures.push(e); + }, + // solana_ibc::events::Event::BlockFinalised(e) => { + // let block_height = u64::from(e.block_height); + // finalized_heights.push(block_height); + // }, + _ => (), + } + } + if current_height < upto_height { + break; + } + } + } + ( + all_block_headers + .iter() + .filter_map(|(b, epoch)| { + let signatures_for_header: Vec<_> = all_signatures + .iter() + .filter_map(|s| { + if s.block_height == b.block_height { + Some(( + Pubkey::new_from_array(s.pubkey.clone().into()), + Signature::from_bytes(&s.signature.to_vec()).unwrap(), + )) + } else { + None + } + }) + .collect(); + if signatures_for_header.is_empty() { + return None; + } + let epoch = epoch.as_ref().and_then(|e| Some(e.0.clone())); + Some((signatures_for_header, b.clone(), epoch)) + }) + .collect(), + all_ibc_events, + ) +} + +pub async fn get_events_upto_height( + rpc: RpcClient, + program_id: Pubkey, + upto_height: u64, +) -> Vec { + let mut current_height = rpc.get_slot().await.unwrap(); + let mut before_hash = None; + let mut all_ibc_events = vec![]; + while current_height >= upto_height { + let (transactions, last_searched_hash) = + get_previous_transactions(&rpc, program_id, before_hash, SearchIn::GuestChain).await; + if transactions.is_empty() { + break; + } + before_hash = Some( + anchor_client::solana_sdk::signature::Signature::from_str(&last_searched_hash).unwrap(), + ); + for tx in transactions { + current_height = tx.result.slot; + if current_height < upto_height { + break; + } + let transaction_err = tx.result.transaction.meta.clone().unwrap().err; + if transaction_err.is_some() { + continue; + } + let logs = match tx.result.transaction.meta.clone().unwrap().log_messages { + solana_transaction_status::option_serializer::OptionSerializer::Some(e) => e, + _ => Vec::new(), + }; + let (ibc_events, _) = get_ibc_events_from_logs(logs); + all_ibc_events.extend(ibc_events); + log::info!("Current height {} and upto height {}", current_height, upto_height); + } + } + all_ibc_events +} + +pub async fn get_previous_transactions( + rpc: &RpcClient, + program_id: Pubkey, + before_hash: Option, + search_in: SearchIn, +) -> (Vec, String) { + let search_address = match search_in { + SearchIn::IBC => { + let storage_seeds = &[solana_ibc::SOLANA_IBC_STORAGE_SEED]; + Pubkey::find_program_address(storage_seeds, &program_id).0 + }, + SearchIn::GuestChain => program_id, + }; + let transaction_signatures = rpc + .get_signatures_for_address_with_config( + &search_address, // Since ibc storage is only used for ibc and not for guest chain + GetConfirmedSignaturesForAddress2Config { + limit: Some(200), + before: before_hash, + commitment: Some(CommitmentConfig::confirmed()), + ..Default::default() + }, + ) + .await + .unwrap(); + if transaction_signatures.is_empty() { + return (vec![], before_hash.map_or("".to_string(), |sig| sig.to_string())); + } + let last_searched_hash = transaction_signatures + .last() + .map_or("".to_string(), |sig| sig.signature.clone()); + let mut body = vec![]; + for sig in transaction_signatures { + let signature = sig.signature.clone(); + let payload = Payload:: { + jsonrpc: "2.0".to_string(), + id: 1 as u64, + method: "getTransaction".to_string(), + params: ( + signature, + Param { commitment: "confirmed".to_string(), maxSupportedTransactionVersion: 0 }, + ), + }; + body.push(payload); + } + let url = rpc.url(); + tokio::task::spawn_blocking(move || { + for _ in 0..5 { + let response = reqwest::blocking::Client::new().post(url.clone()).json(&body).send(); + let response = skip_fail!(response); + let response: std::result::Result, reqwest::Error> = response.json(); + let transactions = skip_fail!(response); + return (transactions, last_searched_hash); + } + log::error!("Couldnt get transactions after 5 retries"); + (vec![], "".to_string()) + }) + .await + .unwrap() +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Payload { + pub jsonrpc: String, + pub id: u64, + pub method: String, + pub params: (String, T), +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PayloadWithoutParams { + pub jsonrpc: String, + pub id: u64, + pub method: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PayloadWithSingleParam { + pub jsonrpc: String, + pub id: u64, + pub method: String, + pub params: T, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Param { + commitment: String, + maxSupportedTransactionVersion: u16, +} + +pub enum SearchIn { + IBC, + GuestChain, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Response { + pub jsonrpc: String, + pub id: u64, + pub result: EncodedConfirmedTransactionWithStatusMeta, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct TrieResponse { + pub jsonrpc: String, + pub result: (u64, SlotData), + pub id: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SingleTrieResponse { + pub jsonrpc: String, + pub result: SlotData, + pub id: u64, +} + +/// Data provided by the Witnessed Trie Geyser plugin. +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct SlotData { + /// Proof of the accounts delta hash. + pub delta_hash_proof: cf_solana_og::proof::DeltaHashProof, + /// Trie root account. + pub root_account: cf_solana_og::proof::AccountHashData, + /// Proof of the witness trie. + pub witness_proof: cf_solana_og::proof::AccountProof, +} + +#[test] +pub fn testing_signatures() { + println!("I am testing signatures"); + let rpc = RpcClient::new( + "https://mainnet.helius-rpc.com/?api-key=65520d87-04b2-43a5-b5d5-35d5db0601b3".to_string(), + ); + let program_id = Pubkey::from_str("2HLLVco5HvwWriNbUhmVwA2pCetRkpgrqwnjcsZdyTKT").unwrap(); + let upto_height = 116806; + println!("I am testing signatures"); + let signatures = + Runtime::new() + .unwrap() + .block_on(get_signatures_upto_height(rpc, program_id, upto_height)); + signatures.0.iter().for_each(|sig| { + println!("Height {}", sig.1.block_height); + }) +} + +#[tokio::test] +pub async fn testing_events_final() { + let rpc = RpcClient::new("http://127.0.0.1:8899".to_string()); + let mut last_hash = None; + loop { + let (events, prev) = get_previous_transactions( + &rpc, + Pubkey::from_str("9FeHRJLHJSEw4dYZrABHWTRKruFjxDmkLtPmhM5WFYL7").unwrap(), + last_hash, + SearchIn::IBC, + ) + .await; + if events.is_empty() { + println!("No events found"); + break; + } + println!("Received events {}", events.len()); + last_hash = Some(anchor_client::solana_sdk::signature::Signature::from_str(&prev).unwrap()); + } +} + +#[test] +pub fn test_slot_data() { + let url = "http://127.0.0.1:42069".to_string(); + let body = crate::events::PayloadWithSingleParam::> { + jsonrpc: "2.0".to_string(), + id: 10, + method: "getSlotData".to_string(), + params: vec![663], + }; + let response = reqwest::blocking::Client::new().post(url.clone()).json(&body).send().unwrap(); + let response: SingleTrieResponse = response.json().unwrap(); + println!("response {:?}", response.result); +} + +#[test] +pub fn testing_events() { + let events = vec![ + "Program data: ABUC".to_string(), + "Program data: AA+kAAAAeyJhbW91bnQiOiIxNjAwMDA4Mzk5NDAxNjg3NjgwIiwiZGVub20iOiJwcGljYSIsInJlY2VpdmVyIjoib3h5ekVzVWo5Q1Y2SHNxUENVWnFWd3JGSkp2cGQ5aUNCclBkelRCV0xCYiIsInNlbmRlciI6ImNlbnRhdXJpMWYwcm1kZnVmM2s4c3FubXJmYTBwbHQzeGdtM2xmeDR2cXU0dHA2In0BAQAAAAAAAAAGCAAAAAAAAAAaVjgvdcMXAQAAAAAAAAAIAAAAdHJhbnNmZXIKAAAAY2hhbm5lbC0xNQgAAAB0cmFuc2ZlcgkAAABjaGFubmVsLTABDAAAAGNvbm5lY3Rpb24tMA==".to_string(), + "Program data: ABUC".to_string(), + "Program data: ABCkAAAAeyJhbW91bnQiOiIxNjAwMDA4Mzk5NDAxNjg3NjgwIiwiZGVub20iOiJwcGljYSIsInJlY2VpdmVyIjoib3h5ekVzVWo5Q1Y2SHNxUENVWnFWd3JGSkp2cGQ5aUNCclBkelRCV0xCYiIsInNlbmRlciI6ImNlbnRhdXJpMWYwcm1kZnVmM2s4c3FubXJmYTBwbHQzeGdtM2xmeDR2cXU0dHA2In0BAQAAAAAAAAAGCAAAAAAAAAAaVjgvdcMXAQAAAAAAAAAIAAAAdHJhbnNmZXIKAAAAY2hhbm5lbC0xNQgAAAB0cmFuc2ZlcgkAAABjaGFubmVsLTARAAAAeyJyZXN1bHQiOiJBUT09In0MAAAAY29ubmVjdGlvbi0w".to_string(), + "Program data: ABQSAAAAZGVub21pbmF0aW9uX3RyYWNlAQAAAAUAAABkZW5vbRgAAAB0cmFuc2Zlci9jaGFubmVsLTAvcHBpY2E=".to_string(), + "Program data: ABQVAAAAZnVuZ2libGVfdG9rZW5fcGFja2V0BwAAAAYAAABtb2R1bGUIAAAAdHJhbnNmZXIGAAAAc2VuZGVyLwAAAGNlbnRhdXJpMWYwcm1kZnVmM2s4c3FubXJmYTBwbHQzeGdtM2xmeDR2cXU0dHA2CAAAAHJlY2VpdmVyKwAAAG94eXpFc1VqOUNWNkhzcVBDVVpxVndyRkpKdnBkOWlDQnJQZHpUQldMQmIFAAAAZGVub20FAAAAcHBpY2EGAAAAYW1vdW50EwAAADE2MDAwMDgzOTk0MDE2ODc2ODAEAAAAbWVtbwAAAAAHAAAAc3VjY2VzcwQAAAB0cnVl".to_string(), + ]; + let (eves, height) = get_events_from_logs(events); + eves.iter().for_each(|event| println!("{:?}", event)); + let seqs = vec![1]; + let port_id = PortId::transfer(); + let channel_id = ChannelId::new(15); + let recv_packet_events: Vec<_> = eves + .iter() + .filter_map(|tx| match tx { + solana_ibc::events::Event::IbcEvent(e) => match e { + ibc_core_handler_types::events::IbcEvent::WriteAcknowledgement(packet) => { + if packet.chan_id_on_a().as_str() == &channel_id.to_string() + && packet.port_id_on_a().as_str() == port_id.as_str() + && seqs.iter().find(|&&seq| packet.seq_on_a().value() == seq).is_some() + { + println!("We found packet"); + Some(packet) + } else { + None + } + }, + _ => None, + }, + _ => None, + }) + .collect(); + // let client_state_logs: Vec<&str> = events + // .iter() + // .filter_map(|log| { + // if log.starts_with("Program logged: This is updated client state ") { + // Some(log.strip_prefix("Program logged: This is updated client state ").unwrap()) + // } else { + // None + // } + // }) + // .collect(); + // // There can be only one client state event in a tx + // let client_state_log = client_state_logs[0]; + // // Remove the square brackets and whitespace, then split the string into an iterator of &str, + // // each representing a byte. Then parse each &str to a u8 and collect into a Vec + // let bytes: Vec = client_state_log + // .trim_matches(|c: char| c == '[' || c == ']') // Trim the square brackets + // .split(", ") // Split the string into individual numbers + // .map(|s| s.parse::().unwrap()) // Convert each number from &str to u8 + // .collect(); // Collect into a Vec + // let any_client_state: solana_ibc::client_state::AnyClientState = + // borsh::BorshDeserialize::try_from_slice(bytes.as_slice()).unwrap(); + // println!("This is any client state {:?}", any_client_state); +} diff --git a/hyperspace/rollup/src/lib.rs b/hyperspace/rollup/src/lib.rs new file mode 100644 index 000000000..b25dd2523 --- /dev/null +++ b/hyperspace/rollup/src/lib.rs @@ -0,0 +1,2402 @@ +// #![feature(more_qualified_paths)] +extern crate alloc; + +use anchor_client::{ + solana_client::{ + nonblocking::pubsub_client::PubsubClient, rpc_client::RpcClient, + rpc_config::RpcSendTransactionConfig, + }, + solana_sdk::{ + compute_budget::ComputeBudgetInstruction, + instruction::Instruction, + system_instruction::transfer, + transaction::{Transaction, VersionedTransaction}, + }, +}; +use anchor_spl::associated_token::get_associated_token_address; +use client::FinalityEvent; +use client_state::convert_new_client_state_to_old; +use consensus_state::convert_new_consensus_state_to_old; +use core::{pin::Pin, str::FromStr, time::Duration}; +use futures::{future::join_all, StreamExt}; +use guestchain::{BlockHeader, Epoch, PubKey, Validator}; +use ibc_core_channel_types::msgs::PacketMsg; +use ibc_core_client_types::msgs::ClientMsg; +use ibc_core_commitment_types::commitment::CommitmentRoot; +use ibc_core_handler_types::msgs::MsgEnvelope; +use ics07_tendermint::{ + client_message::ClientMessage, client_state::ClientState as TmClientState, + consensus_state::ConsensusState as TmConsensusState, +}; +use msgs::convert_old_msgs_to_new; +use serde::{Deserialize, Serialize}; +use solana_transaction_status::UiTransactionEncoding; +use std::{ + num::{NonZeroU128, NonZeroU64}, + ops::Deref, + thread::sleep, + time::Instant, +}; +use tendermint::{Hash, Time}; +use tendermint_proto::Protobuf; +use tokio::{ + sync::mpsc::unbounded_channel, + task::{spawn_blocking, JoinSet}, +}; + +use anchor_client::{ + solana_client::{ + // pubsub_client::PubsubClient, + rpc_client::GetConfirmedSignaturesForAddress2Config, + rpc_config::{ + RpcBlockSubscribeConfig, RpcBlockSubscribeFilter, RpcTransactionLogsConfig, + RpcTransactionLogsFilter, + }, + }, + solana_sdk::{ + commitment_config::CommitmentConfig, signature::Signature, signer::Signer as AnchorSigner, + }, +}; +use anchor_lang::prelude::*; +use error::Error; +use ibc::{ + applications::transfer::{Amount, BaseDenom, PrefixedCoin, PrefixedDenom, TracePath}, + core::{ + ics02_client::{ + events::{NewBlock, UpdateClient}, + msgs::update_client::MsgUpdateAnyClient, + trust_threshold::TrustThreshold, + }, + ics23_commitment::specs::ProofSpecs, + ics24_host::identifier::{ChainId, ChannelId, ClientId, ConnectionId, PortId}, + ics26_routing::msgs::Ics26Envelope, + }, + events::IbcEvent, + timestamp::Timestamp, + tx_msg::Msg, + Height, +}; +use ibc_proto::{ + google::protobuf::Any, + ibc::core::{ + channel::v1::{ + Channel, Counterparty as ChanCounterparty, IdentifiedChannel, QueryChannelResponse, + QueryNextSequenceReceiveResponse, QueryPacketAcknowledgementResponse, + QueryPacketCommitmentResponse, QueryPacketReceiptResponse, + }, + client::v1::{QueryClientStateResponse, QueryConsensusStateResponse}, + connection::v1::{ + ConnectionEnd, Counterparty as ConnCounterparty, IdentifiedConnection, + QueryConnectionResponse, Version, + }, + }, +}; +use jito_protos::{ + convert::versioned_tx_from_packet, + searcher::{ + mempool_subscription, searcher_service_client::SearcherServiceClient, + ConnectedLeadersRegionedRequest, GetTipAccountsRequest, MempoolSubscription, + NextScheduledLeaderRequest, PendingTxNotification, ProgramSubscriptionV0, + SubscribeBundleResultsRequest, WriteLockedAccountSubscriptionV0, + }, +}; +use lib::hash::CryptoHash; +use pallet_ibc::light_clients::{AnyClientMessage, AnyClientState, AnyConsensusState}; +use primitives::{ + mock::LocalClientTypes, Chain, CommonClientState, IbcProvider, KeyProvider, LightClientSync, + MisbehaviourHandler, UndeliveredType, UpdateType, +}; +use solana_ibc::events::Event; +use std::{result::Result, sync::Arc}; +use tokio_stream::Stream; + +use solana_ibc::storage::{SequenceKind, Serialised}; + +use trie_ids::{ClientIdx, ConnectionIdx, PortChannelPK, Tag, TrieKey}; + +use crate::{ + client::TransactionSender, + events::{get_events_from_logs, SearchIn, TrieResponse}, +}; +pub use crate::{ + client::{DeliverIxType, RollupClient, RollupClientConfig}, + events::convert_new_event_to_old, +}; + +pub mod client; +mod client_state; +mod consensus_state; +mod error; +mod events; +// mod jito; +mod msgs; +#[cfg(feature = "testing")] +mod test_provider; +mod utils; + +const solana_ibc_STORAGE_SEED: &[u8] = b"private"; +const TRIE_SEED: &[u8] = b"trie"; +const CHAIN_SEED: &[u8] = b"chain"; +pub const NUMBER_OF_BLOCKS_TO_PROCESS_PER_ITER: u64 = 250; +pub const WRITE_ACCOUNT_SEED: &[u8] = b"write"; +pub const SIGNATURE_ACCOUNT_SEED: &[u8] = b"signature"; + +pub const BLOCK_ENGINE_URL: &str = "https://mainnet.block-engine.jito.wtf"; + +pub const MIN_TIME_UNTIL_UPDATE: u64 = 30 * 60; // 30 mins + +pub struct InnerAny { + pub type_url: String, + pub value: Vec, +} + +#[async_trait::async_trait] +impl IbcProvider for RollupClient { + type FinalityEvent = FinalityEvent; + + type TransactionId = String; + + type AssetId = String; + + type Error = Error; + + async fn query_latest_ibc_events( + &mut self, + finality_event: Self::FinalityEvent, + counterparty: &T, + ) -> Result, primitives::UpdateType)>, anyhow::Error> + where + T: Chain, + { + log::info!("Came into rollup lts events"); + let (finality_blockhash, finality_height) = match finality_event { + FinalityEvent::Guest { blockhash, block_height } => (blockhash, block_height), + FinalityEvent::Rollup { slot, previous_blockhash, blockhash } => { + (CryptoHash::default(), slot) + }, + }; + log::info!("This is rollup height {:?}", finality_height); + let client_id = self.client_id(); + let latest_cp_height = counterparty.latest_height_and_timestamp().await?.0; + log::info!("this is the latest cp height {:?}", latest_cp_height); + let latest_cp_client_state = + counterparty.query_client_state(latest_cp_height, client_id.clone()).await?; + let client_state_response = latest_cp_client_state + .client_state + .ok_or_else(|| Error::Custom("counterparty returned empty client state".to_string()))?; + log::info!("This is the type url in rollup {:?}", client_state_response.type_url); + let AnyClientState::Rollup(client_state) = + AnyClientState::decode_recursive(client_state_response.clone(), |c| { + matches!(c, AnyClientState::Rollup(_)) + }) + .or_else(|| { + log::info!("This is wasm {:?}", client_state_response); + let wasm_client_state = + AnyClientState::decode_recursive(client_state_response, |c| { + matches!(c, AnyClientState::Rollup(_)) + }) + .unwrap(); + Some(wasm_client_state.unpack_recursive().clone()) + }) + .unwrap() + else { + unreachable!() + }; + log::info!("This is client state {:?}", client_state); + let latest_cp_client_height = u64::from(client_state.0.latest_slot); + println!("This is counterparty client height {:?}", latest_cp_client_height); + let new_block_events = events::get_events_upto_height( + self.rpc_client(), + self.solana_ibc_program_id, + latest_cp_client_height + 1, + ) + .await; + + log::info!("This is all events {:?}", new_block_events); + let block_events: Vec = new_block_events + .iter() + .filter_map(|event| { + convert_new_event_to_old(event.clone(), Height::new(1, u64::from(finality_height))) + }) + .collect(); + + let body = events::PayloadWithoutParams { + jsonrpc: "2.0".to_string(), + id: 10, + method: "getLatestSlotData".to_string(), + }; + let url = self.trie_rpc_url.clone(); + let response = tokio::task::spawn_blocking(move || { + for _ in 0..5 { + let response = + reqwest::blocking::Client::new().post(url.clone()).json(&body).send(); + let response = skip_fail!(response); + let response: std::result::Result = + response.json(); + let transactions = skip_fail!(response); + return transactions; + } + log::error!("Couldnt get transactions after 5 retries"); + panic!("WTF"); + }) + .await + .unwrap(); + let slot_data = response.result.1; + let bank_hash = slot_data.delta_hash_proof.calculate_bank_hash(); + let header = cf_solana_og::Header { + slot: NonZeroU64::new(response.result.0).unwrap(), + bank_hash, + delta_hash_proof: slot_data.delta_hash_proof, + witness_proof: slot_data.witness_proof, + }; + let msg = MsgUpdateAnyClient:: { + client_id: self.client_id(), + client_message: AnyClientMessage::Rollup(cf_solana::ClientMessage( + cf_solana_og::ClientMessage::from(header), + )), + signer: counterparty.account_id(), + }; + + let has_init_event = block_events + .iter() + .find(|eve| matches!(eve, IbcEvent::OpenConfirmConnection(_))); + + let update_type = + if (block_events.len() > 0) { UpdateType::Mandatory } else { UpdateType::Optional }; + + let update = (msg.to_any(), Height::new(1, response.result.0), block_events, update_type); + + Ok(vec![update]) + } + + async fn ibc_events(&self) -> Pin + Send + 'static>> { + let (tx, rx) = unbounded_channel(); + let ws_url = self.ws_url.clone(); + let program_id = self.solana_ibc_program_id; + tokio::spawn(async move { + let client = PubsubClient::new(&ws_url).await.unwrap(); + let (mut stream, receiver) = client + .logs_subscribe( + // &ws_url, + RpcTransactionLogsFilter::Mentions(vec![program_id.to_string()]), + RpcTransactionLogsConfig { commitment: Some(CommitmentConfig::finalized()) }, + ) + .await + .unwrap(); + + loop { + match stream.next().await { + Some(logs) => { + let (events, proof_height) = + events::get_ibc_events_from_logs(logs.value.logs); + // log::info!("These are events {:?} ", events); + // log::info!("Total {:?} events", events.len()); + let mut broke = false; + events.iter().for_each(|event| { + log::info!("Came into ibc events rollup"); + let height = Height::new(1, proof_height); + let converted_event = + events::convert_new_event_to_old(event.clone(), height); + if let Some(event) = converted_event { + log::info!("Sending message"); + tx.send(event.clone()).unwrap_or_else(|_| { + log::info!("Broke"); + broke = true; + }); + } + }); + if broke { + break; + } + }, + None => { + panic!("{}", format!("Disconnected: ")); + }, + } + } + }); + let streams = tokio_stream::wrappers::UnboundedReceiverStream::new(rx); + Box::pin(streams) + } + + async fn query_client_consensus( + &self, + at: Height, + client_id: ClientId, + consensus_height: Height, + ) -> Result { + use ibc_proto_new::Protobuf; + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let storage = self.get_ibc_storage().await; + let revision_height = consensus_height.revision_height; + let revision_number = consensus_height.revision_number; + let new_client_id = + ibc_core_host_types::identifiers::ClientId::from_str(client_id.as_str()).unwrap(); + log::info!("query_client_consensus before trie key"); + let consensus_state_trie_key = TrieKey::for_consensus_state( + ClientIdx::try_from(new_client_id).unwrap(), + ibc_core_client_types::Height::new( + consensus_height.revision_number, + consensus_height.revision_height, + ) + .unwrap(), + ); + log::info!("query_client_consensus before prove trie"); + let (val, consensus_state_proof) = trie + .prove(&consensus_state_trie_key) + .map_err(|_| Error::Custom("value is sealed and cannot be fetched".to_owned()))?; + log::info!("query_client_consensus before search clients"); + let client_store = storage + .clients + .iter() + .find(|&client| client.client_id.as_str() == client_id.as_str()) + .ok_or( + "Client not found with the given client id while querying client consensus" + .to_owned(), + )?; + log::info!("query_client_consensus before get cs states"); + let serialized_consensus_state = client_store + .consensus_states + .get(&ibc_core_client_types::Height::new(revision_number, revision_height).unwrap()) + .ok_or(Error::Custom("No value at given key".to_owned()))?; + log::info!("query_client_consensus before convert cs states"); + let consensus_state = serialized_consensus_state + .state() + .map_err(|_| { + Error::Custom( + "Could not +deserialize consensus state" + .to_owned(), + ) + }) + .unwrap(); + log::info!("query_client_consensus before encode"); + let cs_state = convert_new_consensus_state_to_old(consensus_state.clone()); + let inner_any = consensus_state.clone().encode_vec(); + log::info!("this is consensus state {:?}", consensus_state); + log::info!("This is inner any consensus state {:?}", inner_any); + Ok(QueryConsensusStateResponse { + consensus_state: Some(cs_state.into()), + proof: borsh::to_vec(&consensus_state_proof).unwrap(), + proof_height: Some(at.into()), + }) + } + + async fn query_client_state( + &self, + at: Height, + client_id: ClientId, + ) -> Result { + log::info!("Quering solana client state at height {:?} {:?}", at, client_id); + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let storage = self.get_ibc_storage().await; + let new_client_id = + ibc_core_host_types::identifiers::ClientId::from_str(client_id.as_str()).unwrap(); + let client_state_trie_key = + TrieKey::for_client_state(ClientIdx::try_from(new_client_id).unwrap()); + let (val, client_state_proof) = trie + .prove(&client_state_trie_key) + .map_err(|_| Error::Custom("value is sealed and cannot be fetched".to_owned()))?; + let client_state = events::get_client_state_at_height( + self.rpc_client(), + self.solana_ibc_program_id, + at.revision_height + 1, + ) + .await + .unwrap_or_else(|| { + log::info!("Fetching latest client state"); + let client_store = storage + .clients + .iter() + .find(|&client| client.client_id.as_str() == client_id.as_str()) + .expect("Client not found with the given client id while querying client state"); + let serialized_client_state = &client_store.client_state; + serialized_client_state + .get() + .map_err(|_| { + Error::Custom( + "Could not +deserialize client state" + .to_owned(), + ) + }) + .unwrap() + }); + // let inner_any = client_state.clone().encode_vec(); + log::info!("this is client state {:?}", client_state); + // log::info!("This is inner any client state {:?}", inner_any); + let any_client_state = convert_new_client_state_to_old(client_state); + let chain_account = self.get_chain_storage().await; + Ok(QueryClientStateResponse { + client_state: Some(any_client_state.into()), + proof: borsh::to_vec(&client_state_proof).unwrap(), + proof_height: Some(at.into()), + }) + } + + async fn query_connection_end( + &self, + at: Height, + connection_id: ConnectionId, + ) -> Result { + use ibc_proto_new::Protobuf; + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let storage = self.get_ibc_storage().await; + let connection_idx = ConnectionIdx::try_from( + ibc_core_host_types::identifiers::ConnectionId::from_str(connection_id.as_str()) + .unwrap(), + ) + .unwrap(); + log::info!( + "This is connection ID {:?} and index {:?} while querying connection end", + connection_id, + connection_idx + ); + let connection_end_trie_key = TrieKey::for_connection(connection_idx); + log::info!("This is connection end trie key {:?}", connection_end_trie_key); + let (val, connection_end_proof) = trie + .prove(&connection_end_trie_key) + .map_err(|_| Error::Custom("value is sealed and cannot be fetched".to_owned()))?; + log::info!("This is serialized connection {:?}", storage.connections); + let serialized_connection_end = + storage.connections.get(usize::from(connection_idx)).ok_or( + "Connection not found with the given + client id" + .to_owned(), + )?; + let inner_connection_end = Serialised::get(serialized_connection_end) + .map_err(|_| { + Error::Custom( + "Could not + deserialize connection end" + .to_owned(), + ) + }) + .unwrap(); + log::info!("This is new connection end {:?}", inner_connection_end); + log::info!("Borsh serialized connection end {:?}", borsh::to_vec(&inner_connection_end)); + log::info!("This is in any {:?}", inner_connection_end.clone().encode_vec()); + log::info!("This is the hashed value {:?}", val); + let inner_any = inner_connection_end.clone().encode_vec(); + let inner_counterparty = inner_connection_end.counterparty(); + let connection_end = ConnectionEnd { + client_id: inner_connection_end.client_id().to_string(), + versions: inner_connection_end + .versions() + .to_vec() + .iter() + .map(|version| { + let raw_version = + ibc_proto_new::ibc::core::connection::v1::Version::from(version.clone()); + Version { identifier: raw_version.identifier, features: raw_version.features } + }) + .collect(), + state: inner_connection_end.state.into(), + counterparty: Some(ConnCounterparty { + client_id: inner_counterparty.client_id().to_string(), + connection_id: inner_counterparty + .connection_id + .as_ref() + .map_or_else(|| "".to_string(), |v| v.as_str().to_string()), + prefix: Some(ibc_proto::ibc::core::commitment::v1::MerklePrefix { + key_prefix: inner_counterparty.prefix().clone().into_vec(), + }), + }), + delay_period: inner_connection_end.delay_period().as_nanos() as u64, + }; + Ok(QueryConnectionResponse { + connection: Some(connection_end), + proof: borsh::to_vec(&connection_end_proof).unwrap(), + proof_height: Some(at.into()), + }) + } + + async fn query_channel_end( + &self, + at: Height, + channel_id: ibc::core::ics24_host::identifier::ChannelId, + port_id: ibc::core::ics24_host::identifier::PortId, + ) -> Result { + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let storage = self.get_ibc_storage().await; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_id.as_str()).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()); + let channel_end_path = + PortChannelPK::try_from(new_port_id.clone(), new_channel_id.clone()).unwrap(); + let channel_end_trie_key = TrieKey::for_channel_end(&channel_end_path); + let (_, channel_end_proof) = trie + .prove(&channel_end_trie_key) + .map_err(|_| Error::Custom("value is sealed and cannot be fetched".to_owned()))?; + log::info!("This is channel end path {:?}", channel_end_path); + let inner_channel_end = storage + .port_channel + .deref() + .get(&channel_end_path) + .ok_or(Error::Custom("No value at given key".to_owned()))? + .channel_end() + .unwrap() + .ok_or(Error::Custom("Channel end not found".to_owned()))?; + let inner_counterparty = inner_channel_end.counterparty(); + let state = match inner_channel_end.state { + ibc_core_channel_types::channel::State::Uninitialized => 0, + ibc_core_channel_types::channel::State::Init => 1, + ibc_core_channel_types::channel::State::TryOpen => 2, + ibc_core_channel_types::channel::State::Open => 3, + ibc_core_channel_types::channel::State::Closed => 4, + }; + let ordering = match inner_channel_end.ordering { + ibc_core_channel_types::channel::Order::None => 0, + ibc_core_channel_types::channel::Order::Unordered => 1, + ibc_core_channel_types::channel::Order::Ordered => 2, + }; + let channel_end = Channel { + state, + ordering, + counterparty: Some(ChanCounterparty { + port_id: inner_counterparty.port_id.to_string(), + channel_id: if inner_counterparty.channel_id.is_none() { + "".to_owned() + } else { + inner_counterparty.channel_id.clone().unwrap().to_string() + }, + }), + connection_hops: inner_channel_end + .connection_hops + .iter() + .map(|connection_id| connection_id.to_string()) + .collect(), + version: inner_channel_end.version.to_string(), + }; + Ok(QueryChannelResponse { + channel: Some(channel_end), + proof: borsh::to_vec(&channel_end_proof).unwrap(), + proof_height: Some(at.into()), + }) + } + + async fn query_proof(&self, at: Height, keys: Vec>) -> Result, Self::Error> { + log::info!("Querying proof at {:?}", at.revision_height); + log::info!("This is the bytes for keys {:?} ", keys); + let key_str = String::from_utf8(keys[0].clone()) + .map_err(|_| Error::Custom("Invalid key".to_owned()))?; + log::info!("This is the keys in string{:?}", key_str); + let split_keys = key_str.split("/").collect::>(); + let trie_key = match split_keys[0] { + "nextSequenceRecv" => { + let port_str = split_keys[2]; + let channel_str = split_keys[4]; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_str).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::from_str(channel_str).unwrap(); + let next_seq_recv_path = + PortChannelPK::try_from(new_port_id.clone(), new_channel_id.clone()).unwrap(); + TrieKey::for_next_sequence(&next_seq_recv_path) + }, + "receipts" => { + let port_str = split_keys[2]; + let channel_str = split_keys[4]; + let sequence_str = split_keys[6]; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_str).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::from_str(channel_str).unwrap(); + let new_seq = + ibc_core_host_types::identifiers::Sequence::from_str(sequence_str).unwrap(); + let packet_receipt_path = ibc_core_host_types::path::ReceiptPath { + port_id: new_port_id, + channel_id: new_channel_id, + sequence: new_seq, + }; + TrieKey::try_from(&packet_receipt_path).unwrap() + }, + "commitments" => { + log::info!("Entered commitments"); + let port_str = split_keys[2]; + let channel_str = split_keys[4]; + let sequence_str = split_keys[6]; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_str).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::from_str(channel_str).unwrap(); + let new_seq = + ibc_core_host_types::identifiers::Sequence::from_str(sequence_str).unwrap(); + let packet_commitment_path = ibc_core_host_types::path::CommitmentPath { + port_id: new_port_id, + channel_id: new_channel_id, + sequence: new_seq, + }; + TrieKey::try_from(&packet_commitment_path).unwrap() + }, + "acks" => { + let port_str = split_keys[2]; + let channel_str = split_keys[4]; + let sequence_str = split_keys[6]; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_str).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::from_str(channel_str).unwrap(); + let new_seq = + ibc_core_host_types::identifiers::Sequence::from_str(sequence_str).unwrap(); + let packet_ack_path = ibc_core_host_types::path::AckPath { + port_id: new_port_id, + channel_id: new_channel_id, + sequence: new_seq, + }; + TrieKey::try_from(&packet_ack_path).unwrap() + }, + "channelEnds" => { + let port_str = split_keys[2]; + let channel_str = split_keys[4]; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_str).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::from_str(channel_str).unwrap(); + let channel_end_path = + PortChannelPK::try_from(new_port_id.clone(), new_channel_id.clone()).unwrap(); + TrieKey::for_channel_end(&channel_end_path) + }, + _ => { + log::error!("invalid key in proof query proof"); + return Err(Error::Custom("Invalid key".to_owned())); + }, + }; + let (trie, at_height) = self.get_trie(at.revision_height, true).await; + let (val, proof) = trie + .prove(&trie_key) + .map_err(|_| Error::Custom("value is sealed and cannot be fetched".to_owned()))?; + Ok(borsh::to_vec(&proof).unwrap()) + } + + async fn query_packet_commitment( + &self, + at: Height, + port_id: &ibc::core::ics24_host::identifier::PortId, + channel_id: &ibc::core::ics24_host::identifier::ChannelId, + seq: u64, + ) -> Result { + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_id.as_str()).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()); + let new_seq = ibc_core_host_types::identifiers::Sequence::from(seq); + let packet_commitment_path = ibc_core_host_types::path::CommitmentPath { + port_id: new_port_id, + channel_id: new_channel_id, + sequence: new_seq, + }; + let packet_commitment_trie_key = TrieKey::try_from(&packet_commitment_path).unwrap(); + let (packet_commitment, packet_commitment_proof) = trie + .prove(&packet_commitment_trie_key) + .map_err(|_| Error::Custom("value is sealed and cannot be fetched".to_owned()))?; + let commitment = + packet_commitment.ok_or(Error::Custom("No value at given key".to_owned()))?; + log::info!("This is packet commitment {:?}", commitment.0.to_vec()); + Ok(QueryPacketCommitmentResponse { + commitment: commitment.0.to_vec(), + proof: borsh::to_vec(&packet_commitment_proof).unwrap(), + proof_height: Some(at.into()), + }) + } + + async fn query_packet_acknowledgement( + &self, + at: Height, + port_id: &ibc::core::ics24_host::identifier::PortId, + channel_id: &ibc::core::ics24_host::identifier::ChannelId, + seq: u64, + ) -> Result { + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_id.as_str()).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()); + let new_seq = ibc_core_host_types::identifiers::Sequence::from(seq); + let packet_ack_path = ibc_core_host_types::path::AckPath { + port_id: new_port_id, + channel_id: new_channel_id, + sequence: new_seq, + }; + let packet_ack_trie_key = TrieKey::try_from(&packet_ack_path).unwrap(); + let (packet_ack, packet_ack_proof) = trie + .prove(&packet_ack_trie_key) + .map_err(|_| Error::Custom("value is sealed and cannot be fetched".to_owned()))?; + let ack = packet_ack.ok_or(Error::Custom("No value at given key".to_owned()))?; + let (packet_ack, packet_ack_proof) = trie + .prove(&packet_ack_trie_key) + .map_err(|_| Error::Custom("value is sealed and cannot be fetched".to_owned()))?; + let ack = packet_ack.ok_or(Error::Custom("No value at given key".to_owned()))?; + log::info!("This is packet ack {:?}", ack.0.to_vec()); + Ok(QueryPacketAcknowledgementResponse { + acknowledgement: ack.0.to_vec(), + proof: borsh::to_vec(&packet_ack_proof).unwrap(), + proof_height: Some(at.into()), + }) + } + + async fn query_next_sequence_recv( + &self, + at: Height, + port_id: &ibc::core::ics24_host::identifier::PortId, + channel_id: &ibc::core::ics24_host::identifier::ChannelId, + ) -> Result { + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let storage = self.get_ibc_storage().await; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_id.as_str()).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()); + let next_sequence_recv_path = PortChannelPK::try_from(new_port_id, new_channel_id).unwrap(); + let next_sequence_recv_trie_key = TrieKey::for_next_sequence(&next_sequence_recv_path); + let (_, next_sequence_recv_proof) = trie + .prove(&next_sequence_recv_trie_key) + .map_err(|_| Error::Custom("value is sealed and cannot be fetched".to_owned()))?; + let next_seq = &storage + .port_channel + .deref() + .get(&next_sequence_recv_path) + .ok_or(Error::Custom("No value at given key".to_owned()))? + .next_sequence; + let next_seq_recv = next_seq + .get(SequenceKind::Recv) + .ok_or(Error::Custom("No value set for the next sequence receive".to_owned()))?; + Ok(QueryNextSequenceReceiveResponse { + next_sequence_receive: next_seq_recv.into(), + proof: borsh::to_vec(&next_sequence_recv_proof).unwrap(), + proof_height: Some(at.into()), + }) + } + + async fn query_packet_receipt( + &self, + at: Height, + port_id: &ibc::core::ics24_host::identifier::PortId, + channel_id: &ibc::core::ics24_host::identifier::ChannelId, + seq: u64, + ) -> Result { + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_id.as_str()).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()); + let new_seq = ibc_core_host_types::identifiers::Sequence::from(seq); + let packet_recv_path = ibc_core_host_types::path::ReceiptPath { + port_id: new_port_id, + channel_id: new_channel_id, + sequence: new_seq, + }; + let packet_recv_trie_key = TrieKey::try_from(&packet_recv_path).unwrap(); + let (packet_recv, packet_recv_proof) = trie + .prove(&packet_recv_trie_key) + .map_err(|_| Error::Custom("value is sealed and cannot be fetched".to_owned()))?; + Ok(QueryPacketReceiptResponse { + received: packet_recv.is_some(), + proof: borsh::to_vec(&packet_recv_proof).unwrap(), + proof_height: Some(at.into()), + }) + } + + async fn latest_height_and_timestamp( + &self, + ) -> Result<(Height, ibc::timestamp::Timestamp), Self::Error> { + let rpc = self.rpc_client(); + let slot = rpc.get_slot().await.unwrap(); + let timestamp = rpc.get_block_time(slot).await.unwrap() as u64; + Ok((Height::new(1, slot), Timestamp::from_nanoseconds(timestamp * 10u64.pow(9)).unwrap())) + } + + async fn query_packet_commitments( + &self, + at: Height, + channel_id: ibc::core::ics24_host::identifier::ChannelId, + port_id: ibc::core::ics24_host::identifier::PortId, + ) -> Result, Self::Error> { + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_id.as_str()).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()); + let trie_comp = PortChannelPK::try_from(new_port_id, new_channel_id).unwrap(); + let key = TrieKey::new(Tag::Commitment, trie_comp); + let sequences: Vec = trie + .get_subtrie(&key) + .unwrap() + .iter() + .map(|c| { + u64::from_be_bytes( + Vec::::try_from(c.sub_key.clone()).unwrap().as_slice().try_into().unwrap(), + ) + }) + .collect(); + Ok(sequences) + } + + async fn query_packet_acknowledgements( + &self, + at: Height, + channel_id: ibc::core::ics24_host::identifier::ChannelId, + port_id: ibc::core::ics24_host::identifier::PortId, + ) -> Result, Self::Error> { + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_id.as_str()).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()); + let trie_comp = PortChannelPK::try_from(new_port_id, new_channel_id).unwrap(); + let key = TrieKey::new(Tag::Ack, trie_comp); + let sequences: Vec = trie + .get_subtrie(&key) + .unwrap() + .iter() + .map(|c| { + u64::from_be_bytes( + Vec::::try_from(c.sub_key.clone()).unwrap().as_slice().try_into().unwrap(), + ) + }) + .collect(); + Ok(sequences) + } + + async fn query_unreceived_packets( + &self, + at: Height, + channel_id: ibc::core::ics24_host::identifier::ChannelId, + port_id: ibc::core::ics24_host::identifier::PortId, + seqs: Vec, + ) -> Result, Self::Error> { + log::info!("----------Unreceived packets seqs on solana {:?} ", seqs); + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_id.as_str()).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()); + let trie_comp = PortChannelPK::try_from(new_port_id, new_channel_id).unwrap(); + let key = TrieKey::new(Tag::Receipt, trie_comp); + let packet_receipt_sequences: Vec = trie + .get_subtrie(&key) + .unwrap() + .iter() + .map(|c| { + u64::from_be_bytes( + Vec::::try_from(c.sub_key.clone()).unwrap().as_slice().try_into().unwrap(), + ) + }) + .collect(); + Ok(seqs + .iter() + .flat_map(|&seq| { + match packet_receipt_sequences.iter().find(|&&receipt_seq| receipt_seq == seq) { + Some(_) => None, + None => Some(seq), + } + }) + .collect()) + } + + async fn query_unreceived_acknowledgements( + &self, + at: Height, + channel_id: ibc::core::ics24_host::identifier::ChannelId, + port_id: ibc::core::ics24_host::identifier::PortId, + seqs: Vec, + ) -> Result, Self::Error> { + let (trie, at_height) = self.get_trie(at.revision_height, false).await; + let new_port_id = + ibc_core_host_types::identifiers::PortId::from_str(port_id.as_str()).unwrap(); + let new_channel_id = + ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()); + let trie_comp = PortChannelPK::try_from(new_port_id, new_channel_id).unwrap(); + let key = TrieKey::new(Tag::Commitment, trie_comp); + let packet_receipt_sequences: Vec = trie + .get_subtrie(&key) + .unwrap() + .iter() + .map(|c| { + u64::from_be_bytes( + Vec::::try_from(c.sub_key.clone()).unwrap().as_slice().try_into().unwrap(), + ) + }) + .collect(); + Ok(seqs + .iter() + .flat_map(|&seq| { + match packet_receipt_sequences.iter().find(|&&receipt_seq| receipt_seq == seq) { + Some(_) => Some(seq), + None => None, + } + }) + .collect()) + } + + fn channel_whitelist( + &self, + ) -> std::collections::HashSet<( + ibc::core::ics24_host::identifier::ChannelId, + ibc::core::ics24_host::identifier::PortId, + )> { + self.channel_whitelist.lock().unwrap().clone() + } + + /// We just return all the channels since there doesnt seem to be any kind of relation between + /// connection ID and channels. + async fn query_connection_channels( + &self, + at: Height, + _connection_id: &ConnectionId, + ) -> Result { + let storage = self.get_ibc_storage().await; + let channels: Vec = storage + .port_channel + .deref() + .into_iter() + .map(|(key, value)| { + let channel = value.channel_end().unwrap().unwrap(); + let state = match channel.state { + ibc_core_channel_types::channel::State::Uninitialized => 0, + ibc_core_channel_types::channel::State::Init => 1, + ibc_core_channel_types::channel::State::TryOpen => 2, + ibc_core_channel_types::channel::State::Open => 3, + ibc_core_channel_types::channel::State::Closed => 4, + }; + let ordering = match channel.ordering { + ibc_core_channel_types::channel::Order::None => 0, + ibc_core_channel_types::channel::Order::Unordered => 1, + ibc_core_channel_types::channel::Order::Ordered => 2, + }; + IdentifiedChannel { + state, + ordering, + counterparty: Some(ChanCounterparty { + port_id: channel.counterparty().port_id.to_string(), + channel_id: channel.counterparty().channel_id.clone().unwrap().to_string(), + }), + connection_hops: channel + .connection_hops + .iter() + .map(|connection_id| connection_id.to_string()) + .collect(), + version: channel.version.to_string(), + port_id: key.port_id().to_string(), + channel_id: key.channel_id().to_string(), + } + }) + .collect(); + Ok(ibc_proto::ibc::core::channel::v1::QueryChannelsResponse { + channels, + pagination: None, + height: Some(at.into()), + }) + } + + async fn query_send_packets( + &self, + channel_id: ibc::core::ics24_host::identifier::ChannelId, + port_id: ibc::core::ics24_host::identifier::PortId, + seqs: Vec, + ) -> Result, Self::Error> { + log::info!("Inside query send packets"); + let rpc_client = self.rpc_client(); + if seqs.is_empty() { + return Ok(Vec::new()); + } + let mut total_packets = Vec::new(); + let mut before_hash = None; + let maximum_sequence_number = seqs.iter().max().unwrap(); + let mut is_sequence_greater = false; + let mut is_maximum_seq_found = false; + while total_packets.len() < seqs.len() { + let (transactions, last_searched_hash) = events::get_previous_transactions( + &rpc_client, + self.solana_ibc_program_id, + before_hash, + SearchIn::IBC, + ) + .await; + before_hash = Some( + anchor_client::solana_sdk::signature::Signature::from_str(&last_searched_hash) + .unwrap(), + ); + let send_packet_events: Vec<_> = + transactions + .iter() + .filter_map(|tx| { + let logs = match tx.result.transaction.meta.clone().unwrap().log_messages { + solana_transaction_status::option_serializer::OptionSerializer::Some(e) => e, + _ => Vec::new(), + }; + let (events, _proof_height) = events::get_events_from_logs(logs.clone()); + let mut send_packet = None; + for event in events { + send_packet = match event { + solana_ibc::events::Event::IbcEvent(event) => match event { + ibc_core_handler_types::events::IbcEvent::SendPacket( + packet, + ) => { + if packet.chan_id_on_a().as_str() == &channel_id.to_string() + && packet.port_id_on_a().as_str() == port_id.as_str() + && seqs + .iter() + .find(|&&seq| packet.seq_on_a().value() == seq) + .is_some() + { + if *maximum_sequence_number == packet.seq_on_a().value() + { + is_maximum_seq_found = true; + } + log::info!( + "These are logs for send packet transaction {:?}", + logs + ); + let height_str = logs.iter().find_map(|log| { + if log.starts_with("Program log: Current Block height ") { + Some(log.strip_prefix("Program log: Current Block height ").unwrap()) + } else { + None + } + }).expect("No height found while fetching send packet event"); + log::info!("This is height_str {:?}", height_str); + let height = height_str.parse::().unwrap(); + return Some((packet.clone(), tx.result.slot)); + } + if *maximum_sequence_number > packet.seq_on_a().value() { + is_sequence_greater = true; + } + None + }, + _ => None, + }, + _ => None, + }; + if send_packet.is_some() { + break; + } + } + send_packet + }) + .collect(); + if is_sequence_greater && !is_maximum_seq_found { + log::info!("Sequence number found in logs is lesser than the set of sequence which we are looking for"); + return Ok(Vec::new()); + } + let packets: Vec<_> = send_packet_events + .iter() + .map(|(packet, proof_height)| ibc_rpc::PacketInfo { + height: Some(proof_height.clone()), + sequence: packet.seq_on_a().value(), + source_port: packet.port_id_on_a().to_string(), + source_channel: packet.chan_id_on_a().to_string(), + destination_port: packet.port_id_on_b().to_string(), + destination_channel: packet.chan_id_on_b().to_string(), + channel_order: packet.channel_ordering().to_string(), + data: packet.packet_data().to_vec(), + timeout_height: Height { + revision_height: packet.timeout_height_on_b().commitment_revision_height(), + revision_number: packet.timeout_height_on_b().commitment_revision_number(), + } + .into(), + timeout_timestamp: packet.timeout_timestamp_on_b().nanoseconds(), + ack: None, + }) + .collect(); + total_packets.extend(packets); + } + log::info!("Found sent packets {:?}", total_packets); + Ok(total_packets) + } + + async fn query_received_packets( + &self, + channel_id: ibc::core::ics24_host::identifier::ChannelId, + port_id: ibc::core::ics24_host::identifier::PortId, + seqs: Vec, + ) -> Result, Self::Error> { + log::info!("Inside received packets"); + let rpc_client = self.rpc_client(); + if seqs.is_empty() { + return Ok(Vec::new()); + } + let maximum_sequence_number = seqs.iter().max().unwrap(); + let mut before_hash = None; + let mut total_packets = Vec::new(); + while total_packets.len() < seqs.len() { + let (transactions, last_searched_hash) = events::get_previous_transactions( + &rpc_client, + self.solana_ibc_program_id, + before_hash, + SearchIn::IBC, + ) + .await; + before_hash = Some( + anchor_client::solana_sdk::signature::Signature::from_str(&last_searched_hash) + .unwrap(), + ); + let mut is_sequence_greater = false; + let mut is_maximum_seq_found = false; + let recv_packet_events: Vec<_> = transactions + .iter() + .filter_map(|tx| { + let logs = match tx.result.transaction.meta.clone().unwrap().log_messages { + solana_transaction_status::option_serializer::OptionSerializer::Some(e) => e, + _ => Vec::new(), + }; + let (events, proof_height) = events::get_ibc_events_from_logs(logs.clone()); + let receive_packet_event = events.iter().find(|event| { + matches!( + event, + ibc_core_handler_types::events::IbcEvent::WriteAcknowledgement(_) + ) + }); + match receive_packet_event { + Some(e) => match e { + ibc_core_handler_types::events::IbcEvent::WriteAcknowledgement(packet) => + if packet.chan_id_on_b().as_str() == &channel_id.to_string() && + packet.port_id_on_b().as_str() == port_id.as_str() && + seqs.iter() + .find(|&&seq| packet.seq_on_a().value() == seq) + .is_some() + { + if *maximum_sequence_number == packet.seq_on_a().value() { + is_maximum_seq_found = true; + } + log::info!("Found receive packet"); + Some((e.clone(), tx.result.slot)) + } else { + if *maximum_sequence_number > packet.seq_on_a().value() { + is_sequence_greater = true; + } + log::info!("Receive Ids dont match expected channel id: {:?} got channel id: {:?} expect port id: {:?} got port id: {:?} expected seq: {:?} got seq: {:?}", packet.chan_id_on_b(), channel_id, packet.port_id_on_b(), port_id, seqs, packet.seq_on_a().value()); + None + }, + _ => None, + }, + None => None, + } + }) + .collect(); + if is_sequence_greater && !is_maximum_seq_found { + log::info!("Sequence number found in logs is lesser than the set of sequence which we are looking for"); + return Ok(Vec::new()); + } + let packets: Vec<_> = recv_packet_events + .iter() + .map(|(recv_packet, height)| match recv_packet { + ibc_core_handler_types::events::IbcEvent::WriteAcknowledgement(packet) => { + ibc_rpc::PacketInfo { + height: Some(*height), + sequence: packet.seq_on_a().value(), + source_port: packet.port_id_on_a().to_string(), + source_channel: packet.chan_id_on_a().to_string(), + destination_port: packet.port_id_on_b().to_string(), + destination_channel: packet.chan_id_on_b().to_string(), + channel_order: String::from(""), + data: packet.packet_data().to_vec(), + timeout_height: Height { + revision_height: packet + .timeout_height_on_b() + .commitment_revision_height(), + revision_number: packet + .timeout_height_on_b() + .commitment_revision_number(), + } + .into(), + timeout_timestamp: packet.timeout_timestamp_on_b().nanoseconds(), + ack: Some(packet.acknowledgement().as_bytes().to_vec()), + } + }, + _ => panic!("Infallible"), + }) + .collect(); + total_packets.extend(packets); + } + println!("Length of receive packets {}", total_packets.len()); + Ok(total_packets) + } + + fn expected_block_time(&self) -> Duration { + // solana block time is roughly 400 milliseconds + Duration::from_millis(400) + } + + async fn query_client_update_time_and_height( + &self, + client_id: ClientId, + client_height: Height, + ) -> Result<(Height, ibc::timestamp::Timestamp), Self::Error> { + let storage = self.get_ibc_storage().await; + let client_store = storage + .clients + .iter() + .find(|&client| client.client_id.as_str() == client_id.as_str()) + .ok_or("Client not found with the given client id while querying client update time and height".to_owned())?; + let inner_client_height = ibc_core_client_types::Height::new( + client_height.revision_number, + client_height.revision_height, + ) + .unwrap(); + let height = client_store + .consensus_states + .deref() + .get(&inner_client_height) + .ok_or("No host height found with the given height".to_owned())? + .processed_height() + .ok_or("No height found".to_owned())?; + let timestamp = client_store + .consensus_states + .deref() + .get(&inner_client_height) + .ok_or("No timestamp found with the given height".to_owned())? + .processed_time() + .ok_or("No timestamp found".to_owned())?; + Ok(( + Height { + revision_height: u64::from(height), + // TODO: Use epoch + revision_number: 1_u64, + }, + Timestamp::from_nanoseconds(u64::from(timestamp)).unwrap(), + )) + } + + async fn query_host_consensus_state_proof( + &self, + client_state: &pallet_ibc::light_clients::AnyClientState, + ) -> Result>, Self::Error> { + let height = client_state.latest_height(); + let (trie, at_height) = self.get_trie(height.revision_height, false).await; + let client_id = self.client_id(); + let new_client_id = + ibc_core_host_types::identifiers::ClientId::from_str(client_id.as_str()).unwrap(); + let client_idx = ClientIdx::try_from(new_client_id).unwrap(); + let consensus_state_trie_key = TrieKey::for_consensus_state( + client_idx, + ibc_core_client_types::Height::new(height.revision_number, height.revision_height) + .unwrap(), + ); + let (_, host_consensus_state_proof) = trie + .prove(&consensus_state_trie_key) + .map_err(|_| Error::Custom("value is sealed and cannot be fetched".to_owned()))?; + Ok(Some(borsh::to_vec(&host_consensus_state_proof).unwrap())) + } + + async fn query_ibc_balance( + &self, + asset_id: Self::AssetId, + ) -> Result, Self::Error> { + let denom = &asset_id; + // let (token_mint_key, _bump) = + // Pubkey::find_program_address(&[denom.as_ref()], &self.solana_ibc_program_id); + let token_mint_key = Pubkey::from_str(&asset_id).unwrap(); + let user_token_address = + get_associated_token_address(&self.keybase.public_key, &token_mint_key); + let sol_rpc_client = self.rpc_client(); + let balance = sol_rpc_client.get_token_account_balance(&user_token_address).await.unwrap(); + log::info!("IBC Balance on solana {}", balance.amount); + Ok(vec![PrefixedCoin { + denom: PrefixedDenom { + trace_path: TracePath::default(), + base_denom: BaseDenom::from_str(denom).unwrap(), + }, + amount: Amount::from_str(&balance.amount).unwrap(), + }]) + } + + fn connection_prefix(&self) -> ibc::core::ics23_commitment::commitment::CommitmentPrefix { + self.commitment_prefix.clone() + } + + fn client_id(&self) -> ClientId { + self.client_id + .lock() + .unwrap() + .as_ref() + .expect("Client Id should be defined") + .clone() + } + + fn set_client_id(&mut self, client_id: ClientId) { + *self.client_id.lock().unwrap() = Some(client_id); + } + + fn connection_id(&self) -> Option { + self.connection_id.lock().unwrap().clone() + } + + fn set_channel_whitelist( + &mut self, + channel_whitelist: std::collections::HashSet<( + ibc::core::ics24_host::identifier::ChannelId, + ibc::core::ics24_host::identifier::PortId, + )>, + ) { + *self.channel_whitelist.lock().unwrap() = channel_whitelist; + } + + fn add_channel_to_whitelist( + &mut self, + channel: ( + ibc::core::ics24_host::identifier::ChannelId, + ibc::core::ics24_host::identifier::PortId, + ), + ) { + self.channel_whitelist.lock().unwrap().insert(channel); + } + + fn set_connection_id(&mut self, connection_id: ConnectionId) { + *self.connection_id.lock().unwrap() = Some(connection_id) + } + + fn client_type(&self) -> ibc::core::ics02_client::client_state::ClientType { + self.client_type.clone() + } + + async fn query_timestamp_at(&self, block_number: u64) -> Result { + let rpc_client = self.rpc_client(); + let header = + events::get_header_from_height(rpc_client, self.solana_ibc_program_id, block_number) + .await; + if let Some(header) = header { + return Ok(header.timestamp_ns.into()); + } else { + log::error!("No block header found for height {:?}", block_number); + return Err(Error::RpcError(format!( + "No block header found for height {:?}", + block_number + ))); + } + } + + async fn query_clients(&self) -> Result, Self::Error> { + let storage = self.get_ibc_storage().await; + let client_ids: Vec = storage + .clients + .iter() + .map(|client| ClientId::from_str(&client.client_id.to_string()).unwrap()) + .collect(); + Ok(client_ids) + } + + async fn query_channels( + &self, + ) -> Result< + Vec<( + ibc::core::ics24_host::identifier::ChannelId, + ibc::core::ics24_host::identifier::PortId, + )>, + Self::Error, + > { + let storage = self.get_ibc_storage().await; + let channels: Vec<(ChannelId, PortId)> = storage + .port_channel + .deref() + .keys() + .map(|channel_store| { + ( + ChannelId::from_str(&channel_store.channel_id().as_str()).unwrap(), + PortId::from_str(&channel_store.port_id().as_str()).unwrap(), + ) + }) + .collect(); + Ok(channels) + } + + async fn query_connection_using_client( + &self, + _height: u32, + client_id: String, + ) -> Result, Self::Error> { + let storage = self.get_ibc_storage().await; + let client_id_key = + ibc_core_host_types::identifiers::ClientId::from_str(&client_id).unwrap(); + let mut index = -1; + let connections: Vec = storage + .connections + .iter() + .filter_map(|serialized_connection| { + index += 1; + // let serialized_connection = &storage.connections[0]; + let connection = serialized_connection.get().unwrap(); + if connection.client_id_matches(&client_id_key) { + let versions = connection + .versions() + .iter() + .map(|version| { + let proto_version: ibc_proto_new::ibc::core::connection::v1::Version = + version.clone().try_into().unwrap(); + Version { + identifier: proto_version.identifier, + features: proto_version.features, + } + }) + .collect(); + let counterparty = connection.counterparty(); + let connection_id = format!("{}-{}", ConnectionId::prefix(), index); + return Some(IdentifiedConnection { + id: connection_id, + client_id: client_id.clone(), + versions, + state: i32::from(connection.state), + counterparty: Some(ConnCounterparty { + client_id: counterparty.client_id.to_string(), + connection_id: counterparty + .connection_id() + .map_or_else(|| "".to_string(), |v| v.as_str().to_string()), + prefix: Some(ibc_proto::ibc::core::commitment::v1::MerklePrefix { + key_prefix: counterparty.prefix.clone().into_vec(), + }), + }), + delay_period: connection.delay_period().as_nanos() as u64, + }); + }; + None + }) + .collect(); + log::info!("querying connecting using client final"); + Ok(connections) + } + + async fn is_update_required( + &self, + _latest_height: u64, + _latest_client_height_on_counterparty: u64, + ) -> Result { + // we never need to use LightClientSync trait in this case, because + // all the events will be eventually submitted via `finality_notifications` + Ok(false) + } + + async fn initialize_client_state( + &self, + ) -> Result< + (pallet_ibc::light_clients::AnyClientState, pallet_ibc::light_clients::AnyConsensusState), + Self::Error, + > { + let (height, timestamp) = self.latest_height_and_timestamp().await?; + println!("This is height on solana {:?} and timestamp {:?}", height, timestamp); + let (trie_data, _at_height) = self.get_trie(0, false).await; + let witness_key = self.get_witness_key(); + let storage = self.get_ibc_storage().await; + let (slot, slot_timestamp, trie_root) = + storage.local_consensus_state.iter().last().unwrap(); + let slot_time = self.rpc_client().get_block_time(slot.clone()).await.unwrap(); + log::info!("Slot time and timestamp\n{}\n{}", slot_time, slot_timestamp); + let client_state = cf_solana_og::ClientState { + latest_slot: NonZeroU64::new(slot.clone()).unwrap(), + witness_account: witness_key.into(), + trusting_period_ns: 86400 * 7 * 10u64.pow(9), // 1 week + is_frozen: false, + }; + let consensus_state = cf_solana_og::ConsensusState { + trie_root: CommitmentRoot::from_bytes(trie_root.as_slice()), + timestamp_sec: NonZeroU64::new(slot_time as u64).unwrap(), + }; + Ok(( + AnyClientState::Rollup(cf_solana::ClientState(client_state)), + AnyConsensusState::Rollup(cf_solana::ConsensusState(consensus_state.into())), + )) + } + + async fn query_client_id_from_tx_hash( + &self, + tx_id: Self::TransactionId, + ) -> Result { + log::info!("This is signature {:?}", tx_id); + let program = self.program(); + let signature = Signature::from_str(&tx_id).unwrap(); + let sol_rpc_client = program.async_rpc(); + let tx = sol_rpc_client + .get_transaction(&signature, UiTransactionEncoding::Base64) + .await + .unwrap(); + let logs = match tx.transaction.meta.unwrap().log_messages { + solana_transaction_status::option_serializer::OptionSerializer::Some(logs) => logs, + solana_transaction_status::option_serializer::OptionSerializer::None => { + return Err(Error::Custom(String::from("No logs found"))) + }, + solana_transaction_status::option_serializer::OptionSerializer::Skip => { + return Err(Error::Custom(String::from("Logs were skipped, so not available"))) + }, + }; + let (events, _proof_height) = events::get_ibc_events_from_logs(logs); + log::info!("These are events {:?}", events); + let result: Vec<&ibc_core_client_types::events::CreateClient> = events + .iter() + .filter_map(|event| match event { + ibc_core_handler_types::events::IbcEvent::CreateClient(e) => Some(e), + _ => None, + }) + .collect(); + if result.len() != 1 { + return Err(Error::Custom(format!( + "Expected exactly one CreateClient event, found {}", + result.len() + ))); + } + let client_id = result[0].client_id(); + Ok(ClientId::from_str(client_id.as_str()).unwrap()) + } + + async fn query_connection_id_from_tx_hash( + &self, + tx_id: Self::TransactionId, + ) -> Result { + log::info!("This is tx id {:?}", tx_id); + let program = self.program(); + let signature = Signature::from_str(&tx_id).unwrap(); + let sol_rpc_client = program.async_rpc(); + let tx = sol_rpc_client + .get_transaction(&signature, UiTransactionEncoding::Base64) + .await + .unwrap(); + let logs = match tx.transaction.meta.unwrap().log_messages { + solana_transaction_status::option_serializer::OptionSerializer::Some(logs) => logs, + solana_transaction_status::option_serializer::OptionSerializer::None => { + return Err(Error::Custom(String::from("No logs found"))) + }, + solana_transaction_status::option_serializer::OptionSerializer::Skip => { + return Err(Error::Custom(String::from("Logs were skipped, so not available"))) + }, + }; + let (events, _proof_height) = events::get_ibc_events_from_logs(logs); + log::info!("These are events {:?}", events); + let result: Vec<&ibc_core_connection_types::events::OpenInit> = events + .iter() + .filter_map(|event| match event { + ibc_core_handler_types::events::IbcEvent::OpenInitConnection(e) => Some(e), + _ => None, + }) + .collect(); + if result.len() != 1 { + return Err(Error::Custom(format!( + "Expected exactly one OpenInitConnection event, found {}", + result.len() + ))); + } + let connection_id = result[0].conn_id_on_a(); + Ok(ConnectionId::from_str(connection_id.as_str()).unwrap()) + } + + async fn query_channel_id_from_tx_hash( + &self, + tx_id: Self::TransactionId, + ) -> Result< + (ibc::core::ics24_host::identifier::ChannelId, ibc::core::ics24_host::identifier::PortId), + Self::Error, + > { + let program = self.program(); + let signature = Signature::from_str(&tx_id).unwrap(); + let sol_rpc_client = program.async_rpc(); + let tx = sol_rpc_client + .get_transaction(&signature, UiTransactionEncoding::Json) + .await + .unwrap(); + let logs = match tx.transaction.meta.unwrap().log_messages { + solana_transaction_status::option_serializer::OptionSerializer::Some(logs) => logs, + solana_transaction_status::option_serializer::OptionSerializer::None => { + return Err(Error::Custom(String::from("No logs found"))) + }, + solana_transaction_status::option_serializer::OptionSerializer::Skip => { + return Err(Error::Custom(String::from("Logs were skipped, so not available"))) + }, + }; + let (events, _proof_height) = events::get_ibc_events_from_logs(logs); + let result: Vec<&ibc_core_channel_types::events::OpenInit> = events + .iter() + .filter_map(|event| match event { + ibc_core_handler_types::events::IbcEvent::OpenInitChannel(e) => Some(e), + _ => None, + }) + .collect(); + if result.len() != 1 { + return Err(Error::Custom(format!( + "Expected exactly one OpenInitChannel event, found {}", + result.len() + ))); + } + let channel_id = result[0].chan_id_on_a(); + let port_id = result[0].port_id_on_a(); + Ok(( + ChannelId::from_str(channel_id.as_str()).unwrap(), + PortId::from_str(port_id.as_str()).unwrap(), + )) + } + + async fn upload_wasm(&self, _wasm: Vec) -> Result, Self::Error> { + todo!() + } +} + +impl KeyProvider for RollupClient { + fn account_id(&self) -> ibc::signer::Signer { + let key_entry = &self.keybase; + let public_key = key_entry.public_key; + ibc::signer::Signer::from_str(&public_key.to_string()).unwrap() + } +} + +#[async_trait::async_trait] +impl MisbehaviourHandler for RollupClient { + async fn check_for_misbehaviour( + &self, + _counterparty: &C, + _client_message: AnyClientMessage, + ) -> Result<(), anyhow::Error> { + Ok(()) + } +} + +#[async_trait::async_trait] +impl LightClientSync for RollupClient { + async fn is_synced(&self, _counterparty: &C) -> Result { + Ok(true) + } + + async fn fetch_mandatory_updates( + &self, + counterparty: &C, + ) -> Result<(Vec, Vec), anyhow::Error> { + log::info!("Fetching mandatory updates"); + let latest_height = counterparty.latest_height_and_timestamp().await?.0; + let response = counterparty.query_client_state(latest_height, self.client_id()).await?; + let any_client_state = response.client_state.ok_or_else(|| { + Error::Custom("Received an empty client state from counterparty".to_string()) + })?; + let AnyClientState::Guest(client_state) = + AnyClientState::decode_recursive(any_client_state.clone(), |c| { + matches!(c, AnyClientState::Guest(_)) + }) + .or_else(|| { + log::info!("This is wasm {:?}", any_client_state); + let wasm_client_state = AnyClientState::decode_recursive(any_client_state, |c| { + matches!(c, AnyClientState::Wasm(_)) + }) + .unwrap(); + Some(wasm_client_state.unpack_recursive().clone()) + }) + .unwrap() + else { + unreachable!() + }; + let height = client_state.0.latest_height.into(); + let (signatures, _events) = events::get_signatures_upto_height( + self.rpc_client(), + self.solana_ibc_program_id, + height, + ) + .await; + let chain_account = self.get_chain_storage().await; + let mut heights = Vec::new(); + let updates: Vec = signatures + .iter() + .map(|(sig, block_header, epoch)| { + let validators = epoch.clone().unwrap().validators().to_vec(); + let all_validators: Vec> = validators + .iter() + .map(|validator| { + let new_validator: Validator = + Validator::new( + PubKey::from_bytes(&validator.pubkey.to_vec()).unwrap(), + validator.stake, + ); + new_validator + }) + .collect(); + let final_signatures: Vec<_> = sig + .iter() + .enumerate() + .map(|(index, (validator, signature))| { + let validator_idx = all_validators + .iter() + .position(|v| { + v.pubkey + == PubKey::from_bytes(&validator.to_bytes().as_slice()).unwrap() + }) + .unwrap(); + (validator_idx as u16, signature.clone()) + }) + .collect(); + let current_epoch = Epoch::new_with(all_validators, |total| { + let quorum = NonZeroU128::new(total.get() / 2 + 1).unwrap(); + // min_quorum_stake may be greater than total_stake so we’re not + // using .clamp to make sure we never return value higher than + // total_stake. + quorum.max(NonZeroU128::new(1000).unwrap()).min(total) + }) + .unwrap(); + let guest_header = cf_guest_og::Header { + genesis_hash: chain_account.genesis().unwrap().clone(), + block_hash: block_header.calc_hash(), + block_header: block_header.clone(), + epoch_commitment: current_epoch.calc_commitment(), + epoch: current_epoch, + signatures: final_signatures, + }; + let msg = MsgUpdateAnyClient:: { + client_id: self.client_id(), + client_message: AnyClientMessage::Guest(guest_header.into()), + signer: counterparty.account_id(), + }; + let value = msg.clone().encode_vec(); + heights.push(IbcEvent::NewBlock(NewBlock::new(Height::new( + 1, + block_header.block_height.into(), + )))); + Any { type_url: msg.type_url(), value } + }) + .collect(); + + Ok((updates, heights)) + } +} + +#[async_trait::async_trait] +impl Chain for RollupClient { + fn name(&self) -> &str { + &self.name + } + + fn block_max_weight(&self) -> u64 { + self.max_tx_size as u64 + } + + async fn estimate_weight(&self, _msg: Vec) -> Result { + Ok(0) + } + + async fn finality_notifications( + &self, + ) -> Result< + Pin::FinalityEvent> + Send + Sync>>, + Error, + > { + let (tx, rx) = unbounded_channel(); + let ws_url = self.ws_url.clone(); + // let client = PubsubClient::new(&ws_url).await.unwrap(); + let program_id = self.solana_ibc_program_id; + // tokio::task::spawn_blocking(move || { + // let (mut stream, receiver) = client + // .block_subscribe( + // // &ws_url, + // // RpcBlockSubscribeFilter::MentionsAccountOrProgram(program_id.to_string()), + // RpcBlockSubscribeFilter::All, + // Some(RpcBlockSubscribeConfig { + // commitment: Some(CommitmentConfig::finalized()), + // ..Default::default() + // }), + // ) + // .await + // .unwrap(); + + // loop { + // match stream.next().await { + // Some(block) => { + // // sleep(Duration::from_secs(10)); + // // log::info!("Found rollup event"); + // let block = block.value; + // let confirmed_block = block.block.unwrap(); + // // log::info!("Found finality event"); + // let mut broke = false; + // let finality_event = FinalityEvent::Rollup { + // slot: block.slot, + // previous_blockhash: confirmed_block.previous_blockhash, + // blockhash: confirmed_block.blockhash, + // }; + // let _ = tx.send(finality_event).map_err(|_| broke = true); + // if broke { + // break; + // } + // }, + // None => { + // log::error!("Disconnected: "); + // // continue; + // }, + // } + // } + // }); + // Spawn a new task to handle the stream + // Spawn a new task to handle the stream + tokio::spawn(async move { + let client = PubsubClient::new(&ws_url).await.unwrap(); + let (mut stream, _receiver) = client + .block_subscribe( + // RpcBlockSubscribeFilter::All, + RpcBlockSubscribeFilter::MentionsAccountOrProgram(program_id.to_string()), + Some(RpcBlockSubscribeConfig { + commitment: Some(CommitmentConfig::finalized()), + ..Default::default() + }), + ) + .await + .unwrap(); + + while let Some(block) = stream.next().await { + let block = block.value; + if let Some(confirmed_block) = block.block { + let finality_event = FinalityEvent::Rollup { + slot: block.slot, + previous_blockhash: confirmed_block.previous_blockhash, + blockhash: confirmed_block.blockhash, + }; + if tx.send(finality_event).is_err() { + // The receiver has been dropped, exit the loop + break; + } + } + } + log::info!("Finality notifications stream ended"); + }); + + let streams = tokio_stream::wrappers::UnboundedReceiverStream::new(rx); + log::info!("THis is stream {:?}", streams); + Ok(Box::pin(streams)) + } + + async fn submit(&self, messages: Vec) -> Result { + let keypair = self.keybase.keypair(); + log::info!("submitting tx now, {}", keypair.pubkey()); + let authority = Arc::new(keypair); + let program = self.program(); + + // Build, sign, and send program instruction + let mut signature = String::new(); + let rpc = program.async_rpc(); + + let mut all_transactions = Vec::new(); + let mut index = -1; + + for message in messages { + let storage = self.get_ibc_storage().await; + let my_message = Ics26Envelope::::try_from(message.clone()).unwrap(); + let new_messages = convert_old_msgs_to_new(vec![my_message]); + let message = new_messages[0].clone(); + let mut instruction_data = + anchor_lang::InstructionData::data(&solana_ibc::instruction::Deliver { + message: message.clone(), + }); + let instruction_len = instruction_data.len() as u32; + instruction_data.splice(..0, instruction_len.to_le_bytes()); + + let balance = program.async_rpc().get_balance(&authority.pubkey()).await.unwrap(); + log::info!("This is balance {}", balance); + log::info!("This is start of payload ---------------------------------"); + log::info!("{:?}", message); + log::info!("This is end of payload ----------------------------------"); + + let max_tries = 5; + + let (mut chunks, chunk_account, _) = write::instruction::WriteIter::new( + &self.write_program_id, + authority.pubkey(), + WRITE_ACCOUNT_SEED, + instruction_data.clone(), + ) + .unwrap(); + + chunks.chunk_size = core::num::NonZeroU16::new(800).unwrap(); + + let compute_budget_ix = ComputeBudgetInstruction::set_compute_unit_limit(30_000); + // let compute_unit_price_ix = + // ComputeBudgetInstruction::set_compute_unit_price(10_000_001); + + let chunking_transactions: Vec = chunks + .map(|ix| { + Transaction::new_with_payer( + &[compute_budget_ix.clone(), ix], + Some(&authority.pubkey()), + ) + }) + .collect(); + + // for instruction in &mut chunks { + // let transaction = Transaction::new_signed_with_payer( + // &[instruction], + // Some(&authority.pubkey()), + // &[&*authority], + // blockhash, + // ); + // let sig = rpc.send_and_confirm_transaction_with_spinner(&transaction).await.unwrap(); + // // futures.push(x); + // println!(" Signature {sig}"); + // } + + let (signature_chunking_transactions, further_transactions) = + if let MsgEnvelope::Client(ClientMsg::UpdateClient(e)) = message { + self.send_deliver( + DeliverIxType::UpdateClient { + client_message: e.client_message, + client_id: e.client_id, + }, + chunk_account, + max_tries, + ) + .await? + // msg!("Packet Update Signature {:?}", signature); + } else if let MsgEnvelope::Packet(PacketMsg::Recv(e)) = message { + let packet_data: ibc_app_transfer_types::packet::PacketData = + serde_json::from_slice(&e.packet.data).unwrap(); + self.send_deliver( + DeliverIxType::Recv { + token: packet_data.token, + port_id: e.packet.port_id_on_b, + channel_id: e.packet.chan_id_on_b, + receiver: packet_data.receiver.to_string(), + }, + chunk_account, + max_tries, + ) + .await? + // msg!("Packet Recv Signature {:?}", signature); + } else if let MsgEnvelope::Packet(PacketMsg::Timeout(e)) = message { + let packet_data: ibc_app_transfer_types::packet::PacketData = + serde_json::from_slice(&e.packet.data).unwrap(); + self.send_deliver( + DeliverIxType::Timeout { + token: packet_data.token, + port_id: e.packet.port_id_on_a, + channel_id: e.packet.chan_id_on_a, + sender_account: packet_data.sender.to_string(), + }, + chunk_account, + max_tries, + ) + .await? + // msg!("Packet Timeout Signature {:?}", signature); + } else if let MsgEnvelope::Packet(PacketMsg::Ack(e)) = message { + let packet_data: ibc_app_transfer_types::packet::PacketData = + serde_json::from_slice(&e.packet.data).unwrap(); + self.send_deliver( + DeliverIxType::Timeout { + token: packet_data.token, + port_id: e.packet.port_id_on_a, + channel_id: e.packet.chan_id_on_a, + sender_account: packet_data.sender.to_string(), + }, + chunk_account, + max_tries, + ) + .await? + // msg!("Packet Acknowledgement Signature {:?}", signature); + } else { + // signature = + self.send_deliver(DeliverIxType::Normal, chunk_account, max_tries).await? + // msg!("Packet Normal Signature {:?}", signature); + }; + // transactions.extend(further_transactions); + // log::info!("Chunking tx {:?}", chunking_transactions); + // log::info!("Complete tx {:?}", further_transactions); + // let message_chunking_futures = + // further_transactions.iter().map(|tx| rpc.send_and_confirm_transaction(tx)); + // if chunking_transactions.is_empty() { + // all_transactions.extend(further_transactions); + // } else { + // let message_chunking_futures = + // chunking_transactions.iter().map(|tx| rpc.send_and_confirm_transaction(tx)); + // let futures = join_all(message_chunking_futures).await; + // for sig in futures { + // println!(" Chunking Signature {:?}", sig); + // signature = sig.unwrap().to_string(); + // } + // if !signature_chunking_transactions.is_empty() { + // let signature_chunking_futures = signature_chunking_transactions + // .iter() + // .map(|tx| rpc.send_and_confirm_transaction(tx)); + // let futures = join_all(signature_chunking_futures).await; + // for sig in futures { + // println!(" Signature chunking Signature {:?}", sig); + // signature = sig.unwrap().to_string(); + // } + // } + // if !further_transactions.is_empty() { + // let further_transactions_futures = + // further_transactions.iter().map(|tx| rpc.send_and_confirm_transaction(tx)); + // let futures = join_all(further_transactions_futures).await; + // for sig in futures { + // println!(" Completed Signature {:?}", sig); + // let blockhash = rpc.get_latest_blockhash().await.unwrap(); + // // Wait for finalizing the transaction + // let _ = rpc + // .confirm_transaction_with_spinner( + // &sig.as_ref().unwrap(), + // &blockhash, + // CommitmentConfig::finalized(), + // ) + // .await + // .unwrap(); + // signature = sig.unwrap().to_string(); + // } + // } + // } + + let mut current_transactions = Vec::new(); + + current_transactions.extend(chunking_transactions); + current_transactions.extend(signature_chunking_transactions); + current_transactions.extend(further_transactions); + + all_transactions.push(current_transactions); + + // let signatures = join_all(futures).await; + // for sig in signatures { + // println!(" Signature {:?}", sig); + // signature = sig.unwrap().to_string(); + // } + } + + let total_transactions_length = all_transactions.iter().fold(0, |acc, tx| acc + tx.len()); + let mut failure = false; + + match self.transaction_sender { + TransactionSender::RPC => { + let length = all_transactions.len(); + log::info!("Total transactions {:?}", length); + let start_time = Instant::now(); + for transactions_iter in all_transactions { + let mut tries = 0; + let before_time = Instant::now(); + for mut transaction in transactions_iter { + loop { + sleep(Duration::from_secs(1)); + log::info!("Current Try in rollup: {}", tries); + let blockhash = rpc.get_latest_blockhash().await.unwrap(); + log::info!("Blockhash {:?}", blockhash); + transaction.sign(&[&*authority], blockhash); + let tx = transaction.clone(); + log::info!("Signed now "); + let sync_rpc = self.program().rpc(); + let rpc = self.rpc_client(); + let sig = spawn_blocking(move || { + sync_rpc + .send_transaction_with_config( + &tx, + RpcSendTransactionConfig { + skip_preflight: true, + max_retries: Some(0), + ..RpcSendTransactionConfig::default() + }, + ) + .unwrap() + }) + .await; + + log::info!("This is sig {:?}", sig); + + if let Ok(si) = sig { + signature = si.to_string(); + // Wait for finalizing the transaction + let mut success = false; + // let blockhash = rpc.get_latest_blockhash().await.unwrap(); + for status_retry in 0..usize::MAX { + match rpc.get_signature_status(&si).await.unwrap() { + Some(Ok(_)) => { + log::info!(" Signature {:?}", si); + success = true; + break; + }, + Some(Err(e)) => { + log::error!( + "Error while sending the transaction {:?}", + e + ); + success = true; + failure = true; + break; + }, + None => { + if !rpc + .is_blockhash_valid( + &blockhash, + CommitmentConfig::processed(), + ) + .await + .unwrap() + { + // Block hash is not found by some reason + log::error!("Blockhash not found"); + success = false; + break; + } else if status_retry < usize::MAX { + // Retry twice a second + sleep(Duration::from_millis(500)); + continue; + } + }, + } + } + if !success { + tries += 1; + continue; + } + break; + } else { + log::error!("Error {:?}", sig); + tries += 1; + continue; + } + } + let after_time = Instant::now(); + let diff = after_time - before_time; + let success_rate = 100 / (tries + 1); + log::info!("Time taken {}", diff.as_millis()); + log::info!("Success rate {}", success_rate); + } + } + let end_time = Instant::now(); + let diff = end_time - start_time; + log::info!("Time taken for all transactions {}", diff.as_millis()); + log::info!( + "Average time for 1 transaction {}", + (diff.as_millis() / length as u128) + ); + }, + TransactionSender::JITO => { + log::info!("Total transactions {:?}", all_transactions.len()); + let start_time = Instant::now(); + for transactions_iter in all_transactions { + log::info!("Transactions to be sent {:?}", transactions_iter.len()); + + for transactions in transactions_iter.chunks(4) { + let mut tries = 0; + + let before_time = Instant::now(); + while tries < 5 { + println!("Try For Tx: {}", tries); + let mut current_transactions = Vec::new(); + + let mut client = jito_searcher_client::get_searcher_client( + &BLOCK_ENGINE_URL, + &authority, + ) + .await + .expect("connects to searcher client"); + let mut bundle_results_subscription = client + .subscribe_bundle_results(SubscribeBundleResultsRequest {}) + .await + .expect("subscribe to bundle results") + .into_inner(); + + let jito_address = + Pubkey::from_str("96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5") + .unwrap(); + let ix = anchor_lang::solana_program::system_instruction::transfer( + &authority.pubkey(), + &jito_address, + 40000, + ); + let rpc_client = self.rpc_client(); + let blockhash = rpc.get_latest_blockhash().await.unwrap(); + let tx = Transaction::new_with_payer(&[ix], Some(&authority.pubkey())); + + current_transactions.push(tx); + current_transactions.extend(transactions.to_vec()); + + let versioned_transactions: Vec = + current_transactions + .into_iter() + .map(|mut tx| { + tx.sign(&[&*authority], blockhash); + let serialized_tx = bincode::serialize(&tx).unwrap(); + // encode in base 58 + let encoded_tx = bs58::encode(serialized_tx).into_string(); + log::info!("Encoded tx {:?}", encoded_tx); + tx.clone().into() + }) + .collect(); + + let signatures = jito_searcher_client::send_bundle_with_confirmation( + &versioned_transactions, + &rpc_client, + &mut client, + &mut bundle_results_subscription, + ) + .await + .or_else(|e| { + println!("This is error {:?}", e); + ibc::prelude::Err("Error".to_owned()) + }); + + if let Ok(sigs) = signatures { + signature = sigs.last().unwrap().to_string(); + break; + } else { + tries += 1; + continue; + } + } + if tries == 5 { + log::error!("Failed to send transaction"); + } else { + let after_time = Instant::now(); + let diff = after_time - before_time; + let success_rate = 100 / (tries + 1); + log::info!("Time taken {}", diff.as_millis()); + log::info!("Success rate {}", success_rate); + } + } + } + let end_time = Instant::now(); + let diff = end_time - start_time; + log::info!("Time taken for all transactions {}", diff.as_millis()); + log::info!( + "Average time for 1 transaction {}", + (diff.as_millis() / total_transactions_length as u128) + ); + }, + }; + + let blockhash = rpc.get_latest_blockhash().await.unwrap(); + // Wait for finalizing the transaction + if !self.common_state.handshake_completed { + loop { + log::info!("Finalizing Transaction {}", failure); + if failure { + log::error!("Breaking due to error"); + break; + } + let result = rpc + .confirm_transaction_with_commitment( + &Signature::from_str(&signature).unwrap(), + CommitmentConfig::finalized(), + ) + .await + .unwrap(); + if result.value { + break; + } + sleep(Duration::from_secs(1)); + log::info!("This is result {:?}", result); + continue; + } + } + + Ok(signature) + } + + async fn query_client_message( + &self, + _update: UpdateClient, + ) -> Result { + todo!() + } + + async fn get_proof_height(&self, block_height: Height) -> Height { + block_height + } + + async fn handle_error(&mut self, error: &anyhow::Error) -> Result<(), anyhow::Error> { + let err_str = if let Some(rpc_err) = error.downcast_ref::() { + match rpc_err { + Error::RpcError(s) => s.clone(), + _ => "".to_string(), + } + } else { + error.to_string() + }; + log::info!(target: "hyperspace_solana", "Handling error: {err_str}"); + if err_str.contains("dispatch task is gone") + || err_str.contains("failed to send message to internal channel") + { + // self.reconnect().await?; + self.common_state.rpc_call_delay *= 2; + } + + Ok(()) + } + + fn common_state(&self) -> &CommonClientState { + &self.common_state + } + + fn common_state_mut(&mut self) -> &mut CommonClientState { + &mut self.common_state + } + + async fn reconnect(&mut self) -> anyhow::Result<()> { + todo!() + } + + async fn on_undelivered_sequences(&self, has: bool, kind: UndeliveredType) { + let _ = Box::pin(async move { + let __self = self; + let has = has; + let kind = kind; + let () = { __self.common_state().on_undelivered_sequences(has, kind).await }; + }); + } + + fn has_undelivered_sequences(&self, kind: UndeliveredType) -> bool { + self.common_state().has_undelivered_sequences(kind) + } + + fn rpc_call_delay(&self) -> Duration { + self.common_state().rpc_call_delay() + } + + fn initial_rpc_call_delay(&self) -> Duration { + self.common_state().initial_rpc_call_delay + } + + fn set_rpc_call_delay(&mut self, delay: Duration) { + self.common_state_mut().set_rpc_call_delay(delay) + } +} + +// // #[tokio::test] +// #[test] +// pub fn test_rollup_client() { +// println!("Starting"); +// let bytes = vec![ +// 85, 115, 101, 100, 32, 72, 84, 84, 80, 32, 77, 101, 116, 104, 111, 100, 32, 105, 115, 32, +// 110, 111, 116, 32, 97, 108, 108, 111, 119, 101, 100, 46, 32, 80, 79, 83, 84, 32, 111, 114, +// 32, 79, 80, 84, 73, 79, 78, 83, 32, 105, 115, 32, 114, 101, 113, 117, 105, 114, 101, 100, +// 10, +// ]; + +// match String::from_utf8(bytes) { +// Ok(s) => println!("Converted string: {}", s), +// Err(e) => println!("Failed to convert bytes to string: {}", e), +// } +// // let (tx, rx) = unbounded_channel(); +// // let ws_url = "wss://mantis-testnet-rollup.composable-shared-artifacts.composablenodes.tech/ws"; +// let ws_url = "wss://devnet.helius-rpc.com/?api-key=5ae782d8-6bf6-489c-b6df-ef7e6289e193"; +// let program_id = "2HLLVco5HvwWriNbUhmVwA2pCetRkpgrqwnjcsZdyTKT"; +// // tokio::task::spawn_blocking(move || { +// let (_block_subscription, receiver) = PubsubClient::logs_subscribe( +// &ws_url, +// // RpcBlockSubscribeFilter::MentionsAccountOrProgram(program_id.to_string()), +// // RpcBlockSubscribeFilter::All, +// RpcTransactionLogsFilter::All, +// RpcTransactionLogsConfig { commitment: Some(CommitmentConfig::finalized()) }, +// ) +// .unwrap(); + +// loop { +// match receiver.recv() { +// Ok(block) => { +// // sleep(Duration::from_secs(10)); +// log::info!("Found rollup event"); +// // let block = block.value; +// // let confirmed_block = block.block.unwrap(); +// // // log::info!("Found finality event"); +// // let mut broke = false; +// // let finality_event = FinalityEvent::Rollup { +// // slot: block.slot, +// // previous_blockhash: confirmed_block.previous_blockhash, +// // blockhash: confirmed_block.blockhash, +// // }; +// // let _ = tx.send(finality_event).map_err(|_| broke = true); +// // if broke { +// // break; +// // } +// }, +// Err(err) => { +// panic!("{}", format!("Disconnected: {err}")); +// }, +// } +// } +// // }); + +// // let streams = tokio_stream::wrappers::UnboundedReceiverStream::new(rx); +// // println!("Streams: {:?}", streams); +// } diff --git a/hyperspace/rollup/src/msgs.rs b/hyperspace/rollup/src/msgs.rs new file mode 100644 index 000000000..51a816887 --- /dev/null +++ b/hyperspace/rollup/src/msgs.rs @@ -0,0 +1,545 @@ +use ibc::core::ics26_routing::msgs::Ics26Envelope; +use ibc_core_channel_types::{ + channel::Order, + msgs::{ + ChannelMsg, MsgAcknowledgement, MsgChannelCloseConfirm, MsgChannelCloseInit, + MsgChannelOpenAck, MsgChannelOpenConfirm, MsgChannelOpenInit, MsgChannelOpenTry, + MsgRecvPacket, MsgTimeout, MsgTimeoutOnClose, PacketMsg, + }, + packet::Packet, + timeout::TimeoutHeight, + Version as ChanVersion, +}; +use ibc_core_client_types::{ + msgs::{ClientMsg, MsgCreateClient, MsgUpdateClient, MsgUpgradeClient}, + Height, +}; +use ibc_core_commitment_types::commitment::{CommitmentPrefix, CommitmentProofBytes}; +use ibc_core_connection_types::{ + msgs::{ + ConnectionMsg, MsgConnectionOpenAck, MsgConnectionOpenConfirm, MsgConnectionOpenInit, + MsgConnectionOpenTry, + }, + Counterparty, +}; +use ibc_core_handler_types::msgs::MsgEnvelope; +use ibc_core_host_types::identifiers::{ChannelId, ClientId, ConnectionId, PortId, Sequence}; +use ibc_new_primitives::{Signer, Timestamp}; +use ibc_proto_new::{google::protobuf::Any, ibc::core::connection::v1::Version}; +use primitives::mock::LocalClientTypes; +use std::str::FromStr; +use prost::Message; + +const ROLLUP_CLIENT_STATE_TYPE_URL: &'static str = cf_solana::proto::ClientState::IBC_TYPE_URL; + +use crate::{ + client_state::convert_old_client_state_to_new, + consensus_state::convert_old_consensus_state_to_new, +}; + +pub fn convert_old_msgs_to_new(messages: Vec>) -> Vec { + let new_messages: Vec = messages + .iter() + .map(|message| match message { + Ics26Envelope::Ics2Msg(msg) => match msg { + ibc::core::ics02_client::msgs::ClientMsg::CreateClient(e) => + MsgEnvelope::Client(ClientMsg::CreateClient(MsgCreateClient::new( + convert_old_client_state_to_new(e.client_state.clone()).into(), + convert_old_consensus_state_to_new(e.consensus_state.clone()).into(), + Signer::from(e.signer.as_ref().to_string()), + ))), + ibc::core::ics02_client::msgs::ClientMsg::UpdateClient(e) => { + let header = match &e.client_message { + pallet_ibc::light_clients::AnyClientMessage::Tendermint(msg) => + ibc_proto::google::protobuf::Any::from(msg.clone()), + pallet_ibc::light_clients::AnyClientMessage::Guest(msg) => + ibc_proto::google::protobuf::Any::from(msg.clone()), + _ => panic!("Not supported"), + }; + let new_any_header = Any { type_url: header.type_url, value: header.value }; + MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { + client_id: ClientId::from_str(e.client_id.as_str()).unwrap(), + client_message: new_any_header, + signer: Signer::from(e.signer.as_ref().to_string()), + })) + }, + ibc::core::ics02_client::msgs::ClientMsg::UpgradeClient(e) => + MsgEnvelope::Client(ClientMsg::UpgradeClient(MsgUpgradeClient { + client_id: ClientId::from_str(e.client_id.as_str()).unwrap(), + upgraded_client_state: convert_old_client_state_to_new( + e.client_state.clone(), + ) + .into(), + upgraded_consensus_state: convert_old_consensus_state_to_new( + e.consensus_state.clone(), + ) + .into(), + proof_upgrade_client: CommitmentProofBytes::try_from( + e.proof_upgrade_client.clone(), + ) + .unwrap(), + proof_upgrade_consensus_state: CommitmentProofBytes::try_from( + e.proof_upgrade_consensus_state.clone(), + ) + .unwrap(), + signer: Signer::from(e.signer.as_ref().to_string()), + })), + }, + Ics26Envelope::Ics3Msg(msg) => match msg { + ibc::core::ics03_connection::msgs::ConnectionMsg::ConnectionOpenInit(e) => + MsgEnvelope::Connection(ConnectionMsg::OpenInit(MsgConnectionOpenInit { + client_id_on_a: ClientId::from_str(e.client_id.as_str()).unwrap(), + counterparty: Counterparty { + client_id: ClientId::from_str(e.counterparty.client_id().as_str()) + .unwrap(), + connection_id: e.counterparty.connection_id.clone().and_then( + |conn_id| Some(ConnectionId::from_str(conn_id.as_str()).unwrap()), + ), + prefix: CommitmentPrefix::try_from( + e.counterparty.prefix().as_bytes().to_vec(), + ) + .unwrap(), + }, + version: e.version.clone().and_then(|ver| { + let raw_version = + ibc_proto::ibc::core::connection::v1::Version::from(ver.clone()); + Some( + Version { + identifier: raw_version.identifier, + features: raw_version.features, + } + .try_into() + .unwrap(), + ) + }), + delay_period: e.delay_period, + signer: Signer::from(e.signer.as_ref().to_string()), + })), + #[allow(deprecated)] + ibc::core::ics03_connection::msgs::ConnectionMsg::ConnectionOpenTry(e) => { + let encoded_cs = ibc_proto::google::protobuf::Any::from( + e.client_state.as_ref().unwrap().clone(), + ); + MsgEnvelope::Connection(ConnectionMsg::OpenTry(MsgConnectionOpenTry { + counterparty: Counterparty { + client_id: ClientId::from_str(e.counterparty.client_id().as_str()) + .unwrap(), + connection_id: e.counterparty.connection_id.clone().and_then( + |conn_id| Some(ConnectionId::from_str(conn_id.as_str()).unwrap()), + ), + prefix: CommitmentPrefix::try_from( + e.counterparty.prefix().as_bytes().to_vec(), + ) + .unwrap(), + }, + delay_period: e.delay_period, + signer: Signer::from(e.signer.as_ref().to_string()), + client_id_on_b: ClientId::from_str(e.client_id.as_str()).unwrap(), + client_state_of_b_on_a: Any { + type_url: ROLLUP_CLIENT_STATE_TYPE_URL.to_string(), + value: encoded_cs.value, + }, + versions_on_a: e + .counterparty_versions + .iter() + .map(|version| { + let raw_version = + ibc_proto::ibc::core::connection::v1::Version::from( + version.clone(), + ); + Version { + identifier: raw_version.identifier, + features: raw_version.features, + } + .try_into() + .unwrap() + }) + .collect(), + proof_conn_end_on_a: CommitmentProofBytes::try_from( + e.proofs.object_proof().as_bytes().to_vec(), + ) + .unwrap(), + proof_client_state_of_b_on_a: CommitmentProofBytes::try_from( + e.proofs.client_proof().clone().unwrap().as_bytes().to_vec(), + ) + .unwrap(), + proof_consensus_state_of_b_on_a: CommitmentProofBytes::try_from( + e.proofs.consensus_proof().unwrap().proof().as_bytes().to_vec(), + ) + .unwrap(), + proofs_height_on_a: Height::new( + e.proofs.height().revision_number, + e.proofs.height().revision_height, + ) + .unwrap(), + consensus_height_of_b_on_a: Height::new( + e.proofs.consensus_proof().unwrap().height().revision_number, + e.proofs.consensus_proof().unwrap().height().revision_height, + ) + .unwrap(), + proof_consensus_state_of_b: if e.host_consensus_state_proof.is_empty() { + None + } else { + Some( + CommitmentProofBytes::try_from( + e.host_consensus_state_proof.clone(), + ) + .unwrap(), + ) + }, + previous_connection_id: String::default(), + })) + }, + ibc::core::ics03_connection::msgs::ConnectionMsg::ConnectionOpenAck(e) => { + let encoded_cs = ibc_proto::google::protobuf::Any::from( + e.client_state.as_ref().unwrap().clone(), + ); + log::info!( + "This is the proof height for consensus state {:?}", + e.proofs.consensus_proof().unwrap().height() + ); + + MsgEnvelope::Connection(ConnectionMsg::OpenAck(MsgConnectionOpenAck { + signer: Signer::from(e.signer.as_ref().to_string()), + conn_id_on_a: ConnectionId::from_str(e.connection_id.as_str()).unwrap(), + conn_id_on_b: ConnectionId::from_str(e.counterparty_connection_id.as_str()) + .unwrap(), + client_state_of_a_on_b: Any { + type_url: ROLLUP_CLIENT_STATE_TYPE_URL.to_string(), + value: encoded_cs.value, + }, + proof_conn_end_on_b: CommitmentProofBytes::try_from( + e.proofs.object_proof().as_bytes().to_vec(), + ) + .unwrap(), + proof_client_state_of_a_on_b: CommitmentProofBytes::try_from( + e.proofs.client_proof().clone().unwrap().as_bytes().to_vec(), + ) + .unwrap(), + proof_consensus_state_of_a_on_b: CommitmentProofBytes::try_from( + e.proofs.consensus_proof().unwrap().proof().as_bytes().to_vec(), + ) + .unwrap(), + proofs_height_on_b: Height::new( + e.proofs.height().revision_number, + e.proofs.height().revision_height, + ) + .unwrap(), + consensus_height_of_a_on_b: Height::new( + e.proofs.consensus_proof().unwrap().height().revision_number, + e.proofs.consensus_proof().unwrap().height().revision_height, + ) + .unwrap(), + version: { + let raw_version = ibc_proto::ibc::core::connection::v1::Version::from( + e.version.clone(), + ); + Version { + identifier: raw_version.identifier, + features: raw_version.features, + } + .try_into() + .unwrap() + }, + proof_consensus_state_of_a: if e.host_consensus_state_proof.is_empty() { + None + } else { + Some( + CommitmentProofBytes::try_from( + e.host_consensus_state_proof.clone(), + ) + .unwrap(), + ) + }, + })) + }, + ibc::core::ics03_connection::msgs::ConnectionMsg::ConnectionOpenConfirm(e) => + MsgEnvelope::Connection(ConnectionMsg::OpenConfirm(MsgConnectionOpenConfirm { + signer: Signer::from(e.signer.as_ref().to_string()), + conn_id_on_b: ConnectionId::from_str(e.connection_id.as_str()).unwrap(), + proof_conn_end_on_a: CommitmentProofBytes::try_from( + e.proofs.object_proof().as_bytes().to_vec(), + ) + .unwrap(), + proof_height_on_a: Height::new( + e.proofs.height().revision_number, + e.proofs.height().revision_height, + ) + .unwrap(), + })), + }, + Ics26Envelope::Ics4ChannelMsg(msg) => match msg { + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenInit(e) => + MsgEnvelope::Channel(ChannelMsg::OpenInit(MsgChannelOpenInit { + port_id_on_a: PortId::from_str(e.port_id.as_str()).unwrap(), + connection_hops_on_a: e + .channel + .connection_hops + .iter() + .map(|hops| ConnectionId::from_str(hops.as_str()).unwrap()) + .collect(), + port_id_on_b: PortId::from_str(e.port_id.as_str()).unwrap(), + ordering: match e.channel.ordering { + ibc::core::ics04_channel::channel::Order::Unordered => Order::Unordered, + ibc::core::ics04_channel::channel::Order::Ordered => Order::Ordered, + }, + signer: Signer::from(e.signer.as_ref().to_string()), + version_proposal: ChanVersion::from_str(&e.channel.version.to_string()) + .unwrap(), + })), + #[allow(deprecated)] + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenTry(e) => + MsgEnvelope::Channel(ChannelMsg::OpenTry(MsgChannelOpenTry { + port_id_on_b: PortId::from_str(e.port_id.as_str()).unwrap(), + connection_hops_on_b: e + .channel + .connection_hops + .iter() + .map(|hops| ConnectionId::from_str(hops.as_str()).unwrap()) + .collect(), + port_id_on_a: PortId::from_str(e.channel.remote.port_id.as_str()).unwrap(), + chan_id_on_a: ChannelId::new( + e.channel.remote.channel_id.unwrap().sequence(), + ), + version_supported_on_a: ChanVersion::from_str( + &e.channel.version.to_string(), + ) + .unwrap(), + proof_chan_end_on_a: CommitmentProofBytes::try_from( + e.proofs.object_proof().as_bytes().to_vec(), + ) + .unwrap(), + proof_height_on_a: Height::new( + e.proofs.height().revision_number, + e.proofs.height().revision_height, + ) + .unwrap(), + ordering: match e.channel.ordering { + ibc::core::ics04_channel::channel::Order::Unordered => Order::Unordered, + ibc::core::ics04_channel::channel::Order::Ordered => Order::Ordered, + }, + signer: Signer::from(e.signer.as_ref().to_string()), + version_proposal: ChanVersion::from_str(&e.channel.version.to_string()) + .unwrap(), + })), + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenAck(e) => + MsgEnvelope::Channel(ChannelMsg::OpenAck(MsgChannelOpenAck { + port_id_on_a: PortId::from_str(e.port_id.as_str()).unwrap(), + chan_id_on_a: ChannelId::new(e.channel_id.sequence()), + proof_chan_end_on_b: CommitmentProofBytes::try_from( + e.proofs.object_proof().as_bytes().to_vec(), + ) + .unwrap(), + proof_height_on_b: Height::new( + e.proofs.height().revision_number, + e.proofs.height().revision_height, + ) + .unwrap(), + signer: Signer::from(e.signer.as_ref().to_string()), + chan_id_on_b: ChannelId::new(e.counterparty_channel_id.sequence()), + version_on_b: ChanVersion::from_str(&e.counterparty_version.to_string()) + .unwrap(), + })), + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenConfirm(e) => + MsgEnvelope::Channel(ChannelMsg::OpenConfirm(MsgChannelOpenConfirm { + port_id_on_b: PortId::from_str(e.port_id.as_str()).unwrap(), + chan_id_on_b: ChannelId::new(e.channel_id.sequence()), + proof_chan_end_on_a: CommitmentProofBytes::try_from( + e.proofs.object_proof().as_bytes().to_vec(), + ) + .unwrap(), + proof_height_on_a: Height::new( + e.proofs.height().revision_number, + e.proofs.height().revision_height, + ) + .unwrap(), + signer: Signer::from(e.signer.as_ref().to_string()), + })), + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelCloseInit(e) => + MsgEnvelope::Channel(ChannelMsg::CloseInit(MsgChannelCloseInit { + port_id_on_a: PortId::from_str(e.port_id.as_str()).unwrap(), + chan_id_on_a: ChannelId::new(e.channel_id.sequence()), + signer: Signer::from(e.signer.as_ref().to_string()), + })), + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelCloseConfirm(e) => + MsgEnvelope::Channel(ChannelMsg::CloseConfirm(MsgChannelCloseConfirm { + port_id_on_b: PortId::from_str(e.port_id.as_str()).unwrap(), + chan_id_on_b: ChannelId::new(e.channel_id.sequence()), + proof_chan_end_on_a: CommitmentProofBytes::try_from( + e.proofs.object_proof().as_bytes().to_vec(), + ) + .unwrap(), + proof_height_on_a: Height::new( + e.proofs.height().revision_number, + e.proofs.height().revision_height, + ) + .unwrap(), + signer: Signer::from(e.signer.as_ref().to_string()), + })), + }, + Ics26Envelope::Ics4PacketMsg(msg) => match msg { + ibc::core::ics04_channel::msgs::PacketMsg::RecvPacket(e) => + MsgEnvelope::Packet(PacketMsg::Recv(MsgRecvPacket { + packet: Packet { + seq_on_a: Sequence::from(e.packet.sequence.0), + port_id_on_a: PortId::from_str(e.packet.source_port.as_str()).unwrap(), + chan_id_on_a: ChannelId::new(e.packet.source_channel.sequence()), + port_id_on_b: PortId::from_str(e.packet.destination_port.as_str()) + .unwrap(), + chan_id_on_b: ChannelId::new(e.packet.destination_channel.sequence()), + data: e.packet.data.clone(), + timeout_height_on_b: if e.packet.timeout_height.revision_number > 0 { + TimeoutHeight::At( + Height::new( + e.packet.timeout_height.revision_number, + e.packet.timeout_height.revision_height, + ) + .unwrap(), + ) + } else { + TimeoutHeight::Never + }, + timeout_timestamp_on_b: Timestamp::from_nanoseconds( + e.packet.timeout_timestamp.nanoseconds(), + ) + .unwrap(), + }, + proof_commitment_on_a: CommitmentProofBytes::try_from( + e.proofs.object_proof().as_bytes().to_vec(), + ) + .unwrap(), + proof_height_on_a: Height::new( + e.proofs.height().revision_number, + e.proofs.height().revision_height, + ) + .unwrap(), + signer: Signer::from(e.signer.as_ref().to_string()), + })), + ibc::core::ics04_channel::msgs::PacketMsg::AckPacket(e) => + MsgEnvelope::Packet(PacketMsg::Ack(MsgAcknowledgement { + packet: Packet { + seq_on_a: Sequence::from(e.packet.sequence.0), + port_id_on_a: PortId::from_str(e.packet.source_port.as_str()).unwrap(), + chan_id_on_a: ChannelId::new(e.packet.source_channel.sequence()), + port_id_on_b: PortId::from_str(e.packet.destination_port.as_str()) + .unwrap(), + chan_id_on_b: ChannelId::new(e.packet.destination_channel.sequence()), + data: e.packet.data.clone(), + timeout_height_on_b: if e.packet.timeout_height.revision_number > 0 { + TimeoutHeight::At( + Height::new( + e.packet.timeout_height.revision_number, + e.packet.timeout_height.revision_height, + ) + .unwrap(), + ) + } else { + TimeoutHeight::Never + }, + timeout_timestamp_on_b: Timestamp::from_nanoseconds( + e.packet.timeout_timestamp.nanoseconds(), + ) + .unwrap(), + }, + signer: Signer::from(e.signer.as_ref().to_string()), + acknowledgement: e + .acknowledgement() + .clone() + .into_bytes() + .try_into() + .unwrap(), + proof_acked_on_b: CommitmentProofBytes::try_from( + e.proofs.object_proof().as_bytes().to_vec(), + ) + .unwrap(), + proof_height_on_b: Height::new( + e.proofs.height().revision_number, + e.proofs.height().revision_height, + ) + .unwrap(), + })), + ibc::core::ics04_channel::msgs::PacketMsg::ToPacket(e) => + MsgEnvelope::Packet(PacketMsg::Timeout(MsgTimeout { + packet: Packet { + seq_on_a: Sequence::from(e.packet.sequence.0), + port_id_on_a: PortId::from_str(e.packet.source_port.as_str()).unwrap(), + chan_id_on_a: ChannelId::new(e.packet.source_channel.sequence()), + port_id_on_b: PortId::from_str(e.packet.destination_port.as_str()) + .unwrap(), + chan_id_on_b: ChannelId::new(e.packet.destination_channel.sequence()), + data: e.packet.data.clone(), + timeout_height_on_b: if e.packet.timeout_height.revision_number > 0 { + TimeoutHeight::At( + Height::new( + e.packet.timeout_height.revision_number, + e.packet.timeout_height.revision_height, + ) + .unwrap(), + ) + } else { + TimeoutHeight::Never + }, + timeout_timestamp_on_b: Timestamp::from_nanoseconds( + e.packet.timeout_timestamp.nanoseconds(), + ) + .unwrap(), + }, + signer: Signer::from(e.signer.as_ref().to_string()), + proof_height_on_b: Height::new( + e.proofs.height().revision_number, + e.proofs.height().revision_height, + ) + .unwrap(), + next_seq_recv_on_b: Sequence::from(e.next_sequence_recv.0), + proof_unreceived_on_b: CommitmentProofBytes::try_from( + e.proofs.object_proof().as_bytes().to_vec(), + ) + .unwrap(), + })), + ibc::core::ics04_channel::msgs::PacketMsg::ToClosePacket(e) => + MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(MsgTimeoutOnClose { + packet: Packet { + seq_on_a: Sequence::from(e.packet.sequence.0), + port_id_on_a: PortId::from_str(e.packet.source_port.as_str()).unwrap(), + chan_id_on_a: ChannelId::new(e.packet.source_channel.sequence()), + port_id_on_b: PortId::from_str(e.packet.destination_port.as_str()) + .unwrap(), + chan_id_on_b: ChannelId::new(e.packet.destination_channel.sequence()), + data: e.packet.data.clone(), + timeout_height_on_b: if e.packet.timeout_height.revision_number > 0 { + TimeoutHeight::At( + Height::new( + e.packet.timeout_height.revision_number, + e.packet.timeout_height.revision_height, + ) + .unwrap(), + ) + } else { + TimeoutHeight::Never + }, + timeout_timestamp_on_b: Timestamp::from_nanoseconds( + e.packet.timeout_timestamp.nanoseconds(), + ) + .unwrap(), + }, + signer: Signer::from(e.signer.as_ref().to_string()), + proof_height_on_b: Height::new( + e.proofs.height().revision_number, + e.proofs.height().revision_height, + ) + .unwrap(), + next_seq_recv_on_b: Sequence::from(e.next_sequence_recv.0), + proof_unreceived_on_b: CommitmentProofBytes::try_from( + e.proofs.object_proof().as_bytes().to_vec(), + ) + .unwrap(), + proof_close_on_b: CommitmentProofBytes::try_from( + e.proofs.other_proof().clone().unwrap().as_bytes().to_vec(), + ) + .unwrap(), + })), + }, + }) + .collect(); + new_messages +} diff --git a/hyperspace/rollup/src/test_provider.rs b/hyperspace/rollup/src/test_provider.rs new file mode 100644 index 000000000..709a9dc2f --- /dev/null +++ b/hyperspace/rollup/src/test_provider.rs @@ -0,0 +1,79 @@ +use crate::{client::RollupClient, error::Error, events}; +use anchor_client::{ + solana_client::{ + pubsub_client::PubsubClient, + rpc_config::{ + RpcBlockSubscribeConfig, RpcBlockSubscribeFilter, RpcTransactionLogsConfig, + RpcTransactionLogsFilter, + }, + }, + solana_sdk::commitment_config::CommitmentConfig, +}; +use core::pin::Pin; +use futures::Stream; +use ibc::{ + applications::transfer::{msgs::transfer::MsgTransfer, PrefixedCoin}, + core::ics24_host::identifier::ChannelId, +}; +use primitives::TestProvider; +use tokio::sync::mpsc::unbounded_channel; + +#[async_trait::async_trait] +impl TestProvider for RollupClient { + /// Initiate an ibc transfer on chain. + async fn send_transfer(&self, msg: MsgTransfer) -> Result<(), Self::Error> { + let hash = self.send_transfer_inner(msg).await?; + log::info!(target: "hyperspace_cosmos", "🤝 Transfer transaction confirmed with hash: {}", hash); + Ok(()) + } + + /// Send a packet on an ordered channel + async fn send_ordered_packet( + &self, + _channel_id: ChannelId, + _timeout: pallet_ibc::Timeout, + ) -> Result<(), Self::Error> { + Err(Error::Custom("send_ordered_packet is not implemented yet".to_string())) + } + + /// Returns a stream that yields chain Block number + async fn subscribe_blocks(&self) -> Pin + Send + Sync>> { + let (tx, rx) = unbounded_channel(); + let ws_url = self.ws_url.clone(); + let program_id = self.solana_ibc_program_id; + tokio::task::spawn_blocking(move || { + let (_block_subscription, receiver) = PubsubClient::block_subscribe( + &ws_url, + // RpcBlockSubscribeFilter::MentionsAccountOrProgram(program_id.to_string()), + RpcBlockSubscribeFilter::All, + Some(RpcBlockSubscribeConfig { + commitment: Some(CommitmentConfig::finalized()), + ..Default::default() + }), + ) + .unwrap(); + + loop { + match receiver.recv() { + Ok(logs) => { + let mut broke = false; + let _ = tx.send(u64::from(logs.value.slot)).map_err(|_| broke = true); + if broke { + break; + } + }, + Err(err) => { + panic!("{}", format!("Disconnected: {err}")); + }, + } + } + }); + + let streams = tokio_stream::wrappers::UnboundedReceiverStream::new(rx); + Box::pin(streams) + } + + async fn increase_counters(&mut self) -> Result<(), Self::Error> { + unimplemented!() + } +} diff --git a/hyperspace/rollup/src/utils.rs b/hyperspace/rollup/src/utils.rs new file mode 100644 index 000000000..0dba07792 --- /dev/null +++ b/hyperspace/rollup/src/utils.rs @@ -0,0 +1,121 @@ +use anchor_client::solana_sdk::{ + ed25519_instruction::SIGNATURE_OFFSETS_SERIALIZED_SIZE, instruction::Instruction, +}; +use itertools::izip; +use sigverify::ed25519_program::{new_instruction, Entry, SignatureOffsets}; +use tendermint_light_client_verifier_new::types::Commit; +use tendermint_new::{ + block::CommitSig, + vote::{ValidatorIndex, Vote}, +}; + +pub fn non_absent_vote( + commit_sig: &CommitSig, + validator_index: ValidatorIndex, + commit: &Commit, +) -> Option { + let (validator_address, timestamp, signature, block_id) = match commit_sig { + CommitSig::BlockIdFlagAbsent { .. } => return None, + CommitSig::BlockIdFlagCommit { validator_address, timestamp, signature } => + (*validator_address, *timestamp, signature, Some(commit.block_id)), + CommitSig::BlockIdFlagNil { validator_address, timestamp, signature } => + (*validator_address, *timestamp, signature, None), + }; + + Some(Vote { + vote_type: tendermint_new::vote::Type::Precommit, + height: commit.height, + round: commit.round, + block_id, + timestamp: Some(timestamp), + validator_address, + validator_index, + signature: signature.clone(), + extension: Default::default(), + extension_signature: None, + }) +} + +/// Solana sdk only accepts a keypair to form ed25519 instruction. +/// Until they implement a method which accepts a pubkey and signature instead of keypair +/// we have to use the below method instead. +/// +/// Reference: https://github.com/solana-labs/solana/pull/32806 +pub fn new_ed25519_instruction_with_signature( + pubkeys: Vec>, + signatures: Vec>, + messages: Vec>, +) -> Instruction { + use anchor_client::solana_sdk::ed25519_instruction::{ + DATA_START, PUBKEY_SERIALIZED_SIZE, SIGNATURE_SERIALIZED_SIZE, + }; + use bytemuck::bytes_of; + assert_eq!(signatures.len(), messages.len()); + let num_signatures: u8 = signatures.len().try_into().unwrap(); + let mut instruction_data = Vec::new(); + instruction_data.extend_from_slice(&[num_signatures, 0]); + let mut offset = 0; + for (index, _) in signatures.iter().enumerate() { + let signature = &signatures[index]; + let message = &messages[index]; + let pubkey = &pubkeys[index]; + assert_eq!(pubkey.len(), PUBKEY_SERIALIZED_SIZE); + assert_eq!(signature.len(), SIGNATURE_SERIALIZED_SIZE); + + let public_key_offset = offset + DATA_START; + let signature_offset = public_key_offset.saturating_add(PUBKEY_SERIALIZED_SIZE); + let message_data_offset = signature_offset.saturating_add(SIGNATURE_SERIALIZED_SIZE); + + let offsets = SignatureOffsets { + signature_offset: signature_offset as u16, + signature_instruction_index: u16::MAX, + public_key_offset: public_key_offset as u16, + public_key_instruction_index: u16::MAX, + message_data_offset: message_data_offset as u16, + message_data_size: message.len() as u16, + message_instruction_index: u16::MAX, + }; + let current_instruction = [bytes_of(&offsets), &pubkey, &signature, &message].concat(); + instruction_data.extend_from_slice(¤t_instruction); + offset += SIGNATURE_OFFSETS_SERIALIZED_SIZE + .saturating_add(SIGNATURE_SERIALIZED_SIZE) + .saturating_add(PUBKEY_SERIALIZED_SIZE) + .saturating_add(message.len()) + } + + // let instruction = + // [&[num_signatures, 0], bytes_of(&offsets), pubkey, signature, message].concat(); + + Instruction { + program_id: anchor_client::solana_sdk::ed25519_program::id(), + accounts: vec![], + data: instruction_data, + } +} + +pub fn ed25519_signature_payload(entries: Vec) -> Option { + new_instruction(&entries) +} + +/// Displays the error if present, waits for few seconds and +/// retries execution. +/// +/// The error is usually due to load on rpc which is solved +/// by waiting a few seconds. +#[macro_export] +macro_rules! skip_fail { + ($res:expr) => { + match $res { + Ok(val) => val, + Err(e) => { + use crate::Duration; + use std::thread::sleep; + log::error!("{:?}", e); + sleep(Duration::from_secs(2)); + continue; + }, + } + }; +} + +pub(crate) use skip_fail; diff --git a/hyperspace/solana/Cargo.toml b/hyperspace/solana/Cargo.toml index ea527aeea..b61577c56 100644 --- a/hyperspace/solana/Cargo.toml +++ b/hyperspace/solana/Cargo.toml @@ -54,17 +54,17 @@ bs58 = { version = "0.5.0", features = ["alloc"] } bincode = { version = "1.3.3" } # New IBC -ibc-new-primitives = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false, features = ["borsh", "serde"] , package="ibc-primitives" } -ibc-core-host-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false, features = ["borsh", "serde"]} -ibc-core-handler-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-client-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-connection-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-channel-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-app-transfer-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-commitment-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-client-tendermint-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-client-wasm-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false} -# ibc-testkit = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } +ibc-new-primitives = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false, features = ["borsh", "serde"] , package="ibc-primitives" } +ibc-core-host-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false, features = ["borsh", "serde"]} +ibc-core-handler-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-client-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-connection-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-channel-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-app-transfer-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-commitment-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-client-tendermint-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-client-wasm-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false} +# ibc-testkit = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } ibc-proto-new = { version = "0.41.0", default-features = false, package="ibc-proto" } ics23 = { version = "0.11.1" } @@ -81,20 +81,24 @@ ibc-rpc = { path = "../../contracts/pallet-ibc/rpc" } pallet-ibc = { path = "../../contracts/pallet-ibc", features = ["testing"]} # Trie -lib = { path = "../../../emulated-light-client/common/lib" } -memory = { path = "../../../emulated-light-client/common/memory" } -sealable-trie = { path = "../../../emulated-light-client/common/sealable-trie" ,features = ["borsh"] } -stdx = { path = "../../../emulated-light-client/common/stdx" } -solana-trie = { path = "../../../emulated-light-client/solana/trie" } -trie-ids = { path = "../../../emulated-light-client/common/trie-ids" ,features = ["borsh"] } +lib = { git = "https://github.com/ComposableFi/emulated-light-client/" } +memory = { git = "https://github.com/ComposableFi/emulated-light-client/" } +sealable-trie = { git = "https://github.com/ComposableFi/emulated-light-client/" ,features = ["borsh"] } +stdx = { git = "https://github.com/ComposableFi/emulated-light-client/" } +solana-trie = { git = "https://github.com/ComposableFi/emulated-light-client/" } +trie-ids = { git = "https://github.com/ComposableFi/emulated-light-client/" ,features = ["borsh"] } cf-guest = { path = "../../light-clients/cf-guest", default-features = false } -cf-guest-og = { path = "../../../emulated-light-client/common/cf-guest" ,package = "cf-guest" } -guestchain = { path = "../../../emulated-light-client/common/guestchain" ,default-features = false } +cf-guest-og = { git = "https://github.com/ComposableFi/emulated-light-client/" ,package = "cf-guest" } +guestchain = { git = "https://github.com/ComposableFi/emulated-light-client/" ,default-features = false } + +cf-solana = { path = "../../light-clients/cf-solana", default-features = false} +cf-solana-og = { git = "https://github.com/ComposableFi/emulated-light-client/", package = "cf-solana", features = ["serde"] } #Contract -solana-ibc = { path = "../../../emulated-light-client/solana/solana-ibc/programs/solana-ibc" ,features = ["no-entrypoint"]} -solana-write-account = { path = "../../../emulated-light-client/solana/write-account" ,features = ["library"] } -solana-signature-verifier = { path = "../../../emulated-light-client/solana/signature-verifier" } +solana-ibc = { git = "https://github.com/ComposableFi/emulated-light-client/" ,features = ["no-entrypoint"]} +solana-write-account = { git = "https://github.com/ComposableFi/emulated-light-client/", features = ["library"] } +solana-witnessed-trie = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false, features = ["api"] } +solana-signature-verifier = { git = "https://github.com/ComposableFi/emulated-light-client/" } tracing = "0.1.36" diff --git a/hyperspace/solana/src/client.rs b/hyperspace/solana/src/client.rs index b9ff020e3..9ceed569a 100644 --- a/hyperspace/solana/src/client.rs +++ b/hyperspace/solana/src/client.rs @@ -120,6 +120,7 @@ pub struct SolanaClient { pub trie_db_path: String, // Sets whether to use JITO or RPC for submitting transactions pub transaction_sender: TransactionSender, + pub is_transaction_processing: Arc>, } #[derive(std::fmt::Debug, Serialize, Deserialize, Clone)] @@ -211,6 +212,13 @@ impl SolanaClient { trie } + pub fn get_witness_key(&self, trie_key: &Pubkey) -> Pubkey { + wittrie::api::find_witness_account( + &self.solana_ibc_program_id, + trie_key, + ).unwrap().0 + } + pub fn get_ibc_storage_key(&self) -> Pubkey { let storage_seeds = &[SOLANA_IBC_STORAGE_SEED]; let ibc_storage = @@ -243,7 +251,7 @@ impl SolanaClient { &self, at: u64, require_proof: bool, - ) -> (solana_trie::TrieAccount>, bool) { + ) -> (solana_trie::TrieAccount, ()>, bool) { let connection = self.get_db(); if require_proof { let row = connection.query_row("SELECT * FROM Trie WHERE height=?1", [at], |row| { @@ -364,6 +372,7 @@ impl SolanaClient { channel_whitelist: Arc::new(Mutex::new(config.channel_whitelist.into_iter().collect())), trie_db_path: config.trie_db_path, transaction_sender, + is_transaction_processing: Arc::new(Mutex::new(false)) }) } @@ -383,6 +392,7 @@ impl SolanaClient { let mut signature = String::new(); let solana_ibc_storage_key = self.get_ibc_storage_key(); let trie_key = self.get_trie_key(); + let witness_key = self.get_witness_key(&trie_key); let chain_key = self.get_chain_key(); // while tries < max_tries { // println!("Try For Tx: {}", tries); @@ -610,6 +620,7 @@ deserialize consensus state" receiver: Some(self.solana_ibc_program_id), storage: solana_ibc_storage_key, trie: trie_key, + witness: witness_key, chain: chain_key, system_program: system_program::ID, mint_authority: Some(self.solana_ibc_program_id), @@ -684,7 +695,7 @@ deserialize consensus state" ); let (escrow_account, token_mint, receiver_account, receiver_address) = get_accounts( - token.denom.clone(), + &token.denom, self.solana_ibc_program_id, receiver, port_id, @@ -715,6 +726,7 @@ deserialize consensus state" receiver: receiver_account, storage: solana_ibc_storage_key, trie: trie_key, + witness: witness_key, chain: chain_key, system_program: system_program::ID, mint_authority: Some(mint_authority), @@ -756,7 +768,7 @@ deserialize consensus state" token ); let (escrow_account, token_mint, sender_account, sender_address) = get_accounts( - token.denom.clone(), + &token.denom, self.solana_ibc_program_id, &sender_account, port_id, @@ -787,6 +799,7 @@ deserialize consensus state" receiver: sender_account, storage: solana_ibc_storage_key, trie: trie_key, + witness: witness_key, chain: chain_key, system_program: system_program::ID, mint_authority: Some(mint_authority), @@ -827,6 +840,7 @@ deserialize consensus state" receiver: Some(sender), storage: solana_ibc_storage_key, trie: trie_key, + witness: witness_key, chain: chain_key, system_program: system_program::ID, mint_authority: Some(self.solana_ibc_program_id), @@ -886,6 +900,7 @@ deserialize consensus state" receiver: Some(self.solana_ibc_program_id), storage: solana_ibc_storage_key, trie: trie_key, + witness: witness_key, chain: chain_key, system_program: system_program::ID, mint_authority: Some(self.solana_ibc_program_id), @@ -956,6 +971,7 @@ deserialize consensus state" // Build, sign, and send program instruction let solana_ibc_storage_key = self.get_ibc_storage_key(); let trie_key = self.get_trie_key(); + let witness_key = self.get_witness_key(&trie_key); let chain_key = self.get_chain_key(); let mint_authority = self.get_mint_auth_key(); @@ -1036,6 +1052,7 @@ deserialize consensus state" receiver: Some(authority.pubkey()), storage: solana_ibc_storage_key, trie: trie_key, + witness: witness_key, chain: chain_key, system_program: system_program::ID, mint_authority: Some(mint_authority), @@ -1070,7 +1087,7 @@ deserialize consensus state" } pub async fn get_accounts( - denom: PrefixedDenom, + denom: &PrefixedDenom, program_id: Pubkey, receiver: &String, port_id: &ibc_core_host_types::identifiers::PortId, @@ -1078,7 +1095,9 @@ pub async fn get_accounts( rpc: &AsyncRpcClient, refund: bool, ) -> Result<(Option, Option, Option, Option), ParsePubkeyError> { - if Pubkey::from_str(&denom.base_denom.to_string()).is_ok() { + if (!refund && is_receiver_chain_source(port_id.clone(), channel_id.clone(), denom) + || (refund && is_sender_chain_source(port_id.clone(), channel_id.clone(), denom))) + { log::info!("Receiver chain source"); let hashed_denom = CryptoHash::digest(denom.base_denom.as_str().as_bytes()); let escrow_seeds = ["escrow".as_bytes(), hashed_denom.as_ref()]; diff --git a/hyperspace/solana/src/client_state.rs b/hyperspace/solana/src/client_state.rs index fbadcaa9b..92f7ee61b 100644 --- a/hyperspace/solana/src/client_state.rs +++ b/hyperspace/solana/src/client_state.rs @@ -57,7 +57,26 @@ pub fn convert_new_client_state_to_old( // Some(Height::new(height.revision_number(), height.revision_height())) // }), // }), + solana_ibc::client_state::AnyClientState::Rollup(cs) => { + AnyClientState::Rollup(cf_solana::ClientState(cf_solana_og::ClientState { + latest_slot: cs.latest_slot, + witness_account: cs.witness_account, + trusting_period_ns: cs.trusting_period_ns, + is_frozen: cs.is_frozen, + })) + }, + solana_ibc::client_state::AnyClientState::Guest(cs) => { + AnyClientState::Guest(cf_guest::ClientState(cf_guest_og::ClientState::new( + cs.genesis_hash, + cs.latest_height, + cs.trusting_period_ns, + cs.epoch_commitment, + Some(cs.prev_epoch_commitment), + cs.is_frozen, + ))) + }, solana_ibc::client_state::AnyClientState::Wasm(_) => unimplemented!(), + _ => unimplemented!(), } } @@ -167,6 +186,26 @@ pub fn convert_old_client_state_to_new( _ => panic!("Invalid state {:?}", cs), } }, + AnyClientState::Rollup(cs) => { + let cs = cs.0; + solana_ibc::client_state::AnyClientState::Rollup(cf_solana_og::ClientState { + latest_slot: cs.latest_slot, + witness_account: cs.witness_account, + trusting_period_ns: cs.trusting_period_ns, + is_frozen: cs.is_frozen, + }) + }, + AnyClientState::Guest(cs) => { + let cs = cs.0; + solana_ibc::client_state::AnyClientState::Guest(cf_guest_og::ClientState::new( + cs.genesis_hash, + cs.latest_height, + cs.trusting_period_ns, + cs.epoch_commitment, + Some(cs.prev_epoch_commitment), + cs.is_frozen, + )) + }, _ => panic!("Client state not supported"), } } diff --git a/hyperspace/solana/src/consensus_state.rs b/hyperspace/solana/src/consensus_state.rs index 9f986dbfd..a5b7cf9b7 100644 --- a/hyperspace/solana/src/consensus_state.rs +++ b/hyperspace/solana/src/consensus_state.rs @@ -38,8 +38,16 @@ pub fn convert_new_consensus_state_to_old( // }, // root: CommitmentRoot { bytes: cs.root.into_vec() }, // }), - solana_ibc::consensus_state::AnyConsensusState::Wasm(_) => - panic!("Guest consensus not supported"), + solana_ibc::consensus_state::AnyConsensusState::Wasm(_) => { + panic!("Guest consensus not supported") + }, + solana_ibc::consensus_state::AnyConsensusState::Rollup(cs) => { + AnyConsensusState::Rollup(cf_solana::ConsensusState(cf_solana_og::ConsensusState { + trie_root: cs.trie_root, + timestamp_sec: cs.timestamp_sec, + })) + }, + _ => unimplemented!() } } @@ -66,6 +74,13 @@ pub fn convert_old_consensus_state_to_new( .unwrap(), ) }, + AnyConsensusState::Rollup(cs) => { + let cs = cs.0; + solana_ibc::consensus_state::AnyConsensusState::Rollup(cf_solana_og::ConsensusState { + trie_root: cs.trie_root, + timestamp_sec: cs.timestamp_sec, + }) + }, // AnyConsensusState::Mock(cs) => solana_ibc::consensus_state::AnyConsensusState::Mock( // ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState { // header: ibc_testkit::testapp::ibc::clients::mock::header::MockHeader { diff --git a/hyperspace/solana/src/events.rs b/hyperspace/solana/src/events.rs index a565d3771..7351ed5db 100644 --- a/hyperspace/solana/src/events.rs +++ b/hyperspace/solana/src/events.rs @@ -151,12 +151,10 @@ pub fn convert_new_event_to_old( height, client_id: ClientId::from_str(e.client_id_on_a().as_str()).unwrap(), counterparty_client_id: ClientId::from_str(e.client_id_on_b().as_str()).unwrap(), - counterparty_connection_id: Some( - ConnectionId::from_str(e.conn_id_on_b().as_str()).unwrap(), - ), - connection_id: e + counterparty_connection_id: e .conn_id_on_a() .and_then(|conn| Some(ConnectionId::from_str(conn.as_str()).unwrap())), + connection_id: Some(ConnectionId::from_str(e.conn_id_on_b().as_str()).unwrap()), }); Some(IbcEvent::OpenConfirmConnection(eve)) }, @@ -174,11 +172,13 @@ pub fn convert_new_event_to_old( ibc_core_handler_types::events::IbcEvent::OpenTryChannel(e) => { let eve = ChanOpenTry { height, - port_id: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), - channel_id: Some(ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap()), + port_id: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + channel_id: Some(ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap()), connection_id: ConnectionId::from_str(e.conn_id_on_b().as_str()).unwrap(), - counterparty_port_id: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), - counterparty_channel_id: None, + counterparty_port_id: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + counterparty_channel_id: Some( + ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap(), + ), }; Some(IbcEvent::OpenTryChannel(eve)) }, @@ -189,18 +189,22 @@ pub fn convert_new_event_to_old( channel_id: Some(ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap()), connection_id: ConnectionId::from_str(e.conn_id_on_a().as_str()).unwrap(), counterparty_port_id: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), - counterparty_channel_id: None, + counterparty_channel_id: Some( + ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), + ), }; Some(IbcEvent::OpenAckChannel(eve)) }, ibc_core_handler_types::events::IbcEvent::OpenConfirmChannel(e) => { let eve = ChanOpenConfirm { height, - port_id: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), - channel_id: Some(ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap()), + port_id: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), + channel_id: Some(ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap()), connection_id: ConnectionId::from_str(e.conn_id_on_b().as_str()).unwrap(), - counterparty_port_id: PortId::from_str(e.port_id_on_b().as_str()).unwrap(), - counterparty_channel_id: None, + counterparty_port_id: PortId::from_str(e.port_id_on_a().as_str()).unwrap(), + counterparty_channel_id: Some( + ChannelId::from_str(e.chan_id_on_a().as_str()).unwrap(), + ), }; Some(IbcEvent::OpenConfirmChannel(eve)) }, @@ -237,8 +241,9 @@ pub fn convert_new_event_to_old( destination_channel: ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), data: e.packet_data().to_vec(), timeout_height: match e.timeout_height_on_b() { - ibc_core_channel_types::timeout::TimeoutHeight::Never => - Height { revision_height: 0, revision_number: 0 }, + ibc_core_channel_types::timeout::TimeoutHeight::Never => { + Height { revision_height: 0, revision_number: 0 } + }, ibc_core_channel_types::timeout::TimeoutHeight::At(h) => Height { revision_height: h.revision_height(), revision_number: h.revision_number(), @@ -263,8 +268,9 @@ pub fn convert_new_event_to_old( destination_channel: ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), data: e.packet_data().to_vec(), timeout_height: match e.timeout_height_on_b() { - ibc_core_channel_types::timeout::TimeoutHeight::Never => - Height { revision_height: 0, revision_number: 0 }, + ibc_core_channel_types::timeout::TimeoutHeight::Never => { + Height { revision_height: 0, revision_number: 0 } + }, ibc_core_channel_types::timeout::TimeoutHeight::At(h) => Height { revision_height: h.revision_height(), revision_number: h.revision_number(), @@ -289,8 +295,9 @@ pub fn convert_new_event_to_old( destination_channel: ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), data: e.packet_data().to_vec(), timeout_height: match e.timeout_height_on_b() { - ibc_core_channel_types::timeout::TimeoutHeight::Never => - Height { revision_height: 0, revision_number: 0 }, + ibc_core_channel_types::timeout::TimeoutHeight::Never => { + Height { revision_height: 0, revision_number: 0 } + }, ibc_core_channel_types::timeout::TimeoutHeight::At(h) => Height { revision_height: h.revision_height(), revision_number: h.revision_number(), @@ -316,8 +323,9 @@ pub fn convert_new_event_to_old( destination_channel: ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), data: Vec::new(), timeout_height: match e.timeout_height_on_b() { - ibc_core_channel_types::timeout::TimeoutHeight::Never => - Height { revision_height: 0, revision_number: 0 }, + ibc_core_channel_types::timeout::TimeoutHeight::Never => { + Height { revision_height: 0, revision_number: 0 } + }, ibc_core_channel_types::timeout::TimeoutHeight::At(h) => Height { revision_height: h.revision_height(), revision_number: h.revision_number(), @@ -342,8 +350,9 @@ pub fn convert_new_event_to_old( destination_channel: ChannelId::from_str(e.chan_id_on_b().as_str()).unwrap(), data: Vec::new(), // Not sure about this timeout_height: match e.timeout_height_on_b() { - ibc_core_channel_types::timeout::TimeoutHeight::Never => - Height { revision_height: 0, revision_number: 0 }, + ibc_core_channel_types::timeout::TimeoutHeight::Never => { + Height { revision_height: 0, revision_number: 0 } + }, ibc_core_channel_types::timeout::TimeoutHeight::At(h) => Height { revision_height: h.revision_height(), revision_number: h.revision_number(), @@ -619,7 +628,6 @@ pub async fn get_signatures_upto_height( }; let (events, _proof_height) = get_events_from_logs(logs.clone()); let (ibc_events, _) = get_ibc_events_from_logs(logs); - all_ibc_events.extend(ibc_events); for event in events { match event { solana_ibc::events::Event::NewBlock(e) => { @@ -648,6 +656,7 @@ pub async fn get_signatures_upto_height( if current_height < upto_height { break; } + all_ibc_events.extend(ibc_events); } } ( @@ -695,7 +704,7 @@ pub async fn get_previous_transactions( .get_signatures_for_address_with_config( &search_address, // Since ibc storage is only used for ibc and not for guest chain GetConfirmedSignaturesForAddress2Config { - limit: Some(200), + limit: Some(100), before: before_hash, commitment: Some(CommitmentConfig::confirmed()), ..Default::default() @@ -785,16 +794,19 @@ pub fn testing_signatures() { #[tokio::test] pub async fn testing_events_final() { - let rpc = RpcClient::new("http://127.0.0.1:8899".to_string()); + let rpc = RpcClient::new( + "https://mainnet.helius-rpc.com/?api-key=5ae782d8-6bf6-489c-b6df-ef7e6289e193".to_string(), + ); let mut last_hash = None; loop { let (events, prev) = get_previous_transactions( &rpc, - Pubkey::from_str("9FeHRJLHJSEw4dYZrABHWTRKruFjxDmkLtPmhM5WFYL7").unwrap(), + Pubkey::from_str("2HLLVco5HvwWriNbUhmVwA2pCetRkpgrqwnjcsZdyTKT").unwrap(), last_hash, - SearchIn::IBC, + SearchIn::GuestChain, ) .await; + println!("Events {:?} and prev {}", events, prev); if events.is_empty() { println!("No events found"); break; @@ -823,16 +835,17 @@ pub fn testing_events() { .iter() .filter_map(|tx| match tx { solana_ibc::events::Event::IbcEvent(e) => match e { - ibc_core_handler_types::events::IbcEvent::WriteAcknowledgement(packet) => - if packet.chan_id_on_a().as_str() == &channel_id.to_string() && - packet.port_id_on_a().as_str() == port_id.as_str() && - seqs.iter().find(|&&seq| packet.seq_on_a().value() == seq).is_some() + ibc_core_handler_types::events::IbcEvent::WriteAcknowledgement(packet) => { + if packet.chan_id_on_a().as_str() == &channel_id.to_string() + && packet.port_id_on_a().as_str() == port_id.as_str() + && seqs.iter().find(|&&seq| packet.seq_on_a().value() == seq).is_some() { println!("We found packet"); Some(packet) } else { None - }, + } + }, _ => None, }, _ => None, diff --git a/hyperspace/solana/src/lib.rs b/hyperspace/solana/src/lib.rs index 9d4fa3941..08f168f3c 100644 --- a/hyperspace/solana/src/lib.rs +++ b/hyperspace/solana/src/lib.rs @@ -160,9 +160,11 @@ impl IbcProvider for SolanaClient { T: Chain, { log::info!("Came into solana lts events"); - let (finality_blockhash, finality_height) = match finality_event { - FinalityEvent::Guest { blockhash, block_height } => (blockhash, block_height), - }; + // let (finality_blockhash, finality_height) = match finality_event { + // FinalityEvent::Guest { blockhash, block_height } => (blockhash, block_height), + // }; + let chain_account = self.get_chain_storage().await; + let finality_height = chain_account.head().unwrap().block_height.into(); log::info!("This is solaan height {:?}", finality_height); let client_id = self.client_id(); let latest_cp_height = counterparty.latest_height_and_timestamp().await?.0; @@ -210,7 +212,6 @@ impl IbcProvider for SolanaClient { }) .collect(); - let chain_account = self.get_chain_storage().await; let mut updates = Vec::new(); let mut rev_all_signatures = all_signatures.clone(); // Reversing so that updates are sent in ascending order of their height. @@ -220,6 +221,11 @@ impl IbcProvider for SolanaClient { && u64::from(block_header.block_height) != finality_height) || epoch.is_none() { + log::info!( + "Skipping for height {} and finality height {}", + block_header.block_height, + finality_height + ); continue; } @@ -308,6 +314,10 @@ impl IbcProvider for SolanaClient { if !block_events.is_empty() || time_since_last_update > MIN_TIME_UNTIL_UPDATE * 1_000_000_000 { + log::info!( + "--------------------------Updating at {}---------------------------", + finality_height + ); UpdateType::Mandatory } else { UpdateType::Optional @@ -381,7 +391,7 @@ impl IbcProvider for SolanaClient { consensus_height: Height, ) -> Result { use ibc_proto_new::Protobuf; - let (trie, at_height) = self.get_trie(at.revision_height + 1, true).await; + let (trie, at_height) = self.get_trie(at.revision_height, true).await; let storage = self.get_ibc_storage().await; let revision_height = consensus_height.revision_height; let revision_number = consensus_height.revision_number; @@ -431,7 +441,7 @@ deserialize consensus state" log::info!("this is consensus state {:?}", consensus_state); log::info!("This is inner any consensus state {:?}", inner_any); let chain_account = self.get_chain_storage().await; - let block_header = if !self.common_state.handshake_completed { + let block_header = if !self.common_state.handshake_completed && at_height { log::info!("Fetching previous block header"); events::get_header_from_height( self.rpc_client(), @@ -446,11 +456,12 @@ deserialize consensus state" }; // let block_header = chain_account.head().unwrap().clone(); log::info!( - "proof {:?} state root {:?}, trie key {:?} and value {:?}", + "proof {:?} state root {:?}, trie key {:?} and value {:?} and trie root {:?}", consensus_state_proof, block_header.state_root, consensus_state_trie_key, - val + val, + trie.hash() ); let result = consensus_state_proof.verify( &block_header.state_root, @@ -471,7 +482,7 @@ deserialize consensus state" client_id: ClientId, ) -> Result { log::info!("Quering solana client state at height {:?} {:?}", at, client_id); - let (trie, at_height) = self.get_trie(at.revision_height + 1, true).await; + let (trie, at_height) = self.get_trie(at.revision_height, true).await; let storage = self.get_ibc_storage().await; let new_client_id = ibc_core_host_types::identifiers::ClientId::from_str(client_id.as_str()).unwrap(); @@ -483,7 +494,7 @@ deserialize consensus state" let client_state = events::get_client_state_at_height( self.rpc_client(), self.solana_ibc_program_id, - at.revision_height + 1, + at.revision_height, ) .await .unwrap_or_else(|| { @@ -510,7 +521,7 @@ deserialize client state" // log::info!("This is inner any client state {:?}", inner_any); let any_client_state = convert_new_client_state_to_old(client_state); let chain_account = self.get_chain_storage().await; - let block_header = if !self.common_state.handshake_completed { + let block_header = if !self.common_state.handshake_completed && at_height { log::info!("Fetching previous block header"); events::get_header_from_height( self.rpc_client(), @@ -531,6 +542,14 @@ deserialize client state" client_state_trie_key, val ); + let state_any = any_client_state.clone().encode_vec(); + log::info!( + "This is any client state {:?} and any client state {:?}", + state_any, + Into::::into(any_client_state.clone()) + ); + let hash = cf_guest::digest_with_client_id(&client_id, state_any.as_slice()); + log::info!("hash proof {:?} and derived hash {}", val, hash); let result = client_state_proof.verify( &block_header.state_root, &client_state_trie_key, @@ -550,7 +569,15 @@ deserialize client state" connection_id: ConnectionId, ) -> Result { use ibc_proto_new::Protobuf; - let (trie, at_height) = self.get_trie(at.revision_height + 1, true).await; + let chain_account = self.get_chain_storage().await; + let proof_height = if u64::from(chain_account.head().unwrap().block_height) == at.revision_height { + log::info!("Using existing proof height {}", at.revision_height); + at.revision_height + } else { + log::info!("Using incremented proof height {}", at.revision_height + 1); + at.revision_height + }; + let (trie, at_height) = self.get_trie(proof_height, true).await; let storage = self.get_ibc_storage().await; let connection_idx = ConnectionIdx::try_from( ibc_core_host_types::identifiers::ConnectionId::from_str(connection_id.as_str()) @@ -586,6 +613,10 @@ deserialize client state" log::info!("This is new connection end {:?}", inner_connection_end); log::info!("Borsh serialized connection end {:?}", borsh::to_vec(&inner_connection_end)); log::info!("This is in any {:?}", inner_connection_end.clone().encode_vec()); + log::info!( + "What i get hashed value {:?}", + CryptoHash::digest(inner_connection_end.clone().encode_vec().as_slice()) + ); log::info!("This is the hashed value {:?}", val); let inner_any = inner_connection_end.clone().encode_vec(); let inner_counterparty = inner_connection_end.counterparty(); @@ -615,13 +646,18 @@ deserialize client state" delay_period: inner_connection_end.delay_period().as_nanos() as u64, }; log::info!("This is after connection end {:?}", connection_end); - let chain_account = self.get_chain_storage().await; - let block_header = if !self.common_state.handshake_completed { + log::info!( + "Proof height {:?}, Latest Height {:?}", + proof_height, + chain_account.head().unwrap().block_height + ); + log::info!("at height {:?}", at_height); + let block_header = if !self.common_state.handshake_completed && at_height { log::info!("Fetching previous block header"); events::get_header_from_height( self.rpc_client(), self.solana_ibc_program_id, - at.revision_height, + proof_height, ) .await .expect(&format!("No block header found for height {:?}", at.revision_height)) @@ -714,7 +750,7 @@ deserialize client state" .collect(), version: inner_channel_end.version.to_string(), }; - let block_header = if !self.common_state.handshake_completed { + let block_header = if !self.common_state.handshake_completed && at_height { log::info!("Fetching previous block header"); events::get_header_from_height( self.rpc_client(), @@ -1169,7 +1205,7 @@ deserialize client state" ordering, counterparty: Some(ChanCounterparty { port_id: channel.counterparty().port_id.to_string(), - channel_id: channel.counterparty().channel_id.clone().unwrap().to_string(), + channel_id: "".to_string(), }), connection_hops: channel .connection_hops @@ -2037,15 +2073,26 @@ impl Chain for SolanaClient { async fn submit(&self, messages: Vec) -> Result { let keypair = self.keybase.keypair(); - println!("submitting tx now, {}", keypair.pubkey()); + log::info!("submitting tx now, {} with messages {}", keypair.pubkey(), messages.len()); + log::info!("Messages {:?}", messages); let authority = Arc::new(keypair); let program = self.program(); + if *self.is_transaction_processing.lock().unwrap() { + log::info!("Waiting for other thread to complete"); + sleep(Duration::from_millis(500)) + } + + *self.is_transaction_processing.lock().unwrap() = true; + // Build, sign, and send program instruction let mut signature = String::new(); let rpc = program.async_rpc(); let mut all_transactions = Vec::new(); + let mut index = -1; + + let mut messages_indeed = vec![]; for message in messages { let storage = self.get_ibc_storage().await; @@ -2059,11 +2106,13 @@ impl Chain for SolanaClient { let instruction_len = instruction_data.len() as u32; instruction_data.splice(..0, instruction_len.to_le_bytes()); + messages_indeed.push(message.clone()); + let balance = program.async_rpc().get_balance(&authority.pubkey()).await.unwrap(); - println!("This is balance {}", balance); - println!("This is start of payload ---------------------------------"); - println!("{:?}", message); - println!("This is end of payload ----------------------------------"); + log::info!("This is balance {}", balance); + log::info!("This is start of payload ---------------------------------"); + log::info!("{:?}", message); + log::info!("This is end of payload ----------------------------------"); let max_tries = 5; @@ -2103,18 +2152,19 @@ impl Chain for SolanaClient { // } let (signature_chunking_transactions, further_transactions) = - if let MsgEnvelope::Client(ClientMsg::UpdateClient(e)) = message { - self.send_deliver( - DeliverIxType::UpdateClient { - client_message: e.client_message, - client_id: e.client_id, - }, - chunk_account, - max_tries, - ) - .await? - // msg!("Packet Update Signature {:?}", signature); - } else if let MsgEnvelope::Packet(PacketMsg::Recv(e)) = message { + // if let MsgEnvelope::Client(ClientMsg::UpdateClient(e)) = message { + // self.send_deliver( + // DeliverIxType::UpdateClient { + // client_message: e.client_message, + // client_id: e.client_id, + // }, + // chunk_account, + // max_tries, + // ) + // .await? + // // msg!("Packet Update Signature {:?}", signature); + // } else + if let MsgEnvelope::Packet(PacketMsg::Recv(e)) = message { let packet_data: ibc_app_transfer_types::packet::PacketData = serde_json::from_slice(&e.packet.data).unwrap(); self.send_deliver( @@ -2226,6 +2276,7 @@ impl Chain for SolanaClient { } let total_transactions_length = all_transactions.iter().fold(0, |acc, tx| acc + tx.len()); + let mut failure = false; match self.transaction_sender { TransactionSender::RPC => { @@ -2233,24 +2284,39 @@ impl Chain for SolanaClient { log::info!("Total transactions {:?}", length); let start_time = Instant::now(); for transactions_iter in all_transactions { + log::info!("Came inside all events"); let mut tries = 0; let before_time = Instant::now(); for mut transaction in transactions_iter { loop { - sleep(Duration::from_secs(1)); - log::info!("Current Try: {}", tries); + log::info!("Current Try in solana: {}", tries); let blockhash = rpc.get_latest_blockhash().await.unwrap(); + // log::info!( + // "Blockhash in solana {:?} and {:?}", + // blockhash, + // messages_indeed.clone() + // ); transaction.sign(&[&*authority], blockhash); - let sig = rpc - .send_transaction_with_config( - &transaction, - RpcSendTransactionConfig { - skip_preflight: true, - max_retries: Some(0), - ..RpcSendTransactionConfig::default() - }, - ) - .await; + let tx = transaction.clone(); + // log::info!("Signed now in solana {:?}", messages_indeed.clone()); + let sync_rpc = self.program().rpc(); + let rpc = self.rpc_client(); + let messagessss = messages_indeed.clone(); + let sig = spawn_blocking(move || { + // log::info!("Sending transaction {:?}", messagessss.clone()); + sync_rpc + .send_transaction_with_config( + &tx, + RpcSendTransactionConfig { + skip_preflight: true, + max_retries: Some(0), + ..RpcSendTransactionConfig::default() + }, + ) + .unwrap() + }) + .await; + log::info!("This is sig {:?}", sig); if let Ok(si) = sig { signature = si.to_string(); @@ -2270,6 +2336,8 @@ impl Chain for SolanaClient { e ); success = true; + failure = true; + log::error!("Failure {}", failure); break; }, None => { @@ -2287,6 +2355,7 @@ impl Chain for SolanaClient { break; } else if status_retry < usize::MAX { // Retry twice a second + log::info!("Retyring since blockhash is not valid"); sleep(Duration::from_millis(500)); continue; } @@ -2296,11 +2365,13 @@ impl Chain for SolanaClient { if !success { tries += 1; continue; + sleep(Duration::from_secs(1)); } break; } else { log::error!("Error {:?}", sig); tries += 1; + sleep(Duration::from_secs(1)); continue; } } @@ -2309,6 +2380,7 @@ impl Chain for SolanaClient { let success_rate = 100 / (tries + 1); log::info!("Time taken {}", diff.as_millis()); log::info!("Success rate {}", success_rate); + log::info!("Failure {}", failure); } } let end_time = Instant::now(); @@ -2418,7 +2490,11 @@ impl Chain for SolanaClient { // Wait for finalizing the transaction if !self.common_state.handshake_completed { loop { - log::info!("Finalizing Transaction"); + log::info!("Finalizing Transaction {}", failure); + if failure { + log::error!("Breaking due to error"); + break; + } let result = rpc .confirm_transaction_with_commitment( &Signature::from_str(&signature).unwrap(), @@ -2435,6 +2511,8 @@ impl Chain for SolanaClient { } } + *self.is_transaction_processing.lock().unwrap() = false; + log::info!("Finished processing transaction"); Ok(signature) } @@ -2507,34 +2585,34 @@ impl Chain for SolanaClient { } } -#[test] -pub fn test_seq() { - let program_id = Pubkey::from_str("2HLLVco5HvwWriNbUhmVwA2pCetRkpgrqwnjcsZdyTKT").unwrap(); - let port_id = PortId::from_str("transfer").unwrap(); - let channel_id = ChannelId::from_str("channel-0").unwrap(); - let rpc_client = RpcClient::new("https://api.devnet.solana.com".to_string()); - let trie_seeds = &[TRIE_SEED]; - let trie_key = Pubkey::find_program_address(trie_seeds, &program_id).0; - let trie_account = rpc_client - .get_account_with_commitment(&trie_key, CommitmentConfig::processed()) - // .await - .unwrap() - .value - .unwrap(); - let trie = solana_trie::TrieAccount::new(trie_account.data).unwrap(); - let new_port_id = ibc_core_host_types::identifiers::PortId::from_str(port_id.as_str()).unwrap(); - let new_channel_id = ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()); - let trie_comp = PortChannelPK::try_from(new_port_id, new_channel_id).unwrap(); - let key = TrieKey::new(Tag::Receipt, trie_comp); - let packet_receipt_sequences: Vec = trie - .get_subtrie(&key) - .unwrap() - .iter() - .map(|c| { - u64::from_be_bytes( - Vec::::try_from(c.sub_key.clone()).unwrap().as_slice().try_into().unwrap(), - ) - }) - .collect(); - println!("{:?}", packet_receipt_sequences); -} +// #[test] +// pub fn test_seq() { +// let program_id = Pubkey::from_str("2HLLVco5HvwWriNbUhmVwA2pCetRkpgrqwnjcsZdyTKT").unwrap(); +// let port_id = PortId::from_str("transfer").unwrap(); +// let channel_id = ChannelId::from_str("channel-0").unwrap(); +// let rpc_client = RpcClient::new("https://api.devnet.solana.com".to_string()); +// let trie_seeds = &[TRIE_SEED]; +// let trie_key = Pubkey::find_program_address(trie_seeds, &program_id).0; +// let trie_account = rpc_client +// .get_account_with_commitment(&trie_key, CommitmentConfig::processed()) +// // .await +// .unwrap() +// .value +// .unwrap(); +// let trie = solana_trie::TrieAccount::new(trie_account.data).unwrap(); +// let new_port_id = ibc_core_host_types::identifiers::PortId::from_str(port_id.as_str()).unwrap(); +// let new_channel_id = ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()); +// let trie_comp = PortChannelPK::try_from(new_port_id, new_channel_id).unwrap(); +// let key = TrieKey::new(Tag::Receipt, trie_comp); +// let packet_receipt_sequences: Vec = trie +// .get_subtrie(&key) +// .unwrap() +// .iter() +// .map(|c| { +// u64::from_be_bytes( +// Vec::::try_from(c.sub_key.clone()).unwrap().as_slice().try_into().unwrap(), +// ) +// }) +// .collect(); +// println!("{:?}", packet_receipt_sequences); +// } diff --git a/hyperspace/solana/src/msgs.rs b/hyperspace/solana/src/msgs.rs index 545430494..df4c6aaae 100644 --- a/hyperspace/solana/src/msgs.rs +++ b/hyperspace/solana/src/msgs.rs @@ -26,10 +26,11 @@ use ibc_core_handler_types::msgs::MsgEnvelope; use ibc_core_host_types::identifiers::{ChannelId, ClientId, ConnectionId, PortId, Sequence}; use ibc_new_primitives::{Signer, Timestamp}; use ibc_proto_new::{google::protobuf::Any, ibc::core::connection::v1::Version}; -use ics08_wasm::client_state::WASM_CLIENT_STATE_TYPE_URL; use primitives::mock::LocalClientTypes; use std::str::FromStr; +const GUEST_CLIENT_STATE_TYPE_URL: &'static str = cf_guest::proto::ClientState::IBC_TYPE_URL; + use crate::{ client_state::convert_old_client_state_to_new, consensus_state::convert_old_consensus_state_to_new, @@ -40,16 +41,23 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - .iter() .map(|message| match message { Ics26Envelope::Ics2Msg(msg) => match msg { - ibc::core::ics02_client::msgs::ClientMsg::CreateClient(e) => + ibc::core::ics02_client::msgs::ClientMsg::CreateClient(e) => { MsgEnvelope::Client(ClientMsg::CreateClient(MsgCreateClient::new( convert_old_client_state_to_new(e.client_state.clone()).into(), convert_old_consensus_state_to_new(e.consensus_state.clone()).into(), Signer::from(e.signer.as_ref().to_string()), - ))), + ))) + }, ibc::core::ics02_client::msgs::ClientMsg::UpdateClient(e) => { let header = match &e.client_message { - pallet_ibc::light_clients::AnyClientMessage::Tendermint(msg) => - ibc_proto::google::protobuf::Any::from(msg.clone()), + pallet_ibc::light_clients::AnyClientMessage::Tendermint(msg) => { + log::info!("This is tendermint"); + ibc_proto::google::protobuf::Any::from(msg.clone()) + }, + pallet_ibc::light_clients::AnyClientMessage::Rollup(msg) => { + log::info!("This is rollup"); + ibc_proto::google::protobuf::Any::from(msg.clone()) + }, _ => panic!("Not supported"), }; let new_any_header = Any { type_url: header.type_url, value: header.value }; @@ -59,7 +67,7 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - signer: Signer::from(e.signer.as_ref().to_string()), })) }, - ibc::core::ics02_client::msgs::ClientMsg::UpgradeClient(e) => + ibc::core::ics02_client::msgs::ClientMsg::UpgradeClient(e) => { MsgEnvelope::Client(ClientMsg::UpgradeClient(MsgUpgradeClient { client_id: ClientId::from_str(e.client_id.as_str()).unwrap(), upgraded_client_state: convert_old_client_state_to_new( @@ -79,10 +87,11 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - ) .unwrap(), signer: Signer::from(e.signer.as_ref().to_string()), - })), + })) + }, }, Ics26Envelope::Ics3Msg(msg) => match msg { - ibc::core::ics03_connection::msgs::ConnectionMsg::ConnectionOpenInit(e) => + ibc::core::ics03_connection::msgs::ConnectionMsg::ConnectionOpenInit(e) => { MsgEnvelope::Connection(ConnectionMsg::OpenInit(MsgConnectionOpenInit { client_id_on_a: ClientId::from_str(e.client_id.as_str()).unwrap(), counterparty: Counterparty { @@ -110,7 +119,8 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - }), delay_period: e.delay_period, signer: Signer::from(e.signer.as_ref().to_string()), - })), + })) + }, #[allow(deprecated)] ibc::core::ics03_connection::msgs::ConnectionMsg::ConnectionOpenTry(e) => { let encoded_cs = ibc_proto::google::protobuf::Any::from( @@ -132,7 +142,7 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - signer: Signer::from(e.signer.as_ref().to_string()), client_id_on_b: ClientId::from_str(e.client_id.as_str()).unwrap(), client_state_of_b_on_a: Any { - type_url: WASM_CLIENT_STATE_TYPE_URL.to_string(), + type_url: GUEST_CLIENT_STATE_TYPE_URL.to_string(), value: encoded_cs.value, }, versions_on_a: e @@ -201,7 +211,7 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - conn_id_on_b: ConnectionId::from_str(e.counterparty_connection_id.as_str()) .unwrap(), client_state_of_a_on_b: Any { - type_url: WASM_CLIENT_STATE_TYPE_URL.to_string(), + type_url: GUEST_CLIENT_STATE_TYPE_URL.to_string(), value: encoded_cs.value, }, proof_conn_end_on_b: CommitmentProofBytes::try_from( @@ -249,7 +259,7 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - }, })) }, - ibc::core::ics03_connection::msgs::ConnectionMsg::ConnectionOpenConfirm(e) => + ibc::core::ics03_connection::msgs::ConnectionMsg::ConnectionOpenConfirm(e) => { MsgEnvelope::Connection(ConnectionMsg::OpenConfirm(MsgConnectionOpenConfirm { signer: Signer::from(e.signer.as_ref().to_string()), conn_id_on_b: ConnectionId::from_str(e.connection_id.as_str()).unwrap(), @@ -262,10 +272,11 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - e.proofs.height().revision_height, ) .unwrap(), - })), + })) + }, }, Ics26Envelope::Ics4ChannelMsg(msg) => match msg { - ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenInit(e) => + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenInit(e) => { MsgEnvelope::Channel(ChannelMsg::OpenInit(MsgChannelOpenInit { port_id_on_a: PortId::from_str(e.port_id.as_str()).unwrap(), connection_hops_on_a: e @@ -282,9 +293,10 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - signer: Signer::from(e.signer.as_ref().to_string()), version_proposal: ChanVersion::from_str(&e.channel.version.to_string()) .unwrap(), - })), + })) + }, #[allow(deprecated)] - ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenTry(e) => + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenTry(e) => { MsgEnvelope::Channel(ChannelMsg::OpenTry(MsgChannelOpenTry { port_id_on_b: PortId::from_str(e.port_id.as_str()).unwrap(), connection_hops_on_b: e @@ -317,8 +329,9 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - signer: Signer::from(e.signer.as_ref().to_string()), version_proposal: ChanVersion::from_str(&e.channel.version.to_string()) .unwrap(), - })), - ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenAck(e) => + })) + }, + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenAck(e) => { MsgEnvelope::Channel(ChannelMsg::OpenAck(MsgChannelOpenAck { port_id_on_a: PortId::from_str(e.port_id.as_str()).unwrap(), chan_id_on_a: ChannelId::new(e.channel_id.sequence()), @@ -335,8 +348,9 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - chan_id_on_b: ChannelId::new(e.counterparty_channel_id.sequence()), version_on_b: ChanVersion::from_str(&e.counterparty_version.to_string()) .unwrap(), - })), - ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenConfirm(e) => + })) + }, + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelOpenConfirm(e) => { MsgEnvelope::Channel(ChannelMsg::OpenConfirm(MsgChannelOpenConfirm { port_id_on_b: PortId::from_str(e.port_id.as_str()).unwrap(), chan_id_on_b: ChannelId::new(e.channel_id.sequence()), @@ -350,14 +364,16 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - ) .unwrap(), signer: Signer::from(e.signer.as_ref().to_string()), - })), - ibc::core::ics04_channel::msgs::ChannelMsg::ChannelCloseInit(e) => + })) + }, + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelCloseInit(e) => { MsgEnvelope::Channel(ChannelMsg::CloseInit(MsgChannelCloseInit { port_id_on_a: PortId::from_str(e.port_id.as_str()).unwrap(), chan_id_on_a: ChannelId::new(e.channel_id.sequence()), signer: Signer::from(e.signer.as_ref().to_string()), - })), - ibc::core::ics04_channel::msgs::ChannelMsg::ChannelCloseConfirm(e) => + })) + }, + ibc::core::ics04_channel::msgs::ChannelMsg::ChannelCloseConfirm(e) => { MsgEnvelope::Channel(ChannelMsg::CloseConfirm(MsgChannelCloseConfirm { port_id_on_b: PortId::from_str(e.port_id.as_str()).unwrap(), chan_id_on_b: ChannelId::new(e.channel_id.sequence()), @@ -371,10 +387,11 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - ) .unwrap(), signer: Signer::from(e.signer.as_ref().to_string()), - })), + })) + }, }, Ics26Envelope::Ics4PacketMsg(msg) => match msg { - ibc::core::ics04_channel::msgs::PacketMsg::RecvPacket(e) => + ibc::core::ics04_channel::msgs::PacketMsg::RecvPacket(e) => { MsgEnvelope::Packet(PacketMsg::Recv(MsgRecvPacket { packet: Packet { seq_on_a: Sequence::from(e.packet.sequence.0), @@ -410,8 +427,9 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - ) .unwrap(), signer: Signer::from(e.signer.as_ref().to_string()), - })), - ibc::core::ics04_channel::msgs::PacketMsg::AckPacket(e) => + })) + }, + ibc::core::ics04_channel::msgs::PacketMsg::AckPacket(e) => { MsgEnvelope::Packet(PacketMsg::Ack(MsgAcknowledgement { packet: Packet { seq_on_a: Sequence::from(e.packet.sequence.0), @@ -453,8 +471,9 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - e.proofs.height().revision_height, ) .unwrap(), - })), - ibc::core::ics04_channel::msgs::PacketMsg::ToPacket(e) => + })) + }, + ibc::core::ics04_channel::msgs::PacketMsg::ToPacket(e) => { MsgEnvelope::Packet(PacketMsg::Timeout(MsgTimeout { packet: Packet { seq_on_a: Sequence::from(e.packet.sequence.0), @@ -491,8 +510,9 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - e.proofs.object_proof().as_bytes().to_vec(), ) .unwrap(), - })), - ibc::core::ics04_channel::msgs::PacketMsg::ToClosePacket(e) => + })) + }, + ibc::core::ics04_channel::msgs::PacketMsg::ToClosePacket(e) => { MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(MsgTimeoutOnClose { packet: Packet { seq_on_a: Sequence::from(e.packet.sequence.0), @@ -533,7 +553,8 @@ pub fn convert_old_msgs_to_new(messages: Vec>) - e.proofs.other_proof().clone().unwrap().as_bytes().to_vec(), ) .unwrap(), - })), + })) + }, }, }) .collect(); diff --git a/hyperspace/testsuite/Cargo.toml b/hyperspace/testsuite/Cargo.toml index f4ff50cf6..08dc13376 100644 --- a/hyperspace/testsuite/Cargo.toml +++ b/hyperspace/testsuite/Cargo.toml @@ -26,6 +26,7 @@ hyperspace-core = { path = "../core", features = ["testing"] } hyperspace-parachain = { path = "../parachain", features = ["testing"] } hyperspace-primitives = { path = "../primitives", features = ["testing"] } hyperspace-solana = { path = "../solana", features = ["testing"] } +hyperspace-rollup = { path = "../rollup", features = ["testing"] } pallet-ibc = { path = "../../contracts/pallet-ibc" } pallet-ibc-ping = { path = "../../contracts/pallet-ibc/ping" } ics10-grandpa = { path = "../../light-clients/ics10-grandpa" } @@ -55,6 +56,7 @@ hyperspace-core = { path = "../core", features = ["testing"] } hyperspace-parachain = { path = "../parachain", features = ["testing"] } hyperspace-cosmos = { path = "../cosmos", features = [] } hyperspace-solana = { path = "../solana", features = ["testing"] } +hyperspace-rollup = { path = "../rollup", features = ["testing"] } # We need this so the tests run sequentially [[test]] diff --git a/hyperspace/testsuite/src/lib.rs b/hyperspace/testsuite/src/lib.rs index b6d3a267b..088df6bd7 100644 --- a/hyperspace/testsuite/src/lib.rs +++ b/hyperspace/testsuite/src/lib.rs @@ -263,7 +263,7 @@ async fn send_packet_and_assert_height_timeout( chain_b, asset_a, channel_id, - Some(Timeout::Offset { timestamp: Some(120 * 60), height: Some(10) }), + Some(Timeout::Offset { timestamp: Some(10), height: Some(2) }), ) .await; @@ -374,9 +374,9 @@ async fn send_packet_with_connection_delay( log::info!(target: "hyperspace", "Sending transfer from {}", chain_b.name()); let (previous_balance, ..) = send_transfer(chain_b, chain_a, asset_b.clone(), channel_id_b, None).await; - println!("send packet on cosmos done"); - assert_send_transfer(chain_b, asset_b, previous_balance, 220).await; - println!("assert send packet on cosmos done"); + println!("send packet on mantis done"); + assert_send_transfer(chain_b, asset_b, previous_balance, 1520).await; + println!("assert send packet on mantis done"); // now send from chain b. log::info!(target: "hyperspace", "🚀🚀 Token Transfer successful with connection delay"); } diff --git a/hyperspace/testsuite/tests/solana_rollup.rs b/hyperspace/testsuite/tests/solana_rollup.rs new file mode 100644 index 000000000..7c36db0f3 --- /dev/null +++ b/hyperspace/testsuite/tests/solana_rollup.rs @@ -0,0 +1,345 @@ +// Copyright 2022 ComposableFi +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use anchor_lang::prelude::*; +use core::time::Duration; +use futures::StreamExt; +use hyperspace_core::{ + chain::{AnyAssetId, AnyChain, AnyConfig}, logging, relay, substrate::DefaultConfig +}; +use hyperspace_rollup::client::{RollupClient, RollupClientConfig}; +use hyperspace_primitives::{utils::create_clients, CommonClientConfig, IbcProvider, KeyProvider}; +use hyperspace_solana::{client::SolanaClient, SolanaClientConfig}; +use hyperspace_testsuite::{ + ibc_channel_close, ibc_messaging_packet_height_timeout_with_connection_delay, + ibc_messaging_packet_timeout_on_channel_close, + ibc_messaging_packet_timestamp_timeout_with_connection_delay, + ibc_messaging_with_connection_delay, misbehaviour::ibc_messaging_submit_misbehaviour, + setup_connection_and_channel, +}; +use ibc::core::{ + ics02_client::msgs::update_client::MsgUpdateAnyClient, + ics24_host::identifier::{ClientId, PortId}, +}; +use ibc_proto::ibc::core::client::v1::MsgUpdateClient; +use sp_core::hashing::sha2_256; +use std::str::FromStr; + +#[derive(Debug, Clone)] +pub struct Args { + pub chain_a: String, + pub chain_b: String, + pub connection_prefix_a: String, + pub connection_prefix_b: String, + pub solana_ws: String, + pub rollup_ws: String, + pub rollup_trie_db_rpc: String, +} + +impl Default for Args { + fn default() -> Self { + let solana = std::env::var("SOLANA_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()); + let rollup = std::env::var("ROLLUP_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()); + + Args { + // chain_a: format!("https://devnet.helius-rpc.com/?api-key=bc5c0cfc-46df-4781-978f-af6ca7a202c2"), + chain_a: format!("http://{solana}:9000"), + // chain_a: format!("https://devnet.helius-rpc.com/?api-key=5ae782d8-6bf6-489c-b6df-ef7e6289e193"), + chain_b: format!("http://{rollup}:8899"), + // chain_b: format!("https://mantis-rollup.composable-shared-artifacts.composablenodes.tech/rpc"), + connection_prefix_a: "ibc".to_string(), + connection_prefix_b: "ibc".to_string(), + solana_ws: format!("ws://{solana}:9001"), + // solana_ws: format!("wss://devnet.helius-rpc.com/?api-key=5ae782d8-6bf6-489c-b6df-ef7e6289e193"), + // solana_ws: + // format!("wss://devnet.helius-rpc.com/?api-key=bc5c0cfc-46df-4781-978f-af6ca7a202c2"), + rollup_ws: format!("ws://{rollup}:8900"), + // rollup_ws: format!("wss://mantis-rollup.composable-shared-artifacts.composablenodes.tech/ws"), + // rollup_ws: format!("ws://{solana}:8900"), + // rollup_trie_db_rpc: format!("http://{rollup}:42069") + rollup_trie_db_rpc: format!("http://{rollup}:42069") + } + } +} + +async fn setup_clients() -> (AnyChain, AnyChain) { + log::info!(target: "hyperspace", "=========================== Starting Test ==========================="); + let args = Args::default(); + + // Create client configurations + let config_a = SolanaClientConfig { + name: "solana".to_string(), + client_id: None, + connection_id: None, + commitment_prefix: args.connection_prefix_a.clone().into_bytes(), + wasm_checksum: None, + rpc_url: args.chain_a.clone().parse().unwrap(), + ws_url: args.solana_ws.clone().parse().unwrap(), + chain_id: "solana-1".to_string(), + account_prefix: args.connection_prefix_a.clone(), + fee_denom: "stake".to_string(), + fee_amount: "4000".to_string(), + gas_limit: 100000000, + store_prefix: args.connection_prefix_a.clone(), + max_tx_size: 320000, + common_state_config: CommonClientConfig { + skip_optional_client_updates: true, + max_packets_to_process: 1, + client_update_interval_sec: 10, + handshake_completed: false, + }, + channel_whitelist: vec![], + commitment_level: "confirmed".to_string(), + private_key: vec![ + 48, 123, 8, 80, 248, 0, 217, 142, 124, 193, 95, 24, 168, 139, 214, 136, 147, 210, 168, + 135, 26, 36, 162, 89, 150, 185, 99, 191, 247, 135, 78, 111, 12, 8, 4, 81, 129, 165, + 153, 230, 192, 225, 51, 119, 216, 14, 69, 225, 73, 7, 204, 144, 39, 213, 91, 255, 136, + 38, 95, 131, 197, 4, 101, 186, + ], + solana_ibc_program_id: "2HLLVco5HvwWriNbUhmVwA2pCetRkpgrqwnjcsZdyTKT".to_string(), + write_program_id: "FufGpHqMQgGVjtMH9AV8YMrJYq8zaK6USRsJkZP4yDjo".to_string(), + signature_verifier_program_id: "C6r1VEbn3mSpecgrZ7NdBvWUtYVJWrDPv4uU9Xs956gc".to_string(), + trie_db_path: "../../../solana-ibc-indexer/indexer.db3".to_string(), + transaction_sender: "RPC".to_string(), + }; + + let mut config_b = RollupClientConfig { + name: "mantis".to_string(), + client_id: None, + connection_id: None, + commitment_prefix: args.connection_prefix_a.clone().into_bytes(), + wasm_checksum: None, + rpc_url: args.chain_b.clone().parse().unwrap(), + ws_url: args.rollup_ws.clone().parse().unwrap(), + chain_id: "mantis-1".to_string(), + account_prefix: args.connection_prefix_b.clone(), + fee_denom: "stake".to_string(), + fee_amount: "4000".to_string(), + gas_limit: 100000000, + store_prefix: args.connection_prefix_b, + max_tx_size: 320000, + common_state_config: CommonClientConfig { + skip_optional_client_updates: true, + max_packets_to_process: 1, + client_update_interval_sec: 10, + handshake_completed: false, + }, + channel_whitelist: vec![], + commitment_level: "confirmed".to_string(), + private_key: vec![ + 48, 123, 8, 80, 248, 0, 217, 142, 124, 193, 95, 24, 168, 139, 214, 136, 147, 210, 168, + 135, 26, 36, 162, 89, 150, 185, 99, 191, 247, 135, 78, 111, 12, 8, 4, 81, 129, 165, + 153, 230, 192, 225, 51, 119, 216, 14, 69, 225, 73, 7, 204, 144, 39, 213, 91, 255, 136, + 38, 95, 131, 197, 4, 101, 186, + ], + solana_ibc_program_id: "2HLLVco5HvwWriNbUhmVwA2pCetRkpgrqwnjcsZdyTKT".to_string(), + write_program_id: "FufGpHqMQgGVjtMH9AV8YMrJYq8zaK6USRsJkZP4yDjo".to_string(), + signature_verifier_program_id: "C6r1VEbn3mSpecgrZ7NdBvWUtYVJWrDPv4uU9Xs956gc".to_string(), + trie_db_path: "../../../solana-ibc-indexer/indexer.db3".to_string(), + transaction_sender: "RPC".to_string(), + trie_rpc_url: args.rollup_trie_db_rpc.clone().parse().unwrap(), + }; + + // println!("This is config b {:?}", config_b); + + let chain_a = SolanaClient::new(config_a.clone()).await.expect("Solana error"); + let chain_b = RollupClient::new(config_b.clone()) + .await + .map_err(|e| println!("{:?}", e)) + .unwrap(); + + println!("This is chain b prefix {:?}", chain_b.commitment_prefix.as_bytes()); + + let mut chain_a_wrapped = AnyConfig::Solana(config_a).into_client().await.unwrap(); + let mut chain_b_wrapped = AnyConfig::Rollup(config_b).into_client().await.unwrap(); + + let AnyChain::Solana(chain_a) = &mut chain_a_wrapped else { unreachable!() }; + + let clients_on_a = chain_a_wrapped.query_clients().await.unwrap(); + let clients_on_b = chain_b_wrapped.query_clients().await.unwrap(); + + // if !clients_on_a.is_empty() && !clients_on_b.is_empty() { + // chain_a_wrapped.set_client_id(clients_on_b[0].clone()); + // chain_b_wrapped.set_client_id(clients_on_a[0].clone()); + // return (chain_a_wrapped, chain_b_wrapped) + // } + + let (client_a, client_b) = + create_clients(&mut chain_a_wrapped, &mut chain_b_wrapped).await.unwrap(); + chain_a_wrapped.set_client_id(client_a); + chain_b_wrapped.set_client_id(client_b); + // chain_b_wrapped.set_client_id(ClientId::new("cf-solana", 8).unwrap()); + // chain_a_wrapped.set_client_id(ClientId::new("cf-guest", 8).unwrap()); + (chain_a_wrapped, chain_b_wrapped) +} + +// #[tokio::test] +#[tokio::test(flavor = "multi_thread", worker_threads = 12)] +// #[ignore] +async fn solana_to_rollup_ibc_messaging_full_integration_test() { + use ibc::core::ics24_host::identifier::{ChannelId, ConnectionId}; + use std::str::FromStr; + logging::setup_logging(); + + let asset_id_a = AnyAssetId::Solana("33WVSef9zaw49KbNdPGTmACVRnAXzN3o1fsqbUrLp2mh".to_string()); + let asset_id_b = AnyAssetId::Rollup("33WVSef9zaw49KbNdPGTmACVRnAXzN3o1fsqbUrLp2mh".to_string()); + let (mut chain_a, mut chain_b) = setup_clients().await; + let (handle, channel_a, channel_b, connection_id_a, connection_id_b) = + setup_connection_and_channel(&mut chain_a, &mut chain_b, Duration::from_secs(10)).await; + + handle.abort(); + + // relay(chain_a.clone(), chain_b.clone(), None, None, None).await; + + // let connection_id_a = ConnectionId::from_str("connection-0").unwrap(); + // let connection_id_b = ConnectionId::from_str("connection-0").unwrap(); + + // let channel_a = ChannelId::from_str("channel-0").unwrap(); + // let channel_b = ChannelId::from_str("channel-0").unwrap(); + + log::info!("Channel A: {:?}", channel_a); + log::info!("Channel B: {:?}", channel_b); + log::info!("Connection A: {:?}", connection_id_a); + log::info!("Connection B: {:?}", connection_id_b); + + // Set connections and channel whitelist + chain_a.set_connection_id(connection_id_a); + chain_b.set_connection_id(connection_id_b); + + chain_a.set_channel_whitelist(vec![(channel_a, PortId::transfer())].into_iter().collect()); + chain_b.set_channel_whitelist(vec![(channel_b, PortId::transfer())].into_iter().collect()); + + // Run tests sequentially + + // no timeouts + connection delay + + ibc_messaging_with_connection_delay( + &mut chain_a, + &mut chain_b, + asset_id_a.clone(), + asset_id_b.clone(), + channel_a, + channel_b, + ) + .await; + + // timeouts + connection delay + ibc_messaging_packet_height_timeout_with_connection_delay( + &mut chain_a, + &mut chain_b, + asset_id_a.clone(), + channel_a, + channel_b, + ) + .await; + ibc_messaging_packet_height_timeout_with_connection_delay( + &mut chain_b, + &mut chain_a, + asset_id_b.clone(), + channel_b, + channel_a, + ) + .await; + ibc_messaging_packet_timestamp_timeout_with_connection_delay( + &mut chain_b, + &mut chain_a, + asset_id_b.clone(), + channel_b, + channel_a, + ) + .await; + ibc_messaging_packet_timestamp_timeout_with_connection_delay( + &mut chain_a, + &mut chain_b, + asset_id_a.clone(), + channel_a, + channel_b, + ) + .await; + + // channel closing semantics + // ibc_messaging_packet_timeout_on_channel_close( + // &mut chain_b, + // &mut chain_a, + // asset_id_b.clone(), + // channel_b, + // ) + // .await; + // ibc_channel_close(&mut chain_a, &mut chain_b).await; + + // // TODO: tendermint misbehaviour? + // // ibc_messaging_submit_misbehaviour(&mut chain_a, &mut chain_b).await; +} + +// #[tokio::test] +#[tokio::test(flavor = "multi_thread", worker_threads = 5)] +#[ignore] +async fn rollup_to_solana_ibc_messaging_full_integration_test() { + logging::setup_logging(); + + let (chain_a, chain_b) = setup_clients().await; + let (mut chain_b, mut chain_a) = (chain_a, chain_b); + + let (handle, channel_a, channel_b, connection_id_a, connection_id_b) = + setup_connection_and_channel(&mut chain_a, &mut chain_b, Duration::from_secs(20)).await; + handle.abort(); + + // Set connections and channel whitelist + chain_a.set_connection_id(connection_id_a); + chain_b.set_connection_id(connection_id_b); + + chain_a.set_channel_whitelist(vec![(channel_a, PortId::transfer())].into_iter().collect()); + chain_b.set_channel_whitelist(vec![(channel_b, PortId::transfer())].into_iter().collect()); + + let asset_id_a = AnyAssetId::Rollup("33WVSef9zaw49KbNdPGTmACVRnAXzN3o1fsqbUrLp2mh".to_string()); + let asset_id_b = AnyAssetId::Solana("33WVSef9zaw49KbNdPGTmACVRnAXzN3o1fsqbUrLp2mh".to_string()); + + // Run tests sequentially + + // no timeouts + connection delay + ibc_messaging_with_connection_delay( + &mut chain_a, + &mut chain_b, + asset_id_a.clone(), + asset_id_b.clone(), + channel_a, + channel_b, + ) + .await; + + // // timeouts + connection delay + // ibc_messaging_packet_height_timeout_with_connection_delay( + // &mut chain_a, + // &mut chain_b, + // asset_id_a.clone(), + // channel_a, + // channel_b, + // ) + // .await; + // ibc_messaging_packet_timestamp_timeout_with_connection_delay( + // &mut chain_a, + // &mut chain_b, + // asset_id_a.clone(), + // channel_a, + // channel_b, + // ) + // .await; + + // // channel closing semantics (doesn't work on cosmos) + // // ibc_messaging_packet_timeout_on_channel_close(&mut chain_a, &mut chain_b, + // asset_id_a.clone()) // .await; + // // ibc_channel_close(&mut chain_a, &mut chain_b).await; + + // ibc_messaging_submit_misbehaviour(&mut chain_a, &mut chain_b).await; +} diff --git a/ibc/proto-compiler/src/cmd/compile.rs b/ibc/proto-compiler/src/cmd/compile.rs index 01ef922c9..c03272360 100644 --- a/ibc/proto-compiler/src/cmd/compile.rs +++ b/ibc/proto-compiler/src/cmd/compile.rs @@ -251,16 +251,14 @@ impl CompileCmd { format!("{}/proto/cosmos/upgrade", sdk_dir.display()), ]; - let proto_includes_paths = vec![ - format!("{}", gogo.display()), + let proto_includes_paths = [format!("{}", gogo.display()), format!("{}", google.display()), format!("{}/proto", cosmos_proto.display()), format!("{}/proto", ics23.display()), format!("{root}/../proto"), format!("{}/proto", sdk_dir.display()), format!("{}/third_party/proto", sdk_dir.display()), - format!("{}/proto", ibc_dir.display()), - ]; + format!("{}/proto", ibc_dir.display())]; // List available proto files let mut protos: Vec = vec![]; diff --git a/light-clients/cf-guest-cw/Cargo.toml b/light-clients/cf-guest-cw/Cargo.toml index 89cfa4e21..3a7664236 100644 --- a/light-clients/cf-guest-cw/Cargo.toml +++ b/light-clients/cf-guest-cw/Cargo.toml @@ -44,8 +44,8 @@ ed25519-dalek = { version = "2.1.1", default-features = false, features = ["pkcs byteorder = { version = "1.3.2", default-features = false } digest = { version = "0.10.3", default-features = false } hex = "0.4.3" -guestchain = { path = "../../../emulated-light-client/common/guestchain", default-features = false } -trie-ids = { path = "../../../emulated-light-client/common/trie-ids", default-features = false } +guestchain = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false } +trie-ids = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false } borsh = { version = "0.10.3", default-features = false } diff --git a/light-clients/cf-guest-cw/src/context.rs b/light-clients/cf-guest-cw/src/context.rs index 527a325db..f05495e97 100644 --- a/light-clients/cf-guest-cw/src/context.rs +++ b/light-clients/cf-guest-cw/src/context.rs @@ -21,7 +21,7 @@ use crate::{ ContractError, }; use cf_guest::{ClientState, ConsensusState}; -use cosmwasm_std::{Deps, DepsMut, Empty, Env, Storage}; +use cosmwasm_std::{Deps, DepsMut, Env, Storage}; use ibc::{ core::{ ics02_client::{error::Error, events::Checksum}, diff --git a/light-clients/cf-guest-cw/src/contract.rs b/light-clients/cf-guest-cw/src/contract.rs index c753ed624..524fe7928 100644 --- a/light-clients/cf-guest-cw/src/contract.rs +++ b/light-clients/cf-guest-cw/src/contract.rs @@ -137,7 +137,7 @@ fn process_message( verify( &CommitmentPrefix::default(), &msg.proof, - &consensus_state.root(), + consensus_state.root(), msg.path, Some(msg.value.as_ref()), )?; @@ -157,7 +157,7 @@ fn process_message( verify( &CommitmentPrefix::default(), &msg.proof, - &consensus_state.root(), + consensus_state.root(), msg.path, None, ) @@ -296,7 +296,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { let msg = VerifyClientMessage::try_from(msg)?; client .verify_client_message(&ctx, client_id, client_state, msg.client_message) - .map_err(|e| ContractError::Client(e)) + .map_err(ContractError::Client) .map(|_| to_binary(&QueryResponse::success()))? }, QueryMsg::TimestampAtHeight(_msg) => todo!(), @@ -307,7 +307,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { let msg = CheckForMisbehaviourMsg::try_from(msg)?; client .check_for_misbehaviour(&ctx, client_id, client_state, msg.client_message) - .map_err(|e| ContractError::Client(e)) + .map_err(ContractError::Client) .map(|result| to_binary(&QueryResponse::success().misbehaviour(result)))? }, } diff --git a/light-clients/cf-guest-cw/src/crypto.rs b/light-clients/cf-guest-cw/src/crypto.rs index e2848bb8f..88a7e0b33 100644 --- a/light-clients/cf-guest-cw/src/crypto.rs +++ b/light-clients/cf-guest-cw/src/crypto.rs @@ -12,7 +12,7 @@ impl guestchain::PubKey for PubKey { self.0.as_bytes().to_vec() } - fn as_bytes<'a>(&'a self) -> alloc::borrow::Cow<'a, [u8]> { + fn as_bytes(&self) -> alloc::borrow::Cow<'_, [u8]> { alloc::borrow::Cow::Borrowed(&self.0.as_bytes()[..]) } @@ -59,7 +59,7 @@ impl guestchain::Signature for Signature { self.0.to_vec() } - fn as_bytes<'a>(&'a self) -> alloc::borrow::Cow<'a, [u8]> { + fn as_bytes(&self) -> alloc::borrow::Cow<'_, [u8]> { self.0.to_vec().into() } diff --git a/light-clients/cf-guest-cw/src/msg.rs b/light-clients/cf-guest-cw/src/msg.rs index f122d7d9b..9e092bec3 100644 --- a/light-clients/cf-guest-cw/src/msg.rs +++ b/light-clients/cf-guest-cw/src/msg.rs @@ -301,11 +301,11 @@ impl VerifyClientMessage { let client_message = match raw { ClientMessageRaw::Header(header) => { let any = Any::decode(&mut header.data.as_slice())?; - ClientMessage::decode_vec(&any.value)?.into() + ClientMessage::decode_vec(&any.value)? }, ClientMessageRaw::Misbehaviour(misbehaviour) => { let any = Any::decode(&mut misbehaviour.data.as_slice())?; - ClientMessage::decode_vec(&any.value)?.into() + ClientMessage::decode_vec(&any.value)? }, }; Ok(client_message) diff --git a/light-clients/cf-guest/Cargo.toml b/light-clients/cf-guest/Cargo.toml index 5d1c14fd3..6df90096a 100644 --- a/light-clients/cf-guest/Cargo.toml +++ b/light-clients/cf-guest/Cargo.toml @@ -14,11 +14,11 @@ ed25519-consensus = { version = "2", default-features = false } serde = { version = "1.0", default-features = false, features = ["derive"] } # New IBC -ibc-core-client-context = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-client-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-handler-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-core-host-types = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } -ibc-primitives = { git = "https://github.com/mina86/ibc-rs", rev = "f07276383091f75b7ee8bff6fd434f8214ac5054", default-features = false } +ibc-core-client-context = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-client-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-handler-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-host-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-primitives = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } # Old IBC ibc = { path = "../../ibc/modules", default-features = false } @@ -27,12 +27,12 @@ ibc-derive = { path = "../../ibc/derive", default-features = false } tendermint-proto = { git = "https://github.com/mina86/tendermint-rs", rev = "45fbd500d731effb95a98257630feb46f6c41d06", default-features = false } -guestchain = { path = "../../../emulated-light-client/common/guestchain", default-features = false } -lib = { path = "../../../emulated-light-client/common/lib", features = ["borsh"], default-features = false } -trie-ids = { path = "../../../emulated-light-client/common/trie-ids", default-features = false } -sealable-trie = { path = "../../../emulated-light-client/common/sealable-trie", features = ["borsh"], default-features = false } -stdx = { path = "../../../emulated-light-client/common/stdx", default-features = false } -cf-guest-upstream = { package = "cf-guest", path = "../../../emulated-light-client/common/cf-guest", default-features = false } +guestchain = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false } +lib = { git = "https://github.com/ComposableFi/emulated-light-client/", features = ["borsh"], default-features = false } +trie-ids = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false } +sealable-trie = { git = "https://github.com/ComposableFi/emulated-light-client/", features = ["borsh"], default-features = false } +stdx = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false } +cf-guest-upstream = { package = "cf-guest", git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false } [build-dependencies] prost-build = { version = "0.11", default-features = false } @@ -41,9 +41,9 @@ prost-build = { version = "0.11", default-features = false } insta = { version = "1.34.0" } rand = { version = "0.8.5" } -guestchain = { path = "../../../emulated-light-client/common/guestchain", default-features = false, features = ["test_utils"] } -lib = { path = "../../../emulated-light-client/common/lib", default-features = false, features = ["test_utils"] } -memory = { path = "../../../emulated-light-client/common/memory", default-features = false, features = ["test_utils"] } +guestchain = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false, features = ["test_utils"] } +lib = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false, features = ["test_utils"] } +memory = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false, features = ["test_utils"] } [features] std = [] \ No newline at end of file diff --git a/light-clients/cf-guest/src/client.rs b/light-clients/cf-guest/src/client.rs index 6b3a6e35e..7e661ce5e 100644 --- a/light-clients/cf-guest/src/client.rs +++ b/light-clients/cf-guest/src/client.rs @@ -32,7 +32,7 @@ impl ClientState { } pub fn with_header(&self, header: &cf_guest_upstream::Header) -> Self { - Self(self.0.with_header(&header)) + Self(self.0.with_header(header)) } pub fn frozen(&self) -> Self { diff --git a/light-clients/cf-guest/src/client_def.rs b/light-clients/cf-guest/src/client_def.rs index 454a7d8ca..28e2694cf 100644 --- a/light-clients/cf-guest/src/client_def.rs +++ b/light-clients/cf-guest/src/client_def.rs @@ -186,7 +186,7 @@ where let path = ibc_core_host_types::path::ChannelEndPath(convert(port_id), convert(channel_id)); let value = expected_channel_end.clone().encode_vec(); - verify(proof, root, path.into(), Some(value)).map_err(|e| e.into()) + verify(proof, root, path.into(), Some(value)) } fn verify_client_full_state( @@ -204,7 +204,7 @@ where let path = ibc_core_host_types::path::ClientStatePath(convert(client_id)); let value = expected_client_state.encode_to_vec().map_err(Ics02ClientError::encode)?; - verify(proof, root, path.into(), Some(value)).map_err(|e| e.into()) + verify(proof, root, path.into(), Some(value)) } fn verify_packet_data( @@ -458,7 +458,7 @@ fn verify( path: ibc_core_host_types::path::Path, value: Option>, ) -> Result<(), Ics02ClientError> { - cf_guest_upstream::proof::verify( + cf_guest_upstream::proof::verify_for_block( &[], proof.as_bytes(), root.bytes.as_slice(), diff --git a/light-clients/cf-guest/src/proof.rs b/light-clients/cf-guest/src/proof.rs index 4966ce4c6..653cbf1b6 100644 --- a/light-clients/cf-guest/src/proof.rs +++ b/light-clients/cf-guest/src/proof.rs @@ -8,12 +8,8 @@ use ibc_core_host_types::path::{ mod ibc { pub use ibc::core::{ - ics02_client::error::Error as ClientError, - ics04_channel::packet::Sequence, ics23_commitment::commitment::{CommitmentPrefix, CommitmentProofBytes, CommitmentRoot}, ics24_host::{ - identifier, - identifier::{ChannelId, ClientId, ConnectionId, PortId}, path, }, }; @@ -58,7 +54,7 @@ pub fn generate( path: ibc::path::Path, ) -> Result { let path = convert_old_path_to_new(path); - cf_guest_upstream::proof::generate(block_header, trie, path) + cf_guest_upstream::proof::generate_for_block(block_header, trie, path) } /// Verifies a proof for given entry or lack of entry. @@ -101,7 +97,7 @@ pub fn verify_bytes( path: ibc::path::Path, value: Option<&[u8]>, ) -> Result<(), VerifyError> { - cf_guest_upstream::proof::verify(prefix, proof, root, convert_old_path_to_new(path), value) + cf_guest_upstream::proof::verify_for_block(prefix, proof, root, convert_old_path_to_new(path), value) } fn convert_old_path_to_new(path: ibc::path::Path) -> ibc_core_host_types::path::Path { @@ -162,13 +158,13 @@ fn convert_old_path_to_new(path: ibc::path::Path) -> ibc_core_host_types::path:: channel_id: ibc_core_host_types::identifiers::ChannelId::new( e.channel_id.sequence(), ), - sequence: u64::from(e.sequence.0).into(), + sequence: e.sequence.0.into(), }), ::ibc::core::ics24_host::Path::Acks(e) => ibc_core_host_types::path::Path::Ack(AckPath { port_id: ibc_core_host_types::identifiers::PortId::from_str(e.port_id.as_str()) .unwrap(), channel_id: ibc_core_host_types::identifiers::ChannelId::new(e.channel_id.sequence()), - sequence: u64::from(e.sequence.0).into(), + sequence: e.sequence.0.into(), }), ::ibc::core::ics24_host::Path::Receipts(e) => ibc_core_host_types::path::Path::Receipt(ReceiptPath { @@ -177,7 +173,7 @@ fn convert_old_path_to_new(path: ibc::path::Path) -> ibc_core_host_types::path:: channel_id: ibc_core_host_types::identifiers::ChannelId::new( e.channel_id.sequence(), ), - sequence: u64::from(e.sequence.0).into(), + sequence: e.sequence.0.into(), }), ::ibc::core::ics24_host::Path::Upgrade(path) => { use ::ibc::core::ics24_host::ClientUpgradePath; diff --git a/light-clients/cf-solana/Cargo.toml b/light-clients/cf-solana/Cargo.toml new file mode 100644 index 000000000..99d3da59d --- /dev/null +++ b/light-clients/cf-solana/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "cf-solana" +authors = ["Michal Nazarewicz ", "Dhruv D Jain "] +version = "0.1.0" +edition = "2021" + +[dependencies] +borsh = "0.10" +bytemuck = { version = "1.14", default-features = false, features = ["must_cast"]} +derive_more = { version = "0.99", features = ["from"], default-features = false } +prost = { version = "0.11" ,features = ["prost-derive"], default-features = false } +prost-12 = { package = "prost", version = "0.12", default-features = false } +ed25519-consensus = { version = "2", default-features = false } +serde = { version = "1.0", default-features = false, features = ["derive"] } + +# New IBC +ibc-core-client-context = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-client-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-handler-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-core-host-types = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } +ibc-primitives = { git = "https://github.com/mina86/ibc-rs", rev = "e1be8c9292c82c1e7c158067f0014fb292ee652d", default-features = false } + +# Old IBC +ibc = { path = "../../ibc/modules", default-features = false } +ibc-proto = { path = "../../ibc/proto", default-features = false } +ibc-derive = { path = "../../ibc/derive", default-features = false } + +tendermint-proto = { git = "https://github.com/mina86/tendermint-rs", rev = "45fbd500d731effb95a98257630feb46f6c41d06", default-features = false } + +guestchain = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false } +lib = { git = "https://github.com/ComposableFi/emulated-light-client/", features = ["borsh"], default-features = false } +trie-ids = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false } +sealable-trie = { git = "https://github.com/ComposableFi/emulated-light-client/", features = ["borsh"], default-features = false } +stdx = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false } +cf-solana-upstream = { package = "cf-solana", git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false } + +[build-dependencies] +prost-build = { version = "0.11", default-features = false } + +[dev-dependencies] +insta = { version = "1.34.0" } +rand = { version = "0.8.5" } + +guestchain = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false, features = ["test_utils"] } +lib = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false, features = ["test_utils"] } +memory = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false, features = ["test_utils"] } + +[features] +std = [] diff --git a/light-clients/cf-solana/src/client.rs b/light-clients/cf-solana/src/client.rs new file mode 100644 index 000000000..145f8149a --- /dev/null +++ b/light-clients/cf-solana/src/client.rs @@ -0,0 +1,110 @@ +use core::num::NonZeroU64; + +use crate::{client_def::SolanaClient, CLIENT_TYPE}; +use alloc::string::{String, ToString}; +use ibc::{ + core::{ics02_client::height::Height, ics24_host::identifier::ClientId}, + timestamp::Timestamp, +}; +use serde::{Deserialize, Serialize}; +use crate::error::Error; + +super::wrap!(cf_solana_upstream::ClientState as ClientState); +super::wrap!(impl proto for ClientState); +super::wrap!(impl Eq for ClientState); + +impl ClientState { + pub fn with_header(&self, header: &cf_solana_upstream::Header) -> Self { + Self(self.0.with_header(header)) + } + + pub fn frozen(&self) -> Self { + Self(self.0.frozen()) + } + + /// Verify the time and height delays + pub fn verify_delay_passed( + current_time: Timestamp, + current_height: Height, + processed_time: u64, + processed_height: u64, + delay_period_time: u64, + delay_period_blocks: u64, + ) -> Result<(), Error> { + let earliest_time = processed_time + delay_period_time; + // NOTE: delay time period is inclusive, so if current_time is earliest_time, then we + // return no error https://github.com/cosmos/ibc-go/blob/9ebc2f81049869bc40c443ffb72d9f3e47afb4fc/modules/light-clients/07-tendermint/client_state.go#L306 + if current_time.nanoseconds() < earliest_time { + return Err(Error::NotEnoughTimeElapsed { current_time, earliest_time }) + } + + let earliest_height = processed_height + delay_period_blocks; + if current_height.revision_height < earliest_height { + return Err(Error::NotEnoughBlocksElapsed { current_height, earliest_height }) + } + + Ok(()) + } + + pub fn verify_height(&self, client_id: &ClientId, height: ibc::Height) -> Result<(), Error> { + if u64::from(self.0.latest_slot) < height.revision_height { + return Err(Error::InsufficientHeight { + latest_height: Height::new(1, self.0.latest_slot.into()), + target_height: height, + }) + } + + if self.0.is_frozen { + return Err(Error::ClientFrozen { client_id: client_id.clone() }) + } + Ok(()) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct UpgradeOptions {} + +impl ibc::core::ics02_client::client_state::ClientState for ClientState +{ + type UpgradeOptions = UpgradeOptions; + + type ClientDef = SolanaClient; + + fn chain_id(&self) -> ibc::core::ics24_host::identifier::ChainId { + ibc::core::ics24_host::identifier::ChainId::new(String::from("Mantis"), 0) + } + + fn client_def(&self) -> Self::ClientDef { + SolanaClient::default() + } + + fn client_type(&self) -> ibc::core::ics02_client::client_state::ClientType { + CLIENT_TYPE.to_string() + } + + fn latest_height(&self) -> ibc::Height { + Height::new(1, u64::from(self.0.latest_slot)) + } + + fn frozen_height(&self) -> Option { + self.0.is_frozen.then(|| Height::new(1, u64::from(self.0.latest_slot))) + } + + fn upgrade( + mut self, + upgrade_height: ibc::Height, + _upgrade_options: Self::UpgradeOptions, + _chain_id: ibc::core::ics24_host::identifier::ChainId, + ) -> Self { + self.0.latest_slot = NonZeroU64::new(upgrade_height.revision_height).unwrap(); + self + } + + fn expired(&self, elapsed: core::time::Duration) -> bool { + elapsed.as_nanos() as u64 > self.0.trusting_period_ns + } + + fn encode_to_vec(&self) -> Result, ibc::protobuf::Error> { + Ok(self.0.encode()) + } +} diff --git a/light-clients/cf-solana/src/client_def.rs b/light-clients/cf-solana/src/client_def.rs new file mode 100644 index 000000000..7ef94efe0 --- /dev/null +++ b/light-clients/cf-solana/src/client_def.rs @@ -0,0 +1,551 @@ +use core::str::FromStr; + +use crate::alloc::string::ToString; +use alloc::vec::Vec; +use ibc::{ + core::{ + ics02_client::{ + client_consensus::ConsensusState, + client_def::{ClientDef, ConsensusUpdateResult}, + client_state::ClientState as OtherClientState, + error::Error as Ics02ClientError, + }, + ics26_routing::context::ReaderContext, + }, + protobuf::Protobuf, +}; +use prost::Message; + +use crate::{error::Error, ClientMessage, ClientState, ConsensusState as ClientConsensusState}; + +type Result = ::core::result::Result; + +#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Default)] +pub struct SolanaClient {} + + +impl ClientDef for SolanaClient +{ + type ClientMessage = ClientMessage; + type ClientState = ClientState; + type ConsensusState = ClientConsensusState; + + fn verify_client_message( + &self, + _ctx: &Ctx, + _client_id: ibc::core::ics24_host::identifier::ClientId, + client_state: Self::ClientState, + client_msg: Self::ClientMessage, + ) -> Result<(), Ics02ClientError> { + let ctx = CommonContext::new(_ctx); + client_state.0.do_verify_client_message(ctx, client_msg.0).map_err(convert) + } + + fn update_state( + &self, + _ctx: &Ctx, + _client_id: ibc::core::ics24_host::identifier::ClientId, + client_state: Self::ClientState, + client_msg: Self::ClientMessage, + ) -> Result< + (Self::ClientState, ibc::core::ics02_client::client_def::ConsensusUpdateResult), + Ics02ClientError, + > { + let header = match client_msg.0 { + cf_solana_upstream::ClientMessage::Header(header) => header, + _ => unreachable!("02-client will check for Header before calling update_state; qed"), + }; + let header_consensus_state = ClientConsensusState::from(&header); + let cs = Ctx::AnyConsensusState::wrap(&header_consensus_state).ok_or_else(|| { + Error::UnknownConsensusStateType { description: "Ctx::AnyConsensusState".to_string() } + })?; + Ok((client_state.with_header(&header), ConsensusUpdateResult::Single(cs))) + } + + fn update_state_on_misbehaviour( + &self, + client_state: Self::ClientState, + _client_msg: Self::ClientMessage, + ) -> Result { + Ok(client_state.frozen()) + } + + fn check_for_misbehaviour( + &self, + ctx: &Ctx, + client_id: ibc::core::ics24_host::identifier::ClientId, + client_state: Self::ClientState, + client_msg: Self::ClientMessage, + ) -> Result { + let client_id = convert(client_id); + let ctx = CommonContext::new(ctx); + client_state + .0 + .do_check_for_misbehaviour(ctx, &client_id, client_msg.0) + .map_err(convert) + } + + fn verify_upgrade_and_update_state( + &self, + _ctx: &Ctx, + _client_id: ibc::core::ics24_host::identifier::ClientId, + _old_client_state: &Self::ClientState, + _upgrade_client_state: &Self::ClientState, + _upgrade_consensus_state: &Self::ConsensusState, + _proof_upgrade_client: ibc::prelude::Vec, + _proof_upgrade_consensus_state: ibc::prelude::Vec, + ) -> Result< + (Self::ClientState, ibc::core::ics02_client::client_def::ConsensusUpdateResult), + Ics02ClientError, + > { + // TODO: tendermint verify_upgrade_and_update_state + Err(Ics02ClientError::implementation_specific("Not implemented".to_string())) + } + + fn check_substitute_and_update_state( + &self, + _ctx: &Ctx, + _subject_client_id: ibc::core::ics24_host::identifier::ClientId, + _substitute_client_id: ibc::core::ics24_host::identifier::ClientId, + _old_client_state: Self::ClientState, + _substitute_client_state: Self::ClientState, + ) -> Result< + (Self::ClientState, ibc::core::ics02_client::client_def::ConsensusUpdateResult), + Ics02ClientError, + > { + // TODO: tendermint check_substitute_and_update_state + Err(Ics02ClientError::implementation_specific("Not implemented".to_string())) + } + + fn verify_client_consensus_state( + &self, + _ctx: &Ctx, + client_state: &Self::ClientState, + height: ibc::Height, + _prefix: &ibc::core::ics23_commitment::commitment::CommitmentPrefix, + proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes, + root: &ibc::core::ics23_commitment::commitment::CommitmentRoot, + client_id: &ibc::core::ics24_host::identifier::ClientId, + consensus_height: ibc::Height, + expected_consensus_state: &Ctx::AnyConsensusState, + ) -> Result<(), Ics02ClientError> { + client_state.verify_height(client_id, height)?; + + let path = ibc_core_host_types::path::ClientConsensusStatePath { + client_id: convert(client_id), + revision_number: consensus_height.revision_number, + revision_height: consensus_height.revision_height, + }; + let value = expected_consensus_state.encode_to_vec().map_err(Ics02ClientError::encode)?; + verify(proof, root, path.into(), Some(value)) + } + + fn verify_connection_state( + &self, + _ctx: &Ctx, + client_id: &ibc::core::ics24_host::identifier::ClientId, + client_state: &Self::ClientState, + height: ibc::Height, + _prefix: &ibc::core::ics23_commitment::commitment::CommitmentPrefix, + proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes, + root: &ibc::core::ics23_commitment::commitment::CommitmentRoot, + connection_id: &ibc::core::ics24_host::identifier::ConnectionId, + expected_connection_end: &ibc::core::ics03_connection::connection::ConnectionEnd, + ) -> Result<(), Ics02ClientError> { + client_state.verify_height(client_id, height)?; + + let path = ibc_core_host_types::path::ConnectionPath(convert(connection_id)); + let value = expected_connection_end.clone().encode_vec(); + verify(proof, root, path.into(), Some(value)) + } + + fn verify_channel_state( + &self, + _ctx: &Ctx, + client_id: &ibc::core::ics24_host::identifier::ClientId, + client_state: &Self::ClientState, + height: ibc::Height, + _prefix: &ibc::core::ics23_commitment::commitment::CommitmentPrefix, + proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes, + root: &ibc::core::ics23_commitment::commitment::CommitmentRoot, + port_id: &ibc::core::ics24_host::identifier::PortId, + channel_id: &ibc::core::ics24_host::identifier::ChannelId, + expected_channel_end: &ibc::core::ics04_channel::channel::ChannelEnd, + ) -> Result<(), Ics02ClientError> { + client_state.verify_height(client_id, height)?; + + let path = ibc_core_host_types::path::ChannelEndPath(convert(port_id), convert(channel_id)); + let value = expected_channel_end.clone().encode_vec(); + verify(proof, root, path.into(), Some(value)) + } + + fn verify_client_full_state( + &self, + _ctx: &Ctx, + client_state: &Self::ClientState, + height: ibc::Height, + _prefix: &ibc::core::ics23_commitment::commitment::CommitmentPrefix, + proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes, + root: &ibc::core::ics23_commitment::commitment::CommitmentRoot, + client_id: &ibc::core::ics24_host::identifier::ClientId, + expected_client_state: &Ctx::AnyClientState, + ) -> Result<(), Ics02ClientError> { + client_state.verify_height(client_id, height)?; + + let path = ibc_core_host_types::path::ClientStatePath(convert(client_id)); + let value = expected_client_state.encode_to_vec().map_err(Ics02ClientError::encode)?; + verify(proof, root, path.into(), Some(value)) + } + + fn verify_packet_data( + &self, + ctx: &Ctx, + client_id: &ibc::core::ics24_host::identifier::ClientId, + client_state: &Self::ClientState, + height: ibc::Height, + connection_end: &ibc::core::ics03_connection::connection::ConnectionEnd, + proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes, + root: &ibc::core::ics23_commitment::commitment::CommitmentRoot, + port_id: &ibc::core::ics24_host::identifier::PortId, + channel_id: &ibc::core::ics24_host::identifier::ChannelId, + sequence: ibc::core::ics04_channel::packet::Sequence, + commitment: ibc::core::ics04_channel::commitment::PacketCommitment, + ) -> Result<(), Ics02ClientError> { + client_state.verify_height(client_id, height)?; + verify_delay_passed::(ctx, height, connection_end)?; + + let path = ibc_core_host_types::path::CommitmentPath { + port_id: convert(port_id), + channel_id: convert(channel_id), + sequence: sequence.0.into(), + }; + verify(proof, root, path.into(), Some(commitment.into_vec())) + } + + fn verify_packet_acknowledgement( + &self, + ctx: &Ctx, + client_id: &ibc::core::ics24_host::identifier::ClientId, + client_state: &Self::ClientState, + height: ibc::Height, + connection_end: &ibc::core::ics03_connection::connection::ConnectionEnd, + proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes, + root: &ibc::core::ics23_commitment::commitment::CommitmentRoot, + port_id: &ibc::core::ics24_host::identifier::PortId, + channel_id: &ibc::core::ics24_host::identifier::ChannelId, + sequence: ibc::core::ics04_channel::packet::Sequence, + ack: ibc::core::ics04_channel::commitment::AcknowledgementCommitment, + ) -> Result<(), Ics02ClientError> { + // client state height = consensus state height + client_state.verify_height(client_id, height)?; + verify_delay_passed::(ctx, height, connection_end)?; + + let path = ibc_core_host_types::path::AckPath { + port_id: convert(port_id), + channel_id: convert(channel_id), + sequence: sequence.0.into(), + }; + verify(proof, root, path.into(), Some(ack.into_vec())) + } + + fn verify_next_sequence_recv( + &self, + ctx: &Ctx, + client_id: &ibc::core::ics24_host::identifier::ClientId, + client_state: &Self::ClientState, + height: ibc::Height, + connection_end: &ibc::core::ics03_connection::connection::ConnectionEnd, + proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes, + root: &ibc::core::ics23_commitment::commitment::CommitmentRoot, + port_id: &ibc::core::ics24_host::identifier::PortId, + channel_id: &ibc::core::ics24_host::identifier::ChannelId, + sequence: ibc::core::ics04_channel::packet::Sequence, + ) -> Result<(), Ics02ClientError> { + client_state.verify_height(client_id, height)?; + verify_delay_passed::(ctx, height, connection_end)?; + + let path = ibc_core_host_types::path::SeqRecvPath(convert(port_id), convert(channel_id)); + let mut seq_bytes = Vec::new(); + u64::from(sequence).encode(&mut seq_bytes).expect("buffer size too small"); + verify(proof, root, path.into(), Some(seq_bytes)) + } + + fn verify_packet_receipt_absence( + &self, + ctx: &Ctx, + client_id: &ibc::core::ics24_host::identifier::ClientId, + client_state: &Self::ClientState, + height: ibc::Height, + connection_end: &ibc::core::ics03_connection::connection::ConnectionEnd, + proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes, + root: &ibc::core::ics23_commitment::commitment::CommitmentRoot, + port_id: &ibc::core::ics24_host::identifier::PortId, + channel_id: &ibc::core::ics24_host::identifier::ChannelId, + sequence: ibc::core::ics04_channel::packet::Sequence, + ) -> Result<(), Ics02ClientError> { + client_state.verify_height(client_id, height)?; + verify_delay_passed::(ctx, height, connection_end)?; + + let path = ibc_core_host_types::path::ReceiptPath { + port_id: convert(port_id), + channel_id: convert(channel_id), + sequence: sequence.0.into(), + }; + verify(proof, root, path.into(), None) + } +} + +fn verify_delay_passed( + ctx: &Ctx, + height: ibc::Height, + connection_end: &ibc::core::ics03_connection::connection::ConnectionEnd, +) -> Result<(), Ics02ClientError> { + let current_timestamp = ctx.host_timestamp(); + let current_height = ctx.host_height(); + + let client_id = connection_end.client_id(); + let processed_time = ctx + .client_update_time(client_id, height) + .map_err(|_| Error::ProcessedTimeNotFound { height })?; + let processed_height = ctx + .client_update_height(client_id, height) + .map_err(|_| Error::ProcessedHeightNotFound { height })?; + + let delay_period_time = connection_end.delay_period(); + let delay_period_height = ctx.block_delay(delay_period_time); + let delay_period_time_u64 = u64::try_from(delay_period_time.as_nanos()).unwrap(); + + ClientState::verify_delay_passed( + current_timestamp, + current_height, + processed_time.nanoseconds(), + processed_height.revision_height, + delay_period_time_u64, + delay_period_height, + ) + .map_err(|e| e.into()) +} + +// impl Verifier for SolanaClient { +// fn verify(&self, message: &[u8], pubkey: &PK, signature: &PK::Signature) -> bool { +// (|| { +// let pubkey = pubkey.as_bytes(); +// let pubkey = ed25519_consensus::VerificationKey::try_from(&pubkey[..]).ok()?; +// let signature = signature.as_bytes(); +// let sig = ed25519_consensus::Signature::try_from(&signature[..]).ok()?; +// pubkey.verify(&sig, message).ok()?; +// Some(()) +// })() +// .is_some() +// } +// } + +#[derive(bytemuck::TransparentWrapper)] +#[repr(transparent)] +#[transparent(Ctx)] +struct CommonContext { + ctx: Ctx, +} + +impl CommonContext { + fn new(ctx: &Ctx) -> &Self { + bytemuck::TransparentWrapper::wrap_ref(ctx) + } +} + +type NewResult = Result; + +impl cf_solana_upstream::CommonContext + for CommonContext +{ + type ConversionError = core::convert::Infallible; + type AnyClientState = ClientState; + type AnyConsensusState = ClientConsensusState; + + fn host_metadata( + &self, + ) -> NewResult<(ibc_primitives::Timestamp, ibc_core_client_types::Height)> { + unimplemented!("host_metadata") + } + + fn set_client_state( + &mut self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + _state: ClientState, + ) -> NewResult<()> { + unimplemented!("set_client_state") + } + + fn consensus_state( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + _height: ibc_core_client_types::Height, + ) -> NewResult { + unimplemented!("consensus_state") + } + + fn consensus_state_neighbourhood( + &self, + client_id: &ibc_core_host_types::identifiers::ClientId, + height: ibc_core_client_types::Height, + ) -> NewResult> { + use cf_solana_upstream::Neighbourhood; + + let res: Result<_, Ics02ClientError> = (|| { + let client_id = convert(client_id); + let height = convert(height); + Ok(if let Some(state) = self.ctx.maybe_consensus_state(&client_id, height)? { + Neighbourhood::This(state) + } else { + let prev = self.ctx.prev_consensus_state(&client_id, height)?; + let next = self.ctx.next_consensus_state(&client_id, height)?; + Neighbourhood::Neighbours(prev, next) + }) + })(); + match res { + Ok(res) => Ok(res.map(|state: Ctx::AnyConsensusState| { + // TODO(mina86): propagate error rather than unwrapping + let state: Self::AnyConsensusState = state.downcast().unwrap(); + state + })), + Err(err) => Err(convert(err)), + } + } + + fn store_consensus_state_and_metadata( + &mut self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + _height: ibc_core_client_types::Height, + _consensus: Self::AnyConsensusState, + _host_timestamp: ibc_primitives::Timestamp, + _host_height: ibc_core_client_types::Height, + ) -> NewResult { + unimplemented!("store_consensus_state_and_metadata") + } + + fn delete_consensus_state_and_metadata( + &mut self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + _height: ibc_core_client_types::Height, + ) -> NewResult { + unimplemented!("delete_consensus_state_and_metadata") + } + + fn earliest_consensus_state( + &self, + _client_id: &ibc_core_host_types::identifiers::ClientId, + ) -> NewResult> { + unimplemented!("earliest_consensus_state") + } +} + +// Helper wrappers + +fn verify( + proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes, + root: &ibc::core::ics23_commitment::commitment::CommitmentRoot, + path: ibc_core_host_types::path::Path, + value: Option>, +) -> Result<(), Ics02ClientError> { + cf_solana_upstream::proof::verify_for_trie( + &[], + proof.as_bytes(), + root.bytes.as_slice(), + path, + value.as_deref(), + ) + .map_err(|err| Ics02ClientError::implementation_specific(err.to_string())) +} + +// Conversion between old and new. + +fn convert>(value: F) -> T { + T::convert(value) +} + +trait ConvertFrom: Sized { + fn convert(value: From) -> Self; +} + +macro_rules! conv { + ($( $value:ident: $From:ty => $To:ty { $($body:tt)*} )*) => { + $( + impl ConvertFrom<$From> for $To { + fn convert($value: $From) -> Self { + Self::convert(&$value) + } + } + + impl ConvertFrom<&$From> for $To { + fn convert($value: &$From) -> Self { + $($body)* + } + } + )* + } +} + +conv! { + client_id: ibc::core::ics24_host::identifier::ClientId => + ibc_core_host_types::identifiers::ClientId { + FromStr::from_str(client_id.as_str()).unwrap() + } + client_id: ibc_core_host_types::identifiers::ClientId => ibc::core::ics24_host::identifier::ClientId { + FromStr::from_str(client_id.as_str()).unwrap() + } + + connection_id: ibc::core::ics24_host::identifier::ConnectionId => + ibc_core_host_types::identifiers::ConnectionId { + FromStr::from_str(connection_id.as_str()).unwrap() + } + + port_id: ibc::core::ics24_host::identifier::PortId => + ibc_core_host_types::identifiers::PortId { + FromStr::from_str(port_id.as_str()).unwrap() + } + + channel_id: ibc::core::ics24_host::identifier::ChannelId => + ibc_core_host_types::identifiers::ChannelId { + ibc_core_host_types::identifiers::ChannelId::new(channel_id.sequence()) + } + + + timestamp: ibc::timestamp::Timestamp => ibc_primitives::Timestamp { + Self::from_nanoseconds(timestamp.nanoseconds()).unwrap() + } + + height: ibc::core::ics02_client::height::Height => ibc_core_client_types::Height { + Self::new(height.revision_number, height.revision_height).unwrap() + } + + height: ibc_core_client_types::Height => ibc::core::ics02_client::height::Height { + Self::new(height.revision_number(), height.revision_height()) + } + + + err: ibc_core_client_context::types::error::ClientError => Ics02ClientError { + // TODO(mina86): Create better mapping. + Self::implementation_specific(err.to_string()) + } + err: Ics02ClientError => ibc_core_client_context::types::error::ClientError { + // TODO(mina86): Create better mapping. + Self::ClientSpecific { + description: err.to_string() + } + } +} + +impl> ConvertFrom> for Option { + fn convert(value: Option) -> Self { + value.map(convert) + } +} + +impl, TE: ConvertFrom> ConvertFrom> + for Result +{ + fn convert(value: Result) -> Self { + value.map(convert).map_err(convert) + } +} diff --git a/light-clients/cf-solana/src/client_impls.rs b/light-clients/cf-solana/src/client_impls.rs new file mode 100644 index 000000000..ace3bf593 --- /dev/null +++ b/light-clients/cf-solana/src/client_impls.rs @@ -0,0 +1,468 @@ +use core::num::NonZeroU64; + +use alloc::{string::ToString, vec::Vec}; + +use ibc_proto::google::protobuf::Any; + +use super::{ClientMessage, ClientState, ConsensusState}; +use cf_solana_upstream::Neighbourhood; + +mod ibc { + pub use ibc::core::ics02_client::{error::Error as ClientError, height::Height}; + pub use ibc::core::ics24_host::identifier::ClientId; + pub use ibc::timestamp::Timestamp; +} + +type Result = ::core::result::Result; + +pub trait CommonContext { + type ConversionError: ToString; + type AnyClientState: From; + type AnyConsensusState: TryInto + + From; + + fn host_metadata(&self) -> Result<(ibc::Timestamp, ibc::Height)>; + + fn set_client_state( + &mut self, + client_id: &ibc::ClientId, + state: Self::AnyClientState, + ) -> Result<()>; + + fn consensus_state( + &self, + client_id: &ibc::ClientId, + height: ibc::Height, + ) -> Result; + + /// Returns consensus at given height or its neighbours. + /// + /// If consensus state at given height returns `This(state)` for that state. + /// Otherwise, returns `Neighbours(prev, next)` where `prev` and `next` are + /// states with lower and greater height respectively if they exist. + fn consensus_state_neighbourhood( + &self, + client_id: &ibc::ClientId, + height: ibc::Height, + ) -> Result>; + + fn store_consensus_state_and_metadata( + &mut self, + client_id: &ibc::ClientId, + height: ibc::Height, + consensus: Self::AnyConsensusState, + host_timestamp: ibc::Timestamp, + host_height: ibc::Height, + ) -> Result; + + fn delete_consensus_state_and_metadata( + &mut self, + client_id: &ibc::ClientId, + height: ibc::Height, + ) -> Result; + + /// Returns earliest consensus state for given client. + fn earliest_consensus_state( + &self, + client_id: &ibc::ClientId, + ) -> Result>; +} + +// impl ibc::ClientStateCommon for ClientState { +// fn verify_consensus_state(&self, consensus_state: Any) -> Result { +// ConsensusState::try_from(consensus_state)?; +// Ok(()) +// } + +// fn client_type(&self) -> ibc::ClientType { +// ibc::ClientType::new(super::CLIENT_TYPE).unwrap() +// } + +// fn latest_height(&self) -> ibc::Height { +// ibc::Height::new(0, self.latest_height.into()).unwrap() +// } + +// fn validate_proof_height(&self, proof_height: ibc::Height) -> Result { +// let latest_height = self.latest_height(); +// if proof_height <= latest_height { +// Ok(()) +// } else { +// Err(ibc::ClientError::InvalidProofHeight { +// latest_height, +// proof_height, +// }) +// } +// } + +// /// Panics since client upgrades aren’t supported. +// fn verify_upgrade_client( +// &self, +// _upgraded_client_state: Any, +// _upgraded_consensus_state: Any, +// _proof_upgrade_client: ibc::CommitmentProofBytes, +// _proof_upgrade_consensus_state: ibc::CommitmentProofBytes, +// _root: &ibc::CommitmentRoot, +// ) -> Result { unimplemented!("IBC cilent upgrades are currently not supported") +// } + +// /// Verifies membership proof. +// /// +// /// See [`proof::verify`] for documentation of the proof format. +// fn verify_membership( +// &self, +// prefix: &ibc::CommitmentPrefix, +// proof: &ibc::CommitmentProofBytes, +// root: &ibc::CommitmentRoot, +// path: ibc::path::Path, +// value: Vec, +// ) -> Result { let value = Some(value.as_slice()); proof::verify(prefix, proof, root, path, +// value).map_err(Into::into) +// } + +// /// Verifies membership proof. +// /// +// /// See [`proof::verify`] for documentation of the proof format. +// fn verify_non_membership( +// &self, +// prefix: &ibc::CommitmentPrefix, +// proof: &ibc::CommitmentProofBytes, +// root: &ibc::CommitmentRoot, +// path: ibc::path::Path, +// ) -> Result { proof::verify(prefix, proof, root, path, None).map_err(Into::into) +// } +// } + +// impl From for ibc::ClientError { +// fn from(err: proof::VerifyError) -> Self { +// use ::ibc::core::ics23_commitment::error::Error; +// use proof::VerifyError::*; + +// Self::invalid_commitment_proof(match err { +// ProofDecodingFailure(msg) => +// Error::commitment_proof_decoding_failed(DecodeError::new(msg)), +// WrongSequenceNumber(err) => Error::commitment_proof_decoding_failed(err), +// _ => ibc::CommitmentError::invalid_merkle_proof(), +// }) +// } +// } +// impl ibc::ClientStateExecution for ClientState +// where +// E: ibc::ExecutionContext + ibc::ClientExecutionContext + CommonContext, +// ::AnyClientState: From>, +// ::AnyConsensusState: From, +// { +// fn initialise( +// &self, +// ctx: &mut E, +// client_id: &ibc::ClientId, +// consensus_state: Any, +// ) -> Result { parse_client_id(client_id)?; let consensus_state = +// super::ConsensusState::try_from(consensus_state)?; + +// ctx.store_client_state( +// ibc::path::ClientStatePath::new(client_id.clone()), +// self.clone().into(), +// )?; +// ctx.store_consensus_state( +// ibc::path::ClientConsensusStatePath::new( +// client_id.clone(), +// 0, +// u64::from(self.latest_height), +// ), +// consensus_state.into(), +// )?; + +// Ok(()) +// } + +// fn update_state( +// &self, +// ctx: &mut E, +// client_id: &ibc::ClientId, +// header: Any, +// ) -> Result> { let header = crate::proto::Header::try_from(header)?; let +// header = crate::Header::::try_from(header)?; let header_height = ibc::Height::new(0, +// header.block_header.block_height.into())?; + +// let (host_timestamp, host_height) = CommonContext::host_metadata(ctx)?; +// self.prune_oldest_consensus_state(ctx, client_id, host_timestamp)?; + +// let maybe_existing_consensus = +// CommonContext::consensus_state(ctx, client_id, header_height).ok(); +// if maybe_existing_consensus.is_none() { +// let new_consensus_state = ConsensusState::from(&header); +// let new_client_state = self.with_header(&header); + +// ctx.store_client_state( +// ibc::path::ClientStatePath::new(client_id.clone()), +// new_client_state.into(), +// )?; +// ctx.store_consensus_state_and_metadata( +// client_id, +// header_height, +// new_consensus_state.into(), +// host_timestamp, +// host_height, +// )?; +// } + +// Ok(alloc::vec![header_height]) +// } + +// fn update_state_on_misbehaviour( +// &self, +// ctx: &mut E, +// client_id: &ibc::ClientId, +// _client_message: Any, +// ) -> Result { ctx.store_client_state( ibc::path::ClientStatePath::new(client_id.clone()), +// self.frozen().into(), )?; Ok(()) +// } + +// fn update_state_on_upgrade( +// &self, +// _ctx: &mut E, +// _client_id: &ibc::ClientId, +// _upgraded_client_state: Any, +// _upgraded_consensus_state: Any, +// ) -> Result { Err(ibc::UpgradeClientError::Other { reason: "upgrade not +// supported".into(), } .into()) +// } +// } + +// impl ibc::ClientStateValidation for ClientState +// where +// V: ibc::ValidationContext +// + ibc::ClientValidationContext +// + CommonContext +// + guestchain::Verifier, +// { +// fn verify_client_message( +// &self, +// ctx: &V, +// client_id: &ibc::ClientId, +// client_message: Any, +// ) -> Result { self.verify_client_message(ctx, client_id, client_message) +// } + +// fn check_for_misbehaviour( +// &self, +// ctx: &V, +// client_id: &ibc::ClientId, +// client_message: Any, +// ) -> Result { self.check_for_misbehaviour(ctx, client_id, client_message) +// } + +// fn status( +// &self, +// ctx: &V, +// client_id: &ibc::ClientId, +// ) -> Result { if self.is_frozen { return Ok(ibc::Status::Frozen); } + +// let height = ibc::Height::new(0, self.latest_height.into())?; +// let consensus = CommonContext::consensus_state(ctx, client_id, height) +// .and_then(|state| state.try_into().map_err(error)); +// let consensus = match consensus { +// Ok(consensus) => consensus, +// Err(ibc::ClientError::ConsensusStateNotFound { .. }) => { +// return Ok(ibc::Status::Expired) +// } +// Err(err) => return Err(err), +// }; + +// let (host_timestamp, _height) = CommonContext::host_metadata(ctx)?; +// Ok(if self.consensus_has_expired(&consensus, host_timestamp) { +// ibc::Status::Expired +// } else { +// ibc::Status::Active +// }) +// } +// } + +impl ClientState { + pub fn do_update_state( + &self, + ctx: &mut impl CommonContext, + client_id: &ibc::ClientId, + header: cf_solana_upstream::Header, + ) -> Result> { + let header_height = ibc::Height::new(1, header.slot.into()); + let (host_timestamp, host_height) = CommonContext::host_metadata(ctx)?; + self.prune_oldest_consensus_state(ctx, client_id, host_timestamp)?; + + let maybe_existing_consensus = + CommonContext::consensus_state(ctx, client_id, header_height).ok(); + if maybe_existing_consensus.is_none() { + let new_consensus_state = ConsensusState::from(&header); + let new_client_state = self.with_header(&header); + + ctx.set_client_state(client_id, new_client_state.into())?; + ctx.store_consensus_state_and_metadata( + client_id, + header_height, + new_consensus_state.into(), + host_timestamp, + host_height, + )?; + } + + Ok(alloc::vec![header_height]) + } + + pub fn do_verify_client_message( + &self, + _ctx: &impl CommonContext, + client_message: cf_solana_upstream::ClientMessage, + ) -> Result<()> { + match client_message { + cf_solana_upstream::ClientMessage::Header(header) => self.verify_header(header), + cf_solana_upstream::ClientMessage::Misbehaviour(misbehaviour) => { + let cf_solana_upstream::Misbehaviour { header1, header2 } = misbehaviour; + self.verify_header(header1)?; + self.verify_header(header2)?; + Ok(()) + }, + } + } + + pub fn check_for_misbehaviour( + &self, + ctx: &impl CommonContext, + client_id: &ibc::ClientId, + client_message: Any, + ) -> Result { + let client_message = ClientMessage::try_from(client_message)?; + self.do_check_for_misbehaviour(ctx, client_id, client_message) + } + + pub fn do_check_for_misbehaviour( + &self, + ctx: &impl CommonContext, + client_id: &ibc::ClientId, + client_message: ClientMessage, + ) -> Result { + match client_message.0 { + cf_solana_upstream::ClientMessage::Header(header) => { + self.check_for_misbehaviour_in_header(ctx, client_id, header) + }, + cf_solana_upstream::ClientMessage::Misbehaviour(misbehaviour) => { + self.check_for_misbehaviour_in_misbehavior(ctx, client_id, misbehaviour) + }, + } + } + + fn verify_header(&self, header: cf_solana_upstream::Header) -> Result<()> { + let cf_solana_upstream::Header { bank_hash, delta_hash_proof, witness_proof, .. } = header; + if bank_hash != delta_hash_proof.calculate_bank_hash() { + Err(error("Invalid accounts delta hash proof")) + } else if delta_hash_proof.accounts_delta_hash != witness_proof.expected_root() { + Err(error("Invalid witness proof")) + } else if witness_proof.account_hash_data.key() != &self.0.witness_account { + Err(error("Invalid witness account")) + } else { + Ok(()) + } + } + + fn check_for_misbehaviour_in_header( + &self, + ctx: &impl CommonContext, + client_id: &ibc::ClientId, + header: cf_solana_upstream::Header, + ) -> Result { + fn check_timestamp, E: ToString>( + state: Option, + test: impl FnOnce(NonZeroU64) -> bool, + ) -> Result { + match state.map(|state| state.try_into()) { + None => Ok(false), + Some(Ok(state)) => Ok(test(state.0.timestamp_sec)), + Some(Err(err)) => Err(error(err)), + } + } + + let height = ibc::Height::new(1, header.slot.into()); + Ok(match ctx.consensus_state_neighbourhood(client_id, height)? { + Neighbourhood::This(state) => { + // If we already have existing consensus for given height, check + // that what we’ve been sent is the same thing we have. If it + // isn’t, that’s evidence of misbehaviour. + let existing_state = state.try_into().map_err(error)?; + let header_state = ConsensusState::from(&header); + existing_state != header_state + }, + + Neighbourhood::Neighbours(prev, next) => { + // Otherwise, first try to decode witness in the header. If + // it’s invalid that this doesn't prove misbehaviour (though it + // also won’t update the consensus but that’s handled in + // do_update_state). + let current = match header.decode_witness() { + Some((_, timestamp_sec)) => timestamp_sec, + None => return Ok(false), + }; + + // Make sure that timestamp of each consensus is non-decreasing. + // If it isn’t, that’s evidence of misbehaviour. Solana uses + // timestamps with second-granularity with sub-second blocks so + // consecutive slots may have the same timestamp. + check_timestamp(prev, |prev| current < prev)? + || check_timestamp(next, |next| next < current)? + }, + }) + } + + fn check_for_misbehaviour_in_misbehavior( + &self, + _ctx: &impl CommonContext, + _client_id: &ibc::ClientId, + misbehaviour: cf_solana_upstream::Misbehaviour, + ) -> Result { + let cf_solana_upstream::Misbehaviour { header1, header2 } = misbehaviour; + if header1.slot == header2.slot { + // If blocks have the same height they must be the same, i.e. have + // the same witness account. + Ok(header1.witness_proof.account_hash_data != header2.witness_proof.account_hash_data) + } else { + // Otherwise, if blocks have different heights, their ordering must + // match ordering of their timestamps (with the exception that it’s + // valid for timestamps to be equal). + let mut first = header1.decode_witness().map(|(_, ts)| ts); + let mut second = header2.decode_witness().map(|(_, ts)| ts); + if header2.slot < header1.slot { + core::mem::swap(&mut first, &mut second); + } + Ok(first > second) + } + } + + /// Checks whether consensus state has expired. + fn consensus_has_expired( + &self, + consensus: &ConsensusState, + host_timestamp: ibc::Timestamp, + ) -> bool { + let expiry_ns = consensus.0.timestamp_sec.get().saturating_add(self.0.trusting_period_ns); + let host_timestamp_ns = host_timestamp.nanoseconds(); + expiry_ns <= host_timestamp_ns + } + + /// Removes all expired consensus states. + fn prune_oldest_consensus_state( + &self, + ctx: &mut impl CommonContext, + client_id: &ibc::ClientId, + host_timestamp: ibc::Timestamp, + ) -> Result { + if let Some((height, state)) = ctx.earliest_consensus_state(client_id)? { + let state = state.try_into().map_err(error)?; + if self.consensus_has_expired(&state, host_timestamp) { + ctx.delete_consensus_state_and_metadata(client_id, height)?; + } + } + Ok(()) + } +} + +fn error(msg: impl ToString) -> ibc::ClientError { + ibc::ClientError::implementation_specific(msg.to_string()) +} diff --git a/light-clients/cf-solana/src/consensus.rs b/light-clients/cf-solana/src/consensus.rs new file mode 100644 index 000000000..f9895be51 --- /dev/null +++ b/light-clients/cf-solana/src/consensus.rs @@ -0,0 +1,98 @@ +use core::{convert::Infallible, num::NonZeroU64}; + +use lib::hash::CryptoHash; +use prost::Message as _; + +use crate::proto; + +super::wrap!(cf_solana_upstream::ConsensusState as ConsensusState); +super::wrap!(impl Eq for ConsensusState); +super::wrap!(impl proto for ConsensusState); + +impl ConsensusState { + pub fn new(block_hash: &CryptoHash, timestamp_ns: NonZeroU64) -> Self { + Self(cf_solana_upstream::ConsensusState::new(block_hash, timestamp_ns)) + } +} + +impl ibc::core::ics02_client::client_consensus::ConsensusState for ConsensusState { + type Error = Infallible; + + fn root(&self) -> &ibc::core::ics23_commitment::commitment::CommitmentRoot { + // SAFETY: Both types are wrappers around Vec. + unsafe { core::mem::transmute(&self.0.trie_root) } + } + + fn timestamp(&self) -> ibc::timestamp::Timestamp { + let ns = self.0.timestamp_sec.get() * 1_000_000_000; + ibc::timestamp::Timestamp::from_nanoseconds(ns).unwrap() + } + + fn encode_to_vec(&self) -> Result, ibc::protobuf::Error> { + Ok(proto::ConsensusState::from(self).encode_to_vec()) + } +} + +impl From for ConsensusState { + fn from(header: crate::Header) -> Self { + Self::from(&header.0) + } +} + +impl From<&crate::Header> for ConsensusState { + fn from(header: &crate::Header) -> Self { + Self::from(&header.0) + } +} + +impl From for ConsensusState { + fn from(header: cf_solana_upstream::Header) -> Self { + Self::from(&header) + } +} + +impl From<&cf_solana_upstream::Header> for ConsensusState { + fn from(header: &cf_solana_upstream::Header) -> Self { + Self(header.try_into().unwrap()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const ANY_MESSAGE: [u8; 85] = [ + 10, 37, 47, 108, 105, 103, 104, 116, 99, 108, 105, 101, 110, 116, 115, 46, 103, 117, 101, + 115, 116, 46, 118, 49, 46, 67, 111, 110, 115, 101, 110, 115, 117, 115, 83, 116, 97, 116, + 101, 18, 44, 10, 32, 74, 147, 61, 207, 26, 96, 73, 253, 54, 118, 91, 237, 36, 210, 58, 218, + 179, 236, 158, 187, 5, 231, 241, 133, 178, 150, 85, 151, 36, 160, 36, 105, 16, 128, 220, + 164, 128, 131, 220, 190, 228, 23, + ]; + + fn message() -> &'static [u8] { + &ANY_MESSAGE[41..] + } + + const BLOCK_HASH: CryptoHash = CryptoHash([ + 74, 147, 61, 207, 26, 96, 73, 253, 54, 118, 91, 237, 36, 210, 58, 218, 179, 236, 158, 187, + 5, 231, 241, 133, 178, 150, 85, 151, 36, 160, 36, 105, + ]); + + fn check(state: ConsensusState) { + let want = ConsensusState::new(&BLOCK_HASH, NonZeroU64::new(1713895499000000000).unwrap()); + assert_eq!(want, state); + } + + #[test] + fn test_decode_vec() { + check(ibc::protobuf::Protobuf::decode_vec(message()).unwrap()); + } + + #[test] + fn test_from_any() { + use ibc_proto::google::protobuf::Any; + + let any: Any = prost::Message::decode(ANY_MESSAGE.as_ref()).unwrap(); + check(any.try_into().unwrap()); + } +} diff --git a/light-clients/cf-solana/src/error.rs b/light-clients/cf-solana/src/error.rs new file mode 100644 index 000000000..7c6cee943 --- /dev/null +++ b/light-clients/cf-solana/src/error.rs @@ -0,0 +1,43 @@ +// Copyright 2022 ComposableFi +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::CLIENT_TYPE; +use alloc::{ + fmt, + string::{String, ToString}, +}; +use ibc::{core::ics24_host::identifier::ClientId, timestamp::Timestamp, Height}; + +#[derive(Clone, Debug)] +pub enum Error { + ProcessedHeightNotFound { height: Height }, + ProcessedTimeNotFound { height: Height }, + NotEnoughTimeElapsed { current_time: Timestamp, earliest_time: u64 }, + NotEnoughBlocksElapsed { current_height: Height, earliest_height: u64 }, + InsufficientHeight { latest_height: Height, target_height: Height }, + ClientFrozen { client_id: ClientId }, + UnknownConsensusStateType { description: String }, +} + +impl fmt::Display for Error { + fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, fmtr) + } +} + +impl From for ibc::core::ics02_client::error::Error { + fn from(err: Error) -> Self { + Self::client_error(CLIENT_TYPE.into(), err.to_string()) + } +} diff --git a/light-clients/cf-solana/src/header.rs b/light-clients/cf-solana/src/header.rs new file mode 100644 index 000000000..c6ef1df22 --- /dev/null +++ b/light-clients/cf-solana/src/header.rs @@ -0,0 +1,2 @@ +super::wrap!(cf_solana_upstream::Header as Header); +super::wrap!(impl proto for Header); \ No newline at end of file diff --git a/light-clients/cf-solana/src/lib.rs b/light-clients/cf-solana/src/lib.rs new file mode 100644 index 000000000..febd221f9 --- /dev/null +++ b/light-clients/cf-solana/src/lib.rs @@ -0,0 +1,244 @@ +#![allow(clippy::unit_arg, clippy::comparison_chain)] +#![no_std] +extern crate alloc; +#[cfg(any(feature = "std", test))] +extern crate std; + +pub mod client; +pub mod proto; +mod error; +pub mod header; +pub mod client_def; +pub mod consensus; +pub mod message; +mod misbehaviour; +mod client_impls; + +use alloc::string::ToString; +// pub use client::impls::{CommonContext, Neighbourhood}; +pub use client::ClientState; +pub use consensus::ConsensusState; +pub use header::Header; +pub use message::ClientMessage; +pub use misbehaviour::Misbehaviour; +// pub use proof::IbcProof; + +use ibc::core::ics02_client::error::Error as ClientError; + +/// Client type of the Solana blockchain’s light client. +pub const CLIENT_TYPE: &str = "cf-solana"; + +pub use crate::proto::{BadMessage, DecodeError}; + +impl From for ClientError { + fn from(err: DecodeError) -> Self { + ClientError::implementation_specific(err.to_string()) + } +} + +impl From for ClientError { + fn from(_: BadMessage) -> Self { + ClientError::implementation_specific("BadMessage".to_string()) + } +} + +/// Returns digest of the value. +/// +/// This is used, among other places, as packet commitment. +#[inline] +pub fn digest(value: &[u8]) -> lib::hash::CryptoHash { + lib::hash::CryptoHash::digest(value) +} + +/// Returns digest of the value with client id mixed in. +/// +/// We don’t store full client id in the trie key for paths which include +/// client id. To avoid accepting malicious proofs, we must include it in +/// some other way. We do this by mixing in the client id into the hash of +/// the value stored at the path. +/// +/// Specifically, this calculates `digest(client_id || b'0' || serialised)`. +#[inline] +pub fn digest_with_client_id( + client_id: &ibc::core::ics24_host::identifier::ClientId, + value: &[u8], +) -> lib::hash::CryptoHash { + lib::hash::CryptoHash::digestv(&[client_id.as_bytes(), b"\0", value]) +} + +macro_rules! wrap { + ($($Inner:ident)::* as $Outer:ident) => { + #[derive(Clone, derive_more::From, derive_more::Into)] + #[repr(transparent)] + pub struct $Outer(pub $($Inner)::*); + + impl core::fmt::Debug for $Outer { + fn fmt(&self, fmtr: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(fmtr) + } + } + + impl From<$Outer> for ibc_proto::google::protobuf::Any { + fn from(msg: $Outer) -> Self { + Self::from(&msg) + } + } + + impl From<&$Outer> for ibc_proto::google::protobuf::Any { + fn from(msg: &$Outer) -> Self { + let any = cf_solana_upstream::proto::Any::from(&msg.0); + Self { + type_url: any.type_url, + value: any.value + } + } + } + + impl TryFrom for $Outer { + type Error = $crate::DecodeError; + fn try_from(any: ibc_proto::google::protobuf::Any) -> Result { + Self::try_from(&any) + } + } + + impl TryFrom<&ibc_proto::google::protobuf::Any> for $Outer { + type Error = $crate::DecodeError; + fn try_from(any: &ibc_proto::google::protobuf::Any) -> Result { + Ok(Self(cf_solana_upstream::proto::AnyConvert::try_from_any(&any.type_url, &any.value)?)) + } + } + }; + + ($($Inner:ident)::* as $Outer:ident) => { + #[derive(Clone, PartialEq, Eq, derive_more::From, derive_more::Into)] + #[repr(transparent)] + pub struct $Outer(pub $($Inner)::*); + + impl core::fmt::Debug for $Outer { + fn fmt(&self, fmtr: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(fmtr) + } + } + + impl From<$Outer> for ibc_proto::google::protobuf::Any { + fn from(msg: $Outer) -> Self { + Self::from(&msg) + } + } + + impl From<&$Outer> for ibc_proto::google::protobuf::Any { + fn from(msg: &$Outer) -> Self { + let any = cf_solana_upstream::proto::Any::from(&msg.0); + Self { + type_url: any.type_url, + value: any.value + } + } + } + + impl TryFrom for $Outer { + type Error = $crate::DecodeError; + fn try_from(any: ibc_proto::google::protobuf::Any) -> Result { + Self::try_from(&any) + } + } + + impl TryFrom<&ibc_proto::google::protobuf::Any> for $Outer { + type Error = $crate::DecodeError; + fn try_from(any: &ibc_proto::google::protobuf::Any) -> Result { + Ok(Self(cf_solana_upstream::proto::AnyConvert::try_from_any(&any.type_url, &any.value)?)) + } + } + }; + + (impl Default for $Outer:ident) => { + impl Default for $Outer { + fn default() -> Self { Self(Default::default()) } + } + }; + + (impl Default for $Outer:ident) => { + impl Default for $Outer { + fn default() -> Self { Self(Default::default()) } + } + }; + + (impl Eq for $Outer:ident) => { + impl core::cmp::PartialEq for $Outer { + fn eq(&self, other: &Self) -> bool { self.0.eq(&other.0) } + } + impl core::cmp::Eq for $Outer { } + }; + + (impl proto for $Type:ident) => { + impl $crate::proto::$Type { + pub const IBC_TYPE_URL: &'static str = + cf_solana_upstream::proto::$Type::IBC_TYPE_URL; + } + + impl From<$Type> for $crate::proto::$Type { + fn from(msg: $Type) -> Self { + Self(cf_solana_upstream::proto::$Type::from(&msg.0)) + } + } + + impl From<&$Type> for $crate::proto::$Type { + fn from(msg: &$Type) -> Self { + Self(cf_solana_upstream::proto::$Type::from(&msg.0)) + } + } + + impl TryFrom<$crate::proto::$Type> for $Type { + type Error = $crate::proto::BadMessage; + fn try_from(msg: $crate::proto::$Type) -> Result { + Self::try_from(&msg) + } + } + + impl TryFrom<&$crate::proto::$Type> for $Type { + type Error = $crate::proto::BadMessage; + fn try_from(msg: &$crate::proto::$Type) -> Result { + Ok(Self(cf_solana_upstream::$Type::try_from(&msg.0)?)) + } + } + + impl ibc::protobuf::Protobuf<$crate::proto::$Type> for $Type {} + }; + + (impl proto for $Type:ident) => { + impl $crate::proto::$Type { + pub const IBC_TYPE_URL: &'static str = + cf_solana_upstream::proto::$Type::IBC_TYPE_URL; + } + + impl From<$Type> for $crate::proto::$Type { + fn from(msg: $Type) -> Self { + Self(cf_solana_upstream::proto::$Type::from(&msg.0)) + } + } + + impl From<&$Type> for $crate::proto::$Type { + fn from(msg: &$Type) -> Self { + Self(cf_solana_upstream::proto::$Type::from(&msg.0)) + } + } + + impl TryFrom<$crate::proto::$Type> for $Type { + type Error = $crate::proto::BadMessage; + fn try_from(msg: $crate::proto::$Type) -> Result { + Self::try_from(&msg) + } + } + + impl TryFrom<&$crate::proto::$Type> for $Type { + type Error = $crate::proto::BadMessage; + fn try_from(msg: &$crate::proto::$Type) -> Result { + Ok(Self(cf_solana_upstream::$Type::try_from(&msg.0)?)) + } + } + + impl ibc::protobuf::Protobuf<$crate::proto::$Type> for $Type {} + }; +} + +use wrap; \ No newline at end of file diff --git a/light-clients/cf-solana/src/message.rs b/light-clients/cf-solana/src/message.rs new file mode 100644 index 000000000..fc4c83764 --- /dev/null +++ b/light-clients/cf-solana/src/message.rs @@ -0,0 +1,48 @@ +use prost::Message as _; + +use crate::proto; + +super::wrap!(cf_solana_upstream::ClientMessage as ClientMessage); +super::wrap!(impl proto for ClientMessage); + +impl ClientMessage { + pub fn maybe_header_height(&self) -> Option { + if let cf_solana_upstream::ClientMessage::Header(hdr) = &self.0 { + let height = hdr.slot; + Some(ibc::Height::new(1, height.into())) + } else { + None + } + } +} + +impl ibc::core::ics02_client::client_message::ClientMessage for ClientMessage +{ + fn encode_to_vec(&self) -> Result, ibc::protobuf::Error> { + Ok(proto::ClientMessage::from(self).encode_to_vec()) + } +} + +impl From for ClientMessage { + fn from(hdr: cf_solana_upstream::Header) -> Self { + Self(cf_solana_upstream::ClientMessage::Header(hdr)) + } +} + +impl From for ClientMessage { + fn from(hdr: crate::Header) -> Self { + Self(cf_solana_upstream::ClientMessage::Header(hdr.0)) + } +} + +impl From for ClientMessage { + fn from(msg: cf_solana_upstream::Misbehaviour) -> Self { + Self(cf_solana_upstream::ClientMessage::Misbehaviour(msg)) + } +} + +impl From for ClientMessage { + fn from(msg: crate::Misbehaviour) -> Self { + Self(cf_solana_upstream::ClientMessage::Misbehaviour(msg.0)) + } +} diff --git a/light-clients/cf-solana/src/misbehaviour.rs b/light-clients/cf-solana/src/misbehaviour.rs new file mode 100644 index 000000000..fbb326a88 --- /dev/null +++ b/light-clients/cf-solana/src/misbehaviour.rs @@ -0,0 +1,2 @@ +super::wrap!(cf_solana_upstream::Misbehaviour as Misbehaviour); +super::wrap!(impl proto for Misbehaviour); diff --git a/light-clients/cf-solana/src/proto.rs b/light-clients/cf-solana/src/proto.rs new file mode 100644 index 000000000..5e4a15e0f --- /dev/null +++ b/light-clients/cf-solana/src/proto.rs @@ -0,0 +1,135 @@ +use alloc::string::ToString; + +macro_rules! import_proto { + ($Msg:ident) => { + $crate::wrap!(cf_solana_upstream::proto::$Msg as $Msg); + $crate::wrap!(impl Default for $Msg); + + impl prost::Message for $Msg { + fn encode_raw(&self, buf: &mut B) { + prost_12::Message::encode_raw(&self.0, buf) + } + + fn merge_field( + &mut self, + tag: u32, + wire_type: prost::encoding::WireType, + buf: &mut B, + _ctx: prost::encoding::DecodeContext, + ) -> Result<(), prost::DecodeError> { + // SAFETY: The types are identical in prost 0.11 and prost.12. + let wire_type = unsafe { + core::mem::transmute(wire_type as u8) + }; + prost_12::Message::merge_field(&mut self.0, tag, wire_type, buf, Default::default()) + .map_err(|err| { + // SAFETY: The types are identical in prost 0.11 and prost.12. + unsafe { + core::mem::transmute(err) + } + }) + } + + fn encoded_len(&self) -> usize { + prost_12::Message::encoded_len(&self.0) + } + + fn clear(&mut self) { + prost_12::Message::clear(&mut self.0) + } + } + } +} + +import_proto!(ClientMessage); +import_proto!(ClientState); +import_proto!(ConsensusState); +import_proto!(Header); +import_proto!(Misbehaviour); + +/// Error during decoding of a protocol message. +#[derive(Clone, PartialEq, Eq, derive_more::From)] +pub enum DecodeError { + /// Failed decoding the wire encoded protocol message. + /// + /// This means that the supplied bytes weren’t a valid protocol buffer or + /// they didn’t correspond to the expected message. + BadProto(alloc::string::String), + + /// Protocol message represents invalid state; see [`BadMessage`]. + #[from(ignore)] + BadMessage, + + /// When decoding an `Any` message, the type URL doesn’t equal the expected + /// one. + #[from(ignore)] + BadType, +} + +impl From for DecodeError { + fn from(err: cf_solana_upstream::proto::DecodeError) -> Self { + match err { + cf_solana_upstream::proto::DecodeError::BadProto(err) => Self::BadProto(err.to_string()), + cf_solana_upstream::proto::DecodeError::BadMessage => Self::BadMessage, + cf_solana_upstream::proto::DecodeError::BadType => Self::BadType, + } + } +} + +/// Error during validation of a protocol message. +/// +/// Typing in protocol messages is less descriptive than in Rust. It’s possible +/// to represent state in the protocol message which doesn’t correspond to +/// a valid state. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct BadMessage; + +impl From for BadMessage { + fn from(_: cf_solana_upstream::proto::BadMessage) -> Self { + Self + } +} + +impl From for DecodeError { + fn from(_: BadMessage) -> Self { + Self::BadMessage + } +} + +impl core::fmt::Debug for DecodeError { + fn fmt(&self, fmtr: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + Self::BadProto(err) => err.fmt(fmtr), + Self::BadMessage => fmtr.write_str("BadMessage"), + Self::BadType => fmtr.write_str("BadType"), + } + } +} + +impl core::fmt::Display for DecodeError { + #[inline] + fn fmt(&self, fmtr: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Debug::fmt(self, fmtr) + } +} + +impl core::fmt::Display for BadMessage { + #[inline] + fn fmt(&self, fmtr: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Debug::fmt(self, fmtr) + } +} + +impl From
for ClientMessage { + #[inline] + fn from(msg: Header) -> Self { + Self(cf_solana_upstream::proto::ClientMessage::from(msg.0)) + } +} + +impl From for ClientMessage { + #[inline] + fn from(msg: Misbehaviour) -> Self { + Self(cf_solana_upstream::proto::ClientMessage::from(msg.0)) + } +} diff --git a/light-clients/ics07-tendermint-cw/src/contract.rs b/light-clients/ics07-tendermint-cw/src/contract.rs index c57cef78e..54a835c0c 100644 --- a/light-clients/ics07-tendermint-cw/src/contract.rs +++ b/light-clients/ics07-tendermint-cw/src/contract.rs @@ -248,7 +248,7 @@ fn process_message( .client_state(&client_id) .map_err(|e| ContractError::Tendermint(e.to_string()))?; let msg = VerifyMembershipMsg::try_from(msg)?; - verify_delay_passed(&ctx, msg.height, msg.delay_time_period, msg.delay_block_period) + verify_delay_passed(ctx, msg.height, msg.delay_time_period, msg.delay_block_period) .map_err(|e| ContractError::Tendermint(e.to_string()))?; let consensus_state = ctx .consensus_state(&client_id, msg.height) @@ -269,7 +269,7 @@ fn process_message( .client_state(&client_id) .map_err(|e| ContractError::Tendermint(e.to_string()))?; let msg = VerifyNonMembershipMsg::try_from(msg)?; - verify_delay_passed(&ctx, msg.height, msg.delay_time_period, msg.delay_block_period) + verify_delay_passed(ctx, msg.height, msg.delay_time_period, msg.delay_block_period) .map_err(|e| ContractError::Tendermint(e.to_string()))?; let consensus_state = ctx .consensus_state(&client_id, msg.height) diff --git a/light-clients/ics07-tendermint/src/client_message.rs b/light-clients/ics07-tendermint/src/client_message.rs index 420ebc771..7f1990852 100644 --- a/light-clients/ics07-tendermint/src/client_message.rs +++ b/light-clients/ics07-tendermint/src/client_message.rs @@ -34,11 +34,8 @@ use ibc_proto::{ use prost::Message; use serde::{Deserialize, Serialize}; use tendermint::{ - block::{signed_header::SignedHeader, Commit, CommitSig}, - crypto::signature::Verifier, + block::{signed_header::SignedHeader}, validator::Set as ValidatorSet, - vote::{SignedVote, ValidatorIndex}, - PublicKey, Vote, }; use tendermint_proto::Protobuf; diff --git a/light-clients/ics08-wasm/src/client_state.rs b/light-clients/ics08-wasm/src/client_state.rs index 8f1d331f0..6be361853 100644 --- a/light-clients/ics08-wasm/src/client_state.rs +++ b/light-clients/ics08-wasm/src/client_state.rs @@ -211,7 +211,7 @@ impl Default data: vec![], checksum: vec![], latest_height: Default::default(), - inner: Box::new(AnyClientState::default()), + inner: Box::::default(), _phantom: Default::default(), } } diff --git a/light-clients/ics08-wasm/src/consensus_state.rs b/light-clients/ics08-wasm/src/consensus_state.rs index e2689fc9a..6aae58e50 100644 --- a/light-clients/ics08-wasm/src/consensus_state.rs +++ b/light-clients/ics08-wasm/src/consensus_state.rs @@ -127,6 +127,6 @@ where impl Default for ConsensusState { fn default() -> Self { - ConsensusState { data: vec![], inner: Box::new(AnyConsensusState::default()) } + ConsensusState { data: vec![], inner: Box::::default() } } } diff --git a/light-clients/ics10-grandpa-cw/src/contract.rs b/light-clients/ics10-grandpa-cw/src/contract.rs index 4589e6400..ca5167737 100644 --- a/light-clients/ics10-grandpa-cw/src/contract.rs +++ b/light-clients/ics10-grandpa-cw/src/contract.rs @@ -220,7 +220,7 @@ fn process_message( // load the substitute client state from the combined storage using the appropriate // prefix let substitute_client_state = ctx - .client_state_prefixed(SUBSTITUTE_PREFIX.as_bytes()) + .client_state_prefixed(SUBSTITUTE_PREFIX) .map_err(|e| ContractError::Grandpa(e.to_string()))?; // No items for the grandpa client state are required to be the same @@ -228,15 +228,15 @@ fn process_message( let height = substitute_client_state.latest_height(); // consensus state should be replaced as well let substitute_consensus_state = - ctx.consensus_state_prefixed(height, SUBSTITUTE_PREFIX.as_bytes())?; + ctx.consensus_state_prefixed(height, SUBSTITUTE_PREFIX)?; ctx.store_consensus_state_prefixed( height, substitute_consensus_state, - SUBJECT_PREFIX.as_bytes(), + SUBJECT_PREFIX, ); ctx.store_client_state_prefixed( substitute_client_state, - SUBJECT_PREFIX.as_bytes(), + SUBJECT_PREFIX, client_id, ) .map_err(|e| ContractError::Grandpa(e.to_string()))?; diff --git a/utils/parachain-node/runtime/build.rs b/utils/parachain-node/runtime/build.rs index 4d2712b30..501793eab 100644 --- a/utils/parachain-node/runtime/build.rs +++ b/utils/parachain-node/runtime/build.rs @@ -12,12 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use substrate_wasm_builder::WasmBuilder; - fn main() { - // WasmBuilder::new() - // .with_current_project() - // .export_heap_base() - // .import_memory() - // .build() + #[cfg(feature = "std")] + substrate_wasm_builder::WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() }