diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..3f66687da --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,919 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "anyhow" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[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.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "num-traits", +] + +[[package]] +name = "const-oid" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" + +[[package]] +name = "cosmwasm-crypto" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e272708a9745dad8b591ef8a718571512130f2b39b33e3d7a27c558e3069394" +dependencies = [ + "digest 0.10.7", + "ed25519-zebra", + "k256", + "rand_core 0.6.4", + "thiserror", +] + +[[package]] +name = "cosmwasm-derive" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "296db6a3caca5283425ae0cf347f4e46999ba3f6620dbea8939a0e00347831ce" +dependencies = [ + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-schema" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63c337e097a089e5b52b5d914a7ff6613332777f38ea6d9d36e1887cd0baa72e" +dependencies = [ + "cosmwasm-schema-derive", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "766cc9e7c1762d8fc9c0265808910fcad755200cd0e624195a491dd885a61169" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-std" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5e05a95fd2a420cca50f4e94eb7e70648dac64db45e90403997ebefeb143bd" +dependencies = [ + "base64", + "bnum", + "cosmwasm-crypto", + "cosmwasm-derive", + "derivative", + "forward_ref", + "hex", + "schemars", + "serde", + "serde-json-wasm 0.5.1", + "sha2 0.10.7", + "thiserror", +] + +[[package]] +name = "cosmwasm-storage" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "800aaddd70ba915e19bf3d2d992aa3689d8767857727fdd3b414df4fd52d2aa1" +dependencies = [ + "cosmwasm-std", + "serde", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +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 = "cw-multi-test" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f9a8ab7c3c29ec93cb7a39ce4b14a05e053153b4a17ef7cf2246af1b7c087e" +dependencies = [ + "anyhow", + "cosmwasm-std", + "cosmwasm-storage", + "cw-storage-plus 0.13.4", + "cw-utils", + "derivative", + "itertools", + "prost 0.9.0", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-storage-plus" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + +[[package]] +name = "cw-storage-plus" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b6f91c0b94481a3e9ef1ceb183c37d00764f8751e39b45fc09f4d9b970d469" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + +[[package]] +name = "cw-utils" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw2" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" +dependencies = [ + "cosmwasm-std", + "cw-storage-plus 0.13.4", + "schemars", + "serde", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[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 = "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", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clone" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek", + "hashbrown", + "hex", + "rand_core 0.6.4", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "forward_ref" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[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 = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.7", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "osmosis-std" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b3792977036dc49cfc9af9fd7a6c021fd48dfffc8ebf09324201506c65a47a" +dependencies = [ + "chrono", + "cosmwasm-std", + "osmosis-std-derive", + "prost 0.11.9", + "prost-types", + "schemars", + "serde", + "serde-cw-value", +] + +[[package]] +name = "osmosis-std-derive" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c501f2b8ff88b1c60ab671d7b808e947f384fa2524fe4ec8c06f63ef4be29979" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +dependencies = [ + "bytes", + "prost-derive 0.9.0", +] + +[[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-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[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 = "quote" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +dependencies = [ + "proc-macro2", +] + +[[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 = "rate-limiter" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-multi-test", + "cw-storage-plus 0.16.0", + "cw2", + "hex", + "osmosis-std", + "osmosis-std-derive", + "prost 0.11.9", + "schemars", + "semver", + "serde", + "serde-json-wasm 0.4.1", + "sha2 0.10.7", + "thiserror", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schemars" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "serde" +version = "1.0.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb30a74471f5b7a1fa299f40b4bf1be93af61116df95465b2b5fc419331e430" +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.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" +dependencies = [ + "serde", +] + +[[package]] +name = "serde-json-wasm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f4c2c6ea4bc09b5c419012eafcdb0fcef1d9119d626c8f3a0708a5b92d38a70" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +dependencies = [ + "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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..8ba6289e0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +resolver = "2" +members = [ + "x/ibc-rate-limit/contracts/rate-limiter" +] \ No newline at end of file diff --git a/x/ibc-rate-limit/Cargo.lock b/x/ibc-rate-limit/Cargo.lock index 07e4f7e4a..20fa3576f 100644 --- a/x/ibc-rate-limit/Cargo.lock +++ b/x/ibc-rate-limit/Cargo.lock @@ -663,6 +663,7 @@ dependencies = [ "osmosis-std-derive", "prost 0.11.2", "schemars", + "semver", "serde", "serde-json-wasm", "sha2 0.10.6", @@ -724,6 +725,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + [[package]] name = "serde" version = "1.0.147" diff --git a/x/ibc-rate-limit/bytecode/rate_limiter.wasm b/x/ibc-rate-limit/bytecode/rate_limiter.wasm index 9dee6eabb..add304354 100644 Binary files a/x/ibc-rate-limit/bytecode/rate_limiter.wasm and b/x/ibc-rate-limit/bytecode/rate_limiter.wasm differ diff --git a/x/ibc-rate-limit/contracts/rate-limiter/Cargo.toml b/x/ibc-rate-limit/contracts/rate-limiter/Cargo.toml index 9a82ff8d9..c1743ac6b 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/Cargo.toml +++ b/x/ibc-rate-limit/contracts/rate-limiter/Cargo.toml @@ -35,7 +35,7 @@ optimize = """docker run --rm -v "$(pwd)":/code \ cosmwasm-std = { version = "1.1.5", features = ["stargate", "cosmwasm_1_1"]} cosmwasm-schema = "1.1.5" cosmwasm-storage = "1.1.5" -cw-storage-plus = "0.16.0" +cw-storage-plus = {version = "0.16.0", features = ["iterator"]} cw2 = "0.13.2" schemars = "0.8.8" serde = { version = "1.0.137", default-features = false, features = ["derive"] } @@ -45,6 +45,8 @@ osmosis-std-derive = {version = "0.12.0"} osmosis-std = "0.12.0" sha2 = "0.10.6" hex = "0.4.3" +semver = "1" + [dev-dependencies] cw-multi-test = "0.13.2" diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/contract.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/contract.rs index 30bae5b33..5d6fea520 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/contract.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/contract.rs @@ -1,7 +1,7 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; -use cw2::set_contract_version; +use cosmwasm_std::{Binary, Deps, DepsMut, Env, Event, MessageInfo, Response, StdError, StdResult}; +use cw2::{get_contract_version, set_contract_version, ContractVersion}; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, SudoMsg}; @@ -9,8 +9,11 @@ use crate::state::{FlowType, GOVMODULE, IBCMODULE}; use crate::{execute, query, sudo}; // version info for migration info -const CONTRACT_NAME: &str = "crates.io:rate-limiter"; -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub(crate) const CONTRACT_NAME: &str = "crates.io:rate-limiter"; + +pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + + #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -19,7 +22,13 @@ pub fn instantiate( _info: MessageInfo, msg: InstantiateMsg, ) -> Result { + // for testing purposes always set version to 0.1.0 + #[cfg(test)] + set_contract_version(deps.storage, CONTRACT_NAME, "0.1.0")?; + + #[cfg(not(test))] set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + IBCMODULE.save(deps.storage, &msg.ibc_module)?; GOVMODULE.save(deps.storage, &msg.gov_module)?; @@ -90,6 +99,10 @@ pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> Result sudo::undo_send(deps, packet), + SudoMsg::RolloverRules => { + crate::sudo::rollover_expired_rate_limits(deps, env)?; + Ok(Response::default()) + }, } } @@ -101,6 +114,6 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result { - unimplemented!() +pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> Result { + crate::migrations::migrate_internal(deps, env, msg) } diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/contract_tests.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/contract_tests.rs index b51009c06..d551940a5 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/contract_tests.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/contract_tests.rs @@ -1,18 +1,80 @@ #![cfg(test)] +use std::collections::HashMap; + +use crate::helpers::{expired_rate_limits}; +use crate::sudo::rollover_expired_rate_limits; +use crate::msg::MigrateMsg; use crate::packet::Packet; use crate::{contract::*, test_msg_recv, test_msg_send, ContractError}; -use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; -use cosmwasm_std::{from_binary, Addr, Attribute, Uint256}; - +use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockStorage, MockQuerier}; +use cosmwasm_std::{from_binary, Addr, Attribute, Env, Uint256, Querier, OwnedDeps, MemoryStorage}; +use cw_multi_test::{App, AppBuilder, BankKeeper, ContractWrapper, Executor}; +use cosmwasm_std::Timestamp; use crate::helpers::tests::verify_query_response; use crate::msg::{InstantiateMsg, PathMsg, QueryMsg, QuotaMsg, SudoMsg}; -use crate::state::tests::RESET_TIME_WEEKLY; +use crate::state::tests::{RESET_TIME_WEEKLY, RESET_TIME_DAILY, RESET_TIME_MONTHLY}; use crate::state::{RateLimit, GOVMODULE, IBCMODULE, RATE_LIMIT_TRACKERS}; - const IBC_ADDR: &str = "IBC_MODULE"; const GOV_ADDR: &str = "GOV_MODULE"; +pub const SECONDS_PER_DAY: u64 = 86400; +pub const SECONDS_PER_HOUR: u64 = 3600; + +pub(crate) struct TestEnv { + pub env: Env, + pub deps: OwnedDeps +} +fn new_test_env(paths: &[PathMsg]) -> TestEnv { + + let mut deps: OwnedDeps = mock_dependencies(); + let env = mock_env(); + let msg = InstantiateMsg { + gov_module: Addr::unchecked(GOV_ADDR), + ibc_module: Addr::unchecked(IBC_ADDR), + paths: paths.to_vec(), + }; + let info = mock_info(GOV_ADDR, &vec![]); + instantiate(deps.as_mut(), env.clone(), info, msg).unwrap(); + + TestEnv { + deps, + env, + } +} + +impl TestEnv { + pub fn plus_hours(&mut self, hours: u64) { + self.env.block.time = self.env.block.time.plus_seconds( hours * SECONDS_PER_HOUR); + } + pub fn plus_days(&mut self, days: u64) { + self.env.block.time = self.env.block.time.plus_seconds(days * SECONDS_PER_DAY); + } +} + +// performs a very basic migration test, ensuring that standard migration logic works +#[test] +fn test_basic_migration() { + let test_env = new_test_env(&[PathMsg { + channel_id: format!("any"), + denom: format!("denom"), + quotas: vec![QuotaMsg::new("weekly", RESET_TIME_WEEKLY, 10, 10)], + }]); + + + + for key in RATE_LIMIT_TRACKERS.keys(&test_env.deps.storage, None, None, cosmwasm_std::Order::Ascending) { + match key { + Ok((k, v)) => { + println!("got key {}, {}", k, v); + } + Err(err) => { + println!("got error {err:#?}"); + } + } + } +} + #[test] // Tests we ccan instantiate the contract and that the owners are set correctly fn proper_instantiation() { let mut deps = mock_dependencies(); @@ -219,7 +281,7 @@ fn asymetric_quotas() { #[test] // Tests we can get the current state of the trackers fn query_state() { - let mut deps = mock_dependencies(); + let mut deps: OwnedDeps = mock_dependencies(); let quota = QuotaMsg::new("weekly", RESET_TIME_WEEKLY, 10, 10); let msg = InstantiateMsg { @@ -397,3 +459,154 @@ fn test_tokenfactory_message() { let _parsed: SudoMsg = serde_json_wasm::from_str(json).unwrap(); //println!("{parsed:?}"); } + + +#[test] +fn test_expired_rate_limits() { + let mut test_env = new_test_env(&[PathMsg { + channel_id: format!("any"), + denom: format!("denom"), + quotas: vec![ + QuotaMsg::new("daily", RESET_TIME_DAILY, 1, 1), + QuotaMsg::new("weekly", RESET_TIME_WEEKLY, 5, 5), + QuotaMsg::new("monthly", RESET_TIME_MONTHLY, 5, 5), + ], + }]); + // no rules should be expired + let expired_limits = expired_rate_limits(test_env.deps.as_ref(), test_env.env.block.time); + assert_eq!(expired_limits.len(), 0); + + // advance timestamp by half day + test_env.plus_hours(12); + + // still no rules should be expired + let expired_limits = expired_rate_limits(test_env.deps.as_ref(), test_env.env.block.time); + assert_eq!(expired_limits.len(), 0); + + // advance timestamp by 13 hours + test_env.plus_hours(13); + + // only 1 rule should be expired + let expired_limits = expired_rate_limits(test_env.deps.as_ref(), test_env.env.block.time); + assert_eq!(expired_limits[0].1.len(), 1); + assert_eq!(expired_limits[0].1[0].quota.name, "daily"); + + // advance timestamp by 6 days + test_env.plus_days(6); + + // weekly + daily rules should be expired + let expired_limits = expired_rate_limits(test_env.deps.as_ref(), test_env.env.block.time); + assert_eq!(expired_limits[0].1.len(), 2); + // as long as the ordering of the `range(..)` function is the same + // this test shouldn't fail + assert_eq!(expired_limits[0].1[0].quota.name, "daily"); + assert_eq!(expired_limits[0].1[1].quota.name, "weekly"); + // advance timestamp by 24 days for a total of 31 days passed + test_env.plus_days(24); + + // daily, weekly, monthly rules should be expired + let expired_limits = expired_rate_limits(test_env.deps.as_ref(), test_env.env.block.time); + assert_eq!(expired_limits[0].1.len(), 3); + assert_eq!(expired_limits[0].1[0].quota.name, "daily"); + assert_eq!(expired_limits[0].1[1].quota.name, "weekly"); + assert_eq!(expired_limits[0].1[2].quota.name, "monthly"); +} +#[test] +fn test_rollover_expired_rate_limits() { + let mut test_env = new_test_env(&[PathMsg { + channel_id: format!("any"), + denom: format!("denom"), + quotas: vec![ + QuotaMsg::new("daily", RESET_TIME_DAILY, 1, 1), + QuotaMsg::new("weekly", RESET_TIME_WEEKLY, 5, 5), + QuotaMsg::new("monthly", RESET_TIME_MONTHLY, 5, 5), + ], + }]); + + // shorthand for returning all rules + fn get_rules(test_env: &TestEnv) -> HashMap { + let rules = RATE_LIMIT_TRACKERS.range(&test_env.deps.storage, None, None, cosmwasm_std::Order::Ascending).flatten().collect::>(); + let mut indexed_rules: HashMap = HashMap::new(); + rules.into_iter().for_each(|(_, rules)| { + rules.into_iter().for_each(|rule| {indexed_rules.insert(rule.quota.name.clone(), rule);}); + }); + indexed_rules + } + + // store a copy of the unchanged rules + let original_rules = get_rules(&test_env); + // ensure the helper function indexes rules as expected + assert!(original_rules.contains_key("daily")); + assert!(original_rules.contains_key("weekly")); + assert!(original_rules.contains_key("monthly")); + + // no rules should be expired + let expired_limits = expired_rate_limits(test_env.deps.as_ref(), test_env.env.block.time); + assert_eq!(expired_limits.len(), 0); + + // advance timestamp by a day + test_env.plus_hours(25); + + // only 1 rule should be expired + let expired_limits = expired_rate_limits(test_env.deps.as_ref(), test_env.env.block.time); + assert_eq!(expired_limits[0].1.len(), 1); + assert_eq!(expired_limits[0].1[0].quota.name, "daily"); + + // trigger expiration of daily rate limits + rollover_expired_rate_limits(test_env.deps.as_mut(), test_env.env.clone()).unwrap(); + + // store a copy of rules after the daily limit has changed + let daily_rules_changed = get_rules(&test_env); + // ensure the daily period is different + assert!(daily_rules_changed.get("daily").unwrap().flow.period_end > original_rules.get("daily").unwrap().flow.period_end); + // ensure weekly and monthly rules are the same + assert!(daily_rules_changed.get("weekly").unwrap().flow.period_end == original_rules.get("weekly").unwrap().flow.period_end); + assert!(daily_rules_changed.get("monthly").unwrap().flow.period_end == original_rules.get("monthly").unwrap().flow.period_end); + + // advance timestamp by half day, no rules should be changed + test_env.plus_hours(12); + + // there should be no expired rate limits + let expired_limits = expired_rate_limits(test_env.deps.as_ref(), test_env.env.block.time); + assert_eq!(expired_limits.len(), 0); + + // advance timestamp by another half day + test_env.plus_hours(13); + + // daily rule should change again + rollover_expired_rate_limits(test_env.deps.as_mut(), test_env.env.clone()).unwrap(); + + let daily_rules_changed2 = get_rules(&test_env); + // ensure the daily period is different + assert!(daily_rules_changed2.get("daily").unwrap().flow.period_end > daily_rules_changed.get("daily").unwrap().flow.period_end); + // ensure weekly and monthly rules are the same + assert!(daily_rules_changed2.get("weekly").unwrap().flow.period_end == original_rules.get("weekly").unwrap().flow.period_end); + assert!(daily_rules_changed2.get("monthly").unwrap().flow.period_end == original_rules.get("monthly").unwrap().flow.period_end); + + // advance timestamp by 6 days + test_env.plus_days(6); + + // daily rule + weekly rules should change + rollover_expired_rate_limits(test_env.deps.as_mut(), test_env.env.clone()).unwrap(); + + let weekly_rules_changed = get_rules(&test_env); + // ensure the daily period is different + assert!(weekly_rules_changed.get("daily").unwrap().flow.period_end > daily_rules_changed2.get("daily").unwrap().flow.period_end); + // ensure weekly is different + assert!(weekly_rules_changed.get("weekly").unwrap().flow.period_end > daily_rules_changed2.get("weekly").unwrap().flow.period_end); + // ensure monthly is unchanged + assert!(weekly_rules_changed.get("monthly").unwrap().flow.period_end == original_rules.get("monthly").unwrap().flow.period_end); + + // advance timestamp by 24 days + test_env.plus_days(24); + + // all rules should now rollover + rollover_expired_rate_limits(test_env.deps.as_mut(), test_env.env.clone()).unwrap(); + + let monthly_rules_changed = get_rules(&test_env); + // ensure all three periods have reset + assert!(monthly_rules_changed.get("daily").unwrap().flow.period_end > weekly_rules_changed.get("daily").unwrap().flow.period_end); + assert!(monthly_rules_changed.get("weekly").unwrap().flow.period_end > weekly_rules_changed.get("weekly").unwrap().flow.period_end); + assert!(monthly_rules_changed.get("monthly").unwrap().flow.period_end > weekly_rules_changed.get("monthly").unwrap().flow.period_end); + +} \ No newline at end of file diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/error.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/error.rs index f5dcda946..2e24c7d04 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/error.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/error.rs @@ -26,4 +26,13 @@ pub enum ContractError { channel_id: String, denom: String, }, + #[error("semver parse error {0}")] + SemVer(String) } + +impl From for ContractError { + fn from(err: semver::Error) -> Self { + Self::SemVer(err.to_string()) + } +} + diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/helpers.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/helpers.rs index 530d3b6cf..0e02928f6 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/helpers.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/helpers.rs @@ -1,4 +1,8 @@ #![cfg(test)] +use cosmwasm_std::Deps; +use cosmwasm_std::DepsMut; +use cosmwasm_std::Env; +use cosmwasm_std::Timestamp; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -6,6 +10,9 @@ use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg}; use crate::msg::ExecuteMsg; use crate::msg::SudoMsg; +use crate::state::RateLimit; +use crate::state::RATE_LIMIT_TRACKERS; +use crate::ContractError; /// CwTemplateContract is a wrapper around Addr that provides a lot of helpers /// for working with this. @@ -36,6 +43,27 @@ impl RateLimitingContract { } } +/// returns all rate limits that have expired and can be rolled over +pub fn expired_rate_limits(deps: Deps, time: Timestamp) -> Vec<((String, String), Vec)> { + RATE_LIMIT_TRACKERS + .range(deps.storage, None, None, cosmwasm_std::Order::Ascending) + .flatten() + .filter_map(|(k, rules)| { + let rules = rules + .into_iter() + .filter(|rule| rule.flow.is_expired(time)) + .collect::>(); + if rules.is_empty() { + return None; + } + Some(( + k, + rules, + )) + }) + .collect() +} + pub mod tests { use cosmwasm_std::{Timestamp, Uint256}; diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/integration_tests.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/integration_tests.rs index bd9befeb8..5dc918f9b 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/integration_tests.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/integration_tests.rs @@ -1,8 +1,8 @@ #![cfg(test)] -use crate::{helpers::RateLimitingContract, msg::ExecuteMsg, test_msg_send, ContractError}; -use cosmwasm_std::{Addr, Coin, Empty, Timestamp, Uint128, Uint256}; +use crate::{helpers::RateLimitingContract, msg::{ExecuteMsg, MigrateMsg}, test_msg_send, ContractError}; +use cosmwasm_std::{Addr, Coin, Empty, Timestamp, Uint128, Uint256, testing::{mock_env, mock_dependencies}}; use cw_multi_test::{App, AppBuilder, Contract, ContractWrapper, Executor}; - +use crate::contract::migrate; use crate::{ msg::{InstantiateMsg, PathMsg, QuotaMsg}, state::tests::{RESET_TIME_DAILY, RESET_TIME_MONTHLY, RESET_TIME_WEEKLY}, @@ -415,3 +415,139 @@ fn add_paths_later() { let cosmos_msg = cw_rate_limit_contract.sudo(msg); app.sudo(cosmos_msg).unwrap_err(); } + + +#[test] // Tests we can have different maximums for different quotaas (daily, weekly, etc) and that they all are active at the same time +fn rate_limit_rollover() { + let quotas = vec![ + QuotaMsg::new("daily", RESET_TIME_DAILY, 1, 1), + QuotaMsg::new("weekly", RESET_TIME_WEEKLY, 5, 5), + QuotaMsg::new("monthly", RESET_TIME_MONTHLY, 5, 5), + ]; + + let (mut app, cw_rate_limit_contract) = proper_instantiate(vec![PathMsg { + channel_id: format!("any"), + denom: format!("denom"), + quotas, + }]); + + // Sending 1% to use the daily allowance + let msg = test_msg_send!( + channel_id: format!("channel"), + denom: format!("denom"), + channel_value: 101_u32.into(), + funds: 1_u32.into() + ); + let cosmos_msg = cw_rate_limit_contract.sudo(msg); + app.sudo(cosmos_msg).unwrap(); + + // Another packet is rate limited + let msg = test_msg_send!( + channel_id: format!("channel"), + denom: format!("denom"), + channel_value: 101_u32.into(), + funds: 1_u32.into() + ); + let cosmos_msg = cw_rate_limit_contract.sudo(msg); + app.sudo(cosmos_msg).unwrap_err(); + + // ... One day passes + app.update_block(|b| { + b.height += 10; + b.time = b.time.plus_seconds(RESET_TIME_DAILY + 1) + }); + + // Sending the packet should work now + let msg = test_msg_send!( + channel_id: format!("channel"), + denom: format!("denom"), + channel_value: 101_u32.into(), + funds: 1_u32.into() + ); + + let cosmos_msg = cw_rate_limit_contract.sudo(msg); + app.sudo(cosmos_msg).unwrap(); + + // Do that for 4 more days + for _ in 1..4 { + // ... One day passes + app.update_block(|b| { + b.height += 10; + b.time = b.time.plus_seconds(RESET_TIME_DAILY + 1) + }); + + // Sending the packet should work now + let msg = test_msg_send!( + channel_id: format!("channel"), + denom: format!("denom"), + channel_value: 101_u32.into(), + funds: 1_u32.into() + ); + let cosmos_msg = cw_rate_limit_contract.sudo(msg); + app.sudo(cosmos_msg).unwrap(); + } + + // ... One day passes + app.update_block(|b| { + b.height += 10; + b.time = b.time.plus_seconds(RESET_TIME_DAILY + 1) + }); + + // We now have exceeded the weekly limit! Even if the daily limit allows us, the weekly doesn't + let msg = test_msg_send!( + channel_id: format!("channel"), + denom: format!("denom"), + channel_value: 101_u32.into(), + funds: 1_u32.into() + ); + let cosmos_msg = cw_rate_limit_contract.sudo(msg); + app.sudo(cosmos_msg).unwrap_err(); + + // ... One week passes + app.update_block(|b| { + b.height += 10; + b.time = b.time.plus_seconds(RESET_TIME_WEEKLY + 1) + }); + + // We can still can't send because the weekly and monthly limits are the same + let msg = test_msg_send!( + channel_id: format!("channel"), + denom: format!("denom"), + channel_value: 101_u32.into(), + funds: 1_u32.into() + ); + let cosmos_msg = cw_rate_limit_contract.sudo(msg); + app.sudo(cosmos_msg).unwrap_err(); + + // Waiting a week again, doesn't help!! + // ... One week passes + app.update_block(|b| { + b.height += 10; + b.time = b.time.plus_seconds(RESET_TIME_WEEKLY + 1) + }); + + // We can still can't send because the monthly limit hasn't passed + let msg = test_msg_send!( + channel_id: format!("channel"), + denom: format!("denom"), + channel_value: 101_u32.into(), + funds: 1_u32.into() + ); + let cosmos_msg = cw_rate_limit_contract.sudo(msg); + app.sudo(cosmos_msg).unwrap_err(); + + // Only after two more weeks we can send again + app.update_block(|b| { + b.height += 10; + b.time = b.time.plus_seconds((RESET_TIME_WEEKLY * 2) + 1) // Two weeks + }); + + let msg = test_msg_send!( + channel_id: format!("channel"), + denom: format!("denom"), + channel_value: 101_u32.into(), + funds: 1_u32.into() + ); + let cosmos_msg = cw_rate_limit_contract.sudo(msg); + app.sudo(cosmos_msg).unwrap(); +} \ No newline at end of file diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/lib.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/lib.rs index 6fcd1c32c..8db94911b 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/lib.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/lib.rs @@ -6,6 +6,7 @@ mod error; pub mod msg; mod state; +mod migrations; pub mod packet; // Functions @@ -14,8 +15,10 @@ mod query; mod sudo; // Tests -mod contract_tests; -mod helpers; +#[cfg(test)] +pub mod contract_tests; + +pub(crate) mod helpers; mod integration_tests; pub use crate::error::ContractError; diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/migrations/mod.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/migrations/mod.rs new file mode 100644 index 000000000..56ed37a94 --- /dev/null +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/migrations/mod.rs @@ -0,0 +1,56 @@ +use cosmwasm_std::{DepsMut, Env, Event, Response, StdError}; +use cw2::{get_contract_version, set_contract_version, ContractVersion}; + +use crate::{contract::CONTRACT_NAME, msg::MigrateMsg, ContractError}; + +pub mod v1; + +pub(crate) fn migrate_internal( + deps: DepsMut, + env: Env, + msg: MigrateMsg, +) -> Result { + let c_version = get_contract_version(deps.storage)?; + if &c_version.contract != CONTRACT_NAME { + return Err(StdError::generic_err("Can only upgrade from same type").into()); + } + let stored_version: semver::Version = c_version.version.parse()?; + + #[cfg(test)] + let code_version = get_code_version(stored_version.clone()); + #[cfg(not(test))] + let code_version = get_code_version(); + + if stored_version < code_version { + // update version + set_contract_version(deps.storage, CONTRACT_NAME, code_version.to_string())?; + + if stored_version.major == 0 && stored_version.minor == 1 && stored_version.patch == 0 { + v1::v1_migrate(stored_version, code_version, deps, env, msg) + } else { + return Err(StdError::generic_err("Missing migrate function").into()); + } + } else { + return Err(StdError::generic_err("Can't upgrade from a newer version").into()); + } +} + +// intended for testing purposes only +#[cfg(test)] +fn get_code_version(stored_version: semver::Version) -> semver::Version { + if stored_version.major == 0 && stored_version.minor == 1 && stored_version.patch == 0 { + let code_version: semver::Version = "0.1.1".parse().unwrap(); + code_version + } else { + let code_version: semver::Version = "0.1.1".parse().unwrap(); + code_version + } +} + +// returns the version of the contract as defined in the codebase +#[cfg(not(test))] +fn get_code_version() -> semver::Version { + use crate::contract::CONTRACT_VERSION; + + CONTRACT_VERSION.parse().unwrap() +} diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/migrations/v1.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/migrations/v1.rs new file mode 100644 index 000000000..f990648d3 --- /dev/null +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/migrations/v1.rs @@ -0,0 +1,19 @@ +use cosmwasm_std::{DepsMut, Env, Event, Response, StdError}; +use cw2::{get_contract_version, set_contract_version, ContractVersion}; + +use crate::{contract::CONTRACT_NAME, msg::MigrateMsg, ContractError}; + +pub(crate) fn v1_migrate( + stored_version: semver::Version, + code_version: semver::Version, + deps: DepsMut, + env: Env, + msg: MigrateMsg, +) -> Result { + Ok(Response::default().add_event( + Event::new("migration_ok") + .add_attribute("migration_version", "v1") + .add_attribute("old_version", stored_version.to_string()) + .add_attribute("new_version", code_version.to_string()), + )) +} diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/msg.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/msg.rs index 57279c0ad..694d0f699 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/msg.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/msg.rs @@ -99,7 +99,8 @@ pub enum SudoMsg { UndoSend { packet: Packet, }, + RolloverRules, } #[cw_serde] -pub enum MigrateMsg {} +pub struct MigrateMsg {} diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/state.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/state.rs index e699936d8..6c36f760f 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/state.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/state.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Addr, Timestamp, Uint256}; +use cosmwasm_std::{Addr, Timestamp, Uint256, DepsMut, Env}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::cmp; @@ -232,7 +232,7 @@ pub struct RateLimit { // contract. This function takes that into account so that the channel value // that we track matches the channel value at the moment when the ibc // transaction started executing -fn calculate_channel_value( +pub(crate) fn calculate_channel_value( channel_value: Uint256, denom: &str, funds: Uint256, @@ -304,7 +304,6 @@ impl RateLimit { } } } - /// Only this address can manage the contract. This will likely be the /// governance module, but could be set to something else if needed pub const GOVMODULE: Item = Item::new("gov_module"); diff --git a/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs b/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs index 80e6985d5..2204cd383 100644 --- a/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs +++ b/x/ibc-rate-limit/contracts/rate-limiter/src/sudo.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{DepsMut, Response, Timestamp, Uint256}; +use cosmwasm_std::{DepsMut, Response, Timestamp, Uint256, Env}; use crate::{ packet::Packet, @@ -191,3 +191,26 @@ pub fn undo_send(deps: DepsMut, packet: Packet) -> Result Result<(), ContractError> { + // possible alternative here is to not collect the iterator, and then use a dequeue or something similiar to track rate limit keys that need to be updated + for ((channel_id, denom), mut rules) in RATE_LIMIT_TRACKERS.range(deps.storage, None, None, cosmwasm_std::Order::Ascending).flatten().collect::>() { + // avoid storage saves unless an actual rule was updated + let mut rule_updated = false; + rules.iter_mut().for_each(|rule| { + if rule.flow.is_expired(env.block.time) { + rule.flow.expire(env.block.time, rule.quota.duration); + // is this the correct way to reset the channel value?? + rule.quota.channel_value = Some(Uint256::zero()); + rule_updated = true; + } + }); + if rule_updated { + RATE_LIMIT_TRACKERS.save(deps.storage, (channel_id, denom), &rules)?; + } + } + + Ok(()) +} diff --git a/x/ibc-rate-limit/ibc_middleware_test.go b/x/ibc-rate-limit/ibc_middleware_test.go index 3a3e99c59..229127b6f 100644 --- a/x/ibc-rate-limit/ibc_middleware_test.go +++ b/x/ibc-rate-limit/ibc_middleware_test.go @@ -1,6 +1,7 @@ package ibc_rate_limit_test import ( + "encoding/json" "fmt" "strconv" "strings" @@ -538,3 +539,155 @@ func (suite *MiddlewareTestSuite) TestUnsetRateLimitingContract() { // N.B.: this panics if validation fails. paramSpace.SetParamSet(suite.chainA.GetContext(), ¶ms) } + +// Test rate limiting on sends +func (suite *MiddlewareTestSuite) Test_Query_RateLimit_NonNative() { + // Sends denom=stake from A->B. Rate limit receives "stake" in the packet. Nothing to do in the contract + attrs := suite.fullSendTest(false) + contractAddress, err := sdk.AccAddressFromBech32(attrs["_contract_address"]) + suite.Require().NoError(err) + denom := attrs["denom"] + channel := attrs["channel_id"] + key := fmt.Sprintf(`{"get_quotas": {"channel_id": "%s", "denom": "%s"}}`, channel, denom) + + chainCtx := suite.chainA.GetContext() + osmosisApp := suite.chainA.GetOsmosisApp() + gotContract := osmosisApp.RateLimitingICS4Wrapper.GetContractAddress(chainCtx) + suite.Require().Equal(gotContract, attrs["_contract_address"]) + + res, err := osmosisApp.WasmKeeper.QuerySmart(chainCtx, contractAddress, []byte(key)) + suite.Require().NoError(err) + suite.Require().NotNil(res) + +} + +// Test rate limiting on sends +func (suite *MiddlewareTestSuite) Test_Query_RateLimit_Native() { + // Sends denom=stake from A->B. Rate limit receives "stake" in the packet. Nothing to do in the contract + attrs := suite.fullSendTest(true) + contractAddress, err := sdk.AccAddressFromBech32(attrs["_contract_address"]) + suite.Require().NoError(err) + denom := attrs["denom"] + channel := attrs["channel_id"] + key := fmt.Sprintf(`{"get_quotas": {"channel_id": "%s", "denom": "%s"}}`, channel, denom) + + chainCtx := suite.chainA.GetContext() + osmosisApp := suite.chainA.GetOsmosisApp() + gotContract := osmosisApp.RateLimitingICS4Wrapper.GetContractAddress(chainCtx) + suite.Require().Equal(gotContract, attrs["_contract_address"]) + + res, err := osmosisApp.WasmKeeper.QuerySmart(chainCtx, contractAddress, []byte(key)) + suite.Require().NoError(err) + suite.Require().NotNil(res) + +} + +func (suite *MiddlewareTestSuite) Test_RateLimit_Rollover_NonNative() { + attrs := suite.fullSendTest(true) + fmt.Println("start ", suite.chainA.CurrentHeader.Time) + contractAddress, err := sdk.AccAddressFromBech32(attrs["_contract_address"]) + suite.Require().NoError(err) + denom := attrs["denom"] + channel := attrs["channel_id"] + key := fmt.Sprintf(`{"get_quotas": {"channel_id": "%s", "denom": "%s"}}`, channel, denom) + chainCtx := suite.chainA.GetContext() + osmosisApp := suite.chainA.GetOsmosisApp() + + res, err := osmosisApp.WasmKeeper.QuerySmart(chainCtx, contractAddress, []byte(key)) + suite.Require().NoError(err) + var out = make([]map[string]interface{}, 0) + suite.Require().NoError(json.Unmarshal(res, &out)) + output, ok := out[0]["flow"].(map[string]interface{}) + suite.Require().True(ok) + periodEnd := output["period_end"].(string) + periodEndInt, err := strconv.Atoi(periodEnd) + suite.Require().NoError(err) + initialPeriodEndTime := time.Unix(0, int64(periodEndInt)) + output, ok = out[0]["quota"].(map[string]interface{}) + suite.Require().True(ok) + initialChannelValue, ok := output["channel_value"].(string) + suite.Require().True(ok) + suite.Require().Equal(initialChannelValue, "100000000000009000000") + + suite.chainA.Coordinator.IncrementTimeBy(time.Second * 604800) + + asJson, err := json.Marshal("rollover_rules") + _, err = osmosisApp.WasmKeeper.Sudo( + chainCtx, + contractAddress, + asJson, + ) + suite.Require().NoError(err) + res, err = osmosisApp.WasmKeeper.QuerySmart(chainCtx, contractAddress, []byte(key)) + suite.Require().NoError(err) + out = make([]map[string]interface{}, 0) + suite.Require().NoError(json.Unmarshal(res, &out)) + output, ok = out[0]["flow"].(map[string]interface{}) + suite.Require().True(ok) + periodEnd = output["period_end"].(string) + periodEndInt, err = strconv.Atoi(periodEnd) + suite.Require().NoError(err) + currentPeriodEndTime := time.Unix(0, int64(periodEndInt)) + output, ok = out[0]["quota"].(map[string]interface{}) + suite.Require().True(ok) + currentChannelValue, ok := output["channel_value"].(string) + suite.Require().True(ok) + suite.Require().Equal(currentChannelValue, "0") + + suite.Require().True(currentPeriodEndTime.After(initialPeriodEndTime)) +} + +func (suite *MiddlewareTestSuite) Test_RateLimit_Rollover_Native() { + attrs := suite.fullSendTest(false) + fmt.Println("start ", suite.chainA.CurrentHeader.Time) + contractAddress, err := sdk.AccAddressFromBech32(attrs["_contract_address"]) + suite.Require().NoError(err) + denom := attrs["denom"] + channel := attrs["channel_id"] + key := fmt.Sprintf(`{"get_quotas": {"channel_id": "%s", "denom": "%s"}}`, channel, denom) + chainCtx := suite.chainA.GetContext() + osmosisApp := suite.chainA.GetOsmosisApp() + + res, err := osmosisApp.WasmKeeper.QuerySmart(chainCtx, contractAddress, []byte(key)) + suite.Require().NoError(err) + var out = make([]map[string]interface{}, 0) + suite.Require().NoError(json.Unmarshal(res, &out)) + output, ok := out[0]["flow"].(map[string]interface{}) + suite.Require().True(ok) + periodEnd := output["period_end"].(string) + periodEndInt, err := strconv.Atoi(periodEnd) + suite.Require().NoError(err) + initialPeriodEndTime := time.Unix(0, int64(periodEndInt)) + output, ok = out[0]["quota"].(map[string]interface{}) + suite.Require().True(ok) + initialChannelValue, ok := output["channel_value"].(string) + suite.Require().True(ok) + suite.Require().Equal(initialChannelValue, "4875000000000438750") + + suite.chainA.Coordinator.IncrementTimeBy(time.Second * 604800) + + asJson, err := json.Marshal("rollover_rules") + _, err = osmosisApp.WasmKeeper.Sudo( + chainCtx, + contractAddress, + asJson, + ) + suite.Require().NoError(err) + res, err = osmosisApp.WasmKeeper.QuerySmart(chainCtx, contractAddress, []byte(key)) + suite.Require().NoError(err) + out = make([]map[string]interface{}, 0) + suite.Require().NoError(json.Unmarshal(res, &out)) + output, ok = out[0]["flow"].(map[string]interface{}) + suite.Require().True(ok) + periodEnd = output["period_end"].(string) + periodEndInt, err = strconv.Atoi(periodEnd) + suite.Require().NoError(err) + currentPeriodEndTime := time.Unix(0, int64(periodEndInt)) + output, ok = out[0]["quota"].(map[string]interface{}) + suite.Require().True(ok) + currentChannelValue, ok := output["channel_value"].(string) + suite.Require().True(ok) + suite.Require().Equal(currentChannelValue, "0") + + suite.Require().True(currentPeriodEndTime.After(initialPeriodEndTime)) +} diff --git a/x/ibc-rate-limit/ibcratelimitmodule/module.go b/x/ibc-rate-limit/ibcratelimitmodule/module.go index 978b3bcec..f01e02a6f 100644 --- a/x/ibc-rate-limit/ibcratelimitmodule/module.go +++ b/x/ibc-rate-limit/ibcratelimitmodule/module.go @@ -136,7 +136,27 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // BeginBlock executes all ABCI BeginBlock logic respective to the txfees module. -func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} +func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { + contract := am.ics4wrapper.GetContractAddress(ctx) + if contract == "" { + return + } + contractAddr, err := sdk.AccAddressFromBech32(contract) + if err != nil { + fmt.Printf("[ERROR] failed to parse contract address %v\n", err) + return + } + asJson, err := json.Marshal("rollover_rules") + if err != nil { + fmt.Printf("[ERROR] failed to marshal rollover msg %v\n", err) + return + } + _, err = am.ics4wrapper.ContractKeeper.Sudo(ctx, contractAddr, asJson) + if err != nil { + fmt.Printf("[ERROR] failed to send sudo msg %v\n", err) + return + } +} // EndBlock executes all ABCI EndBlock logic respective to the txfees module. It // returns no validator updates.