diff --git a/.gitignore b/.gitignore index 62abf6fb..df455888 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ tarpaulin-report.* packages/*/schema contracts/*/schema + +.env +state.json diff --git a/Cargo.lock b/Cargo.lock index 696c1fc4..cb37ed0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,14 +1,39 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "abstract-cw-multi-test" +version = "2.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e09b032e3379899df1872e6a8e38ce31d1d446bb26adfe7d3ff432b0fe0bf4c1" +dependencies = [ + "anyhow", + "bech32 0.11.0", + "cosmwasm-schema 2.2.0", + "cosmwasm-std 2.2.0", + "cw-storage-plus 2.0.0", + "cw-utils 2.0.0", + "cw20-ics20", + "hex", + "itertools 0.13.0", + "log", + "prost 0.13.4", + "schemars", + "serde", + "serde_json", + "sha2 0.10.8", + "thiserror", + "tiny-keccak", +] [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ - "gimli", + "gimli 0.31.1", ] [[package]] @@ -17,6 +42,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.11" @@ -29,17 +65,41 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" dependencies = [ "backtrace", ] @@ -85,7 +145,7 @@ dependencies = [ "ark-serialize", "ark-std", "derivative", - "digest", + "digest 0.10.7", "itertools 0.10.5", "num-bigint", "num-traits", @@ -139,7 +199,7 @@ checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ "ark-serialize-derive", "ark-std", - "digest", + "digest 0.10.7", "num-bigint", ] @@ -165,11 +225,114 @@ dependencies = [ "rayon", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 1.0.2", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", +] [[package]] name = "backtrace" @@ -183,21 +346,55 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] +[[package]] +name = "base16" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8" + [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.0", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bech32" version = "0.11.0" @@ -205,931 +402,4799 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" [[package]] -name = "block-buffer" -version = "0.10.4" +name = "bip32" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "aa13fae8b6255872fd86f7faf4b41168661d7d78609f7bfe6771b85c6739a15b" dependencies = [ - "generic-array", + "bs58", + "hmac", + "k256", + "rand_core 0.6.4", + "ripemd", + "sha2 0.10.8", + "subtle", + "zeroize", ] [[package]] -name = "bnum" -version = "0.11.0" +name = "bip39" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" +checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" +dependencies = [ + "bitcoin_hashes 0.13.0", + "rand", + "rand_core 0.6.4", + "serde", + "unicode-normalization", +] [[package]] -name = "byteorder" -version = "1.5.0" +name = "bitcoin" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" +dependencies = [ + "base58ck", + "bech32 0.11.0", + "bitcoin-internals 0.3.0", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes 0.14.0", + "hex-conservative 0.2.1", + "hex_lit", + "secp256k1", +] [[package]] -name = "bytes" -version = "1.7.2" +name = "bitcoin-internals" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" [[package]] -name = "cfg-if" -version = "1.0.0" +name = "bitcoin-internals" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" [[package]] -name = "const-oid" -version = "0.9.6" +name = "bitcoin-io" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] -name = "cosmwasm-core" -version = "2.1.4" +name = "bitcoin-units" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6ceb8624260d0d3a67c4e1a1d43fc7e9406720afbcb124521501dd138f90aa" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +dependencies = [ + "bitcoin-internals 0.3.0", +] [[package]] -name = "cosmwasm-crypto" -version = "2.1.4" +name = "bitcoin_hashes" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4125381e5fd7fefe9f614640049648088015eca2b60d861465329a5d87dfa538" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" dependencies = [ - "ark-bls12-381", - "ark-ec", - "ark-ff", - "ark-serialize", - "cosmwasm-core", - "digest", - "ecdsa", - "ed25519-zebra", - "k256", - "num-traits", - "p256", - "rand_core", - "rayon", - "sha2", - "thiserror", + "bitcoin-internals 0.2.0", + "hex-conservative 0.1.2", ] [[package]] -name = "cosmwasm-derive" -version = "2.1.4" +name = "bitcoin_hashes" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5658b1dc64e10b56ae7a449f678f96932a96f6cfad1769d608d1d1d656480a" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", + "bitcoin-io", + "hex-conservative 0.2.1", ] [[package]] -name = "cosmwasm-schema" -version = "2.1.4" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b4d949b6041519c58993a73f4bbfba8083ba14f7001eae704865a09065845" -dependencies = [ - "cosmwasm-schema-derive", - "schemars", - "serde", - "serde_json", - "thiserror", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "cosmwasm-schema-derive" -version = "2.1.4" +name = "bitflags" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ef1b5835a65fcca3ab8b9a02b4f4dacc78e233a5c2f20b270efb9db0666d12" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] -name = "cosmwasm-std" -version = "2.1.4" +name = "bitvec" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70eb7ab0c1e99dd6207496963ba2a457c4128ac9ad9c72a83f8d9808542b849b" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "base64", - "bech32", - "bnum", - "cosmwasm-core", - "cosmwasm-crypto", - "cosmwasm-derive", - "derive_more", - "hex", - "rand_core", - "schemars", - "serde", - "serde-json-wasm", - "sha2", - "static_assertions", - "thiserror", + "funty", + "radium", + "tap", + "wyz", ] [[package]] -name = "cpufeatures" -version = "0.2.14" +name = "blake2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "libc", + "digest 0.10.7", ] [[package]] -name = "crossbeam-deque" -version = "0.8.5" +name = "block-buffer" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", + "generic-array", ] [[package]] -name = "crossbeam-epoch" -version = "0.9.18" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "crossbeam-utils", + "generic-array", ] [[package]] -name = "crossbeam-utils" -version = "0.8.20" +name = "bnum" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" [[package]] -name = "crypto-bigint" -version = "0.5.5" +name = "bnum" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] +checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" [[package]] -name = "crypto-common" -version = "0.1.6" +name = "bs58" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "generic-array", - "typenum", + "sha2 0.10.8", ] [[package]] -name = "curve25519-dalek" -version = "4.1.3" +name = "bumpalo" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", + "bytecheck_derive", + "ptr_meta", + "simdutf8", ] [[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" +name = "bytecheck_derive" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 1.0.109", ] [[package]] -name = "cw-multi-test" -version = "2.2.0" +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" dependencies = [ - "anyhow", - "bech32", - "cosmwasm-schema", - "cosmwasm-std", - "cw-storage-plus", - "cw-utils", - "hex", - "hex-literal", - "itertools 0.13.0", - "once_cell", - "prost", - "schemars", "serde", - "sha2", - "thiserror", ] [[package]] -name = "cw-storage-plus" -version = "2.0.0" +name = "camino" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13360e9007f51998d42b1bc6b7fa0141f74feae61ed5fd1e5b0a89eec7b5de1" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" dependencies = [ - "cosmwasm-std", - "schemars", "serde", ] [[package]] -name = "cw-utils" -version = "2.0.0" +name = "cargo-platform" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07dfee7f12f802431a856984a32bce1cb7da1e6c006b5409e3981035ce562dec" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "schemars", "serde", - "thiserror", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "clone-cw-multi-test" +version = "0.7.0" +dependencies = [ + "anyhow", + "bech32 0.11.0", + "cargo_metadata", + "chrono", + "cosmrs", + "cosmwasm-schema 2.2.0", + "cosmwasm-std 2.2.0", + "cosmwasm-vm", + "cw-orch", + "cw-orch-daemon", + "cw-storage-plus 2.0.0", + "cw-utils 2.0.0", + "cw2 2.0.0", + "cw20 2.0.0", + "env_logger", + "file-lock", + "hex", + "hex-literal", + "itertools 0.13.0", + "log", + "moneymarket", + "num-bigint", + "once_cell", + "osmosis-std-derive", + "prost 0.13.4", + "prost-types 0.13.4", + "rustc-serialize", + "schemars", + "serde", + "serde-cw-value", + "serde_json", + "sha2 0.10.8", + "thiserror", + "tokio", + "tonic", + "treediff", + "wasmer", +] + +[[package]] +name = "clru" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "corosensei" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80128832c58ea9cbd041d2a759ec449224487b2c1e400453d99d244eead87a8e" +dependencies = [ + "autocfg", + "cfg-if", + "libc", + "scopeguard", + "windows-sys 0.33.0", +] + +[[package]] +name = "cosmos-sdk-proto" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d0afc4daf81936e6ef5a2cf76f00c913ba5bc385d58ae1e09644e25d16b0381" +dependencies = [ + "prost 0.13.4", + "tendermint-proto", + "tonic", +] + +[[package]] +name = "cosmrs" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af28a0ee4149da7cea0486fd7e3fbddea7dd81625279fdc521141ffb07fbd482" +dependencies = [ + "bip32", + "cosmos-sdk-proto", + "ecdsa", + "eyre", + "k256", + "rand_core 0.6.4", + "serde", + "serde_json", + "signature", + "subtle-encoding", + "tendermint", + "tendermint-rpc", + "thiserror", + "tokio", +] + +[[package]] +name = "cosmwasm-core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34c440d4d8e3ecec783d0f9c89d25565168b0f4cdb80a1f6a387cf2168c0740" + +[[package]] +name = "cosmwasm-crypto" +version = "1.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba94b9f3fb79b9f1101b3e0c61995a557828e2c0d3f5579c1d0bfbea333c19e" +dependencies = [ + "digest 0.10.7", + "ed25519-zebra 3.1.0", + "k256", + "rand_core 0.6.4", + "thiserror", +] + +[[package]] +name = "cosmwasm-crypto" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134e765161d60228cc27635032d2a466542ca83fd6c87f3c87f4963c0bd51008" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "cosmwasm-core", + "curve25519-dalek 4.1.3", + "digest 0.10.7", + "ecdsa", + "ed25519-zebra 4.0.3", + "k256", + "num-traits", + "p256", + "rand_core 0.6.4", + "rayon", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "cosmwasm-derive" +version = "1.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d67457e4acb04e738788d3489e343957455df2c4643f2b53050eb052ca631d19" +dependencies = [ + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-derive" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c94a4b93e722c91d2e58471cfe69480f4a656cfccacd8bfda5638f2a5d4512b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "cosmwasm-schema" +version = "1.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bf06bf1c7ea737f6b3d955d9cabeb8cbbe4dcb8dea392e30f6fab4493a4b7a" +dependencies = [ + "cosmwasm-schema-derive 1.5.9", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9a7b56d154870ec4b57b224509854f706c9744449548d8a3bf91ac75c59192" +dependencies = [ + "cosmwasm-schema-derive 2.2.0", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077fe378f16b54e3d0a57adb3f39a65bcf7bdbda6a5eade2f8ba7755c2fb1250" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd3d80310cd7b86b09dbe886f4f2ca235a5ddb8d478493c6e50e720a3b38a42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "cosmwasm-std" +version = "1.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3745e9fd9aad96236c3d6f1a6d844249ed3bb6b92fcdae16d8fe067c7a5121e8" +dependencies = [ + "base64 0.21.7", + "bech32 0.9.1", + "bnum 0.10.0", + "cosmwasm-crypto 1.5.9", + "cosmwasm-derive 1.5.9", + "derivative", + "forward_ref", + "hex", + "schemars", + "serde", + "serde-json-wasm 0.5.2", + "sha2 0.10.8", + "static_assertions", + "thiserror", +] + +[[package]] +name = "cosmwasm-std" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434e556b0aebff34bf082e75d175b5d7edbcf1d90d4cedb59623a1249fff567" +dependencies = [ + "base64 0.22.1", + "bech32 0.11.0", + "bnum 0.11.0", + "cosmwasm-core", + "cosmwasm-crypto 2.2.0", + "cosmwasm-derive 2.2.0", + "derive_more", + "hex", + "rand_core 0.6.4", + "rmp-serde", + "schemars", + "serde", + "serde-json-wasm 1.0.1", + "sha2 0.10.8", + "static_assertions", + "thiserror", +] + +[[package]] +name = "cosmwasm-storage" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66de2ab9db04757bcedef2b5984fbe536903ada4a8a9766717a4a71197ef34f6" +dependencies = [ + "cosmwasm-std 1.5.9", + "serde", +] + +[[package]] +name = "cosmwasm-vm" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8609b60a64d3db43ccfe775828b701cc29d27c7ac136c0a89d5279353bdc1513" +dependencies = [ + "bech32 0.11.0", + "blake2", + "bytes", + "clru", + "cosmwasm-core", + "cosmwasm-crypto 2.2.0", + "cosmwasm-std 2.2.0", + "cosmwasm-vm-derive", + "crc32fast", + "derivative", + "hex", + "rand_core 0.6.4", + "schemars", + "serde", + "serde_json", + "sha2 0.10.8", + "strum 0.26.3", + "thiserror", + "tracing", + "wasmer", + "wasmer-middlewares", + "wasmer-types", +] + +[[package]] +name = "cosmwasm-vm-derive" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fc803c6bb936441d5224e6cc04bd32b2f17a5030a1a186ad103bc4328e5401" +dependencies = [ + "blake2", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2ab4512dfd3a6f4be184403a195f76e81a8a9f9e6c898e19d2dc3ce20e0115" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98b022ed2a5913a38839dfbafe6cf135342661293b08049843362df4301261dc" +dependencies = [ + "arrayvec", + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-egraph", + "cranelift-entity", + "cranelift-isle", + "gimli 0.26.2", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "639307b45434ad112a98f8300c0f0ab085cbefcd767efcdef9ef19d4c0756e74" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "278e52e29c53fcf32431ef08406c295699a70306d05a0715c5b1bf50e33a9ab7" + +[[package]] +name = "cranelift-egraph" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624b54323b06e675293939311943ba82d323bb340468ce1889be5da7932c8d73" +dependencies = [ + "cranelift-entity", + "fxhash", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "log", + "smallvec", +] + +[[package]] +name = "cranelift-entity" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a59bcbca89c3f1b70b93ab3cbba5e5e0cbf3e63dadb23c7525cb142e21a9d4c" + +[[package]] +name = "cranelift-frontend" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d70abacb8cfef3dc8ff7e8836e9c1d70f7967dfdac824a4cd5e30223415aca6" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "393bc73c451830ff8dbb3a07f61843d6cb41a084f9996319917c0b291ed785bb" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", +] + +[[package]] +name = "cw-controllers" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c1804013d21060b994dea28a080f9eab78a3bcb6b617f05e7634b0600bf7b1" +dependencies = [ + "cosmwasm-schema 2.2.0", + "cosmwasm-std 2.2.0", + "cw-storage-plus 2.0.0", + "cw-utils 2.0.0", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-orch" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8970af7113824ab8d82f0a0d12f92b41d85410f3b7651425989ffa55d517708b" +dependencies = [ + "anyhow", + "cosmwasm-std 2.2.0", + "cw-orch-contract-derive", + "cw-orch-core", + "cw-orch-fns-derive", + "cw-orch-mock", + "cw-orch-traits", + "cw-utils 2.0.0", + "hex", + "log", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-orch-contract-derive" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad52865e313bb7ed3f3938f7ad9d566e430fb6143a63476c22bed505ea78cd7" +dependencies = [ + "convert_case", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "cw-orch-core" +version = "2.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd60ffab817101418b26b6bceadfdfc430dad4219c7e4cd972f8838e21190981" +dependencies = [ + "abstract-cw-multi-test", + "anyhow", + "cosmos-sdk-proto", + "cosmwasm-std 2.2.0", + "cw-storage-plus 2.0.0", + "cw-utils 2.0.0", + "dirs", + "log", + "serde", + "serde_json", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "cw-orch-daemon" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "148b57fe55a3f7bc56a42773646ee2a5722084e0c1b9a2f618226b610acfb2fb" +dependencies = [ + "anyhow", + "async-recursion", + "base16", + "base64 0.22.1", + "bech32 0.11.0", + "bip39", + "bitcoin", + "chrono", + "cosmrs", + "cosmwasm-std 2.2.0", + "cw-orch-core", + "cw-orch-networks", + "cw-orch-traits", + "dirs", + "ed25519-dalek", + "eyre", + "file-lock", + "flate2", + "hex", + "http 1.2.0", + "lazy_static", + "libc-print", + "log", + "once_cell", + "prost 0.13.4", + "prost-types 0.13.4", + "rand_core 0.6.4", + "reqwest 0.12.9", + "ring", + "ripemd", + "schemars", + "serde", + "serde_json", + "sha2 0.10.8", + "thiserror", + "tokio", + "toml", + "tonic", + "uid", +] + +[[package]] +name = "cw-orch-fns-derive" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194e944e6bcb51a53f99e2b0a510ecc8919605b9a83d93641748cf1b163315f6" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "cw-orch-mock" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce639c909e59fc1505f56772b9a37057a54d3c5a20f7a4c197cd518a27ce9770" +dependencies = [ + "abstract-cw-multi-test", + "cosmwasm-std 2.2.0", + "cw-orch-core", + "cw-utils 2.0.0", + "log", + "serde", + "sha2 0.10.8", +] + +[[package]] +name = "cw-orch-networks" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd4b6651ba68b83474ecfb09d12c7d59e2d9bab76810a41ffe6a948f635136b" +dependencies = [ + "cw-orch-core", + "serde", +] + +[[package]] +name = "cw-orch-traits" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ccb1670cc49a7a10c425ba1072cb607eb00d8085f97692877b9bdb00d14dd3d" +dependencies = [ + "cw-orch-core", + "prost-types 0.13.4", +] + +[[package]] +name = "cw-storage-plus" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6cf70ef7686e2da9ad7b067c5942cd3e88dd9453f7af42f54557f8af300fb0" +dependencies = [ + "cosmwasm-std 1.5.9", + "schemars", + "serde", +] + +[[package]] +name = "cw-storage-plus" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f13360e9007f51998d42b1bc6b7fa0141f74feae61ed5fd1e5b0a89eec7b5de1" +dependencies = [ + "cosmwasm-std 2.2.0", + "schemars", + "serde", +] + +[[package]] +name = "cw-utils" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae0b69fa7679de78825b4edeeec045066aa2b2c4b6e063d80042e565bb4da5c" +dependencies = [ + "cosmwasm-schema 1.5.9", + "cosmwasm-std 1.5.9", + "cw2 0.15.1", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw-utils" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07dfee7f12f802431a856984a32bce1cb7da1e6c006b5409e3981035ce562dec" +dependencies = [ + "cosmwasm-schema 2.2.0", + "cosmwasm-std 2.2.0", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw2" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5abb8ecea72e09afff830252963cb60faf945ce6cef2c20a43814516082653da" +dependencies = [ + "cosmwasm-schema 1.5.9", + "cosmwasm-std 1.5.9", + "cw-storage-plus 0.15.1", + "schemars", + "serde", +] + +[[package]] +name = "cw2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b04852cd38f044c0751259d5f78255d07590d136b8a86d4e09efdd7666bd6d27" +dependencies = [ + "cosmwasm-schema 2.2.0", + "cosmwasm-std 2.2.0", + "cw-storage-plus 2.0.0", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw20" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6025276fb6e603e974c21f3e4606982cdc646080e8fba3198816605505e1d9a" +dependencies = [ + "cosmwasm-schema 1.5.9", + "cosmwasm-std 1.5.9", + "cw-utils 0.15.1", + "schemars", + "serde", +] + +[[package]] +name = "cw20" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a42212b6bf29bbdda693743697c621894723f35d3db0d5df930be22903d0e27c" +dependencies = [ + "cosmwasm-schema 2.2.0", + "cosmwasm-std 2.2.0", + "cw-utils 2.0.0", + "schemars", + "serde", +] + +[[package]] +name = "cw20-ics20" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80a9e377dbbd1ffb3b6a8a2dbf9128609a6458a3292f88f99e0b6840a7e9762e" +dependencies = [ + "cosmwasm-schema 2.2.0", + "cosmwasm-std 2.2.0", + "cw-controllers", + "cw-storage-plus 2.0.0", + "cw-utils 2.0.0", + "cw2 2.0.0", + "cw20 2.0.0", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] name = "der" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2 0.5.10", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "serde", + "signature", +] + +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek 4.1.3", + "ed25519", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek 3.2.0", + "hashbrown 0.12.3", + "hex", + "rand_core 0.6.4", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-zebra" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9" +dependencies = [ + "curve25519-dalek 4.1.3", + "ed25519", + "hashbrown 0.14.5", + "hex", + "rand_core 0.6.4", + "sha2 0.10.8", + "zeroize", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-iterator" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enumset" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "file-lock" +version = "2.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "040b48f80a749da50292d0f47a1e2d5bf1d772f52836c07f64bfccc62ba6e664" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flex-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" +dependencies = [ + "eyre", + "paste", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "forward_ref" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.7.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.2.0", + "indexmap 2.7.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.11", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" + +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.2.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.2.0", + "hyper 1.5.2", + "hyper-util", + "rustls 0.23.20", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.1", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper 1.5.2", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.5.2", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.5.2", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.8", + "signature", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.168" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" + +[[package]] +name = "libc-print" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a660208db49e35faf57b37484350f1a61072f2a5becf0592af6015d9ddd4b0" +dependencies = [ + "libc", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "moneymarket" +version = "0.3.1" +source = "git+https://github.com/CavernPerson/money-market-contracts#6a3b3f586cd085d434485bb13e2d12f95e8beef2" +dependencies = [ + "cosmwasm-schema 1.5.9", + "cosmwasm-std 1.5.9", + "cosmwasm-storage", + "cw20 0.15.1", + "schemars", + "serde", + "strum 0.24.1", + "strum_macros 0.24.3", +] + +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework 2.11.1", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "osmosis-std-derive" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0240fd030a4bbc79fa6cbea0b3eb0260a4b79075ebc039b93e2652bff8655b" +dependencies = [ + "itertools 0.10.5", + "proc-macro2", + "prost-types 0.11.9", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "peg" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +dependencies = [ + "bytes", + "prost-derive 0.13.4", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-derive" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost 0.11.9", +] + +[[package]] +name = "prost-types" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" +dependencies = [ + "prost 0.13.4", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regalloc2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +dependencies = [ + "fxhash", + "log", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "region" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" +dependencies = [ + "bitflags 1.3.2", + "libc", + "mach2", + "windows-sys 0.52.0", +] + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.12", + "rustls-native-certs 0.6.3", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration 0.5.1", + "tokio", + "tokio-rustls 0.24.1", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "reqwest" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.2", + "hyper-rustls 0.27.3", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.2.0", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "system-configuration 0.6.1", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-serialize" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.23.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework 2.11.1", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.2.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "schemars" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.90", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "bitcoin_hashes 0.14.0", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "self_cell" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" + +[[package]] +name = "semver" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-cw-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75d32da6b8ed758b7d850b6c3c08f1d7df51a4df3cb201296e63e34a78e99d4" +dependencies = [ + "serde", +] + +[[package]] +name = "serde-json-wasm" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9213a07d53faa0b8dd81e767a54a8188a242fdb9be99ab75ec576a774bfdd7" +dependencies = [ + "serde", +] + +[[package]] +name = "serde-json-wasm" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" +dependencies = [ + "serde", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ - "const-oid", - "zeroize", + "itoa", + "memchr", + "ryu", + "serde", ] [[package]] -name = "derivative" +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "shared-buffer" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6c99835bad52957e7aa241d3975ed17c1e5f8c92026377d117a606f36b84b16" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ + "heck 0.4.1", "proc-macro2", "quote", + "rustversion", "syn 1.0.109", ] [[package]] -name = "derive_more" -version = "1.0.0" +name = "strum_macros" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "derive_more-impl", + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.90", ] [[package]] -name = "derive_more-impl" -version = "1.0.0" +name = "subtle" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "subtle-encoding" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" +dependencies = [ + "zeroize", +] + +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", - "unicode-xid", + "unicode-ident", ] [[package]] -name = "digest" -version = "0.10.7" +name = "syn" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.9.4", + "system-configuration-sys 0.6.0", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "tendermint" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3afea7809ffaaf1e5d9c3c9997cb3a834df7e94fbfab2fad2bc4577f1cde41" +dependencies = [ + "bytes", + "digest 0.10.7", + "ed25519", + "ed25519-consensus", + "flex-error", + "futures", + "k256", + "num-traits", + "once_cell", + "prost 0.13.4", + "ripemd", + "serde", + "serde_bytes", + "serde_json", + "serde_repr", + "sha2 0.10.8", + "signature", + "subtle", + "subtle-encoding", + "tendermint-proto", + "time", + "zeroize", +] + +[[package]] +name = "tendermint-config" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8add7b85b0282e5901521f78fe441956ac1e2752452f4e1f2c0ce7e1f10d485" +dependencies = [ + "flex-error", + "serde", + "serde_json", + "tendermint", + "toml", + "url", +] + +[[package]] +name = "tendermint-proto" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3abf34ecf33125621519e9952688e7a59a98232d51538037ba21fbe526a802" +dependencies = [ + "bytes", + "flex-error", + "prost 0.13.4", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "tendermint-rpc" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9693f42544bf3b41be3cbbfa418650c86e137fb8f5a57981659a84b677721ecf" +dependencies = [ + "async-trait", + "bytes", + "flex-error", + "futures", + "getrandom", + "peg", + "pin-project", + "rand", + "reqwest 0.11.27", + "semver", + "serde", + "serde_bytes", + "serde_json", "subtle", + "subtle-encoding", + "tendermint", + "tendermint-config", + "tendermint-proto", + "thiserror", + "time", + "tokio", + "tracing", + "url", + "uuid", + "walkdir", ] [[package]] -name = "dyn-clone" -version = "1.0.17" +name = "termcolor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] [[package]] -name = "ecdsa" -version = "0.16.9" +name = "thiserror" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", + "thiserror-impl", ] [[package]] -name = "ed25519" -version = "2.2.3" +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "signature", + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] -name = "ed25519-zebra" -version = "4.0.3" +name = "time" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ - "curve25519-dalek", - "ed25519", - "hashbrown 0.14.5", - "hex", - "rand_core", - "sha2", - "zeroize", + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", ] [[package]] -name = "either" -version = "1.13.0" +name = "time-core" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] -name = "elliptic-curve" -version = "0.13.8" +name = "time-macros" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "rand_core", - "sec1", - "subtle", - "zeroize", + "num-conv", + "time-core", ] [[package]] -name = "ff" -version = "0.13.0" +name = "tiny-keccak" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ - "rand_core", - "subtle", + "crunchy", ] [[package]] -name = "fiat-crypto" -version = "0.2.9" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] [[package]] -name = "generic-array" -version = "0.14.7" +name = "tinyvec" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ - "typenum", - "version_check", - "zeroize", + "tinyvec_macros", ] [[package]] -name = "getrandom" -version = "0.2.15" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ - "cfg-if", + "backtrace", + "bytes", "libc", - "wasi", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", ] [[package]] -name = "gimli" -version = "0.31.0" +name = "tokio-macros" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] [[package]] -name = "group" -version = "0.13.0" +name = "tokio-native-tls" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ - "ff", - "rand_core", - "subtle", + "native-tls", + "tokio", ] [[package]] -name = "hashbrown" -version = "0.13.2" +name = "tokio-rustls" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "ahash", + "rustls 0.21.12", + "tokio", ] [[package]] -name = "hashbrown" -version = "0.14.5" +name = "tokio-rustls" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "ahash", - "allocator-api2", + "rustls 0.23.20", + "tokio", ] [[package]] -name = "hex" -version = "0.4.3" +name = "tokio-stream" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] [[package]] -name = "hex-literal" -version = "0.4.1" +name = "tokio-util" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] [[package]] -name = "hmac" -version = "0.12.1" +name = "toml" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ - "digest", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", ] [[package]] -name = "itertools" -version = "0.10.5" +name = "toml_datetime" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ - "either", + "serde", ] [[package]] -name = "itertools" -version = "0.13.0" +name = "toml_edit" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "either", + "indexmap 2.7.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] -name = "itoa" -version = "1.0.11" +name = "tonic" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.22.1", + "bytes", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.2", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost 0.13.4", + "rustls-native-certs 0.8.1", + "rustls-pemfile 2.2.0", + "socket2", + "tokio", + "tokio-rustls 0.26.1", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] [[package]] -name = "k256" -version = "0.13.4" +name = "tower" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sha2", + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "libc" -version = "0.2.158" +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] -name = "memchr" -version = "2.7.4" +name = "tower-service" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] -name = "miniz_oxide" -version = "0.8.0" +name = "tracing" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "adler2", + "pin-project-lite", + "tracing-attributes", + "tracing-core", ] [[package]] -name = "num-bigint" -version = "0.4.6" +name = "tracing-attributes" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ - "num-integer", - "num-traits", + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] -name = "num-integer" -version = "0.1.46" +name = "tracing-core" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ - "num-traits", + "once_cell", ] [[package]] -name = "num-traits" -version = "0.2.19" +name = "treediff" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "4d127780145176e2b5d16611cc25a900150e86e9fd79d3bde6ff3a37359c9cb5" dependencies = [ - "autocfg", + "rustc-serialize", ] [[package]] -name = "object" -version = "0.36.4" +name = "try-lock" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" -dependencies = [ - "memchr", -] +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "once_cell" -version = "1.19.0" +name = "typenum" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] -name = "p256" -version = "0.13.2" +name = "uid" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] +checksum = "7041bb797d82c5728d3a4a40432809095d8acc59bdd9e67426a2529b3b80c9be" [[package]] -name = "paste" -version = "1.0.15" +name = "unicode-ident" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] -name = "ppv-lite86" -version = "0.2.20" +name = "unicode-normalization" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ - "zerocopy", + "tinyvec", ] [[package]] -name = "primeorder" -version = "0.13.6" +name = "unicode-segmentation" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] -name = "proc-macro2" -version = "1.0.86" +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] -name = "prost" -version = "0.13.3" +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" -dependencies = [ - "bytes", - "prost-derive", -] +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] -name = "prost-derive" -version = "0.13.3" +name = "url" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ - "anyhow", - "itertools 0.13.0", - "proc-macro2", - "quote", - "syn 2.0.77", + "form_urlencoded", + "idna", + "percent-encoding", ] [[package]] -name = "quote" -version = "1.0.37" +name = "utf16_iter" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] -name = "rand" -version = "0.8.5" +name = "utf8_iter" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_chacha", - "rand_core", -] +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "uuid" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" [[package]] -name = "rand_core" -version = "0.6.4" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] -name = "rayon" -version = "1.10.0" +name = "version_check" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "rayon-core" -version = "1.12.1" +name = "walkdir" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ - "crossbeam-deque", - "crossbeam-utils", + "same-file", + "winapi-util", ] [[package]] -name = "rfc6979" -version = "0.4.0" +name = "want" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "hmac", - "subtle", + "try-lock", ] [[package]] -name = "rustc-demangle" -version = "0.1.24" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] [[package]] -name = "rustc_version" -version = "0.4.1" +name = "wasm-bindgen-backend" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ - "semver", + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.90", + "wasm-bindgen-shared", ] [[package]] -name = "ryu" -version = "1.0.18" +name = "wasm-bindgen-futures" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] [[package]] -name = "schemars" -version = "0.8.21" +name = "wasm-bindgen-macro" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", + "quote", + "wasm-bindgen-macro-support", ] [[package]] -name = "schemars_derive" -version = "0.8.21" +name = "wasm-bindgen-macro-support" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "serde_derive_internals", - "syn 2.0.77", + "syn 2.0.90", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] -name = "sec1" -version = "0.7.3" +name = "wasm-bindgen-shared" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "wasmer" +version = "4.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b28d4251f96ece14460328c56ee0525edcf4bbb08748cfd87fef3580ae4d403" dependencies = [ - "base16ct", - "der", - "generic-array", - "subtle", - "zeroize", + "bytes", + "cfg-if", + "derivative", + "indexmap 1.9.3", + "js-sys", + "more-asserts", + "rustc-demangle", + "serde", + "serde-wasm-bindgen", + "shared-buffer", + "target-lexicon", + "thiserror", + "tracing", + "wasm-bindgen", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-compiler-singlepass", + "wasmer-derive", + "wasmer-types", + "wasmer-vm", + "windows-sys 0.59.0", ] [[package]] -name = "semver" -version = "1.0.23" +name = "wasmer-compiler" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "009b8417d51dbca8ac9a640ea999cc924fc59040a81245ecd0e092cb7c45dc10" +dependencies = [ + "backtrace", + "bytes", + "cfg-if", + "enum-iterator", + "enumset", + "lazy_static", + "leb128", + "libc", + "memmap2 0.5.10", + "more-asserts", + "region", + "rkyv", + "self_cell", + "shared-buffer", + "smallvec", + "thiserror", + "wasmer-types", + "wasmer-vm", + "wasmparser", + "windows-sys 0.59.0", + "xxhash-rust", +] [[package]] -name = "serde" -version = "1.0.210" +name = "wasmer-compiler-cranelift" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "2445c6fb03824979448293e91d8a6daf0cdf66e8d996f31ef270e0d2cc3ea1f3" dependencies = [ - "serde_derive", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "gimli 0.26.2", + "more-asserts", + "rayon", + "smallvec", + "target-lexicon", + "tracing", + "wasmer-compiler", + "wasmer-types", ] [[package]] -name = "serde-json-wasm" -version = "1.0.1" +name = "wasmer-compiler-singlepass" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" +checksum = "c6add6b3abdbd2db38dd6a42e4727d860b893e5a6ba3ac49bdd42fe0e6dc06db" dependencies = [ - "serde", + "byteorder", + "dynasm", + "dynasmrt", + "enumset", + "gimli 0.26.2", + "lazy_static", + "more-asserts", + "rayon", + "smallvec", + "wasmer-compiler", + "wasmer-types", ] [[package]] -name = "serde_derive" -version = "1.0.210" +name = "wasmer-derive" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "02592d86ac19fb09c972e72edeb3e57ac5c569eac7e77b919b165da014e8c139" dependencies = [ + "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.77", + "syn 1.0.109", ] [[package]] -name = "serde_derive_internals" -version = "0.29.1" +name = "wasmer-middlewares" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +checksum = "9b8606706b694465035cbdd85a5a1ea437b7cd851e6a8dfe4e387a3e8f81ef78" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", + "wasmer", + "wasmer-types", + "wasmer-vm", ] [[package]] -name = "serde_json" -version = "1.0.128" +name = "wasmer-types" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "3d22a00f1a90e9e66d5427853f41e76d8ab89e03eb3034debd11933607fef56a" dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", + "bytecheck", + "enum-iterator", + "enumset", + "getrandom", + "hex", + "indexmap 1.9.3", + "more-asserts", + "rkyv", + "sha2 0.10.8", + "target-lexicon", + "thiserror", + "xxhash-rust", ] [[package]] -name = "sha2" -version = "0.10.8" +name = "wasmer-vm" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "87d88e8355157cd730fb81e33c3b4d6849fd44c26d32bf78820638e1d935967b" dependencies = [ + "backtrace", + "cc", "cfg-if", - "cpufeatures", - "digest", + "corosensei", + "crossbeam-queue", + "dashmap", + "derivative", + "enum-iterator", + "fnv", + "indexmap 1.9.3", + "lazy_static", + "libc", + "mach2", + "memoffset", + "more-asserts", + "region", + "scopeguard", + "thiserror", + "wasmer-types", + "windows-sys 0.59.0", ] [[package]] -name = "signature" -version = "2.2.0" +name = "wasmparser" +version = "0.121.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" dependencies = [ - "digest", - "rand_core", + "bitflags 2.6.0", + "indexmap 2.7.0", + "semver", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "web-sys" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] -name = "subtle" -version = "2.6.1" +name = "winapi-util" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] [[package]] -name = "syn" -version = "1.0.109" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "windows-targets 0.52.6", ] [[package]] -name = "syn" -version = "2.0.77" +name = "windows-registry" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", ] [[package]] -name = "thiserror" -version = "1.0.64" +name = "windows-result" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "thiserror-impl", + "windows-targets 0.52.6", ] [[package]] -name = "thiserror-impl" -version = "1.0.64" +name = "windows-strings" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", + "windows-result", + "windows-targets 0.52.6", ] [[package]] -name = "typenum" -version = "1.17.0" +name = "windows-sys" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" +dependencies = [ + "windows_aarch64_msvc 0.33.0", + "windows_i686_gnu 0.33.0", + "windows_i686_msvc 0.33.0", + "windows_x86_64_gnu 0.33.0", + "windows_x86_64_msvc 0.33.0", +] [[package]] -name = "unicode-ident" -version = "1.0.13" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] [[package]] -name = "unicode-xid" -version = "0.2.6" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] [[package]] -name = "version_check" -version = "0.9.5" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "windows-targets" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] [[package]] name = "windows-targets" @@ -1137,28 +5202,58 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1171,30 +5266,142 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -1213,7 +5420,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", ] [[package]] @@ -1233,5 +5461,27 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] diff --git a/Cargo.toml b/Cargo.toml index 4b190537..b754ddf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "cw-multi-test" -version = "2.2.0" +name = "clone-cw-multi-test" +version = "0.7.0" authors = [ "Ethan Frey ", - "Dariusz Depta " + "Dariusz Depta ", ] -description = "Testing tools for multi-contract interactions" +description = "Testing tools for multi-contract interactions. Helps simulating chain behavior with on-chain storage locally" repository = "https://github.com/CosmWasm/cw-multi-test" homepage = "https://cosmwasm.com" license = "Apache-2.0" @@ -25,6 +25,12 @@ cosmwasm_1_3 = ["cosmwasm_1_2", "cosmwasm-std/cosmwasm_1_3"] cosmwasm_1_4 = ["cosmwasm_1_3", "cosmwasm-std/cosmwasm_1_4"] cosmwasm_2_0 = ["cosmwasm_1_4", "cosmwasm-std/cosmwasm_2_0"] cosmwasm_2_1 = ["cosmwasm_2_0", "cosmwasm-std/cosmwasm_2_1"] +tokenfactory = [ + "dep:osmosis-std-derive", + "dep:chrono", + "dep:prost-types", + "dep:serde-cw-value", +] [dependencies] anyhow = "1.0.89" @@ -40,7 +46,56 @@ serde = "1.0.210" sha2 = "0.10.8" thiserror = "1.0.64" +# Clone testing deps +## Network +cw-orch-daemon = { version = "0.29.1" } +tokio = "1.28.2" +tonic = "0.12.2" + +## Emulation +cosmwasm-vm = { version = "=2.2.0", features = [ + "staking", + "stargate", + "iterator", +] } + +## Dual Storage +cosmrs = "0.19.0" +num-bigint = "0.4.3" + +# Analyzer +treediff = { version = "4.0.2", features = ["with-rustc-serialize"] } +rustc-serialize = "0.3.24" +serde_json = "1.0.105" +log = "0.4.19" +wasmer = { version = "4.3.0", default-features = false, features = [ + "cranelift", + "singlepass", +] } + +# Caching +cargo_metadata = "0.18" # For finding Cargo target dir +file-lock = "2.1" + +# For tokenfactory +osmosis-std-derive = { version = "0.26.0", optional = true } +prost-types = { version = "0.13.4", optional = true } +serde-cw-value = { version = "0.7.0", optional = true } +chrono = { version = "0.4.39", optional = true } +cw-orch = "0.27.0" + [dev-dependencies] hex = "0.4.3" hex-literal = "0.4.1" once_cell = "1.19.0" + +# Clone testing deps + +# General +env_logger = "0.10.0" +cosmwasm-schema = "2.1.3" + +# Cavern Test App +cw20 = "2.0.0" +moneymarket = { git = "https://github.com/CavernPerson/money-market-contracts" } +cw2 = "2.0.0" diff --git a/examples/abstract_test_app.rs b/examples/abstract_test_app.rs new file mode 100644 index 00000000..2a400376 --- /dev/null +++ b/examples/abstract_test_app.rs @@ -0,0 +1,338 @@ +// use abstract_core::ans_host::AssetsResponse; +// use abstract_core::objects::pool_id::PoolAddressBase; +// use abstract_core::objects::PoolMetadata; +// use cosmwasm_std::coins; +// use cosmwasm_std::Timestamp; +// use cosmwasm_std::Uint128; +// use cw_orch_daemon::queriers::Node; +// use cw_orch_daemon::DaemonQuerier; +// use tokio::runtime::Runtime; + +// use abstract_core::adapter::AuthorizedAddressesResponse; +// use abstract_core::adapter::BaseQueryMsg; +// use cw_multi_test::wasm_emulation::storage::analyzer::StorageAnalyzer; + +// use abstract_core::adapter::AdapterRequestMsg; +// use abstract_core::adapter::BaseInstantiateMsg; +// use abstract_core::objects::module::Module; +// use abstract_core::objects::module_reference::ModuleReference; +// use abstract_core::objects::AnsAsset; +// use abstract_core::objects::AssetEntry; +// use abstract_dex_adapter::msg::InstantiateMsg; +// use abstract_dex_adapter::msg::{DexAction, DexExecuteMsg, DexInstantiateMsg}; +// use cosmwasm_std::Decimal; +// use cw_multi_test::wasm_emulation::contract::WasmContract; +// use cw_orch_daemon::CosmWasm; +// use std::path::Path; + +// use cw_asset::AssetInfo; + +// use abstract_core::version_control::{self}; + +// use abstract_core::objects::module::ModuleInfo; +// use abstract_core::objects::module::ModuleVersion; +// use cosmwasm_std::{to_json_binary, Addr}; +// use cw_multi_test::AppBuilder; +// use cw_multi_test::BankKeeper; +// use cw_multi_test::Executor; +// use cw_multi_test::FailingModule; + +// use abstract_interface::get_account_contracts; +// use abstract_interface::Abstract; +// use abstract_interface::ManagerQueryFns; +// use cosmwasm_std::Empty; +// use cw_multi_test::WasmKeeper; +// use cw_orch_daemon::Daemon; +// use cw_orch::deploy::Deploy; + +// use cw_orch_daemon::prelude::ContractInstance; +// use dotenv::dotenv; + +// use abstract_core::manager::ExecuteMsg; + +// // Abstract patch + +// #[cosmwasm_schema::cw_serde] +// pub struct ModulesResponse { +// pub modules: Vec, +// } + +// fn main() { +// dotenv().ok(); +// let runtime = tokio::runtime::Runtime::new().unwrap(); + +// let mut chain = cw_orch_daemon::networks::JUNO_1; +// chain.grpc_urls = &["http://juno-grpc.polkachu.com:12690"]; + +// let daemon = Daemon::builder() +// .chain(chain.clone()) +// .handle(runtime.handle()) +// .build() +// .unwrap(); + +// let abstract_ = Abstract::load_from(daemon.clone()).unwrap(); + +// // Query an account, its owner and install a module for + +// let (manager, proxy) = get_account_contracts(&abstract_.version_control, Some(1)); + +// let ownership = manager.ownership().unwrap(); + +// let owner = ownership.owner.unwrap(); +// // We use this owner to install and uninstall a module +// let owner_addr = Addr::unchecked(owner.clone()); + +// env_logger::init(); +// let mut wasm = WasmKeeper::::new(); +// wasm.set_chain(chain.clone().into()); + +// let mut bank = BankKeeper::new(); +// bank.set_chain(chain.clone().into()); + +// let node_querier = daemon.query_client::(); +// let block = runtime.block_on(node_querier.latest_block()).unwrap(); + +// // First we instantiate a new app +// let app = AppBuilder::default() +// .with_wasm::, _>(wasm) +// .with_bank(bank) +// .with_block(cosmwasm_std::BlockInfo { +// height: block.header.height.into(), +// time: Timestamp::from_seconds(block.header.time.unix_timestamp().try_into().unwrap()), +// chain_id: block.header.chain_id.to_string(), +// }); +// let mut app = app.build(|_, _, _| {}); + +// log::info!("Built App Environment"); + +// // We need to register a pool pairing on the ans host +// app.execute_contract( +// Addr::unchecked(owner.clone()), +// abstract_.ans_host.address().unwrap(), +// &abstract_core::ans_host::ExecuteMsg::UpdateDexes { +// to_add: vec!["wyndex".to_string()], +// to_remove: vec![], +// }, +// &[], +// ) +// .unwrap(); + +// app.execute_contract( +// Addr::unchecked(owner.clone()), +// abstract_.ans_host.address().unwrap(), +// &abstract_core::ans_host::ExecuteMsg::UpdatePools { +// to_add: vec![( +// PoolAddressBase::contract( +// "juno1gqy6rzary8vwnslmdavqre6jdhakcd4n2z4r803ajjmdq08r66hq7zcwrj".to_string(), +// ), +// PoolMetadata { +// dex: "wyndex".to_string(), +// pool_type: abstract_core::objects::PoolType::ConstantProduct, +// assets: vec!["axelar>usdc".into(), "juno>juno".into()], +// }, +// )], +// to_remove: vec![], +// }, +// &[], +// ) +// .unwrap(); +// // End registering the pool pairing + +// // test +// let rt = Runtime::new().unwrap(); +// let test = rt +// .block_on( +// CosmWasm::new(daemon.channel()).contract_raw_state( +// "juno13q8rv8w9ew5cn6wecr2p4scegzu9nac0hv2dx807l4vz60h0ldns0ksvz0", +// hex::decode( +// "0008706f6f6c5f69647300096a756e6f3e6a756e6f00096a756e6f3e77796e6477796e646578", +// ) +// .unwrap(), +// ), +// ) +// .unwrap(); +// log::info!("{:x?}", test); + +// // We deploy the adapter : +// // 1. upload the code +// let code = std::fs::read( +// Path::new(env!("CARGO_MANIFEST_DIR")) +// .join("artifacts") +// .join("abstract_dex_adapter-juno.wasm"), +// ) +// .unwrap(); +// let dex_code = WasmContract::new_local(code, chain.clone()); +// let code_id = app.store_code(dex_code); + +// // 2. Instantiate the code +// let dex_addr = app +// .instantiate_contract( +// code_id, +// owner_addr.clone(), +// &InstantiateMsg { +// module: DexInstantiateMsg { +// swap_fee: Decimal::percent(1), +// recipient_account: 0, +// }, +// base: BaseInstantiateMsg { +// ans_host_address: abstract_.ans_host.address().unwrap().to_string(), +// version_control_address: abstract_ +// .version_control +// .address() +// .unwrap() +// .to_string(), +// }, +// }, +// &[], +// "Dex adapter", +// None, +// ) +// .unwrap(); + +// log::info!("Instantiated Dex adapter"); + +// // 3. Register the adapter in version control +// let module = +// ModuleInfo::from_id("abstract:dex", ModuleVersion::Version("0.17.1".to_string())).unwrap(); +// app.execute_contract( +// owner_addr.clone(), +// abstract_.version_control.address().unwrap(), +// &version_control::ExecuteMsg::ProposeModules { +// modules: vec![(module.clone(), ModuleReference::Adapter(dex_addr.clone()))], +// }, +// &[], +// ) +// .unwrap(); + +// log::info!("Proposed and registered Dex adapter"); +// // We install the module on the account +// app.execute_contract( +// Addr::unchecked(owner.clone()), +// manager.address().unwrap(), +// &ExecuteMsg::InstallModule { +// init_msg: Some(to_json_binary(&Empty {}).unwrap()), +// module, +// }, +// &[], +// ) +// .unwrap(); + +// log::info!("Installed Dex adapter"); +// // Let's see what registered in the ans as assets + +// /* +// let assets = abstract_.ans_host.asset_list(None, Some(30), Some("juno>future".to_string())).unwrap(); +// log::info!("{:?}", assets); + +// let pools = abstract_.ans_host.pool_list(None, Some(30), None).unwrap(); +// log::info!("{:?}", pools); +// */ +// // We need to get the authorized addresses on the adapter + +// /* +// app.execute_contract(Addr::unchecked(owner.clone()), manager.address().unwrap(),&ExecuteMsg::ExecOnModule { module_id: "abstract:dex".to_string(), +// exec_msg: to_json_binary(&abstract_dex_adapter::msg::ExecuteMsg::Base( +// abstract_core::adapter::BaseExecuteMsg::UpdateAuthorizedAddresses { +// to_add: vec![manager.address().unwrap().to_string()], +// to_remove: vec![] +// } +// )).unwrap()}, &[]).unwrap(); +// */ +// log::info!("Updated authorized address on the Dex adapter"); +// /* Query to verify that the manager was authorized to execute on the adapter */ +// let addresses: AuthorizedAddressesResponse = app +// .wrap() +// .query_wasm_smart( +// dex_addr, +// &abstract_dex_adapter::msg::QueryMsg::Base(BaseQueryMsg::AuthorizedAddresses { +// proxy_address: proxy.address().unwrap().to_string(), +// }), +// ) +// .unwrap(); +// log::info!("AuthorizedAddresses on dex {:?}", addresses); + +// let analysis = StorageAnalyzer::new(&app).unwrap(); +// analysis.compare_all_readable_contract_storage(chain.into()); +// //log::info!("analysis, dex {:x?}", analysis.get_contract_storage(dex_addr)); +// /* End query */ +// log::info!("Some queries to check everything is alright"); + +// // We deposit funds on the proxy + +// app.execute( +// owner_addr, +// cosmwasm_std::CosmosMsg::Bank(cosmwasm_std::BankMsg::Send { +// to_address: proxy.address().unwrap().to_string(), +// amount: coins(100_000u128, "ujuno"), +// }), +// ) +// .unwrap(); + +// // We get the balances (for asserting) +// let usdc: AssetsResponse = app +// .wrap() +// .query_wasm_smart( +// abstract_.ans_host.address().unwrap(), +// &abstract_core::ans_host::QueryMsg::Assets { +// names: vec!["axelar>usdc".to_string()], +// }, +// ) +// .unwrap(); +// let usdc = match &usdc.assets[0].1 { +// AssetInfo::Native(denom) => denom, +// _ => panic!("Expected native denom"), +// }; +// let old_balance = app +// .wrap() +// .query_balance(proxy.address().unwrap(), "ujuno") +// .unwrap(); +// let old_usdc_balance = app +// .wrap() +// .query_balance(proxy.address().unwrap(), usdc) +// .unwrap(); + +// // We test a swap interaction +// app.execute_contract( +// Addr::unchecked(owner), +// manager.address().unwrap(), +// &ExecuteMsg::ExecOnModule { +// module_id: "abstract:dex".to_string(), +// exec_msg: to_json_binary(&abstract_dex_adapter::msg::ExecuteMsg::Module( +// AdapterRequestMsg { +// proxy_address: None, +// request: DexExecuteMsg::Action { +// action: DexAction::Swap { +// ask_asset: AssetEntry::new("axelar>usdc"), +// offer_asset: AnsAsset::new(AssetEntry::new("juno>juno"), 100_000u128), +// belief_price: None, +// max_spread: None, +// }, +// dex: "wyndex".to_string(), +// }, +// }, +// )) +// .unwrap(), +// }, +// &[], +// ) +// .unwrap(); +// log::info!("Execute the swap"); + +// // We get the juno balance (should be lower) +// let new_balance = app +// .wrap() +// .query_balance(proxy.address().unwrap(), "ujuno") +// .unwrap(); +// assert_eq!( +// old_balance.amount - new_balance.amount, +// Uint128::from(100_000u128) +// ); + +// let new_usdc_balance = app +// .wrap() +// .query_balance(proxy.address().unwrap(), usdc) +// .unwrap(); +// assert!(old_usdc_balance.amount < new_usdc_balance.amount); +// } + +fn main() {} diff --git a/examples/cavern_test_app.rs b/examples/cavern_test_app.rs new file mode 100644 index 00000000..c9b1ae59 --- /dev/null +++ b/examples/cavern_test_app.rs @@ -0,0 +1,211 @@ +use clone_cw_multi_test::{ + wasm_emulation::{channel::RemoteChannel, storage::analyzer::StorageAnalyzer}, + AppBuilder, Executor, MockApiBech32, +}; +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{coins, Addr, BlockInfo, ContractInfoResponse, QueryRequest, WasmQuery}; +use cw20::BalanceResponse; +use cw_orch_daemon::{networks::PHOENIX_1, queriers::Node}; +use std::path::Path; +use tokio::runtime::Runtime; + +use cw20::Cw20QueryMsg; + +use cosmwasm_std::Empty; +use moneymarket::market::ExecuteMsg; + +/// COUNTER CONTRACT MSGs +#[cw_serde] +#[cfg_attr(feature = "interface", derive(cw_orch::QueryFns))] // Function generation +#[derive(QueryResponses)] +pub enum QueryMsg { + // GetCount returns the current count as a json-encoded number + #[returns(GetCountResponse)] + GetCount {}, + // GetCount returns the current count of the cousin contract + #[returns(GetCountResponse)] + GetCousinCount {}, +} + +// Custom response for the query +#[cw_serde] +pub struct GetCountResponse { + pub count: i32, +} + +#[cw_serde] +pub struct MigrateMsg { + pub t: String, +} + +#[cw_serde] +pub struct InstantiateMsg { + pub count: i32, +} + +/// END CONTRACT MSGs + +pub fn test() -> anyhow::Result<()> { + env_logger::init(); + + let sender = "terra1ytj0hhw39j88qsx4yapsr6ker83jv3aj354gmj"; + let market = "terra1zqlcp3aty4p4rjv96h6qdascdn953v6crhwedu5vddxjnp349upscluex6"; + let currency = "ibc/B3504E092456BA618CC28AC671A71FB08C6CA0FD0BE7C8A5B5A3E2DD933CC9E4"; + let a_currency = "terra1gwdxyqtu75es0x5l6cd9flqhh87zjtj7qdankayyr0vtt7s9w4ssm7ds8m"; + + let runtime = Runtime::new()?; + let chain = PHOENIX_1; + let remote_channel = RemoteChannel::new( + &runtime, + chain.grpc_urls, + chain.chain_id, + chain.network_info.pub_address_prefix, + )?; + + let block = runtime.block_on( + Node { + channel: remote_channel.channel.clone(), + rt_handle: Some(runtime.handle().clone()), + } + ._block_info(), + )?; + // First we instantiate a new app + let app = AppBuilder::default() + .with_remote(remote_channel.clone()) + .with_block(BlockInfo { + height: block.height, + time: block.time, + chain_id: chain.chain_id.to_string(), + }) + .with_api(MockApiBech32::new(chain.network_info.pub_address_prefix)); + let mut app = app.build(|_, _, _| {}); + // Then we send a message to the blockchain through the app + + // We query to verify the state changed + let response: BalanceResponse = app + .wrap() + .query_wasm_smart( + a_currency, + &Cw20QueryMsg::Balance { + address: sender.to_string(), + }, + ) + .unwrap(); + log::info!("Before deposit : {:?}", response); + + app.execute_contract( + Addr::unchecked(sender), + Addr::unchecked(market), + &ExecuteMsg::DepositStable {}, + &coins(10_000, currency), + ) + .unwrap(); + + // We query to verify the state changed + let response: BalanceResponse = app + .wrap() + .query_wasm_smart( + a_currency, + &Cw20QueryMsg::Balance { + address: sender.to_string(), + }, + ) + .unwrap(); + log::info!("After deposit : {:?}", response); + + // Now we try to migrate the contract + + let code = std::fs::read( + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("artifacts") + .join("counter_contract_with_cousin.wasm"), + ) + .unwrap(); + + let code_id = app.store_wasm_code(code); + + // We try to instantiate a new contract. Should work ok ! + let contract_addr = app.instantiate_contract( + code_id, + Addr::unchecked(sender), + &InstantiateMsg { count: 87 }, + &[], + "label".to_string(), + None, + )?; + + log::info!("New contract address {:?}", contract_addr); + + let contract_info: ContractInfoResponse = app + .wrap() + .query(&QueryRequest::Wasm(WasmQuery::ContractInfo { + contract_addr: market.to_string(), + })) + .unwrap(); + + app.migrate_contract( + Addr::unchecked(contract_info.admin.clone().unwrap()), + Addr::unchecked(market), + &MigrateMsg { t: "t".to_string() }, + code_id, + ) + .unwrap(); + + // The query count message should error with a specific storage error + + let err = app + .wrap() + .query_wasm_smart::(market, &QueryMsg::GetCount {}) + .unwrap_err(); + + if !err + .to_string() + .contains("counter_contract::state::State not found") + { + panic!( + "Error {} should contain counter_contract::state::State not found", + err + ); + } + + // Now we migrate back and deposit again + app.migrate_contract( + Addr::unchecked(contract_info.admin.unwrap()), + Addr::unchecked(market), + &Empty {}, + contract_info.code_id, + ) + .unwrap(); + app.execute_contract( + Addr::unchecked(sender), + Addr::unchecked(market), + &ExecuteMsg::DepositStable {}, + &coins(10_000, currency), + ) + .unwrap(); + + // We query to verify the state changed + let response: BalanceResponse = app + .wrap() + .query_wasm_smart( + a_currency, + &Cw20QueryMsg::Balance { + address: sender.to_string(), + }, + ) + .unwrap(); + log::info!("After migrate and deposit : {:?}", response); + + let analysis = StorageAnalyzer::new(&app).unwrap(); + log::info!( + "All contracts storage {:?}", + analysis.all_readable_contract_storage() + ); + + analysis.compare_all_readable_contract_storage(); + Ok(()) +} + +fn main() { + test().unwrap(); +} diff --git a/examples/counter/contract.rs b/examples/counter/contract.rs new file mode 100644 index 00000000..3df53e71 --- /dev/null +++ b/examples/counter/contract.rs @@ -0,0 +1,130 @@ +use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cw2::set_contract_version; + +use crate::counter::{error::*, execute, msg::*, query, state::*}; + +// version info for migration info +pub const CONTRACT_NAME: &str = "crates.io:counter"; +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +// ANCHOR: interface_entry +// ANCHOR: entry_point_line +#[cfg_attr(feature = "export", entry_point)] +// ANCHOR_END: entry_point_line +pub fn instantiate( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + let state = State { + count: msg.count, + owner: info.sender.clone(), + cousin: None, + }; + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + STATE.save(deps.storage, &state)?; + + Ok(Response::new() + .add_attribute("method", "instantiate") + .add_attribute("owner", info.sender) + .add_attribute("count", msg.count.to_string())) +} + +#[cfg_attr(feature = "export", entry_point)] +pub fn execute( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::Increment {} => execute::increment(deps), + ExecuteMsg::Reset { count } => execute::reset(deps, info, count), + ExecuteMsg::SetCousin { cousin } => execute::set_cousin(deps, info, cousin), + } +} + +#[cfg_attr(feature = "export", entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::GetCount {} => to_json_binary(&query::count(deps)?), + QueryMsg::GetCousinCount {} => to_json_binary(&query::cousin_count(deps)?), + QueryMsg::GetRawCousinCount {} => to_json_binary(&query::raw_cousin_count(deps)?), + } +} + +// ANCHOR_END: interface_entry + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::{ + coins, from_json, + testing::{mock_dependencies, mock_env, mock_info}, + }; + + #[test] + fn proper_initialization() { + let mut deps = mock_dependencies(); + + let msg = InstantiateMsg { count: 17 }; + let info = mock_info("creator", &coins(1000, "earth")); + + // we can just call .unwrap() to assert this was a success + let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); + assert_eq!(0, res.messages.len()); + + // it worked, let's query the state + let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap(); + let value: GetCountResponse = from_json(res).unwrap(); + assert_eq!(17, value.count); + } + + #[test] + fn increment() { + let mut deps = mock_dependencies(); + + let msg = InstantiateMsg { count: 17 }; + let info = mock_info("creator", &coins(2, "token")); + let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); + + // beneficiary can release it + let info = mock_info("anyone", &coins(2, "token")); + let msg = ExecuteMsg::Increment {}; + let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); + + // should increase counter by 1 + let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap(); + let value: GetCountResponse = from_json(res).unwrap(); + assert_eq!(18, value.count); + } + + #[test] + fn reset() { + let mut deps = mock_dependencies(); + + let msg = InstantiateMsg { count: 17 }; + let info = mock_info("creator", &coins(2, "token")); + let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); + + // beneficiary can release it + let unauth_info = mock_info("anyone", &coins(2, "token")); + let msg = ExecuteMsg::Reset { count: 5 }; + let res = execute(deps.as_mut(), mock_env(), unauth_info, msg); + match res { + Err(ContractError::Unauthorized {}) => {} + _ => panic!("Must return unauthorized error"), + } + + // only the original creator can reset the counter + let auth_info = mock_info("creator", &coins(2, "token")); + let msg = ExecuteMsg::Reset { count: 5 }; + let _res = execute(deps.as_mut(), mock_env(), auth_info, msg).unwrap(); + + // should now be 5 + let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap(); + let value: GetCountResponse = from_json(res).unwrap(); + assert_eq!(5, value.count); + } +} diff --git a/examples/counter/error.rs b/examples/counter/error.rs new file mode 100644 index 00000000..16b589d3 --- /dev/null +++ b/examples/counter/error.rs @@ -0,0 +1,13 @@ +use cosmwasm_std::StdError; +use thiserror::Error; + +#[derive(Error, PartialEq, Debug)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("Unauthorized")] + Unauthorized {}, + // Add any other custom errors you like here. + // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. +} diff --git a/examples/counter/execute.rs b/examples/counter/execute.rs new file mode 100644 index 00000000..c1c94bfc --- /dev/null +++ b/examples/counter/execute.rs @@ -0,0 +1,39 @@ +use cosmwasm_std::{DepsMut, MessageInfo, Response}; + +use crate::counter::{error::*, state::*}; + +pub fn increment(deps: DepsMut) -> Result { + STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { + state.count += 1; + Ok(state) + })?; + + Ok(Response::new().add_attribute("action", "increment")) +} + +pub fn reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result { + STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { + if info.sender != state.owner { + return Err(ContractError::Unauthorized {}); + } + state.count = count; + Ok(state) + })?; + Ok(Response::new().add_attribute("action", "reset")) +} + +pub fn set_cousin( + deps: DepsMut, + info: MessageInfo, + cousin: String, +) -> Result { + let cousin_addr = deps.api.addr_validate(&cousin)?; + STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { + if info.sender != state.owner { + return Err(ContractError::Unauthorized {}); + } + state.cousin = Some(cousin_addr); + Ok(state) + })?; + Ok(Response::new().add_attribute("action", "set_cousin")) +} diff --git a/examples/counter/interface.rs b/examples/counter/interface.rs new file mode 100644 index 00000000..89e15635 --- /dev/null +++ b/examples/counter/interface.rs @@ -0,0 +1,65 @@ +// ANCHOR: custom_interface +use cw_orch::{interface, prelude::*}; + +use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; + +#[interface(InstantiateMsg, ExecuteMsg, QueryMsg, MigrateMsg)] +pub struct CounterContract; + +impl Uploadable for CounterContract { + /// Return the path to the wasm file corresponding to the contract + fn wasm(&self) -> WasmPath { + artifacts_dir_from_workspace!() + .find_wasm_path("counter_contract") + .unwrap() + } + /// Returns a CosmWasm contract wrapper + fn wrapper(&self) -> Box> { + Box::new( + ContractWrapper::new_with_empty( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ) + .with_migrate(crate::contract::migrate), + ) + } +} +// ANCHOR_END: custom_interface + +use crate::contract::CONTRACT_NAME; +use cw_orch::anyhow::Result; +use cw_orch::prelude::queriers::Node; + +// ANCHOR: daemon +impl CounterContract { + /// Deploys the counter contract at a specific block height + pub fn await_launch(&self) -> Result<()> { + let daemon = self.get_chain(); + let rt = daemon.rt_handle.clone(); + + rt.block_on(async { + // Get the node query client, there are a lot of other clients available. + let node = daemon.query_client::(); + let mut latest_block = node.latest_block().await.unwrap(); + + while latest_block.header.height.value() < 100 { + // wait for the next block + daemon.next_block().unwrap(); + latest_block = node.latest_block().await.unwrap(); + } + }); + + let contract = CounterContract::new(CONTRACT_NAME, daemon.clone()); + + // Upload the contract + contract.upload().unwrap(); + + // Instantiate the contract + let msg = InstantiateMsg { count: 1i32 }; + contract.instantiate(&msg, None, None).unwrap(); + + Ok(()) + } +} +// ANCHOR_END: daemon diff --git a/examples/counter/mod.rs b/examples/counter/mod.rs new file mode 100644 index 00000000..2ab5d224 --- /dev/null +++ b/examples/counter/mod.rs @@ -0,0 +1,6 @@ +pub mod contract; +mod error; +pub(crate) mod execute; +pub mod msg; +pub(crate) mod query; +pub mod state; diff --git a/examples/counter/msg.rs b/examples/counter/msg.rs new file mode 100644 index 00000000..bfd81a99 --- /dev/null +++ b/examples/counter/msg.rs @@ -0,0 +1,62 @@ +#![warn(missing_docs)] +//! # Counter contract + +use cosmwasm_schema::{cw_serde, QueryResponses}; + +#[cw_serde] +/// Instantiate method for counter +pub struct InstantiateMsg { + /// Initial count + pub count: i32, +} + +// ANCHOR: exec_msg +#[cw_serde] +#[cfg_attr(feature = "interface", derive(cw_orch::ExecuteFns))] // Function generation +/// Execute methods for counter +pub enum ExecuteMsg { + /// Increment count by one + Increment {}, + /// Reset count + Reset { + /// Count value after reset + count: i32, + }, + SetCousin { + cousin: String, + }, +} +// ANCHOR_END: exec_msg + +// ANCHOR: query_msg +#[cw_serde] +#[cfg_attr(feature = "interface", derive(cw_orch::QueryFns))] // Function generation +#[derive(QueryResponses)] +/// Query methods for counter +pub enum QueryMsg { + /// GetCount returns the current count as a json-encoded number + #[returns(GetCountResponse)] + GetCount {}, + /// GetCount returns the current count as a json-encoded number + #[returns(GetCountResponse)] + GetCousinCount {}, + /// GetCount returns the current count as a json-encoded number + #[returns(GetCountResponse)] + GetRawCousinCount {}, +} + +// Custom response for the query +#[cw_serde] +/// Response from get_count query +pub struct GetCountResponse { + /// Current count in the state + pub count: i32, +} +// ANCHOR_END: query_msg + +#[cw_serde] +/// Migrate message for count contract +pub struct MigrateMsg { + /// Your favorite type of tea + pub t: String, +} diff --git a/examples/counter/query.rs b/examples/counter/query.rs new file mode 100644 index 00000000..31a1d585 --- /dev/null +++ b/examples/counter/query.rs @@ -0,0 +1,29 @@ +use cosmwasm_std::{to_json_binary, Deps, QueryRequest, StdResult, WasmQuery}; + +use crate::counter::{ + msg::{GetCountResponse, QueryMsg}, + state::STATE, +}; + +pub fn count(deps: Deps) -> StdResult { + let state = STATE.load(deps.storage)?; + Ok(GetCountResponse { count: state.count }) +} + +pub fn cousin_count(deps: Deps) -> StdResult { + let state = STATE.load(deps.storage)?; + let cousin_count: GetCountResponse = + deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: state.cousin.unwrap().to_string(), + msg: to_json_binary(&QueryMsg::GetCount {})?, + }))?; + Ok(cousin_count) +} + +pub fn raw_cousin_count(deps: Deps) -> StdResult { + let state = STATE.load(deps.storage)?; + let cousin_state = STATE.query(&deps.querier, state.cousin.unwrap())?; + Ok(GetCountResponse { + count: cousin_state.count, + }) +} diff --git a/examples/counter/state.rs b/examples/counter/state.rs new file mode 100644 index 00000000..7fe15222 --- /dev/null +++ b/examples/counter/state.rs @@ -0,0 +1,14 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use cosmwasm_std::Addr; +use cw_storage_plus::Item; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct State { + pub count: i32, + pub owner: Addr, + pub cousin: Option, +} + +pub const STATE: Item = Item::new("state"); diff --git a/examples/cousin_test.rs b/examples/cousin_test.rs new file mode 100644 index 00000000..27234597 --- /dev/null +++ b/examples/cousin_test.rs @@ -0,0 +1,152 @@ +fn main() { + test().unwrap() +} +use std::path::Path; + +use anyhow::Result as AnyResult; +use clone_cw_multi_test::{ + wasm_emulation::{channel::RemoteChannel, query::ContainsRemote}, + App, AppBuilder, BankKeeper, ContractWrapper, Executor, MockApiBech32, WasmKeeper, +}; +use cosmwasm_std::{Addr, Empty}; +use counter::msg::{ExecuteMsg, GetCountResponse, QueryMsg}; +use cw_orch_daemon::networks::PHOENIX_1; +use tokio::runtime::Runtime; + +mod counter; + +pub const SENDER: &str = "terra17c6ts8grcfrgquhj3haclg44le8s7qkx6l2yx33acguxhpf000xqhnl3je"; +fn increment(app: &mut App, contract: Addr) -> AnyResult<()> { + let sender = Addr::unchecked(SENDER); + app.execute_contract( + sender.clone(), + contract.clone(), + &ExecuteMsg::Increment {}, + &[], + )?; + Ok(()) +} + +fn count(app: &App, contract: Addr) -> AnyResult { + Ok(app + .wrap() + .query_wasm_smart(contract.clone(), &QueryMsg::GetCount {})?) +} + +fn raw_cousin_count( + app: &App, + contract: Addr, +) -> AnyResult { + Ok(app + .wrap() + .query_wasm_smart(contract.clone(), &QueryMsg::GetRawCousinCount {})?) +} + +fn cousin_count( + app: &App, + contract: Addr, +) -> AnyResult { + Ok(app + .wrap() + .query_wasm_smart(contract.clone(), &QueryMsg::GetCousinCount {})?) +} + +fn test() -> AnyResult<()> { + env_logger::init(); + let rust_contract = ContractWrapper::new( + counter::contract::execute, + counter::contract::instantiate, + counter::contract::query, + ); + + let code = std::fs::read( + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("artifacts") + .join("counter_contract_with_cousin.wasm"), + ) + .unwrap(); + + let runtime = Runtime::new()?; + let chain = PHOENIX_1; + let remote_channel = RemoteChannel::new( + &runtime, + chain.grpc_urls, + chain.chain_id, + chain.network_info.pub_address_prefix, + )?; + + let wasm = WasmKeeper::::new().with_remote(remote_channel.clone()); + + let bank = BankKeeper::new().with_remote(remote_channel.clone()); + + // First we instantiate a new app + let mut app = AppBuilder::default() + .with_wasm(wasm) + .with_bank(bank) + .with_remote(remote_channel) + .with_api(MockApiBech32::new(chain.network_info.pub_address_prefix)) + .build(|_, _, _| {}); + + let sender = Addr::unchecked(SENDER); + let rust_code_id = app.store_code(Box::new(rust_contract)); + let wasm_code_id = app.store_wasm_code(code); + + let counter_rust = app + .instantiate_contract( + rust_code_id, + sender.clone(), + &counter::msg::InstantiateMsg { count: 1 }, + &[], + "cousin-counter", + Some(sender.to_string()), + ) + .unwrap(); + + let counter_wasm = app + .instantiate_contract( + wasm_code_id, + sender.clone(), + &counter::msg::InstantiateMsg { count: 1 }, + &[], + "cousin-counter", + Some(sender.to_string()), + ) + .unwrap(); + + app.execute_contract( + sender.clone(), + counter_rust.clone(), + &ExecuteMsg::SetCousin { + cousin: counter_wasm.to_string(), + }, + &[], + )?; + + app.execute_contract( + sender.clone(), + counter_wasm.clone(), + &ExecuteMsg::SetCousin { + cousin: counter_rust.to_string(), + }, + &[], + )?; + + // Increment the count on both and see what's what + increment(&mut app, counter_rust.clone())?; + increment(&mut app, counter_rust.clone())?; + increment(&mut app, counter_wasm.clone())?; + + // Assert the count + assert_eq!(count(&app, counter_rust.clone())?.count, 3); + assert_eq!(count(&app, counter_wasm.clone())?.count, 2); + + // Assert the raw cousin count + assert_eq!(raw_cousin_count(&app, counter_rust.clone())?.count, 2); + assert_eq!(raw_cousin_count(&app, counter_wasm.clone())?.count, 3); + + // Assert the cousin count + assert_eq!(cousin_count(&app, counter_rust.clone())?.count, 2); + assert_eq!(cousin_count(&app, counter_wasm.clone())?.count, 3); + + Ok(()) +} diff --git a/examples/local_test_app.rs b/examples/local_test_app.rs new file mode 100644 index 00000000..4077c7ab --- /dev/null +++ b/examples/local_test_app.rs @@ -0,0 +1,189 @@ +// use cw_multi_test::wasm_emulation::channel::RemoteChannel; +// use std::path::Path; +// use tokio::runtime::Runtime; + +// use cosmwasm_schema::{cw_serde, QueryResponses}; +// use cosmwasm_std::Empty; + +// use cw_multi_test::error::AnyResult; +// use cw_multi_test::wasm_emulation::contract::WasmContract; +// use cw_multi_test::wasm_emulation::storage::analyzer::StorageAnalyzer; +// use cw_multi_test::AppBuilder; +// use cw_multi_test::Executor; +// use cw_multi_test::WasmKeeper; + +// use cw_orch_daemon::networks::PHOENIX_1; + +// #[cw_serde] +// pub struct InstantiateMsg { +// pub count: i32, +// } + +// // ANCHOR: exec_msg +// #[cw_serde] +// #[cfg_attr(feature = "interface", derive(cw_orch::ExecuteFns))] // Function generation +// pub enum ExecuteMsg { +// Increment {}, +// IncrementAndQuery {}, +// SetCousin { addr: String }, +// Reset { count: i32 }, +// } +// // ANCHOR_END: exec_msg + +// // ANCHOR: query_msg +// #[cw_serde] +// #[cfg_attr(feature = "interface", derive(cw_orch::QueryFns))] // Function generation +// #[derive(QueryResponses)] +// pub enum QueryMsg { +// // GetCount returns the current count as a json-encoded number +// #[returns(GetCountResponse)] +// GetCount {}, +// // GetCount returns the current count of the cousin contract +// #[returns(GetCountResponse)] +// GetCousinCount {}, +// } + +// // Custom response for the query +// #[cw_serde] +// pub struct GetCountResponse { +// pub count: i32, +// } +// #[cw_serde] +// pub struct GetCousinCountResponse { +// pub raw: i32, +// pub smart: i32, +// } +// // ANCHOR_END: query_msg + +// #[cw_serde] +// pub struct MigrateMsg { +// pub t: String, +// } + +// pub fn test() -> AnyResult<()> { +// env_logger::init(); + +// let runtime = Runtime::new()?; +// let chain = PHOENIX_1; +// let remote_channel = RemoteChannel::new(&runtime, chain)?; + +// let wasm = WasmKeeper::::new().with_remote(remote_channel.clone()); + +// // First we instantiate a new app +// let app = AppBuilder::default() +// .with_remote(remote_channel.clone()) +// .with_wasm(wasm); +// let mut app = app.build(|_, _, _| {})?; + +// // Then we send a message to the blockchain through the app +// let sender = app.next_address(); + +// let code = std::fs::read( +// Path::new(env!("CARGO_MANIFEST_DIR")) +// .join("artifacts") +// .join("counter_contract.wasm"), +// ) +// .unwrap(); +// let counter_contract = WasmContract::new_local(code); + +// let code_id = app.store_code(counter_contract); + +// let counter1 = app +// .instantiate_contract( +// code_id, +// sender.clone(), +// &InstantiateMsg { count: 1 }, +// &[], +// "cousin-counter", +// Some(sender.to_string()), +// ) +// .unwrap(); +// let counter2 = app +// .instantiate_contract( +// code_id, +// sender.clone(), +// &InstantiateMsg { count: 1 }, +// &[], +// "cousin-counter", +// Some(sender.to_string()), +// ) +// .unwrap(); + +// app.execute_contract( +// sender.clone(), +// counter1.clone(), +// &ExecuteMsg::Increment {}, +// &[], +// ) +// .unwrap(); +// app.execute_contract( +// sender.clone(), +// counter1.clone(), +// &ExecuteMsg::Increment {}, +// &[], +// ) +// .unwrap(); +// app.execute_contract( +// sender.clone(), +// counter2.clone(), +// &ExecuteMsg::Increment {}, +// &[], +// ) +// .unwrap(); + +// app.execute_contract( +// sender.clone(), +// counter1.clone(), +// &ExecuteMsg::SetCousin { +// addr: counter2.to_string(), +// }, +// &[], +// ) +// .unwrap(); +// app.execute_contract( +// sender, +// counter2.clone(), +// &ExecuteMsg::SetCousin { +// addr: counter1.to_string(), +// }, +// &[], +// ) +// .unwrap(); + +// let cousin_count: GetCousinCountResponse = app +// .wrap() +// .query_wasm_smart(counter2.clone(), &QueryMsg::GetCousinCount {}) +// .unwrap(); +// assert_eq!(cousin_count.raw, cousin_count.smart); +// assert_eq!(cousin_count.raw, 3); + +// let cousin_count: GetCousinCountResponse = app +// .wrap() +// .query_wasm_smart(counter1.clone(), &QueryMsg::GetCousinCount {}) +// .unwrap(); +// assert_eq!(cousin_count.raw, cousin_count.smart); +// assert_eq!(cousin_count.raw, 2); + +// // Analyze the storage + +// let analysis = StorageAnalyzer::new(&app).unwrap(); + +// log::info!( +// "analysis, wasm1 {:?}", +// analysis.get_contract_storage(counter1.clone()) +// ); +// log::info!("analysis, wasm1 {:?}", analysis.readable_storage(counter1)); +// log::info!( +// "analysis, wasm2 {:?}", +// analysis.get_contract_storage(counter2.clone()) +// ); +// log::info!("analysis, wasm2 {:?}", analysis.readable_storage(counter2)); +// log::info!( +// "All contracts storage {:?}", +// analysis.all_readable_contract_storage() +// ); +// analysis.compare_all_readable_contract_storage(); +// Ok(()) +// } + +fn main() {} diff --git a/examples/test_app.rs b/examples/test_app.rs new file mode 100644 index 00000000..3d209cc0 --- /dev/null +++ b/examples/test_app.rs @@ -0,0 +1,78 @@ +use clone_cw_multi_test::wasm_emulation::query::ContainsRemote; +use clone_cw_multi_test::{ + wasm_emulation::channel::RemoteChannel, AppBuilder, BankKeeper, Executor, WasmKeeper, +}; +use cosmwasm_std::Addr; +use cw20::AllAccountsResponse; +use cw20::Cw20ExecuteMsg; + +use cw20::Cw20QueryMsg; +use cw_orch_daemon::networks::PHOENIX_1; + +use cosmwasm_std::Empty; +use tokio::runtime::Runtime; + +pub fn main() { + test().unwrap() +} + +pub fn test() -> anyhow::Result<()> { + env_logger::init(); + + let runtime = Runtime::new()?; + let chain = PHOENIX_1; + let remote_channel = RemoteChannel::new( + &runtime, + chain.grpc_urls, + chain.chain_id, + chain.network_info.pub_address_prefix, + )?; + let wasm = WasmKeeper::::new().with_remote(remote_channel.clone()); + + let bank = BankKeeper::new().with_remote(remote_channel.clone()); + + // First we instantiate a new app + let mut app = AppBuilder::default() + .with_wasm(wasm) + .with_bank(bank) + .with_remote(remote_channel) + .build(|_, _, _| {}); + + // Then we send a message to the blockchain through the app + let sender = "terra17c6ts8grcfrgquhj3haclg44le8s7qkx6l2yx33acguxhpf000xqhnl3je"; + let recipient = "terra1e9lqmv3egtgps9nux04vw8gd4pr3qp9h00y8um"; + let contract_addr = "terra1lxx40s29qvkrcj8fsa3yzyehy7w50umdvvnls2r830rys6lu2zns63eelv"; + let query = "terra1e8lqmv3egtgps9nux04vw8gd4pr3qp9h00y7um"; + + let response: AllAccountsResponse = app.wrap().query_wasm_smart( + contract_addr, + &Cw20QueryMsg::AllAccounts { + start_after: Some(query.to_string()), + limit: Some(30), + }, + )?; + log::info!("Before transfer : {:?}", response); + + // We execute a transfer + app.execute_contract( + Addr::unchecked(sender), + Addr::unchecked(contract_addr), + &Cw20ExecuteMsg::Transfer { + recipient: recipient.to_string(), + amount: 1_000_000u128.into(), + }, + &[], + )?; + + // We query to verify the state changed + let response: AllAccountsResponse = app.wrap().query_wasm_smart( + contract_addr, + &Cw20QueryMsg::AllAccounts { + start_after: Some(query.to_string()), + limit: Some(30), + }, + )?; + log::info!("After transfer : {:?}", response); + + Ok(()) +} diff --git a/src/addresses.rs b/src/addresses.rs index 43d49341..f4edc837 100644 --- a/src/addresses.rs +++ b/src/addresses.rs @@ -7,7 +7,7 @@ use cosmwasm_std::{instantiate2_address, Addr, Api, CanonicalAddr, Storage}; use sha2::digest::Update; use sha2::{Digest, Sha256}; -const DEFAULT_PREFIX: &str = "cosmwasm"; +const DEFAULT_PREFIX: &str = "xion"; /// Defines conversions to [Addr], this conversion is format agnostic /// and should be aligned with the format generated by [MockApi]. diff --git a/src/api.rs b/src/api.rs index c09d0079..bd3fa9ed 100644 --- a/src/api.rs +++ b/src/api.rs @@ -8,17 +8,17 @@ use sha2::{Digest, Sha256}; pub struct MockApiBech { api: MockApi, - prefix: &'static str, + prefix: String, _phantom_data: std::marker::PhantomData, } impl MockApiBech { /// Returns `Api` implementation that uses specified prefix /// to generate addresses in `Bech32` or `Bech32m` format. - pub fn new(prefix: &'static str) -> Self { + pub fn new(prefix: &str) -> Self { Self { api: MockApi::default(), - prefix, + prefix: prefix.to_string(), _phantom_data: std::marker::PhantomData, } } @@ -39,7 +39,7 @@ impl Api for MockApiBech { } fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult { - let hrp = Hrp::parse(self.prefix).map_err(|e| StdError::generic_err(e.to_string()))?; + let hrp = Hrp::parse(&self.prefix).map_err(|e| StdError::generic_err(e.to_string()))?; if let Ok(encoded) = encode::(hrp, canonical.as_slice()) { Ok(Addr::unchecked(encoded)) } else { @@ -99,7 +99,7 @@ impl MockApiBech { /// This function panics when generating a valid address in `Bech32` or `Bech32m` /// format is not possible, especially when the prefix is too long or empty. pub fn addr_make(&self, input: &str) -> Addr { - match Hrp::parse(self.prefix) { + match Hrp::parse(&self.prefix) { Ok(hrp) => Addr::unchecked(encode::(hrp, Sha256::digest(input).as_slice()).unwrap()), Err(reason) => panic!("Generating address failed with reason: {}", reason), } diff --git a/src/app.rs b/src/app.rs index e58e589a..731d7e98 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,5 +1,9 @@ +use crate::wasm_emulation::channel::RemoteChannel; +use crate::wasm_emulation::input::QuerierStorage; +use crate::wasm_emulation::query::ContainsRemote; +use cosmwasm_std::CustomMsg; + use crate::bank::{Bank, BankKeeper, BankSudo}; -use crate::contracts::Contract; use crate::error::{bail, AnyResult}; use crate::executor::{AppResponse, Executor}; use crate::featured::staking::{ @@ -13,10 +17,10 @@ use crate::prefixed_storage::{ }; use crate::transactions::transactional; use crate::wasm::{ContractData, Wasm, WasmKeeper, WasmSudo}; -use crate::{AppBuilder, GovFailingModule, IbcFailingModule, Stargate, StargateFailing}; +use crate::{AppBuilder, Contract, GovFailingModule, IbcFailingModule, Stargate, StargateFailing}; use cosmwasm_std::testing::{MockApi, MockStorage}; use cosmwasm_std::{ - from_json, to_json_binary, Addr, Api, Binary, BlockInfo, ContractResult, CosmosMsg, CustomMsg, + from_json, to_json_binary, Addr, Api, Binary, BlockInfo, ContractResult, CosmosMsg, CustomQuery, Empty, Querier, QuerierResult, QuerierWrapper, QueryRequest, Record, Storage, SystemError, SystemResult, }; @@ -66,6 +70,46 @@ pub struct App< pub(crate) api: Api, pub(crate) storage: Storage, pub(crate) block: BlockInfo, + pub(crate) remote: Option, +} + +impl< + Bank: ContainsRemote, + Api, + Storage, + Custom, + Wasm: ContainsRemote, + Staking, + Distr, + Ibc, + Gov, + Stargate, + > ContainsRemote for App +{ + fn with_remote(self, remote: RemoteChannel) -> Self { + let Self { + mut router, + api, + storage, + block, + .. + } = self; + router.bank.set_remote(remote.clone()); + router.wasm.set_remote(remote.clone()); + Self { + router, + api, + storage, + block, + remote: Some(remote), + } + } + + fn set_remote(&mut self, remote: RemoteChannel) { + self.router.bank.set_remote(remote.clone()); + self.router.wasm.set_remote(remote.clone()); + self.remote = Some(remote); + } } /// No-op application initialization function. @@ -77,12 +121,6 @@ pub fn no_init Self { - Self::new(no_init) - } -} - impl BasicApp { /// Creates new default `App` implementation working with Empty custom messages. pub fn new(init_fn: F) -> Self @@ -274,6 +312,22 @@ where self.router.wasm.store_code(creator, code) } + /// Registers contract code (like uploading wasm bytecode on a chain), + /// so it can later be used to instantiate a contract. + /// Only for wasm codes + pub fn store_wasm_code(&mut self, code: Vec) -> u64 { + self.init_modules(|router, _, _| { + router + .wasm + .store_wasm_code(Addr::unchecked("code-creator"), code) + }) + } + /// Registers contract code (like [store_code](Self::store_code)), + /// but takes the address of the code creator as an additional argument. + pub fn store_wasm_code_with_creator(&mut self, creator: Addr, code: Vec) -> u64 { + self.init_modules(|router, _, _| router.wasm.store_wasm_code(creator, code)) + } + /// Registers contract code (like [store_code_with_creator](Self::store_code_with_creator)), /// but takes the code identifier as an additional argument. pub fn store_code_with_id( @@ -434,6 +488,13 @@ where QuerierWrapper::new(self) } + pub fn get_querier_storage(&self) -> AnyResult { + // We get the wasm storage for all wasm contract to make sure we dispatch everything (with the mock Querier) + let wasm = self.router.wasm.query_all(&self.storage)?; + let bank = self.router.bank.query_all(&self.storage)?; + Ok(QuerierStorage { wasm, bank }) + } + /// Runs multiple CosmosMsg in one atomic operation. /// This will create a cache before the execution, so no state changes are persisted if any of them /// return an error. But all writes are persisted on success. @@ -451,6 +512,7 @@ where router, api, storage, + .. } = self; transactional(&mut *storage, |write_cache, _| { @@ -478,6 +540,7 @@ where router, api, storage, + .. } = self; transactional(&mut *storage, |write_cache, _| { @@ -497,6 +560,7 @@ where router, api, storage, + .. } = self; transactional(&mut *storage, |write_cache, _| { @@ -624,6 +688,8 @@ pub trait CosmosRouter { block: &BlockInfo, msg: SudoMsg, ) -> AnyResult; + + fn get_querier_storage(&self, storage: &dyn Storage) -> AnyResult; } impl CosmosRouter @@ -690,7 +756,7 @@ where ) -> AnyResult { let querier = self.querier(api, storage, block); match request { - QueryRequest::Wasm(req) => self.wasm.query(api, storage, &querier, block, req), + QueryRequest::Wasm(req) => self.wasm.query(api, storage, self, &querier, block, req), QueryRequest::Bank(req) => self.bank.query(api, storage, &querier, block, req), QueryRequest::Custom(req) => self.custom.query(api, storage, &querier, block, req), #[cfg(feature = "staking")] @@ -723,6 +789,13 @@ where _ => unimplemented!(), } } + + fn get_querier_storage(&self, storage: &dyn Storage) -> AnyResult { + // We get the wasm storage for all wasm contract to make sure we dispatch everything (with the mock Querier) + let wasm = self.wasm.query_all(storage)?; + let bank = self.bank.query_all(storage)?; + Ok(QuerierStorage { wasm, bank }) + } } pub struct MockRouter(PhantomData<(ExecC, QueryC)>); @@ -780,6 +853,10 @@ where ) -> AnyResult { panic!("Cannot sudo MockRouters"); } + + fn get_querier_storage(&self, _storage: &dyn Storage) -> AnyResult { + Ok(QuerierStorage::default()) + } } pub struct RouterQuerier<'a, ExecC, QueryC> { @@ -805,7 +882,7 @@ impl<'a, ExecC, QueryC> RouterQuerier<'a, ExecC, QueryC> { } } -impl<'a, ExecC, QueryC> Querier for RouterQuerier<'a, ExecC, QueryC> +impl Querier for RouterQuerier<'_, ExecC, QueryC> where ExecC: CustomMsg + DeserializeOwned + 'static, QueryC: CustomQuery + DeserializeOwned + 'static, diff --git a/src/app_builder.rs b/src/app_builder.rs index 67fc8d41..327709ad 100644 --- a/src/app_builder.rs +++ b/src/app_builder.rs @@ -1,6 +1,6 @@ //! AppBuilder helps you set up your test blockchain environment step by step [App]. - use crate::featured::staking::{Distribution, DistributionKeeper, StakeKeeper, Staking}; +use crate::wasm_emulation::channel::RemoteChannel; use crate::{ App, Bank, BankKeeper, FailingModule, Gov, GovFailingModule, Ibc, IbcFailingModule, Module, Router, Stargate, StargateFailing, Wasm, WasmKeeper, @@ -54,6 +54,7 @@ pub struct AppBuilder, stargate: Stargate, } @@ -104,6 +105,7 @@ impl ibc: IbcFailingModule::new(), gov: GovFailingModule::new(), stargate: StargateFailing, + remote: None, } } } @@ -140,6 +142,7 @@ where ibc: IbcFailingModule::new(), gov: GovFailingModule::new(), stargate: StargateFailing, + remote: None, } } } @@ -149,6 +152,8 @@ impl, + BankT: Bank, + CustomT::QueryT: CustomQuery, { /// Overwrites the default wasm executor. /// @@ -157,7 +162,7 @@ where /// done on final building. pub fn with_wasm>( self, - wasm: NewWasm, + mut wasm: NewWasm, ) -> AppBuilder { let AppBuilder { @@ -170,10 +175,13 @@ where distribution, ibc, gov, + remote, stargate, .. } = self; - + if let Some(remote) = remote.as_ref() { + wasm.set_remote(remote.clone()); + } AppBuilder { api, block, @@ -185,6 +193,7 @@ where distribution, ibc, gov, + remote, stargate, } } @@ -192,7 +201,7 @@ where /// Overwrites the default bank interface. pub fn with_bank( self, - bank: NewBank, + mut bank: NewBank, ) -> AppBuilder { let AppBuilder { @@ -205,10 +214,14 @@ where distribution, ibc, gov, + remote, stargate, .. } = self; + if let Some(remote) = remote.as_ref() { + bank.set_remote(remote.clone()); + } AppBuilder { api, block, @@ -221,6 +234,7 @@ where ibc, gov, stargate, + remote, } } @@ -241,6 +255,7 @@ where ibc, gov, stargate, + remote, .. } = self; @@ -256,6 +271,7 @@ where ibc, gov, stargate, + remote, } } @@ -276,6 +292,7 @@ where ibc, gov, stargate, + remote, .. } = self; @@ -291,6 +308,7 @@ where ibc, gov, stargate, + remote, } } @@ -315,6 +333,7 @@ where ibc, gov, stargate, + remote, .. } = self; @@ -330,6 +349,7 @@ where ibc, gov, stargate, + remote, } } @@ -350,6 +370,7 @@ where ibc, gov, stargate, + remote, .. } = self; @@ -365,6 +386,7 @@ where ibc, gov, stargate, + remote, } } @@ -395,6 +417,7 @@ where ibc, gov, stargate, + remote, .. } = self; @@ -410,6 +433,7 @@ where ibc, gov, stargate, + remote, } } @@ -435,6 +459,7 @@ where bank, distribution, gov, + remote, stargate, .. } = self; @@ -451,6 +476,7 @@ where distribution, ibc, gov, + remote, } } @@ -470,6 +496,7 @@ where bank, distribution, ibc, + remote, stargate, .. } = self; @@ -486,9 +513,22 @@ where ibc, gov, stargate, + remote, } } + /// Sets the chain of the app + pub fn with_remote( + mut self, + remote: RemoteChannel, + ) -> AppBuilder + { + self.remote = Some(remote.clone()); + self.wasm.set_remote(remote.clone()); + self.bank.set_remote(remote.clone()); + self + } + /// Overwrites the default stargate interface. pub fn with_stargate( self, @@ -506,6 +546,7 @@ where distribution, ibc, gov, + remote, .. } = self; @@ -521,6 +562,7 @@ where ibc, gov, stargate, + remote, } } @@ -569,6 +611,7 @@ where api: self.api, block: self.block, storage: self.storage, + remote: self.remote, }; // execute initialization provided by the caller app.init_modules(init_fn); diff --git a/src/bank.rs b/src/bank.rs index 756254ef..14f8781f 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -3,6 +3,9 @@ use crate::error::{bail, AnyResult}; use crate::executor::AppResponse; use crate::module::Module; use crate::prefixed_storage::{prefixed, prefixed_read}; +use crate::queries::bank::BankRemoteQuerier; +use crate::wasm_emulation::channel::RemoteChannel; +use crate::wasm_emulation::query::{AllBankQuerier, ContainsRemote}; use cosmwasm_std::{ coin, to_json_binary, Addr, AllBalanceResponse, Api, BalanceResponse, BankMsg, BankQuery, Binary, BlockInfo, Coin, DenomMetadata, Event, Querier, Storage, @@ -42,7 +45,10 @@ pub enum BankSudo { /// In the test environment, it is essential for testing financial transactions, /// like transfers and balance checks, within your smart contracts. /// This trait implements all of these functionalities. -pub trait Bank: Module {} +pub trait Bank: + Module + AllBankQuerier + ContainsRemote +{ +} /// A structure representing a default bank keeper. /// @@ -50,7 +56,9 @@ pub trait Bank: Module {} /// and account balances. This is particularly important for contracts that deal with financial /// operations in the Cosmos ecosystem. #[derive(Default)] -pub struct BankKeeper {} +pub struct BankKeeper { + remote: Option, +} impl BankKeeper { /// Creates a new instance of a bank keeper with default settings. @@ -95,10 +103,13 @@ impl BankKeeper { .map_err(Into::into) } - /// Returns balance for specified address. - fn get_balance(&self, bank_storage: &dyn Storage, addr: &Addr) -> AnyResult> { - let val = BALANCES.may_load(bank_storage, addr)?; - Ok(val.unwrap_or_default().into_vec()) + fn get_balance(&self, bank_storage: &dyn Storage, account: &Addr) -> AnyResult> { + // If there is no balance present, we query it on the distant chain + if let Some(val) = BALANCES.may_load(bank_storage, account)? { + Ok(val.into_vec()) + } else { + BankRemoteQuerier::get_balance(self.remote.clone().unwrap(), account) + } } #[cfg(feature = "cosmwasm_1_1")] @@ -166,6 +177,17 @@ impl BankKeeper { } } +impl ContainsRemote for BankKeeper { + fn with_remote(mut self, remote: RemoteChannel) -> Self { + self.set_remote(remote); + self + } + + fn set_remote(&mut self, remote: RemoteChannel) { + self.remote = Some(remote) + } +} + fn coins_to_string(coins: &[Coin]) -> String { coins .iter() @@ -284,11 +306,34 @@ impl Module for BankKeeper { } } +pub mod storage_querier { + use anyhow::Result as AnyResult; + use cosmwasm_std::{Order, Storage}; + + use crate::{ + prefixed_storage::prefixed_read, + wasm_emulation::{input::BankStorage, query::AllBankQuerier}, + }; + + use super::{BankKeeper, BALANCES, NAMESPACE_BANK}; + + impl AllBankQuerier for BankKeeper { + fn query_all(&self, storage: &dyn Storage) -> AnyResult { + let bank_storage = prefixed_read(storage, NAMESPACE_BANK); + let balances: Result, _> = BALANCES + .range(&bank_storage, None, None, Order::Ascending) + .collect(); + Ok(BankStorage { storage: balances? }) + } + } +} + #[cfg(test)] mod test { use super::*; use crate::app::MockRouter; + use crate::tests::remote_channel; use cosmwasm_std::testing::{mock_env, MockApi, MockQuerier, MockStorage}; use cosmwasm_std::{coins, from_json, Empty, StdError}; @@ -324,7 +369,7 @@ mod test { let norm = vec![coin(20, "btc"), coin(100, "eth")]; // set money - let bank = BankKeeper::new(); + let bank = BankKeeper::new().with_remote(remote_channel()); bank.init_balance(&mut store, &owner, init_funds).unwrap(); let bank_storage = prefixed_read(&store, NAMESPACE_BANK); @@ -418,7 +463,7 @@ mod test { let rcpt_funds = vec![coin(5, "btc")]; // set money - let bank = BankKeeper::new(); + let bank = BankKeeper::new().with_remote(remote_channel()); bank.init_balance(&mut store, &owner, init_funds).unwrap(); bank.init_balance(&mut store, &rcpt, rcpt_funds).unwrap(); @@ -470,7 +515,7 @@ mod test { let init_funds = vec![coin(20, "btc"), coin(100, "eth")]; // set money - let bank = BankKeeper::new(); + let bank = BankKeeper::new().with_remote(remote_channel()); bank.init_balance(&mut store, &owner, init_funds).unwrap(); // burn both tokens @@ -510,7 +555,7 @@ mod test { let mut store = MockStorage::new(); let block = mock_env().block; let querier: MockQuerier = MockQuerier::new(&[]); - let bank = BankKeeper::new(); + let bank = BankKeeper::new().with_remote(remote_channel()); // set metadata for Ether let denom_eth_name = "eth".to_string(); bank.set_denom_metadata( @@ -538,7 +583,7 @@ mod test { let mut store = MockStorage::new(); let block = mock_env().block; let querier: MockQuerier = MockQuerier::new(&[]); - let bank = BankKeeper::new(); + let bank = BankKeeper::new().with_remote(remote_channel()); // set metadata for Bitcoin let denom_btc_name = "btc".to_string(); bank.set_denom_metadata( @@ -581,7 +626,7 @@ mod test { let init_funds = vec![coin(5000, "atom"), coin(100, "eth")]; // set money - let bank = BankKeeper::new(); + let bank = BankKeeper::new().with_remote(remote_channel()); bank.init_balance(&mut store, &owner, init_funds).unwrap(); // can send normal amounts diff --git a/src/contracts.rs b/src/contracts.rs index 4ac1605b..80747379 100644 --- a/src/contracts.rs +++ b/src/contracts.rs @@ -1,6 +1,11 @@ //! # Implementation of the contract trait and contract wrapper -use crate::error::{anyhow, bail, AnyError, AnyResult}; +use crate::error::{anyhow, bail, AnyResult}; +use crate::wasm_emulation::query::mock_querier::ForkState; +use crate::wasm_emulation::query::MockQuerier; +use crate::wasm_emulation::storage::dual_std_storage::DualStorage; +use crate::wasm_emulation::storage::storage_wrappers::{ReadonlyStorageWrapper, StorageWrapper}; +use anyhow::Error as AnyError; use cosmwasm_std::{ from_json, Binary, Checksum, CosmosMsg, CustomMsg, CustomQuery, Deps, DepsMut, Empty, Env, MessageInfo, QuerierWrapper, Reply, Response, SubMsg, @@ -10,29 +15,66 @@ use std::fmt::{Debug, Display}; use std::ops::Deref; /// This trait serves as a primary interface for interacting with contracts. -#[rustfmt::skip] pub trait Contract where C: CustomMsg, - Q: CustomQuery, + Q: CustomQuery + DeserializeOwned, { /// Evaluates contract's `execute` entry-point. - fn execute(&self, deps: DepsMut, env: Env, info: MessageInfo, msg: Vec) -> AnyResult>; + fn execute( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult>; /// Evaluates contract's `instantiate` entry-point. - fn instantiate(&self, deps: DepsMut, env: Env, info: MessageInfo, msg: Vec) -> AnyResult>; + fn instantiate( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult>; /// Evaluates contract's `query` entry-point. - fn query(&self, deps: Deps, env: Env, msg: Vec) -> AnyResult; + fn query( + &self, + deps: Deps, + env: Env, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult; /// Evaluates contract's `sudo` entry-point. - fn sudo(&self, deps: DepsMut, env: Env, msg: Vec) -> AnyResult>; + fn sudo( + &self, + deps: DepsMut, + env: Env, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult>; /// Evaluates contract's `reply` entry-point. - fn reply(&self, deps: DepsMut, env: Env, msg: Reply) -> AnyResult>; + fn reply( + &self, + deps: DepsMut, + env: Env, + msg: Reply, + fork_state: ForkState, + ) -> AnyResult>; /// Evaluates contract's `migrate` entry-point. - fn migrate(&self, deps: DepsMut, env: Env, msg: Vec) -> AnyResult>; + fn migrate( + &self, + deps: DepsMut, + env: Env, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult>; /// Returns the provided checksum of the contract's Wasm blob. fn checksum(&self) -> Option { @@ -446,7 +488,7 @@ where { SubMsg { id: msg.id, - payload: Binary::default(), + payload: msg.payload, msg: match msg.msg { CosmosMsg::Wasm(wasm) => CosmosMsg::Wasm(wasm), CosmosMsg::Bank(bank) => CosmosMsg::Bank(bank), @@ -459,7 +501,7 @@ where CosmosMsg::Ibc(ibc) => CosmosMsg::Ibc(ibc), #[cfg(feature = "cosmwasm_2_0")] CosmosMsg::Any(any) => CosmosMsg::Any(any), - _ => panic!("unknown message variant {:?}", msg), + other => panic!("unknown message variant {:?}", other), }, gas_limit: msg.gas_limit, reply_on: msg.reply_on, @@ -480,7 +522,7 @@ where E4: Display + Debug + Send + Sync + 'static, // Type of error returned from `sudo` entry-point. E5: Display + Debug + Send + Sync + 'static, // Type of error returned from `reply` entry-point. E6: Display + Debug + Send + Sync + 'static, // Type of error returned from `migrate` entry-point. - C: CustomMsg, // Type of custom message returned from all entry-points except `query`. + C: CustomMsg + DeserializeOwned, // Type of custom message returned from all entry-points except `query`. Q: CustomQuery + DeserializeOwned, // Type of custom query in querier passed as deps/deps_mut to all entry-points. { /// Calls [execute] on wrapped [Contract] trait implementor. @@ -492,7 +534,20 @@ where env: Env, info: MessageInfo, msg: Vec, + fork_state: ForkState, ) -> AnyResult> { + let querier = MockQuerier::new(fork_state.clone()); + let mut storage = DualStorage::new( + fork_state.remote, + env.contract.address.to_string(), + Box::new(StorageWrapper::new(deps.storage)), + )?; + let deps = DepsMut { + storage: &mut storage, + api: deps.api, + querier: QuerierWrapper::new(&querier), + }; + let msg: T1 = from_json(msg)?; (self.execute_fn)(deps, env, info, msg).map_err(|err: E1| anyhow!(err)) } @@ -506,7 +561,19 @@ where env: Env, info: MessageInfo, msg: Vec, + fork_state: ForkState, ) -> AnyResult> { + let querier = MockQuerier::new(fork_state.clone()); + let mut storage = DualStorage::new( + fork_state.remote, + env.contract.address.to_string(), + Box::new(StorageWrapper::new(deps.storage)), + )?; + let deps = DepsMut { + storage: &mut storage, + api: deps.api, + querier: QuerierWrapper::new(&querier), + }; let msg: T2 = from_json(msg)?; (self.instantiate_fn)(deps, env, info, msg).map_err(|err: E2| anyhow!(err)) } @@ -514,7 +581,24 @@ where /// Calls [query] on wrapped [Contract] trait implementor. /// /// [query]: Contract::query - fn query(&self, deps: Deps, env: Env, msg: Vec) -> AnyResult { + fn query( + &self, + deps: Deps, + env: Env, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult { + let querier = MockQuerier::new(fork_state.clone()); + let mut storage = DualStorage::new( + fork_state.remote, + env.contract.address.to_string(), + Box::new(ReadonlyStorageWrapper::new(deps.storage)), + )?; + let deps = Deps { + storage: &mut storage, + api: deps.api, + querier: QuerierWrapper::new(&querier), + }; let msg: T3 = from_json(msg)?; (self.query_fn)(deps, env, msg).map_err(|err: E3| anyhow!(err)) } @@ -523,8 +607,25 @@ where /// Returns an error when the contract does not implement [sudo]. /// /// [sudo]: Contract::sudo - fn sudo(&self, deps: DepsMut, env: Env, msg: Vec) -> AnyResult> { - let msg: T4 = from_json(msg)?; + fn sudo( + &self, + deps: DepsMut, + env: Env, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult> { + let querier = MockQuerier::new(fork_state.clone()); + let mut storage = DualStorage::new( + fork_state.remote, + env.contract.address.to_string(), + Box::new(StorageWrapper::new(deps.storage)), + )?; + let deps = DepsMut { + storage: &mut storage, + api: deps.api, + querier: QuerierWrapper::new(&querier), + }; + let msg = from_json(msg)?; match &self.sudo_fn { Some(sudo) => sudo(deps, env, msg).map_err(|err: E4| anyhow!(err)), None => bail!("sudo is not implemented for contract"), @@ -535,8 +636,25 @@ where /// Returns an error when the contract does not implement [reply]. /// /// [reply]: Contract::reply - fn reply(&self, deps: DepsMut, env: Env, reply_data: Reply) -> AnyResult> { + fn reply( + &self, + deps: DepsMut, + env: Env, + reply_data: Reply, + fork_state: ForkState, + ) -> AnyResult> { let msg: Reply = reply_data; + let querier = MockQuerier::new(fork_state.clone()); + let mut storage = DualStorage::new( + fork_state.remote, + env.contract.address.to_string(), + Box::new(StorageWrapper::new(deps.storage)), + )?; + let deps = DepsMut { + storage: &mut storage, + api: deps.api, + querier: QuerierWrapper::new(&querier), + }; match &self.reply_fn { Some(reply) => reply(deps, env, msg).map_err(|err: E5| anyhow!(err)), None => bail!("reply is not implemented for contract"), @@ -547,8 +665,25 @@ where /// Returns an error when the contract does not implement [migrate]. /// /// [migrate]: Contract::migrate - fn migrate(&self, deps: DepsMut, env: Env, msg: Vec) -> AnyResult> { - let msg: T6 = from_json(msg)?; + fn migrate( + &self, + deps: DepsMut, + env: Env, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult> { + let querier = MockQuerier::new(fork_state.clone()); + let mut storage = DualStorage::new( + fork_state.remote, + env.contract.address.to_string(), + Box::new(StorageWrapper::new(deps.storage)), + )?; + let deps = DepsMut { + storage: &mut storage, + api: deps.api, + querier: QuerierWrapper::new(&querier), + }; + let msg = from_json(msg)?; match &self.migrate_fn { Some(migrate) => migrate(deps, env, msg).map_err(|err: E6| anyhow!(err)), None => bail!("migrate is not implemented for contract"), diff --git a/src/lib.rs b/src/lib.rs index 76f1413b..f9bfcbf3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,7 +120,7 @@ //! //! (tbd) -#![deny(missing_docs)] +// #![deny(missing_docs)] #![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::missing_crate_level_docs)] @@ -138,15 +138,25 @@ mod featured; mod gov; mod ibc; mod module; -mod prefixed_storage; +pub(crate) mod prefixed_storage; #[cfg(feature = "staking")] mod staking; mod stargate; mod test_helpers; -mod tests; +pub(crate) mod tests; mod transactions; mod wasm; +/// TokenFactory integration for cw-multi-test +#[cfg(feature = "tokenfactory")] +pub mod tokenfactory; +#[cfg(feature = "tokenfactory")] +pub(crate) use tokenfactory::shim; +// --- Clone Testing Modules --- // +pub mod queries; +pub mod wasm_emulation; +// --- End --- // + pub use crate::addresses::{ AddressGenerator, IntoAddr, IntoBech32, IntoBech32m, SimpleAddressGenerator, }; @@ -167,4 +177,5 @@ pub use crate::staking::{ Distribution, DistributionKeeper, StakeKeeper, Staking, StakingInfo, StakingSudo, }; pub use crate::stargate::{Stargate, StargateAccepting, StargateFailing}; +pub use crate::wasm::LOCAL_CODE_OFFSET; pub use crate::wasm::{ContractData, Wasm, WasmKeeper, WasmSudo}; diff --git a/src/prefixed_storage/length_prefixed.rs b/src/prefixed_storage/length_prefixed.rs index c00bb2a5..931b8acd 100644 --- a/src/prefixed_storage/length_prefixed.rs +++ b/src/prefixed_storage/length_prefixed.rs @@ -6,6 +6,7 @@ //! Everything in this file is only responsible for building such keys //! and is not specific to any kind of storage. +pub const CONTRACT_STORAGE_PREFIX: &str = "contract_data/"; /// Calculates the raw key prefix for a given namespace. /// /// See @@ -44,6 +45,22 @@ fn encode_length(namespace: &[u8]) -> [u8; 2] { [length_bytes[2], length_bytes[3]] } +/// Decodes the length of a given namespace from a 2 byte big endian encoded integer +pub fn decode_length(bytes: [u8; 2]) -> u32 { + u32::from_be_bytes([0, 0, bytes[0], bytes[1]]) +} + +pub fn contract_namespace(contract: &cosmwasm_std::Addr) -> Vec { + let mut name = CONTRACT_STORAGE_PREFIX.as_bytes().to_vec(); + name.extend_from_slice(contract.as_bytes()); + name +} + +pub fn get_full_contract_storage_namespace(contract_addr: &cosmwasm_std::Addr) -> Vec { + let namespace = contract_namespace(contract_addr); + to_length_prefixed_nested(&[crate::wasm::NAMESPACE_WASM, &namespace]) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/prefixed_storage/mod.rs b/src/prefixed_storage/mod.rs index 77416fc6..ce96a655 100644 --- a/src/prefixed_storage/mod.rs +++ b/src/prefixed_storage/mod.rs @@ -1,11 +1,15 @@ use cosmwasm_std::Storage; use cosmwasm_std::{Order, Record}; -use length_prefixed::{to_length_prefixed, to_length_prefixed_nested}; +use length_prefixed::to_length_prefixed_nested; use namespace_helpers::{get_with_prefix, range_with_prefix, remove_with_prefix, set_with_prefix}; mod length_prefixed; mod namespace_helpers; +pub use length_prefixed::{ + decode_length, get_full_contract_storage_namespace, to_length_prefixed, CONTRACT_STORAGE_PREFIX, +}; + /// An alias of [PrefixedStorage::new] for less verbose usage. pub fn prefixed<'a>(storage: &'a mut dyn Storage, namespace: &[u8]) -> PrefixedStorage<'a> { PrefixedStorage::new(storage, namespace) @@ -61,7 +65,7 @@ impl<'a> PrefixedStorage<'a> { } } -impl<'a> Storage for PrefixedStorage<'a> { +impl Storage for PrefixedStorage<'_> { fn get(&self, key: &[u8]) -> Option> { get_with_prefix(self.storage, &self.prefix, key) } @@ -112,7 +116,7 @@ impl<'a> ReadonlyPrefixedStorage<'a> { } } -impl<'a> Storage for ReadonlyPrefixedStorage<'a> { +impl Storage for ReadonlyPrefixedStorage<'_> { fn get(&self, key: &[u8]) -> Option> { get_with_prefix(self.storage, &self.prefix, key) } diff --git a/src/queries/bank.rs b/src/queries/bank.rs new file mode 100644 index 00000000..012ebc65 --- /dev/null +++ b/src/queries/bank.rs @@ -0,0 +1,17 @@ +use anyhow::Result as AnyResult; +use cosmwasm_std::{Addr, Coin}; + +use crate::wasm_emulation::channel::RemoteChannel; + +pub struct BankRemoteQuerier; + +impl BankRemoteQuerier { + pub fn get_balance(remote: RemoteChannel, account: &Addr) -> AnyResult> { + let querier = cw_orch_daemon::queriers::Bank { + channel: remote.channel, + rt_handle: Some(remote.rt.clone()), + }; + let distant_amounts: Vec = remote.rt.block_on(querier._balance(account, None))?; + Ok(distant_amounts) + } +} diff --git a/src/queries/mod.rs b/src/queries/mod.rs new file mode 100644 index 00000000..31a22af0 --- /dev/null +++ b/src/queries/mod.rs @@ -0,0 +1,2 @@ +pub mod bank; +pub mod wasm; diff --git a/src/queries/wasm.rs b/src/queries/wasm.rs new file mode 100644 index 00000000..d2b53bed --- /dev/null +++ b/src/queries/wasm.rs @@ -0,0 +1,76 @@ +use std::collections::HashMap; + +use anyhow::Result as AnyResult; +use cosmwasm_std::{Addr, Binary, CodeInfoResponse, CustomQuery, Order, Storage}; +use cw_orch_daemon::queriers::CosmWasm; + +use crate::{ + prefixed_storage::prefixed_read, + wasm::{ContractData, CONTRACTS, NAMESPACE_WASM}, + wasm_emulation::{channel::RemoteChannel, input::WasmStorage, query::AllWasmQuerier}, + WasmKeeper, +}; + +pub struct WasmRemoteQuerier; + +impl WasmRemoteQuerier { + pub fn code_info(remote: RemoteChannel, code_id: u64) -> AnyResult { + let wasm_querier = CosmWasm::new_sync(remote.channel, &remote.rt); + + let code_info = remote.rt.block_on(wasm_querier._code(code_id))?; + Ok(code_info) + } + + pub fn load_distant_contract(remote: RemoteChannel, address: &Addr) -> AnyResult { + let wasm_querier = CosmWasm::new_sync(remote.channel, &remote.rt); + + let code_info = remote.rt.block_on(wasm_querier._contract_info(address))?; + + Ok(ContractData { + admin: code_info.admin.map(Addr::unchecked), + code_id: code_info.code_id, + creator: Addr::unchecked(code_info.creator), + label: "Distant contract with no label".to_string(), + created: 0, + }) + } + + pub fn raw_query( + remote: RemoteChannel, + contract_addr: &Addr, + key: Binary, + ) -> AnyResult> { + let wasm_querier = CosmWasm::new_sync(remote.channel, &remote.rt); + let query_result = remote + .rt + .block_on(wasm_querier._contract_raw_state(contract_addr, key.to_vec())) + .map(|query_result| query_result.data); + Ok(query_result?) + } +} + +impl AllWasmQuerier for WasmKeeper { + fn query_all(&self, storage: &dyn Storage) -> AnyResult { + let all_local_state: Vec<_> = storage.range(None, None, Order::Ascending).collect(); + + let contracts = CONTRACTS + .range( + &prefixed_read(storage, NAMESPACE_WASM), + None, + None, + Order::Ascending, + ) + .map(|res| match res { + Ok((key, value)) => Ok((key.to_string(), value)), + Err(e) => Err(e), + }) + .collect::, _>>()?; + + Ok(WasmStorage { + contracts, + storage: all_local_state, + codes: self.code_base.borrow().clone(), + code_data: self.code_data.clone(), + }) + } +} diff --git a/src/test_helpers/caller.rs b/src/test_helpers/caller.rs index 1d50a62a..691c3752 100644 --- a/src/test_helpers/caller.rs +++ b/src/test_helpers/caller.rs @@ -2,6 +2,7 @@ use crate::{Contract, ContractWrapper}; use cosmwasm_std::{ Binary, CustomMsg, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdError, SubMsg, WasmMsg, }; +use serde::de::DeserializeOwned; fn instantiate( _deps: DepsMut, @@ -31,7 +32,7 @@ fn query(_deps: Deps, _env: Env, _msg: Empty) -> Result { pub fn contract() -> Box> where - C: CustomMsg + 'static, + C: CustomMsg + DeserializeOwned + 'static, { let contract = ContractWrapper::new_with_empty(execute, instantiate, query); Box::new(contract) diff --git a/src/test_helpers/error.rs b/src/test_helpers/error.rs index 1654440a..7d0b7eb1 100644 --- a/src/test_helpers/error.rs +++ b/src/test_helpers/error.rs @@ -1,5 +1,6 @@ use crate::{Contract, ContractWrapper}; use cosmwasm_std::{Binary, CustomMsg, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdError}; +use serde::de::DeserializeOwned; fn instantiate_err( _deps: DepsMut, @@ -34,7 +35,7 @@ fn query(_deps: Deps, _env: Env, _msg: Empty) -> Result { pub fn contract(instantiable: bool) -> Box> where - C: CustomMsg + 'static, + C: CustomMsg + DeserializeOwned + 'static, { let contract = if instantiable { ContractWrapper::new_with_empty(execute, instantiate_ok, query) diff --git a/src/test_helpers/hackatom.rs b/src/test_helpers/hackatom.rs index 6c0d7dd6..ebc9a7c9 100644 --- a/src/test_helpers/hackatom.rs +++ b/src/test_helpers/hackatom.rs @@ -7,6 +7,7 @@ use cosmwasm_std::{ StdError, }; use cw_storage_plus::Item; +use serde::de::DeserializeOwned; #[cw_serde] pub struct InstantiateMsg { @@ -75,7 +76,7 @@ pub fn contract() -> Box> { #[allow(dead_code)] pub fn custom_contract() -> Box> where - C: CustomMsg + 'static, + C: CustomMsg + DeserializeOwned + 'static, { let contract = ContractWrapper::new_with_empty(execute, instantiate, query).with_migrate_empty(migrate); diff --git a/src/test_helpers/payout.rs b/src/test_helpers/payout.rs index 5d0eddb4..a2f58cd3 100644 --- a/src/test_helpers/payout.rs +++ b/src/test_helpers/payout.rs @@ -6,6 +6,7 @@ use cosmwasm_std::{ Response, StdError, }; use cw_storage_plus::Item; +use serde::de::DeserializeOwned; #[cw_serde] pub struct InstantiateMessage { @@ -74,7 +75,7 @@ fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result { pub fn contract() -> Box> where - C: CustomMsg + 'static, + C: CustomMsg + DeserializeOwned + 'static, { let contract = ContractWrapper::new_with_empty(execute, instantiate, query).with_sudo_empty(sudo); diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 2f5d2092..024a63df 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,5 +1,34 @@ #![cfg(test)] +use cosmwasm_std::Empty; +use cw_orch::prelude::ChainInfo; +use cw_orch_daemon::{networks::XION_TESTNET_1, RUNTIME}; + +use crate::{ + no_init, + wasm_emulation::{channel::RemoteChannel, query::ContainsRemote}, + App, AppBuilder, WasmKeeper, +}; +pub const CHAIN: ChainInfo = XION_TESTNET_1; +pub fn remote_channel() -> RemoteChannel { + RemoteChannel::new( + &RUNTIME, + CHAIN.grpc_urls, + CHAIN.chain_id, + CHAIN.network_info.pub_address_prefix, + ) + .unwrap() +} + +pub fn default_app() -> App { + let remote_channel = remote_channel(); + let wasm = WasmKeeper::::new().with_remote(remote_channel.clone()); + AppBuilder::default() + .with_wasm(wasm) + .with_remote(remote_channel.clone()) + .build(no_init) +} + mod test_app; mod test_custom_handler; mod test_error; diff --git a/src/tests/test_app.rs b/src/tests/test_app.rs index a7fe0457..011fbea6 100644 --- a/src/tests/test_app.rs +++ b/src/tests/test_app.rs @@ -3,8 +3,11 @@ use crate::error::{bail, AnyResult}; use crate::featured::staking::{Distribution, Staking}; use crate::test_helpers::echo::EXECUTE_REPLY_BASE_ID; use crate::test_helpers::{caller, echo, error, hackatom, payout, reflect, CustomHelperMsg}; +use crate::tests::default_app; +use crate::tests::remote_channel; use crate::transactions::{transactional, StorageTransaction}; use crate::wasm::ContractData; +use crate::wasm_emulation::query::ContainsRemote; use crate::{ custom_app, next_block, no_init, App, AppResponse, Bank, CosmosRouter, Executor, Module, Router, Wasm, WasmSudo, @@ -99,7 +102,7 @@ fn addr_make(addr: &str) -> Addr { #[test] fn update_block() { - let mut app = App::default(); + let mut app = default_app(); let BlockInfo { time, height, .. } = app.block_info(); app.update_block(next_block); assert_eq!(time.plus_seconds(5), app.block_info().time); @@ -119,7 +122,8 @@ fn multi_level_bank_cache() { .bank .init_balance(storage, &owner_addr, init_funds) .unwrap(); - }); + }) + .with_remote(remote_channel()); // cache 1 - send some tokens let mut cache = StorageTransaction::new(app.storage()); @@ -170,10 +174,9 @@ fn multi_level_bank_cache() { } #[test] -#[cfg(feature = "cosmwasm_1_2")] fn duplicate_contract_code() { // set up the multi-test application - let mut app = App::default(); + let mut app = default_app(); // store the original contract code let code_id = app.store_code(payout::contract()); @@ -209,7 +212,8 @@ fn send_tokens() { .bank .init_balance(storage, &recipient_addr, rcpt_funds) .unwrap(); - }); + }) + .with_remote(remote_channel()); // send both tokens let to_send = vec![coin(30, "eth"), coin(5, "btc")]; @@ -251,7 +255,8 @@ fn simple_contract() { .bank .init_balance(storage, &owner_addr, init_funds) .unwrap(); - }); + }) + .with_remote(remote_channel()); // set up contract let code_id = app.store_code(payout::contract()); @@ -340,7 +345,10 @@ fn reflect_success() { .bank .init_balance(storage, &owner_addr, init_funds) .unwrap(); - }); + router.bank.set_remote(remote_channel()); + router.wasm.set_remote(remote_channel()); + }) + .with_remote(remote_channel()); // set up payout contract let payout_id = app.store_code(payout::contract()); @@ -447,7 +455,10 @@ fn reflect_error() { .bank .init_balance(storage, &owner, init_funds) .unwrap(); - }); + router.bank.set_remote(remote_channel()); + router.wasm.set_remote(remote_channel()); + }) + .with_remote(remote_channel()); // set up reflect contract let reflect_id = app.store_code(reflect::contract()); @@ -544,7 +555,8 @@ fn sudo_works() { .bank .init_balance(storage, &owner_addr, init_funds) .unwrap(); - }); + }) + .with_remote(remote_channel()); let payout_id = app.store_code(payout::contract()); @@ -608,7 +620,10 @@ fn reflect_sub_message_reply_works() { .bank .init_balance(storage, &owner, init_funds) .unwrap(); - }); + router.bank.set_remote(remote_channel()); + router.wasm.set_remote(remote_channel()); + }) + .with_remote(remote_channel()); // set up reflect contract let reflect_id = app.store_code(reflect::contract()); @@ -694,7 +709,7 @@ fn send_update_admin_works() { // update admin succeeds if admin // update admin fails if not (new) admin // check admin set properly - let mut app = App::default(); + let mut app = default_app(); let owner = addr_make("owner"); let owner2 = addr_make("owner2"); @@ -771,7 +786,8 @@ fn sent_wasm_migration_works() { .bank .init_balance(storage, &owner_addr, init_funds) .unwrap(); - }); + }) + .with_remote(remote_channel()); // create a hackatom contract with some funds let code_id = app.store_code(hackatom::contract()); @@ -846,7 +862,8 @@ fn sent_funds_properly_visible_on_execution() { .bank .init_balance(storage, &owner_addr, init_funds) .unwrap(); - }); + }) + .with_remote(remote_channel()); let code_id = app.store_code(hackatom::contract()); @@ -989,6 +1006,7 @@ mod custom_handler { let mut app = BasicAppBuilder::::new_custom() .with_custom(CustomHandler {}) + .with_remote(remote_channel()) .build(|router, _, storage| { router .custom @@ -1064,7 +1082,7 @@ mod reply_data_overwrite { #[test] fn no_submsg() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1091,7 +1109,7 @@ mod reply_data_overwrite { #[test] fn single_submsg() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1124,7 +1142,7 @@ mod reply_data_overwrite { #[test] fn single_submsg_no_reply() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1152,7 +1170,7 @@ mod reply_data_overwrite { #[test] fn single_no_submsg_data() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1180,7 +1198,7 @@ mod reply_data_overwrite { #[test] fn single_no_top_level_data() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1222,7 +1240,10 @@ mod reply_data_overwrite { .bank .init_balance(storage, &owner, init_funds) .unwrap(); - }); + router.bank.set_remote(remote_channel()); + router.wasm.set_remote(remote_channel()); + }) + .with_remote(remote_channel()); // set up reflect contract let reflect_id = app.store_code(reflect::contract()); @@ -1269,7 +1290,7 @@ mod reply_data_overwrite { #[test] fn multiple_submsg() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1312,7 +1333,7 @@ mod reply_data_overwrite { #[test] fn multiple_submsg_no_reply() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1345,7 +1366,7 @@ mod reply_data_overwrite { #[test] fn multiple_submsg_mixed() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1383,7 +1404,7 @@ mod reply_data_overwrite { #[test] fn nested_submsg() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1436,7 +1457,7 @@ mod response_validation { #[test] fn empty_attribute_key() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1467,7 +1488,7 @@ mod response_validation { #[test] fn empty_attribute_value_should_work() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); let code_id = app.store_code(echo::contract()); @@ -1495,7 +1516,7 @@ mod response_validation { #[test] fn empty_event_attribute_key() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1525,7 +1546,7 @@ mod response_validation { #[test] fn empty_event_attribute_value_should_work() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); let code_id = app.store_code(echo::contract()); @@ -1552,7 +1573,7 @@ mod response_validation { #[test] fn too_short_event_type() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1582,12 +1603,11 @@ mod response_validation { mod contract_instantiation { #[test] - #[cfg(feature = "cosmwasm_1_2")] fn instantiate2_works() { use super::*; // prepare application and actors - let mut app = App::default(); + let mut app = default_app(); let sender = app.api().addr_make("sender"); let creator = app.api().addr_make("creator"); @@ -1623,10 +1643,9 @@ mod contract_instantiation { mod wasm_queries { #[test] - #[cfg(feature = "cosmwasm_1_2")] fn query_existing_code_info() { use super::*; - let mut app = App::default(); + let mut app = default_app(); let creator = app.api().addr_make("creator"); let code_id = app.store_code_with_creator(creator.clone(), echo::contract()); let code_info_response = app.wrap().query_wasm_code_info(code_id).unwrap(); @@ -1636,10 +1655,9 @@ mod wasm_queries { } #[test] - #[cfg(feature = "cosmwasm_1_2")] fn query_non_existing_code_info() { use super::*; - let app = App::default(); + let app = default_app(); assert_eq!( "Generic error: Querier contract error: code id: invalid", app.wrap().query_wasm_code_info(0).unwrap_err().to_string() @@ -1661,6 +1679,7 @@ mod custom_messages { let mut app = AppBuilder::new_custom() .with_custom(custom_handler) + .with_remote(remote_channel()) .build(no_init); let sender = app.api().addr_make("sender"); @@ -1696,7 +1715,6 @@ mod custom_messages { mod protobuf_wrapped_data { use super::*; - use crate::BasicApp; #[test] fn instantiate_wrapped_properly() { @@ -1710,7 +1728,10 @@ mod protobuf_wrapped_data { .bank .init_balance(storage, &owner, init_funds) .unwrap(); - }); + router.bank.set_remote(remote_channel()); + router.wasm.set_remote(remote_channel()); + }) + .with_remote(remote_channel()); // set up reflect contract let code_id = app.store_code(reflect::contract()); @@ -1739,7 +1760,7 @@ mod protobuf_wrapped_data { #[test] fn instantiate_with_data_works() { - let mut app = BasicApp::new(no_init); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1769,7 +1790,7 @@ mod protobuf_wrapped_data { #[test] fn instantiate_with_reply_works() { - let mut app = BasicApp::new(no_init); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1822,7 +1843,7 @@ mod protobuf_wrapped_data { #[test] fn execute_wrapped_properly() { - let mut app = BasicApp::new(no_init); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1849,7 +1870,7 @@ mod errors { #[test] fn simple_instantiation() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); @@ -1876,7 +1897,7 @@ mod errors { #[test] fn simple_call() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); let random_addr = app.api().addr_make("random"); @@ -1909,7 +1930,7 @@ mod errors { #[test] fn nested_call() { - let mut app = App::default(); + let mut app = default_app(); let owner = app.api().addr_make("owner"); let random_addr = app.api().addr_make("random"); @@ -1951,7 +1972,7 @@ mod errors { #[test] fn double_nested_call() { - let mut app = App::default(); + let mut app = default_app(); let owner_addr = app.api().addr_make("owner"); let random_addr = app.api().addr_make("random"); diff --git a/src/tests/test_custom_handler.rs b/src/tests/test_custom_handler.rs index d38d6dea..98a7a01c 100644 --- a/src/tests/test_custom_handler.rs +++ b/src/tests/test_custom_handler.rs @@ -1,6 +1,7 @@ use crate::custom_handler::CachingCustomHandler; use crate::test_helpers::CustomHelperMsg; -use crate::{App, Module}; +use crate::tests::default_app; +use crate::Module; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::Empty; @@ -10,7 +11,7 @@ use cosmwasm_std::Empty; #[test] fn custom_handler_works() { // prepare needed tools - let app = App::default(); + let app = default_app(); let mut storage = MockStorage::default(); // create custom handler @@ -66,7 +67,7 @@ fn custom_handler_works() { #[test] fn custom_handler_has_no_sudo() { // prepare needed tools - let app = App::default(); + let app = default_app(); let mut storage = MockStorage::default(); // create custom handler diff --git a/src/tests/test_gov.rs b/src/tests/test_gov.rs index 65d560a4..e4dcf4a1 100644 --- a/src/tests/test_gov.rs +++ b/src/tests/test_gov.rs @@ -1,12 +1,12 @@ #![cfg(feature = "stargate")] use crate::test_helpers::gov; -use crate::{no_init, App, AppBuilder, Executor, GovAcceptingModule}; +use crate::tests::default_app; +use crate::{no_init, AppBuilder, Executor, GovAcceptingModule}; use cosmwasm_std::Empty; - #[test] fn default_gov() { - let mut app = App::default(); + let mut app = default_app(); let creator_addr = app.api().addr_make("creator"); let code = app.store_code_with_creator(creator_addr, gov::contract()); diff --git a/src/tests/test_ibc.rs b/src/tests/test_ibc.rs index c3e7a756..913318c7 100644 --- a/src/tests/test_ibc.rs +++ b/src/tests/test_ibc.rs @@ -1,12 +1,12 @@ #![cfg(feature = "stargate")] use crate::test_helpers::ibc; -use crate::{no_init, App, AppBuilder, Executor, IbcAcceptingModule}; +use crate::tests::default_app; +use crate::{no_init, AppBuilder, Executor, IbcAcceptingModule}; use cosmwasm_std::Empty; - #[test] fn default_ibc() { - let mut app = App::default(); + let mut app = default_app(); let creator_addr = app.api().addr_make("creator"); let code = app.store_code_with_creator(creator_addr, ibc::contract()); diff --git a/src/tests/test_stargate.rs b/src/tests/test_stargate.rs index db00a5ab..246536b4 100644 --- a/src/tests/test_stargate.rs +++ b/src/tests/test_stargate.rs @@ -1,12 +1,12 @@ #![cfg(feature = "stargate")] use crate::test_helpers::stargate; -use crate::{no_init, App, AppBuilder, Executor, StargateAccepting}; +use crate::tests::default_app; +use crate::{no_init, AppBuilder, Executor, StargateAccepting}; use cosmwasm_std::Empty; - #[test] fn default_failing_stargate_handler_should_work() { - let mut app = App::default(); + let mut app = default_app(); // store the contract let creator_addr = app.api().addr_make("creator"); diff --git a/src/tokenfactory/mod.rs b/src/tokenfactory/mod.rs new file mode 100644 index 00000000..31835868 --- /dev/null +++ b/src/tokenfactory/mod.rs @@ -0,0 +1,257 @@ +use anyhow::{bail, Result as AnyResult}; +use cosmwasm_schema::serde::de::DeserializeOwned; +use cosmwasm_std::{ + Addr, AnyMsg, Api, Binary, BlockInfo, CosmosMsg, CustomMsg, CustomQuery, GrpcQuery, Querier, + Storage, +}; +use cw_storage_plus::Map; +use prost::Message; + +use crate::{AppResponse, CosmosRouter, Stargate}; + +pub mod osmosis; +pub(crate) mod serde; +pub use shim::Coin; + +pub(crate) mod shim; + +/// Always accepting handler for `Stargate`/`Any` message variants and `Stargate`/`Grpc` queries. +pub struct TokenFactoryStargate; + +const TOKENFACTORY_DENOMS: Map<(&String, &String), ()> = Map::new("tokenfactory_denoms"); + +/// Merge sender and subdenom to create the denom +pub fn denom(sender: &str, subdenom: &str) -> String { + format!("factory/{sender}/{subdenom}") +} + +fn split_denom(denom: &str) -> AnyResult<(String, String)> { + let split_result: Vec<&str> = denom.splitn(3, "/").collect(); + + if split_result.len() != 3 { + bail!("Error decoding denom {denom} into factory/sender/subdenom") + } + if split_result[0] != "factory" { + bail!("Error decoding denom {denom} into factory/sender/subdenom") + } + + Ok((split_result[1].to_string(), split_result[2].to_string())) +} + +impl Stargate for TokenFactoryStargate { + fn execute_stargate( + &self, + api: &dyn Api, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + sender: Addr, + type_url: String, + value: Binary, + ) -> AnyResult + where + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, + { + // We match the type url + match type_url.as_str() { + osmosis::MsgCreateDenom::TYPE_URL => { + let msg = osmosis::MsgCreateDenom::decode(value.as_slice())?; + if sender != api.addr_validate(&msg.sender)? { + bail!("Sender field should match the message sender") + } + self.create_denom(storage, msg.sender, msg.subdenom) + } + osmosis::MsgMint::TYPE_URL => { + let msg = osmosis::MsgMint::decode(value.as_slice())?; + if sender != api.addr_validate(&msg.sender)? { + bail!("Sender field should match the message sender") + } + self.mint( + api, + storage, + router, + block, + msg.sender, + msg.amount, + msg.mint_to_address, + ) + } + osmosis::MsgBurn::TYPE_URL => { + let msg = osmosis::MsgBurn::decode(value.as_slice())?; + if sender != api.addr_validate(&msg.sender)? { + bail!("Sender field should match the message sender") + } + self.burn( + api, + storage, + router, + block, + msg.sender, + msg.amount, + msg.burn_from_address, + ) + } + _ => { + bail!("type url {} is not supported", type_url) + } + } + } + + fn query_stargate( + &self, + _api: &dyn Api, + _storage: &dyn Storage, + _querier: &dyn Querier, + _block: &BlockInfo, + _path: String, + _data: Binary, + ) -> AnyResult { + bail!("Stargate / any queries, unsupported") + } + + fn execute_any( + &self, + api: &dyn Api, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + sender: Addr, + msg: AnyMsg, + ) -> AnyResult + where + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, + { + self.execute_stargate(api, storage, router, block, sender, msg.type_url, msg.value) + } + + fn query_grpc( + &self, + api: &dyn Api, + storage: &dyn Storage, + querier: &dyn Querier, + block: &BlockInfo, + request: GrpcQuery, + ) -> AnyResult { + self.query_stargate(api, storage, querier, block, request.path, request.data) + } +} + +impl TokenFactoryStargate { + fn create_denom( + &self, + storage: &mut dyn Storage, + sender: String, + subdenom: String, + ) -> AnyResult { + if TOKENFACTORY_DENOMS.has(storage, (&sender, &subdenom)) { + bail!("Subdenom {subdenom} by sender {sender} already exists") + } + + TOKENFACTORY_DENOMS.save(storage, (&sender, &subdenom), &())?; + + let data = osmosis::MsgCreateDenomResponse { + new_token_denom: denom(&sender, &subdenom), + } + .to_proto_bytes(); + + Ok(AppResponse { + data: Some(data.into()), + events: vec![], + }) + } + fn mint( + &self, + api: &dyn Api, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + sender: String, + amount: Option, + mint_to_address: String, + ) -> AnyResult + where + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, + { + let amount = if let Some(amount) = amount { + amount + } else { + bail!("Can't mint nothing, please specify an amount") + }; + + // We verify the denom exists + let (creator, subdenom) = split_denom(&amount.denom)?; + if creator != sender { + bail!("Only creator can mint token factory tokens") + } + if !TOKENFACTORY_DENOMS.has(storage, (&sender, &subdenom)) { + bail!("Subdenom {subdenom} by sender {sender} doesn't exist") + } + + // We mint. No need for transactional cache here, if this fails, the tokenfactory mint fails + router.sudo( + api, + storage, + block, + crate::SudoMsg::Bank(crate::BankSudo::Mint { + to_address: mint_to_address, + amount: vec![amount.try_into()?], + }), + )?; + + let data = osmosis::MsgMintResponse {}.to_proto_bytes(); + + Ok(AppResponse { + data: Some(data.into()), + events: vec![], + }) + } + + fn burn( + &self, + api: &dyn Api, + storage: &mut dyn Storage, + router: &dyn CosmosRouter, + block: &BlockInfo, + sender: String, + amount: Option, + burn_from_address: String, + ) -> AnyResult + where + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, + { + let amount = if let Some(amount) = amount { + amount + } else { + bail!("Can't burn nothing, please specify an amount") + }; + let (creator, subdenom) = split_denom(&amount.denom)?; + if creator != sender { + bail!("Only creator can burn token factory tokens from any address") + } + // We verify the denom exists + if !TOKENFACTORY_DENOMS.has(storage, (&sender, &subdenom)) { + bail!("Subdenom {subdenom} by sender {sender} doesn't exist") + } + + // We burn. No need for transactional cache here, if this fails, the tokenfactory burn fails + router.execute( + api, + storage, + block, + api.addr_validate(&burn_from_address)?, + CosmosMsg::Bank(cosmwasm_std::BankMsg::Burn { + amount: vec![amount.try_into()?], + }), + )?; + let data = osmosis::MsgBurnResponse {}.to_proto_bytes(); + + Ok(AppResponse { + data: Some(data.into()), + events: vec![], + }) + } +} diff --git a/src/tokenfactory/osmosis.rs b/src/tokenfactory/osmosis.rs new file mode 100644 index 00000000..e4caa02a --- /dev/null +++ b/src/tokenfactory/osmosis.rs @@ -0,0 +1,392 @@ +#![allow(missing_docs)] + +use osmosis_std_derive::CosmwasmExt; + +use super::shim::Coin; + +/// QueryDenomsFromCreatorRequest defines the request structure for the +/// DenomsFromCreator gRPC query. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorRequest")] +#[proto_query( + path = "/osmosis.tokenfactory.v1beta1.Query/DenomsFromCreator", + response_type = QueryDenomsFromCreatorResponse +)] +pub struct QueryDenomsFromCreatorRequest { + #[prost(string, tag = "1")] + pub creator: ::prost::alloc::string::String, +} +/// QueryDenomsFromCreatorRequest defines the response structure for the +/// DenomsFromCreator gRPC query. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorResponse")] +pub struct QueryDenomsFromCreatorResponse { + #[prost(string, repeated, tag = "1")] + pub denoms: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.QueryBeforeSendHookAddressRequest")] +#[proto_query( + path = "/osmosis.tokenfactory.v1beta1.Query/BeforeSendHookAddress", + response_type = QueryBeforeSendHookAddressResponse +)] +pub struct QueryBeforeSendHookAddressRequest { + #[prost(string, tag = "1")] + pub denom: ::prost::alloc::string::String, +} +/// QueryBeforeSendHookAddressResponse defines the response structure for the +/// DenomBeforeSendHook gRPC query. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.QueryBeforeSendHookAddressResponse")] +pub struct QueryBeforeSendHookAddressResponse { + #[prost(string, tag = "1")] + pub cosmwasm_address: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.QueryAllBeforeSendHooksAddressesRequest")] +#[proto_query( + path = "/osmosis.tokenfactory.v1beta1.Query/AllBeforeSendHooksAddresses", + response_type = QueryAllBeforeSendHooksAddressesResponse +)] +pub struct QueryAllBeforeSendHooksAddressesRequest {} +/// QueryAllBeforeSendHooksAddressesResponse defines the response structure for +/// the AllBeforeSendHooksAddresses gRPC query. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message( + type_url = "/osmosis.tokenfactory.v1beta1.QueryAllBeforeSendHooksAddressesResponse" +)] +pub struct QueryAllBeforeSendHooksAddressesResponse { + #[prost(string, repeated, tag = "1")] + pub denoms: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, repeated, tag = "2")] + pub before_send_hook_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +/// MsgCreateDenom defines the message structure for the CreateDenom gRPC service +/// method. It allows an account to create a new denom. It requires a sender +/// address and a sub denomination. The (sender_address, sub_denomination) tuple +/// must be unique and cannot be re-used. +/// +/// The resulting denom created is defined as +/// . The resulting denom's admin is +/// originally set to be the creator, but this can be changed later. The token +/// denom does not indicate the current admin. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgCreateDenom")] +pub struct MsgCreateDenom { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + /// subdenom can be up to 44 "alphanumeric" characters long. + #[prost(string, tag = "2")] + pub subdenom: ::prost::alloc::string::String, +} +/// MsgCreateDenomResponse is the return value of MsgCreateDenom +/// It returns the full string of the newly created denom +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgCreateDenomResponse")] +pub struct MsgCreateDenomResponse { + #[prost(string, tag = "1")] + pub new_token_denom: ::prost::alloc::string::String, +} +/// MsgMint is the sdk.Msg type for allowing an admin account to mint +/// more of a token. +/// Only the admin of the token factory denom has permission to mint unless +/// the denom does not have any admin. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgMint")] +pub struct MsgMint { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub amount: ::core::option::Option, + #[prost(string, tag = "3")] + pub mint_to_address: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgMintResponse")] +pub struct MsgMintResponse {} +/// MsgBurn is the sdk.Msg type for allowing an admin account to burn +/// a token. +/// Only the admin of the token factory denom has permission to burn unless +/// the denom does not have any admin. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgBurn")] +pub struct MsgBurn { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub amount: ::core::option::Option, + #[prost(string, tag = "3")] + pub burn_from_address: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgBurnResponse")] +pub struct MsgBurnResponse {} +/// MsgChangeAdmin is the sdk.Msg type for allowing an admin account to reassign +/// adminship of a denom to a new account +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgChangeAdmin")] +pub struct MsgChangeAdmin { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub denom: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub new_admin: ::prost::alloc::string::String, +} +/// MsgChangeAdminResponse defines the response structure for an executed +/// MsgChangeAdmin message. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgChangeAdminResponse")] +pub struct MsgChangeAdminResponse {} +/// MsgSetBeforeSendHook is the sdk.Msg type for allowing an admin account to +/// assign a CosmWasm contract to call with a BeforeSend hook +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgSetBeforeSendHook")] +pub struct MsgSetBeforeSendHook { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub denom: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub cosmwasm_address: ::prost::alloc::string::String, +} +/// MsgSetBeforeSendHookResponse defines the response structure for an executed +/// MsgSetBeforeSendHook message. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgSetBeforeSendHookResponse")] +pub struct MsgSetBeforeSendHookResponse {} +/// MsgSetDenomMetadataResponse defines the response structure for an executed +/// MsgSetDenomMetadata message. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgSetDenomMetadataResponse")] +pub struct MsgSetDenomMetadataResponse {} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgForceTransfer")] +pub struct MsgForceTransfer { + #[prost(string, tag = "1")] + pub sender: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub amount: ::core::option::Option, + #[prost(string, tag = "3")] + pub transfer_from_address: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub transfer_to_address: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgForceTransferResponse")] +pub struct MsgForceTransferResponse {} +pub struct TokenfactoryQuerier<'a, Q: cosmwasm_std::CustomQuery> { + querier: &'a cosmwasm_std::QuerierWrapper<'a, Q>, +} +impl<'a, Q: cosmwasm_std::CustomQuery> TokenfactoryQuerier<'a, Q> { + pub fn new(querier: &'a cosmwasm_std::QuerierWrapper<'a, Q>) -> Self { + Self { querier } + } + pub fn denoms_from_creator( + &self, + creator: ::prost::alloc::string::String, + ) -> Result { + QueryDenomsFromCreatorRequest { creator }.query(self.querier) + } + pub fn before_send_hook_address( + &self, + denom: ::prost::alloc::string::String, + ) -> Result { + QueryBeforeSendHookAddressRequest { denom }.query(self.querier) + } + pub fn all_before_send_hooks_addresses( + &self, + ) -> Result { + QueryAllBeforeSendHooksAddressesRequest {}.query(self.querier) + } +} diff --git a/src/tokenfactory/serde.rs b/src/tokenfactory/serde.rs new file mode 100644 index 00000000..5461e99a --- /dev/null +++ b/src/tokenfactory/serde.rs @@ -0,0 +1,104 @@ +pub mod as_str { + use serde::{de, Deserialize, Deserializer, Serializer}; + use std::{fmt::Display, str::FromStr}; + + pub fn deserialize<'de, T, D>(deserializer: D) -> Result + where + T: FromStr, + T::Err: Display, + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + T::from_str(&s).map_err(de::Error::custom) + } + + pub fn serialize(value: &T, serializer: S) -> Result + where + S: Serializer, + T: Display, + { + serializer.serialize_str(&value.to_string()) + } +} + +pub mod as_str_vec { + use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + use std::{fmt::Display, str::FromStr}; + + pub fn deserialize<'de, T, D>(deserializer: D) -> Result, D::Error> + where + T: FromStr, + T::Err: Display, + D: Deserializer<'de>, + { + let vec_of_strings: Vec = Vec::deserialize(deserializer)?; + vec_of_strings + .into_iter() + .map(|s| T::from_str(&s).map_err(de::Error::custom)) + .collect() + } + + pub fn serialize(values: &[T], serializer: S) -> Result + where + S: Serializer, + T: Display, + { + let vec_of_strings: Vec = values.iter().map(|value| value.to_string()).collect(); + vec_of_strings.serialize(serializer) + } +} + +pub mod as_base64_encoded_string { + use cosmwasm_std::Binary; + use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let encoded_string = String::deserialize(deserializer)?; + Binary::from_base64(&encoded_string) + .map(|b| b.to_vec()) + .map_err(de::Error::custom) + } + + pub fn serialize(values: &[u8], serializer: S) -> Result + where + S: Serializer, + { + Binary::new(values.to_vec()) + .to_base64() + .serialize(serializer) + } +} + +pub mod as_option_base64_encoded_string { + use cosmwasm_std::Binary; + use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + + pub fn deserialize<'de, D>(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + let encoded_string: Option = Option::deserialize(deserializer)?; + match encoded_string { + Some(s) => Binary::from_base64(&s) + .map(|b| Some(b.to_vec())) + .map_err(de::Error::custom), + None => Ok(None), + } + } + + pub fn serialize(value: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match value { + Some(vec) => { + let encoded_string = Binary::new(vec.clone()).to_base64(); + encoded_string.serialize(serializer) + } + None => serializer.serialize_none(), + } + } +} diff --git a/src/tokenfactory/shim.rs b/src/tokenfactory/shim.rs new file mode 100644 index 00000000..623d6ac2 --- /dev/null +++ b/src/tokenfactory/shim.rs @@ -0,0 +1,284 @@ +#![allow(missing_docs)] + +use ::serde::{Deserialize, Deserializer, Serialize, Serializer}; +use chrono::{DateTime, NaiveDateTime, Utc}; +use cosmwasm_std::StdResult; +use osmosis_std_derive::CosmwasmExt; +use serde::de; +use serde::de::Visitor; + +use std::fmt; +use std::str::FromStr; + +#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)] +pub struct Timestamp { + /// Represents seconds of UTC time since Unix epoch + /// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + /// 9999-12-31T23:59:59Z inclusive. + #[prost(int64, tag = "1")] + pub seconds: i64, + /// Non-negative fractions of a second at nanosecond resolution. Negative + /// second values with fractions must still have non-negative nanos values + /// that count forward in time. Must be from 0 to 999,999,999 + /// inclusive. + #[prost(int32, tag = "2")] + pub nanos: i32, +} + +impl Serialize for Timestamp { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer, + { + let mut ts = prost_types::Timestamp { + seconds: self.seconds, + nanos: self.nanos, + }; + ts.normalize(); + let dt = NaiveDateTime::from_timestamp_opt(ts.seconds, ts.nanos as u32) + .expect("invalid or out-of-range datetime"); + let dt: DateTime = DateTime::from_naive_utc_and_offset(dt, Utc); + serializer.serialize_str(format!("{:?}", dt).as_str()) + } +} + +impl<'de> Deserialize<'de> for Timestamp { + fn deserialize(deserializer: D) -> Result>::Error> + where + D: Deserializer<'de>, + { + struct TimestampVisitor; + + impl<'de> Visitor<'de> for TimestampVisitor { + type Value = Timestamp; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Timestamp in RFC3339 format") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + let utc: DateTime = chrono::DateTime::from_str(value).map_err(|err| { + serde::de::Error::custom(format!( + "Failed to parse {} as datetime: {:?}", + value, err + )) + })?; + let ts = Timestamp::from(utc); + Ok(ts) + } + } + deserializer.deserialize_str(TimestampVisitor) + } +} + +impl From> for Timestamp { + fn from(dt: DateTime) -> Self { + Timestamp { + seconds: dt.timestamp(), + nanos: dt.timestamp_subsec_nanos() as i32, + } + } +} +#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)] +pub struct Duration { + /// Signed seconds of the span of time. Must be from -315,576,000,000 + /// to +315,576,000,000 inclusive. Note: these bounds are computed from: + /// 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + #[prost(int64, tag = "1")] + pub seconds: i64, + /// Signed fractions of a second at nanosecond resolution of the span + /// of time. Durations less than one second are represented with a 0 + /// `seconds` field and a positive or negative `nanos` field. For durations + /// of one second or more, a non-zero value for the `nanos` field must be + /// of the same sign as the `seconds` field. Must be from -999,999,999 + /// to +999,999,999 inclusive. + #[prost(int32, tag = "2")] + pub nanos: i32, +} + +impl Serialize for Duration { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer, + { + let mut d = prost_types::Duration::from(self.to_owned()); + d.normalize(); + + serializer.serialize_str(d.to_string().as_str()) + } +} + +impl<'de> Deserialize<'de> for Duration { + fn deserialize(deserializer: D) -> Result>::Error> + where + D: Deserializer<'de>, + { + struct DurationVisitor; + + impl<'de> Visitor<'de> for DurationVisitor { + type Value = Duration; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Timestamp in RFC3339 format") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + value + .parse::() + .map(Into::into) + .map_err(de::Error::custom) + } + } + deserializer.deserialize_str(DurationVisitor) + } +} + +#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)] +pub struct Any { + /// A URL/resource name that uniquely identifies the type of the serialized + /// protocol buffer message. This string must contain at least + /// one "/" character. The last segment of the URL's path must represent + /// the fully qualified name of the type (as in + /// `path/google.protobuf.Duration`). The name should be in a canonical form + /// (e.g., leading "." is not accepted). + /// + /// In practice, teams usually precompile into the binary all types that they + /// expect it to use in the context of Any. However, for URLs which use the + /// scheme `http`, `https`, or no scheme, one can optionally set up a type + /// server that maps type URLs to message definitions as follows: + /// + /// * If no scheme is provided, `https` is assumed. + /// * An HTTP GET on the URL must yield a \[google.protobuf.Type][\] + /// value in binary format, or produce an error. + /// * Applications are allowed to cache lookup results based on the + /// URL, or have them precompiled into a binary to avoid any + /// lookup. Therefore, binary compatibility needs to be preserved + /// on changes to types. (Use versioned type names to manage + /// breaking changes.) + /// + /// Note: this functionality is not currently available in the official + /// protobuf release, and it is not used for type URLs beginning with + /// type.googleapis.com. + /// + /// Schemes other than `http`, `https` (or the empty scheme) might be + /// used with implementation specific semantics. + /// + #[prost(string, tag = "1")] + pub type_url: ::prost::alloc::string::String, + /// Must be a valid serialized protocol buffer of the above specified type. + #[prost(bytes = "vec", tag = "2")] + pub value: ::prost::alloc::vec::Vec, +} + +macro_rules! impl_prost_types_exact_conversion { + ($t:ident | $($arg:ident),*) => { + impl From<$t> for prost_types::$t { + fn from(src: $t) -> Self { + prost_types::$t { + $( + $arg: src.$arg, + )* + } + } + } + + impl From for $t { + fn from(src: prost_types::$t) -> Self { + $t { + $( + $arg: src.$arg, + )* + } + } + } + }; +} + +impl_prost_types_exact_conversion! { Timestamp | seconds, nanos } +impl_prost_types_exact_conversion! { Duration | seconds, nanos } +impl_prost_types_exact_conversion! { Any | type_url, value } +/// Coin defines a token with a denomination and an amount. +/// +/// NOTE: The amount field is an Int which implements the custom method +/// signatures required by gogoproto. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/cosmos.base.v1beta1.Coin")] +pub struct Coin { + #[prost(string, tag = "1")] + pub denom: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub amount: ::prost::alloc::string::String, +} +impl From for Coin { + fn from(cosmwasm_std::Coin { denom, amount }: cosmwasm_std::Coin) -> Self { + Coin { + denom, + amount: amount.into(), + } + } +} + +impl TryFrom for cosmwasm_std::Coin { + type Error = cosmwasm_std::StdError; + + fn try_from(Coin { denom, amount }: Coin) -> cosmwasm_std::StdResult { + Ok(cosmwasm_std::Coin { + denom, + amount: amount.parse()?, + }) + } +} + +/// Convert a list of `Coin` from osmosis proto generated proto `Coin` type to cosmwasm `Coin` type +pub fn try_proto_to_cosmwasm_coins( + coins: impl IntoIterator, +) -> StdResult> { + coins.into_iter().map(|c| c.try_into()).collect() +} + +/// Convert a list of `Coin` from cosmwasm `Coin` type to osmosis proto generated proto `Coin` type +pub fn cosmwasm_to_proto_coins(coins: impl IntoIterator) -> Vec { + coins.into_iter().map(|c| c.into()).collect() +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::Uint128; + + use super::*; + + #[test] + fn test_coins_conversion() { + let coins = vec![ + cosmwasm_std::Coin { + denom: "uatom".to_string(), + amount: Uint128::new(100), + }, + cosmwasm_std::Coin { + denom: "uosmo".to_string(), + amount: Uint128::new(200), + }, + ]; + + let proto_coins = cosmwasm_to_proto_coins(coins.clone()); + let cosmwasm_coins = try_proto_to_cosmwasm_coins(proto_coins).unwrap(); + + assert_eq!(coins, cosmwasm_coins); + } +} diff --git a/src/transactions.rs b/src/transactions.rs index 59ac0b0f..8c4aeb7d 100644 --- a/src/transactions.rs +++ b/src/transactions.rs @@ -45,7 +45,7 @@ impl<'a> StorageTransaction<'a> { } } -impl<'a> Storage for StorageTransaction<'a> { +impl Storage for StorageTransaction<'_> { fn get(&self, key: &[u8]) -> Option> { match self.local_state.get(key) { Some(val) => match val { diff --git a/src/wasm.rs b/src/wasm.rs index 2fc0e044..5c239507 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -5,7 +5,14 @@ use crate::contracts::Contract; use crate::error::{bail, AnyContext, AnyError, AnyResult, Error}; use crate::executor::AppResponse; use crate::prefixed_storage::{prefixed, prefixed_read, PrefixedStorage, ReadonlyPrefixedStorage}; +use crate::queries::wasm::WasmRemoteQuerier; use crate::transactions::transactional; +use crate::wasm_emulation::channel::RemoteChannel; +use crate::wasm_emulation::contract::{LocalWasmContract, WasmContract}; +use crate::wasm_emulation::input::QuerierStorage; +use crate::wasm_emulation::instance::create_module; +use crate::wasm_emulation::query::mock_querier::{ForkState, LocalForkedState}; +use crate::wasm_emulation::query::{AllWasmQuerier, ContainsRemote}; use cosmwasm_std::testing::mock_wasmd_attr; use cosmwasm_std::{ to_json_binary, Addr, Api, Attribute, BankMsg, Binary, BlockInfo, Checksum, Coin, ContractInfo, @@ -18,20 +25,22 @@ use prost::Message; use schemars::JsonSchema; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use std::borrow::Borrow; +use std::cell::RefCell; use std::collections::BTreeMap; +use std::collections::HashMap; use std::fmt::Debug; /// Contract state kept in storage, separate from the contracts themselves (contract code). -const CONTRACTS: Map<&Addr, ContractData> = Map::new("contracts"); +pub const CONTRACTS: Map<&Addr, ContractData> = Map::new("contracts"); /// Wasm module namespace. -const NAMESPACE_WASM: &[u8] = b"wasm"; +pub const NAMESPACE_WASM: &[u8] = b"wasm"; /// Contract [address namespace]. /// /// [address namespace]: https://github.com/CosmWasm/wasmd/blob/96e2b91144c9a371683555f3c696f882583cc6a2/x/wasm/types/events.go#L59 const CONTRACT_ATTR: &str = "_contract_address"; +pub const LOCAL_CODE_OFFSET: usize = 5_000_000; /// A structure representing a privileged message. #[derive(Clone, Debug, PartialEq, Eq, JsonSchema)] @@ -68,18 +77,18 @@ pub struct ContractData { pub created: u64, } +#[derive(Serialize, Deserialize, Debug, Clone)] /// Contract code base data. -struct CodeData { +pub struct CodeData { /// Address of an account that initially stored the contract code. - creator: Addr, + pub creator: Addr, /// Checksum of the contract's code base. - checksum: Checksum, - /// Identifier of the _source_ code of the contract stored in wasm keeper. - source_id: usize, + pub checksum: Checksum, + /// Identifier of the code base where the contract code is stored in memory. + pub source_id: usize, } - /// This trait implements the interface of the Wasm module. -pub trait Wasm { +pub trait Wasm: AllWasmQuerier + ContainsRemote { /// Handles all `WasmMsg` messages. fn execute( &self, @@ -96,6 +105,7 @@ pub trait Wasm { &self, api: &dyn Api, storage: &dyn Storage, + router: &dyn CosmosRouter, querier: &dyn Querier, block: &BlockInfo, request: WasmQuery, @@ -114,6 +124,8 @@ pub trait Wasm { /// Stores the contract's code and returns an identifier of the stored contract's code. fn store_code(&mut self, creator: Addr, code: Box>) -> u64; + /// Stores the contract's code and returns an identifier of the stored contract's code. + fn store_wasm_code(&mut self, creator: Addr, code: Vec) -> u64; /// Stores the contract's code under specified identifier, /// returns the same code identifier when successful. fn store_code_with_id( @@ -167,29 +179,36 @@ pub trait Wasm { } } +pub type LocalRustContract = *mut dyn Contract; /// A structure representing a default wasm keeper. -pub struct WasmKeeper { +pub struct WasmKeeper { /// Contract codes that stand for wasm code in real-life blockchain. - code_base: Vec>>, - /// Code data with code base identifier and additional attributes. - code_data: BTreeMap, + pub code_base: RefCell>, + /// Code data with code base identifier and additional attributes. + pub code_data: BTreeMap, + /// Contract codes that stand for rust code living in the current instance + /// We also associate the queries to them to make sure we are able to use them with the vm instance + pub rust_codes: HashMap>, /// Contract's address generator. address_generator: Box, /// Contract's code checksum generator. checksum_generator: Box, + // chain on which the contract should be queried/tested against + remote: Option, /// Just markers to make type elision fork when using it as `Wasm` trait _p: std::marker::PhantomData, } -impl Default for WasmKeeper { - /// Returns the default value for [WasmKeeper]. - fn default() -> Self { +impl Default for WasmKeeper { + fn default() -> WasmKeeper { Self { - code_base: Vec::default(), + code_base: BTreeMap::default().into(), code_data: BTreeMap::default(), address_generator: Box::new(SimpleAddressGenerator), checksum_generator: Box::new(SimpleChecksumGenerator), _p: std::marker::PhantomData, + remote: None, + rust_codes: HashMap::new(), } } } @@ -219,6 +238,7 @@ where &self, api: &dyn Api, storage: &dyn Storage, + router: &dyn CosmosRouter, querier: &dyn Querier, block: &BlockInfo, request: WasmQuery, @@ -226,7 +246,15 @@ where match request { WasmQuery::Smart { contract_addr, msg } => { let addr = api.addr_validate(&contract_addr)?; - self.query_smart(addr, api, storage, querier, block, msg.into()) + self.query_smart( + addr, + api, + storage, + querier, + block, + msg.into(), + router.get_querier_storage(storage)?, + ) } WasmQuery::Raw { contract_addr, key } => { let addr = api.addr_validate(&contract_addr)?; @@ -267,6 +295,9 @@ where msg: WasmSudo, ) -> AnyResult { let custom_event = Event::new("sudo").add_attribute(CONTRACT_ATTR, &msg.contract_addr); + + let querier_storage = router.get_querier_storage(storage)?; + let res = self.call_sudo( msg.contract_addr.clone(), api, @@ -274,16 +305,42 @@ where router, block, msg.message.to_vec(), + querier_storage, )?; let (res, msgs) = self.build_app_response(&msg.contract_addr, custom_event, res); self.process_response(api, router, storage, block, msg.contract_addr, res, msgs) } + /// Stores the contract's code in the in-memory lookup table. + /// Returns an identifier of the stored contract code. + fn store_wasm_code(&mut self, creator: Addr, code: Vec) -> u64 { + let code_id = self + .next_local_code_id() + .unwrap_or_else(|| panic!("{}", Error::NoMoreCodeIdAvailable)); + let code = WasmContract::Local(LocalWasmContract { + module: create_module(&code).unwrap(), + code, + }); + let checksum = >::checksum(&code) + .unwrap_or(self.checksum_generator.checksum(&creator, code_id)); + + self.code_base.borrow_mut().insert(code_id, code); + self.code_data.insert( + code_id, + CodeData { + creator, + checksum, + source_id: code_id as usize, + }, + ); + code_id + } + /// Stores the contract's code in the in-memory lookup table. /// Returns an identifier of the stored contract code. fn store_code(&mut self, creator: Addr, code: Box>) -> u64 { let code_id = self - .next_code_id() + .next_local_code_id() .unwrap_or_else(|| panic!("{}", Error::NoMoreCodeIdAvailable)); self.save_code(code_id, creator, code) } @@ -310,7 +367,7 @@ where fn duplicate_code(&mut self, code_id: u64) -> AnyResult { let code_data = self.code_data(code_id)?; let new_code_id = self - .next_code_id() + .next_local_code_id() .ok_or_else(Error::no_more_code_id_available)?; self.code_data.insert( new_code_id, @@ -327,6 +384,9 @@ where fn contract_data(&self, storage: &dyn Storage, address: &Addr) -> AnyResult { CONTRACTS .load(&prefixed_read(storage, NAMESPACE_WASM), address) + .or_else(|_| { + WasmRemoteQuerier::load_distant_contract(self.remote.clone().unwrap(), address) + }) .map_err(Into::into) } @@ -337,11 +397,35 @@ where } } +pub enum ContractBox<'a, ExecC, QueryC> { + Borrowed(&'a dyn Contract), + Owned(Box>), +} + impl WasmKeeper where ExecC: CustomMsg + DeserializeOwned + 'static, QueryC: CustomQuery + DeserializeOwned + 'static, { + /// Only for Clone-testing + fn fork_state( + &self, + querier_storage: QuerierStorage, + env: &Env, + ) -> AnyResult> { + Ok(ForkState { + remote: self.remote.clone().unwrap(), + querier_storage, + local_state: LocalForkedState { + contracts: self + .rust_codes + .iter() + .map(|(id, &code)| (*id, code)) + .collect(), + env: env.clone(), + }, + }) + } /// Creates a wasm keeper with default settings. /// /// # Example @@ -430,20 +514,53 @@ where } /// Returns a handler to code of the contract with specified code id. - pub fn contract_code(&self, code_id: u64) -> AnyResult<&dyn Contract> { - let code_data = self.code_data(code_id)?; - Ok(self.code_base[code_data.source_id].borrow()) + pub fn contract_code(&self, code_id: u64) -> AnyResult> { + let code = self.code_base.borrow().get(&code_id).cloned(); + if let Some(code) = code { + Ok(ContractBox::Owned(Box::new(code))) + } else if let Some(&rust_code) = self.rust_codes.get(&code_id) { + Ok(ContractBox::Borrowed(unsafe { + rust_code.as_ref().unwrap() + })) + } else { + // We fetch the code and save the corresponding module in memory + let wasm_contract = + WasmContract::new_distant_code_id(code_id, self.remote.clone().unwrap()); + + // We save it in memory + self.code_base + .borrow_mut() + .insert(code_id, wasm_contract.clone()); + + // And return a Owned reference + Ok(ContractBox::Owned(Box::new(wasm_contract))) + } } /// Returns code data of the contract with specified code id. - fn code_data(&self, code_id: u64) -> AnyResult<&CodeData> { + fn code_data(&self, code_id: u64) -> AnyResult { if code_id < 1 { bail!(Error::invalid_code_id()); } Ok(self .code_data .get(&code_id) - .ok_or_else(|| Error::unregistered_code_id(code_id))?) + .cloned() + .ok_or_else(|| { + let code_info_response = + WasmRemoteQuerier::code_info(self.remote.clone().unwrap(), code_id)?; + Ok::<_, anyhow::Error>(CodeData { + creator: Addr::unchecked(code_info_response.creator), + checksum: code_info_response.checksum, + source_id: code_id as usize, + }) + }) + .map_err(|_| Error::unregistered_code_id(code_id))?) + } + + pub fn dump_wasm_raw(&self, storage: &dyn Storage, address: &Addr) -> Vec { + let storage = self.contract_storage(storage, address); + storage.range(None, None, Order::Ascending).collect() } /// Validates all attributes. @@ -489,13 +606,15 @@ where code: Box>, ) -> u64 { // prepare the next identifier for the contract's code - let source_id = self.code_base.len(); + let source_id = code_id as usize; // prepare the contract's Wasm blob checksum let checksum = code .checksum() .unwrap_or(self.checksum_generator.checksum(&creator, code_id)); // store the 'source' code of the contract - self.code_base.push(code); + let static_ref = Box::leak(code); + let raw_pointer = static_ref as *mut dyn Contract; + self.rust_codes.insert(code_id, raw_pointer); // store the additional code attributes like creator address and checksum self.code_data.insert( code_id, @@ -509,8 +628,8 @@ where } /// Returns the next contract's code identifier. - fn next_code_id(&self) -> Option { - self.code_data.keys().last().unwrap_or(&0u64).checked_add(1) + fn next_local_code_id(&self) -> Option { + Some((self.code_data.len() + 1 + LOCAL_CODE_OFFSET) as u64) } /// Executes the contract's `query` entry-point. @@ -522,6 +641,7 @@ where querier: &dyn Querier, block: &BlockInfo, msg: Vec, + querier_storage: QuerierStorage, ) -> AnyResult { self.with_storage_readonly( api, @@ -529,14 +649,33 @@ where querier, block, address, - |handler, deps, env| handler.query(deps, env, msg), + |handler, deps, env| match handler { + ContractBox::Borrowed(contract) => contract.query( + deps, + env.clone(), + msg, + self.fork_state(querier_storage, &env)?, + ), + ContractBox::Owned(contract) => contract.query( + deps, + env.clone(), + msg, + self.fork_state(querier_storage, &env)?, + ), + }, ) } /// Returns the value stored under specified key in contracts storage. pub fn query_raw(&self, address: Addr, storage: &dyn Storage, key: &[u8]) -> Binary { let storage = self.contract_storage(storage, &address); - let data = storage.get(key).unwrap_or_default(); + let data = storage + .get(key) + .or_else(|| { + WasmRemoteQuerier::raw_query(self.remote.clone().unwrap(), &address, key.into()) + .ok() + }) + .unwrap_or_default(); data.into() } @@ -624,6 +763,8 @@ where // then call the contract let info = MessageInfo { sender, funds }; + let querier_storage = router.get_querier_storage(storage)?; + let res = self.call_execute( api, storage, @@ -632,6 +773,7 @@ where block, info, msg.to_vec(), + querier_storage, )?; let custom_event = @@ -652,7 +794,6 @@ where } => self.process_wasm_msg_instantiate( api, storage, router, block, sender, admin, code_id, msg, funds, label, None, ), - #[cfg(feature = "cosmwasm_1_2")] WasmMsg::Instantiate2 { admin, code_id, @@ -680,10 +821,8 @@ where } => { let contract_addr = api.addr_validate(&contract_addr)?; - // check admin status and update the stored code_id - if new_code_id as usize > self.code_data.len() { - bail!("Cannot migrate contract to unregistered code id"); - } + // We don't check if the code exists here, the call_migrate hook, will take care of that + // This allows migrating to an on-chain code_id let mut data = self.contract_data(storage, &contract_addr)?; if data.admin != Some(sender) { bail!("Only admin can migrate contract: {:?}", data.admin); @@ -692,6 +831,7 @@ where self.save_contract(storage, &contract_addr, &data)?; // then call migrate + let querier_storage = router.get_querier_storage(storage)?; let res = self.call_migrate( contract_addr.clone(), api, @@ -699,6 +839,7 @@ where router, block, msg.to_vec(), + querier_storage, )?; let custom_event = Event::new("migrate") @@ -764,6 +905,7 @@ where // then call the contract let info = MessageInfo { sender, funds }; + let querier_storage = router.get_querier_storage(storage)?; let res = self.call_instantiate( contract_addr.clone(), api, @@ -772,6 +914,7 @@ where block, info, msg.to_vec(), + querier_storage, )?; let custom_event = Event::new("instantiate") @@ -811,7 +954,11 @@ where msg: SubMsg, ) -> AnyResult { let SubMsg { - msg, id, reply_on, .. + msg, + id, + reply_on, + payload, + .. } = msg; // execute in cache @@ -824,7 +971,7 @@ where if matches!(reply_on, ReplyOn::Always | ReplyOn::Success) { let reply = Reply { id, - payload: Default::default(), + payload, gas_used: 0, result: SubMsgResult::Ok( #[allow(deprecated)] @@ -850,7 +997,7 @@ where if matches!(reply_on, ReplyOn::Always | ReplyOn::Error) { let reply = Reply { id, - payload: Default::default(), + payload, gas_used: 0, result: SubMsgResult::Err(format!("{:?}", e)), }; @@ -883,6 +1030,7 @@ where let res = self.call_reply(contract.clone(), api, storage, router, block, reply)?; let (res, msgs) = self.build_app_response(&contract, custom_event, res); + self.process_response(api, router, storage, block, contract, res, msgs) } @@ -971,11 +1119,7 @@ where created: u64, salt: impl Into>, ) -> AnyResult { - // check if the contract's code with specified code_id exists - if code_id as usize > self.code_data.len() { - bail!("Cannot init contract with unregistered code id"); - } - + // We don't error if the code id doesn't exist, it allows us to instantiate remote contracts // generate a new contract address let instance_id = self.instance_count(storage) as u64; let addr = if let Some(salt_binary) = salt.into() { @@ -1024,6 +1168,7 @@ where block: &BlockInfo, info: MessageInfo, msg: Vec, + querier_storage: QuerierStorage, ) -> AnyResult> { Self::verify_response(self.with_storage( api, @@ -1031,7 +1176,22 @@ where router, block, address, - |contract, deps, env| contract.execute(deps, env, info, msg), + |contract, deps, env| match contract { + ContractBox::Borrowed(contract) => contract.execute( + deps, + env.clone(), + info, + msg, + self.fork_state(querier_storage, &env)?, + ), + ContractBox::Owned(contract) => contract.execute( + deps, + env.clone(), + info, + msg, + self.fork_state(querier_storage, &env)?, + ), + }, )?) } @@ -1045,6 +1205,7 @@ where block: &BlockInfo, info: MessageInfo, msg: Vec, + querier_storage: QuerierStorage, ) -> AnyResult> { Self::verify_response(self.with_storage( api, @@ -1052,7 +1213,22 @@ where router, block, address, - |contract, deps, env| contract.instantiate(deps, env, info, msg), + |contract, deps, env| match contract { + ContractBox::Borrowed(contract) => contract.instantiate( + deps, + env.clone(), + info, + msg, + self.fork_state(querier_storage, &env)?, + ), + ContractBox::Owned(contract) => contract.instantiate( + deps, + env.clone(), + info, + msg, + self.fork_state(querier_storage, &env)?, + ), + }, )?) } @@ -1066,13 +1242,27 @@ where block: &BlockInfo, reply: Reply, ) -> AnyResult> { + let querier_storage = router.get_querier_storage(storage)?; Self::verify_response(self.with_storage( api, storage, router, block, address, - |contract, deps, env| contract.reply(deps, env, reply), + |contract, deps, env| match contract { + ContractBox::Borrowed(contract) => contract.reply( + deps, + env.clone(), + reply, + self.fork_state(querier_storage, &env)?, + ), + ContractBox::Owned(contract) => contract.reply( + deps, + env.clone(), + reply, + self.fork_state(querier_storage, &env)?, + ), + }, )?) } @@ -1085,6 +1275,7 @@ where router: &dyn CosmosRouter, block: &BlockInfo, msg: Vec, + querier_storage: QuerierStorage, ) -> AnyResult> { Self::verify_response(self.with_storage( api, @@ -1092,7 +1283,20 @@ where router, block, address, - |contract, deps, env| contract.sudo(deps, env, msg), + |contract, deps, env| match contract { + ContractBox::Borrowed(contract) => contract.sudo( + deps, + env.clone(), + msg, + self.fork_state(querier_storage, &env)?, + ), + ContractBox::Owned(contract) => contract.sudo( + deps, + env.clone(), + msg, + self.fork_state(querier_storage, &env)?, + ), + }, )?) } @@ -1105,6 +1309,7 @@ where router: &dyn CosmosRouter, block: &BlockInfo, msg: Vec, + querier_storage: QuerierStorage, ) -> AnyResult> { Self::verify_response(self.with_storage( api, @@ -1112,7 +1317,20 @@ where router, block, address, - |contract, deps, env| contract.migrate(deps, env, msg), + |contract, deps, env| match contract { + ContractBox::Borrowed(contract) => contract.migrate( + deps, + env.clone(), + msg, + self.fork_state(querier_storage, &env)?, + ), + ContractBox::Owned(contract) => contract.migrate( + deps, + env.clone(), + msg, + self.fork_state(querier_storage, &env)?, + ), + }, )?) } @@ -1136,7 +1354,7 @@ where action: F, ) -> AnyResult where - F: FnOnce(&dyn Contract, Deps, Env) -> AnyResult, + F: FnOnce(ContractBox, Deps, Env) -> AnyResult, { let contract = self.contract_data(storage, &address)?; let handler = self.contract_code(contract.code_id)?; @@ -1161,7 +1379,7 @@ where action: F, ) -> AnyResult where - F: FnOnce(&dyn Contract, DepsMut, Env) -> AnyResult, + F: FnOnce(ContractBox, DepsMut, Env) -> AnyResult, ExecC: DeserializeOwned, { let contract = self.contract_data(storage, &address)?; @@ -1210,6 +1428,21 @@ where } } +impl ContainsRemote for WasmKeeper +where + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, +{ + fn with_remote(mut self, remote: RemoteChannel) -> Self { + self.set_remote(remote); + self + } + + fn set_remote(&mut self, remote: RemoteChannel) { + self.remote = Some(remote) + } +} + #[derive(Clone, PartialEq, Message)] struct InstantiateResponse { #[prost(string, tag = "1")] @@ -1255,6 +1488,7 @@ mod test { use crate::featured::staking::{DistributionKeeper, StakeKeeper}; use crate::module::FailingModule; use crate::test_helpers::{caller, error, payout}; + use crate::tests::remote_channel; use crate::transactions::StorageTransaction; use crate::{GovFailingModule, IbcFailingModule, StargateFailing}; use cosmwasm_std::testing::{message_info, mock_env, MockApi, MockQuerier, MockStorage}; @@ -1277,7 +1511,7 @@ mod test { >; fn wasm_keeper() -> WasmKeeper { - WasmKeeper::new() + WasmKeeper::new().with_remote(remote_channel()) } fn mock_router() -> BasicRouter { @@ -1365,6 +1599,7 @@ mod test { &block, info, b"{}".to_vec(), + QuerierStorage::default(), ) }) .unwrap_err(); @@ -1386,6 +1621,7 @@ mod test { &block, info, b"{}".to_vec(), + QuerierStorage::default(), ) }) .unwrap_err(); @@ -1427,7 +1663,7 @@ mod test { }; let contract_info = wasm_keeper - .query(&api, &wasm_storage, &querier, &block, query) + .query(&api, &wasm_storage, &mock_router(), &querier, &block, query) .unwrap(); let actual: ContractInfoResponse = from_json(contract_info).unwrap(); @@ -1448,7 +1684,7 @@ mod test { let querier: MockQuerier = MockQuerier::new(&[]); let query = WasmQuery::CodeInfo { code_id }; let code_info = wasm_keeper - .query(&api, &wasm_storage, &querier, &block, query) + .query(&api, &wasm_storage, &mock_router(), &querier, &block, query) .unwrap(); let actual: CodeInfoResponse = from_json(code_info).unwrap(); assert_eq!(code_id, actual.code_id); @@ -1474,10 +1710,24 @@ mod test { code_id: code_id_caller, }; let code_info_payout = wasm_keeper - .query(&api, &wasm_storage, &querier, &block, query_payout) + .query( + &api, + &wasm_storage, + &mock_router(), + &querier, + &block, + query_payout, + ) .unwrap(); let code_info_caller = wasm_keeper - .query(&api, &wasm_storage, &querier, &block, query_caller) + .query( + &api, + &wasm_storage, + &mock_router(), + &querier, + &block, + query_caller, + ) .unwrap(); let info_payout: CodeInfoResponse = from_json(code_info_payout).unwrap(); let info_caller: CodeInfoResponse = from_json(code_info_caller).unwrap(); @@ -1500,7 +1750,7 @@ mod test { let query = WasmQuery::CodeInfo { code_id: 100 }; wasm_keeper - .query(&api, &wasm_storage, &querier, &block, query) + .query(&api, &wasm_storage, &mock_router(), &querier, &block, query) .unwrap_err(); } @@ -1546,6 +1796,7 @@ mod test { &block, message_info(&user_addr, &[]), to_json_vec(&msg).unwrap(), + QuerierStorage::default(), ) .unwrap(); @@ -1608,6 +1859,7 @@ mod test { &block, info, init_msg, + QuerierStorage::default(), ) .unwrap(); assert_eq!(0, res.messages.len()); @@ -1623,6 +1875,7 @@ mod test { &block, info, b"{}".to_vec(), + QuerierStorage::default(), ) .unwrap(); assert_eq!(1, res.messages.len()); @@ -1641,7 +1894,15 @@ mod test { let query = to_json_vec(&payout::QueryMsg::Payout {}).unwrap(); let querier: MockQuerier = MockQuerier::new(&[]); let data = wasm_keeper - .query_smart(contract_addr, &api, &wasm_storage, &querier, &block, query) + .query_smart( + contract_addr, + &api, + &wasm_storage, + &querier, + &block, + query, + QuerierStorage::default(), + ) .unwrap(); let res: payout::InstantiateMessage = from_json(data).unwrap(); assert_eq!(res.payout, payout); @@ -1665,6 +1926,7 @@ mod test { &mock_env().block, info, b"{}".to_vec(), + QuerierStorage::default(), ) .unwrap(); assert_eq!(1, res.messages.len()); @@ -1727,6 +1989,7 @@ mod test { &block, info, init_msg, + QuerierStorage::default(), ) .unwrap(); @@ -1768,6 +2031,7 @@ mod test { &block, info, init_msg, + QuerierStorage::default(), ) .unwrap(); assert_payout(&wasm_keeper, cache, &contract2, &payout2); @@ -1804,6 +2068,7 @@ mod test { &block, info, init_msg, + QuerierStorage::default(), ) .unwrap(); assert_payout(&wasm_keeper, cache2, &contract3, &payout3); @@ -1847,6 +2112,7 @@ mod test { .query( &api, storage, + &mock_router(), &querier, &mock_env().block, WasmQuery::ContractInfo { @@ -1897,6 +2163,7 @@ mod test { &block, info, init_msg, + QuerierStorage::default(), ) .unwrap(); assert_eq!(0, res.messages.len()); diff --git a/src/wasm_emulation/api/mod.rs b/src/wasm_emulation/api/mod.rs new file mode 100644 index 00000000..707dea7f --- /dev/null +++ b/src/wasm_emulation/api/mod.rs @@ -0,0 +1,168 @@ +use crate::wasm_emulation::query::gas::{GAS_COST_CANONICALIZE, GAS_COST_HUMANIZE}; +use bech32::{Bech32, Hrp}; +use cosmwasm_std::Addr; +use cosmwasm_vm::{BackendApi, BackendError, GasInfo}; +use std::ops::AddAssign; + +const SHORT_CANON_LEN: usize = 20; +const LONG_CANON_LEN: usize = 32; + +pub fn bytes_from_bech32(address: &str, prefix: &Hrp) -> Result, BackendError> { + if address.is_empty() { + return Err(BackendError::Unknown { + msg: "empty address string is not allowed".to_string(), + }); + } + + let (hrp, data) = bech32::decode(address).map_err(|e| BackendError::Unknown { + msg: format!("Invalid Bech32 address : Err {}", e), + })?; + if hrp.ne(prefix) { + return Err(BackendError::Unknown { + msg: format!("invalid Bech32 prefix; expected {}, got {}", prefix, hrp), + }); + } + + Ok(data) +} + +pub const MAX_PREFIX_CHARS: usize = 10; +// Prefixes are limited to MAX_PREFIX_CHARS chars +// This allows one to specify a string prefix and still implement Copy +#[derive(Clone, Copy)] +pub struct RealApi { + pub prefix: [char; MAX_PREFIX_CHARS], +} + +impl RealApi { + pub fn new(prefix: &str) -> RealApi { + if prefix.len() > MAX_PREFIX_CHARS { + panic!("More chars in the prefix than {}", MAX_PREFIX_CHARS); + } + + let mut api_prefix = ['\0'; 10]; + for (i, c) in prefix.chars().enumerate() { + api_prefix[i] = c; + } + Self { prefix: api_prefix } + } + + pub fn get_prefix(&self) -> Result { + let mut prefix = Vec::new(); + + for &c in self.prefix.iter() { + if c != '\0' { + prefix.push(c); + } + } + let prefix_string: String = prefix.into_iter().collect(); + Hrp::parse(&prefix_string).map_err(|e| BackendError::Unknown { msg: e.to_string() }) + } + + pub fn next_address(&self, count: usize) -> Addr { + let mut canon = format!("ADDRESS_{}", count).as_bytes().to_vec(); + canon.resize(SHORT_CANON_LEN, 0); + Addr::unchecked(self.addr_humanize(&canon).0.unwrap()) + } + + pub fn next_contract_address(&self, count: usize) -> Addr { + let mut canon = format!("CONTRACT_{}", count).as_bytes().to_vec(); + canon.resize(LONG_CANON_LEN, 0); + Addr::unchecked(self.addr_humanize(&canon).0.unwrap()) + } +} + +impl BackendApi for RealApi { + fn addr_validate(&self, input: &str) -> cosmwasm_vm::BackendResult<()> { + let (canon, mut gas_cost) = self.addr_canonicalize(input); + + if let Err(e) = canon { + return (Err(e), gas_cost); + } + let canon = canon.unwrap(); + + let (new_human, human_gas_cost) = self.addr_humanize(&canon); + + if let Err(e) = new_human { + gas_cost.add_assign(human_gas_cost); + return (Err(e), gas_cost); + } + let new_human = new_human.unwrap(); + + if input == new_human { + (Ok(()), gas_cost) + } else { + ( + Err(BackendError::user_err(format!( + "Address invalid : {}", + input + ))), + gas_cost, + ) + } + } + + fn addr_canonicalize(&self, human: &str) -> cosmwasm_vm::BackendResult> { + let gas_cost = GasInfo::with_externally_used(GAS_COST_CANONICALIZE); + if human.trim().is_empty() { + return ( + Err(BackendError::Unknown { + msg: "empty address string is not allowed".to_string(), + }), + gas_cost, + ); + } + + ( + self.get_prefix() + .and_then(|prefix| bytes_from_bech32(human, &prefix)), + gas_cost, + ) + } + + fn addr_humanize(&self, canonical: &[u8]) -> cosmwasm_vm::BackendResult { + let gas_cost = GasInfo::with_externally_used(GAS_COST_HUMANIZE); + + if canonical.len() != SHORT_CANON_LEN && canonical.len() != LONG_CANON_LEN { + return ( + Err(BackendError::Unknown { + msg: "Canon address doesn't have the right length".to_string(), + }), + gas_cost, + ); + } + + if canonical.is_empty() { + return (Ok("".to_string()), gas_cost); + } + + let human = self.get_prefix().and_then(|prefix| { + bech32::encode::(prefix, canonical) + .map_err(|e| BackendError::Unknown { msg: e.to_string() }) + }); + + (human, gas_cost) + } +} + +#[cfg(test)] +mod test { + use super::RealApi; + + #[test] + fn prefix() { + let prefix = "migaloo"; + + let api = RealApi::new(prefix); + + let final_prefix = api.get_prefix(); + assert_eq!(prefix, final_prefix.unwrap().as_str()); + } + + #[test] + #[should_panic] + fn too_long_prefix() { + let prefix = "migaloowithotherchars"; + RealApi::new(prefix); + } +} diff --git a/src/wasm_emulation/channel.rs b/src/wasm_emulation/channel.rs new file mode 100644 index 00000000..041e5b97 --- /dev/null +++ b/src/wasm_emulation/channel.rs @@ -0,0 +1,48 @@ +use anyhow::Result as AnyResult; +use cw_orch_daemon::GrpcChannel; +use tokio::runtime::{Handle, Runtime}; +use tonic::transport::Channel; + +/// Simple helper to get the GRPC transport channel +fn get_channel( + grpc_urls: &[String], + chain_id: impl Into, + rt: &Runtime, +) -> anyhow::Result { + let channel = rt.block_on(GrpcChannel::connect(grpc_urls, &chain_id.into()))?; + Ok(channel) +} + +#[derive(Clone)] +pub struct RemoteChannel { + pub rt: Handle, + pub channel: Channel, + pub pub_address_prefix: String, + // For caching + pub chain_id: String, +} + +impl RemoteChannel { + pub fn new( + rt: &Runtime, + grpc_urls: &[&str], + chain_id: impl Into, + pub_address_prefix: impl Into, + ) -> AnyResult { + let chain_id = chain_id.into(); + Ok(Self { + rt: rt.handle().clone(), + channel: get_channel( + &grpc_urls + .iter() + .cloned() + .map(Into::into) + .collect::>(), + chain_id.clone(), + rt, + )?, + pub_address_prefix: pub_address_prefix.into(), + chain_id, + }) + } +} diff --git a/src/wasm_emulation/contract.rs b/src/wasm_emulation/contract.rs new file mode 100644 index 00000000..a3e3c5e3 --- /dev/null +++ b/src/wasm_emulation/contract.rs @@ -0,0 +1,594 @@ +use crate::wasm_emulation::{ + api::RealApi, + input::{InstanceArguments, ReplyArgs}, + instance::instance_from_reused_module, + output::{StorageChanges, WasmRunnerOutput}, + query::MockQuerier, + storage::DualStorage, +}; +use cosmwasm_std::{ + Binary, Checksum, CustomMsg, CustomQuery, Deps, DepsMut, Env, MessageInfo, Order, Reply, + Response, StdError, Storage, +}; +use cosmwasm_vm::{ + call_execute, call_instantiate, call_migrate, call_query, call_reply, call_sudo, + internals::check_wasm, Backend, BackendApi, Instance, InstanceOptions, Querier, WasmLimits, +}; +use cw_orch_daemon::queriers::CosmWasm; + +use serde::de::DeserializeOwned; +use wasmer::Engine; +use wasmer::Module; + +use std::collections::HashSet; + +use crate::Contract; + +use anyhow::Result as AnyResult; + +use super::{ + channel::RemoteChannel, + input::{ExecuteArgs, InstantiateArgs, MigrateArgs, QueryArgs, SudoArgs, WasmFunction}, + instance::create_module, + output::WasmOutput, + query::mock_querier::ForkState, +}; + +fn apply_storage_changes(storage: &mut dyn Storage, output: &WasmRunnerOutput) { + // We change all the values with the output + for (key, value) in &output.storage.current_keys { + storage.set(key, value); + } + + // We remove all values that need to be removed from it + for key in &output.storage.removed_keys { + storage.remove(key); + } +} + +/// Taken from cosmwasm_vm::testing +/// This gas limit is used in integration tests and should be high enough to allow a reasonable +/// number of contract executions and queries on one instance. For this reason it is significatly +/// higher than the limit for a single execution that we have in the production setup. +const DEFAULT_GAS_LIMIT: u64 = 500_000_000_000_000; // ~0.5s + +#[derive(Debug, Clone)] +pub struct DistantCodeId { + pub code_id: u64, + pub module: (Engine, Module), +} + +#[derive(Clone)] +pub struct LocalWasmContract { + pub code: Vec, + pub module: (Engine, Module), +} + +#[derive(Debug, Clone)] +pub enum WasmContract { + Local(LocalWasmContract), + DistantCodeId(DistantCodeId), +} + +impl std::fmt::Debug for LocalWasmContract { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!( + f, + "LocalContract {{ checksum: {} }}", + Checksum::generate(&self.code), + ) + } +} + +impl WasmContract { + pub fn new_local(code: Vec) -> Self { + check_wasm( + &code, + &HashSet::from([ + "iterator".to_string(), + "staking".to_string(), + "stargate".to_string(), + ]), + &WasmLimits::default(), + cosmwasm_vm::internals::Logger::Off, + ) + .unwrap(); + Self::Local(LocalWasmContract { + code: code.clone(), + module: create_module(&code).unwrap(), + }) + } + + pub fn new_distant_code_id(code_id: u64, remote: RemoteChannel) -> Self { + let code = { + let wasm_querier = CosmWasm::new_sync(remote.channel.clone(), &remote.rt); + + let cache_key = format!("{}:{}", remote.chain_id, &code_id); + + let code = wasm_caching::maybe_cached_wasm(cache_key, || { + remote + .rt + .block_on(wasm_querier._code_data(code_id)) + .map_err(|e| e.into()) + }) + .unwrap(); + + code + }; + Self::DistantCodeId(DistantCodeId { + code_id, + module: create_module(&code).unwrap(), + }) + } + + pub fn get_module(&self) -> (Engine, Module) { + match self { + WasmContract::Local(LocalWasmContract { module, .. }) => module.clone(), + WasmContract::DistantCodeId(DistantCodeId { module, .. }) => module.clone(), + } + } + + pub fn run_contract< + QueryC: CustomQuery + DeserializeOwned + 'static, + ExecC: CustomMsg + DeserializeOwned, + >( + &self, + args: InstanceArguments, + fork_state: ForkState, + ) -> AnyResult> { + let InstanceArguments { + function, + init_storage, + } = args; + let address = function.get_address(); + let module = self.get_module(); + + let api = RealApi::new(&fork_state.remote.pub_address_prefix); + + // We create the backend here from outside information; + let backend = Backend { + api, + storage: DualStorage::new( + fork_state.remote.clone(), + address.to_string(), + Some(init_storage), + )?, + querier: MockQuerier::::new(fork_state), + }; + let options = InstanceOptions { + gas_limit: DEFAULT_GAS_LIMIT, + }; + + // Then we create the instance + + let mut instance = instance_from_reused_module(module, backend, options)?; + + let gas_before = instance.get_gas_left(); + + // Then we call the function that we wanted to call + let result = execute_function(&mut instance, function)?; + + let gas_after = instance.get_gas_left(); + + // We return the code response + any storage change (or the whole local storage object), with serializing + let mut recycled_instance = instance.recycle().unwrap(); + + let wasm_result = WasmRunnerOutput { + storage: StorageChanges { + current_keys: recycled_instance.storage.get_all_storage()?, + removed_keys: recycled_instance.storage.removed_keys.into_iter().collect(), + }, + gas_used: gas_before - gas_after, + wasm: result, + }; + + Ok(wasm_result) + } + + pub fn after_execution_callback(&self, output: &WasmRunnerOutput) { + // We log the gas used + let operation = match output.wasm { + WasmOutput::Execute(_) => "execution", + WasmOutput::Query(_) => "query", + WasmOutput::Instantiate(_) => "instantiation", + WasmOutput::Migrate(_) => "migration", + WasmOutput::Sudo(_) => "sudo", + WasmOutput::Reply(_) => "reply", + }; + log::debug!( + "Gas used {:?} for {:} on contract {:?}", + output.gas_used, + operation, + self + ); + } + + pub fn code_id(&self) -> u64 { + match self { + WasmContract::Local(_) => unimplemented!(), + WasmContract::DistantCodeId(d) => d.code_id, + } + } +} + +impl Contract for WasmContract +where + ExecC: CustomMsg + DeserializeOwned, + QueryC: CustomQuery + DeserializeOwned, +{ + fn execute( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult> { + // We start by building the dependencies we will pass through the wasm executer + let execute_args = InstanceArguments { + function: WasmFunction::Execute(ExecuteArgs { env, info, msg }), + init_storage: deps.storage.range(None, None, Order::Ascending).collect(), + }; + + let decoded_result = self.run_contract(execute_args, fork_state)?; + + apply_storage_changes(deps.storage, &decoded_result); + self.after_execution_callback(&decoded_result); + + match decoded_result.wasm { + WasmOutput::Execute(x) => Ok(x), + _ => panic!("Wrong kind of answer from wasm container"), + } + } + + fn instantiate( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult> { + // We start by building the dependencies we will pass through the wasm executer + let instantiate_arguments = InstanceArguments { + function: WasmFunction::Instantiate(InstantiateArgs { env, info, msg }), + init_storage: deps.storage.range(None, None, Order::Ascending).collect(), + }; + + let decoded_result = self.run_contract(instantiate_arguments, fork_state)?; + + apply_storage_changes(deps.storage, &decoded_result); + self.after_execution_callback(&decoded_result); + + match decoded_result.wasm { + WasmOutput::Instantiate(x) => Ok(x), + _ => panic!("Wrong kind of answer from wasm container"), + } + } + + fn query( + &self, + deps: Deps, + env: Env, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult { + // We start by building the dependencies we will pass through the wasm executer + let query_arguments = InstanceArguments { + function: WasmFunction::Query(QueryArgs { env, msg }), + init_storage: deps.storage.range(None, None, Order::Ascending).collect(), + }; + + let decoded_result: WasmRunnerOutput = + self.run_contract(query_arguments, fork_state)?; + + self.after_execution_callback(&decoded_result); + + match decoded_result.wasm { + WasmOutput::Query(x) => Ok(x), + _ => panic!("Wrong kind of answer from wasm container"), + } + } + + // this returns an error if the contract doesn't implement sudo + fn sudo( + &self, + deps: DepsMut, + env: Env, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult> { + let sudo_args = InstanceArguments { + function: WasmFunction::Sudo(SudoArgs { env, msg }), + init_storage: deps.storage.range(None, None, Order::Ascending).collect(), + }; + + let decoded_result = self.run_contract(sudo_args, fork_state)?; + + apply_storage_changes(deps.storage, &decoded_result); + self.after_execution_callback(&decoded_result); + + match decoded_result.wasm { + WasmOutput::Sudo(x) => Ok(x), + _ => panic!("Wrong kind of answer from wasm container"), + } + } + + // this returns an error if the contract doesn't implement reply + fn reply( + &self, + deps: DepsMut, + env: Env, + reply: Reply, + fork_state: ForkState, + ) -> AnyResult> { + let reply_args = InstanceArguments { + function: WasmFunction::Reply(ReplyArgs { env, reply }), + init_storage: deps.storage.range(None, None, Order::Ascending).collect(), + }; + + let decoded_result = self.run_contract(reply_args, fork_state)?; + + apply_storage_changes(deps.storage, &decoded_result); + self.after_execution_callback(&decoded_result); + + match decoded_result.wasm { + WasmOutput::Reply(x) => Ok(x), + _ => panic!("Wrong kind of answer from wasm container"), + } + } + + // this returns an error if the contract doesn't implement migrate + fn migrate( + &self, + deps: DepsMut, + env: Env, + msg: Vec, + fork_state: ForkState, + ) -> AnyResult> { + let migrate_args = InstanceArguments { + function: WasmFunction::Migrate(MigrateArgs { env, msg }), + init_storage: deps.storage.range(None, None, Order::Ascending).collect(), + }; + + let decoded_result = self.run_contract(migrate_args, fork_state)?; + + apply_storage_changes(deps.storage, &decoded_result); + self.after_execution_callback(&decoded_result); + + match decoded_result.wasm { + WasmOutput::Migrate(x) => Ok(x), + _ => panic!("Wrong kind of answer from wasm container"), + } + } +} + +pub fn execute_function< + A: BackendApi + 'static, + B: cosmwasm_vm::Storage + 'static, + C: Querier + 'static, + ExecC: CustomMsg + DeserializeOwned, +>( + instance: &mut Instance, + function: WasmFunction, +) -> AnyResult> { + match function { + WasmFunction::Execute(args) => { + let result = call_execute(instance, &args.env, &args.info, &args.msg)? + .into_result() + .map_err(StdError::generic_err)?; + Ok(WasmOutput::Execute(result)) + } + WasmFunction::Query(args) => { + let result = call_query(instance, &args.env, &args.msg)? + .into_result() + .map_err(StdError::generic_err)?; + Ok(WasmOutput::Query(result)) + } + WasmFunction::Instantiate(args) => { + let result = call_instantiate(instance, &args.env, &args.info, &args.msg)? + .into_result() + .map_err(StdError::generic_err)?; + Ok(WasmOutput::Instantiate(result)) + } + WasmFunction::Reply(args) => { + let result = call_reply(instance, &args.env, &args.reply)? + .into_result() + .map_err(StdError::generic_err)?; + Ok(WasmOutput::Reply(result)) + } + WasmFunction::Migrate(args) => { + let result = call_migrate(instance, &args.env, &args.msg)? + .into_result() + .map_err(StdError::generic_err)?; + Ok(WasmOutput::Migrate(result)) + } + WasmFunction::Sudo(args) => { + let result = call_sudo(instance, &args.env, &args.msg)? + .into_result() + .map_err(StdError::generic_err)?; + Ok(WasmOutput::Sudo(result)) + } + } +} + +mod wasm_caching { + use super::*; + + use std::{ + env, fs, + io::{Read, Seek}, + os::unix::fs::FileExt, + path::PathBuf, + }; + + use anyhow::{bail, Context}; + + const WASM_CACHE_DIR: &str = "wasm_cache"; + const WASM_CACHE_ENV: &str = "WASM_CACHE"; + + static CARGO_TARGET_DIR: std::sync::OnceLock = std::sync::OnceLock::new(); + + pub(crate) fn cargo_target_dir() -> &'static PathBuf { + CARGO_TARGET_DIR.get_or_init(|| { + cargo_metadata::MetadataCommand::new() + .no_deps() + .exec() + .unwrap() + .target_directory + .into() + }) + } + + #[repr(u8)] + enum WasmCachingStatus { + /// Currently writing + Writing, + /// This wasm ready for use + Ready, + /// Writing to it have failed, it's not usable until valid cache written to it + Corrupted, + } + + impl From for WasmCachingStatus { + fn from(value: u8) -> Self { + match value { + 0 => WasmCachingStatus::Writing, + 1 => WasmCachingStatus::Ready, + 2 => WasmCachingStatus::Corrupted, + _ => unimplemented!(), + } + } + } + + impl WasmCachingStatus { + pub fn set_status(self, file: &fs::File) { + file.write_at(&[self as u8], 0) + .expect("Failed to update wasm caching status"); + } + + pub fn status(file: &fs::File) -> Self { + let buf = &mut [0]; + match file.read_at(buf, 0) { + Ok(_) => buf[0].into(), + Err(_) => WasmCachingStatus::Corrupted, + } + } + } + + /// Returns wasm bytes for the contract + /// + /// Will get cached wasm stored in `CARGO_TARGET_DIR` (./target/ from project root by default) + /// This feature can be disabled by setting wasm cache environment variable to `false`: `WASM_CACHE=false` + /// + /// # Arguments + /// + /// * `key` - A string key that represents unique id of the contract (usually chain-id:code_id) + /// * `wasm_code_bytes` - Function that returns non-cached version of the contract + /// + /// # Scenarios + /// + /// - Wasm caching disabled: return result of the `wasm_code_bytes` function + /// - Wasm file not found in cache location: return result of the `wasm_code_bytes` function, saving cache on on success + /// - Wasm stored in cache: return bytes + pub(crate) fn maybe_cached_wasm AnyResult>>( + key: String, + wasm_code_bytes: F, + ) -> AnyResult> { + let wasm_cache_enabled = env::var(WASM_CACHE_ENV) + .ok() + .and_then(|wasm_cache| wasm_cache.parse().ok()) + .unwrap_or(true); + + // Wasm caching disabled, return function result + if !wasm_cache_enabled { + return wasm_code_bytes(); + } + + let wasm_cache_dir = cargo_target_dir().join(WASM_CACHE_DIR); + // Prepare cache directory in `./target/` + match fs::metadata(&wasm_cache_dir) { + // Verify it's dir + Ok(wasm_cache_metadata) => { + if !wasm_cache_metadata.is_dir() { + bail!("{WASM_CACHE_DIR} supposed to be directory") + } + } + // Error on checking cache dir, try to create it + Err(_) => { + // We try to create the dir silently, it's ok if it fails, the error will pop-up later if there's an issue + let _ = fs::create_dir(&wasm_cache_dir); + } + } + + let cached_wasm_file = wasm_cache_dir.join(key); + let wasm_bytes = match fs::metadata(&cached_wasm_file) { + // Cache file exists, try to read it + Ok(_) => { + let mut file = + fs::File::open(&cached_wasm_file).context("unable to open wasm cache file")?; + // If someone is writing to it we need to wait, and then check again + // TODO: decide what is the best way to wait for it + let mut status = WasmCachingStatus::status(&file); + if let WasmCachingStatus::Writing = status { + let options = file_lock::FileOptions::new().read(true); + // Blocking lock until writer unlocks it + let file_lock = file_lock::FileLock::lock(&cached_wasm_file, true, options)?; + status = WasmCachingStatus::status(&file_lock.file); + file_lock.unlock()? + } + match status { + WasmCachingStatus::Ready => { + let mut buf = vec![]; + file.seek(std::io::SeekFrom::Start(1))?; + file.read_to_end(&mut buf) + .context("unable to open wasm cache file")?; + buf + } + // Ready for read + // Corrupted, need to write new wasm + WasmCachingStatus::Corrupted => { + store_new_wasm(wasm_code_bytes, &cached_wasm_file)? + } + // Someone dropped file lock with caching status Writing it means it's corrupted + WasmCachingStatus::Writing => { + store_new_wasm(wasm_code_bytes, &cached_wasm_file)? + } + } + } + // Error on checking cache dir, get wasm bytes and try to cache it + Err(_) => store_new_wasm(wasm_code_bytes, &cached_wasm_file)?, + }; + Ok(wasm_bytes) + } + + fn store_new_wasm AnyResult>>( + wasm_code_bytes: F, + cached_wasm_file: &PathBuf, + ) -> Result, anyhow::Error> { + let options = file_lock::FileOptions::new().create(true).write(true); + let file_lock_status = file_lock::FileLock::lock(cached_wasm_file, false, options); + let wasm = wasm_code_bytes()?; + if let Err(cache_save_err) = file_lock_status.and_then(|file_lock| { + // Set writing status + WasmCachingStatus::Writing.set_status(&file_lock.file); + + match file_lock.file.write_all_at(&wasm, 1) { + // Done writing, set ready status + Ok(()) => { + WasmCachingStatus::Ready.set_status(&file_lock.file); + Ok(()) + } + // Failed to write, set corrupted status + Err(e) => { + WasmCachingStatus::Corrupted.set_status(&file_lock.file); + Err(e) + } + } + }) { + // It's not critical if it fails, as we already have wasm bytes, so we just log it + log::error!(target: "wasm_caching", "Failed to save wasm cache: {cache_save_err}") + } + Ok(wasm) + } +} diff --git a/src/wasm_emulation/input.rs b/src/wasm_emulation/input.rs new file mode 100644 index 00000000..67cd832d --- /dev/null +++ b/src/wasm_emulation/input.rs @@ -0,0 +1,116 @@ +use std::collections::{BTreeMap, HashMap}; + +use cosmwasm_std::Addr; +use cosmwasm_std::{Env, MessageInfo, Reply}; + +use cw_utils::NativeBalance; + +use crate::prefixed_storage::get_full_contract_storage_namespace; +use crate::wasm::{CodeData, ContractData}; + +use super::contract::WasmContract; + +#[derive(Debug, Clone, Default)] +pub struct WasmStorage { + pub contracts: HashMap, + pub codes: BTreeMap, + pub code_data: BTreeMap, + pub storage: Vec<(Vec, Vec)>, +} + +impl WasmStorage { + pub fn get_contract_storage(&self, contract_addr: &Addr) -> Vec<(Vec, Vec)> { + let namespace = + get_full_contract_storage_namespace(&Addr::unchecked(contract_addr)).to_vec(); + let namespace_len = namespace.len(); + let keys: Vec<(Vec, Vec)> = self + .storage + .iter() + // We filter only value in this namespace + .filter(|(k, _)| k.len() >= namespace_len && k[..namespace_len] == namespace) + .cloned() + // We remove the namespace prefix from the key + .map(|(k, value)| (k[namespace_len..].to_vec(), value)) + .collect(); + + keys + } +} + +#[derive(Debug, Clone, Default)] +pub struct BankStorage { + pub storage: Vec<(Addr, NativeBalance)>, +} + +#[derive(Clone, Default)] +pub struct QuerierStorage { + pub wasm: WasmStorage, + pub bank: BankStorage, +} + +#[derive(Debug)] +pub struct InstanceArguments { + pub function: WasmFunction, + pub init_storage: Vec<(Vec, Vec)>, +} + +#[derive(Debug)] +pub enum WasmFunction { + Execute(ExecuteArgs), + Instantiate(InstantiateArgs), + Query(QueryArgs), + Sudo(SudoArgs), + Reply(ReplyArgs), + Migrate(MigrateArgs), +} + +#[derive(Debug)] +pub struct ExecuteArgs { + pub env: Env, + pub info: MessageInfo, + pub msg: Vec, +} + +#[derive(Debug)] +pub struct InstantiateArgs { + pub env: Env, + pub info: MessageInfo, + pub msg: Vec, +} + +#[derive(Debug)] +pub struct QueryArgs { + pub env: Env, + pub msg: Vec, +} + +#[derive(Debug)] +pub struct SudoArgs { + pub env: Env, + pub msg: Vec, +} + +#[derive(Debug)] +pub struct ReplyArgs { + pub env: Env, + pub reply: Reply, +} + +#[derive(Debug)] +pub struct MigrateArgs { + pub env: Env, + pub msg: Vec, +} + +impl WasmFunction { + pub fn get_address(&self) -> Addr { + match self { + WasmFunction::Execute(ExecuteArgs { env, .. }) => env.contract.address.clone(), + WasmFunction::Instantiate(InstantiateArgs { env, .. }) => env.contract.address.clone(), + WasmFunction::Query(QueryArgs { env, .. }) => env.contract.address.clone(), + WasmFunction::Reply(ReplyArgs { env, .. }) => env.contract.address.clone(), + WasmFunction::Sudo(SudoArgs { env, .. }) => env.contract.address.clone(), + WasmFunction::Migrate(MigrateArgs { env, .. }) => env.contract.address.clone(), + } + } +} diff --git a/src/wasm_emulation/instance.rs b/src/wasm_emulation/instance.rs new file mode 100644 index 00000000..41237207 --- /dev/null +++ b/src/wasm_emulation/instance.rs @@ -0,0 +1,26 @@ +use cosmwasm_vm::internals::compile; +use cosmwasm_vm::internals::{instance_from_module, make_compiling_engine}; +use cosmwasm_vm::{Backend, BackendApi, Instance, InstanceOptions, Querier, Storage, VmResult}; +use wasmer::{Engine, Module, Store}; + +pub fn create_module(code: &[u8]) -> VmResult<(Engine, Module)> { + let engine = make_compiling_engine(None); + let module = compile(&engine, code)?; + Ok((engine, module)) +} + +/// This is the only Instance constructor that can be called from outside of cosmwasm-vm, +/// e.g. in test code that needs a customized variant of cosmwasm_vm::testing::mock_instance*. +pub fn instance_from_reused_module( + (engine, module): (Engine, Module), + backend: Backend, + options: InstanceOptions, +) -> VmResult> +where + A: BackendApi + 'static, + S: Storage + 'static, + Q: Querier + 'static, +{ + let store = Store::new(engine); + instance_from_module(store, &module, backend, options.gas_limit, None) +} diff --git a/src/wasm_emulation/mod.rs b/src/wasm_emulation/mod.rs new file mode 100644 index 00000000..5a3b7ced --- /dev/null +++ b/src/wasm_emulation/mod.rs @@ -0,0 +1,11 @@ +pub mod input; +pub mod output; +pub mod query; +pub mod storage; + +pub mod channel; +pub mod contract; + +pub mod api; + +pub mod instance; diff --git a/src/wasm_emulation/output.rs b/src/wasm_emulation/output.rs new file mode 100644 index 00000000..d46c2bfb --- /dev/null +++ b/src/wasm_emulation/output.rs @@ -0,0 +1,24 @@ +use cosmwasm_std::{Binary, Response}; + +#[derive(Debug)] +pub enum WasmOutput { + Execute(Response), + Instantiate(Response), + Query(Binary), + Sudo(Response), + Reply(Response), + Migrate(Response), +} + +#[derive(Debug)] +pub struct StorageChanges { + pub current_keys: Vec<(Vec, Vec)>, + pub removed_keys: Vec>, +} + +#[derive(Debug)] +pub struct WasmRunnerOutput { + pub wasm: WasmOutput, + pub storage: StorageChanges, + pub gas_used: u64, +} diff --git a/src/wasm_emulation/query/bank.rs b/src/wasm_emulation/query/bank.rs new file mode 100644 index 00000000..2a577bd1 --- /dev/null +++ b/src/wasm_emulation/query/bank.rs @@ -0,0 +1,155 @@ +use crate::wasm_emulation::channel::RemoteChannel; +use crate::wasm_emulation::query::gas::{GAS_COST_ALL_BALANCE_QUERY, GAS_COST_BALANCE_QUERY}; +use crate::wasm_emulation::query::mock_querier::QueryResultWithGas; +use cosmwasm_std::{Addr, SupplyResponse}; +use cosmwasm_vm::GasInfo; + +use cw_utils::NativeBalance; + +use cw_orch_daemon::queriers::Bank; + +use cosmwasm_std::Binary; +use cosmwasm_std::Coin; +use std::collections::HashMap; + +use cosmwasm_std::Uint128; +use cosmwasm_std::{AllBalanceResponse, BalanceResponse, BankQuery}; + +use cosmwasm_std::to_json_binary; +use cosmwasm_std::{ContractResult, SystemResult}; + +use super::gas::GAS_COST_SUPPLY_QUERY; + +#[derive(Clone)] +pub struct BankQuerier { + #[allow(dead_code)] + /// HashMap + supplies: HashMap, + /// HashMap + balances: HashMap>, + remote: RemoteChannel, +} + +impl BankQuerier { + pub fn new(remote: RemoteChannel, init: Vec<(Addr, NativeBalance)>) -> Self { + let balances: HashMap<_, _> = init + .iter() + .map(|(s, c)| (s.to_string(), c.clone().into_vec())) + .collect(); + + BankQuerier { + supplies: Self::calculate_supplies(&balances), + balances, + remote, + } + } + + pub fn update_balance( + &mut self, + addr: impl Into, + balance: Vec, + ) -> Option> { + let result = self.balances.insert(addr.into(), balance); + self.supplies = Self::calculate_supplies(&self.balances); + + result + } + + fn calculate_supplies(balances: &HashMap>) -> HashMap { + let mut supplies = HashMap::new(); + + let all_coins = balances.iter().flat_map(|(_, coins)| coins); + + for coin in all_coins { + *supplies + .entry(coin.denom.clone()) + .or_insert_with(Uint128::zero) += coin.amount; + } + + supplies + } + + pub fn query(&self, request: &BankQuery) -> QueryResultWithGas { + let contract_result: ContractResult = match request { + BankQuery::Balance { address, denom } => { + // proper error on not found, serialize result on found + let mut amount = self + .balances + .get(address) + .and_then(|v| v.iter().find(|c| &c.denom == denom).map(|c| c.amount)); + + // If the amount is not available, we query it from the distant chain + if amount.is_none() { + let querier = Bank { + channel: self.remote.channel.clone(), + rt_handle: Some(self.remote.rt.clone()), + }; + + let query_result = self + .remote + .rt + .block_on(querier._balance(&Addr::unchecked(address), Some(denom.clone()))); + if let Ok(distant_amount) = query_result { + amount = Some(distant_amount[0].amount) + } + } + + let bank_res = BalanceResponse::new(Coin { + amount: amount.unwrap(), + denom: denom.to_string(), + }); + to_json_binary(&bank_res).into() + } + BankQuery::AllBalances { address } => { + // proper error on not found, serialize result on found + let mut amount = self.balances.get(address).cloned(); + + // We query only if the bank balance doesn't exist + if amount.is_none() { + let querier = Bank { + channel: self.remote.channel.clone(), + rt_handle: Some(self.remote.rt.clone()), + }; + let query_result: Result, _> = self + .remote + .rt + .block_on(querier._balance(&Addr::unchecked(address), None)); + if let Ok(distant_amount) = query_result { + amount = Some(distant_amount) + } + } + + let bank_res = AllBalanceResponse::new(amount.unwrap()); + to_json_binary(&bank_res).into() + } + BankQuery::Supply { denom } => { + let supply_clone = self.supplies.get(denom).cloned().unwrap_or_default(); + let querier = Bank { + channel: self.remote.channel.clone(), + rt_handle: Some(self.remote.rt.clone()), + }; + let query_result: Result = + self.remote.rt.block_on(querier._supply_of(denom)); + let mut supply = query_result.unwrap_or(Coin::new(Uint128::zero(), denom)); + supply.amount += supply_clone; + let supply_res = SupplyResponse::new(supply); + to_json_binary(&supply_res).into() + } + &_ => panic!("Not implemented {:?}", request), + }; + + // We handle the gas_info + let gas_info = match request { + BankQuery::Balance { .. } => GAS_COST_BALANCE_QUERY, + BankQuery::AllBalances { .. } => GAS_COST_ALL_BALANCE_QUERY, + BankQuery::Supply { .. } => GAS_COST_SUPPLY_QUERY, + &_ => panic!("Not implemented {:?}", request), + }; + + // system result is always ok in the mock implementation + ( + SystemResult::Ok(contract_result), + GasInfo::with_externally_used(gas_info), + ) + } +} diff --git a/src/wasm_emulation/query/gas.rs b/src/wasm_emulation/query/gas.rs new file mode 100644 index 00000000..5ee4c613 --- /dev/null +++ b/src/wasm_emulation/query/gas.rs @@ -0,0 +1,25 @@ +// Bank +pub const GAS_COST_BALANCE_QUERY: u64 = 1000; +pub const GAS_COST_ALL_BALANCE_QUERY: u64 = 10000; +pub const GAS_COST_SUPPLY_QUERY: u64 = 1000; + +// Staking +pub const GAS_COST_BONDED_DENOM: u64 = 100; +pub const GAS_COST_ALL_VALIDATORS: u64 = 10000; +pub const GAS_COST_VALIDATOR: u64 = 1000; +pub const GAS_COST_ALL_DELEGATIONS: u64 = 10000; +pub const GAS_COST_DELEGATIONS: u64 = 1000; + +// Wasm +pub const GAS_COST_CONTRACT_INFO: u64 = 1000; +pub const GAS_COST_RAW_COSMWASM_QUERY: u64 = 10000; + +// ERROR +pub const GAS_COST_QUERY_ERROR: u64 = 1000; + +// API (from cosmwasm_vm directly) +pub const GAS_COST_HUMANIZE: u64 = 44; +pub const GAS_COST_CANONICALIZE: u64 = 55; + +// All queries (test) +pub const GAS_COST_ALL_QUERIES: u64 = 10_000; diff --git a/src/wasm_emulation/query/mock_querier.rs b/src/wasm_emulation/query/mock_querier.rs new file mode 100644 index 00000000..7e1ebf13 --- /dev/null +++ b/src/wasm_emulation/query/mock_querier.rs @@ -0,0 +1,224 @@ +use std::collections::HashMap; + +use crate::wasm_emulation::channel::RemoteChannel; +use crate::wasm_emulation::query::bank::BankQuerier; +use crate::wasm_emulation::query::staking::StakingQuerier; +use crate::wasm_emulation::query::wasm::WasmQuerier; + +use cosmwasm_std::CustomMsg; +use cosmwasm_std::Env; +use cosmwasm_vm::BackendResult; +use cosmwasm_vm::GasInfo; + +use serde::de::DeserializeOwned; + +use cosmwasm_std::Binary; +use cosmwasm_std::Coin; + +use cosmwasm_std::SystemError; + +use cosmwasm_std::from_json; +use cosmwasm_std::{ContractResult, SystemResult}; +use cosmwasm_std::{CustomQuery, QueryRequest}; +use cosmwasm_std::{FullDelegation, Validator}; + +use cosmwasm_std::Attribute; +use cosmwasm_std::QuerierResult; + +use crate::wasm_emulation::input::QuerierStorage; +use crate::Contract; + +use super::gas::GAS_COST_QUERY_ERROR; + +#[derive(Clone)] +pub struct LocalForkedState { + pub contracts: HashMap>, + pub env: Env, +} + +#[derive(Clone)] +pub struct ForkState +where + QueryC: CustomQuery + DeserializeOwned + 'static, + ExecC: CustomMsg + 'static, +{ + pub remote: RemoteChannel, + /// Only query function right now, but we might pass along the whole application state to avoid stargate queries + pub local_state: LocalForkedState, + pub querier_storage: QuerierStorage, +} + +pub type QueryResultWithGas = (QuerierResult, GasInfo); + +/// The same type as cosmwasm-std's QuerierResult, but easier to reuse in +/// cosmwasm-vm. It might diverge from QuerierResult at some point. +pub type MockQuerierCustomHandlerResult = SystemResult>; + +/// MockQuerier holds an immutable table of bank balances +/// and configurable handlers for Wasm queries and custom queries. +pub struct MockQuerier< + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, +> { + bank: BankQuerier, + + staking: StakingQuerier, + wasm: WasmQuerier, + + //Box, Env, Vec) -> Result>, //fn(deps: Deps, env: Env, msg: Vec) -> Result, + /// A handler to handle custom queries. This is set to a dummy handler that + /// always errors by default. Update it via `with_custom_handler`. + /// + /// Use box to avoid the need of another generic type + custom_handler: Box Fn(&'a QueryC) -> QueryResultWithGas>, + remote: RemoteChannel, +} + +impl< + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, + > MockQuerier +{ + pub fn new(fork_state: ForkState) -> Self { + // We create query_closures for all local_codes + + MockQuerier { + bank: BankQuerier::new( + fork_state.remote.clone(), + fork_state.querier_storage.bank.storage.clone(), + ), + + staking: StakingQuerier::default(), + wasm: WasmQuerier::new(fork_state.clone()), + // strange argument notation suggested as a workaround here: https://github.com/rust-lang/rust/issues/41078#issuecomment-294296365 + custom_handler: Box::from(|_: &_| -> QueryResultWithGas { + ( + SystemResult::Err(SystemError::UnsupportedRequest { + kind: "custom".to_string(), + }), + GasInfo::free(), + ) + }), + remote: fork_state.remote, + } + } + + // set a new balance for the given address and return the old balance + pub fn update_balance( + &mut self, + addr: impl Into, + balance: Vec, + ) -> Option> { + self.bank.update_balance(addr, balance) + } + + pub fn update_staking( + &mut self, + denom: &str, + validators: &[Validator], + delegations: &[FullDelegation], + ) { + self.staking = StakingQuerier::new(denom, validators, delegations); + } + + pub fn with_custom_handler(mut self, handler: CH) -> Self + where + CH: Fn(&QueryC) -> QueryResultWithGas + 'static, + { + self.custom_handler = Box::from(handler); + self + } +} + +impl< + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, + > cosmwasm_vm::Querier for MockQuerier +{ + fn query_raw( + &self, + bin_request: &[u8], + _gas_limit: u64, + ) -> BackendResult>> { + let request: QueryRequest = match from_json(bin_request) { + Ok(v) => v, + Err(e) => { + return ( + Ok(SystemResult::Err(SystemError::InvalidRequest { + error: format!("Parsing query request: {}", e), + request: bin_request.into(), + })), + GasInfo::with_externally_used(GAS_COST_QUERY_ERROR), + ) + } + }; + let result = self.handle_query(&request); + + (Ok(result.0), result.1) + } +} + +impl< + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, + > cosmwasm_std::Querier for MockQuerier +{ + fn raw_query(&self, bin_request: &[u8]) -> SystemResult> { + let request: QueryRequest = match from_json(bin_request) { + Ok(v) => v, + Err(e) => { + return SystemResult::Err(SystemError::InvalidRequest { + error: format!("Parsing query request: {}", e), + request: bin_request.into(), + }) + } + }; + let result = self.handle_query(&request); + + result.0 + } +} + +impl< + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, + > MockQuerier +{ + pub fn handle_query(&self, request: &QueryRequest) -> QueryResultWithGas { + match &request { + QueryRequest::Bank(bank_query) => self.bank.query(bank_query), + QueryRequest::Custom(custom_query) => (*self.custom_handler)(custom_query), + + QueryRequest::Staking(staking_query) => self.staking.query(staking_query), + QueryRequest::Wasm(msg) => self.wasm.query(self.remote.clone(), msg), + #[allow(deprecated)] + QueryRequest::Stargate { .. } => ( + SystemResult::Err(SystemError::UnsupportedRequest { + kind: "Stargate".to_string(), + }), + GasInfo::with_externally_used(GAS_COST_QUERY_ERROR), + ), + #[cfg(feature = "cosmwasm_2_0")] + QueryRequest::Grpc(_req) => ( + SystemResult::Err(SystemError::UnsupportedRequest { + kind: "Stargate".to_string(), + }), + GasInfo::with_externally_used(GAS_COST_QUERY_ERROR), + ), + &_ => panic!("Query Type Not implemented"), + } + } +} + +pub fn digit_sum(input: &[u8]) -> usize { + input.iter().fold(0, |sum, val| sum + (*val as usize)) +} + +/// Only for test code. This bypasses assertions in new, allowing us to create _* +/// Attributes to simulate responses from the blockchain +pub fn mock_wasmd_attr(key: impl Into, value: impl Into) -> Attribute { + Attribute { + key: key.into(), + value: value.into(), + } +} diff --git a/src/wasm_emulation/query/mod.rs b/src/wasm_emulation/query/mod.rs new file mode 100644 index 00000000..d483e656 --- /dev/null +++ b/src/wasm_emulation/query/mod.rs @@ -0,0 +1,29 @@ +pub mod bank; +pub mod mock_querier; +pub mod staking; +pub mod wasm; +use cosmwasm_std::Storage; + +pub use mock_querier::MockQuerier; +pub mod gas; + +use anyhow::Result as AnyResult; + +use super::{ + channel::RemoteChannel, + input::{BankStorage, WasmStorage}, +}; + +pub trait ContainsRemote { + fn with_remote(self, remote: RemoteChannel) -> Self; + + fn set_remote(&mut self, remote: RemoteChannel); +} + +pub trait AllWasmQuerier { + fn query_all(&self, storage: &dyn Storage) -> AnyResult; +} + +pub trait AllBankQuerier { + fn query_all(&self, storage: &dyn Storage) -> AnyResult; +} diff --git a/src/wasm_emulation/query/staking.rs b/src/wasm_emulation/query/staking.rs new file mode 100644 index 00000000..277e3158 --- /dev/null +++ b/src/wasm_emulation/query/staking.rs @@ -0,0 +1,94 @@ +use crate::wasm_emulation::query::gas::{ + GAS_COST_ALL_DELEGATIONS, GAS_COST_ALL_VALIDATORS, GAS_COST_BONDED_DENOM, GAS_COST_DELEGATIONS, + GAS_COST_VALIDATOR, +}; +use crate::wasm_emulation::query::mock_querier::QueryResultWithGas; +use cosmwasm_std::Binary; +use cosmwasm_vm::GasInfo; + +use cosmwasm_std::to_json_binary; +use cosmwasm_std::{ + AllDelegationsResponse, AllValidatorsResponse, BondedDenomResponse, DelegationResponse, + FullDelegation, StakingQuery, Validator, ValidatorResponse, +}; +use cosmwasm_std::{ContractResult, SystemResult}; + +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +pub struct StakingQuerier { + denom: String, + validators: Vec, + delegations: Vec, +} + +impl StakingQuerier { + pub fn new(denom: &str, validators: &[Validator], delegations: &[FullDelegation]) -> Self { + StakingQuerier { + denom: denom.to_string(), + validators: validators.to_vec(), + delegations: delegations.to_vec(), + } + } + + pub fn query(&self, request: &StakingQuery) -> QueryResultWithGas { + let contract_result: ContractResult = match request { + StakingQuery::BondedDenom {} => { + let res = BondedDenomResponse::new(self.denom.clone()); + to_json_binary(&res).into() + } + StakingQuery::AllValidators {} => { + let res = AllValidatorsResponse::new(self.validators.clone()); + to_json_binary(&res).into() + } + StakingQuery::Validator { address } => { + let validator: Option = self + .validators + .iter() + .find(|validator| validator.address == *address) + .cloned(); + let res = ValidatorResponse::new(validator); + to_json_binary(&res).into() + } + StakingQuery::AllDelegations { delegator } => { + let delegations: Vec<_> = self + .delegations + .iter() + .filter(|d| d.delegator.as_str() == delegator) + .cloned() + .map(|d| d.into()) + .collect(); + let res = AllDelegationsResponse::new(delegations); + to_json_binary(&res).into() + } + StakingQuery::Delegation { + delegator, + validator, + } => { + let delegation = self + .delegations + .iter() + .find(|d| d.delegator.as_str() == delegator && d.validator == *validator); + let res = DelegationResponse::new(delegation.cloned()); + to_json_binary(&res).into() + } + &_ => panic!("Not implemented {:?}", request), + }; + + // We handle the gas_info + let gas_info = match request { + StakingQuery::BondedDenom { .. } => GAS_COST_BONDED_DENOM, + StakingQuery::AllValidators { .. } => GAS_COST_ALL_VALIDATORS, + StakingQuery::Validator { .. } => GAS_COST_VALIDATOR, + StakingQuery::AllDelegations { .. } => GAS_COST_ALL_DELEGATIONS, + StakingQuery::Delegation { .. } => GAS_COST_DELEGATIONS, + &_ => panic!("Not implemented {:?}", request), + }; + + // system result is always ok in the mock implementation + ( + SystemResult::Ok(contract_result), + GasInfo::with_externally_used(gas_info), + ) + } +} diff --git a/src/wasm_emulation/query/wasm.rs b/src/wasm_emulation/query/wasm.rs new file mode 100644 index 00000000..18eb4dab --- /dev/null +++ b/src/wasm_emulation/query/wasm.rs @@ -0,0 +1,245 @@ +use std::marker::PhantomData; + +use crate::prefixed_storage::get_full_contract_storage_namespace; +use crate::queries::wasm::WasmRemoteQuerier; +use crate::wasm_emulation::query::gas::{ + GAS_COST_ALL_QUERIES, GAS_COST_CONTRACT_INFO, GAS_COST_RAW_COSMWASM_QUERY, +}; +use crate::wasm_emulation::query::mock_querier::QueryResultWithGas; +use crate::wasm_emulation::query::MockQuerier; +use crate::{Contract, MockApiBech32}; + +use crate::wasm_emulation::contract::WasmContract; +use cosmwasm_std::testing::MockStorage; +use cosmwasm_vm::GasInfo; + +use cosmwasm_std::{ + to_json_binary, Addr, ContractInfoResponse, CustomMsg, CustomQuery, OwnedDeps, Storage, + SystemError, SystemResult, +}; +use cosmwasm_std::{ContractInfo, ContractResult}; + +use cosmwasm_std::WasmQuery; +use cw_orch_daemon::queriers::CosmWasm; +use serde::de::DeserializeOwned; + +use crate::wasm_emulation::channel::RemoteChannel; + +use super::mock_querier::ForkState; + +pub struct WasmQuerier< + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, +> { + fork_state: ForkState, +} + +impl< + ExecC: CustomMsg + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, + > WasmQuerier +{ + pub fn new(fork_state: ForkState) -> Self { + Self { fork_state } + } + + pub fn query(&self, remote: RemoteChannel, request: &WasmQuery) -> QueryResultWithGas { + match request { + WasmQuery::ContractInfo { contract_addr } => { + let addr = Addr::unchecked(contract_addr); + let data = if let Some(local_contract) = self + .fork_state + .querier_storage + .wasm + .contracts + .get(contract_addr) + { + local_contract.clone() + } else { + let maybe_distant_contract = WasmRemoteQuerier::load_distant_contract( + self.fork_state.remote.clone(), + &addr, + ); + + if let Ok(contract) = maybe_distant_contract { + contract + } else { + return ( + SystemResult::Err(SystemError::NoSuchContract { + addr: contract_addr.to_string(), + }), + GasInfo::with_externally_used(GAS_COST_CONTRACT_INFO), + ); + } + }; + let response = + ContractInfoResponse::new(data.code_id, data.creator, data.admin, false, None); + ( + SystemResult::Ok(to_json_binary(&response).into()), + GasInfo::with_externally_used(GAS_COST_CONTRACT_INFO), + ) + } + WasmQuery::Raw { contract_addr, key } => { + // We first try to load that information locally + let mut total_key = + get_full_contract_storage_namespace(&Addr::unchecked(contract_addr)).to_vec(); + total_key.extend_from_slice(key); + + if let Some(value) = self + .fork_state + .querier_storage + .wasm + .storage + .iter() + .find(|e| e.0 == total_key) + { + ( + SystemResult::Ok(ContractResult::Ok(value.1.clone().into())), + GasInfo::with_externally_used(GAS_COST_RAW_COSMWASM_QUERY), + ) + } else { + ( + SystemResult::Ok( + WasmRemoteQuerier::raw_query( + remote, + &Addr::unchecked(contract_addr), + key.clone(), + ) + .map(Into::into) + .into(), + ), + GasInfo::with_externally_used(GAS_COST_RAW_COSMWASM_QUERY), + ) + } + } + WasmQuery::Smart { contract_addr, msg } => { + let addr = Addr::unchecked(contract_addr); + + let mut storage = MockStorage::default(); + // Set the storage + for (key, value) in self + .fork_state + .querier_storage + .wasm + .get_contract_storage(&addr) + { + storage.set(&key, &value); + } + + let deps = OwnedDeps { + storage, + api: MockApiBech32::new(&self.fork_state.remote.pub_address_prefix), + querier: MockQuerier::new(self.fork_state.clone()), + custom_query_type: PhantomData::, + }; + let mut env = self.fork_state.local_state.env.clone(); + env.contract = ContractInfo { + address: addr.clone(), + }; + + // First we get the code id corresponding to the contract + let code_id = if let Some(local_contract) = self + .fork_state + .querier_storage + .wasm + .contracts + .get(contract_addr) + { + local_contract.code_id + } else { + let wasm_querier = CosmWasm::new_sync(remote.channel.clone(), &remote.rt); + + let code_info = remote + .rt + .block_on(wasm_querier._contract_info(&addr)) + .unwrap(); + + code_info.code_id + }; + + // Then, we get the corresponding wasm contract + + // If the contract data is already defined in our storage, we load it from there + let result = + if let Some(code) = self.fork_state.querier_storage.wasm.codes.get(&code_id) { + // Local Wasm Contract case + >::query( + code, + deps.as_ref(), + env, + msg.to_vec(), + self.fork_state.clone(), + ) + } else if let Some(local_contract) = + self.fork_state.local_state.contracts.get(&code_id) + { + // Local Rust Contract case + unsafe { + local_contract.as_ref().unwrap().query( + deps.as_ref(), + env, + msg.to_vec(), + self.fork_state.clone(), + ) + } + } else { + // Distant Registered Contract case + // TODO, this should be part of the cache as well + // However, it's not really possible to register that data inside the App, because this is deep in the execution layer + >::query( + &WasmContract::new_distant_code_id(code_id, remote.clone()), + deps.as_ref(), + env, + msg.to_vec(), + self.fork_state.clone(), + ) + }; + + let result = match result { + Err(e) => { + return ( + SystemResult::Err(SystemError::InvalidRequest { + error: format!("Error querying a contract: {}", e), + request: msg.clone(), + }), + GasInfo::with_externally_used(0), + ) + } + Ok(result) => result, + }; + + ( + SystemResult::Ok(ContractResult::Ok(result)), + GasInfo::with_externally_used(GAS_COST_ALL_QUERIES), + ) + } + WasmQuery::CodeInfo { code_id } => { + let code_data = self.fork_state.querier_storage.wasm.code_data.get(code_id); + let res = if let Some(code_data) = code_data { + cosmwasm_std::CodeInfoResponse::new( + *code_id, + code_data.creator.clone(), + code_data.checksum, + ) + } else { + let maybe_code_info = + WasmRemoteQuerier::code_info(self.fork_state.remote.clone(), *code_id); + + if let Ok(code_info) = maybe_code_info { + code_info + } else { + return ( + SystemResult::Err(SystemError::NoSuchCode { code_id: *code_id }), + GasInfo::with_externally_used(GAS_COST_CONTRACT_INFO), + ); + } + }; + ( + SystemResult::Ok(to_json_binary(&res).into()), + GasInfo::with_externally_used(GAS_COST_CONTRACT_INFO), + ) + } + _ => unimplemented!(), + } + } +} diff --git a/src/wasm_emulation/storage/analyzer.rs b/src/wasm_emulation/storage/analyzer.rs new file mode 100644 index 00000000..2ec4a6bc --- /dev/null +++ b/src/wasm_emulation/storage/analyzer.rs @@ -0,0 +1,271 @@ +use crate::{ + featured::staking::{Distribution, Staking}, + prefixed_storage::{decode_length, to_length_prefixed, CONTRACT_STORAGE_PREFIX}, + wasm_emulation::channel::RemoteChannel, + BankKeeper, Gov, Ibc, Module, Stargate, WasmKeeper, +}; +use cosmwasm_std::{Addr, Api, Coin, CustomMsg, CustomQuery, Storage}; +use cw_orch::prelude::BankQuerier; +use cw_utils::NativeBalance; +use rustc_serialize::json::Json; +use serde::de::DeserializeOwned; +use serde::Serialize; +use treediff::diff; +use treediff::tools::Recorder; + +use crate::wasm::NAMESPACE_WASM; + +use crate::{wasm_emulation::input::QuerierStorage, App}; + +use anyhow::Result as AnyResult; + +#[derive(Serialize)] +pub struct SerializableCoin { + amount: String, + denom: String, +} + +pub struct StorageAnalyzer { + pub storage: QuerierStorage, + pub remote: RemoteChannel, +} + +pub type CustomWasmKeeper = + WasmKeeper<::ExecT, ::QueryT>; + +impl StorageAnalyzer { + pub fn new( + app: &App< + BankKeeper, + ApiT, + StorageT, + CustomT, + CustomWasmKeeper, + StakingT, + DistrT, + IbcT, + GovT, + StargateT, + >, + ) -> AnyResult + where + CustomT::ExecT: CustomMsg + DeserializeOwned + 'static, + CustomT::QueryT: CustomQuery + DeserializeOwned + 'static, + + ApiT: Api, + StorageT: Storage, + CustomT: Module, + StakingT: Staking, + DistrT: Distribution, + IbcT: Ibc, + GovT: Gov, + StargateT: Stargate, + { + Ok(Self { + storage: app.get_querier_storage()?, + remote: app + .remote + .clone() + .expect("Remote has to be defined to use storage analyzer"), + }) + } + + pub fn get_contract_storage( + &self, + contract_addr: impl Into, + ) -> Vec<(Vec, Vec)> { + self.storage + .wasm + .get_contract_storage(&Addr::unchecked(contract_addr.into())) + } + + pub fn readable_storage(&self, contract_addr: impl Into) -> Vec<(String, String)> { + self.storage + .wasm + .get_contract_storage(&Addr::unchecked(contract_addr.into())) + .into_iter() + .map(|(key, value)| { + ( + String::from_utf8_lossy(&key).to_string(), + String::from_utf8_lossy(&value).to_string(), + ) + }) + .collect() + } + + /// We leverage the data structure we introduced for contracts to get their storage easily + pub fn all_contract_storage(&self) -> Vec<(String, Vec, Vec)> { + // In all wasm storage keys, we look for the `contract_addr/(...)/` pattern in the key + self.storage + .wasm + .storage + .iter() + .filter(|(key, _)| { + // The key must contain the NAMESPACE_WASM prefix + let prefix = to_length_prefixed(NAMESPACE_WASM); + key.len() >= prefix.len() && key[..prefix.len()] == prefix + }) + .filter_map(|(key, value)| { + // Now we need to get the contract addr from the namespace + + let prefix_len = to_length_prefixed(NAMESPACE_WASM).len(); + let resulting_key = &key[prefix_len..]; + let addr_len: usize = decode_length([resulting_key[0], resulting_key[1]]) + .try_into() + .unwrap(); + let contract_addr_addr = + String::from_utf8_lossy(&resulting_key[2..(addr_len + 2)]).to_string(); + + let split: Vec<_> = contract_addr_addr.split('/').collect(); + if split.len() != 2 || format!("{}/", split[0]) != CONTRACT_STORAGE_PREFIX { + return None; + } + + Some(( + split[1].to_string(), + resulting_key[addr_len + 2..].to_vec(), + value.clone(), + )) + }) + .collect() + } + + pub fn all_readable_contract_storage(&self) -> Vec<(String, String, String)> { + self.all_contract_storage() + .into_iter() + .map(|(contract, key, value)| { + ( + contract, + String::from_utf8_lossy(&key).to_string(), + String::from_utf8_lossy(&value).to_string(), + ) + }) + .collect() + } + + pub fn compare_all_readable_contract_storage(&self) { + let wasm_querier = cw_orch_daemon::queriers::CosmWasm::new_sync( + self.remote.channel.clone(), + &self.remote.rt, + ); + self.all_contract_storage() + .into_iter() + .for_each(|(contract_addr, key, value)| { + // We look for the data at that key on the contract + let distant_data = self.remote.rt.block_on( + wasm_querier + ._contract_raw_state(&Addr::unchecked(contract_addr.clone()), key.clone()), + ); + + if let Ok(data) = distant_data { + let local_json: Json = + if let Ok(v) = String::from_utf8_lossy(&value).to_string().parse() { + v + } else { + log::info!( + "Storage at {}, and key {}, was : {:x?}, now {:x?}", + contract_addr, + String::from_utf8_lossy(&key).to_string(), + data.data, + value + ); + return; + }; + let distant_json: Json = + if let Ok(v) = String::from_utf8_lossy(&data.data).to_string().parse() { + v + } else { + log::info!( + "Storage at {}, and key {}, was : {:x?}, now {:x?}", + contract_addr, + String::from_utf8_lossy(&key).to_string(), + data.data, + value + ); + return; + }; + + let mut d = Recorder::default(); + diff(&distant_json, &local_json, &mut d); + + let changes: Vec<_> = d + .calls + .iter() + .filter(|change| { + !matches!(change, treediff::tools::ChangeType::Unchanged(..)) + }) + .collect(); + + log::info!( + "Storage at {}, and key {}, changed like so : {:?}", + contract_addr, + String::from_utf8_lossy(&key).to_string(), + changes + ); + } else if let Ok(v) = String::from_utf8_lossy(&value).to_string().parse::() { + log::info!( + "Storage at {}, and key {}, is new : {}", + contract_addr, + String::from_utf8_lossy(&key).to_string(), + v + ); + } else { + log::info!( + "Storage at {}, and key {}, is new : {:?}", + contract_addr, + String::from_utf8_lossy(&key).to_string(), + value + ); + } + }); + } + + pub fn get_balance(&self, addr: impl Into) -> Vec { + let addr: String = addr.into(); + self.storage + .bank + .storage + .iter() + .find(|(a, _)| a.as_str() == addr) + .map(|(_, b)| b.0.clone()) + .unwrap_or(vec![]) + } + + pub fn compare_all_balances(&self) { + let bank_querier = cw_orch_daemon::queriers::Bank { + channel: self.remote.channel.clone(), + rt_handle: Some(self.remote.rt.clone()), + }; + self.get_all_local_balances() + .into_iter() + .for_each(|(addr, balances)| { + // We look for the data at that key on the contract + let distant_data = bank_querier.balance(&addr, None); + + if let Ok(distant_coins) = distant_data { + let distant_coins = serde_json::to_string(&distant_coins).unwrap(); + let distant_coins: Json = distant_coins.parse().unwrap(); + + let local_coins = serde_json::to_string(&balances.0).unwrap(); + let local_coins: Json = local_coins.parse().unwrap(); + + let mut d = Recorder::default(); + diff(&distant_coins, &local_coins, &mut d); + + let changes: Vec<_> = d + .calls + .iter() + .filter(|change| { + !matches!(change, treediff::tools::ChangeType::Unchanged(..)) + }) + .collect(); + + log::info!("Bank balance for {} changed like so : {:?}", addr, changes); + } + }); + } + + pub fn get_all_local_balances(&self) -> Vec<(Addr, NativeBalance)> { + self.storage.bank.storage.clone() + } +} diff --git a/src/wasm_emulation/storage/dual_std_storage.rs b/src/wasm_emulation/storage/dual_std_storage.rs new file mode 100644 index 00000000..6d523113 --- /dev/null +++ b/src/wasm_emulation/storage/dual_std_storage.rs @@ -0,0 +1,215 @@ +use crate::wasm_emulation::channel::RemoteChannel; + +use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; +use cosmrs::proto::cosmwasm::wasm::v1::Model; +use cosmwasm_std::{Addr, Record}; +use cosmwasm_std::{Order, Storage}; +use cw_orch_daemon::queriers::CosmWasm; +use num_bigint::{BigInt, Sign}; +use std::iter::{self, Peekable}; + +fn get_key_bigint(mut key1: Vec, mut key2: Vec) -> (BigInt, BigInt) { + if key1.len() >= key2.len() { + key2.extend(iter::repeat(0).take(key1.len() - key2.len())) + } else { + key1.extend(iter::repeat(0).take(key2.len() - key1.len())) + } + + ( + BigInt::from_bytes_be(Sign::Plus, &key1), + BigInt::from_bytes_be(Sign::Plus, &key2), + ) +} + +fn gte(key1: Vec, key2: Vec) -> bool { + let ints = get_key_bigint(key1, key2); + + ints.0 >= ints.1 +} + +fn _gt(key1: Vec, key2: Vec) -> bool { + let ints = get_key_bigint(key1, key2); + ints.0 > ints.1 +} + +use std::collections::HashSet; + +use anyhow::Result as AnyResult; +const DISTANT_LIMIT: u64 = 5u64; + +struct DistantIter { + remote: RemoteChannel, + contract_addr: Addr, + data: Vec, + position: usize, + key: Option>, // if set to None, there is no more keys to investigate in the distant container + start: Option>, + end: Option>, + reverse: bool, +} + +/// Iterator to get multiple keys +struct Iter<'a> { + distant_iter: DistantIter, + local_iter: Peekable + 'a>>, +} + +impl Iterator for Iter<'_> { + type Item = Record; + + fn next(&mut self) -> Option { + // 1. We verify that there is enough elements in the distant iterator + if self.distant_iter.position == self.distant_iter.data.len() + && self.distant_iter.key.is_some() + && (self.distant_iter.position == 0 + || !self.distant_iter.key.clone().unwrap().is_empty()) + { + let wasm_querier = CosmWasm::new_sync( + self.distant_iter.remote.channel.clone(), + &self.distant_iter.remote.rt, + ); + let new_keys = self + .distant_iter + .remote + .rt + .block_on(wasm_querier._all_contract_state( + &self.distant_iter.contract_addr, + Some(PageRequest { + key: self.distant_iter.key.clone().unwrap(), + offset: 0, + limit: DISTANT_LIMIT, + count_total: false, + reverse: self.distant_iter.reverse, + }), + )) + .unwrap_or_default(); + + // We make sure the data queried correspond to all the keys we need + self.distant_iter + .data + .extend(new_keys.models.into_iter().filter(|m| { + let lower_than_end = if let Some(end) = self.distant_iter.end.clone() { + !gte(m.key.clone(), end) + } else { + true + }; + + let higher_than_start = if let Some(start) = self.distant_iter.start.clone() { + gte(m.key.clone(), start) + } else { + true + }; + + lower_than_end && higher_than_start + })); + self.distant_iter.key = new_keys.pagination.map(|p| p.next_key); + } + + // 2. We find the first key in order between distant and local storage + let next_local = self.local_iter.peek(); + let next_distant = self.distant_iter.data.get(self.distant_iter.position); + + if let Some(local) = next_local { + if let Some(distant) = next_distant { + // We compare the two keys with the order and return the higher key + let key_local = BigInt::from_bytes_be(Sign::Plus, &local.0); + let key_distant = BigInt::from_bytes_be(Sign::Plus, &distant.key); + if (key_local < key_distant) == self.distant_iter.reverse { + self.distant_iter.position += 1; + Some((distant.key.clone(), distant.value.clone())) + } else { + self.local_iter.next() + } + } else { + self.local_iter.next() + } + } else if let Some(distant) = next_distant { + self.distant_iter.position += 1; + Some((distant.key.clone(), distant.value.clone())) + } else { + None + } + } +} + +pub struct DualStorage<'a> { + pub local_storage: Box, + pub removed_keys: HashSet>, + pub remote: RemoteChannel, + pub contract_addr: Addr, +} + +impl<'a> DualStorage<'a> { + pub fn new( + remote: RemoteChannel, + contract_addr: String, + local_storage: Box, + ) -> AnyResult> { + Ok(Self { + local_storage, + remote, + removed_keys: HashSet::default(), + contract_addr: Addr::unchecked(contract_addr), + }) + } +} + +impl Storage for DualStorage<'_> { + fn get(&self, key: &[u8]) -> Option> { + // First we try to get the value locally + let mut value = self.local_storage.get(key); + // If it's not available, we query it online if it was not removed locally + if !self.removed_keys.contains(key) && value.as_ref().is_none() { + let wasm_querier = CosmWasm::new_sync(self.remote.channel.clone(), &self.remote.rt); + + let distant_result = self + .remote + .rt + .block_on(wasm_querier._contract_raw_state(&self.contract_addr, key.to_vec())); + + if let Ok(result) = distant_result { + if !result.data.is_empty() { + value = Some(result.data) + } + } + } + value + } + + fn set(&mut self, key: &[u8], value: &[u8]) { + self.removed_keys.remove(key); // It's not locally removed anymore, because we set it locally + self.local_storage.set(key, value) + } + + fn remove(&mut self, key: &[u8]) { + self.removed_keys.insert(key.to_vec()); // We indicate locally if it's removed. So that we can remove keys and not query them on the distant chain + self.local_storage.remove(key) + } + + fn range<'b>( + &'b self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box + 'b> { + let querier_start = if order == Order::Descending { + end.map(|s| s.to_vec()).unwrap_or_default() + } else { + start.map(|s| s.to_vec()).unwrap_or_default() + }; + + Box::new(Iter { + distant_iter: DistantIter { + remote: self.remote.clone(), + contract_addr: self.contract_addr.clone(), + data: vec![], + position: 0, + key: Some(querier_start), + end: end.map(|e| e.to_vec()), + start: start.map(|e| e.to_vec()), + reverse: order == Order::Descending, + }, + local_iter: self.local_storage.range(start, end, order).peekable(), + }) + } +} diff --git a/src/wasm_emulation/storage/dual_storage.rs b/src/wasm_emulation/storage/dual_storage.rs new file mode 100644 index 00000000..b98b10ba --- /dev/null +++ b/src/wasm_emulation/storage/dual_storage.rs @@ -0,0 +1,334 @@ +use crate::wasm_emulation::channel::RemoteChannel; +use crate::wasm_emulation::storage::mock_storage::{GAS_COST_LAST_ITERATION, GAS_COST_RANGE}; +use crate::wasm_emulation::storage::CLONE_TESTING_STORAGE_LOG; + +use super::mock_storage::MockStorage; +use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest; +use cosmrs::proto::cosmwasm::wasm::v1::Model; +use cosmwasm_std::Record; +use cosmwasm_std::{Addr, Order}; +use cosmwasm_vm::BackendError; +use cosmwasm_vm::BackendResult; +use cosmwasm_vm::GasInfo; +use cosmwasm_vm::Storage; +use num_bigint::{BigInt, Sign}; +use std::collections::HashMap; +use std::iter; + +use cw_orch_daemon::queriers::CosmWasm; + +fn get_key_bigint(mut key1: Vec, mut key2: Vec) -> (BigInt, BigInt) { + if key1.len() >= key2.len() { + key2.extend(iter::repeat(0).take(key1.len() - key2.len())) + } else { + key1.extend(iter::repeat(0).take(key2.len() - key1.len())) + } + + ( + BigInt::from_bytes_be(Sign::Plus, &key1), + BigInt::from_bytes_be(Sign::Plus, &key2), + ) +} + +fn gte(key1: Vec, key2: Vec) -> bool { + let ints = get_key_bigint(key1, key2); + + ints.0 >= ints.1 +} + +fn _gt(key1: Vec, key2: Vec) -> bool { + let ints = get_key_bigint(key1, key2); + ints.0 > ints.1 +} + +use std::collections::HashSet; + +use anyhow::Result as AnyResult; +const DISTANT_LIMIT: u64 = 5u64; + +#[derive(Default, Debug, Clone)] +struct DistantIter { + data: Vec, + position: usize, + key: Option>, // if set to None, there is no more keys to investigate in the distant container + start: Option>, + end: Option>, + reverse: bool, +} + +/// Iterator to get multiple keys +#[derive(Default, Debug, Clone)] +struct Iter { + distant_iter: DistantIter, + local_iter: u32, +} + +pub struct DualStorage { + pub local_storage: MockStorage, + pub removed_keys: HashSet>, + pub remote: RemoteChannel, + pub contract_addr: Addr, + iterators: HashMap, +} + +impl DualStorage { + pub fn new( + remote: RemoteChannel, + contract_addr: String, + init: Option, Vec)>>, + ) -> AnyResult { + // We create an instance from a code_id, an address, and we run the code in it + + let mut local_storage = MockStorage::default(); + for (key, value) in init.unwrap() { + local_storage.set(&key, &value).0?; + } + + Ok(Self { + local_storage, + remote, + removed_keys: HashSet::default(), + contract_addr: Addr::unchecked(contract_addr), + iterators: HashMap::new(), + }) + } + + pub fn get_all_storage(&mut self) -> AnyResult, Vec)>> { + let iterator_id = self.local_storage.scan(None, None, Order::Ascending).0?; + let all_records = self.local_storage.all(iterator_id); + + Ok(all_records.0?) + } +} + +impl Storage for DualStorage { + fn get(&self, key: &[u8]) -> BackendResult>> { + log::debug!(target: CLONE_TESTING_STORAGE_LOG, "Getting key on contract: {}; key: {}", self.contract_addr, String::from_utf8_lossy(key)); + // First we try to get the value locally + let (mut value, gas_info) = self.local_storage.get(key); + // If it's not available, we query it online if it was not removed locally + if !self.removed_keys.contains(key) && value.as_ref().unwrap().is_none() { + log::debug!(target: CLONE_TESTING_STORAGE_LOG, "Value not set locally, fetching remote key"); + let wasm_querier = CosmWasm::new_sync(self.remote.channel.clone(), &self.remote.rt); + + let distant_result = self + .remote + .rt + .block_on(wasm_querier._contract_raw_state(&self.contract_addr, key.to_vec())); + + if let Ok(result) = distant_result { + if !result.data.is_empty() { + value = Ok(Some(result.data)) + } + } + } + if let Ok(Some(value)) = value.as_ref() { + log::debug!(target: CLONE_TESTING_STORAGE_LOG, "Value found: {}", String::from_utf8_lossy(value)); + } else { + log::debug!(target: CLONE_TESTING_STORAGE_LOG, "Found no value"); + } + + (value, gas_info) + } + + fn scan( + &mut self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> BackendResult { + let gas_info = GasInfo::with_externally_used(GAS_COST_RANGE); + let iterator_id = self.local_storage.scan(start, end, order).0.unwrap(); + + let querier_start = if order == Order::Descending { + end.map(|s| s.to_vec()).unwrap_or_default() + } else { + start.map(|s| s.to_vec()).unwrap_or_default() + }; + + let iter = Iter { + local_iter: iterator_id, + distant_iter: DistantIter { + data: vec![], + position: 0, + key: Some(querier_start), + end: end.map(|e| e.to_vec()), + start: start.map(|e| e.to_vec()), + reverse: order == Order::Descending, + }, + }; + + let last_id: u32 = self + .iterators + .len() + .try_into() + .expect("Found more iterator IDs than supported"); + let new_id = last_id + 1; + self.iterators.insert(new_id, iter.clone()); + + log::debug!( + target: CLONE_TESTING_STORAGE_LOG, + "Create iterator {} on contract {} between {:?} and {:?}, order: {:?}", + new_id, + self.contract_addr, + start.map(|v|String::from_utf8_lossy(v)), + end.map(|v|String::from_utf8_lossy(v)), + match order{ + Order::Ascending => "ascending", + Order::Descending => "descending" + } + ); + (Ok(new_id), gas_info) + } + + fn next(&mut self, iterator_id: u32) -> BackendResult> { + // In order to get the next element on the iterator, we need to compose with the two iterators we have + let iterator = match self.iterators.get_mut(&iterator_id) { + Some(i) => i, + None => { + return ( + Err(BackendError::iterator_does_not_exist(iterator_id)), + GasInfo::free(), + ); + } + }; + // TODO, work with removed keys and don't take them + + // 1. We verify that there is enough elements in the distant iterator + if iterator.distant_iter.position == iterator.distant_iter.data.len() + && iterator.distant_iter.key.is_some() + { + let wasm_querier = CosmWasm::new_sync(self.remote.channel.clone(), &self.remote.rt); + let new_keys = self + .remote + .rt + .block_on(wasm_querier._all_contract_state( + &self.contract_addr, + Some(PageRequest { + key: iterator.distant_iter.key.clone().unwrap(), + offset: 0, + limit: DISTANT_LIMIT, + count_total: false, + reverse: iterator.distant_iter.reverse, + }), + )) + .unwrap_or_default(); + + // We make sure the data queried correspond to all the keys we need + iterator + .distant_iter + .data + .extend(new_keys.models.into_iter().filter(|m| { + let lower_than_end = if let Some(end) = iterator.distant_iter.end.clone() { + !gte(m.key.clone(), end) + } else { + true + }; + + let higher_than_start = if let Some(start) = iterator.distant_iter.start.clone() + { + gte(m.key.clone(), start) + } else { + true + }; + + lower_than_end && higher_than_start + })); + iterator.distant_iter.key = new_keys.pagination.map(|p| p.next_key); + } + + // 2. We find the first key in order between distant and local storage + // We need to disregard local keys that have been removed + let next_local; + loop { + let next = self.local_storage.peak(iterator.local_iter).unwrap(); + if let Some((next_key, _next_value)) = next.as_ref() { + if self.removed_keys.contains(next_key) { + // We disregard the current key + self.local_storage.next(iterator.local_iter).0.unwrap(); + continue; + } else { + next_local = next; + break; + } + } else { + next_local = next; + break; + } + } + let next_distant = iterator + .distant_iter + .data + .get(iterator.distant_iter.position); + + let key_value = if let Some(local) = next_local { + if let Some(distant) = next_distant { + // We compare the two keys with the order and return the higher key + let key_local = BigInt::from_bytes_be(Sign::Plus, &local.0); + let key_distant = BigInt::from_bytes_be(Sign::Plus, &distant.key); + + if key_local == key_distant { + // Equal keys, means we need to take the local key, which is newer + // And we need to skip the distant key + iterator.distant_iter.position += 1; + self.local_storage.next(iterator.local_iter).0.unwrap() + } else if (key_local < key_distant) == iterator.distant_iter.reverse { + // Keys are ordered such that the distant key is chosen + // We upgrade the distant iterator and return the distant key + iterator.distant_iter.position += 1; + Some((distant.key.clone(), distant.value.clone())) + } else { + // Keys are ordered such that the local key is chosen + // We get the next local key + self.local_storage.next(iterator.local_iter).0.unwrap() + } + } else { + self.local_storage.next(iterator.local_iter).0.unwrap() + } + } else if let Some(distant) = next_distant { + iterator.distant_iter.position += 1; + Some((distant.key.clone(), distant.value.clone())) + } else { + None + }; + + // We add the gas cost + if let Some((key, value)) = key_value { + log::debug!( + target: CLONE_TESTING_STORAGE_LOG, + "Got next iterator value contract {}, iterator {}, key {}, value {}", + self.contract_addr, + iterator_id, + String::from_utf8_lossy(&key), + String::from_utf8_lossy(&value) + ); + ( + Ok(Some((key.clone(), value.clone()))), + GasInfo::with_externally_used((key.len() + value.len()) as u64), + ) + } else { + log::debug!( + target: CLONE_TESTING_STORAGE_LOG, + "No more values for iterator {} contract {}", + self.contract_addr, + iterator_id, + ); + ( + Ok(None), + GasInfo::with_externally_used(GAS_COST_LAST_ITERATION), + ) + } + } + + fn set(&mut self, key: &[u8], value: &[u8]) -> BackendResult<()> { + log::debug!(target: CLONE_TESTING_STORAGE_LOG, "Setting key on contract: {}; key: {}; value: {}", self.contract_addr, String::from_utf8_lossy(key), String::from_utf8_lossy(value)); + self.removed_keys.remove(key); // It's not locally removed anymore, because we set it locally + self.local_storage.set(key, value) + } + + fn remove(&mut self, key: &[u8]) -> BackendResult<()> { + log::debug!(target: CLONE_TESTING_STORAGE_LOG, "Removing key on contract: {}; {}", self.contract_addr, String::from_utf8_lossy(key)); + self.removed_keys.insert(key.to_vec()); // We indicate locally if it's removed. So that we can remove keys and not query them on the distant chain + self.local_storage.remove(key) + } +} diff --git a/src/wasm_emulation/storage/mock_storage.rs b/src/wasm_emulation/storage/mock_storage.rs new file mode 100644 index 00000000..78b6b56e --- /dev/null +++ b/src/wasm_emulation/storage/mock_storage.rs @@ -0,0 +1,347 @@ +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::ops::{Bound, RangeBounds}; + +use cosmwasm_std::{Order, Record}; + +use cosmwasm_vm::BackendError; +use cosmwasm_vm::{BackendResult, GasInfo, Storage}; + +pub const GAS_COST_LAST_ITERATION: u64 = 37; + +pub const GAS_COST_RANGE: u64 = 11; + +// We needed to add the peak function on the cosmwasm-vm code +// This is used for making sure we have the right order between distant and local storage + +#[derive(Default, Debug, Clone)] +pub struct Iter { + data: Vec, + position: usize, +} + +#[derive(Default, Debug, Clone)] +pub struct MockStorage { + pub data: BTreeMap, Vec>, + pub iterators: HashMap, +} + +impl MockStorage { + pub fn new() -> Self { + MockStorage::default() + } + + pub fn all(&mut self, iterator_id: u32) -> BackendResult> { + let mut out: Vec = Vec::new(); + let mut total = GasInfo::free(); + loop { + let (result, info) = self.next(iterator_id); + total += info; + match result { + Err(err) => return (Err(err), total), + Ok(ok) => { + if let Some(v) = ok { + out.push(v); + } else { + break; + } + } + } + } + (Ok(out), total) + } + + pub fn peak(&mut self, iterator_id: u32) -> Result, BackendError> { + let iterator = match self.iterators.get(&iterator_id) { + Some(i) => i, + None => return Err(BackendError::iterator_does_not_exist(iterator_id)), + }; + + let value: Option = if iterator.data.len() > iterator.position { + let item = iterator.data[iterator.position].clone(); + Some(item) + } else { + None + }; + + Ok(value) + } +} + +impl Storage for MockStorage { + fn get(&self, key: &[u8]) -> BackendResult>> { + let gas_info = GasInfo::with_externally_used(key.len() as u64); + (Ok(self.data.get(key).cloned()), gas_info) + } + + fn scan( + &mut self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> BackendResult { + let gas_info = GasInfo::with_externally_used(GAS_COST_RANGE); + let bounds = range_bounds(start, end); + + let values: Vec = match (bounds.start_bound(), bounds.end_bound()) { + // BTreeMap.range panics if range is start > end. + // However, this cases represent just empty range and we treat it as such. + (Bound::Included(start), Bound::Excluded(end)) if start > end => Vec::new(), + _ => match order { + Order::Ascending => self.data.range(bounds).map(clone_item).collect(), + Order::Descending => self.data.range(bounds).rev().map(clone_item).collect(), + }, + }; + + let last_id: u32 = self + .iterators + .len() + .try_into() + .expect("Found more iterator IDs than supported"); + let new_id = last_id + 1; + let iter = Iter { + data: values, + position: 0, + }; + self.iterators.insert(new_id, iter); + + (Ok(new_id), gas_info) + } + + fn next(&mut self, iterator_id: u32) -> BackendResult> { + let iterator = match self.iterators.get_mut(&iterator_id) { + Some(i) => i, + None => { + return ( + Err(BackendError::iterator_does_not_exist(iterator_id)), + GasInfo::free(), + ) + } + }; + + let (value, gas_info): (Option, GasInfo) = + if iterator.data.len() > iterator.position { + let item = iterator.data[iterator.position].clone(); + iterator.position += 1; + let gas_cost = (item.0.len() + item.1.len()) as u64; + (Some(item), GasInfo::with_cost(gas_cost)) + } else { + (None, GasInfo::with_externally_used(GAS_COST_LAST_ITERATION)) + }; + + (Ok(value), gas_info) + } + + fn set(&mut self, key: &[u8], value: &[u8]) -> BackendResult<()> { + self.data.insert(key.to_vec(), value.to_vec()); + let gas_info = GasInfo::with_externally_used((key.len() + value.len()) as u64); + (Ok(()), gas_info) + } + + fn remove(&mut self, key: &[u8]) -> BackendResult<()> { + self.data.remove(key); + let gas_info = GasInfo::with_externally_used(key.len() as u64); + (Ok(()), gas_info) + } +} + +fn range_bounds(start: Option<&[u8]>, end: Option<&[u8]>) -> impl RangeBounds> { + ( + start.map_or(Bound::Unbounded, |x| Bound::Included(x.to_vec())), + end.map_or(Bound::Unbounded, |x| Bound::Excluded(x.to_vec())), + ) +} + +/// The BTreeMap specific key-value pair reference type, as returned by BTreeMap, Vec>::range. +/// This is internal as it can change any time if the map implementation is swapped out. +type BTreeMapRecordRef<'a> = (&'a Vec, &'a Vec); + +fn clone_item(item_ref: BTreeMapRecordRef) -> Record { + let (key, value) = item_ref; + (key.clone(), value.clone()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn get_and_set() { + let mut store = MockStorage::new(); + assert_eq!(None, store.get(b"foo").0.unwrap()); + store.set(b"foo", b"bar").0.unwrap(); + assert_eq!(Some(b"bar".to_vec()), store.get(b"foo").0.unwrap()); + assert_eq!(None, store.get(b"food").0.unwrap()); + } + + #[test] + fn delete() { + let mut store = MockStorage::new(); + store.set(b"foo", b"bar").0.unwrap(); + store.set(b"food", b"bank").0.unwrap(); + store.remove(b"foo").0.unwrap(); + + assert_eq!(None, store.get(b"foo").0.unwrap()); + assert_eq!(Some(b"bank".to_vec()), store.get(b"food").0.unwrap()); + } + + #[test] + + fn iterator() { + let mut store = MockStorage::new(); + store.set(b"foo", b"bar").0.expect("error setting value"); + + // ensure we had previously set "foo" = "bar" + assert_eq!(store.get(b"foo").0.unwrap(), Some(b"bar".to_vec())); + let iter_id = store.scan(None, None, Order::Ascending).0.unwrap(); + assert_eq!(store.all(iter_id).0.unwrap().len(), 1); + + // setup - add some data, and delete part of it as well + store.set(b"ant", b"hill").0.expect("error setting value"); + store.set(b"ze", b"bra").0.expect("error setting value"); + + // noise that should be ignored + store.set(b"bye", b"bye").0.expect("error setting value"); + store.remove(b"bye").0.expect("error removing key"); + + // unbounded + { + let iter_id = store.scan(None, None, Order::Ascending).0.unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!( + elements, + vec![ + (b"ant".to_vec(), b"hill".to_vec()), + (b"foo".to_vec(), b"bar".to_vec()), + (b"ze".to_vec(), b"bra".to_vec()), + ] + ); + } + + // unbounded (descending) + { + let iter_id = store.scan(None, None, Order::Descending).0.unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!( + elements, + vec![ + (b"ze".to_vec(), b"bra".to_vec()), + (b"foo".to_vec(), b"bar".to_vec()), + (b"ant".to_vec(), b"hill".to_vec()), + ] + ); + } + + // bounded + { + let iter_id = store + .scan(Some(b"f"), Some(b"n"), Order::Ascending) + .0 + .unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!(elements, vec![(b"foo".to_vec(), b"bar".to_vec())]); + } + + // bounded (descending) + { + let iter_id = store + .scan(Some(b"air"), Some(b"loop"), Order::Descending) + .0 + .unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!( + elements, + vec![ + (b"foo".to_vec(), b"bar".to_vec()), + (b"ant".to_vec(), b"hill".to_vec()), + ] + ); + } + + // bounded empty [a, a) + { + let iter_id = store + .scan(Some(b"foo"), Some(b"foo"), Order::Ascending) + .0 + .unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!(elements, vec![]); + } + + // bounded empty [a, a) (descending) + { + let iter_id = store + .scan(Some(b"foo"), Some(b"foo"), Order::Descending) + .0 + .unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!(elements, vec![]); + } + + // bounded empty [a, b) with b < a + { + let iter_id = store + .scan(Some(b"z"), Some(b"a"), Order::Ascending) + .0 + .unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!(elements, vec![]); + } + + // bounded empty [a, b) with b < a (descending) + { + let iter_id = store + .scan(Some(b"z"), Some(b"a"), Order::Descending) + .0 + .unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!(elements, vec![]); + } + + // right unbounded + { + let iter_id = store.scan(Some(b"f"), None, Order::Ascending).0.unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!( + elements, + vec![ + (b"foo".to_vec(), b"bar".to_vec()), + (b"ze".to_vec(), b"bra".to_vec()), + ] + ); + } + + // right unbounded (descending) + { + let iter_id = store.scan(Some(b"f"), None, Order::Descending).0.unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!( + elements, + vec![ + (b"ze".to_vec(), b"bra".to_vec()), + (b"foo".to_vec(), b"bar".to_vec()), + ] + ); + } + + // left unbounded + { + let iter_id = store.scan(None, Some(b"f"), Order::Ascending).0.unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!(elements, vec![(b"ant".to_vec(), b"hill".to_vec()),]); + } + + // left unbounded (descending) + { + let iter_id = store.scan(None, Some(b"no"), Order::Descending).0.unwrap(); + let elements = store.all(iter_id).0.unwrap(); + assert_eq!( + elements, + vec![ + (b"foo".to_vec(), b"bar".to_vec()), + (b"ant".to_vec(), b"hill".to_vec()), + ] + ); + } + } +} diff --git a/src/wasm_emulation/storage/mod.rs b/src/wasm_emulation/storage/mod.rs new file mode 100644 index 00000000..252704a0 --- /dev/null +++ b/src/wasm_emulation/storage/mod.rs @@ -0,0 +1,11 @@ +pub mod dual_std_storage; +pub mod dual_storage; +pub mod mock_storage; +pub mod storage_wrappers; +pub use dual_storage::DualStorage; + +pub use mock_storage::MockStorage; + +pub mod analyzer; + +pub const CLONE_TESTING_STORAGE_LOG: &str = "clone_testing_storage"; diff --git a/src/wasm_emulation/storage/storage_wrappers.rs b/src/wasm_emulation/storage/storage_wrappers.rs new file mode 100644 index 00000000..95e7bcbc --- /dev/null +++ b/src/wasm_emulation/storage/storage_wrappers.rs @@ -0,0 +1,71 @@ +use cosmwasm_std::{Order, Record, Storage}; + +pub struct StorageWrapper<'a> { + storage: &'a mut dyn Storage, +} + +impl<'a> StorageWrapper<'a> { + pub fn new(storage: &'a mut dyn Storage) -> Self { + StorageWrapper { storage } + } +} + +impl Storage for StorageWrapper<'_> { + fn get(&self, key: &[u8]) -> Option> { + self.storage.get(key) + } + + fn set(&mut self, key: &[u8], value: &[u8]) { + self.storage.set(key, value) + } + + fn remove(&mut self, key: &[u8]) { + self.storage.remove(key) + } + + /// range allows iteration over a set of keys, either forwards or backwards + /// uses standard rust range notation, and eg db.range(b"foo"..b"bar") also works reverse + fn range<'b>( + &'b self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box + 'b> { + self.storage.range(start, end, order) + } +} + +pub struct ReadonlyStorageWrapper<'a> { + storage: &'a dyn Storage, +} + +impl<'a> ReadonlyStorageWrapper<'a> { + pub fn new(storage: &'a dyn Storage) -> Self { + ReadonlyStorageWrapper { storage } + } +} + +impl Storage for ReadonlyStorageWrapper<'_> { + fn get(&self, key: &[u8]) -> Option> { + self.storage.get(key) + } + + fn set(&mut self, _key: &[u8], _value: &[u8]) { + unimplemented!() + } + + fn remove(&mut self, _key: &[u8]) { + unimplemented!() + } + + /// range allows iteration over a set of keys, either forwards or backwards + /// uses standard rust range notation, and eg db.range(b"foo"..b"bar") also works reverse + fn range<'b>( + &'b self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box + 'b> { + self.storage.range(start, end, order) + } +} diff --git a/tests/mod.rs b/tests/mod.rs index c7e7a91b..7c81a535 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -1,5 +1,11 @@ #![cfg(test)] +use clone_cw_multi_test::wasm_emulation::channel::RemoteChannel; +use clone_cw_multi_test::{no_init, App}; +use cw_multi_test::wasm_emulation::query::ContainsRemote; +use cw_orch_daemon::networks::XION_TESTNET_1; +use cw_orch_daemon::RUNTIME; + mod test_api; mod test_app; mod test_app_builder; @@ -12,15 +18,17 @@ mod test_prefixed_storage; mod test_staking; mod test_wasm; +extern crate clone_cw_multi_test as cw_multi_test; + mod test_contracts { pub mod counter { + use clone_cw_multi_test::{Contract, ContractWrapper}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ to_json_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdError, WasmMsg, }; - use cw_multi_test::{Contract, ContractWrapper}; use cw_storage_plus::Item; const COUNTER: Item = Item::new("counter"); @@ -80,3 +88,19 @@ mod test_contracts { } } } + +use cw_orch::prelude::ChainInfo; +pub const CHAIN: ChainInfo = XION_TESTNET_1; +pub fn remote_channel() -> RemoteChannel { + RemoteChannel::new( + &RUNTIME, + CHAIN.grpc_urls, + CHAIN.chain_id, + CHAIN.network_info.pub_address_prefix, + ) + .unwrap() +} + +pub fn default_app() -> App { + App::new(no_init).with_remote(remote_channel()) +} diff --git a/tests/test_app/test_block_info.rs b/tests/test_app/test_block_info.rs index 9ddee51c..7b5d9240 100644 --- a/tests/test_app/test_block_info.rs +++ b/tests/test_app/test_block_info.rs @@ -1,11 +1,13 @@ use cosmwasm_std::testing::mock_env; use cosmwasm_std::{BlockInfo, Timestamp}; -use cw_multi_test::{next_block, App}; +use cw_multi_test::next_block; + +use crate::default_app; #[test] fn default_block_info_should_work() { let env = mock_env(); - let app = App::default(); + let app = default_app(); let block = app.block_info(); assert_eq!(env.block.chain_id, block.chain_id); assert_eq!(env.block.height, block.height); @@ -19,7 +21,7 @@ fn setting_block_info_should_work() { height: 273_094, time: Timestamp::default().plus_days(366), }; - let mut app = App::default(); + let mut app = default_app(); app.set_block(initial_block.clone()); let block = app.block_info(); assert_eq!(initial_block.chain_id, block.chain_id); @@ -30,7 +32,7 @@ fn setting_block_info_should_work() { #[test] fn incrementing_block_info_should_work() { let env = mock_env(); - let mut app = App::default(); + let mut app = default_app(); app.update_block(next_block); let block = app.block_info(); assert_eq!(env.block.chain_id, block.chain_id); diff --git a/tests/test_app/test_initialize_app.rs b/tests/test_app/test_initialize_app.rs index 977d9947..3c2da198 100644 --- a/tests/test_app/test_initialize_app.rs +++ b/tests/test_app/test_initialize_app.rs @@ -1,13 +1,15 @@ use cw_multi_test::App; use cw_storage_plus::Map; +use crate::default_app; + const USER: &str = "user"; const USERS: Map<&str, u64> = Map::new("users"); const AMOUNT: u64 = 100; #[test] fn initializing_app_should_work() { - let mut app = App::default(); + let mut app = default_app(); let mut amount = 0; app.init_modules(|_router, api, storage| { USERS diff --git a/tests/test_app/test_store_code.rs b/tests/test_app/test_store_code.rs index ef3123c7..4262c099 100644 --- a/tests/test_app/test_store_code.rs +++ b/tests/test_app/test_store_code.rs @@ -1,10 +1,10 @@ -use crate::test_contracts::counter; +use crate::{default_app, test_contracts::counter}; use cw_multi_test::App; #[test] fn storing_code_assigns_consecutive_identifiers() { // prepare the application - let mut app = App::default(); + let mut app = default_app(); // storing contract's code assigns consecutive code identifiers for i in 1..=10 { @@ -18,7 +18,7 @@ fn store_code_generates_default_address_for_creator() { use cosmwasm_std::testing::MockApi; // prepare the application - let mut app = App::default(); + let mut app = default_app(); // store contract's code let code_id = app.store_code(counter::contract()); diff --git a/tests/test_app/test_store_code_with_id.rs b/tests/test_app/test_store_code_with_id.rs index 382764b3..843736b6 100644 --- a/tests/test_app/test_store_code_with_id.rs +++ b/tests/test_app/test_store_code_with_id.rs @@ -1,9 +1,9 @@ -use crate::test_contracts::counter; +use crate::{default_app, test_contracts::counter}; use cw_multi_test::App; #[test] fn storing_code_with_custom_identifier_should_work() { - let mut app = App::default(); + let mut app = default_app(); let creator = app.api().addr_make("prometheus"); assert_eq!( 10, @@ -19,7 +19,7 @@ fn storing_code_with_custom_identifier_should_work() { #[test] fn zero_code_id_is_not_allowed() { - let mut app = App::default(); + let mut app = default_app(); let creator = app.api().addr_make("prometheus"); app.store_code_with_id(creator, 0, counter::contract()) .unwrap_err(); @@ -27,7 +27,7 @@ fn zero_code_id_is_not_allowed() { #[test] fn storing_code_with_consecutive_identifiers() { - let mut app = App::default(); + let mut app = default_app(); let creator = app.api().addr_make("prometheus"); assert_eq!( 11, @@ -41,7 +41,7 @@ fn storing_code_with_consecutive_identifiers() { #[test] fn storing_with_the_same_id_is_not_allowed() { - let mut app = App::default(); + let mut app = default_app(); let creator = app.api().addr_make("prometheus"); let code_id = 2056; assert_eq!( @@ -56,7 +56,7 @@ fn storing_with_the_same_id_is_not_allowed() { #[test] #[should_panic(expected = "no more code identifiers available")] fn no_more_identifiers_available() { - let mut app = App::default(); + let mut app = default_app(); let creator = app.api().addr_make("prometheus"); assert_eq!( u64::MAX, diff --git a/tests/test_app_builder/test_with_bank.rs b/tests/test_app_builder/test_with_bank.rs index a6b00f4a..b17906bc 100644 --- a/tests/test_app_builder/test_with_bank.rs +++ b/tests/test_app_builder/test_with_bank.rs @@ -1,10 +1,32 @@ use crate::test_app_builder::MyKeeper; +use anyhow::bail; use cosmwasm_std::{coins, BankMsg, BankQuery}; -use cw_multi_test::{no_init, AppBuilder, Bank, BankSudo, Executor}; +use cw_multi_test::{ + no_init, + wasm_emulation::query::{AllBankQuerier, ContainsRemote}, + AppBuilder, Bank, BankSudo, Executor, +}; type MyBankKeeper = MyKeeper; impl Bank for MyBankKeeper {} +impl AllBankQuerier for MyBankKeeper { + fn query_all( + &self, + _storage: &dyn cosmwasm_std::Storage, + ) -> anyhow::Result { + bail!(self.1) + } +} +impl ContainsRemote for MyBankKeeper { + fn with_remote(self, _remote: cw_multi_test::wasm_emulation::channel::RemoteChannel) -> Self { + todo!() + } + + fn set_remote(&mut self, _remote: cw_multi_test::wasm_emulation::channel::RemoteChannel) { + todo!() + } +} const EXECUTE_MSG: &str = "bank execute called"; const QUERY_MSG: &str = "bank query called"; diff --git a/tests/test_app_builder/test_with_wasm.rs b/tests/test_app_builder/test_with_wasm.rs index 258894c2..aa6fdc79 100644 --- a/tests/test_app_builder/test_with_wasm.rs +++ b/tests/test_app_builder/test_with_wasm.rs @@ -1,3 +1,6 @@ +use crate::cw_multi_test::wasm_emulation::input::WasmStorage; +use crate::cw_multi_test::wasm_emulation::query::AllWasmQuerier; +use crate::cw_multi_test::wasm_emulation::query::ContainsRemote; use crate::test_app_builder::MyKeeper; use crate::test_contracts; use cosmwasm_std::{ @@ -24,6 +27,16 @@ static WASM_RAW: Lazy> = Lazy::new(|| vec![(vec![154u8], vec![155u8] // when custom wasm keeper implements also Module trait (although it is not needed). type MyWasmKeeper = MyKeeper; +impl ContainsRemote for MyWasmKeeper { + fn with_remote(self, _remote: cw_multi_test::wasm_emulation::channel::RemoteChannel) -> Self { + todo!() + } + + fn set_remote(&mut self, _remote: cw_multi_test::wasm_emulation::channel::RemoteChannel) { + todo!() + } +} + impl Wasm for MyWasmKeeper { fn execute( &self, @@ -41,6 +54,7 @@ impl Wasm for MyWasmKeeper { &self, _api: &dyn Api, _storage: &dyn Storage, + _router: &dyn CosmosRouter, _querier: &dyn Querier, _block: &BlockInfo, _request: WasmQuery, @@ -83,6 +97,17 @@ impl Wasm for MyWasmKeeper { fn dump_wasm_raw(&self, _storage: &dyn Storage, _address: &Addr) -> Vec { WASM_RAW.clone() } + + /// Stores the contract's code and returns an identifier of the stored contract's code. + fn store_wasm_code(&mut self, _creator: Addr, _code: Vec) -> u64 { + CODE_ID + } +} + +impl AllWasmQuerier for MyWasmKeeper { + fn query_all(&self, _storage: &dyn Storage) -> AnyResult { + bail!(self.1) + } } #[test] diff --git a/tests/test_attributes/test_empty_attribute.rs b/tests/test_attributes/test_empty_attribute.rs index 137b9b21..d4bc8d89 100644 --- a/tests/test_attributes/test_empty_attribute.rs +++ b/tests/test_attributes/test_empty_attribute.rs @@ -1,5 +1,7 @@ use cosmwasm_std::Empty; -use cw_multi_test::{App, Contract, ContractWrapper, Executor}; +use cw_multi_test::{Contract, ContractWrapper, Executor}; + +use crate::default_app; mod test_contract { use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, Event, MessageInfo, Response, StdError}; @@ -45,7 +47,7 @@ fn contract() -> Box> { #[test] fn empty_string_attribute_should_work() { // prepare the blockchain - let mut app = App::default(); + let mut app = default_app(); // prepare address for creator=owner=sender let sender_addr = app.api().addr_make("sender"); diff --git a/tests/test_bank/test_init_balance.rs b/tests/test_bank/test_init_balance.rs index 44fd0172..fa55b76e 100644 --- a/tests/test_bank/test_init_balance.rs +++ b/tests/test_bank/test_init_balance.rs @@ -1,6 +1,8 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, CustomMsg, CustomQuery, Uint128}; -use cw_multi_test::{custom_app, App, AppBuilder, BasicApp}; +use cw_multi_test::{custom_app, wasm_emulation::query::ContainsRemote, App, AppBuilder, BasicApp}; + +use crate::{default_app, remote_channel}; const USER: &str = "user"; const DENOM: &str = "denom"; @@ -41,7 +43,8 @@ fn initializing_balance_without_builder_should_work() { .bank .init_balance(storage, &api.addr_make(USER), coins()) .unwrap(); - }); + }) + .with_remote(remote_channel()); assert_balance( app.wrap() .query_all_balances(app.api().addr_make(USER)) @@ -68,7 +71,8 @@ fn initializing_balance_custom_app_should_work() { .bank .init_balance(storage, &api.addr_make(USER), coins()) .unwrap(); - }); + }) + .with_remote(remote_channel()); assert_balance( app.wrap() .query_all_balances(app.api().addr_make(USER)) @@ -78,7 +82,7 @@ fn initializing_balance_custom_app_should_work() { #[test] fn initializing_balance_later_should_work() { - let mut app = App::default(); + let mut app = default_app(); app.init_modules(|router, api, storage| { router .bank diff --git a/tests/test_contract_storage/mod.rs b/tests/test_contract_storage/mod.rs index 1ac55bb4..43867d5a 100644 --- a/tests/test_contract_storage/mod.rs +++ b/tests/test_contract_storage/mod.rs @@ -1,3 +1,4 @@ +use crate::default_app; use crate::test_contracts::counter; use crate::test_contracts::counter::{CounterQueryMsg, CounterResponseMsg}; use cosmwasm_std::{to_json_binary, Empty, WasmMsg}; @@ -10,7 +11,7 @@ fn read_write_contract_storage_should_work() { const COUNTER: Item = Item::new("counter"); // prepare the blockchain - let mut app = App::default(); + let mut app = default_app(); // store the contract code let creator_addr = app.api().addr_make("creator"); diff --git a/tests/test_module/test_accepting_module.rs b/tests/test_module/test_accepting_module.rs index 9f085b5b..3ba23737 100644 --- a/tests/test_module/test_accepting_module.rs +++ b/tests/test_module/test_accepting_module.rs @@ -2,6 +2,8 @@ use cosmwasm_std::testing::MockStorage; use cosmwasm_std::{Binary, Empty}; use cw_multi_test::{AcceptingModule, App, AppResponse, Module}; +use crate::default_app; + /// Utility function for comparing responses. fn eq(actual: AppResponse, expected: AppResponse) { assert_eq!(actual.events, expected.events); @@ -10,7 +12,7 @@ fn eq(actual: AppResponse, expected: AppResponse) { /// Utility function for asserting default outputs returned from accepting module. fn assert_results(accepting_module: AcceptingModule) { - let app = App::default(); + let app = default_app(); let sender_addr = app.api().addr_make("sender"); let empty_msg = Empty {}; let mut storage = MockStorage::default(); diff --git a/tests/test_module/test_failing_module.rs b/tests/test_module/test_failing_module.rs index d8f01279..f9ac2c6e 100644 --- a/tests/test_module/test_failing_module.rs +++ b/tests/test_module/test_failing_module.rs @@ -2,9 +2,11 @@ use cosmwasm_std::testing::MockStorage; use cosmwasm_std::Empty; use cw_multi_test::{App, FailingModule, Module}; +use crate::default_app; + /// Utility function for asserting outputs returned from failing module. fn assert_results(failing_module: FailingModule) { - let app = App::default(); + let app = default_app(); let sender_addr = app.api().addr_make("sender"); let empty_msg = Empty {}; let mut storage = MockStorage::default(); diff --git a/tests/test_prefixed_storage/test_prefixed_multilevel_storage.rs b/tests/test_prefixed_storage/test_prefixed_multilevel_storage.rs index 03dd64c3..0978edda 100644 --- a/tests/test_prefixed_storage/test_prefixed_multilevel_storage.rs +++ b/tests/test_prefixed_storage/test_prefixed_multilevel_storage.rs @@ -4,6 +4,8 @@ use cw_storage_plus::Map; use cw_utils::NativeBalance; use std::ops::{Deref, DerefMut}; +use crate::default_app; + const NAMESPACE_CENTRAL_BANK: &[u8] = b"central-bank"; const NAMESPACE_NATIONAL_BANK: &[u8] = b"national-bank"; const NAMESPACE_LOCAL_BANK: &[u8] = b"local-bank"; @@ -19,7 +21,7 @@ fn multilevel_storage_should_work() { // prepare balance owner let owner_addr = "owner".into_addr(); // create the blockchain - let mut app = App::default(); + let mut app = default_app(); { // get the mutable prefixed, multilevel storage for banks let mut storage_mut = app.prefixed_multilevel_storage_mut(NAMESPACES); diff --git a/tests/test_prefixed_storage/test_prefixed_storage_bank.rs b/tests/test_prefixed_storage/test_prefixed_storage_bank.rs index 653ddcf8..36caa2db 100644 --- a/tests/test_prefixed_storage/test_prefixed_storage_bank.rs +++ b/tests/test_prefixed_storage/test_prefixed_storage_bank.rs @@ -1,9 +1,11 @@ use cosmwasm_std::{coin, Addr}; -use cw_multi_test::{App, IntoAddr}; +use cw_multi_test::{wasm_emulation::query::ContainsRemote, App, IntoAddr}; use cw_storage_plus::Map; use cw_utils::NativeBalance; use std::ops::{Deref, DerefMut}; +use crate::{default_app, remote_channel}; + const NAMESPACE_BANK: &[u8] = b"bank"; const BALANCES: Map<&Addr, NativeBalance> = Map::new("balances"); @@ -19,7 +21,8 @@ fn reading_bank_storage_should_work() { .bank .init_balance(storage, &owner_addr, init_funds) .unwrap(); - }); + }) + .with_remote(remote_channel()); // get the read-only prefixed storage for bank let storage = app.prefixed_storage(NAMESPACE_BANK); @@ -32,7 +35,7 @@ fn writing_bank_storage_should_work() { // prepare balance owner let owner_addr = "owner".into_addr(); - let mut app = App::default(); + let mut app = default_app(); // get the mutable prefixed storage for bank let mut storage = app.prefixed_storage_mut(NAMESPACE_BANK); diff --git a/tests/test_wasm/test_with_checksum_gen.rs b/tests/test_wasm/test_with_checksum_gen.rs index 47685d31..3c1bca3e 100644 --- a/tests/test_wasm/test_with_checksum_gen.rs +++ b/tests/test_wasm/test_with_checksum_gen.rs @@ -1,3 +1,4 @@ +use crate::default_app; use crate::test_contracts; use cosmwasm_std::{Addr, Checksum}; use cw_multi_test::{no_init, App, AppBuilder, ChecksumGenerator, WasmKeeper}; @@ -5,7 +6,7 @@ use cw_multi_test::{no_init, App, AppBuilder, ChecksumGenerator, WasmKeeper}; #[test] fn default_checksum_generator_should_work() { // prepare default application with default wasm keeper - let mut app = App::default(); + let mut app = default_app(); // prepare user addresses let creator_addr = app.api().addr_make("creator");