diff --git a/Cargo.lock b/Cargo.lock index 02e6cb366..050c81d0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,7 +59,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ - "anstyle 1.0.4", + "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", @@ -67,12 +67,6 @@ dependencies = [ "utf8parse", ] -[[package]] -name = "anstyle" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" - [[package]] name = "anstyle" version = "1.0.4" @@ -103,7 +97,7 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ - "anstyle 1.0.4", + "anstyle", "windows-sys 0.48.0", ] @@ -121,11 +115,11 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "assert_cmd" -version = "2.0.10" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0b2340f55d9661d76793b2bfc2eb0e62689bd79d067a95707ea762afd5e9dd" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" dependencies = [ - "anstyle 0.3.5", + "anstyle", "bstr", "doc-comment", "predicates", @@ -184,6 +178,12 @@ 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 = "bitflags" version = "1.3.2" @@ -368,7 +368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ "anstream", - "anstyle 1.0.4", + "anstyle", "clap_lex 0.6.0", "strsim", ] @@ -390,9 +390,9 @@ checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "colorchoice" @@ -455,6 +455,7 @@ version = "1.1.9+0.8.1" dependencies = [ "criterion", "digest 0.10.7", + "ecdsa", "ed25519-zebra", "english-numbers", "hex", @@ -508,6 +509,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "chrono", "cosmwasm-crypto", @@ -523,6 +525,7 @@ dependencies = [ "serde-json-wasm", "serde_json", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -728,6 +731,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -902,7 +915,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -1362,6 +1375,15 @@ 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.8.0" @@ -1518,7 +1540,7 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" dependencies = [ - "anstyle 1.0.4", + "anstyle", "difflib", "float-cmp", "itertools 0.11.0", @@ -1861,6 +1883,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "semver" version = "1.0.20" @@ -1972,6 +2000,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -2016,6 +2054,12 @@ 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 = "strsim" version = "0.10.0" @@ -2342,9 +2386,9 @@ dependencies = [ [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -2355,6 +2399,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -2370,19 +2415,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -2393,9 +2442,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -2412,9 +2461,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -2431,9 +2480,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -2443,9 +2492,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -2454,9 +2503,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -2470,14 +2519,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/MIGRATING.md b/MIGRATING.md index d06877b40..e48b84d37 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -4,6 +4,22 @@ This guide explains what is needed to upgrade contracts when migrating over major releases of `cosmwasm`. Note that you can also view the [complete CHANGELOG](./CHANGELOG.md) to understand the differences. +## 1.4.x -> 1.5.0 + +- Update `cosmwasm-*` dependencies in Cargo.toml (skip the ones you don't use): + + ``` + [dependencies] + cosmwasm-std = "1.5.0" + cosmwasm-storage = "1.5.0" + # ... + + [dev-dependencies] + cosmwasm-schema = "1.5.0" + cosmwasm-vm = "1.5.0" + # ... + ``` + ## 1.3.x -> 1.4.0 - Update `cosmwasm-*` dependencies in Cargo.toml (skip the ones you don't use): diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock index e8b46cf4e..1a2e92d86 100644 --- a/contracts/burner/Cargo.lock +++ b/contracts/burner/Cargo.lock @@ -73,6 +73,12 @@ 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 = "bitflags" version = "1.3.2" @@ -183,9 +189,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -217,6 +223,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -259,6 +266,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -269,6 +277,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -421,6 +430,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -583,7 +602,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -926,6 +945,15 @@ 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.8.0" @@ -1261,6 +1289,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1366,6 +1400,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1410,6 +1454,12 @@ 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 = "subtle" version = "2.5.0" @@ -1655,9 +1705,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1668,6 +1718,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1683,19 +1734,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1706,9 +1761,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1725,9 +1780,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1744,9 +1799,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1756,9 +1811,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1767,9 +1822,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1783,14 +1838,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock index 1e05ea5d6..dd62cc869 100644 --- a/contracts/crypto-verify/Cargo.lock +++ b/contracts/crypto-verify/Cargo.lock @@ -73,6 +73,12 @@ 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 = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -588,7 +607,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -946,6 +965,15 @@ 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.8.0" @@ -1297,6 +1325,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1412,6 +1446,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1456,6 +1500,12 @@ 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 = "subtle" version = "2.5.0" @@ -1701,9 +1751,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1714,6 +1764,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1729,19 +1780,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1752,9 +1807,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1771,9 +1826,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1790,9 +1845,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1802,9 +1857,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1813,9 +1868,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1829,14 +1884,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/crypto-verify/src/contract.rs b/contracts/crypto-verify/src/contract.rs index b2917a90c..41fe87920 100644 --- a/contracts/crypto-verify/src/contract.rs +++ b/contracts/crypto-verify/src/contract.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, + entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, StdError, StdResult, Uint128, }; use sha2::{Digest, Sha256}; @@ -32,7 +32,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { message, signature, public_key, - } => to_binary(&query_verify_cosmos( + } => to_json_binary(&query_verify_cosmos( deps, &message.0, &signature.0, @@ -42,7 +42,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { message, signature, signer_address, - } => to_binary(&query_verify_ethereum_text( + } => to_json_binary(&query_verify_ethereum_text( deps, &message, &signature, @@ -60,14 +60,14 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { r, s, v, - } => to_binary(&query_verify_ethereum_transaction( + } => to_json_binary(&query_verify_ethereum_transaction( deps, from, to, nonce, gas_limit, gas_price, value, data, chain_id, r, s, v, )?), QueryMsg::VerifyTendermintSignature { message, signature, public_key, - } => to_binary(&query_verify_tendermint( + } => to_json_binary(&query_verify_tendermint( deps, &message.0, &signature.0, @@ -77,13 +77,13 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { messages, signatures, public_keys, - } => to_binary(&query_verify_tendermint_batch( + } => to_json_binary(&query_verify_tendermint_batch( deps, &messages, &signatures, &public_keys, )?), - QueryMsg::ListVerificationSchemes {} => to_binary(&query_list_verifications(deps)?), + QueryMsg::ListVerificationSchemes {} => to_json_binary(&query_list_verifications(deps)?), } } @@ -218,7 +218,7 @@ mod tests { mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, }; use cosmwasm_std::{ - from_slice, Binary, OwnedDeps, RecoverPubkeyError, StdError, VerificationError, + from_json, Binary, OwnedDeps, RecoverPubkeyError, StdError, VerificationError, }; use hex_literal::hex; @@ -274,7 +274,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -296,7 +296,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: false }); } @@ -339,7 +339,7 @@ mod tests { signer_address: signer_address.into(), }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -360,7 +360,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: false }); } @@ -380,7 +380,7 @@ mod tests { signer_address: signer_address.into(), }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: false }); // Broken signature @@ -447,7 +447,7 @@ mod tests { v, }; let raw = query(deps.as_ref(), mock_env(), msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -475,7 +475,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -508,7 +508,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -542,7 +542,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -573,7 +573,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: false }); } @@ -626,7 +626,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -648,7 +648,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: false }); } @@ -683,7 +683,7 @@ mod tests { let query_msg = QueryMsg::ListVerificationSchemes {}; let raw = query(deps.as_ref(), mock_env(), query_msg).unwrap(); - let res: ListVerificationsResponse = from_slice(&raw).unwrap(); + let res: ListVerificationsResponse = from_json(raw).unwrap(); assert_eq!( res, diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock index b4bad366c..8d716d400 100644 --- a/contracts/cyberpunk/Cargo.lock +++ b/contracts/cyberpunk/Cargo.lock @@ -91,6 +91,12 @@ 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 = "bitflags" version = "1.3.2" @@ -207,9 +213,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -247,6 +253,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -289,6 +296,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64 0.21.5", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -299,6 +307,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -451,6 +460,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -625,7 +644,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -990,6 +1009,15 @@ 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.8.0" @@ -1350,6 +1378,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1455,6 +1489,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1499,6 +1543,12 @@ 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 = "subtle" version = "2.5.0" @@ -1757,9 +1807,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1770,6 +1820,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1785,19 +1836,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1808,9 +1863,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1827,9 +1882,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1846,9 +1901,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1858,9 +1913,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1869,9 +1924,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1885,14 +1940,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/cyberpunk/src/contract.rs b/contracts/cyberpunk/src/contract.rs index 0374a06a8..2084f630d 100644 --- a/contracts/cyberpunk/src/contract.rs +++ b/contracts/cyberpunk/src/contract.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, to_binary, Api, DenomMetadata, Deps, DepsMut, Empty, Env, MessageInfo, + entry_point, to_json_binary, Api, DenomMetadata, Deps, DepsMut, Empty, Env, MessageInfo, PageRequest, QueryResponse, Response, StdError, StdResult, WasmMsg, }; @@ -94,7 +94,7 @@ fn execute_memory_loop() -> Result { fn execute_message_loop(env: Env) -> Result { let resp = Response::new().add_message(WasmMsg::Execute { contract_addr: env.contract.address.into(), - msg: to_binary(&ExecuteMsg::MessageLoop {})?, + msg: to_json_binary(&ExecuteMsg::MessageLoop {})?, funds: vec![], }); Ok(resp) @@ -148,7 +148,7 @@ fn execute_unreachable() -> Result { } fn execute_mirror_env(env: Env) -> Result { - Ok(Response::new().set_data(to_binary(&env)?)) + Ok(Response::new().set_data(to_json_binary(&env)?)) } fn execute_debug(api: &dyn Api) -> Result { @@ -183,9 +183,9 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { use QueryMsg::*; match msg { - MirrorEnv {} => to_binary(&query_mirror_env(env)), - Denoms {} => to_binary(&query_denoms(deps)?), - Denom { denom } => to_binary(&query_denom(deps, denom)?), + MirrorEnv {} => to_json_binary(&query_mirror_env(env)), + Denoms {} => to_json_binary(&query_denoms(deps)?), + Denom { denom } => to_json_binary(&query_denom(deps, denom)?), } } @@ -226,7 +226,7 @@ mod tests { use cosmwasm_std::testing::{ mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, }; - use cosmwasm_std::{from_binary, DenomMetadata, DenomUnit, OwnedDeps}; + use cosmwasm_std::{from_json, DenomMetadata, DenomUnit, OwnedDeps}; fn setup() -> OwnedDeps { let mut deps = mock_dependencies(); @@ -274,12 +274,12 @@ mod tests { ); let symbols: Vec = - from_binary(&query(deps.as_ref(), mock_env(), QueryMsg::Denoms {}).unwrap()).unwrap(); + from_json(query(deps.as_ref(), mock_env(), QueryMsg::Denoms {}).unwrap()).unwrap(); assert_eq!(symbols.len(), 98); - let denom: DenomMetadata = from_binary( - &query( + let denom: DenomMetadata = from_json( + query( deps.as_ref(), mock_env(), QueryMsg::Denom { diff --git a/contracts/cyberpunk/tests/integration.rs b/contracts/cyberpunk/tests/integration.rs index 8abdc74f5..539a53eb2 100644 --- a/contracts/cyberpunk/tests/integration.rs +++ b/contracts/cyberpunk/tests/integration.rs @@ -17,7 +17,7 @@ //! }); //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) -use cosmwasm_std::{from_binary, Empty, Env, Response}; +use cosmwasm_std::{from_json, Empty, Env, Response}; use cosmwasm_vm::testing::{ execute, instantiate, mock_env, mock_info, mock_instance, mock_instance_with_gas_limit, query, }; @@ -152,13 +152,13 @@ fn test_env() { ) .unwrap(); - let received_env: Env = from_binary(&res.data.unwrap()).unwrap(); + let received_env: Env = from_json(res.data.unwrap()).unwrap(); assert_eq!(received_env, env); let env = mock_env(); let received_env: Env = - from_binary(&query(&mut deps, env.clone(), QueryMsg::MirrorEnv {}).unwrap()).unwrap(); + from_json(query(&mut deps, env.clone(), QueryMsg::MirrorEnv {}).unwrap()).unwrap(); assert_eq!(received_env, env); } diff --git a/contracts/floaty/.vscode/settings.json b/contracts/floaty/.vscode/settings.json new file mode 100644 index 000000000..11fee5dc5 --- /dev/null +++ b/contracts/floaty/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "rust-analyzer.cargo.target": "wasm32-unknown-unknown", + "rust-analyzer.cargo.extraEnv": { + "RUSTFLAGS": "-C target-feature=+nontrapping-fptoint" + } +} diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock index 10871f95b..dbc77095d 100644 --- a/contracts/floaty/Cargo.lock +++ b/contracts/floaty/Cargo.lock @@ -73,6 +73,12 @@ 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 = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -572,7 +591,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -693,9 +712,9 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cosmwasm-vm", + "rand_chacha", "schemars", "serde", - "thiserror", ] [[package]] @@ -927,6 +946,15 @@ 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.8.0" @@ -1016,6 +1044,12 @@ dependencies = [ "spki", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1084,6 +1118,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[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" @@ -1262,6 +1306,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1367,6 +1417,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1411,6 +1471,12 @@ 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 = "subtle" version = "2.5.0" @@ -1656,9 +1722,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1669,6 +1735,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1684,19 +1751,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1707,9 +1778,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1726,9 +1797,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1745,9 +1816,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1757,9 +1828,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1768,9 +1839,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1784,14 +1855,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/floaty/Cargo.toml b/contracts/floaty/Cargo.toml index 0aee91625..e78bf0017 100644 --- a/contracts/floaty/Cargo.toml +++ b/contracts/floaty/Cargo.toml @@ -35,7 +35,7 @@ cosmwasm-schema = { path = "../../packages/schema" } cosmwasm-std = { path = "../../packages/std" } schemars = "0.8.3" serde = { version = "1.0.103", default-features = false, features = ["derive"] } -thiserror = "1.0.26" +rand_chacha = { version = "0.3.1", default-features = false } [dev-dependencies] cosmwasm-vm = { path = "../../packages/vm", default-features = false, features = ["iterator"] } diff --git a/contracts/floaty/README.md b/contracts/floaty/README.md new file mode 100644 index 000000000..921205651 --- /dev/null +++ b/contracts/floaty/README.md @@ -0,0 +1,13 @@ +# Floaty Contract + +This contract contains all WebAssembly floating point instructions. It is used +for testing the floating point support. + +In order to compile it, you need a nightly version of Rust and enable the +`nontrapping-fptoint` target-feature. This allows the usage of +[some more conversion instructions](https://github.com/WebAssembly/spec/blob/main/proposals/nontrapping-float-to-int-conversion/Overview.md). +To do this, run: + +```sh +RUSTFLAGS="-C link-arg=-s -C target-feature=+nontrapping-fptoint" cargo wasm +``` diff --git a/contracts/floaty/schema/floaty.json b/contracts/floaty/schema/floaty.json index 62f697a59..cd80c1433 100644 --- a/contracts/floaty/schema/floaty.json +++ b/contracts/floaty/schema/floaty.json @@ -5,53 +5,50 @@ "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "InstantiateMsg", - "type": "object", - "required": [ - "beneficiary", - "verifier" - ], - "properties": { - "beneficiary": { - "type": "string" - }, - "verifier": { - "type": "string" - } - }, - "additionalProperties": false + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" }, - "execute": { + "execute": null, + "query": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ExecuteMsg", + "title": "QueryMsg", "oneOf": [ { - "description": "Releasing all funds in the contract to the beneficiary. This is the only \"proper\" action of this demo contract.", + "description": "Returns valid random arguments for the given instruction", "type": "object", "required": [ - "release" + "random_args_for" ], "properties": { - "release": { + "random_args_for": { "type": "object", + "required": [ + "instruction", + "seed" + ], + "properties": { + "instruction": { + "type": "string" + }, + "seed": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, "additionalProperties": false } }, "additionalProperties": false - } - ] - }, - "query": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", - "oneOf": [ + }, { - "description": "returns a human-readable representation of the verifier use to ensure query path works in integration tests", + "description": "Returns a list of all instructions", "type": "object", "required": [ - "verifier" + "instructions" ], "properties": { - "verifier": { + "instructions": { "type": "object", "additionalProperties": false } @@ -59,19 +56,26 @@ "additionalProperties": false }, { - "description": "This returns cosmwasm_std::AllBalanceResponse to demo use of the querier", + "description": "Runs the given instruction with the given arguments and returns the result", "type": "object", "required": [ - "other_balance" + "run" ], "properties": { - "other_balance": { + "run": { "type": "object", "required": [ - "address" + "args", + "instruction" ], "properties": { - "address": { + "args": { + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + "instruction": { "type": "string" } }, @@ -80,62 +84,212 @@ }, "additionalProperties": false } - ] + ], + "definitions": { + "Value": { + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + } + } }, "migrate": null, "sudo": null, "responses": { - "other_balance": { + "instructions": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "AllBalanceResponse", - "type": "object", - "required": [ - "amount" - ], - "properties": { - "amount": { - "description": "Returns all non-zero coins held by this account.", - "type": "array", - "items": { - "$ref": "#/definitions/Coin" - } - } + "title": "Array_of_String", + "type": "array", + "items": { + "type": "string" + } + }, + "random_args_for": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_Value", + "type": "array", + "items": { + "$ref": "#/definitions/Value" }, "definitions": { - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" + "Value": { + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false }, - "denom": { - "type": "string" + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false } - } - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" + ] } } }, - "verifier": { + "run": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "VerifierResponse", - "type": "object", - "required": [ - "verifier" - ], - "properties": { - "verifier": { - "type": "string" + "title": "Value", + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false } - }, - "additionalProperties": false + ] } } } diff --git a/contracts/floaty/schema/raw/execute.json b/contracts/floaty/schema/raw/execute.json index 76967fd17..bcd478d67 100644 --- a/contracts/floaty/schema/raw/execute.json +++ b/contracts/floaty/schema/raw/execute.json @@ -1,20 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ExecuteMsg", - "oneOf": [ - { - "description": "Releasing all funds in the contract to the beneficiary. This is the only \"proper\" action of this demo contract.", - "type": "object", - "required": [ - "release" - ], - "properties": { - "release": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" } diff --git a/contracts/floaty/schema/raw/instantiate.json b/contracts/floaty/schema/raw/instantiate.json index 8639103d3..5f6dfaf43 100644 --- a/contracts/floaty/schema/raw/instantiate.json +++ b/contracts/floaty/schema/raw/instantiate.json @@ -1,18 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "InstantiateMsg", - "type": "object", - "required": [ - "beneficiary", - "verifier" - ], - "properties": { - "beneficiary": { - "type": "string" - }, - "verifier": { - "type": "string" - } - }, - "additionalProperties": false + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" } diff --git a/contracts/floaty/schema/raw/query.json b/contracts/floaty/schema/raw/query.json index b96c3dfb8..376f32793 100644 --- a/contracts/floaty/schema/raw/query.json +++ b/contracts/floaty/schema/raw/query.json @@ -3,13 +3,41 @@ "title": "QueryMsg", "oneOf": [ { - "description": "returns a human-readable representation of the verifier use to ensure query path works in integration tests", + "description": "Returns valid random arguments for the given instruction", "type": "object", "required": [ - "verifier" + "random_args_for" ], "properties": { - "verifier": { + "random_args_for": { + "type": "object", + "required": [ + "instruction", + "seed" + ], + "properties": { + "instruction": { + "type": "string" + }, + "seed": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns a list of all instructions", + "type": "object", + "required": [ + "instructions" + ], + "properties": { + "instructions": { "type": "object", "additionalProperties": false } @@ -17,19 +45,26 @@ "additionalProperties": false }, { - "description": "This returns cosmwasm_std::AllBalanceResponse to demo use of the querier", + "description": "Runs the given instruction with the given arguments and returns the result", "type": "object", "required": [ - "other_balance" + "run" ], "properties": { - "other_balance": { + "run": { "type": "object", "required": [ - "address" + "args", + "instruction" ], "properties": { - "address": { + "args": { + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + "instruction": { "type": "string" } }, @@ -38,5 +73,67 @@ }, "additionalProperties": false } - ] + ], + "definitions": { + "Value": { + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + } + } } diff --git a/contracts/floaty/schema/raw/response_to_instructions.json b/contracts/floaty/schema/raw/response_to_instructions.json new file mode 100644 index 000000000..4290cb1a2 --- /dev/null +++ b/contracts/floaty/schema/raw/response_to_instructions.json @@ -0,0 +1,8 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_String", + "type": "array", + "items": { + "type": "string" + } +} diff --git a/contracts/floaty/schema/raw/response_to_random_args_for.json b/contracts/floaty/schema/raw/response_to_random_args_for.json new file mode 100644 index 000000000..4b9c97bed --- /dev/null +++ b/contracts/floaty/schema/raw/response_to_random_args_for.json @@ -0,0 +1,70 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_Value", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + }, + "definitions": { + "Value": { + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/contracts/floaty/schema/raw/response_to_run.json b/contracts/floaty/schema/raw/response_to_run.json new file mode 100644 index 000000000..08c5730ab --- /dev/null +++ b/contracts/floaty/schema/raw/response_to_run.json @@ -0,0 +1,62 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Value", + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/floaty/src/bin/schema.rs b/contracts/floaty/src/bin/schema.rs index b0a8a02b0..6a19141a5 100644 --- a/contracts/floaty/src/bin/schema.rs +++ b/contracts/floaty/src/bin/schema.rs @@ -1,11 +1,11 @@ use cosmwasm_schema::write_api; -use floaty::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cosmwasm_std::Empty; +use floaty::msg::QueryMsg; fn main() { write_api! { - instantiate: InstantiateMsg, + instantiate: Empty, query: QueryMsg, - execute: ExecuteMsg, } } diff --git a/contracts/floaty/src/contract.rs b/contracts/floaty/src/contract.rs index bff239789..9f7e8e4bf 100644 --- a/contracts/floaty/src/contract.rs +++ b/contracts/floaty/src/contract.rs @@ -1,259 +1,46 @@ use cosmwasm_std::{ - entry_point, from_slice, to_binary, to_vec, AllBalanceResponse, BankMsg, Deps, DepsMut, Env, - Event, MessageInfo, QueryResponse, Response, StdError, StdResult, + entry_point, to_json_binary, Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response, + StdResult, }; +use rand_chacha::rand_core::SeedableRng; -use crate::errors::HackError; -use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, VerifierResponse}; -use crate::state::{State, CONFIG_KEY}; +#[cfg(target_arch = "wasm32")] +use crate::instructions::run_instruction; +use crate::{ + instructions::{random_args_for, Value, FLOAT_INSTRUCTIONS}, + msg::QueryMsg, +}; #[entry_point] pub fn instantiate( - deps: DepsMut, + _deps: DepsMut, _env: Env, - info: MessageInfo, - msg: InstantiateMsg, -) -> Result { - deps.api.debug("here we go 🚀"); - - deps.storage.set( - CONFIG_KEY, - &to_vec(&State { - verifier: deps.api.addr_validate(&msg.verifier)?, - beneficiary: deps.api.addr_validate(&msg.beneficiary)?, - funder: info.sender, - })?, - ); - - // This adds some unrelated event attribute for testing purposes - Ok(Response::new().add_attribute("Let the", "hacking begin")) + _info: MessageInfo, + _msg: Empty, +) -> Result { + Ok(Response::default()) } #[entry_point] -pub fn execute( - deps: DepsMut, - env: Env, - info: MessageInfo, - _msg: ExecuteMsg, -) -> Result { - let data = deps - .storage - .get(CONFIG_KEY) - .ok_or_else(|| StdError::not_found("State"))?; - let state: State = from_slice(&data)?; - - if info.sender == state.verifier { - let to_addr = state.beneficiary; - let balance = deps.querier.query_all_balances(env.contract.address)?; - - let mut fl = balance[0].amount.u128() as f64; - fl *= 0.3; - - let resp = Response::new() - .add_attribute("action", "release") - .add_attribute("destination", to_addr.clone()) - .add_attribute("foo", fl.to_string()) - .add_event(Event::new("hackatom").add_attribute("action", "release")) - .add_message(BankMsg::Send { - to_address: to_addr.into(), - amount: balance, - }) - .set_data([0xF0, 0x0B, 0xAA]); - Ok(resp) - } else { - Err(HackError::Unauthorized {}) - } -} - -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { +pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Verifier {} => to_binary(&query_verifier(deps)?), - QueryMsg::OtherBalance { address } => to_binary(&query_other_balance(deps, address)?), + QueryMsg::RandomArgsFor { instruction, seed } => { + let mut rng = rand_chacha::ChaChaRng::seed_from_u64(seed); + to_json_binary(&random_args_for(&instruction, &mut rng)) + } + QueryMsg::Instructions {} => to_json_binary(&FLOAT_INSTRUCTIONS.to_vec()), + QueryMsg::Run { instruction, args } => to_json_binary(&query_run(&instruction, args)?), } } -fn query_verifier(deps: Deps) -> StdResult { - let data = deps - .storage - .get(CONFIG_KEY) - .ok_or_else(|| StdError::not_found("State"))?; - let state: State = from_slice(&data)?; - Ok(VerifierResponse { - verifier: state.verifier.into(), - }) -} - -fn query_other_balance(deps: Deps, address: String) -> StdResult { - let amount = deps.querier.query_all_balances(address)?; - Ok(AllBalanceResponse { amount }) -} - -#[cfg(test)] -mod tests { - use super::*; - use cosmwasm_std::testing::{ - mock_dependencies, mock_dependencies_with_balances, mock_env, mock_info, MOCK_CONTRACT_ADDR, - }; - use cosmwasm_std::Api as _; - // import trait Storage to get access to read - use cosmwasm_std::{attr, coins, Addr, Storage, SubMsg}; - - #[test] - fn proper_initialization() { - let mut deps = mock_dependencies(); - - let verifier = String::from("verifies"); - let beneficiary = String::from("benefits"); - let creator = String::from("creator"); - let expected_state = State { - verifier: deps.api.addr_validate(&verifier).unwrap(), - beneficiary: deps.api.addr_validate(&beneficiary).unwrap(), - funder: deps.api.addr_validate(&creator).unwrap(), - }; - - let msg = InstantiateMsg { - verifier, - beneficiary, - }; - let info = mock_info(creator.as_str(), &[]); - let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); - assert_eq!(res.messages.len(), 0); - assert_eq!(res.attributes.len(), 1); - assert_eq!(res.attributes[0].key, "Let the"); - assert_eq!(res.attributes[0].value, "hacking begin"); - - // it worked, let's check the state - let data = deps.storage.get(CONFIG_KEY).expect("no data stored"); - let state: State = from_slice(&data).unwrap(); - assert_eq!(state, expected_state); - } - - #[test] - fn instantiate_and_query() { - let mut deps = mock_dependencies(); - - let verifier = String::from("verifies"); - let beneficiary = String::from("benefits"); - let creator = String::from("creator"); - let msg = InstantiateMsg { - verifier: verifier.clone(), - beneficiary, - }; - let info = mock_info(&creator, &[]); - let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // now let's query - let query_response = query_verifier(deps.as_ref()).unwrap(); - assert_eq!(query_response.verifier, verifier); - } - - #[test] - fn querier_callbacks_work() { - let rich_addr = String::from("foobar"); - let rich_balance = coins(10000, "gold"); - let deps = mock_dependencies_with_balances(&[(&rich_addr, &rich_balance)]); - - // querying with balance gets the balance - let bal = query_other_balance(deps.as_ref(), rich_addr).unwrap(); - assert_eq!(bal.amount, rich_balance); - - // querying other accounts gets none - let bal = query_other_balance(deps.as_ref(), String::from("someone else")).unwrap(); - assert_eq!(bal.amount, vec![]); - } - - #[test] - fn execute_release_works() { - let mut deps = mock_dependencies(); - - // initialize the store - let creator = String::from("creator"); - let verifier = String::from("verifies"); - let beneficiary = String::from("benefits"); - - let instantiate_msg = InstantiateMsg { - verifier: verifier.clone(), - beneficiary: beneficiary.clone(), - }; - let init_amount = coins(1000, "earth"); - let init_info = mock_info(&creator, &init_amount); - let init_res = instantiate(deps.as_mut(), mock_env(), init_info, instantiate_msg).unwrap(); - assert_eq!(init_res.messages.len(), 0); - - // balance changed in init - deps.querier.update_balance(MOCK_CONTRACT_ADDR, init_amount); - - // beneficiary can release it - let execute_info = mock_info(verifier.as_str(), &[]); - let execute_res = execute( - deps.as_mut(), - mock_env(), - execute_info, - ExecuteMsg::Release {}, - ) - .unwrap(); - assert_eq!(execute_res.messages.len(), 1); - let msg = execute_res.messages.get(0).expect("no message"); - assert_eq!( - msg, - &SubMsg::new(BankMsg::Send { - to_address: beneficiary, - amount: coins(1000, "earth"), - }), - ); - assert_eq!( - execute_res.attributes, - vec![ - attr("action", "release"), - attr("destination", "benefits"), - attr("foo", "300") - ], - ); - assert_eq!(execute_res.data, Some(vec![0xF0, 0x0B, 0xAA].into())); - } - - #[test] - fn execute_release_fails_for_wrong_sender() { - let mut deps = mock_dependencies(); - - // initialize the store - let creator = String::from("creator"); - let verifier = String::from("verifies"); - let beneficiary = String::from("benefits"); - - let instantiate_msg = InstantiateMsg { - verifier: verifier.clone(), - beneficiary: beneficiary.clone(), - }; - let init_amount = coins(1000, "earth"); - let init_info = mock_info(&creator, &init_amount); - let init_res = instantiate(deps.as_mut(), mock_env(), init_info, instantiate_msg).unwrap(); - assert_eq!(init_res.messages.len(), 0); - - // balance changed in init - deps.querier.update_balance(MOCK_CONTRACT_ADDR, init_amount); - - // beneficiary cannot release it - let execute_info = mock_info(beneficiary.as_str(), &[]); - let execute_res = execute( - deps.as_mut(), - mock_env(), - execute_info, - ExecuteMsg::Release {}, - ); - assert_eq!(execute_res.unwrap_err(), HackError::Unauthorized {}); +#[cfg_attr(not(target_arch = "wasm32"), allow(unused_variables))] +fn query_run(instruction: &str, args: Vec) -> StdResult { + #[cfg(not(target_arch = "wasm32"))] + panic!(); - // state should not change - let data = deps.storage.get(CONFIG_KEY).expect("no data stored"); - let state: State = from_slice(&data).unwrap(); - assert_eq!( - state, - State { - verifier: Addr::unchecked(verifier), - beneficiary: Addr::unchecked(beneficiary), - funder: Addr::unchecked(creator), - } - ); + #[cfg(target_arch = "wasm32")] + { + let result = run_instruction(instruction, &args); + Ok(result) } } diff --git a/contracts/floaty/src/errors.rs b/contracts/floaty/src/errors.rs deleted file mode 100644 index 7b46b00b6..000000000 --- a/contracts/floaty/src/errors.rs +++ /dev/null @@ -1,12 +0,0 @@ -use cosmwasm_std::StdError; -use thiserror::Error; - -#[derive(Error, Debug, PartialEq)] -pub enum HackError { - #[error("{0}")] - /// this is needed so we can use `bucket.load(...)?` and have it auto-converted to the custom error - Std(#[from] StdError), - // this is whatever we want - #[error("Unauthorized")] - Unauthorized {}, -} diff --git a/contracts/floaty/src/floats.rs b/contracts/floaty/src/floats.rs new file mode 100644 index 000000000..ccbb06ade --- /dev/null +++ b/contracts/floaty/src/floats.rs @@ -0,0 +1,212 @@ +use rand_chacha::rand_core::RngCore; + +pub const INF_32: u32 = 0x7f800000; +pub const NEG_INF_32: u32 = 0xff800000; +pub const INF_64: u64 = 0x7ff0000000000000; +pub const NEG_INF_64: u64 = 0xfff0000000000000; + +const EXPONENT_MASK_32: u32 = 0x7f800000; +const EXPONENT_MASK_64: u64 = 0x7ff0000000000000; +const SIGN_MASK_32: u32 = 0x80000000; +const SIGN_MASK_64: u64 = 0x8000000000000000; +const MANTISSA_MASK_32: u32 = 0x007fffff; +const MANTISSA_MASK_64: u64 = 0x000fffffffffffff; + +/// Returns a random `f32`. +/// +/// We want to cover all classes of floats: NaNs, subnormals, infinities, and normal floats. +/// Because of that, we don't just generate a random `u32` and convert it to an `f32` +/// (that would make e.g. infinities highly unlikely) +/// Instead, we give each of these classes a probability of 25% and +/// then generate a random pattern within that class +pub fn random_f32(rng: &mut impl RngCore) -> f32 { + let decider = rng.next_u32(); + let bits = match decider % 4 { + 0 => { + // 25% chance of being a NaN + random_nan_32(rng) + } + 1 => { + // 25% chance of being a subnormal + random_subnormal_32(rng) + } + 2 => { + // 25% chance of being an infinite + if decider % 2 == 0 { + INF_32 + } else { + NEG_INF_32 + } + } + 3 => { + // 25% chance of being a random bit pattern + rng.next_u32() + } + _ => unreachable!(), + }; + f32::from_bits(bits) +} + +/// Returns a random `f64`. +/// +/// See [`random_f32`] for more details. +pub fn random_f64(rng: &mut impl RngCore) -> f64 { + let decider = rng.next_u64(); + let bits = match decider % 4 { + 0 => { + // 25% chance of being a NaN + random_nan_64(rng) + } + 1 => { + // 25% chance of being a subnormal + random_subnormal_64(rng) + } + 2 => { + // 25% chance of being an infinite + if decider % 2 == 0 { + INF_64 + } else { + NEG_INF_64 + } + } + 3 => { + // 25% chance of being a random bit pattern + rng.next_u64() + } + _ => unreachable!(), + }; + f64::from_bits(bits) +} + +/// Returns bits for a random NaN +pub fn random_nan_32(rng: &mut impl RngCore) -> u32 { + let mut rnd = rng.next_u32(); + if rnd == 0 { + // we don't want to return an infinity, so we just set the last bit to 1 + rnd = 1; + } + // Set the exponent to all 1s and remaining bits random + EXPONENT_MASK_32 | rnd +} + +/// Returns bits for a random NaN +pub fn random_nan_64(rng: &mut impl RngCore) -> u64 { + let mut rnd = rng.next_u64(); + if rnd == 0 { + // we don't want to return an infinity, so we just set the last bit to 1 + rnd = 1; + } + // Set the exponent to all 1s and remaining bits random + EXPONENT_MASK_64 | rnd +} + +/// Returns bits for a random subnormal +pub fn random_subnormal_32(rng: &mut impl RngCore) -> u32 { + // Set the exponent to all 0s and remaining bits random + let res = rng.next_u32() & (SIGN_MASK_32 | MANTISSA_MASK_32); + + if res == 0 { + // we don't want to return a zero, so we just return a fixed subnormal + SIGN_MASK_32 | MANTISSA_MASK_32 + } else { + res + } +} + +/// Returns bits for a random subnormal +pub fn random_subnormal_64(rng: &mut impl RngCore) -> u64 { + // Set the exponent to all 0s and remaining bits random + let res = rng.next_u64() & (SIGN_MASK_64 | MANTISSA_MASK_64); + + if res == 0 { + // we don't want to return a zero, so we just return a fixed subnormal + SIGN_MASK_64 | MANTISSA_MASK_64 + } else { + res + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Class { + Normal, + Subnormal, + Zero, + Infinite, + NaN, +} + +pub trait Classifier { + fn classify(&self) -> Class; +} + +impl Classifier for u32 { + fn classify(&self) -> Class { + let exponent = self & EXPONENT_MASK_32; + let mantissa = self & MANTISSA_MASK_32; + + match (exponent, mantissa) { + (0, 0) => Class::Zero, + (0, _) => Class::Subnormal, + (EXPONENT_MASK_32, 0) => Class::Infinite, + (EXPONENT_MASK_32, _) => Class::NaN, + _ => Class::Normal, + } + } +} + +impl Classifier for u64 { + fn classify(&self) -> Class { + let exponent = self & EXPONENT_MASK_64; + let mantissa = self & MANTISSA_MASK_64; + + match (exponent, mantissa) { + (0, 0) => Class::Zero, + (0, _) => Class::Subnormal, + (EXPONENT_MASK_64, 0) => Class::Infinite, + (EXPONENT_MASK_64, _) => Class::NaN, + _ => Class::Normal, + } + } +} + +#[cfg(test)] +mod tests { + use rand_chacha::rand_core::SeedableRng; + + use super::*; + + #[test] + fn test_constants() { + assert_eq!(INF_32, f32::INFINITY.to_bits()); + assert_eq!(NEG_INF_32, f32::NEG_INFINITY.to_bits()); + assert_eq!(INF_64, f64::INFINITY.to_bits()); + assert_eq!(NEG_INF_64, f64::NEG_INFINITY.to_bits()); + } + + #[test] + fn test_classify() { + // for 32-bit floats + assert_eq!((-0f32).to_bits().classify(), Class::Zero); + assert_eq!(0u32.classify(), Class::Zero); + assert_eq!(1f32.to_bits().classify(), Class::Normal); + assert_eq!(INF_32.classify(), Class::Infinite); + assert_eq!(NEG_INF_32.classify(), Class::Infinite); + + // for 64-bit floats + assert_eq!((-0f64).to_bits().classify(), Class::Zero); + assert_eq!(0u64.classify(), Class::Zero); + assert_eq!(1f64.to_bits().classify(), Class::Normal); + assert_eq!(INF_64.classify(), Class::Infinite); + assert_eq!(NEG_INF_64.classify(), Class::Infinite); + + // random floats + let mut rng = rand_chacha::ChaChaRng::seed_from_u64(123456); + for _ in 0..1000 { + assert_eq!(random_subnormal_32(&mut rng).classify(), Class::Subnormal); + assert_eq!(random_nan_32(&mut rng).classify(), Class::NaN); + + assert_eq!(random_subnormal_64(&mut rng).classify(), Class::Subnormal); + assert_eq!(random_nan_64(&mut rng).classify(), Class::NaN); + } + } +} diff --git a/contracts/floaty/src/instructions.rs b/contracts/floaty/src/instructions.rs new file mode 100644 index 000000000..d26433085 --- /dev/null +++ b/contracts/floaty/src/instructions.rs @@ -0,0 +1,555 @@ +use cosmwasm_schema::cw_serde; +use rand_chacha::rand_core::RngCore; + +use crate::floats::{random_f32, random_f64}; + +/// Not intended for direct usage +#[cfg_attr(not(target_arch = "wasm32"), allow(unused_macros))] +macro_rules! run_instr { + ($instr:expr, $input:expr, $input_ty:ty, $return_ty:ty) => {{ + let input: $input_ty = $input; + let ret: $return_ty; + unsafe { + core::arch::asm!("local.get {0}", $instr, "local.set {1}", in(local) input, out(local) ret) + }; + ret + }}; + ($instr:expr, $input1:expr, $input1_ty:ty, $input2:expr, $input2_ty:ty, $return_ty:ty) => {{ + let input1: $input1_ty = $input1; + let input2: $input2_ty = $input2; + let ret: $return_ty; + unsafe { + core::arch::asm!("local.get {0}", "local.get {1}", $instr, "local.set {2}", in(local) input1, in(local) input2, out(local) ret) + }; + ret + }}; +} +#[cfg_attr(not(target_arch = "wasm32"), allow(unused_imports))] +pub(crate) use run_instr; + +/// Helper to run a single WebAssembly instruction in a type-safe way +#[cfg_attr(not(target_arch = "wasm32"), allow(unused_macros))] +macro_rules! run { + ("f32.eq", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.eq", $input1, f32, $input2, f32, u32) + }; + ("f32.ne", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.ne", $input1, f32, $input2, f32, u32) + }; + ("f32.lt", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.lt", $input1, f32, $input2, f32, u32) + }; + ("f32.gt", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.gt", $input1, f32, $input2, f32, u32) + }; + ("f32.le", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.le", $input1, f32, $input2, f32, u32) + }; + ("f32.ge", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.ge", $input1, f32, $input2, f32, u32) + }; + ("f64.eq", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.eq", $input1, f64, $input2, f64, u32) + }; + ("f64.ne", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.ne", $input1, f64, $input2, f64, u32) + }; + ("f64.lt", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.lt", $input1, f64, $input2, f64, u32) + }; + ("f64.gt", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.gt", $input1, f64, $input2, f64, u32) + }; + ("f64.le", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.le", $input1, f64, $input2, f64, u32) + }; + ("f64.ge", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.ge", $input1, f64, $input2, f64, u32) + }; + // + ("f32.abs", $input:expr) => { + $crate::instructions::run_instr!("f32.abs", $input, f32, f32) + }; + ("f32.neg", $input:expr) => { + $crate::instructions::run_instr!("f32.neg", $input, f32, f32) + }; + ("f32.ceil", $input:expr) => { + $crate::instructions::run_instr!("f32.ceil", $input, f32, f32) + }; + ("f32.floor", $input:expr) => { + $crate::instructions::run_instr!("f32.floor", $input, f32, f32) + }; + ("f32.trunc", $input:expr) => { + $crate::instructions::run_instr!("f32.trunc", $input, f32, f32) + }; + ("f32.nearest", $input:expr) => { + $crate::instructions::run_instr!("f32.nearest", $input, f32, f32) + }; + ("f32.sqrt", $input:expr) => { + $crate::instructions::run_instr!("f32.sqrt", $input, f32, f32) + }; + ("f32.add", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.add", $input1, f32, $input2, f32, f32) + }; + ("f32.sub", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.sub", $input1, f32, $input2, f32, f32) + }; + ("f32.mul", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.mul", $input1, f32, $input2, f32, f32) + }; + ("f32.div", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.div", $input1, f32, $input2, f32, f32) + }; + ("f32.min", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.min", $input1, f32, $input2, f32, f32) + }; + ("f32.max", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.max", $input1, f32, $input2, f32, f32) + }; + ("f32.copysign", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.copysign", $input1, f32, $input2, f32, f32) + }; + ("f64.abs", $input:expr) => { + $crate::instructions::run_instr!("f64.abs", $input, f64, f64) + }; + ("f64.neg", $input:expr) => { + $crate::instructions::run_instr!("f64.neg", $input, f64, f64) + }; + ("f64.ceil", $input:expr) => { + $crate::instructions::run_instr!("f64.ceil", $input, f64, f64) + }; + ("f64.floor", $input:expr) => { + $crate::instructions::run_instr!("f64.floor", $input, f64, f64) + }; + ("f64.trunc", $input:expr) => { + $crate::instructions::run_instr!("f64.trunc", $input, f64, f64) + }; + ("f64.nearest", $input:expr) => { + $crate::instructions::run_instr!("f64.nearest", $input, f64, f64) + }; + ("f64.sqrt", $input:expr) => { + $crate::instructions::run_instr!("f64.sqrt", $input, f64, f64) + }; + ("f64.add", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.add", $input1, f64, $input2, f64, f64) + }; + ("f64.sub", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.sub", $input1, f64, $input2, f64, f64) + }; + ("f64.mul", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.mul", $input1, f64, $input2, f64, f64) + }; + ("f64.div", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.div", $input1, f64, $input2, f64, f64) + }; + ("f64.min", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.min", $input1, f64, $input2, f64, f64) + }; + ("f64.max", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.max", $input1, f64, $input2, f64, f64) + }; + ("f64.copysign", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.copysign", $input1, f64, $input2, f64, f64) + }; + // + ("i32.trunc_f32_s", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_f32_s", $input, f32, i32) + }; + ("i32.trunc_f32_u", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_f32_u", $input, f32, u32) + }; + ("i32.trunc_f64_s", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_f64_s", $input, f64, i32) + }; + ("i32.trunc_f64_u", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_f64_u", $input, f64, u32) + }; + // + ("i64.trunc_f32_s", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_f32_s", $input, f32, i64) + }; + ("i64.trunc_f32_u", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_f32_u", $input, f32, u64) + }; + ("i64.trunc_f64_s", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_f64_s", $input, f64, i64) + }; + ("i64.trunc_f64_u", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_f64_u", $input, f64, u64) + }; + // + ("f32.convert_i32_s", $input:expr) => { + $crate::instructions::run_instr!("f32.convert_i32_s", $input, i32, f32) + }; + ("f32.convert_i32_u", $input:expr) => { + $crate::instructions::run_instr!("f32.convert_i32_u", $input, u32, f32) + }; + ("f32.convert_i64_s", $input:expr) => { + $crate::instructions::run_instr!("f32.convert_i64_s", $input, i64, f32) + }; + ("f32.convert_i64_u", $input:expr) => { + $crate::instructions::run_instr!("f32.convert_i64_u", $input, u64, f32) + }; + ("f32.demote_f64", $input:expr) => { + $crate::instructions::run_instr!("f32.demote_f64", $input, f64, f32) + }; + ("f64.convert_i32_s", $input:expr) => { + $crate::instructions::run_instr!("f64.convert_i32_s", $input, i32, f64) + }; + ("f64.convert_i32_u", $input:expr) => { + $crate::instructions::run_instr!("f64.convert_i32_u", $input, u32, f64) + }; + ("f64.convert_i64_s", $input:expr) => { + $crate::instructions::run_instr!("f64.convert_i64_s", $input, i64, f64) + }; + ("f64.convert_i64_u", $input:expr) => { + $crate::instructions::run_instr!("f64.convert_i64_u", $input, u64, f64) + }; + ("f64.promote_f32", $input:expr) => { + $crate::instructions::run_instr!("f64.promote_f32", $input, f32, f64) + }; + // + ("i32.reinterpret_f32", $input:expr) => { + $crate::instructions::run_instr!("i32.reinterpret_f32", $input, f32, i32) + }; + ("i64.reinterpret_f64", $input:expr) => { + $crate::instructions::run_instr!("i64.reinterpret_f64", $input, f64, i64) + }; + ("f32.reinterpret_i32", $input:expr) => { + $crate::instructions::run_instr!("f32.reinterpret_i32", $input, u32, f32) + }; + ("f64.reinterpret_i64", $input:expr) => { + $crate::instructions::run_instr!("f64.reinterpret_i64", $input, u64, f64) + }; + // + ("i32.trunc_sat_f32_s", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_sat_f32_s", $input, f32, i32) + }; + ("i32.trunc_sat_f32_u", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_sat_f32_u", $input, f32, u32) + }; + ("i32.trunc_sat_f64_s", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_sat_f64_s", $input, f64, i32) + }; + ("i32.trunc_sat_f64_u", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_sat_f64_u", $input, f64, u32) + }; + ("i64.trunc_sat_f32_s", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_sat_f32_s", $input, f32, i64) + }; + ("i64.trunc_sat_f32_u", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_sat_f32_u", $input, f32, u64) + }; + ("i64.trunc_sat_f64_s", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_sat_f64_s", $input, f64, i64) + }; + ("i64.trunc_sat_f64_u", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_sat_f64_u", $input, f64, u64) + }; +} +#[cfg_attr(not(target_arch = "wasm32"), allow(unused_imports))] +pub(crate) use run; + +#[cw_serde] +pub enum Value { + U32(u32), + U64(u64), + F32(u32), + F64(u64), +} + +impl Value { + pub fn u32(&self) -> u32 { + match self { + Self::U32(x) => *x, + v => panic!("expected u32, got {:?}", v), + } + } + + pub fn u64(&self) -> u64 { + match self { + Self::U64(x) => *x, + v => panic!("expected u64, got {:?}", v), + } + } + + pub fn f32(&self) -> f32 { + match self { + Self::F32(x) => f32::from_bits(*x), + v => panic!("expected f32, got {:?}", v), + } + } + + pub fn f64(&self) -> f64 { + match self { + Self::F64(x) => f64::from_bits(*x), + v => panic!("expected f64, got {:?}", v), + } + } +} + +/// Runs the given instruction with random inputs +#[cfg(target_arch = "wasm32")] +pub fn run_instruction(instr: &str, args: &[Value]) -> Value { + use Value::*; + + let arg1 = || args.get(0).unwrap(); + let arg2 = || args.get(0).unwrap(); + + match instr { + "f32.eq" => U32(run!("f32.eq", arg1().f32(), arg2().f32())), + "f32.ne" => U32(run!("f32.ne", arg1().f32(), arg2().f32())), + "f32.lt" => U32(run!("f32.lt", arg1().f32(), arg2().f32())), + "f32.gt" => U32(run!("f32.gt", arg1().f32(), arg2().f32())), + "f32.le" => U32(run!("f32.le", arg1().f32(), arg2().f32())), + "f32.ge" => U32(run!("f32.ge", arg1().f32(), arg2().f32())), + "f64.eq" => U32(run!("f64.eq", arg1().f64(), arg2().f64())), + "f64.ne" => U32(run!("f64.ne", arg1().f64(), arg2().f64())), + "f64.lt" => U32(run!("f64.lt", arg1().f64(), arg2().f64())), + "f64.gt" => U32(run!("f64.gt", arg1().f64(), arg2().f64())), + "f64.le" => U32(run!("f64.le", arg1().f64(), arg2().f64())), + "f64.ge" => U32(run!("f64.ge", arg1().f64(), arg2().f64())), + // + "f32.abs" => U32(run!("f32.abs", arg1().f32()).to_bits()), + "f32.neg" => U32(run!("f32.neg", arg1().f32()).to_bits()), + "f32.ceil" => U32(run!("f32.ceil", arg1().f32()).to_bits()), + "f32.floor" => U32(run!("f32.floor", arg1().f32()).to_bits()), + "f32.trunc" => U32(run!("f32.trunc", arg1().f32()).to_bits()), + "f32.nearest" => U32(run!("f32.nearest", arg1().f32()).to_bits()), + "f32.sqrt" => U32(run!("f32.sqrt", arg1().f32()).to_bits()), + "f32.add" => U32(run!("f32.add", arg1().f32(), arg2().f32()).to_bits()), + "f32.sub" => U32(run!("f32.sub", arg1().f32(), arg2().f32()).to_bits()), + "f32.mul" => U32(run!("f32.mul", arg1().f32(), arg2().f32()).to_bits()), + "f32.div" => U32(run!("f32.div", arg1().f32(), arg2().f32()).to_bits()), + "f32.min" => U32(run!("f32.min", arg1().f32(), arg2().f32()).to_bits()), + "f32.max" => U32(run!("f32.max", arg1().f32(), arg2().f32()).to_bits()), + "f32.copysign" => U32(run!("f32.copysign", arg1().f32(), arg2().f32()).to_bits()), + "f64.abs" => U64(run!("f64.abs", arg1().f64()).to_bits()), + "f64.neg" => U64(run!("f64.neg", arg1().f64()).to_bits()), + "f64.ceil" => U64(run!("f64.ceil", arg1().f64()).to_bits()), + "f64.floor" => U64(run!("f64.floor", arg1().f64()).to_bits()), + "f64.trunc" => U64(run!("f64.trunc", arg1().f64()).to_bits()), + "f64.nearest" => U64(run!("f64.nearest", arg1().f64()).to_bits()), + "f64.sqrt" => U64(run!("f64.sqrt", arg1().f64()).to_bits()), + "f64.add" => U64(run!("f64.add", arg1().f64(), arg2().f64()).to_bits()), + "f64.sub" => U64(run!("f64.sub", arg1().f64(), arg2().f64()).to_bits()), + "f64.mul" => U64(run!("f64.mul", arg1().f64(), arg2().f64()).to_bits()), + "f64.div" => U64(run!("f64.div", arg1().f64(), arg2().f64()).to_bits()), + "f64.min" => U64(run!("f64.min", arg1().f64(), arg2().f64()).to_bits()), + "f64.max" => U64(run!("f64.max", arg1().f64(), arg2().f64()).to_bits()), + "f64.copysign" => U64(run!("f64.copysign", arg1().f64(), arg2().f64()).to_bits()), + // + "i32.trunc_f32_s" => U32(run!("i32.trunc_f32_s", arg1().f32()) as u32), + "i32.trunc_f32_u" => U32(run!("i32.trunc_f32_u", arg1().f32())), + "i32.trunc_f64_s" => U32(run!("i32.trunc_f64_s", arg1().f64()) as u32), + "i32.trunc_f64_u" => U32(run!("i32.trunc_f64_u", arg1().f64())), + // + "i64.trunc_f32_s" => U64(run!("i64.trunc_f32_s", arg1().f32()) as u64), + "i64.trunc_f32_u" => U64(run!("i64.trunc_f32_u", arg1().f32())), + "i64.trunc_f64_s" => U64(run!("i64.trunc_f64_s", arg1().f64()) as u64), + "i64.trunc_f64_u" => U64(run!("i64.trunc_f64_u", arg1().f64())), + // + "f32.convert_i32_s" => U32(run!("f32.convert_i32_s", arg1().u32() as i32).to_bits()), + "f32.convert_i32_u" => U32(run!("f32.convert_i32_u", arg1().u32()).to_bits()), + "f32.convert_i64_s" => U32(run!("f32.convert_i64_s", arg1().u64() as i64).to_bits()), + "f32.convert_i64_u" => U32(run!("f32.convert_i64_u", arg1().u64()).to_bits()), + "f32.demote_f64" => U32(run!("f32.demote_f64", arg1().f64()).to_bits()), + "f64.convert_i32_s" => U64(run!("f64.convert_i32_s", arg1().u32() as i32).to_bits()), + "f64.convert_i32_u" => U64(run!("f64.convert_i32_u", arg1().u32()).to_bits()), + "f64.convert_i64_s" => U64(run!("f64.convert_i64_s", arg1().u64() as i64).to_bits()), + "f64.convert_i64_u" => U64(run!("f64.convert_i64_u", arg1().u64()).to_bits()), + "f64.promote_f32" => U64(run!("f64.promote_f32", arg1().f32()).to_bits()), + // + "i32.reinterpret_f32" => U32(run!("i32.reinterpret_f32", arg1().f32()) as u32), + "i64.reinterpret_f64" => U64(run!("i64.reinterpret_f64", arg1().f64()) as u64), + "f32.reinterpret_i32" => U32(run!("f32.reinterpret_i32", arg1().u32()).to_bits() as u32), + "f64.reinterpret_i64" => U64(run!("f64.reinterpret_i64", arg1().u64()).to_bits() as u64), + // + "i32.trunc_sat_f32_s" => U32(run!("i32.trunc_sat_f32_s", arg1().f32()) as u32), + "i32.trunc_sat_f32_u" => U32(run!("i32.trunc_sat_f32_u", arg1().f32()) as u32), + "i32.trunc_sat_f64_s" => U32(run!("i32.trunc_sat_f64_s", arg1().f64()) as u32), + "i32.trunc_sat_f64_u" => U32(run!("i32.trunc_sat_f64_u", arg1().f64()) as u32), + "i64.trunc_sat_f32_s" => U64(run!("i64.trunc_sat_f32_s", arg1().f32()) as u64), + "i64.trunc_sat_f32_u" => U64(run!("i64.trunc_sat_f32_u", arg1().f32()) as u64), + "i64.trunc_sat_f64_s" => U64(run!("i64.trunc_sat_f64_s", arg1().f64()) as u64), + "i64.trunc_sat_f64_u" => U64(run!("i64.trunc_sat_f64_u", arg1().f64()) as u64), + _ => panic!("unknown instruction: {}", instr), + } +} + +pub fn random_args_for(instr: &str, rng: &mut impl RngCore) -> Vec { + let a = random_f32(rng); + let b = random_f32(rng); + let c = random_f64(rng); + let d = random_f64(rng); + let e = rng.next_u32(); + let f = rng.next_u64(); + + use Value::*; + + let f32x2 = vec![F32(a.to_bits()), F32(b.to_bits())]; + let f64x2 = vec![F64(c.to_bits()), F64(d.to_bits())]; + let f32 = vec![F32(a.to_bits())]; + let f64 = vec![F64(c.to_bits())]; + let u32 = vec![U32(e)]; + let u64 = vec![U64(f)]; + + match instr { + "f32.eq" => f32x2, + "f32.ne" => f32x2, + "f32.lt" => f32x2, + "f32.gt" => f32x2, + "f32.le" => f32x2, + "f32.ge" => f32x2, + "f64.eq" => f64x2, + "f64.ne" => f64x2, + "f64.lt" => f64x2, + "f64.gt" => f64x2, + "f64.le" => f64x2, + "f64.ge" => f64x2, + // + "f32.abs" => f32, + "f32.neg" => f32, + "f32.ceil" => f32, + "f32.floor" => f32, + "f32.trunc" => f32, + "f32.nearest" => f32, + "f32.sqrt" => f32, + "f32.add" => f32x2, + "f32.sub" => f32x2, + "f32.mul" => f32x2, + "f32.div" => f32x2, + "f32.min" => f32x2, + "f32.max" => f32x2, + "f32.copysign" => f32x2, + "f64.abs" => f64, + "f64.neg" => f64, + "f64.ceil" => f64, + "f64.floor" => f64, + "f64.trunc" => f64, + "f64.nearest" => f64, + "f64.sqrt" => f64, + "f64.add" => f64x2, + "f64.sub" => f64x2, + "f64.mul" => f64x2, + "f64.div" => f64x2, + "f64.min" => f64x2, + "f64.max" => f64x2, + "f64.copysign" => f64x2, + // + "i32.trunc_f32_s" => f32, + "i32.trunc_f32_u" => f32, + "i32.trunc_f64_s" => f64, + "i32.trunc_f64_u" => f64, + // + "i64.trunc_f32_s" => f32, + "i64.trunc_f32_u" => f32, + "i64.trunc_f64_s" => f64, + "i64.trunc_f64_u" => f64, + // + "f32.convert_i32_s" => u32, + "f32.convert_i32_u" => u32, + "f32.convert_i64_s" => u64, + "f32.convert_i64_u" => u64, + "f32.demote_f64" => f64, + "f64.convert_i32_s" => u32, + "f64.convert_i32_u" => u32, + "f64.convert_i64_s" => u64, + "f64.convert_i64_u" => u64, + "f64.promote_f32" => f32, + // + "i32.reinterpret_f32" => f32, + "i64.reinterpret_f64" => f64, + "f32.reinterpret_i32" => u32, + "f64.reinterpret_i64" => u64, + // + "i32.trunc_sat_f32_s" => f32, + "i32.trunc_sat_f32_u" => f32, + "i32.trunc_sat_f64_s" => f64, + "i32.trunc_sat_f64_u" => f64, + "i64.trunc_sat_f32_s" => f32, + "i64.trunc_sat_f32_u" => f32, + "i64.trunc_sat_f64_s" => f64, + "i64.trunc_sat_f64_u" => f64, + _ => panic!("unknown instruction: {}", instr), + } +} + +pub const FLOAT_INSTRUCTIONS: [&str; 70] = [ + "f32.eq", + "f32.ne", + "f32.lt", + "f32.gt", + "f32.le", + "f32.ge", + "f64.eq", + "f64.ne", + "f64.lt", + "f64.gt", + "f64.le", + "f64.ge", + // + "f32.abs", + "f32.neg", + "f32.ceil", + "f32.floor", + "f32.trunc", + "f32.nearest", + "f32.sqrt", + "f32.add", + "f32.sub", + "f32.mul", + "f32.div", + "f32.min", + "f32.max", + "f32.copysign", + "f64.abs", + "f64.neg", + "f64.ceil", + "f64.floor", + "f64.trunc", + "f64.nearest", + "f64.sqrt", + "f64.add", + "f64.sub", + "f64.mul", + "f64.div", + "f64.min", + "f64.max", + "f64.copysign", + // + "i32.trunc_f32_s", + "i32.trunc_f32_u", + "i32.trunc_f64_s", + "i32.trunc_f64_u", + // + "i64.trunc_f32_s", + "i64.trunc_f32_u", + "i64.trunc_f64_s", + "i64.trunc_f64_u", + // + "f32.convert_i32_s", + "f32.convert_i32_u", + "f32.convert_i64_s", + "f32.convert_i64_u", + "f32.demote_f64", + "f64.convert_i32_s", + "f64.convert_i32_u", + "f64.convert_i64_s", + "f64.convert_i64_u", + "f64.promote_f32", + // + "i32.reinterpret_f32", + "i64.reinterpret_f64", + "f32.reinterpret_i32", + "f64.reinterpret_i64", + // + "i32.trunc_sat_f32_s", + "i32.trunc_sat_f32_u", + "i32.trunc_sat_f64_s", + "i32.trunc_sat_f64_u", + "i64.trunc_sat_f32_s", + "i64.trunc_sat_f32_u", + "i64.trunc_sat_f64_s", + "i64.trunc_sat_f64_u", +]; diff --git a/contracts/floaty/src/lib.rs b/contracts/floaty/src/lib.rs index 0573cc96c..ba67016d3 100644 --- a/contracts/floaty/src/lib.rs +++ b/contracts/floaty/src/lib.rs @@ -1,4 +1,7 @@ +#![cfg_attr(target_arch = "wasm32", feature(asm_experimental_arch))] + pub mod contract; -mod errors; +pub(crate) mod floats; +mod instructions; pub mod msg; pub mod state; diff --git a/contracts/floaty/src/msg.rs b/contracts/floaty/src/msg.rs index caa8fc4e3..6baa9a1ef 100644 --- a/contracts/floaty/src/msg.rs +++ b/contracts/floaty/src/msg.rs @@ -1,30 +1,26 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -#[cw_serde] -pub struct InstantiateMsg { - pub verifier: String, - pub beneficiary: String, -} +use crate::instructions::Value; #[cw_serde] -pub enum ExecuteMsg { - /// Releasing all funds in the contract to the beneficiary. This is the only "proper" action of this demo contract. - Release {}, +pub enum ValueType { + Float, + Int, } #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { - /// returns a human-readable representation of the verifier - /// use to ensure query path works in integration tests - #[returns(VerifierResponse)] - Verifier {}, - /// This returns cosmwasm_std::AllBalanceResponse to demo use of the querier - #[returns(cosmwasm_std::AllBalanceResponse)] - OtherBalance { address: String }, -} - -#[cw_serde] -pub struct VerifierResponse { - pub verifier: String, + /// Returns valid random arguments for the given instruction + #[returns(Vec)] + RandomArgsFor { instruction: String, seed: u64 }, + /// Returns a list of all instructions + #[returns(Vec)] + Instructions {}, + /// Runs the given instruction with the given arguments and returns the result + #[returns(Value)] + Run { + instruction: String, + args: Vec, + }, } diff --git a/contracts/floaty/tests/integration.rs b/contracts/floaty/tests/integration.rs index 5d3de04e5..593ae1d1e 100644 --- a/contracts/floaty/tests/integration.rs +++ b/contracts/floaty/tests/integration.rs @@ -6,7 +6,6 @@ static WASM: &[u8] = include_bytes!("../target/wasm32-unknown-unknown/release/fl // static WASM: &[u8] = include_bytes!("../contract.wasm"); #[test] -#[should_panic(expected = "Float operator detected")] -fn instantiate_fails() { - let mut _deps = mock_instance(WASM, &[]); +fn validation_succeeds() { + mock_instance(WASM, &[]); } diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock index ca1919229..7f95aa7ef 100644 --- a/contracts/hackatom/Cargo.lock +++ b/contracts/hackatom/Cargo.lock @@ -73,6 +73,12 @@ 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 = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -572,7 +591,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -928,6 +947,15 @@ 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.8.0" @@ -1263,6 +1291,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1368,6 +1402,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1412,6 +1456,12 @@ 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 = "subtle" version = "2.5.0" @@ -1657,9 +1707,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1670,6 +1720,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1685,19 +1736,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1708,9 +1763,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1727,9 +1782,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1746,9 +1801,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1758,9 +1813,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1769,9 +1824,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1785,14 +1840,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/hackatom/src/contract.rs b/contracts/hackatom/src/contract.rs index 6de12ab53..f62ffcb31 100644 --- a/contracts/hackatom/src/contract.rs +++ b/contracts/hackatom/src/contract.rs @@ -1,7 +1,7 @@ use sha2::{Digest, Sha256}; use cosmwasm_std::{ - entry_point, from_slice, to_binary, to_vec, Addr, AllBalanceResponse, Api, BankMsg, + entry_point, from_json, to_json_binary, to_json_vec, Addr, AllBalanceResponse, Api, BankMsg, CanonicalAddr, Deps, DepsMut, Env, Event, MessageInfo, QueryRequest, QueryResponse, Response, StdError, StdResult, WasmMsg, WasmQuery, }; @@ -24,7 +24,7 @@ pub fn instantiate( deps.storage.set( CONFIG_KEY, - &to_vec(&State { + &to_json_vec(&State { verifier: deps.api.addr_validate(&msg.verifier)?, beneficiary: deps.api.addr_validate(&msg.beneficiary)?, funder: info.sender, @@ -41,9 +41,9 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result Result Result { fn do_message_loop(env: Env) -> Result { let resp = Response::new().add_message(WasmMsg::Execute { contract_addr: env.contract.address.into(), - msg: to_binary(&ExecuteMsg::MessageLoop {})?, + msg: to_json_binary(&ExecuteMsg::MessageLoop {})?, funds: vec![], }); Ok(resp) @@ -248,12 +248,12 @@ fn do_user_errors_in_api_calls(api: &dyn Api) -> Result { #[entry_point] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Verifier {} => to_binary(&query_verifier(deps)?), - QueryMsg::OtherBalance { address } => to_binary(&query_other_balance(deps, address)?), + QueryMsg::Verifier {} => to_json_binary(&query_verifier(deps)?), + QueryMsg::OtherBalance { address } => to_json_binary(&query_other_balance(deps, address)?), QueryMsg::Recurse { depth, work } => { - to_binary(&query_recurse(deps, depth, work, env.contract.address)?) + to_json_binary(&query_recurse(deps, depth, work, env.contract.address)?) } - QueryMsg::GetInt {} => to_binary(&query_int()), + QueryMsg::GetInt {} => to_json_binary(&query_int()), } } @@ -262,7 +262,7 @@ fn query_verifier(deps: Deps) -> StdResult { .storage .get(CONFIG_KEY) .ok_or_else(|| StdError::not_found("State"))?; - let state: State = from_slice(&data)?; + let state: State = from_json(data)?; Ok(VerifierResponse { verifier: state.verifier.into(), }) @@ -293,7 +293,7 @@ fn query_recurse(deps: Deps, depth: u32, work: u32, contract: Addr) -> StdResult }; let query = QueryRequest::Wasm(WasmQuery::Smart { contract_addr: contract.into(), - msg: to_binary(&req)?, + msg: to_json_binary(&req)?, }); deps.querier.query(&query) } @@ -336,7 +336,7 @@ mod tests { // it worked, let's check the state let data = deps.storage.get(CONFIG_KEY).expect("no data stored"); - let state: State = from_slice(&data).unwrap(); + let state: State = from_json(data).unwrap(); assert_eq!(state, expected_state); } @@ -514,7 +514,7 @@ mod tests { // state should not change let data = deps.storage.get(CONFIG_KEY).expect("no data stored"); - let state: State = from_slice(&data).unwrap(); + let state: State = from_json(data).unwrap(); assert_eq!( state, State { diff --git a/contracts/hackatom/tests/integration.rs b/contracts/hackatom/tests/integration.rs index 10f77d091..890309ffd 100644 --- a/contracts/hackatom/tests/integration.rs +++ b/contracts/hackatom/tests/integration.rs @@ -18,7 +18,7 @@ //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) use cosmwasm_std::{ - assert_approx_eq, coins, from_binary, to_vec, Addr, AllBalanceResponse, BankMsg, Binary, + assert_approx_eq, coins, from_json, to_json_vec, Addr, AllBalanceResponse, BankMsg, Binary, ContractResult, Empty, Response, SubMsg, }; use cosmwasm_vm::{ @@ -183,7 +183,7 @@ fn querier_callbacks_work() { // querying with balance gets the balance let query_msg = QueryMsg::OtherBalance { address: rich_addr }; let query_response = query(&mut deps, mock_env(), query_msg).unwrap(); - let bal: AllBalanceResponse = from_binary(&query_response).unwrap(); + let bal: AllBalanceResponse = from_json(query_response).unwrap(); assert_eq!(bal.amount, rich_balance); // querying other accounts gets none @@ -191,7 +191,7 @@ fn querier_callbacks_work() { address: String::from("someone else"), }; let query_response = query(&mut deps, mock_env(), query_msg).unwrap(); - let bal: AllBalanceResponse = from_binary(&query_response).unwrap(); + let bal: AllBalanceResponse = from_json(query_response).unwrap(); assert_eq!(bal.amount, vec![]); } @@ -322,7 +322,7 @@ fn execute_cpu_loop() { &mut deps, &mock_env(), &execute_info, - &to_vec(&ExecuteMsg::CpuLoop {}).unwrap(), + &to_json_vec(&ExecuteMsg::CpuLoop {}).unwrap(), ); assert!(execute_res.is_err()); assert_eq!(deps.get_gas_left(), 0); @@ -344,7 +344,7 @@ fn execute_storage_loop() { &mut deps, &mock_env(), &execute_info, - &to_vec(&ExecuteMsg::StorageLoop {}).unwrap(), + &to_json_vec(&ExecuteMsg::StorageLoop {}).unwrap(), ); assert!(execute_res.is_err()); assert_eq!(deps.get_gas_left(), 0); @@ -366,7 +366,7 @@ fn execute_memory_loop() { &mut deps, &mock_env(), &execute_info, - &to_vec(&ExecuteMsg::MemoryLoop {}).unwrap(), + &to_json_vec(&ExecuteMsg::MemoryLoop {}).unwrap(), ); assert!(execute_res.is_err()); assert_eq!(deps.get_gas_left(), 0); @@ -448,7 +448,7 @@ fn execute_panic() { &mut deps, &mock_env(), &execute_info, - &to_vec(&ExecuteMsg::Panic {}).unwrap(), + &to_json_vec(&ExecuteMsg::Panic {}).unwrap(), ); match execute_res.unwrap_err() { VmError::RuntimeErr { msg, .. } => { diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock index 970cf0c26..826fc3e90 100644 --- a/contracts/ibc-reflect-send/Cargo.lock +++ b/contracts/ibc-reflect-send/Cargo.lock @@ -73,6 +73,12 @@ 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 = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -572,7 +591,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -926,6 +945,15 @@ 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.8.0" @@ -1261,6 +1289,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1366,6 +1400,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1410,6 +1454,12 @@ 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 = "subtle" version = "2.5.0" @@ -1655,9 +1705,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1668,6 +1718,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1683,19 +1734,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1706,9 +1761,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1725,9 +1780,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1744,9 +1799,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1756,9 +1811,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1767,9 +1822,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1783,14 +1838,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/ibc-reflect-send/src/contract.rs b/contracts/ibc-reflect-send/src/contract.rs index 50d074f3a..23adc82b8 100644 --- a/contracts/ibc-reflect-send/src/contract.rs +++ b/contracts/ibc-reflect-send/src/contract.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, to_binary, CosmosMsg, Deps, DepsMut, Env, IbcMsg, MessageInfo, QueryResponse, + entry_point, to_json_binary, CosmosMsg, Deps, DepsMut, Env, IbcMsg, MessageInfo, QueryResponse, Response, StdError, StdResult, }; @@ -79,7 +79,7 @@ pub fn handle_send_msgs( let packet = PacketMsg::Dispatch { msgs }; let msg = IbcMsg::SendPacket { channel_id, - data: to_binary(&packet)?, + data: to_json_binary(&packet)?, timeout: env.block.time.plus_seconds(PACKET_LIFETIME).into(), }; @@ -107,7 +107,7 @@ pub fn handle_check_remote_balance( let packet = PacketMsg::Balances {}; let msg = IbcMsg::SendPacket { channel_id, - data: to_binary(&packet)?, + data: to_json_binary(&packet)?, timeout: env.block.time.plus_seconds(PACKET_LIFETIME).into(), }; @@ -168,9 +168,9 @@ pub fn handle_send_funds( #[entry_point] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Admin {} => to_binary(&query_admin(deps)?), - QueryMsg::Account { channel_id } => to_binary(&query_account(deps, channel_id)?), - QueryMsg::ListAccounts {} => to_binary(&query_list_accounts(deps)?), + QueryMsg::Admin {} => to_json_binary(&query_admin(deps)?), + QueryMsg::Account { channel_id } => to_json_binary(&query_account(deps, channel_id)?), + QueryMsg::ListAccounts {} => to_json_binary(&query_list_accounts(deps)?), } } diff --git a/contracts/ibc-reflect-send/src/ibc.rs b/contracts/ibc-reflect-send/src/ibc.rs index 634fed3e8..6faabd5c1 100644 --- a/contracts/ibc-reflect-send/src/ibc.rs +++ b/contracts/ibc-reflect-send/src/ibc.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, from_slice, to_binary, DepsMut, Env, IbcBasicResponse, IbcChannelCloseMsg, + entry_point, from_json, to_json_binary, DepsMut, Env, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcMsg, IbcOrder, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, Never, StdError, StdResult, }; @@ -59,7 +59,7 @@ pub fn ibc_channel_connect( let packet = PacketMsg::WhoAmI {}; let msg = IbcMsg::SendPacket { channel_id: channel_id.clone(), - data: to_binary(&packet)?, + data: to_json_binary(&packet)?, timeout: env.block.time.plus_seconds(PACKET_LIFETIME).into(), }; @@ -108,18 +108,18 @@ pub fn ibc_packet_ack( // which local channel was this packet send from let caller = msg.original_packet.src.channel_id; // we need to parse the ack based on our request - let packet: PacketMsg = from_slice(&msg.original_packet.data)?; + let packet: PacketMsg = from_json(msg.original_packet.data)?; match packet { PacketMsg::Dispatch { .. } => { - let res: AcknowledgementMsg = from_slice(&msg.acknowledgement.data)?; + let res: AcknowledgementMsg = from_json(msg.acknowledgement.data)?; acknowledge_dispatch(deps, caller, res) } PacketMsg::WhoAmI {} => { - let res: AcknowledgementMsg = from_slice(&msg.acknowledgement.data)?; + let res: AcknowledgementMsg = from_json(msg.acknowledgement.data)?; acknowledge_who_am_i(deps, caller, res) } PacketMsg::Balances {} => { - let res: AcknowledgementMsg = from_slice(&msg.acknowledgement.data)?; + let res: AcknowledgementMsg = from_json(msg.acknowledgement.data)?; acknowledge_balances(deps, env, caller, res) } } @@ -305,7 +305,7 @@ mod tests { channel_id: channel_id.into(), }; let r = query(deps.as_ref(), mock_env(), q).unwrap(); - let acct: AccountResponse = from_slice(&r).unwrap(); + let acct: AccountResponse = from_json(r).unwrap(); assert!(acct.remote_addr.is_none()); assert!(acct.remote_balance.is_empty()); assert_eq!(0, acct.last_update_time.nanos()); @@ -319,7 +319,7 @@ mod tests { channel_id: channel_id.into(), }; let r = query(deps.as_ref(), mock_env(), q).unwrap(); - let acct: AccountResponse = from_slice(&r).unwrap(); + let acct: AccountResponse = from_json(r).unwrap(); assert_eq!(acct.remote_addr.unwrap(), remote_addr); assert!(acct.remote_balance.is_empty()); assert_eq!(0, acct.last_update_time.nanos()); diff --git a/contracts/ibc-reflect-send/src/state.rs b/contracts/ibc-reflect-send/src/state.rs index 062ff187e..44f78f4e0 100644 --- a/contracts/ibc-reflect-send/src/state.rs +++ b/contracts/ibc-reflect-send/src/state.rs @@ -1,9 +1,9 @@ use serde::{Deserialize, Serialize}; use cosmwasm_std::{ - from_slice, + from_json, storage_keys::{namespace_with_key, to_length_prefixed}, - to_vec, Addr, Coin, Order, StdError, StdResult, Storage, Timestamp, + to_json_vec, Addr, Coin, Order, StdError, StdResult, Storage, Timestamp, }; pub const KEY_CONFIG: &[u8] = b"config"; @@ -33,7 +33,7 @@ pub struct AccountData { pub fn may_load_account(storage: &dyn Storage, id: &str) -> StdResult> { storage .get(&namespace_with_key(&[PREFIX_ACCOUNTS], id.as_bytes())) - .map(|v| from_slice(&v)) + .map(from_json) .transpose() } @@ -44,7 +44,7 @@ pub fn load_account(storage: &dyn Storage, id: &str) -> StdResult { pub fn save_account(storage: &mut dyn Storage, id: &str, account: &AccountData) -> StdResult<()> { storage.set( &namespace_with_key(&[PREFIX_ACCOUNTS], id.as_bytes()), - &to_vec(account)?, + &to_json_vec(account)?, ); Ok(()) } @@ -63,7 +63,7 @@ pub fn range_accounts( .map(|(key, val)| { Ok(( String::from_utf8(key[PREFIX_ACCOUNTS.len() + 2..].to_vec())?, - from_slice(&val)?, + from_json(val)?, )) }) } @@ -72,10 +72,10 @@ pub fn load_config(storage: &dyn Storage) -> StdResult { storage .get(&to_length_prefixed(KEY_CONFIG)) .ok_or_else(|| StdError::not_found("config")) - .and_then(|v| from_slice(&v)) + .and_then(from_json) } pub fn save_config(storage: &mut dyn Storage, item: &Config) -> StdResult<()> { - storage.set(&to_length_prefixed(KEY_CONFIG), &to_vec(item)?); + storage.set(&to_length_prefixed(KEY_CONFIG), &to_json_vec(item)?); Ok(()) } diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock index 71dbd710d..a2a9ffbcf 100644 --- a/contracts/ibc-reflect/Cargo.lock +++ b/contracts/ibc-reflect/Cargo.lock @@ -73,6 +73,12 @@ 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 = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -572,7 +591,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -926,6 +945,15 @@ 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.8.0" @@ -1261,6 +1289,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1366,6 +1400,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1410,6 +1454,12 @@ 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 = "subtle" version = "2.5.0" @@ -1655,9 +1705,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1668,6 +1718,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1683,19 +1734,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1706,9 +1761,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1725,9 +1780,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1744,9 +1799,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1756,9 +1811,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1767,9 +1822,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1783,14 +1838,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/ibc-reflect/src/contract.rs b/contracts/ibc-reflect/src/contract.rs index 1af5d90e9..7d8efb44c 100644 --- a/contracts/ibc-reflect/src/contract.rs +++ b/contracts/ibc-reflect/src/contract.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ - entry_point, from_slice, to_binary, wasm_execute, BankMsg, Binary, CosmosMsg, Deps, DepsMut, - Empty, Env, Event, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannelCloseMsg, + entry_point, from_json, to_json_binary, wasm_execute, BankMsg, Binary, CosmosMsg, Deps, + DepsMut, Empty, Env, Event, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, IbcOrder, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, MessageInfo, Never, QueryResponse, Reply, Response, StdError, StdResult, SubMsg, SubMsgResponse, SubMsgResult, @@ -91,8 +91,8 @@ pub fn handle_init_callback(deps: DepsMut, response: SubMsgResponse) -> StdResul #[entry_point] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Account { channel_id } => to_binary(&query_account(deps, channel_id)?), - QueryMsg::ListAccounts {} => to_binary(&query_list_accounts(deps)?), + QueryMsg::Account { channel_id } => to_json_binary(&query_account(deps, channel_id)?), + QueryMsg::ListAccounts {} => to_json_binary(&query_list_accounts(deps)?), } } @@ -224,7 +224,7 @@ pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> StdResult { // this encode an error or error message into a proper acknowledgement to the recevier fn encode_ibc_error(msg: impl Into) -> Binary { // this cannot error, unwrap to keep the interface simple - to_binary(&AcknowledgementMsg::<()>::Error(msg.into())).unwrap() + to_json_binary(&AcknowledgementMsg::<()>::Error(msg.into())).unwrap() } #[entry_point] @@ -241,7 +241,7 @@ pub fn ibc_packet_receive( let packet = msg.packet; // which local channel did this packet come on let caller = packet.dest.channel_id; - let msg: PacketMsg = from_slice(&packet.data)?; + let msg: PacketMsg = from_json(packet.data)?; match msg { PacketMsg::Dispatch { msgs } => receive_dispatch(deps, caller, msgs), PacketMsg::WhoAmI {} => receive_who_am_i(deps, caller), @@ -267,7 +267,7 @@ fn receive_who_am_i(deps: DepsMut, caller: String) -> StdResult StdResult::Ok(()))?; + let acknowledgement = to_json_binary(&AcknowledgementMsg::::Ok(()))?; // create the message to re-dispatch to the reflect contract let reflect_msg = ReflectExecuteMsg::ReflectMsg { msgs }; let wasm_msg = wasm_execute(reflect_addr, &reflect_msg, vec![])?; @@ -322,7 +322,7 @@ fn execute_error(text: String) -> StdResult { } fn execute_return_msgs(msgs: Vec) -> StdResult { - let acknowledgement = to_binary(&AcknowledgementMsg::::Ok(()))?; + let acknowledgement = to_json_binary(&AcknowledgementMsg::::Ok(()))?; Ok(IbcReceiveResponse::new() .set_ack(acknowledgement) @@ -358,7 +358,7 @@ mod tests { mock_ibc_channel_open_init, mock_ibc_channel_open_try, mock_ibc_packet_recv, mock_info, mock_wasmd_attr, MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR, }; - use cosmwasm_std::{attr, coin, coins, from_slice, BankMsg, OwnedDeps, WasmMsg}; + use cosmwasm_std::{attr, coin, coins, from_json, BankMsg, OwnedDeps, WasmMsg}; const CREATOR: &str = "creator"; // code id of the reflect contract @@ -482,7 +482,7 @@ mod tests { // no accounts set yet let raw = query(deps.as_ref(), mock_env(), QueryMsg::ListAccounts {}).unwrap(); - let res: ListAccountsResponse = from_slice(&raw).unwrap(); + let res: ListAccountsResponse = from_json(raw).unwrap(); assert_eq!(0, res.accounts.len()); // fake a reply and ensure this works @@ -497,7 +497,7 @@ mod tests { // ensure this is now registered let raw = query(deps.as_ref(), mock_env(), QueryMsg::ListAccounts {}).unwrap(); - let res: ListAccountsResponse = from_slice(&raw).unwrap(); + let res: ListAccountsResponse = from_json(raw).unwrap(); assert_eq!(1, res.accounts.len()); assert_eq!( &res.accounts[0], @@ -516,7 +516,7 @@ mod tests { }, ) .unwrap(); - let res: AccountResponse = from_slice(&raw).unwrap(); + let res: AccountResponse = from_json(raw).unwrap(); assert_eq!(res.account.unwrap(), REFLECT_ADDR); } @@ -546,7 +546,7 @@ mod tests { res.events[0] ); // acknowledgement is an error - let ack: AcknowledgementMsg = from_slice(&res.acknowledgement).unwrap(); + let ack: AcknowledgementMsg = from_json(res.acknowledgement).unwrap(); assert_eq!( ack.unwrap_err(), "invalid packet: account channel-123 not found" @@ -560,7 +560,7 @@ mod tests { let res = ibc_packet_receive(deps.as_mut(), mock_env(), msg).unwrap(); // assert app-level success - let ack: AcknowledgementMsg<()> = from_slice(&res.acknowledgement).unwrap(); + let ack: AcknowledgementMsg<()> = from_json(res.acknowledgement).unwrap(); ack.unwrap(); // and we dispatch the BankMsg via submessage @@ -577,7 +577,7 @@ mod tests { assert_eq!(account, contract_addr.as_str()); assert_eq!(0, funds.len()); // parse the message - should callback with proper channel_id - let rmsg: ReflectExecuteMsg = from_slice(msg).unwrap(); + let rmsg: ReflectExecuteMsg = from_json(msg).unwrap(); assert_eq!( rmsg, ReflectExecuteMsg::ReflectMsg { @@ -597,7 +597,7 @@ mod tests { // we didn't dispatch anything assert_eq!(0, res.messages.len()); // acknowledgement is an error - let ack: AcknowledgementMsg = from_slice(&res.acknowledgement).unwrap(); + let ack: AcknowledgementMsg = from_json(res.acknowledgement).unwrap(); assert_eq!(ack.unwrap_err(), "invalid packet: Error parsing into type ibc_reflect::msg::PacketMsg: unknown variant `reflect_code_id`, expected one of `dispatch`, `who_am_i`, `balances`, `panic`, `return_err`, `return_msgs`"); } @@ -616,7 +616,7 @@ mod tests { // channel should be listed and have balance let raw = query(deps.as_ref(), mock_env(), QueryMsg::ListAccounts {}).unwrap(); - let res: ListAccountsResponse = from_slice(&raw).unwrap(); + let res: ListAccountsResponse = from_json(raw).unwrap(); assert_eq!(1, res.accounts.len()); let balance = deps.as_ref().querier.query_all_balances(account).unwrap(); assert_eq!(funds, balance); @@ -632,7 +632,7 @@ mod tests { }) = &res.messages[0].msg { assert_eq!(contract_addr.as_str(), account); - let reflect: ReflectExecuteMsg = from_slice(msg).unwrap(); + let reflect: ReflectExecuteMsg = from_json(msg).unwrap(); match reflect { ReflectExecuteMsg::ReflectMsg { msgs } => { assert_eq!(1, msgs.len()); @@ -652,7 +652,7 @@ mod tests { // and removes the account lookup let raw = query(deps.as_ref(), mock_env(), QueryMsg::ListAccounts {}).unwrap(); - let res: ListAccountsResponse = from_slice(&raw).unwrap(); + let res: ListAccountsResponse = from_json(raw).unwrap(); assert_eq!(0, res.accounts.len()); } } diff --git a/contracts/ibc-reflect/src/state.rs b/contracts/ibc-reflect/src/state.rs index bbb1fdaaa..112f0227d 100644 --- a/contracts/ibc-reflect/src/state.rs +++ b/contracts/ibc-reflect/src/state.rs @@ -1,9 +1,9 @@ use std::any::type_name; use cosmwasm_std::{ - from_slice, + from_json, storage_keys::{namespace_with_key, to_length_prefixed}, - to_vec, Addr, Order, StdError, StdResult, Storage, + to_json_vec, Addr, Order, StdError, StdResult, Storage, }; use schemars::JsonSchema; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -22,7 +22,7 @@ pub struct Config { pub fn may_load_account(storage: &dyn Storage, id: &str) -> StdResult> { storage .get(&namespace_with_key(&[PREFIX_ACCOUNTS], id.as_bytes())) - .map(|v| from_slice(&v)) + .map(from_json) .transpose() } @@ -33,7 +33,7 @@ pub fn load_account(storage: &dyn Storage, id: &str) -> StdResult { pub fn save_account(storage: &mut dyn Storage, id: &str, account: &Addr) -> StdResult<()> { storage.set( &namespace_with_key(&[PREFIX_ACCOUNTS], id.as_bytes()), - &to_vec(account)?, + &to_json_vec(account)?, ); Ok(()) } @@ -52,7 +52,7 @@ pub fn range_accounts( .map(|(key, val)| { Ok(( String::from_utf8(key[PREFIX_ACCOUNTS.len() + 2..].to_vec())?, - from_slice(&val)?, + from_json(val)?, )) }) } @@ -61,10 +61,10 @@ pub fn load_item(storage: &dyn Storage, key: &[u8]) -> StdR storage .get(&to_length_prefixed(key)) .ok_or_else(|| StdError::not_found(type_name::())) - .and_then(|v| from_slice(&v)) + .and_then(from_json) } pub fn save_item(storage: &mut dyn Storage, key: &[u8], item: &T) -> StdResult<()> { - storage.set(&to_length_prefixed(key), &to_vec(item)?); + storage.set(&to_length_prefixed(key), &to_json_vec(item)?); Ok(()) } diff --git a/contracts/query-queue/Cargo.lock b/contracts/query-queue/Cargo.lock index ce198e78c..10b6cf50f 100644 --- a/contracts/query-queue/Cargo.lock +++ b/contracts/query-queue/Cargo.lock @@ -73,6 +73,12 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -169,9 +175,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -203,6 +209,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.6", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -245,6 +252,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -255,6 +263,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.6", + "static_assertions", "thiserror", "uuid", ] @@ -418,14 +427,20 @@ dependencies = [ ] [[package]] -name = "crossbeam-utils" -version = "0.8.14" +name = "crossbeam-queue" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", + "crossbeam-utils", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + [[package]] name = "crypto-bigint" version = "0.5.3" @@ -463,9 +478,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -473,26 +488,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 1.0.105", + "syn 2.0.39", ] [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 1.0.105", + "syn 2.0.39", ] [[package]] @@ -579,7 +594,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.8", ] [[package]] @@ -658,23 +673,23 @@ dependencies = [ [[package]] name = "enumset" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.6.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.105", + "syn 2.0.39", ] [[package]] @@ -934,6 +949,15 @@ 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.7.1" @@ -1292,6 +1316,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" + [[package]] name = "serde" version = "1.0.150" @@ -1397,6 +1427,16 @@ dependencies = [ "digest 0.10.6", ] +[[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 = "signature" version = "2.1.0" @@ -1441,6 +1481,12 @@ 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 = "subtle" version = "2.4.1" @@ -1687,9 +1733,9 @@ checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1700,6 +1746,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1715,19 +1762,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.8", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1738,9 +1789,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1757,9 +1808,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1776,9 +1827,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1788,9 +1839,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1799,9 +1850,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1815,14 +1866,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/query-queue/src/contract.rs b/contracts/query-queue/src/contract.rs index 19ecf2108..96ad5f344 100644 --- a/contracts/query-queue/src/contract.rs +++ b/contracts/query-queue/src/contract.rs @@ -2,8 +2,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use cosmwasm_std::{ - entry_point, from_slice, to_binary, to_vec, Deps, DepsMut, Env, MessageInfo, QueryResponse, - Response, StdResult, Storage, + entry_point, from_json, to_json_binary, to_json_vec, Deps, DepsMut, Env, MessageInfo, + QueryResponse, Response, StdResult, Storage, }; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; @@ -57,11 +57,11 @@ fn write_queue_address(storage: &mut dyn Storage, addr: String) { let config = Config { queue_address: addr, }; - storage.set(CONFIG_KEY, &to_vec(&config).unwrap()); + storage.set(CONFIG_KEY, &to_json_vec(&config).unwrap()); } fn read_queue_address(storage: &dyn Storage) -> String { - let config: Config = from_slice(&storage.get(CONFIG_KEY).unwrap()).unwrap(); + let config: Config = from_json(storage.get(CONFIG_KEY).unwrap()).unwrap(); config.queue_address } @@ -96,11 +96,11 @@ fn handle_change_address(deps: DepsMut, address: String) -> StdResult #[entry_point] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Raw { key } => to_binary(&query_raw(deps, key)?), - QueryMsg::Count {} => to_binary(&query_count(deps, msg)?), - QueryMsg::Sum {} => to_binary(&query_sum(deps, msg)?), - QueryMsg::Reducer {} => to_binary(&query_reducer(deps, msg)?), - QueryMsg::List {} => to_binary(&query_list(deps, msg)?), + QueryMsg::Raw { key } => to_json_binary(&query_raw(deps, key)?), + QueryMsg::Count {} => to_json_binary(&query_count(deps, msg)?), + QueryMsg::Sum {} => to_json_binary(&query_sum(deps, msg)?), + QueryMsg::Reducer {} => to_json_binary(&query_reducer(deps, msg)?), + QueryMsg::List {} => to_json_binary(&query_list(deps, msg)?), } } @@ -110,7 +110,7 @@ fn query_raw(deps: Deps, key: u32) -> StdResult { match response { None => Ok(RawResponse { item: None }), Some(v) => { - let i: Item = from_slice(v.as_slice())?; + let i: Item = from_json(v.as_slice())?; Ok(RawResponse { item: Some(i.value), }) @@ -175,22 +175,22 @@ mod tests { contract_addr: _, msg, } => { - let q_msg: QueryMsg = from_slice(msg).unwrap(); + let q_msg: QueryMsg = from_json(msg).unwrap(); match q_msg { QueryMsg::Count {} => SystemResult::Ok(ContractResult::Ok( - to_binary(&CountResponse { count: 1 }).unwrap(), + to_json_binary(&CountResponse { count: 1 }).unwrap(), )), QueryMsg::Sum {} => SystemResult::Ok(ContractResult::Ok( - to_binary(&SumResponse { sum: 42 }).unwrap(), + to_json_binary(&SumResponse { sum: 42 }).unwrap(), )), QueryMsg::Reducer {} => SystemResult::Ok(ContractResult::Ok( - to_binary(&ReducerResponse { + to_json_binary(&ReducerResponse { counters: vec![(42, 0)], }) .unwrap(), )), QueryMsg::List {} => SystemResult::Ok(ContractResult::Ok( - to_binary(&ListResponse { + to_json_binary(&ListResponse { empty: vec![], early: vec![0], late: vec![], @@ -204,7 +204,7 @@ mod tests { contract_addr: _, key: _, } => SystemResult::Ok(ContractResult::Ok( - to_binary(&RawQueryResponse { value: 42 }).unwrap(), + to_json_binary(&RawQueryResponse { value: 42 }).unwrap(), )), _ => SystemResult::Err(SystemError::Unknown {}), }); @@ -214,7 +214,7 @@ mod tests { #[test] fn test_instantiate() { let deps = create_contract(); - let config: Config = from_slice(&deps.storage.get(CONFIG_KEY).unwrap()).unwrap(); + let config: Config = from_json(deps.storage.get(CONFIG_KEY).unwrap()).unwrap(); assert_eq!(config.queue_address, QUEUE_ADDRESS); } diff --git a/contracts/query-queue/tests/integration.rs b/contracts/query-queue/tests/integration.rs index 685a60120..28583a8d7 100644 --- a/contracts/query-queue/tests/integration.rs +++ b/contracts/query-queue/tests/integration.rs @@ -21,7 +21,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use cosmwasm_std::{ - from_binary, from_slice, to_binary, Binary, ContractResult, MessageInfo, Response, SystemError, + from_json, to_json_binary, Binary, ContractResult, MessageInfo, Response, SystemError, SystemResult, WasmQuery, }; use cosmwasm_vm::{ @@ -58,10 +58,10 @@ fn create_contract() -> (Instance, MessageInf addr: contract_addr.to_string(), }); }; - let q_msg: QueryMsg = from_slice(msg).unwrap(); + let q_msg: QueryMsg = from_json(msg).unwrap(); match q_msg { QueryMsg::Sum {} => SystemResult::Ok(ContractResult::Ok( - to_binary(&SumResponse { sum: 42 }).unwrap(), + to_json_binary(&SumResponse { sum: 42 }).unwrap(), )), _ => SystemResult::Err(SystemError::Unknown {}), } @@ -86,7 +86,7 @@ fn create_contract() -> (Instance, MessageInf fn instantiate_and_query() { let (mut instance, _) = create_contract(); let data = query(&mut instance, mock_env(), QueryMsg::Sum {}).unwrap(); - let res: SumResponse = from_binary(&data).unwrap(); + let res: SumResponse = from_json(data).unwrap(); assert_eq!(res.sum, 42); } @@ -168,7 +168,7 @@ fn create_integrated_query_contract() -> (Instance (Instance StdResult<()> { (last_key + 1).to_be_bytes() } }; - let new_value = to_vec(&Item { value })?; + let new_value = to_json_vec(&Item { value })?; storage.set(&new_key, &new_value); Ok(()) @@ -92,11 +92,11 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult StdResult { match msg { - QueryMsg::Count {} => to_binary(&query_count(deps)), - QueryMsg::Sum {} => to_binary(&query_sum(deps)?), - QueryMsg::Reducer {} => to_binary(&query_reducer(deps)?), - QueryMsg::List {} => to_binary(&query_list(deps)), - QueryMsg::OpenIterators { count } => to_binary(&query_open_iterators(deps, count)), + QueryMsg::Count {} => to_json_binary(&query_count(deps)), + QueryMsg::Sum {} => to_json_binary(&query_sum(deps)?), + QueryMsg::Reducer {} => to_json_binary(&query_reducer(deps)?), + QueryMsg::List {} => to_json_binary(&query_list(deps)), + QueryMsg::OpenIterators { count } => to_json_binary(&query_open_iterators(deps, count)), } } @@ -112,7 +112,7 @@ fn query_sum(deps: Deps) -> StdResult { let values: StdResult> = deps .storage .range_values(None, None, Order::Ascending) - .map(|v| from_slice(&v)) + .map(from_json) .collect(); let sum = values?.iter().fold(0, |s, v| s + v.value); Ok(SumResponse { sum }) @@ -124,7 +124,7 @@ fn query_reducer(deps: Deps) -> StdResult { for val in deps .storage .range_values(None, None, Order::Ascending) - .map(|v| from_slice::(&v)) + .map(|v| from_json::(&v)) { // this returns error on parse error let my_val = val?.value; @@ -134,7 +134,7 @@ fn query_reducer(deps: Deps) -> StdResult { .range_values(None, None, Order::Ascending) // get value. ignore parse errors, just count as 0 .map(|v| { - from_slice::(&v) + from_json::(&v) .map(|v| v.value) .expect("error in item") }) @@ -181,7 +181,7 @@ mod tests { use cosmwasm_std::testing::{ mock_dependencies_with_balance, mock_env, mock_info, MockApi, MockQuerier, MockStorage, }; - use cosmwasm_std::{coins, from_binary, OwnedDeps}; + use cosmwasm_std::{coins, from_json, OwnedDeps}; /// Instantiates a contract with no elements fn create_contract() -> (OwnedDeps, MessageInfo) { @@ -270,7 +270,7 @@ mod tests { // ensure we popped properly assert!(res.data.is_some()); let data = res.data.unwrap(); - let state: Item = from_slice(data.as_slice()).unwrap(); + let state: Item = from_json(data.as_slice()).unwrap(); assert_eq!(state.value, 25); assert_eq!(get_count(deps.as_ref()), 1); @@ -340,7 +340,7 @@ mod tests { let query_msg = QueryMsg::List {}; let ids: ListResponse = - from_binary(&query(deps.as_ref(), mock_env(), query_msg).unwrap()).unwrap(); + from_json(query(deps.as_ref(), mock_env(), query_msg).unwrap()).unwrap(); assert_eq!(ids.empty, Vec::::new()); assert_eq!(ids.early, vec![0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]); assert_eq!(ids.late, vec![0x20, 0x21, 0x22, 0x23, 0x24]); diff --git a/contracts/queue/tests/integration.rs b/contracts/queue/tests/integration.rs index 98a5b21f5..b0a86ae1d 100644 --- a/contracts/queue/tests/integration.rs +++ b/contracts/queue/tests/integration.rs @@ -17,7 +17,7 @@ //! }); //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) -use cosmwasm_std::{from_binary, from_slice, MessageInfo, Response}; +use cosmwasm_std::{from_json, MessageInfo, Response}; use cosmwasm_vm::{ testing::{ execute, instantiate, migrate, mock_env, mock_info, mock_instance_with_gas_limit, query, @@ -48,13 +48,13 @@ fn create_contract() -> (Instance, MessageInf fn get_count(deps: &mut Instance) -> u32 { let data = query(deps, mock_env(), QueryMsg::Count {}).unwrap(); - let res: CountResponse = from_binary(&data).unwrap(); + let res: CountResponse = from_json(data).unwrap(); res.count } fn get_sum(deps: &mut Instance) -> i32 { let data = query(deps, mock_env(), QueryMsg::Sum {}).unwrap(); - let res: SumResponse = from_binary(&data).unwrap(); + let res: SumResponse = from_json(data).unwrap(); res.sum } @@ -128,7 +128,7 @@ fn push_and_pop() { // ensure we popped properly assert!(res.data.is_some()); let data = res.data.unwrap(); - let item: Item = from_slice(data.as_slice()).unwrap(); + let item: Item = from_json(data).unwrap(); assert_eq!(item.value, 25); assert_eq!(get_count(&mut deps), 1); @@ -169,7 +169,7 @@ fn push_and_reduce() { assert_eq!(get_count(&mut deps), 4); assert_eq!(get_sum(&mut deps), 130); let data = query(&mut deps, mock_env(), QueryMsg::Reducer {}).unwrap(); - let counters = from_binary::(&data).unwrap().counters; + let counters = from_json::(&data).unwrap().counters; assert_eq!(counters, vec![(40, 85), (15, 125), (85, 0), (-10, 140)]); } @@ -223,7 +223,7 @@ fn query_list() { // since we count up to 0x20 in early, we get early and late both with data let query_msg = QueryMsg::List {}; - let ids: ListResponse = from_binary(&query(&mut deps, mock_env(), query_msg).unwrap()).unwrap(); + let ids: ListResponse = from_json(query(&mut deps, mock_env(), query_msg).unwrap()).unwrap(); assert_eq!(ids.empty, Vec::::new()); assert_eq!(ids.early, vec![0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]); assert_eq!(ids.late, vec![0x20, 0x21, 0x22, 0x23, 0x24]); diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock index ac573e8ab..4cebcec76 100644 --- a/contracts/reflect/Cargo.lock +++ b/contracts/reflect/Cargo.lock @@ -73,6 +73,12 @@ 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 = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -572,7 +591,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -915,6 +934,15 @@ 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.8.0" @@ -1262,6 +1290,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.103" @@ -1367,6 +1401,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1411,6 +1455,12 @@ 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 = "subtle" version = "2.5.0" @@ -1656,9 +1706,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1669,6 +1719,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1684,19 +1735,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1707,9 +1762,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1726,9 +1781,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1745,9 +1800,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1757,9 +1812,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1768,9 +1823,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1784,14 +1839,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/reflect/schema/raw/response_to_sub_msg_result.json b/contracts/reflect/schema/raw/response_to_sub_msg_result.json index e7a26e26f..d3b29ce66 100644 --- a/contracts/reflect/schema/raw/response_to_sub_msg_result.json +++ b/contracts/reflect/schema/raw/response_to_sub_msg_result.json @@ -40,7 +40,7 @@ "type": "string" }, "Event": { - "description": "A full [*Cosmos SDK* event].\n\nThis version uses string attributes (similar to [*Cosmos SDK* StringEvent]), which then get magically converted to bytes for Tendermint somewhere between the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK.\n\n[*Cosmos SDK* event]: https://docs.cosmos.network/main/core/events.html [*Cosmos SDK* StringEvent]: https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70", + "description": "A full [*Cosmos SDK* event].\n\nThis version uses string attributes (similar to [*Cosmos SDK* StringEvent]), which then get magically converted to bytes for Tendermint somewhere between the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK.\n\n[*Cosmos SDK* event]: https://docs.cosmos.network/main/learn/advanced/events [*Cosmos SDK* StringEvent]: https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70", "type": "object", "required": [ "attributes", @@ -48,7 +48,7 @@ ], "properties": { "attributes": { - "description": "The attributes to be included in the event.\n\nYou can learn more about these from [*Cosmos SDK* docs].\n\n[*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html", + "description": "The attributes to be included in the event.\n\nYou can learn more about these from [*Cosmos SDK* docs].\n\n[*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events", "type": "array", "items": { "$ref": "#/definitions/Attribute" diff --git a/contracts/reflect/schema/reflect.json b/contracts/reflect/schema/reflect.json index 169f5943d..5f1737f41 100644 --- a/contracts/reflect/schema/reflect.json +++ b/contracts/reflect/schema/reflect.json @@ -1911,7 +1911,7 @@ "type": "string" }, "Event": { - "description": "A full [*Cosmos SDK* event].\n\nThis version uses string attributes (similar to [*Cosmos SDK* StringEvent]), which then get magically converted to bytes for Tendermint somewhere between the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK.\n\n[*Cosmos SDK* event]: https://docs.cosmos.network/main/core/events.html [*Cosmos SDK* StringEvent]: https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70", + "description": "A full [*Cosmos SDK* event].\n\nThis version uses string attributes (similar to [*Cosmos SDK* StringEvent]), which then get magically converted to bytes for Tendermint somewhere between the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK.\n\n[*Cosmos SDK* event]: https://docs.cosmos.network/main/learn/advanced/events [*Cosmos SDK* StringEvent]: https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70", "type": "object", "required": [ "attributes", @@ -1919,7 +1919,7 @@ ], "properties": { "attributes": { - "description": "The attributes to be included in the event.\n\nYou can learn more about these from [*Cosmos SDK* docs].\n\n[*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html", + "description": "The attributes to be included in the event.\n\nYou can learn more about these from [*Cosmos SDK* docs].\n\n[*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events", "type": "array", "items": { "$ref": "#/definitions/Attribute" diff --git a/contracts/reflect/src/contract.rs b/contracts/reflect/src/contract.rs index 2213bd4b9..fbe9076ec 100644 --- a/contracts/reflect/src/contract.rs +++ b/contracts/reflect/src/contract.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ - entry_point, to_binary, to_vec, Binary, ContractResult, CosmosMsg, Deps, DepsMut, Env, - MessageInfo, QueryRequest, QueryResponse, Reply, Response, StdError, StdResult, SubMsg, + entry_point, to_json_binary, to_json_vec, Binary, ContractResult, CosmosMsg, Deps, DepsMut, + Env, MessageInfo, QueryRequest, QueryResponse, Reply, Response, StdError, StdResult, SubMsg, SystemResult, }; @@ -119,11 +119,11 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Owner {} => to_binary(&query_owner(deps)?), - QueryMsg::Capitalized { text } => to_binary(&query_capitalized(deps, text)?), - QueryMsg::Chain { request } => to_binary(&query_chain(deps, &request)?), - QueryMsg::Raw { contract, key } => to_binary(&query_raw(deps, contract, key)?), - QueryMsg::SubMsgResult { id } => to_binary(&query_subcall(deps, id)?), + QueryMsg::Owner {} => to_json_binary(&query_owner(deps)?), + QueryMsg::Capitalized { text } => to_json_binary(&query_capitalized(deps, text)?), + QueryMsg::Chain { request } => to_json_binary(&query_chain(deps, &request)?), + QueryMsg::Raw { contract, key } => to_json_binary(&query_raw(deps, contract, key)?), + QueryMsg::SubMsgResult { id } => to_json_binary(&query_subcall(deps, id)?), } } @@ -149,7 +149,7 @@ fn query_chain( deps: Deps, request: &QueryRequest, ) -> StdResult { - let raw = to_vec(request).map_err(|serialize_err| { + let raw = to_json_vec(request).map_err(|serialize_err| { StdError::generic_err(format!("Serializing QueryRequest: {serialize_err}")) })?; match deps.querier.raw_query(&raw) { @@ -176,8 +176,8 @@ mod tests { use crate::testing::mock_dependencies_with_custom_querier; use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ - coin, coins, from_binary, AllBalanceResponse, BankMsg, BankQuery, Binary, Event, - StakingMsg, StdError, SubMsgResponse, SubMsgResult, + coin, coins, from_json, AllBalanceResponse, BankMsg, BankQuery, Binary, Event, StakingMsg, + StdError, SubMsgResponse, SubMsgResult, }; #[test] @@ -365,7 +365,7 @@ mod tests { text: "demo one".to_string(), }; let response = query(deps.as_ref(), mock_env(), msg).unwrap(); - let value: CapitalizedResponse = from_binary(&response).unwrap(); + let value: CapitalizedResponse = from_json(response).unwrap(); assert_eq!(value.text, "DEMO ONE"); } @@ -381,8 +381,8 @@ mod tests { .into(), }; let response = query(deps.as_ref(), mock_env(), msg).unwrap(); - let outer: ChainResponse = from_binary(&response).unwrap(); - let inner: AllBalanceResponse = from_binary(&outer.data).unwrap(); + let outer: ChainResponse = from_json(response).unwrap(); + let inner: AllBalanceResponse = from_json(outer.data).unwrap(); assert_eq!(inner.amount, coins(123, "ucosm")); // with custom query @@ -390,8 +390,8 @@ mod tests { request: SpecialQuery::Ping {}.into(), }; let response = query(deps.as_ref(), mock_env(), msg).unwrap(); - let outer: ChainResponse = from_binary(&response).unwrap(); - let inner: SpecialResponse = from_binary(&outer.data).unwrap(); + let outer: ChainResponse = from_json(response).unwrap(); + let inner: SpecialResponse = from_json(outer.data).unwrap(); assert_eq!(inner.msg, "pong"); } @@ -452,7 +452,7 @@ mod tests { // query for the real id let raw = query(deps.as_ref(), mock_env(), QueryMsg::SubMsgResult { id }).unwrap(); - let qres: Reply = from_binary(&raw).unwrap(); + let qres: Reply = from_json(raw).unwrap(); assert_eq!(qres.id, id); let result = qres.result.unwrap(); assert_eq!(result.data, Some(data)); diff --git a/contracts/reflect/src/state.rs b/contracts/reflect/src/state.rs index 417c76315..2fc752b22 100644 --- a/contracts/reflect/src/state.rs +++ b/contracts/reflect/src/state.rs @@ -2,9 +2,9 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use cosmwasm_std::{ - from_slice, + from_json, storage_keys::{namespace_with_key, to_length_prefixed}, - to_vec, Addr, Reply, StdError, StdResult, Storage, + to_json_vec, Addr, Reply, StdError, StdResult, Storage, }; const CONFIG_KEY: &[u8] = b"config"; @@ -19,13 +19,13 @@ pub fn load_reply(storage: &dyn Storage, id: u64) -> StdResult { storage .get(&namespace_with_key(&[RESULT_PREFIX], &id.to_be_bytes())) .ok_or_else(|| StdError::not_found(format!("reply {id}"))) - .and_then(|v| from_slice(&v)) + .and_then(from_json) } pub fn save_reply(storage: &mut dyn Storage, id: u64, reply: &Reply) -> StdResult<()> { storage.set( &namespace_with_key(&[RESULT_PREFIX], &id.to_be_bytes()), - &to_vec(reply)?, + &to_json_vec(reply)?, ); Ok(()) } @@ -38,10 +38,10 @@ pub fn load_config(storage: &dyn Storage) -> StdResult { storage .get(&to_length_prefixed(CONFIG_KEY)) .ok_or_else(|| StdError::not_found("config")) - .and_then(|v| from_slice(&v)) + .and_then(from_json) } pub fn save_config(storage: &mut dyn Storage, item: &State) -> StdResult<()> { - storage.set(&to_length_prefixed(CONFIG_KEY), &to_vec(item)?); + storage.set(&to_length_prefixed(CONFIG_KEY), &to_json_vec(item)?); Ok(()) } diff --git a/contracts/reflect/src/testing.rs b/contracts/reflect/src/testing.rs index 8f6ce714b..f46dac69e 100644 --- a/contracts/reflect/src/testing.rs +++ b/contracts/reflect/src/testing.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use crate::msg::{SpecialQuery, SpecialResponse}; use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR}; -use cosmwasm_std::{to_binary, Binary, Coin, ContractResult, OwnedDeps, SystemResult}; +use cosmwasm_std::{to_json_binary, Binary, Coin, ContractResult, OwnedDeps, SystemResult}; /// A drop-in replacement for cosmwasm_std::testing::mock_dependencies /// this uses our CustomQuerier. @@ -26,18 +26,18 @@ pub fn custom_query_execute(query: &SpecialQuery) -> ContractResult { SpecialQuery::Ping {} => "pong".to_string(), SpecialQuery::Capitalized { text } => text.to_uppercase(), }; - to_binary(&SpecialResponse { msg }).into() + to_json_binary(&SpecialResponse { msg }).into() } #[cfg(test)] mod tests { use super::*; - use cosmwasm_std::{from_binary, QuerierWrapper, QueryRequest}; + use cosmwasm_std::{from_json, QuerierWrapper, QueryRequest}; #[test] fn custom_query_execute_ping() { let res = custom_query_execute(&SpecialQuery::Ping {}).unwrap(); - let response: SpecialResponse = from_binary(&res).unwrap(); + let response: SpecialResponse = from_json(res).unwrap(); assert_eq!(response.msg, "pong"); } @@ -47,7 +47,7 @@ mod tests { text: "fOObaR".to_string(), }) .unwrap(); - let response: SpecialResponse = from_binary(&res).unwrap(); + let response: SpecialResponse = from_json(res).unwrap(); assert_eq!(response.msg, "FOOBAR"); } diff --git a/contracts/reflect/tests/integration.rs b/contracts/reflect/tests/integration.rs index 4df7685dc..1333ad53b 100644 --- a/contracts/reflect/tests/integration.rs +++ b/contracts/reflect/tests/integration.rs @@ -18,9 +18,9 @@ //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) use cosmwasm_std::{ - coin, coins, from_binary, BankMsg, BankQuery, Binary, Coin, ContractResult, Event, - QueryRequest, Reply, Response, StakingMsg, SubMsg, SubMsgResponse, SubMsgResult, - SupplyResponse, SystemResult, + coin, coins, from_json, BankMsg, BankQuery, Binary, Coin, ContractResult, Event, QueryRequest, + Reply, Response, StakingMsg, SubMsg, SubMsgResponse, SubMsgResult, SupplyResponse, + SystemResult, }; use cosmwasm_vm::{ testing::{ @@ -83,7 +83,7 @@ fn proper_initialization() { // it worked, let's query the state let res = query(&mut deps, mock_env(), QueryMsg::Owner {}).unwrap(); - let value: OwnerResponse = from_binary(&res).unwrap(); + let value: OwnerResponse = from_json(res).unwrap(); assert_eq!("creator", value.owner.as_str()); } @@ -159,7 +159,7 @@ fn transfer() { // should change state assert_eq!(0, res.messages.len()); let res = query(&mut deps, mock_env(), QueryMsg::Owner {}).unwrap(); - let value: OwnerResponse = from_binary(&res).unwrap(); + let value: OwnerResponse = from_json(res).unwrap(); assert_eq!("friend", value.owner.as_str()); } @@ -203,8 +203,8 @@ fn supply_query() { ) .unwrap(); - let res: ChainResponse = from_binary(&res).unwrap(); - let res: SupplyResponse = from_binary(&res.data).unwrap(); + let res: ChainResponse = from_json(res).unwrap(); + let res: SupplyResponse = from_json(res.data).unwrap(); assert_eq!(res.amount, coin(25, "OSMO")); } @@ -225,7 +225,7 @@ fn dispatch_custom_query() { }, ) .unwrap(); - let value: CapitalizedResponse = from_binary(&res).unwrap(); + let value: CapitalizedResponse = from_json(res).unwrap(); assert_eq!(value.text, "DEMO ONE"); } @@ -282,7 +282,7 @@ fn reply_and_query() { // query for the real id let raw = query(&mut deps, mock_env(), QueryMsg::SubMsgResult { id }).unwrap(); - let qres: Reply = from_binary(&raw).unwrap(); + let qres: Reply = from_json(raw).unwrap(); assert_eq!(qres.id, id); let result = qres.result.unwrap(); assert_eq!(result.data, Some(data)); diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock index a5d1f152f..9b73aa565 100644 --- a/contracts/staking/Cargo.lock +++ b/contracts/staking/Cargo.lock @@ -73,6 +73,12 @@ 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 = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -578,7 +597,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -921,6 +940,15 @@ 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.8.0" @@ -1256,6 +1284,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1361,6 +1395,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1438,6 +1482,12 @@ dependencies = [ "snafu", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1683,9 +1733,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1696,6 +1746,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1711,19 +1762,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1734,9 +1789,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1753,9 +1808,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1772,9 +1827,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1784,9 +1839,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1795,9 +1850,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1811,14 +1866,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/staking/src/contract.rs b/contracts/staking/src/contract.rs index 19295f8ef..72b20818e 100644 --- a/contracts/staking/src/contract.rs +++ b/contracts/staking/src/contract.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - coin, entry_point, to_binary, BankMsg, Decimal, Deps, DepsMut, DistributionMsg, Env, + coin, entry_point, to_json_binary, BankMsg, Decimal, Deps, DepsMut, DistributionMsg, Env, MessageInfo, QuerierWrapper, QueryResponse, Response, StakingMsg, StdError, StdResult, Uint128, WasmMsg, }; @@ -289,7 +289,7 @@ pub fn claim(deps: DepsMut, env: Env, info: MessageInfo) -> StdResult pub fn reinvest(deps: DepsMut, env: Env, _info: MessageInfo) -> StdResult { let contract_addr = env.contract.address; let invest: InvestmentInfo = load_item(deps.storage, KEY_INVESTMENT)?; - let msg = to_binary(&ExecuteMsg::_BondAllTokens {})?; + let msg = to_json_binary(&ExecuteMsg::_BondAllTokens {})?; // and bond them to the validator let res = Response::new() @@ -349,10 +349,10 @@ pub fn _bond_all_tokens( #[entry_point] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::TokenInfo {} => to_binary(&query_token_info(deps)?), - QueryMsg::Investment {} => to_binary(&query_investment(deps)?), - QueryMsg::Balance { address } => to_binary(&query_balance(deps, &address)?), - QueryMsg::Claims { address } => to_binary(&query_claims(deps, &address)?), + QueryMsg::TokenInfo {} => to_json_binary(&query_token_info(deps)?), + QueryMsg::Investment {} => to_json_binary(&query_investment(deps)?), + QueryMsg::Balance { address } => to_json_binary(&query_balance(deps, &address)?), + QueryMsg::Claims { address } => to_json_binary(&query_claims(deps, &address)?), } } diff --git a/contracts/staking/src/state.rs b/contracts/staking/src/state.rs index 40e875db2..6af667021 100644 --- a/contracts/staking/src/state.rs +++ b/contracts/staking/src/state.rs @@ -4,9 +4,9 @@ use schemars::JsonSchema; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use cosmwasm_std::{ - from_slice, + from_json, storage_keys::{namespace_with_key, to_length_prefixed}, - to_vec, Addr, CanonicalAddr, Decimal, StdError, StdResult, Storage, Uint128, + to_json_vec, Addr, CanonicalAddr, Decimal, StdError, StdResult, Storage, Uint128, }; pub const KEY_INVESTMENT: &[u8] = b"invest"; @@ -23,7 +23,7 @@ pub fn may_load_map( ) -> StdResult> { storage .get(&namespace_with_key(&[prefix], key)) - .map(|v| from_slice(&v)) + .map(from_json) .transpose() } @@ -33,7 +33,7 @@ pub fn save_map( key: &CanonicalAddr, value: Uint128, ) -> StdResult<()> { - storage.set(&namespace_with_key(&[prefix], key), &to_vec(&value)?); + storage.set(&namespace_with_key(&[prefix], key), &to_json_vec(&value)?); Ok(()) } @@ -85,11 +85,11 @@ pub fn load_item(storage: &dyn Storage, key: &[u8]) -> StdR storage .get(&to_length_prefixed(key)) .ok_or_else(|| StdError::not_found(type_name::())) - .and_then(|v| from_slice(&v)) + .and_then(from_json) } pub fn save_item(storage: &mut dyn Storage, key: &[u8], item: &T) -> StdResult<()> { - storage.set(&to_length_prefixed(key), &to_vec(item)?); + storage.set(&to_length_prefixed(key), &to_json_vec(item)?); Ok(()) } diff --git a/contracts/staking/tests/integration.rs b/contracts/staking/tests/integration.rs index cec0aba12..cca65a9db 100644 --- a/contracts/staking/tests/integration.rs +++ b/contracts/staking/tests/integration.rs @@ -17,7 +17,7 @@ //! }); //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) -use cosmwasm_std::{coin, from_binary, ContractResult, Decimal, Response, Uint128, Validator}; +use cosmwasm_std::{coin, from_json, ContractResult, Decimal, Response, Uint128, Validator}; use cosmwasm_vm::testing::{ instantiate, mock_backend, mock_env, mock_info, mock_instance_options, query, }; @@ -106,7 +106,7 @@ fn proper_initialization() { // token info is proper let res = query(&mut deps, mock_env(), QueryMsg::TokenInfo {}).unwrap(); - let token: TokenInfoResponse = from_binary(&res).unwrap(); + let token: TokenInfoResponse = from_json(res).unwrap(); assert_eq!(&token.name, &msg.name); assert_eq!(&token.symbol, &msg.symbol); assert_eq!(token.decimals, msg.decimals); @@ -120,7 +120,7 @@ fn proper_initialization() { }, ) .unwrap(); - let bal: BalanceResponse = from_binary(&res).unwrap(); + let bal: BalanceResponse = from_json(res).unwrap(); assert_eq!(bal.balance, Uint128::new(0)); // no claims @@ -132,12 +132,12 @@ fn proper_initialization() { }, ) .unwrap(); - let claim: ClaimsResponse = from_binary(&res).unwrap(); + let claim: ClaimsResponse = from_json(res).unwrap(); assert_eq!(claim.claims, Uint128::new(0)); // investment info correct let res = query(&mut deps, mock_env(), QueryMsg::Investment {}).unwrap(); - let invest: InvestmentResponse = from_binary(&res).unwrap(); + let invest: InvestmentResponse = from_json(res).unwrap(); assert_eq!(&invest.owner, &creator); assert_eq!(&invest.validator, &msg.validator); assert_eq!(invest.exit_tax, msg.exit_tax); diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock index 591d75b54..1feb6d879 100644 --- a/contracts/virus/Cargo.lock +++ b/contracts/virus/Cargo.lock @@ -73,6 +73,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.2.1" @@ -128,7 +134,7 @@ checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -157,9 +163,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -191,6 +197,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -205,7 +212,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -225,7 +232,7 @@ version = "1.1.9+0.8.1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -233,6 +240,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -243,6 +251,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.3", + "static_assertions", "thiserror", "uuid", ] @@ -414,6 +423,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.8" @@ -461,9 +480,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.0" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -471,27 +490,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.0" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim", - "syn", + "syn 2.0.37", ] [[package]] name = "darling_macro" -version = "0.13.0" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.37", ] [[package]] @@ -525,7 +543,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -567,7 +585,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -578,7 +596,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.0", ] [[package]] @@ -652,28 +670,28 @@ checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "enumset" -version = "1.0.7" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e76129da36102af021b8e5000dab2c1c30dbef85c1e482beeff8da5dde0e0b0" +checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.5.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6451128aa6655d880755345d085494cf7561a6bee7c8dc821e5d77e6d267ecd4" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] @@ -932,6 +950,15 @@ 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.6.4" @@ -1041,7 +1068,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -1058,9 +1085,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -1082,14 +1109,14 @@ checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1212,7 +1239,7 @@ checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1248,7 +1275,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 1.0.109", ] [[package]] @@ -1277,6 +1304,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.126" @@ -1314,7 +1347,7 @@ checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1325,7 +1358,7 @@ checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1382,6 +1415,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1427,10 +1470,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "strsim" -version = "0.10.0" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "subtle" @@ -1449,6 +1492,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "target-lexicon" version = "0.12.7" @@ -1472,7 +1526,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1510,7 +1564,7 @@ checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1621,7 +1675,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -1645,7 +1699,7 @@ checksum = "c5020cfa87c7cecefef118055d44e3c1fc122c7ec25701d528ee458a0b45f38f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1666,7 +1720,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1679,9 +1733,9 @@ checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1692,6 +1746,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1707,19 +1762,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.0", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1730,9 +1789,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1749,9 +1808,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1768,21 +1827,21 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1791,9 +1850,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1807,14 +1866,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/virus/src/contract.rs b/contracts/virus/src/contract.rs index 202881b34..0a1235751 100644 --- a/contracts/virus/src/contract.rs +++ b/contracts/virus/src/contract.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, instantiate2_address, to_binary, Attribute, Binary, CodeInfoResponse, + entry_point, instantiate2_address, to_json_binary, Attribute, Binary, CodeInfoResponse, ContractInfoResponse, DepsMut, Env, MessageInfo, Response, StdResult, WasmMsg, }; @@ -72,7 +72,7 @@ pub fn execute_spread( admin: None, code_id, label, - msg: to_binary(&InstantiateMsg {})?, + msg: to_json_binary(&InstantiateMsg {})?, funds: vec![], salt, }); @@ -80,7 +80,7 @@ pub fn execute_spread( // we know the address of the newly instantiated contract, so let's execute it right away msgs.push(WasmMsg::Execute { contract_addr: address.into(), - msg: to_binary(&ExecuteMsg::Spread { + msg: to_json_binary(&ExecuteMsg::Spread { parent_path: path, levels: levels - 1, })?, diff --git a/contracts/voting-with-uuid/Cargo.lock b/contracts/voting-with-uuid/Cargo.lock index 92014a1a6..94480d729 100644 --- a/contracts/voting-with-uuid/Cargo.lock +++ b/contracts/voting-with-uuid/Cargo.lock @@ -73,6 +73,12 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -169,9 +175,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -203,6 +209,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.6", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -245,6 +252,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -255,6 +263,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.6", + "static_assertions", "thiserror", "uuid", ] @@ -426,14 +435,20 @@ dependencies = [ ] [[package]] -name = "crossbeam-utils" -version = "0.8.14" +name = "crossbeam-queue" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", + "crossbeam-utils", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + [[package]] name = "crypto-bigint" version = "0.5.3" @@ -485,9 +500,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -495,26 +510,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 1.0.105", + "syn 2.0.39", ] [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 1.0.105", + "syn 2.0.39", ] [[package]] @@ -601,7 +616,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.8", ] [[package]] @@ -680,23 +695,23 @@ dependencies = [ [[package]] name = "enumset" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.6.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.105", + "syn 2.0.39", ] [[package]] @@ -956,6 +971,15 @@ 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.7.1" @@ -1303,6 +1327,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" + [[package]] name = "serde" version = "1.0.150" @@ -1408,6 +1438,16 @@ dependencies = [ "digest 0.10.6", ] +[[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 = "signature" version = "2.1.0" @@ -1452,6 +1492,12 @@ 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 = "subtle" version = "2.4.1" @@ -1698,9 +1744,9 @@ checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1711,6 +1757,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1726,19 +1773,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.8", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1749,9 +1800,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1768,9 +1819,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1787,9 +1838,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1799,9 +1850,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1810,9 +1861,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1826,14 +1877,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/voting-with-uuid/src/contract.rs b/contracts/voting-with-uuid/src/contract.rs index 4a9205097..125646053 100644 --- a/contracts/voting-with-uuid/src/contract.rs +++ b/contracts/voting-with-uuid/src/contract.rs @@ -8,8 +8,8 @@ use crate::state::{ save_poll, Poll, PollStatus, State, TokenManager, Voter, }; use cosmwasm_std::{ - attr, coin, entry_point, new_uuid, to_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Deps, - DepsMut, Env, MessageInfo, Response, StdError, StdResult, Storage, Uint128, Uuid, + attr, coin, entry_point, new_uuid, to_json_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, + Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Storage, Uint128, Uuid, }; pub const VOTING_TOKEN: &str = "voting_token"; @@ -219,7 +219,7 @@ pub fn create_poll( ) .add_attribute("end_height", new_poll.end_height.to_string()) .add_attribute("start_height", start_height.unwrap_or(0).to_string()) - .set_data(to_binary(&CreatePollResponse { poll_id })?); + .set_data(to_json_binary(&CreatePollResponse { poll_id })?); Ok(r) } @@ -434,7 +434,7 @@ pub fn make_seq_id( #[entry_point] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Config {} => to_binary(&load_config(deps.storage)?), + QueryMsg::Config {} => to_json_binary(&load_config(deps.storage)?), QueryMsg::TokenStake { address } => { token_balance(deps, deps.api.addr_validate(address.as_str())?) } @@ -456,7 +456,7 @@ fn query_poll(deps: Deps, poll_id: Uuid) -> StdResult { start_height: poll.start_height, description: poll.description, }; - to_binary(&resp) + to_json_binary(&resp) } fn token_balance(deps: Deps, address: Addr) -> StdResult { @@ -466,5 +466,5 @@ fn token_balance(deps: Deps, address: Addr) -> StdResult { token_balance: token_manager.token_balance, }; - to_binary(&resp) + to_json_binary(&resp) } diff --git a/contracts/voting-with-uuid/src/state.rs b/contracts/voting-with-uuid/src/state.rs index 2519e3ea5..92a9a7645 100644 --- a/contracts/voting-with-uuid/src/state.rs +++ b/contracts/voting-with-uuid/src/state.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - from_slice, storage_keys::namespace_with_key, to_vec, Addr, StdError, StdResult, Storage, + from_json, storage_keys::namespace_with_key, to_json_vec, Addr, StdError, StdResult, Storage, Uint128, Uuid, }; use schemars::JsonSchema; @@ -52,7 +52,7 @@ pub struct Poll { } pub fn save_config(storage: &mut dyn Storage, item: &State) -> StdResult<()> { - storage.set(CONFIG_KEY, &to_vec(item)?); + storage.set(CONFIG_KEY, &to_json_vec(item)?); Ok(()) } @@ -60,13 +60,13 @@ pub fn load_config(storage: &dyn Storage) -> StdResult { storage .get(CONFIG_KEY) .ok_or_else(|| StdError::not_found("config")) - .and_then(|v| from_slice(&v)) + .and_then(from_json) } pub fn save_poll(storage: &mut dyn Storage, key: &Uuid, poll: &Poll) -> StdResult<()> { storage.set( &namespace_with_key(&[POLL_KEY], key.as_bytes()), - &to_vec(poll)?, + &to_json_vec(poll)?, ); Ok(()) } @@ -74,7 +74,7 @@ pub fn save_poll(storage: &mut dyn Storage, key: &Uuid, poll: &Poll) -> StdResul pub fn may_load_poll(storage: &dyn Storage, key: &Uuid) -> StdResult> { storage .get(&namespace_with_key(&[POLL_KEY], key.as_bytes())) - .map(|v| from_slice(&v)) + .map(from_json) .transpose() } @@ -89,7 +89,7 @@ pub fn save_bank( ) -> StdResult<()> { storage.set( &namespace_with_key(&[BANK_KEY], key.as_bytes()), - &to_vec(token_manager)?, + &to_json_vec(token_manager)?, ); Ok(()) } @@ -97,7 +97,7 @@ pub fn save_bank( pub fn may_load_bank(storage: &dyn Storage, key: &Addr) -> StdResult> { storage .get(&namespace_with_key(&[BANK_KEY], key.as_bytes())) - .map(|v| from_slice(&v)) + .map(from_json) .transpose() } diff --git a/contracts/voting-with-uuid/src/tests.rs b/contracts/voting-with-uuid/src/tests.rs index bd768bd27..5fc512e22 100644 --- a/contracts/voting-with-uuid/src/tests.rs +++ b/contracts/voting-with-uuid/src/tests.rs @@ -6,7 +6,7 @@ use cosmwasm_std::testing::{ mock_dependencies, mock_dependencies_with_balance, mock_env, mock_info, }; use cosmwasm_std::{ - attr, coins, from_binary, Addr, BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, + attr, coins, from_json, Addr, BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, StdError, Timestamp, Uint128, Uuid, }; use std::str::FromStr; @@ -201,7 +201,7 @@ fn fails_end_poll_before_end_height() { let poll_id = Uuid::from_str("849c1f99-e882-53e6-8e63-e5aa001359c2").unwrap(); let res = query(deps.as_ref(), mock_env(), QueryMsg::Poll { poll_id }).unwrap(); - let value: PollResponse = from_binary(&res).unwrap(); + let value: PollResponse = from_json(res).unwrap(); assert_eq!(Some(10001), value.end_height); let poll_id = Uuid::from_str("849c1f99-e882-53e6-8e63-e5aa001359c2").unwrap(); @@ -298,7 +298,7 @@ fn happy_days_end_poll() { ] ); let res = query(deps.as_ref(), mock_env(), QueryMsg::Poll { poll_id }).unwrap(); - let value: PollResponse = from_binary(&res).unwrap(); + let value: PollResponse = from_json(res).unwrap(); assert_eq!(PollStatus::Passed, value.status); } @@ -337,7 +337,7 @@ fn end_poll_zero_quorum() { ); let res = query(deps.as_ref(), env, QueryMsg::Poll { poll_id }).unwrap(); - let value: PollResponse = from_binary(&res).unwrap(); + let value: PollResponse = from_json(res).unwrap(); assert_eq!(PollStatus::Rejected, value.status); } @@ -415,7 +415,7 @@ fn end_poll_quorum_rejected() { ); let res = query(deps.as_ref(), mock_env(), QueryMsg::Poll { poll_id }).unwrap(); - let value: PollResponse = from_binary(&res).unwrap(); + let value: PollResponse = from_json(res).unwrap(); assert_eq!(PollStatus::Rejected, value.status); } @@ -492,7 +492,7 @@ fn end_poll_nay_rejected() { ); let res = query(deps.as_ref(), mock_env(), QueryMsg::Poll { poll_id }).unwrap(); - let value: PollResponse = from_binary(&res).unwrap(); + let value: PollResponse = from_json(res).unwrap(); assert_eq!(PollStatus::Rejected, value.status); } diff --git a/devtools/check_contracts_full.sh b/devtools/check_contracts_full.sh index 51fd97732..ef0a1c3f9 100755 --- a/devtools/check_contracts_full.sh +++ b/devtools/check_contracts_full.sh @@ -2,6 +2,7 @@ set -o errexit -o nounset -o pipefail command -v shellcheck >/dev/null && shellcheck "$0" +GLOBIGNORE="contracts/floaty/" for contract_dir in contracts/*/; do ( cd "$contract_dir" diff --git a/devtools/check_contracts_medium.sh b/devtools/check_contracts_medium.sh index 265b4d2f4..5e796eaef 100755 --- a/devtools/check_contracts_medium.sh +++ b/devtools/check_contracts_medium.sh @@ -2,6 +2,7 @@ set -o errexit -o nounset -o pipefail command -v shellcheck >/dev/null && shellcheck "$0" +GLOBIGNORE="contracts/floaty/" for contract_dir in contracts/*/; do ( cd "$contract_dir" diff --git a/packages/check/Cargo.toml b/packages/check/Cargo.toml index 6c4ad1608..0136fa7e7 100644 --- a/packages/check/Cargo.toml +++ b/packages/check/Cargo.toml @@ -15,5 +15,5 @@ cosmwasm-vm = { path = "../vm", version = "1.1.9+0.8.1" } cosmwasm-std = { path = "../std", version = "1.1.9+0.8.1" } [dev-dependencies] -assert_cmd = "=2.0.10" # 2.0.11+ requires Rust 1.65.0 which we currently don't want to make the minimum if possible +assert_cmd = "2.0.12" predicates = "3" diff --git a/packages/check/tests/cosmwasm_check_tests.rs b/packages/check/tests/cosmwasm_check_tests.rs index 0c4301d68..751ac2ae0 100644 --- a/packages/check/tests/cosmwasm_check_tests.rs +++ b/packages/check/tests/cosmwasm_check_tests.rs @@ -27,16 +27,13 @@ fn invalid_contract_check() -> Result<(), Box> { } #[test] -fn invalid_contract_check_float_operator() -> Result<(), Box> { +fn valid_contract_check_float_operator() -> Result<(), Box> { let mut cmd = Command::cargo_bin("cosmwasm-check")?; cmd.arg("../vm/testdata/floaty.wasm"); cmd.assert() - .failure() - .stdout(predicate::str::contains("Float operator detected")) - .stdout(predicate::str::contains( - "The use of floats is not supported", - )); + .success() + .stdout(predicate::str::contains("pass")); Ok(()) } diff --git a/packages/crypto/Cargo.toml b/packages/crypto/Cargo.toml index 270ea96c2..2ff89c004 100644 --- a/packages/crypto/Cargo.toml +++ b/packages/crypto/Cargo.toml @@ -25,6 +25,8 @@ digest = "0.10" rand_core = { version = "0.6", features = ["getrandom"] } thiserror = "1.0.38" sha-1 = "0.9.8" +# Not used directly, but needed to bump transitive dependency, see: https://github.com/CosmWasm/cosmwasm/pull/1899 for details. +ecdsa = "0.16.2" [dev-dependencies] criterion = "0.4" diff --git a/packages/go-gen/src/schema.rs b/packages/go-gen/src/schema.rs index 9d9a3fa86..496d0954c 100644 --- a/packages/go-gen/src/schema.rs +++ b/packages/go-gen/src/schema.rs @@ -88,18 +88,18 @@ pub fn schema_object_type( pub fn nullable_type(subschemas: &[Schema]) -> Result, anyhow::Error> { let (found_null, nullable_type): (bool, Option<&SchemaObject>) = subschemas .iter() - .fold(Ok((false, None)), |result: Result<_>, subschema| { - result.and_then(|(nullable, not_null)| { + .try_fold( + (false, None), + |(nullable, not_null), subschema| -> Result<_> { let subschema = subschema.object()?; if is_null(subschema) { Ok((true, not_null)) } else { Ok((nullable, Some(subschema))) } - }) - }) + }, + ) .context("failed to get anyOf subschemas")?; - Ok(if found_null { nullable_type } else { None }) } diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index 2f143b44c..13b4d8f11 100644 --- a/packages/std/Cargo.toml +++ b/packages/std/Cargo.toml @@ -9,7 +9,7 @@ license = "Apache-2.0" readme = "README.md" [package.metadata.docs.rs] -features = ["abort", "stargate", "staking", "ibc3", "cosmwasm_1_3"] +features = ["abort", "stargate", "staking", "ibc3", "cosmwasm_1_4"] [features] default = ["iterator", "abort"] @@ -57,19 +57,27 @@ forward_ref = "1" hex = "0.4" schemars = "0.8.3" sha2 = "0.10.3" -serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } +serde = { version = "1.0.103", default-features = false, features = [ + "derive", + "alloc", +] } serde-json-wasm = { version = "0.5.0" } thiserror = "1.0.26" bnum = "0.8.0" uuid = { version = "1.0.0", features = ["v5", "serde"] } +static_assertions = "1.1.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] +bech32 = "0.9.1" cosmwasm-crypto = { path = "../crypto", version = "1.1.9+0.8.1" } [dev-dependencies] cosmwasm-schema = { path = "../schema" } # The chrono dependency is only used in an example, which Rust compiles for us. If this causes trouble, remove it. -chrono = { version = "0.4", default-features = false, features = ["alloc", "std"] } +chrono = { version = "0.4", default-features = false, features = [ + "alloc", + "std", +] } crc32fast = "1.3.2" hex-literal = "0.3.1" serde_json = "1.0.81" diff --git a/packages/std/src/addresses.rs b/packages/std/src/addresses.rs index 39bc8bc06..f05702e4d 100644 --- a/packages/std/src/addresses.rs +++ b/packages/std/src/addresses.rs @@ -167,7 +167,7 @@ impl<'a> From<&'a Addr> for Cow<'a, Addr> { /// /// The safe way to obtain a valid `CanonicalAddr` is using `Api::addr_canonicalize`. In /// addition to that there are many unsafe ways to convert any binary data into an instance. -/// So the type shoud be treated as a marker to express the intended data type, not as +/// So the type should be treated as a marker to express the intended data type, not as /// a validity guarantee of any sort. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, JsonSchema)] pub struct CanonicalAddr(pub Binary); @@ -358,7 +358,7 @@ pub fn instantiate2_address( } /// The instantiate2 address derivation implementation. This API is used for -/// testing puposes only. The `msg` field is discouraged and should not be used. +/// testing purposes only. The `msg` field is discouraged and should not be used. /// Use [`instantiate2_address`]. #[doc(hidden)] fn instantiate2_address_impl( @@ -751,7 +751,7 @@ mod tests { } #[test] - fn instantiate2_address_impl_works_for_cosmjs_testvectors() { + fn instantiate2_address_impl_works_for_cosmjs_test_vectors() { // Test data from https://github.com/cosmos/cosmjs/pull/1253 const COSMOS_ED25519_TESTS_JSON: &str = "./testdata/instantiate2_addresses.json"; diff --git a/packages/std/src/binary.rs b/packages/std/src/binary.rs index ec5954aeb..bb65eea8d 100644 --- a/packages/std/src/binary.rs +++ b/packages/std/src/binary.rs @@ -253,7 +253,7 @@ mod tests { use super::*; use crate::assert_hash_works; use crate::errors::StdError; - use crate::serde::{from_slice, to_vec}; + use crate::serde::{from_json, to_json_vec}; #[test] fn to_array_works() { @@ -450,8 +450,8 @@ mod tests { fn serialization_works() { let binary = Binary(vec![0u8, 187, 61, 11, 250, 0]); - let json = to_vec(&binary).unwrap(); - let deserialized: Binary = from_slice(&json).unwrap(); + let json = to_json_vec(&binary).unwrap(); + let deserialized: Binary = from_json(json).unwrap(); assert_eq!(binary, deserialized); } @@ -462,16 +462,16 @@ mod tests { // this is the binary behind above string let expected = vec![0u8, 187, 61, 11, 250, 0]; - let serialized = to_vec(&b64_str).unwrap(); - let deserialized: Binary = from_slice(&serialized).unwrap(); + let serialized = to_json_vec(&b64_str).unwrap(); + let deserialized: Binary = from_json(serialized).unwrap(); assert_eq!(expected, deserialized.as_slice()); } #[test] fn deserialize_from_invalid_string() { let invalid_str = "**BAD!**"; - let serialized = to_vec(&invalid_str).unwrap(); - let res = from_slice::(&serialized); + let serialized = to_json_vec(&invalid_str).unwrap(); + let res = from_json::(&serialized); assert!(res.is_err()); } diff --git a/packages/std/src/errors/mod.rs b/packages/std/src/errors/mod.rs index 7bee00461..4f80de8f5 100644 --- a/packages/std/src/errors/mod.rs +++ b/packages/std/src/errors/mod.rs @@ -9,7 +9,8 @@ pub use recover_pubkey_error::RecoverPubkeyError; pub use std_error::{ CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError, CoinFromStrError, CoinsError, ConversionOverflowError, DivideByZeroError, DivisionError, - OverflowError, OverflowOperation, RoundUpOverflowError, StdError, StdResult, + OverflowError, OverflowOperation, RoundDownOverflowError, RoundUpOverflowError, StdError, + StdResult, }; pub use system_error::SystemError; pub use verification_error::VerificationError; diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index 9a9f2f58c..884a81bbc 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -635,6 +635,10 @@ pub enum CheckedFromRatioError { #[error("Round up operation failed because of overflow")] pub struct RoundUpOverflowError; +#[derive(Error, Debug, PartialEq, Eq)] +#[error("Round down operation failed because of overflow")] +pub struct RoundDownOverflowError; + #[derive(Error, Debug, PartialEq, Eq)] pub enum CoinsError { #[error("Duplicate denom")] @@ -920,7 +924,7 @@ mod tests { } #[test] - fn from_std_string_fromutf8error_works() { + fn from_std_string_from_utf8error_works() { let error: StdError = String::from_utf8(b"Hello \xF0\x90\x80World".to_vec()) .unwrap_err() .into(); diff --git a/packages/std/src/errors/system_error.rs b/packages/std/src/errors/system_error.rs index 98dc6f670..8d560c413 100644 --- a/packages/std/src/errors/system_error.rs +++ b/packages/std/src/errors/system_error.rs @@ -7,7 +7,7 @@ use crate::Binary; /// /// This is used on return values for Querier as a nested result: Result, SystemError> /// The first wrap (SystemError) will trigger if the contract address doesn't exist, -/// the QueryRequest is malformated, etc. The second wrap will be an error message from +/// the QueryRequest is malformed, etc. The second wrap will be an error message from /// the contract itself. /// /// Such errors are only created by the VM. The error type is defined in the standard library, to ensure @@ -69,7 +69,7 @@ impl core::fmt::Display for SystemError { #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, to_json_vec}; #[test] fn system_error_no_such_contract_serialization() { @@ -78,14 +78,14 @@ mod tests { }; // ser - let json = to_vec(&err).unwrap(); + let json = to_json_vec(&err).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"no_such_contract":{"addr":"gibtsnicht"}}"#, ); // de - let err: SystemError = from_slice(br#"{"no_such_contract":{"addr":"nada"}}"#).unwrap(); + let err: SystemError = from_json(br#"{"no_such_contract":{"addr":"nada"}}"#).unwrap(); assert_eq!( err, SystemError::NoSuchContract { @@ -99,14 +99,14 @@ mod tests { let err = SystemError::NoSuchCode { code_id: 13 }; // ser - let json = to_vec(&err).unwrap(); + let json = to_json_vec(&err).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"no_such_code":{"code_id":13}}"#, ); // de - let err: SystemError = from_slice(br#"{"no_such_code":{"code_id":987}}"#).unwrap(); + let err: SystemError = from_json(br#"{"no_such_code":{"code_id":987}}"#).unwrap(); assert_eq!(err, SystemError::NoSuchCode { code_id: 987 },); } } diff --git a/packages/std/src/exports.rs b/packages/std/src/exports.rs index 8f057712d..c745bc82e 100644 --- a/packages/std/src/exports.rs +++ b/packages/std/src/exports.rs @@ -25,7 +25,7 @@ use crate::memory::{alloc, consume_region, release_buffer, Region}; use crate::panic::install_panic_handler; use crate::query::CustomQuery; use crate::results::{ContractResult, QueryResponse, Reply, Response}; -use crate::serde::{from_slice, to_vec}; +use crate::serde::{from_json, to_json_vec}; use crate::types::Env; use crate::{CustomMsg, Deps, DepsMut, MessageInfo}; @@ -120,7 +120,7 @@ where info_ptr as *mut Region, msg_ptr as *mut Region, ); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -150,7 +150,7 @@ where info_ptr as *mut Region, msg_ptr as *mut Region, ); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -174,7 +174,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_migrate(migrate_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -198,7 +198,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_sudo(sudo_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -221,7 +221,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_reply(reply_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -243,7 +243,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_query(query_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -266,7 +266,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_channel_open(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -291,7 +291,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_channel_connect(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -316,7 +316,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_channel_close(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -342,7 +342,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_packet_receive(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -368,7 +368,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_packet_ack(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -395,7 +395,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_packet_timeout(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -415,9 +415,9 @@ where let info: Vec = unsafe { consume_region(info_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let info: MessageInfo = try_into_contract_result!(from_slice(&info)); - let msg: M = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let info: MessageInfo = try_into_contract_result!(from_json(info)); + let msg: M = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); instantiate_fn(deps.as_mut(), env, info, msg).into() @@ -439,9 +439,9 @@ where let info: Vec = unsafe { consume_region(info_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let info: MessageInfo = try_into_contract_result!(from_slice(&info)); - let msg: M = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let info: MessageInfo = try_into_contract_result!(from_json(info)); + let msg: M = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); execute_fn(deps.as_mut(), env, info, msg).into() @@ -461,8 +461,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: M = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: M = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); migrate_fn(deps.as_mut(), env, msg).into() @@ -482,8 +482,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: M = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: M = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); sudo_fn(deps.as_mut(), env, msg).into() @@ -502,8 +502,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: Reply = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: Reply = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); reply_fn(deps.as_mut(), env, msg).into() @@ -522,8 +522,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: M = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: M = try_into_contract_result!(from_json(msg)); let deps = make_dependencies(); query_fn(deps.as_ref(), env, msg).into() @@ -542,8 +542,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcChannelOpenMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcChannelOpenMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() @@ -563,8 +563,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcChannelConnectMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcChannelConnectMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() @@ -584,8 +584,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcChannelCloseMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcChannelCloseMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() @@ -605,8 +605,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcPacketReceiveMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcPacketReceiveMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() @@ -626,8 +626,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcPacketAckMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcPacketAckMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() @@ -647,8 +647,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcPacketTimeoutMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcPacketTimeoutMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() diff --git a/packages/std/src/hex_binary.rs b/packages/std/src/hex_binary.rs index 0def99b36..aeb30b4c4 100644 --- a/packages/std/src/hex_binary.rs +++ b/packages/std/src/hex_binary.rs @@ -247,7 +247,7 @@ impl<'de> de::Visitor<'de> for HexVisitor { mod tests { use super::*; - use crate::{assert_hash_works, from_slice, to_vec, StdError}; + use crate::{assert_hash_works, from_json, to_json_vec, StdError}; #[test] fn from_hex_works() { @@ -376,7 +376,7 @@ mod tests { } #[test] - fn from_slice_works() { + fn from_json_works() { let original: &[u8] = &[0u8, 187, 61, 11, 250, 0]; let binary: HexBinary = original.into(); assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]); @@ -515,8 +515,8 @@ mod tests { fn serialization_works() { let binary = HexBinary(vec![0u8, 187, 61, 11, 250, 0]); - let json = to_vec(&binary).unwrap(); - let deserialized: HexBinary = from_slice(&json).unwrap(); + let json = to_json_vec(&binary).unwrap(); + let deserialized: HexBinary = from_json(json).unwrap(); assert_eq!(binary, deserialized); } @@ -527,16 +527,16 @@ mod tests { // this is the binary behind above string let expected = vec![0u8, 187, 61, 11, 250, 0]; - let serialized = to_vec(&hex).unwrap(); - let deserialized: HexBinary = from_slice(&serialized).unwrap(); + let serialized = to_json_vec(&hex).unwrap(); + let deserialized: HexBinary = from_json(serialized).unwrap(); assert_eq!(expected, deserialized.as_slice()); } #[test] fn deserialize_from_invalid_string() { let invalid_str = "**BAD!**"; - let serialized = to_vec(&invalid_str).unwrap(); - let res = from_slice::(&serialized); + let serialized = to_json_vec(&invalid_str).unwrap(); + let res = from_json::(&serialized); assert!(res.is_err()); } diff --git a/packages/std/src/ibc.rs b/packages/std/src/ibc.rs index dab621b71..1ad33e366 100644 --- a/packages/std/src/ibc.rs +++ b/packages/std/src/ibc.rs @@ -11,7 +11,7 @@ use crate::binary::Binary; use crate::coin::Coin; use crate::errors::StdResult; use crate::results::{Attribute, CosmosMsg, Empty, Event, SubMsg}; -use crate::serde::to_binary; +use crate::serde::to_json_binary; use crate::timestamp::Timestamp; /// These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts @@ -240,7 +240,7 @@ impl IbcAcknowledgement { pub fn encode_json(data: &impl Serialize) -> StdResult { Ok(IbcAcknowledgement { - data: to_binary(data)?, + data: to_json_binary(data)?, }) } } @@ -492,14 +492,14 @@ pub struct IbcBasicResponse { /// /// More info about events (and their attributes) can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub attributes: Vec, /// Extra, custom events separate from the main `wasm` one. These will have /// `wasm-` prepended to the type. /// /// More info about events can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub events: Vec, } @@ -637,14 +637,14 @@ pub struct IbcReceiveResponse { /// /// More info about events (and their attributes) can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub attributes: Vec, /// Extra, custom events separate from the main `wasm` one. These will have /// `wasm-` prepended to the type. /// /// More info about events can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub events: Vec, } diff --git a/packages/std/src/imports.rs b/packages/std/src/imports.rs index 3e4e81b4b..a8772509a 100644 --- a/packages/std/src/imports.rs +++ b/packages/std/src/imports.rs @@ -10,7 +10,7 @@ use crate::results::SystemResult; #[cfg(feature = "iterator")] use crate::sections::decode_sections2; use crate::sections::encode_sections; -use crate::serde::from_slice; +use crate::serde::from_json; use crate::traits::{Api, Querier, QuerierResult, Storage}; #[cfg(feature = "iterator")] use crate::{ @@ -523,7 +523,7 @@ impl Querier for ExternalQuerier { let response_ptr = unsafe { query_chain(request_ptr) }; let response = unsafe { consume_region(response_ptr as *mut Region) }; - from_slice(&response).unwrap_or_else(|parsing_err| { + from_json(&response).unwrap_or_else(|parsing_err| { SystemResult::Err(SystemError::InvalidResponse { error: parsing_err.to_string(), response: response.into(), diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index e0589d8f7..d5abb95dd 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -62,7 +62,8 @@ pub use crate::ibc::{ pub use crate::iterator::{Order, Record}; pub use crate::math::{ Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int128, Int256, - Int512, Int64, Isqrt, Uint128, Uint256, Uint512, Uint64, + Int512, Int64, Isqrt, SignedDecimal, SignedDecimal256, SignedDecimal256RangeExceeded, + SignedDecimalRangeExceeded, Uint128, Uint256, Uint512, Uint64, }; pub use crate::metadata::{DenomMetadata, DenomUnit}; pub use crate::never::Never; @@ -89,7 +90,11 @@ pub use crate::results::{ pub use crate::results::{DistributionMsg, StakingMsg}; #[cfg(feature = "stargate")] pub use crate::results::{GovMsg, VoteOption}; -pub use crate::serde::{from_binary, from_slice, to_binary, to_vec}; +#[allow(deprecated)] +pub use crate::serde::{ + from_binary, from_json, from_slice, to_binary, to_json_binary, to_json_string, to_json_vec, + to_vec, +}; pub use crate::stdack::StdAck; pub use crate::storage::MemoryStorage; pub use crate::timestamp::Timestamp; diff --git a/packages/std/src/math/conversion.rs b/packages/std/src/math/conversion.rs new file mode 100644 index 000000000..240cbf865 --- /dev/null +++ b/packages/std/src/math/conversion.rs @@ -0,0 +1,390 @@ +/// Grows a big endian signed integer to a bigger size. +/// See +pub const fn grow_be_int( + input: [u8; INPUT_SIZE], +) -> [u8; OUTPUT_SIZE] { + debug_assert!(INPUT_SIZE <= OUTPUT_SIZE); + // check if sign bit is set + let mut output = if input[0] & 0b10000000 != 0 { + // negative number is filled up with 1s + [0b11111111u8; OUTPUT_SIZE] + } else { + [0u8; OUTPUT_SIZE] + }; + let mut i = 0; + + // copy input to the end of output + // copy_from_slice is not const, so we have to do this manually + while i < INPUT_SIZE { + output[OUTPUT_SIZE - INPUT_SIZE + i] = input[i]; + i += 1; + } + output +} + +/// Shrinks a big endian signed integer to a smaller size. +/// This is the opposite operation of sign extension. +pub fn shrink_be_int( + input: [u8; INPUT_SIZE], +) -> Option<[u8; OUTPUT_SIZE]> { + debug_assert!(INPUT_SIZE >= OUTPUT_SIZE); + + // check bounds + if input[0] & 0b10000000 != 0 { + // a negative number should start with only 1s, otherwise it's too small + for i in &input[0..(INPUT_SIZE - OUTPUT_SIZE)] { + if *i != 0b11111111u8 { + return None; + } + } + // the sign bit also has to be 1 + if input[INPUT_SIZE - OUTPUT_SIZE] & 0b10000000 == 0 { + return None; + } + } else { + // a positive number should start with only 0s, otherwise it's too large + for i in &input[0..(INPUT_SIZE - OUTPUT_SIZE)] { + if *i != 0u8 { + return None; + } + } + // the sign bit also has to be 0 + if input[INPUT_SIZE - OUTPUT_SIZE] & 0b10000000 != 0 { + return None; + } + } + + // Now, we can just copy the last bytes + let mut output = [0u8; OUTPUT_SIZE]; + output.copy_from_slice(&input[(INPUT_SIZE - OUTPUT_SIZE)..]); + Some(output) +} + +/// Helper macro to implement `TryFrom` for a type that is just a wrapper around another type. +/// This can be used for all our integer conversions where `bnum` implements `TryFrom`. +macro_rules! forward_try_from { + ($input: ty, $output: ty) => { + impl TryFrom<$input> for $output { + type Error = $crate::ConversionOverflowError; + + fn try_from(value: $input) -> Result { + value + .0 + .try_into() + .map(Self) + .map_err(|_| Self::Error::new(stringify!($input), stringify!($output), value)) + } + } + }; +} +pub(crate) use forward_try_from; + +/// Helper macro to implement `TryFrom` for a conversion from a bigger signed int to a smaller one. +/// This is needed because `bnum` does not implement `TryFrom` for those conversions +/// because of limitations of const generics. +macro_rules! try_from_int_to_int { + ($input: ty, $output: ty) => { + // statically assert that the input is bigger than the output + static_assertions::const_assert!( + core::mem::size_of::<$input>() > core::mem::size_of::<$output>() + ); + impl TryFrom<$input> for $output { + type Error = $crate::ConversionOverflowError; + + fn try_from(value: $input) -> Result { + $crate::math::conversion::shrink_be_int(value.to_be_bytes()) + .ok_or_else(|| Self::Error::new(stringify!($input), stringify!($output), value)) + .map(Self::from_be_bytes) + } + } + }; +} +pub(crate) use try_from_int_to_int; + +/// Helper macro to implement `TryFrom` for a conversion from a unsigned int to a smaller or +/// equal sized signed int. +/// This is needed because `bnum` does not implement `TryFrom` for all of those conversions. +macro_rules! try_from_uint_to_int { + ($input: ty, $output: ty) => { + // statically assert that... + // input is unsigned + static_assertions::const_assert_eq!(stringify!($input).as_bytes()[0], b'U'); + // output is signed + static_assertions::const_assert_eq!(stringify!($output).as_bytes()[0], b'I'); + // input is bigger than output (otherwise we would not need a `TryFrom` impl) + static_assertions::const_assert!( + core::mem::size_of::<$input>() >= core::mem::size_of::<$output>() + ); + + impl TryFrom<$input> for $output { + type Error = $crate::ConversionOverflowError; + + fn try_from(value: $input) -> Result { + use bnum::prelude::As; + // $input::MAX has to be bigger than $output::MAX, so we can just cast it + if value.0 > Self::MAX.0.as_() { + return Err(Self::Error::new( + stringify!($input), + stringify!($output), + value, + )); + } + + // at this point we know it fits + Ok(Self(value.0.as_())) + } + } + }; +} +pub(crate) use try_from_uint_to_int; + +#[cfg(test)] +pub(crate) fn test_try_from_uint_to_int(input_type: &'static str, output_type: &'static str) +where + I: super::num_consts::NumConsts + + From + + Copy + + TryFrom + + core::fmt::Debug + + core::ops::Add, + O: TryFrom + + From + + super::num_consts::NumConsts + + core::cmp::PartialEq + + core::fmt::Debug, + String: From, +{ + let v = I::MAX; + assert_eq!( + O::try_from(v), + Err(crate::ConversionOverflowError::new( + input_type, + output_type, + v + )), + "input::MAX value should not fit" + ); + + let max = I::try_from(O::MAX).unwrap(); + assert_eq!(O::try_from(max), Ok(O::MAX), "output::MAX value should fit"); + + // but $output::MAX + 1 should not fit + let v = max + I::ONE; + assert_eq!( + O::try_from(v), + Err(crate::ConversionOverflowError::new( + input_type, + output_type, + v + )), + "output::MAX + 1 should not fit" + ); + + // zero should work + let v = I::ZERO; + assert_eq!(O::try_from(v), Ok(O::ZERO), "zero should fit"); + + // 42 should work + assert_eq!( + O::try_from(I::from(42u32)), + Ok(O::from(42u32)), + "42 should fit" + ) +} + +#[cfg(test)] +pub(crate) fn test_try_from_int_to_uint(input_type: &'static str, output_type: &'static str) +where + I: super::num_consts::NumConsts + + From + + Copy + + TryFrom + + core::fmt::Debug + + core::ops::Add, + O: TryFrom + + From + + super::num_consts::NumConsts + + core::cmp::PartialEq + + core::fmt::Debug, + String: From, + >::Error: std::fmt::Debug, +{ + if core::mem::size_of::() <= core::mem::size_of::() { + // if the input type is smaller than the output type, then `I::MAX` should fit into `O` + let v = I::MAX; + assert_eq!( + O::try_from(v), + Ok(O::try_from(v).unwrap()), + "input::MAX value should fit" + ); + } else { + // if the input is bigger than the output, then `I::MAX` should not fit into `O` + let v = I::MAX; + assert_eq!( + O::try_from(v), + Err(crate::ConversionOverflowError::new( + input_type, + output_type, + v + )), + "input::MAX value should not fit" + ); + // but `O::MAX` should fit + let max = I::try_from(O::MAX).unwrap(); + assert_eq!( + O::try_from(max), + Ok(O::try_from(max).unwrap()), + "output::MAX value should fit" + ); + // while `O::MAX + 1` should not + let v = max + I::ONE; + assert_eq!( + O::try_from(v), + Err(crate::ConversionOverflowError::new( + input_type, + output_type, + v + )), + "output::MAX + 1 should not fit" + ); + } + + // negative numbers should fail + let v = I::from(-42i32); + assert_eq!( + O::try_from(v), + Err(crate::ConversionOverflowError::new( + input_type, + output_type, + v + )), + "negative numbers should not fit" + ); + + // zero should work + let v = I::ZERO; + assert_eq!(O::try_from(v), Ok(O::ZERO), "zero should fit"); + + // 42 should work + assert_eq!( + O::try_from(I::from(42i32)), + Ok(O::from(42u32)), + "42 should fit" + ) +} + +/// Helper macro to implement `TryFrom` for a conversion from a signed int to an unsigned int. +/// This is needed because `bnum` does not implement `TryFrom` for all of those conversions. +macro_rules! try_from_int_to_uint { + ($input: ty, $output: ty) => { + // statically assert that... + // input is signed + static_assertions::const_assert_eq!(stringify!($input).as_bytes()[0], b'I'); + // output is unsigned + static_assertions::const_assert_eq!(stringify!($output).as_bytes()[0], b'U'); + + impl TryFrom<$input> for $output { + type Error = ConversionOverflowError; + + fn try_from(value: $input) -> Result { + use bnum::prelude::As; + // if $input::MAX is smaller than $output::MAX, we only need to check the sign + if core::mem::size_of::<$input>() <= core::mem::size_of::<$output>() { + if value.is_negative() { + return Err(ConversionOverflowError::new( + stringify!($input), + stringify!($output), + value, + )); + } + + // otherwise we can just cast it + Ok(Self(value.0.as_())) + } else { + // $output::MAX is smaller than $input::MAX. + // If it is negative or too big, we error. + // We can safely cast $output::MAX to $input size + if value.is_negative() || value.0 > <$output>::MAX.0.as_() { + return Err(ConversionOverflowError::new( + stringify!($input), + stringify!($output), + value, + )); + } + + // at this point we know it fits + Ok(Self(value.0.as_())) + } + } + } + }; +} +pub(crate) use try_from_int_to_uint; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn grow_be_int_works() { + // test against rust std's integers + let i32s = [i32::MIN, -1, 0, 1, 42, i32::MAX]; + for i in i32s { + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i64).to_be_bytes()); + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i128).to_be_bytes()); + } + let i8s = [i8::MIN, -1, 0, 1, 42, i8::MAX]; + for i in i8s { + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i16).to_be_bytes()); + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i32).to_be_bytes()); + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i64).to_be_bytes()); + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i128).to_be_bytes()); + } + } + + #[test] + fn shrink_be_int_works() { + // test against rust std's integers + let i32s = [-42, -1, 0i32, 1, 42]; + for i in i32s { + assert_eq!( + shrink_be_int(i.to_be_bytes()), + Some((i as i16).to_be_bytes()) + ); + assert_eq!( + shrink_be_int(i.to_be_bytes()), + Some((i as i8).to_be_bytes()) + ); + } + // these should be too big to fit into an i16 or i8 + let oob = [ + i32::MIN, + i32::MIN + 10, + i32::MIN + 1234, + i32::MAX - 1234, + i32::MAX - 10, + i32::MAX, + ]; + for i in oob { + // 32 -> 16 bit + assert_eq!(shrink_be_int::<4, 2>(i.to_be_bytes()), None); + // 32 -> 8 bit + assert_eq!(shrink_be_int::<4, 1>(i.to_be_bytes()), None); + } + + // compare against whole i16 range + for i in i16::MIN..=i16::MAX { + let cast = i as i8 as i16; + if i == cast { + // if the cast is lossless, `shrink_be_int` should get the same result + assert_eq!( + shrink_be_int::<2, 1>(i.to_be_bytes()), + Some((i as i8).to_be_bytes()) + ); + } else { + // otherwise, we should get None + assert_eq!(shrink_be_int::<2, 1>(i.to_be_bytes()), None); + } + } + } +} diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index b8de3e90b..12837bc21 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -11,7 +11,7 @@ use crate::errors::{ CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, OverflowOperation, RoundUpOverflowError, StdError, }; -use crate::{forward_ref_partial_eq, Decimal256}; +use crate::{forward_ref_partial_eq, Decimal256, SignedDecimal, SignedDecimal256}; use super::Fraction; use super::Isqrt; @@ -289,7 +289,7 @@ impl Decimal { .try_into() .map(Self) .map_err(|_| OverflowError { - operation: crate::OverflowOperation::Mul, + operation: OverflowOperation::Mul, operand1: self.to_string(), operand2: other.to_string(), }) @@ -331,7 +331,7 @@ impl Decimal { } inner(self, exp).map_err(|_| OverflowError { - operation: crate::OverflowOperation::Pow, + operation: OverflowOperation::Pow, operand1: self.to_string(), operand2: exp.to_string(), }) @@ -435,7 +435,7 @@ impl Decimal { /// let d = Decimal::from_str("75.0").unwrap(); /// assert_eq!(d.to_uint_floor(), Uint128::new(75)); /// ``` - #[must_use] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_uint_floor(self) -> Uint128 { self.0 / Self::DECIMAL_FRACTIONAL } @@ -458,7 +458,7 @@ impl Decimal { /// let d = Decimal::from_str("75.0").unwrap(); /// assert_eq!(d.to_uint_ceil(), Uint128::new(75)); /// ``` - #[must_use] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_uint_ceil(self) -> Uint128 { // Using `q = 1 + ((x - 1) / y); // if x != 0` with unsigned integers x, y, q // from https://stackoverflow.com/a/2745086/2013738. We know `x + y` CAN overflow. @@ -510,6 +510,30 @@ impl TryFrom for Decimal { } } +impl TryFrom for Decimal { + type Error = DecimalRangeExceeded; + + fn try_from(value: SignedDecimal) -> Result { + value + .atomics() + .try_into() + .map(Decimal) + .map_err(|_| DecimalRangeExceeded) + } +} + +impl TryFrom for Decimal { + type Error = DecimalRangeExceeded; + + fn try_from(value: SignedDecimal256) -> Result { + value + .atomics() + .try_into() + .map(Decimal) + .map_err(|_| DecimalRangeExceeded) + } +} + impl FromStr for Decimal { type Err = StdError; @@ -781,7 +805,7 @@ impl<'de> de::Visitor<'de> for DecimalVisitor { #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, to_json_vec}; fn dec(input: &str) -> Decimal { Decimal::from_str(input).unwrap() @@ -845,6 +869,34 @@ mod tests { ); } + #[test] + fn decimal_try_from_signed_works() { + assert_eq!( + Decimal::try_from(SignedDecimal::MAX).unwrap(), + Decimal::raw(SignedDecimal::MAX.atomics().i128() as u128) + ); + assert_eq!( + Decimal::try_from(SignedDecimal::zero()).unwrap(), + Decimal::zero() + ); + assert_eq!( + Decimal::try_from(SignedDecimal::one()).unwrap(), + Decimal::one() + ); + assert_eq!( + Decimal::try_from(SignedDecimal::percent(50)).unwrap(), + Decimal::percent(50) + ); + assert_eq!( + Decimal::try_from(SignedDecimal::negative_one()), + Err(DecimalRangeExceeded) + ); + assert_eq!( + Decimal::try_from(SignedDecimal::MIN), + Err(DecimalRangeExceeded) + ); + } + #[test] fn decimal_from_atomics_works() { let one = Decimal::one(); @@ -1069,7 +1121,7 @@ mod tests { } #[test] - fn decimal_from_str_errors_for_broken_fractinal_part() { + fn decimal_from_str_errors_for_broken_fractional_part() { match Decimal::from_str("1.").unwrap_err() { StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), e => panic!("Unexpected error: {e:?}"), @@ -1485,7 +1537,7 @@ mod tests { assert_eq!( Decimal::MAX.checked_mul(Decimal::percent(200)), Err(OverflowError { - operation: crate::OverflowOperation::Mul, + operation: OverflowOperation::Mul, operand1: Decimal::MAX.to_string(), operand2: Decimal::percent(200).to_string(), }) @@ -1727,7 +1779,7 @@ mod tests { assert_eq!(Decimal::one().checked_pow(exp).unwrap(), Decimal::one()); } - // This case is mathematically undefined but we ensure consistency with Rust stdandard types + // This case is mathematically undefined but we ensure consistency with Rust standard types // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=20df6716048e77087acd40194b233494 assert_eq!(Decimal::zero().checked_pow(0).unwrap(), Decimal::one()); @@ -1799,7 +1851,7 @@ mod tests { assert_eq!( Decimal::MAX.checked_pow(2), Err(OverflowError { - operation: crate::OverflowOperation::Pow, + operation: OverflowOperation::Pow, operand1: Decimal::MAX.to_string(), operand2: "2".to_string(), }) @@ -1908,35 +1960,35 @@ mod tests { #[test] fn decimal_serialize() { - assert_eq!(to_vec(&Decimal::zero()).unwrap(), br#""0""#); - assert_eq!(to_vec(&Decimal::one()).unwrap(), br#""1""#); - assert_eq!(to_vec(&Decimal::percent(8)).unwrap(), br#""0.08""#); - assert_eq!(to_vec(&Decimal::percent(87)).unwrap(), br#""0.87""#); - assert_eq!(to_vec(&Decimal::percent(876)).unwrap(), br#""8.76""#); - assert_eq!(to_vec(&Decimal::percent(8765)).unwrap(), br#""87.65""#); + assert_eq!(to_json_vec(&Decimal::zero()).unwrap(), br#""0""#); + assert_eq!(to_json_vec(&Decimal::one()).unwrap(), br#""1""#); + assert_eq!(to_json_vec(&Decimal::percent(8)).unwrap(), br#""0.08""#); + assert_eq!(to_json_vec(&Decimal::percent(87)).unwrap(), br#""0.87""#); + assert_eq!(to_json_vec(&Decimal::percent(876)).unwrap(), br#""8.76""#); + assert_eq!(to_json_vec(&Decimal::percent(8765)).unwrap(), br#""87.65""#); } #[test] fn decimal_deserialize() { - assert_eq!(from_slice::(br#""0""#).unwrap(), Decimal::zero()); - assert_eq!(from_slice::(br#""1""#).unwrap(), Decimal::one()); - assert_eq!(from_slice::(br#""000""#).unwrap(), Decimal::zero()); - assert_eq!(from_slice::(br#""001""#).unwrap(), Decimal::one()); + assert_eq!(from_json::(br#""0""#).unwrap(), Decimal::zero()); + assert_eq!(from_json::(br#""1""#).unwrap(), Decimal::one()); + assert_eq!(from_json::(br#""000""#).unwrap(), Decimal::zero()); + assert_eq!(from_json::(br#""001""#).unwrap(), Decimal::one()); assert_eq!( - from_slice::(br#""0.08""#).unwrap(), + from_json::(br#""0.08""#).unwrap(), Decimal::percent(8) ); assert_eq!( - from_slice::(br#""0.87""#).unwrap(), + from_json::(br#""0.87""#).unwrap(), Decimal::percent(87) ); assert_eq!( - from_slice::(br#""8.76""#).unwrap(), + from_json::(br#""8.76""#).unwrap(), Decimal::percent(876) ); assert_eq!( - from_slice::(br#""87.65""#).unwrap(), + from_json::(br#""87.65""#).unwrap(), Decimal::percent(8765) ); } diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index f3fea968c..f0db608d2 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -11,7 +11,7 @@ use crate::errors::{ CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, OverflowOperation, RoundUpOverflowError, StdError, }; -use crate::{forward_ref_partial_eq, Decimal, Uint512}; +use crate::{forward_ref_partial_eq, Decimal, SignedDecimal, SignedDecimal256, Uint512}; use super::Fraction; use super::Isqrt; @@ -33,15 +33,9 @@ pub struct Decimal256RangeExceeded; impl Decimal256 { const DECIMAL_FRACTIONAL: Uint256 = // 1*10**18 - Uint256::from_be_bytes([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 224, 182, - 179, 167, 100, 0, 0, - ]); + Uint256::from_u128(1_000_000_000_000_000_000); const DECIMAL_FRACTIONAL_SQUARED: Uint256 = // 1*10**36 - Uint256::from_be_bytes([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 151, 206, 123, 201, 7, 21, 179, - 75, 159, 16, 0, 0, 0, 0, - ]); + Uint256::from_u128(1_000_000_000_000_000_000_000_000_000_000_000_000); /// The number of decimal places. Since decimal types are fixed-point rather than /// floating-point, this is a constant. @@ -304,7 +298,7 @@ impl Decimal256 { .try_into() .map(Self) .map_err(|_| OverflowError { - operation: crate::OverflowOperation::Mul, + operation: OverflowOperation::Mul, operand1: self.to_string(), operand2: other.to_string(), }) @@ -346,7 +340,7 @@ impl Decimal256 { } inner(self, exp).map_err(|_| OverflowError { - operation: crate::OverflowOperation::Pow, + operation: OverflowOperation::Pow, operand1: self.to_string(), operand2: exp.to_string(), }) @@ -454,7 +448,7 @@ impl Decimal256 { /// let d = Decimal256::from_str("75.0").unwrap(); /// assert_eq!(d.to_uint_floor(), Uint256::from(75u64)); /// ``` - #[must_use] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_uint_floor(self) -> Uint256 { self.0 / Self::DECIMAL_FRACTIONAL } @@ -477,7 +471,7 @@ impl Decimal256 { /// let d = Decimal256::from_str("75.0").unwrap(); /// assert_eq!(d.to_uint_ceil(), Uint256::from(75u64)); /// ``` - #[must_use] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_uint_ceil(self) -> Uint256 { // Using `q = 1 + ((x - 1) / y); // if x != 0` with unsigned integers x, y, q // from https://stackoverflow.com/a/2745086/2013738. We know `x + y` CAN overflow. @@ -525,6 +519,30 @@ impl From for Decimal256 { } } +impl TryFrom for Decimal256 { + type Error = Decimal256RangeExceeded; + + fn try_from(value: SignedDecimal) -> Result { + value + .atomics() + .try_into() + .map(Decimal256) + .map_err(|_| Decimal256RangeExceeded) + } +} + +impl TryFrom for Decimal256 { + type Error = Decimal256RangeExceeded; + + fn try_from(value: SignedDecimal256) -> Result { + value + .atomics() + .try_into() + .map(Decimal256) + .map_err(|_| Decimal256RangeExceeded) + } +} + impl FromStr for Decimal256 { type Err = StdError; @@ -797,7 +815,7 @@ impl<'de> de::Visitor<'de> for Decimal256Visitor { mod tests { use super::*; use crate::errors::StdError; - use crate::{from_slice, to_vec}; + use crate::{from_json, to_json_vec}; fn dec(input: &str) -> Decimal256 { Decimal256::from_str(input).unwrap() @@ -1145,7 +1163,7 @@ mod tests { } #[test] - fn decimal256_from_str_errors_for_broken_fractinal_part() { + fn decimal256_from_str_errors_for_broken_fractional_part() { match Decimal256::from_str("1.").unwrap_err() { StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), e => panic!("Unexpected error: {e:?}"), @@ -1581,7 +1599,7 @@ mod tests { assert_eq!( Decimal256::MAX.checked_mul(Decimal256::percent(200)), Err(OverflowError { - operation: crate::OverflowOperation::Mul, + operation: OverflowOperation::Mul, operand1: Decimal256::MAX.to_string(), operand2: Decimal256::percent(200).to_string(), }) @@ -1830,7 +1848,7 @@ mod tests { ); } - // This case is mathematically undefined but we ensure consistency with Rust stdandard types + // This case is mathematically undefined but we ensure consistency with Rust standard types // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=20df6716048e77087acd40194b233494 assert_eq!( Decimal256::zero().checked_pow(0).unwrap(), @@ -1908,7 +1926,7 @@ mod tests { assert_eq!( Decimal256::MAX.checked_pow(2), Err(OverflowError { - operation: crate::OverflowOperation::Pow, + operation: OverflowOperation::Pow, operand1: Decimal256::MAX.to_string(), operand2: "2".to_string(), }) @@ -2020,47 +2038,53 @@ mod tests { #[test] fn decimal256_serialize() { - assert_eq!(to_vec(&Decimal256::zero()).unwrap(), br#""0""#); - assert_eq!(to_vec(&Decimal256::one()).unwrap(), br#""1""#); - assert_eq!(to_vec(&Decimal256::percent(8)).unwrap(), br#""0.08""#); - assert_eq!(to_vec(&Decimal256::percent(87)).unwrap(), br#""0.87""#); - assert_eq!(to_vec(&Decimal256::percent(876)).unwrap(), br#""8.76""#); - assert_eq!(to_vec(&Decimal256::percent(8765)).unwrap(), br#""87.65""#); + assert_eq!(to_json_vec(&Decimal256::zero()).unwrap(), br#""0""#); + assert_eq!(to_json_vec(&Decimal256::one()).unwrap(), br#""1""#); + assert_eq!(to_json_vec(&Decimal256::percent(8)).unwrap(), br#""0.08""#); + assert_eq!(to_json_vec(&Decimal256::percent(87)).unwrap(), br#""0.87""#); + assert_eq!( + to_json_vec(&Decimal256::percent(876)).unwrap(), + br#""8.76""# + ); + assert_eq!( + to_json_vec(&Decimal256::percent(8765)).unwrap(), + br#""87.65""# + ); } #[test] fn decimal256_deserialize() { assert_eq!( - from_slice::(br#""0""#).unwrap(), + from_json::(br#""0""#).unwrap(), Decimal256::zero() ); assert_eq!( - from_slice::(br#""1""#).unwrap(), + from_json::(br#""1""#).unwrap(), Decimal256::one() ); assert_eq!( - from_slice::(br#""000""#).unwrap(), + from_json::(br#""000""#).unwrap(), Decimal256::zero() ); assert_eq!( - from_slice::(br#""001""#).unwrap(), + from_json::(br#""001""#).unwrap(), Decimal256::one() ); assert_eq!( - from_slice::(br#""0.08""#).unwrap(), + from_json::(br#""0.08""#).unwrap(), Decimal256::percent(8) ); assert_eq!( - from_slice::(br#""0.87""#).unwrap(), + from_json::(br#""0.87""#).unwrap(), Decimal256::percent(87) ); assert_eq!( - from_slice::(br#""8.76""#).unwrap(), + from_json::(br#""8.76""#).unwrap(), Decimal256::percent(876) ); assert_eq!( - from_slice::(br#""87.65""#).unwrap(), + from_json::(br#""87.65""#).unwrap(), Decimal256::percent(8765) ); } diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index cc0b7c83b..bc5cd241f 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -9,7 +9,13 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Int64, Uint128, Uint64}; +use crate::{ + forward_ref_partial_eq, CheckedMultiplyRatioError, Int256, Int512, Int64, Uint128, Uint256, + Uint512, Uint64, +}; + +use super::conversion::{forward_try_from, try_from_int_to_int}; +use super::num_consts::NumConsts; /// An implementation of i128 that is using strings for JSON encoding/decoding, /// such that the full i128 range can be used for clients that convert JSON numbers to floats, @@ -25,7 +31,7 @@ use crate::{forward_ref_partial_eq, Int64, Uint128, Uint64}; /// assert_eq!(a.i128(), 258); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Int128(#[schemars(with = "String")] i128); +pub struct Int128(#[schemars(with = "String")] pub(crate) i128); forward_ref_partial_eq!(Int128, Int128); @@ -85,11 +91,55 @@ impl Int128 { self.0 == 0 } + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.is_negative() + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { Self(self.0.pow(exp)) } + /// Returns `self * numerator / denominator`. + /// + /// Due to the nature of the integer division involved, the result is always floored. + /// E.g. 5 * 99/100 = 4. + pub fn checked_multiply_ratio, B: Into>( + &self, + numerator: A, + denominator: B, + ) -> Result { + let numerator = numerator.into(); + let denominator = denominator.into(); + if denominator.is_zero() { + return Err(CheckedMultiplyRatioError::DivideByZero); + } + match (self.full_mul(numerator) / Int256::from(denominator)).try_into() { + Ok(ratio) => Ok(ratio), + Err(_) => Err(CheckedMultiplyRatioError::Overflow), + } + } + + /// Multiplies two [`Int128`] values without overflow, producing an + /// [`Int256`]. + /// + /// # Examples + /// + /// ``` + /// use cosmwasm_std::Int128; + /// + /// let a = Int128::MAX; + /// let result = a.full_mul(2i32); + /// assert_eq!(result.to_string(), "340282366920938463463374607431768211454"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn full_mul(self, rhs: impl Into) -> Int256 { + Int256::from(self.i128()) + .checked_mul(Int256::from(rhs.into())) + .unwrap() + } + pub fn checked_add(self, other: Self) -> Result { self.0 .checked_add(other.0) @@ -209,14 +259,36 @@ impl Int128 { pub const fn abs_diff(self, other: Self) -> Uint128 { Uint128(self.0.abs_diff(other.0)) } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs(self) -> Self { + Self(self.0.abs()) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn unsigned_abs(self) -> Uint128 { + Uint128(self.0.unsigned_abs()) + } } +impl NumConsts for Int128 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + +// Uint to Int impl From for Int128 { fn from(val: Uint64) -> Self { val.u64().into() } } +forward_try_from!(Uint128, Int128); +forward_try_from!(Uint256, Int128); +forward_try_from!(Uint512, Int128); +// uint to Int impl From for Int128 { fn from(val: u64) -> Self { Int128(val.into()) @@ -241,12 +313,17 @@ impl From for Int128 { } } +// Int to Int impl From for Int128 { fn from(val: Int64) -> Self { val.i64().into() } } +try_from_int_to_int!(Int256, Int128); +try_from_int_to_int!(Int512, Int128); + +// int to Int impl From for Int128 { fn from(val: i128) -> Self { Int128(val) @@ -493,7 +570,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, math::conversion::test_try_from_uint_to_int, to_json_vec}; #[test] fn size_of_works() { @@ -608,6 +685,13 @@ mod tests { assert!(result.is_err()); } + #[test] + fn int128_try_from_unsigned_works() { + test_try_from_uint_to_int::("Uint128", "Int128"); + test_try_from_uint_to_int::("Uint256", "Int128"); + test_try_from_uint_to_int::("Uint512", "Int128"); + } + #[test] fn int128_implements_display() { let a = Int128::from(12345u32); @@ -694,6 +778,16 @@ mod tests { assert!(!Int128::from(-123i32).is_zero()); } + #[test] + fn int128_is_negative_works() { + assert!(Int128::MIN.is_negative()); + assert!(Int128::from(-123i32).is_negative()); + + assert!(!Int128::MAX.is_negative()); + assert!(!Int128::zero().is_negative()); + assert!(!Int128::from(123u32).is_negative()); + } + #[test] fn int128_wrapping_methods() { // wrapping_add @@ -728,9 +822,9 @@ mod tests { #[test] fn int128_json() { let orig = Int128::from(1234567890987654321i128); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Int128 = from_slice(&serialized).unwrap(); + let parsed: Int128 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } @@ -863,6 +957,65 @@ mod tests { _ = Int128::MAX.pow(2u32); } + #[test] + fn int128_checked_multiply_ratio_works() { + let base = Int128(500); + + // factor 1/1 + assert_eq!(base.checked_multiply_ratio(1i128, 1i128).unwrap(), base); + assert_eq!(base.checked_multiply_ratio(3i128, 3i128).unwrap(), base); + assert_eq!( + base.checked_multiply_ratio(654321i128, 654321i128).unwrap(), + base + ); + assert_eq!( + base.checked_multiply_ratio(i128::MAX, i128::MAX).unwrap(), + base + ); + + // factor 3/2 + assert_eq!( + base.checked_multiply_ratio(3i128, 2i128).unwrap(), + Int128(750) + ); + assert_eq!( + base.checked_multiply_ratio(333333i128, 222222i128).unwrap(), + Int128(750) + ); + + // factor 2/3 (integer devision always floors the result) + assert_eq!( + base.checked_multiply_ratio(2i128, 3i128).unwrap(), + Int128(333) + ); + assert_eq!( + base.checked_multiply_ratio(222222i128, 333333i128).unwrap(), + Int128(333) + ); + + // factor 5/6 (integer devision always floors the result) + assert_eq!( + base.checked_multiply_ratio(5i128, 6i128).unwrap(), + Int128(416) + ); + assert_eq!( + base.checked_multiply_ratio(100i128, 120i128).unwrap(), + Int128(416) + ); + } + + #[test] + fn int128_checked_multiply_ratio_does_not_panic() { + assert_eq!( + Int128(500i128).checked_multiply_ratio(1i128, 0i128), + Err(CheckedMultiplyRatioError::DivideByZero), + ); + assert_eq!( + Int128(500i128).checked_multiply_ratio(i128::MAX, 1i128), + Err(CheckedMultiplyRatioError::Overflow), + ); + } + #[test] fn int128_shr_works() { let original = Int128::from_be_bytes([ @@ -1088,6 +1241,37 @@ mod tests { assert_eq!(c.abs_diff(b), Uint128::from(10u32)); } + #[test] + fn int128_abs_works() { + let a = Int128::from(42i32); + assert_eq!(a.abs(), a); + + let b = Int128::from(-42i32); + assert_eq!(b.abs(), a); + + assert_eq!(Int128::zero().abs(), Int128::zero()); + assert_eq!((Int128::MIN + Int128::one()).abs(), Int128::MAX); + } + + #[test] + fn int128_unsigned_abs_works() { + assert_eq!(Int128::zero().unsigned_abs(), Uint128::zero()); + assert_eq!(Int128::one().unsigned_abs(), Uint128::one()); + assert_eq!( + Int128::MIN.unsigned_abs(), + Uint128::new(Int128::MAX.0 as u128) + Uint128::one() + ); + + let v = Int128::from(-42i32); + assert_eq!(v.unsigned_abs(), v.abs_diff(Int128::zero())); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int128_abs_min_panics() { + _ = Int128::MIN.abs(); + } + #[test] #[should_panic = "attempt to negate with overflow"] fn int128_neg_min_panics() { diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs index 5616fcc2e..70d17417d 100644 --- a/packages/std/src/math/int256.rs +++ b/packages/std/src/math/int256.rs @@ -9,12 +9,18 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Int128, Int64, Uint128, Uint256, Uint64}; +use crate::{ + forward_ref_partial_eq, CheckedMultiplyRatioError, Int128, Int512, Int64, Uint128, Uint256, + Uint512, Uint64, +}; /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. use bnum::types::{I256, U256}; +use super::conversion::{grow_be_int, try_from_int_to_int, try_from_uint_to_int}; +use super::num_consts::NumConsts; + /// An implementation of i256 that is using strings for JSON encoding/decoding, /// such that the full i256 range can be used for clients that convert JSON numbers to floats, /// like JavaScript and jq. @@ -36,7 +42,7 @@ use bnum::types::{I256, U256}; /// assert_eq!(a, b); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Int256(#[schemars(with = "String")] I256); +pub struct Int256(#[schemars(with = "String")] pub(crate) I256); forward_ref_partial_eq!(Int256, Int256); @@ -63,6 +69,12 @@ impl Int256 { Self(I256::ONE) } + /// A conversion from `i128` that, unlike the one provided by the `From` trait, + /// can be used in a `const` context. + pub const fn from_i128(v: i128) -> Self { + Self::from_be_bytes(grow_be_int(v.to_be_bytes())) + } + #[must_use] pub const fn from_be_bytes(data: [u8; 32]) -> Self { let words: [u64; 4] = [ @@ -134,11 +146,58 @@ impl Int256 { self.0.is_zero() } + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.is_negative() + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { Self(self.0.pow(exp)) } + /// Returns `self * numerator / denominator`. + /// + /// Due to the nature of the integer division involved, the result is always floored. + /// E.g. 5 * 99/100 = 4. + pub fn checked_multiply_ratio, B: Into>( + &self, + numerator: A, + denominator: B, + ) -> Result { + let numerator = numerator.into(); + let denominator = denominator.into(); + if denominator.is_zero() { + return Err(CheckedMultiplyRatioError::DivideByZero); + } + match (self.full_mul(numerator) / Int512::from(denominator)).try_into() { + Ok(ratio) => Ok(ratio), + Err(_) => Err(CheckedMultiplyRatioError::Overflow), + } + } + + /// Multiplies two [`Int256`] values without overflow, producing an + /// [`Int512`]. + /// + /// # Examples + /// + /// ``` + /// use cosmwasm_std::Int256; + /// + /// let a = Int256::MAX; + /// let result = a.full_mul(2i32); + /// assert_eq!( + /// result.to_string(), + /// "115792089237316195423570985008687907853269984665640564039457584007913129639934" + /// ); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn full_mul(self, rhs: impl Into) -> Int512 { + Int512::from(self) + .checked_mul(Int512::from(rhs.into())) + .unwrap() + } + pub fn checked_add(self, other: Self) -> Result { self.0 .checked_add(other.0) @@ -258,8 +317,29 @@ impl Int256 { pub const fn abs_diff(self, other: Self) -> Uint256 { Uint256(self.0.abs_diff(other.0)) } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs(self) -> Self { + Self(self.0.abs()) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn unsigned_abs(self) -> Uint256 { + Uint256(self.0.unsigned_abs()) + } +} + +impl NumConsts for Int256 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; } +// Uint to Int +try_from_uint_to_int!(Uint512, Int256); +try_from_uint_to_int!(Uint256, Int256); + impl From for Int256 { fn from(val: Uint128) -> Self { val.u128().into() @@ -272,6 +352,7 @@ impl From for Int256 { } } +// uint to Int impl From for Int256 { fn from(val: u128) -> Self { Int256(val.into()) @@ -302,6 +383,9 @@ impl From for Int256 { } } +// Int to Int +try_from_int_to_int!(Int512, Int256); + impl From for Int256 { fn from(val: Int128) -> Self { val.i128().into() @@ -314,6 +398,7 @@ impl From for Int256 { } } +// int to Int impl From for Int256 { fn from(val: i128) -> Self { Int256(val.into()) @@ -560,7 +645,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, math::conversion::test_try_from_uint_to_int, to_json_vec}; #[test] fn size_of_works() { @@ -671,6 +756,31 @@ mod tests { assert!(result.is_err()); } + #[test] + fn int256_try_from_unsigned_works() { + test_try_from_uint_to_int::("Uint256", "Int256"); + test_try_from_uint_to_int::("Uint512", "Int256"); + } + + #[test] + fn int256_from_i128() { + assert_eq!(Int256::from_i128(123i128), Int256::from_str("123").unwrap()); + + assert_eq!( + Int256::from_i128(9785746283745i128), + Int256::from_str("9785746283745").unwrap() + ); + + assert_eq!( + Int256::from_i128(i128::MAX).to_string(), + i128::MAX.to_string() + ); + assert_eq!( + Int256::from_i128(i128::MIN).to_string(), + i128::MIN.to_string() + ); + } + #[test] fn int256_implements_display() { let a = Int256::from(12345u32); @@ -775,6 +885,16 @@ mod tests { assert!(!Int256::from(-123i32).is_zero()); } + #[test] + fn int256_is_negative_works() { + assert!(Int256::MIN.is_negative()); + assert!(Int256::from(-123i32).is_negative()); + + assert!(!Int256::MAX.is_negative()); + assert!(!Int256::zero().is_negative()); + assert!(!Int256::from(123u32).is_negative()); + } + #[test] fn int256_wrapping_methods() { // wrapping_add @@ -809,9 +929,9 @@ mod tests { #[test] fn int256_json() { let orig = Int256::from(1234567890987654321u128); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Int256 = from_slice(&serialized).unwrap(); + let parsed: Int256 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } @@ -944,6 +1064,65 @@ mod tests { _ = Int256::MAX.pow(2u32); } + #[test] + fn int256_checked_multiply_ratio_works() { + let base = Int256::from_i128(500); + + // factor 1/1 + assert_eq!(base.checked_multiply_ratio(1i128, 1i128).unwrap(), base); + assert_eq!(base.checked_multiply_ratio(3i128, 3i128).unwrap(), base); + assert_eq!( + base.checked_multiply_ratio(654321i128, 654321i128).unwrap(), + base + ); + assert_eq!( + base.checked_multiply_ratio(i128::MAX, i128::MAX).unwrap(), + base + ); + + // factor 3/2 + assert_eq!( + base.checked_multiply_ratio(3i128, 2i128).unwrap(), + Int256::from_i128(750) + ); + assert_eq!( + base.checked_multiply_ratio(333333i128, 222222i128).unwrap(), + Int256::from_i128(750) + ); + + // factor 2/3 (integer devision always floors the result) + assert_eq!( + base.checked_multiply_ratio(2i128, 3i128).unwrap(), + Int256::from_i128(333) + ); + assert_eq!( + base.checked_multiply_ratio(222222i128, 333333i128).unwrap(), + Int256::from_i128(333) + ); + + // factor 5/6 (integer devision always floors the result) + assert_eq!( + base.checked_multiply_ratio(5i128, 6i128).unwrap(), + Int256::from_i128(416) + ); + assert_eq!( + base.checked_multiply_ratio(100i128, 120i128).unwrap(), + Int256::from_i128(416) + ); + } + + #[test] + fn int256_checked_multiply_ratio_does_not_panic() { + assert_eq!( + Int256::from_i128(500i128).checked_multiply_ratio(1i128, 0i128), + Err(CheckedMultiplyRatioError::DivideByZero), + ); + assert_eq!( + Int256::MAX.checked_multiply_ratio(Int256::MAX, 1i128), + Err(CheckedMultiplyRatioError::Overflow), + ); + } + #[test] fn int256_shr_works() { let original = Int256::new([ @@ -1171,6 +1350,37 @@ mod tests { assert_eq!(c.abs_diff(b), Uint256::from(10u32)); } + #[test] + fn int256_abs_works() { + let a = Int256::from(42i32); + assert_eq!(a.abs(), a); + + let b = Int256::from(-42i32); + assert_eq!(b.abs(), a); + + assert_eq!(Int256::zero().abs(), Int256::zero()); + assert_eq!((Int256::MIN + Int256::one()).abs(), Int256::MAX); + } + + #[test] + fn int256_unsigned_abs_works() { + assert_eq!(Int256::zero().unsigned_abs(), Uint256::zero()); + assert_eq!(Int256::one().unsigned_abs(), Uint256::one()); + assert_eq!( + Int256::MIN.unsigned_abs(), + Uint256::from_be_bytes(Int256::MAX.to_be_bytes()) + Uint256::one() + ); + + let v = Int256::from(-42i32); + assert_eq!(v.unsigned_abs(), v.abs_diff(Int256::zero())); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int256_abs_min_panics() { + _ = Int256::MIN.abs(); + } + #[test] #[should_panic = "attempt to negate with overflow"] fn int256_neg_min_panics() { diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 2b122418e..86b4a93b3 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -9,12 +9,15 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint512, Uint64}; +use crate::{forward_ref_partial_eq, Int128, Int256, Int64, Uint128, Uint256, Uint512, Uint64}; /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. use bnum::types::{I512, U512}; +use super::conversion::{grow_be_int, try_from_uint_to_int}; +use super::num_consts::NumConsts; + /// An implementation of i512 that is using strings for JSON encoding/decoding, /// such that the full i512 range can be used for clients that convert JSON numbers to floats, /// like JavaScript and jq. @@ -40,7 +43,7 @@ use bnum::types::{I512, U512}; /// assert_eq!(a, b); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Int512(#[schemars(with = "String")] I512); +pub struct Int512(#[schemars(with = "String")] pub(crate) I512); forward_ref_partial_eq!(Int512, Int512); @@ -170,6 +173,11 @@ impl Int512 { self.0.is_zero() } + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.is_negative() + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { Self(self.0.pow(exp)) @@ -294,8 +302,28 @@ impl Int512 { pub const fn abs_diff(self, other: Self) -> Uint512 { Uint512(self.0.abs_diff(other.0)) } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs(self) -> Self { + Self(self.0.abs()) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn unsigned_abs(self) -> Uint512 { + Uint512(self.0.unsigned_abs()) + } } +impl NumConsts for Int512 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + +// Uint to Int +try_from_uint_to_int!(Uint512, Int512); + impl From for Int512 { fn from(val: Uint256) -> Self { let mut bytes = [0u8; 64]; @@ -317,6 +345,7 @@ impl From for Int512 { } } +// uint to Int impl From for Int512 { fn from(val: u128) -> Self { Int512(val.into()) @@ -347,6 +376,7 @@ impl From for Int512 { } } +// int to Int impl From for Int512 { fn from(val: i128) -> Self { Int512(val.into()) @@ -377,6 +407,25 @@ impl From for Int512 { } } +// Int to Int +impl From for Int512 { + fn from(val: Int64) -> Self { + Int512(val.i64().into()) + } +} + +impl From for Int512 { + fn from(val: Int128) -> Self { + Int512(val.i128().into()) + } +} + +impl From for Int512 { + fn from(val: Int256) -> Self { + Self::from_be_bytes(grow_be_int(val.to_be_bytes())) + } +} + impl TryFrom<&str> for Int512 { type Error = StdError; @@ -593,7 +642,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, math::conversion::test_try_from_uint_to_int, to_json_vec}; #[test] fn size_of_works() { @@ -700,6 +749,40 @@ mod tests { let a = Int512::from(-5i8); assert_eq!(a.0, I512::from(-5i32)); + // other big signed integers + let values = [ + Int64::MAX, + Int64::MIN, + Int64::one(), + -Int64::one(), + Int64::zero(), + ]; + for v in values { + assert_eq!(Int512::from(v).to_string(), v.to_string()); + } + + let values = [ + Int128::MAX, + Int128::MIN, + Int128::one(), + -Int128::one(), + Int128::zero(), + ]; + for v in values { + assert_eq!(Int512::from(v).to_string(), v.to_string()); + } + + let values = [ + Int256::MAX, + Int256::MIN, + Int256::one(), + -Int256::one(), + Int256::zero(), + ]; + for v in values { + assert_eq!(Int512::from(v).to_string(), v.to_string()); + } + let result = Int512::try_from("34567"); assert_eq!( result.unwrap().0, @@ -710,6 +793,12 @@ mod tests { assert!(result.is_err()); } + #[test] + fn int512_try_from_unsigned_works() { + test_try_from_uint_to_int::("Uint256", "Int256"); + test_try_from_uint_to_int::("Uint512", "Int256"); + } + #[test] fn int512_implements_display() { let a = Int512::from(12345u32); @@ -824,6 +913,16 @@ mod tests { assert!(!Int512::from(-123i32).is_zero()); } + #[test] + fn int512_is_negative_works() { + assert!(Int512::MIN.is_negative()); + assert!(Int512::from(-123i32).is_negative()); + + assert!(!Int512::MAX.is_negative()); + assert!(!Int512::zero().is_negative()); + assert!(!Int512::from(123u32).is_negative()); + } + #[test] fn int512_wrapping_methods() { // wrapping_add @@ -858,9 +957,9 @@ mod tests { #[test] fn int512_json() { let orig = Int512::from(1234567890987654321u128); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Int512 = from_slice(&serialized).unwrap(); + let parsed: Int512 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } @@ -1224,6 +1323,37 @@ mod tests { assert_eq!(c.abs_diff(b), Uint512::from(10u32)); } + #[test] + fn int512_abs_works() { + let a = Int512::from(42i32); + assert_eq!(a.abs(), a); + + let b = Int512::from(-42i32); + assert_eq!(b.abs(), a); + + assert_eq!(Int512::zero().abs(), Int512::zero()); + assert_eq!((Int512::MIN + Int512::one()).abs(), Int512::MAX); + } + + #[test] + fn int512_unsigned_abs_works() { + assert_eq!(Int512::zero().unsigned_abs(), Uint512::zero()); + assert_eq!(Int512::one().unsigned_abs(), Uint512::one()); + assert_eq!( + Int512::MIN.unsigned_abs(), + Uint512::from_be_bytes(Int512::MAX.to_be_bytes()) + Uint512::one() + ); + + let v = Int512::from(-42i32); + assert_eq!(v.unsigned_abs(), v.abs_diff(Int512::zero())); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int512_abs_min_panics() { + _ = Int512::MIN.abs(); + } + #[test] #[should_panic = "attempt to negate with overflow"] fn int512_neg_min_panics() { diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 2e9ac7f68..86a9bc477 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -9,7 +9,13 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Uint64}; +use crate::{ + forward_ref_partial_eq, CheckedMultiplyRatioError, Int128, Int256, Int512, Uint128, Uint256, + Uint512, Uint64, +}; + +use super::conversion::{forward_try_from, try_from_int_to_int}; +use super::num_consts::NumConsts; /// An implementation of i64 that is using strings for JSON encoding/decoding, /// such that the full i64 range can be used for clients that convert JSON numbers to floats, @@ -25,7 +31,7 @@ use crate::{forward_ref_partial_eq, Uint64}; /// assert_eq!(a.i64(), 258); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Int64(#[schemars(with = "String")] i64); +pub struct Int64(#[schemars(with = "String")] pub(crate) i64); forward_ref_partial_eq!(Int64, Int64); @@ -85,11 +91,55 @@ impl Int64 { self.0 == 0 } + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.is_negative() + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { Self(self.0.pow(exp)) } + /// Returns `self * numerator / denominator`. + /// + /// Due to the nature of the integer division involved, the result is always floored. + /// E.g. 5 * 99/100 = 4. + pub fn checked_multiply_ratio, B: Into>( + &self, + numerator: A, + denominator: B, + ) -> Result { + let numerator = numerator.into(); + let denominator = denominator.into(); + if denominator.is_zero() { + return Err(CheckedMultiplyRatioError::DivideByZero); + } + match (self.full_mul(numerator) / Int128::from(denominator)).try_into() { + Ok(ratio) => Ok(ratio), + Err(_) => Err(CheckedMultiplyRatioError::Overflow), + } + } + + /// Multiplies two [`Int64`] values without overflow, producing an + /// [`Int128`]. + /// + /// # Examples + /// + /// ``` + /// use cosmwasm_std::Int64; + /// + /// let a = Int64::MAX; + /// let result = a.full_mul(2i32); + /// assert_eq!(result.to_string(), "18446744073709551614"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn full_mul(self, rhs: impl Into) -> Int128 { + Int128::from(self) + .checked_mul(Int128::from(rhs.into())) + .unwrap() + } + pub fn checked_add(self, other: Self) -> Result { self.0 .checked_add(other.0) @@ -209,8 +259,26 @@ impl Int64 { pub const fn abs_diff(self, other: Self) -> Uint64 { Uint64(self.0.abs_diff(other.0)) } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs(self) -> Self { + Self(self.0.abs()) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn unsigned_abs(self) -> Uint64 { + Uint64(self.0.unsigned_abs()) + } } +impl NumConsts for Int64 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + +// uint to Int impl From for Int64 { fn from(val: u32) -> Self { Int64(val.into()) @@ -229,6 +297,7 @@ impl From for Int64 { } } +// int to Int impl From for Int64 { fn from(val: i64) -> Self { Int64(val) @@ -253,6 +322,17 @@ impl From for Int64 { } } +// Int to Int +try_from_int_to_int!(Int128, Int64); +try_from_int_to_int!(Int256, Int64); +try_from_int_to_int!(Int512, Int64); + +// Uint to Int +forward_try_from!(Uint64, Int64); +forward_try_from!(Uint128, Int64); +forward_try_from!(Uint256, Int64); +forward_try_from!(Uint512, Int64); + impl TryFrom<&str> for Int64 { type Error = StdError; @@ -469,7 +549,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, math::conversion::test_try_from_uint_to_int, to_json_vec}; #[test] fn size_of_works() { @@ -578,6 +658,14 @@ mod tests { assert!(result.is_err()); } + #[test] + fn int64_try_from_unsigned_works() { + test_try_from_uint_to_int::("Uint64", "Int64"); + test_try_from_uint_to_int::("Uint128", "Int64"); + test_try_from_uint_to_int::("Uint256", "Int64"); + test_try_from_uint_to_int::("Uint512", "Int64"); + } + #[test] fn int64_implements_display() { let a = Int64::from(12345u32); @@ -662,6 +750,16 @@ mod tests { assert!(!Int64::from(-123i32).is_zero()); } + #[test] + fn int64_is_negative_works() { + assert!(Int64::MIN.is_negative()); + assert!(Int64::from(-123i32).is_negative()); + + assert!(!Int64::MAX.is_negative()); + assert!(!Int64::zero().is_negative()); + assert!(!Int64::from(123u32).is_negative()); + } + #[test] fn int64_wrapping_methods() { // wrapping_add @@ -696,9 +794,9 @@ mod tests { #[test] fn int64_json() { let orig = Int64::from(1234567890987654321i64); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Int64 = from_slice(&serialized).unwrap(); + let parsed: Int64 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } @@ -831,6 +929,56 @@ mod tests { _ = Int64::MAX.pow(2u32); } + #[test] + fn int64_checked_multiply_ratio_works() { + let base = Int64(500); + + // factor 1/1 + assert_eq!(base.checked_multiply_ratio(1i64, 1i64).unwrap(), base); + assert_eq!(base.checked_multiply_ratio(3i64, 3i64).unwrap(), base); + assert_eq!( + base.checked_multiply_ratio(654321i64, 654321i64).unwrap(), + base + ); + assert_eq!( + base.checked_multiply_ratio(i64::MAX, i64::MAX).unwrap(), + base + ); + + // factor 3/2 + assert_eq!(base.checked_multiply_ratio(3i64, 2i64).unwrap(), Int64(750)); + assert_eq!( + base.checked_multiply_ratio(333333i64, 222222i64).unwrap(), + Int64(750) + ); + + // factor 2/3 (integer devision always floors the result) + assert_eq!(base.checked_multiply_ratio(2i64, 3i64).unwrap(), Int64(333)); + assert_eq!( + base.checked_multiply_ratio(222222i64, 333333i64).unwrap(), + Int64(333) + ); + + // factor 5/6 (integer devision always floors the result) + assert_eq!(base.checked_multiply_ratio(5i64, 6i64).unwrap(), Int64(416)); + assert_eq!( + base.checked_multiply_ratio(100i64, 120i64).unwrap(), + Int64(416) + ); + } + + #[test] + fn int64_checked_multiply_ratio_does_not_panic() { + assert_eq!( + Int64(500i64).checked_multiply_ratio(1i64, 0i64), + Err(CheckedMultiplyRatioError::DivideByZero), + ); + assert_eq!( + Int64(500i64).checked_multiply_ratio(i64::MAX, 1i64), + Err(CheckedMultiplyRatioError::Overflow), + ); + } + #[test] fn int64_shr_works() { let original = Int64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8]); @@ -1031,6 +1179,37 @@ mod tests { assert_eq!(c.abs_diff(b), Uint64::from(10u32)); } + #[test] + fn int64_abs_works() { + let a = Int64::from(42i32); + assert_eq!(a.abs(), a); + + let b = Int64::from(-42i32); + assert_eq!(b.abs(), a); + + assert_eq!(Int64::zero().abs(), Int64::zero()); + assert_eq!((Int64::MIN + Int64::one()).abs(), Int64::MAX); + } + + #[test] + fn int64_unsigned_abs_works() { + assert_eq!(Int64::zero().unsigned_abs(), Uint64::zero()); + assert_eq!(Int64::one().unsigned_abs(), Uint64::one()); + assert_eq!( + Int64::MIN.unsigned_abs(), + Uint64::new(Int64::MAX.0 as u64) + Uint64::one() + ); + + let v = Int64::from(-42i32); + assert_eq!(v.unsigned_abs(), v.abs_diff(Int64::zero())); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int64_abs_min_panics() { + _ = Int64::MIN.abs(); + } + #[test] #[should_panic = "attempt to negate with overflow"] fn int64_neg_min_panics() { diff --git a/packages/std/src/math/isqrt.rs b/packages/std/src/math/isqrt.rs index 6fd58c397..837cada5a 100644 --- a/packages/std/src/math/isqrt.rs +++ b/packages/std/src/math/isqrt.rs @@ -59,19 +59,19 @@ mod tests { #[test] fn isqrt_primitives() { // Let's check correctness. - assert_eq!(0u8.isqrt(), 0); - assert_eq!(1u8.isqrt(), 1); - assert_eq!(24u8.isqrt(), 4); - assert_eq!(25u8.isqrt(), 5); - assert_eq!(26u8.isqrt(), 5); - assert_eq!(36u8.isqrt(), 6); + assert_eq!(super::Isqrt::isqrt(0u8), 0); + assert_eq!(super::Isqrt::isqrt(1u8), 1); + assert_eq!(super::Isqrt::isqrt(24u8), 4); + assert_eq!(super::Isqrt::isqrt(25u8), 5); + assert_eq!(super::Isqrt::isqrt(26u8), 5); + assert_eq!(super::Isqrt::isqrt(36u8), 6); // Let's also check different types. - assert_eq!(26u8.isqrt(), 5); - assert_eq!(26u16.isqrt(), 5); - assert_eq!(26u32.isqrt(), 5); - assert_eq!(26u64.isqrt(), 5); - assert_eq!(26u128.isqrt(), 5); + assert_eq!(super::Isqrt::isqrt(26u8), 5); + assert_eq!(super::Isqrt::isqrt(26u16), 5); + assert_eq!(super::Isqrt::isqrt(26u32), 5); + assert_eq!(super::Isqrt::isqrt(26u64), 5); + assert_eq!(super::Isqrt::isqrt(26u128), 5); } #[test] diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index a6cf8af3c..1e4527f21 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -1,3 +1,4 @@ +mod conversion; mod decimal; mod decimal256; mod fraction; @@ -6,6 +7,9 @@ mod int256; mod int512; mod int64; mod isqrt; +mod num_consts; +mod signed_decimal; +mod signed_decimal_256; mod uint128; mod uint256; mod uint512; @@ -19,6 +23,8 @@ pub use int256::Int256; pub use int512::Int512; pub use int64::Int64; pub use isqrt::Isqrt; +pub use signed_decimal::{SignedDecimal, SignedDecimalRangeExceeded}; +pub use signed_decimal_256::{SignedDecimal256, SignedDecimal256RangeExceeded}; pub use uint128::Uint128; pub use uint256::Uint256; pub use uint512::Uint512; @@ -70,6 +76,7 @@ mod tests { + ShrAssign + ShrAssign<&'a u32> + Not + + super::num_consts::NumConsts { } diff --git a/packages/std/src/math/num_consts.rs b/packages/std/src/math/num_consts.rs new file mode 100644 index 000000000..b53954230 --- /dev/null +++ b/packages/std/src/math/num_consts.rs @@ -0,0 +1,7 @@ +/// Crate internal trait for all our signed and unsigned number types +pub(crate) trait NumConsts { + const MAX: Self; + const MIN: Self; + const ZERO: Self; + const ONE: Self; +} diff --git a/packages/std/src/math/signed_decimal.rs b/packages/std/src/math/signed_decimal.rs new file mode 100644 index 000000000..bdb45cf04 --- /dev/null +++ b/packages/std/src/math/signed_decimal.rs @@ -0,0 +1,3100 @@ +use core::cmp::Ordering; +use core::fmt::{self, Write}; +use core::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, +}; +use core::str::FromStr; +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use thiserror::Error; + +use crate::errors::{ + CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, + OverflowOperation, RoundDownOverflowError, RoundUpOverflowError, StdError, +}; +use crate::{forward_ref_partial_eq, Decimal, Decimal256, Int256, SignedDecimal256}; + +use super::Fraction; +use super::Int128; + +/// A signed fixed-point decimal value with 18 fractional digits, i.e. SignedDecimal(1_000_000_000_000_000_000) == 1.0 +/// +/// The greatest possible value that can be represented is 170141183460469231731.687303715884105727 (which is (2^127 - 1) / 10^18) +/// and the smallest is -170141183460469231731.687303715884105728 (which is -2^127 / 10^18). +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct SignedDecimal(#[schemars(with = "String")] Int128); + +forward_ref_partial_eq!(SignedDecimal, SignedDecimal); + +#[derive(Error, Debug, PartialEq, Eq)] +#[error("SignedDecimal range exceeded")] +pub struct SignedDecimalRangeExceeded; + +impl SignedDecimal { + const DECIMAL_FRACTIONAL: Int128 = Int128::new(1_000_000_000_000_000_000i128); // 1*10**18 + const DECIMAL_FRACTIONAL_SQUARED: Int128 = + Int128::new(1_000_000_000_000_000_000_000_000_000_000_000_000i128); // (1*10**18)**2 = 1*10**36 + + /// The number of decimal places. Since decimal types are fixed-point rather than + /// floating-point, this is a constant. + pub const DECIMAL_PLACES: u32 = 18; // This needs to be an even number. + + /// The largest value that can be represented by this signed decimal type. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// assert_eq!(SignedDecimal::MAX.to_string(), "170141183460469231731.687303715884105727"); + /// ``` + pub const MAX: Self = Self(Int128::MAX); + + /// The smallest value that can be represented by this signed decimal type. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// assert_eq!(SignedDecimal::MIN.to_string(), "-170141183460469231731.687303715884105728"); + /// ``` + pub const MIN: Self = Self(Int128::MIN); + + /// Creates a SignedDecimal(value) + /// This is equivalent to `SignedDecimal::from_atomics(value, 18)` but usable in a const context. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal, Int128}; + /// assert_eq!(SignedDecimal::new(Int128::one()).to_string(), "0.000000000000000001"); + /// ``` + pub const fn new(value: Int128) -> Self { + Self(value) + } + + /// Creates a SignedDecimal(Int128(value)) + /// This is equivalent to `SignedDecimal::from_atomics(value, 18)` but usable in a const context. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// assert_eq!(SignedDecimal::raw(1234i128).to_string(), "0.000000000000001234"); + /// ``` + pub const fn raw(value: i128) -> Self { + Self(Int128::new(value)) + } + + /// Create a 1.0 SignedDecimal + #[inline] + pub const fn one() -> Self { + Self(Self::DECIMAL_FRACTIONAL) + } + + /// Create a -1.0 SignedDecimal + #[inline] + pub const fn negative_one() -> Self { + Self(Int128::new(-Self::DECIMAL_FRACTIONAL.i128())) + } + + /// Create a 0.0 SignedDecimal + #[inline] + pub const fn zero() -> Self { + Self(Int128::zero()) + } + + /// Convert x% into SignedDecimal + pub fn percent(x: i64) -> Self { + Self(((x as i128) * 10_000_000_000_000_000).into()) + } + + /// Convert permille (x/1000) into SignedDecimal + pub fn permille(x: i64) -> Self { + Self(((x as i128) * 1_000_000_000_000_000).into()) + } + + /// Convert basis points (x/10000) into SignedDecimal + pub fn bps(x: i64) -> Self { + Self(((x as i128) * 100_000_000_000_000).into()) + } + + /// Creates a signed decimal from a number of atomic units and the number + /// of decimal places. The inputs will be converted internally to form + /// a signed decimal with 18 decimal places. So the input 123 and 2 will create + /// the decimal 1.23. + /// + /// Using 18 decimal places is slightly more efficient than other values + /// as no internal conversion is necessary. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal, Int128}; + /// let a = SignedDecimal::from_atomics(Int128::new(1234), 3).unwrap(); + /// assert_eq!(a.to_string(), "1.234"); + /// + /// let a = SignedDecimal::from_atomics(1234i128, 0).unwrap(); + /// assert_eq!(a.to_string(), "1234"); + /// + /// let a = SignedDecimal::from_atomics(1i64, 18).unwrap(); + /// assert_eq!(a.to_string(), "0.000000000000000001"); + /// + /// let a = SignedDecimal::from_atomics(-1i64, 18).unwrap(); + /// assert_eq!(a.to_string(), "-0.000000000000000001"); + /// ``` + pub fn from_atomics( + atomics: impl Into, + decimal_places: u32, + ) -> Result { + let atomics = atomics.into(); + const TEN: Int128 = Int128::new(10); + Ok(match decimal_places.cmp(&(Self::DECIMAL_PLACES)) { + Ordering::Less => { + let digits = (Self::DECIMAL_PLACES) - decimal_places; // No overflow because decimal_places < DECIMAL_PLACES + let factor = TEN.checked_pow(digits).unwrap(); // Safe because digits <= 17 + Self( + atomics + .checked_mul(factor) + .map_err(|_| SignedDecimalRangeExceeded)?, + ) + } + Ordering::Equal => Self(atomics), + Ordering::Greater => { + let digits = decimal_places - (Self::DECIMAL_PLACES); // No overflow because decimal_places > DECIMAL_PLACES + if let Ok(factor) = TEN.checked_pow(digits) { + Self(atomics.checked_div(factor).unwrap()) // Safe because factor cannot be zero + } else { + // In this case `factor` exceeds the Int128 range. + // Any Int128 `x` divided by `factor` with `factor > Int128::MAX` is 0. + // Try e.g. Python3: `(2**128-1) // 2**128` + Self(Int128::zero()) + } + } + }) + } + + /// Returns the ratio (numerator / denominator) as a SignedDecimal + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// assert_eq!( + /// SignedDecimal::from_ratio(1, 3).to_string(), + /// "0.333333333333333333" + /// ); + /// ``` + pub fn from_ratio(numerator: impl Into, denominator: impl Into) -> Self { + match SignedDecimal::checked_from_ratio(numerator, denominator) { + Ok(value) => value, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => panic!("Multiplication overflow"), + } + } + + /// Returns the ratio (numerator / denominator) as a SignedDecimal + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal, CheckedFromRatioError}; + /// assert_eq!( + /// SignedDecimal::checked_from_ratio(1, 3).unwrap().to_string(), + /// "0.333333333333333333" + /// ); + /// assert_eq!( + /// SignedDecimal::checked_from_ratio(1, 0), + /// Err(CheckedFromRatioError::DivideByZero) + /// ); + /// ``` + pub fn checked_from_ratio( + numerator: impl Into, + denominator: impl Into, + ) -> Result { + let numerator: Int128 = numerator.into(); + let denominator: Int128 = denominator.into(); + match numerator.checked_multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator) { + Ok(ratio) => { + // numerator * DECIMAL_FRACTIONAL / denominator + Ok(SignedDecimal(ratio)) + } + Err(CheckedMultiplyRatioError::Overflow) => Err(CheckedFromRatioError::Overflow), + Err(CheckedMultiplyRatioError::DivideByZero) => { + Err(CheckedFromRatioError::DivideByZero) + } + } + } + + /// Returns `true` if the number is 0 + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0.is_zero() + } + + /// Returns `true` if the number is negative (< 0) + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.i128() < 0 + } + + /// A decimal is an integer of atomic units plus a number that specifies the + /// position of the decimal dot. So any decimal can be expressed as two numbers. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal, Int128}; + /// # use core::str::FromStr; + /// // Value with whole and fractional part + /// let a = SignedDecimal::from_str("1.234").unwrap(); + /// assert_eq!(a.decimal_places(), 18); + /// assert_eq!(a.atomics(), Int128::new(1234000000000000000)); + /// + /// // Smallest possible value + /// let b = SignedDecimal::from_str("0.000000000000000001").unwrap(); + /// assert_eq!(b.decimal_places(), 18); + /// assert_eq!(b.atomics(), Int128::new(1)); + /// ``` + #[must_use] + #[inline] + pub const fn atomics(&self) -> Int128 { + self.0 + } + + /// The number of decimal places. This is a constant value for now + /// but this could potentially change as the type evolves. + /// + /// See also [`SignedDecimal::atomics()`]. + #[must_use] + #[inline] + pub const fn decimal_places(&self) -> u32 { + Self::DECIMAL_PLACES + } + + /// Rounds value by truncating the decimal places. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// # use core::str::FromStr; + /// assert!(SignedDecimal::from_str("0.6").unwrap().trunc().is_zero()); + /// assert_eq!(SignedDecimal::from_str("-5.8").unwrap().trunc().to_string(), "-5"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn trunc(&self) -> Self { + Self((self.0 / Self::DECIMAL_FRACTIONAL) * Self::DECIMAL_FRACTIONAL) + } + + /// Rounds value down after decimal places. Panics on overflow. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// # use core::str::FromStr; + /// assert!(SignedDecimal::from_str("0.6").unwrap().floor().is_zero()); + /// assert_eq!(SignedDecimal::from_str("-5.2").unwrap().floor().to_string(), "-6"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn floor(&self) -> Self { + match self.checked_floor() { + Ok(value) => value, + Err(_) => panic!("attempt to floor with overflow"), + } + } + + /// Rounds value down after decimal places. + pub fn checked_floor(&self) -> Result { + if self.is_negative() { + let truncated = self.trunc(); + + if truncated != self { + truncated + .checked_sub(SignedDecimal::one()) + .map_err(|_| RoundDownOverflowError) + } else { + Ok(truncated) + } + } else { + Ok(self.trunc()) + } + } + + /// Rounds value up after decimal places. Panics on overflow. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// # use core::str::FromStr; + /// assert_eq!(SignedDecimal::from_str("0.2").unwrap().ceil(), SignedDecimal::one()); + /// assert_eq!(SignedDecimal::from_str("-5.8").unwrap().ceil().to_string(), "-5"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn ceil(&self) -> Self { + match self.checked_ceil() { + Ok(value) => value, + Err(_) => panic!("attempt to ceil with overflow"), + } + } + + /// Rounds value up after decimal places. Returns OverflowError on overflow. + pub fn checked_ceil(&self) -> Result { + let floor = self.floor(); + if floor == self { + Ok(floor) + } else { + floor + .checked_add(SignedDecimal::one()) + .map_err(|_| RoundUpOverflowError) + } + } + + /// Computes `self + other`, returning an `OverflowError` if an overflow occurred. + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Add, self, other)) + } + + /// Computes `self - other`, returning an `OverflowError` if an overflow occurred. + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + /// Multiplies one `SignedDecimal` by another, returning an `OverflowError` if an overflow occurred. + pub fn checked_mul(self, other: Self) -> Result { + let result_as_int256 = + self.numerator().full_mul(other.numerator()) / Int256::from(Self::DECIMAL_FRACTIONAL); + result_as_int256 + .try_into() + .map(Self) + .map_err(|_| OverflowError { + operation: OverflowOperation::Mul, + operand1: self.to_string(), + operand2: other.to_string(), + }) + } + + /// Raises a value to the power of `exp`, panics if an overflow occurred. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => panic!("Multiplication overflow"), + } + } + + /// Raises a value to the power of `exp`, returning an `OverflowError` if an overflow occurred. + pub fn checked_pow(self, exp: u32) -> Result { + // This uses the exponentiation by squaring algorithm: + // https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Basic_method + + fn inner(mut x: SignedDecimal, mut n: u32) -> Result { + if n == 0 { + return Ok(SignedDecimal::one()); + } + + let mut y = SignedDecimal::one(); + + while n > 1 { + if n % 2 == 0 { + x = x.checked_mul(x)?; + n /= 2; + } else { + y = x.checked_mul(y)?; + x = x.checked_mul(x)?; + n = (n - 1) / 2; + } + } + + Ok(x * y) + } + + inner(self, exp).map_err(|_| OverflowError { + operation: OverflowOperation::Pow, + operand1: self.to_string(), + operand2: exp.to_string(), + }) + } + + pub fn checked_div(self, other: Self) -> Result { + SignedDecimal::checked_from_ratio(self.numerator(), other.numerator()) + } + + /// Computes `self % other`, returning an `DivideByZeroError` if `other == 0`. + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .map_err(|_| DivideByZeroError::new(self)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs_diff(self, other: Self) -> Decimal { + Decimal::new(self.0.abs_diff(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + match self.checked_mul(other) { + Ok(value) => value, + Err(_) => { + // both negative or both positive results in positive number, otherwise negative + if self.is_negative() == other.is_negative() { + Self::MAX + } else { + Self::MIN + } + } + } + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => { + // odd exponent of negative number results in negative number + // everything else results in positive number + if self.is_negative() && exp % 2 == 1 { + Self::MIN + } else { + Self::MAX + } + } + } + } + + /// Converts this decimal to a signed integer by rounding down + /// to the next integer, e.g. 22.5 becomes 22 and -1.2 becomes -2. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal, Int128}; + /// + /// let d = SignedDecimal::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_floor(), Int128::new(12)); + /// + /// let d = SignedDecimal::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_floor(), Int128::new(-13)); + /// + /// let d = SignedDecimal::from_str("-0.05").unwrap(); + /// assert_eq!(d.to_int_floor(), Int128::new(-1)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_floor(self) -> Int128 { + if self.is_negative() { + // Using `x.to_int_floor() = -(-x).to_int_ceil()` for a negative `x`, + // but avoiding overflow by implementing the formula from `to_int_ceil` directly. + let x = self.0; + let y = Self::DECIMAL_FRACTIONAL; + // making sure not to negate `x`, as this would overflow + -Int128::one() - ((-Int128::one() - x) / y) + } else { + self.to_int_trunc() + } + } + + /// Converts this decimal to a signed integer by truncating + /// the fractional part, e.g. 22.5 becomes 22. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal, Int128}; + /// + /// let d = SignedDecimal::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int128::new(12)); + /// + /// let d = SignedDecimal::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int128::new(-12)); + /// + /// let d = SignedDecimal::from_str("75.0").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int128::new(75)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_trunc(self) -> Int128 { + self.0 / Self::DECIMAL_FRACTIONAL + } + + /// Converts this decimal to a signed integer by rounding up + /// to the next integer, e.g. 22.3 becomes 23 and -1.2 becomes -1. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal, Int128}; + /// + /// let d = SignedDecimal::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int128::new(13)); + /// + /// let d = SignedDecimal::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int128::new(-12)); + /// + /// let d = SignedDecimal::from_str("75.0").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int128::new(75)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_ceil(self) -> Int128 { + if self.is_negative() { + self.to_int_trunc() + } else { + // Using `q = 1 + ((x - 1) / y); // if x != 0` with unsigned integers x, y, q + // from https://stackoverflow.com/a/2745086/2013738. We know `x + y` CAN overflow. + let x = self.0; + let y = Self::DECIMAL_FRACTIONAL; + if x.is_zero() { + Int128::zero() + } else { + Int128::one() + ((x - Int128::one()) / y) + } + } + } +} + +impl Fraction for SignedDecimal { + #[inline] + fn numerator(&self) -> Int128 { + self.0 + } + + #[inline] + fn denominator(&self) -> Int128 { + Self::DECIMAL_FRACTIONAL + } + + /// Returns the multiplicative inverse `1/d` for decimal `d`. + /// + /// If `d` is zero, none is returned. + fn inv(&self) -> Option { + if self.is_zero() { + None + } else { + // Let self be p/q with p = self.0 and q = DECIMAL_FRACTIONAL. + // Now we calculate the inverse a/b = q/p such that b = DECIMAL_FRACTIONAL. Then + // `a = DECIMAL_FRACTIONAL*DECIMAL_FRACTIONAL / self.0`. + Some(SignedDecimal(Self::DECIMAL_FRACTIONAL_SQUARED / self.0)) + } + } +} + +impl Neg for SignedDecimal { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl TryFrom for SignedDecimal { + type Error = SignedDecimalRangeExceeded; + + fn try_from(value: SignedDecimal256) -> Result { + value + .atomics() + .try_into() + .map(SignedDecimal) + .map_err(|_| SignedDecimalRangeExceeded) + } +} + +impl TryFrom for SignedDecimal { + type Error = SignedDecimalRangeExceeded; + + fn try_from(value: Decimal) -> Result { + value + .atomics() + .try_into() + .map(SignedDecimal) + .map_err(|_| SignedDecimalRangeExceeded) + } +} + +impl TryFrom for SignedDecimal { + type Error = SignedDecimalRangeExceeded; + + fn try_from(value: Decimal256) -> Result { + value + .atomics() + .try_into() + .map(SignedDecimal) + .map_err(|_| SignedDecimalRangeExceeded) + } +} + +impl FromStr for SignedDecimal { + type Err = StdError; + + /// Converts the decimal string to a SignedDecimal + /// Possible inputs: "1.23", "1", "000012", "1.123000000", "-1.12300" + /// Disallowed: "", ".23" + /// + /// This never performs any kind of rounding. + /// More than DECIMAL_PLACES fractional digits, even zeros, result in an error. + fn from_str(input: &str) -> Result { + let mut parts_iter = input.split('.'); + + let whole_part = parts_iter.next().unwrap(); // split always returns at least one element + let is_neg = whole_part.starts_with('-'); + + let whole = whole_part + .parse::() + .map_err(|_| StdError::generic_err("Error parsing whole"))?; + let mut atomics = whole + .checked_mul(Self::DECIMAL_FRACTIONAL) + .map_err(|_| StdError::generic_err("Value too big"))?; + + if let Some(fractional_part) = parts_iter.next() { + let fractional = fractional_part + .parse::() // u64 is enough for 18 decimal places + .map_err(|_| StdError::generic_err("Error parsing fractional"))?; + let exp = (Self::DECIMAL_PLACES.checked_sub(fractional_part.len() as u32)).ok_or_else( + || { + StdError::generic_err(format!( + "Cannot parse more than {} fractional digits", + Self::DECIMAL_PLACES + )) + }, + )?; + debug_assert!(exp <= Self::DECIMAL_PLACES); + let fractional_factor = Int128::from(10i128.pow(exp)); + + // This multiplication can't overflow because + // fractional < 10^DECIMAL_PLACES && fractional_factor <= 10^DECIMAL_PLACES + let fractional_part = Int128::from(fractional) + .checked_mul(fractional_factor) + .unwrap(); + + // for negative numbers, we need to subtract the fractional part + atomics = if is_neg { + atomics.checked_sub(fractional_part) + } else { + atomics.checked_add(fractional_part) + } + .map_err(|_| StdError::generic_err("Value too big"))?; + } + + if parts_iter.next().is_some() { + return Err(StdError::generic_err("Unexpected number of dots")); + } + + Ok(SignedDecimal(atomics)) + } +} + +impl fmt::Display for SignedDecimal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let whole = (self.0) / Self::DECIMAL_FRACTIONAL; + let fractional = (self.0).checked_rem(Self::DECIMAL_FRACTIONAL).unwrap(); + + if fractional.is_zero() { + write!(f, "{whole}") + } else { + let fractional_string = format!( + "{:0>padding$}", + fractional.abs(), // fractional should always be printed as positive + padding = Self::DECIMAL_PLACES as usize + ); + if self.is_negative() { + f.write_char('-')?; + } + write!( + f, + "{whole}.{fractional}", + whole = whole.abs(), + fractional = fractional_string.trim_end_matches('0') + ) + } + } +} + +impl fmt::Debug for SignedDecimal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SignedDecimal({self})") + } +} + +impl Add for SignedDecimal { + type Output = Self; + + fn add(self, other: Self) -> Self { + SignedDecimal(self.0 + other.0) + } +} +forward_ref_binop!(impl Add, add for SignedDecimal, SignedDecimal); + +impl AddAssign for SignedDecimal { + fn add_assign(&mut self, rhs: SignedDecimal) { + *self = *self + rhs; + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for SignedDecimal, SignedDecimal); + +impl Sub for SignedDecimal { + type Output = Self; + + fn sub(self, other: Self) -> Self { + SignedDecimal(self.0 - other.0) + } +} +forward_ref_binop!(impl Sub, sub for SignedDecimal, SignedDecimal); + +impl SubAssign for SignedDecimal { + fn sub_assign(&mut self, rhs: SignedDecimal) { + *self = *self - rhs; + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for SignedDecimal, SignedDecimal); + +impl Mul for SignedDecimal { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn mul(self, other: Self) -> Self { + // SignedDecimals are fractions. We can multiply two decimals a and b + // via + // (a.numerator() * b.numerator()) / (a.denominator() * b.denominator()) + // = (a.numerator() * b.numerator()) / a.denominator() / b.denominator() + + let result_as_int256 = + self.numerator().full_mul(other.numerator()) / Int256::from(Self::DECIMAL_FRACTIONAL); + match result_as_int256.try_into() { + Ok(result) => Self(result), + Err(_) => panic!("attempt to multiply with overflow"), + } + } +} +forward_ref_binop!(impl Mul, mul for SignedDecimal, SignedDecimal); + +impl MulAssign for SignedDecimal { + fn mul_assign(&mut self, rhs: SignedDecimal) { + *self = *self * rhs; + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for SignedDecimal, SignedDecimal); + +impl Div for SignedDecimal { + type Output = Self; + + fn div(self, other: Self) -> Self { + match SignedDecimal::checked_from_ratio(self.numerator(), other.numerator()) { + Ok(ratio) => ratio, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Division failed - denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => { + panic!("Division failed - multiplication overflow") + } + } + } +} +forward_ref_binop!(impl Div, div for SignedDecimal, SignedDecimal); + +impl DivAssign for SignedDecimal { + fn div_assign(&mut self, rhs: SignedDecimal) { + *self = *self / rhs; + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for SignedDecimal, SignedDecimal); + +impl Div for SignedDecimal { + type Output = Self; + + fn div(self, rhs: Int128) -> Self::Output { + SignedDecimal(self.0 / rhs) + } +} + +impl DivAssign for SignedDecimal { + fn div_assign(&mut self, rhs: Int128) { + self.0 /= rhs; + } +} + +impl Rem for SignedDecimal { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for SignedDecimal, SignedDecimal); + +impl RemAssign for SignedDecimal { + fn rem_assign(&mut self, rhs: SignedDecimal) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for SignedDecimal, SignedDecimal); + +impl core::iter::Sum for SignedDecimal +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +/// Serializes as a decimal string +impl Serialize for SignedDecimal { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +/// Deserializes as a base64 string +impl<'de> Deserialize<'de> for SignedDecimal { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(SignedDecimalVisitor) + } +} + +struct SignedDecimalVisitor; + +impl<'de> de::Visitor<'de> for SignedDecimalVisitor { + type Value = SignedDecimal; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded decimal") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + match SignedDecimal::from_str(v) { + Ok(d) => Ok(d), + Err(e) => Err(E::custom(format!("Error parsing decimal '{v}': {e}"))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_json, to_json_vec}; + use schemars::schema_for; + + fn dec(input: &str) -> SignedDecimal { + SignedDecimal::from_str(input).unwrap() + } + + #[test] + fn signed_decimal_new() { + let expected = Int128::from(300i128); + assert_eq!(SignedDecimal::new(expected).0, expected); + + let expected = Int128::from(-300i128); + assert_eq!(SignedDecimal::new(expected).0, expected); + } + + #[test] + fn signed_decimal_raw() { + let value = 300i128; + assert_eq!(SignedDecimal::raw(value).0.i128(), value); + + let value = -300i128; + assert_eq!(SignedDecimal::raw(value).0.i128(), value); + } + + #[test] + fn signed_decimal_one() { + let value = SignedDecimal::one(); + assert_eq!(value.0, SignedDecimal::DECIMAL_FRACTIONAL); + } + + #[test] + fn signed_decimal_zero() { + let value = SignedDecimal::zero(); + assert!(value.0.is_zero()); + } + + #[test] + fn signed_decimal_percent() { + let value = SignedDecimal::percent(50); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(2u8) + ); + + let value = SignedDecimal::percent(-50); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(-2i8) + ); + } + + #[test] + fn signed_decimal_permille() { + let value = SignedDecimal::permille(125); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(8u8) + ); + + let value = SignedDecimal::permille(-125); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(-8i8) + ); + } + + #[test] + fn signed_decimal_bps() { + let value = SignedDecimal::bps(125); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(80u8) + ); + + let value = SignedDecimal::bps(-125); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(-80i8) + ); + } + + #[test] + fn signed_decimal_from_atomics_works() { + let one = SignedDecimal::one(); + let two = one + one; + let neg_one = SignedDecimal::negative_one(); + + assert_eq!(SignedDecimal::from_atomics(1i128, 0).unwrap(), one); + assert_eq!(SignedDecimal::from_atomics(10i128, 1).unwrap(), one); + assert_eq!(SignedDecimal::from_atomics(100i128, 2).unwrap(), one); + assert_eq!(SignedDecimal::from_atomics(1000i128, 3).unwrap(), one); + assert_eq!( + SignedDecimal::from_atomics(1000000000000000000i128, 18).unwrap(), + one + ); + assert_eq!( + SignedDecimal::from_atomics(10000000000000000000i128, 19).unwrap(), + one + ); + assert_eq!( + SignedDecimal::from_atomics(100000000000000000000i128, 20).unwrap(), + one + ); + + assert_eq!(SignedDecimal::from_atomics(2i128, 0).unwrap(), two); + assert_eq!(SignedDecimal::from_atomics(20i128, 1).unwrap(), two); + assert_eq!(SignedDecimal::from_atomics(200i128, 2).unwrap(), two); + assert_eq!(SignedDecimal::from_atomics(2000i128, 3).unwrap(), two); + assert_eq!( + SignedDecimal::from_atomics(2000000000000000000i128, 18).unwrap(), + two + ); + assert_eq!( + SignedDecimal::from_atomics(20000000000000000000i128, 19).unwrap(), + two + ); + assert_eq!( + SignedDecimal::from_atomics(200000000000000000000i128, 20).unwrap(), + two + ); + + assert_eq!(SignedDecimal::from_atomics(-1i128, 0).unwrap(), neg_one); + assert_eq!(SignedDecimal::from_atomics(-10i128, 1).unwrap(), neg_one); + assert_eq!( + SignedDecimal::from_atomics(-100000000000000000000i128, 20).unwrap(), + neg_one + ); + + // Cuts decimal digits (20 provided but only 18 can be stored) + assert_eq!( + SignedDecimal::from_atomics(4321i128, 20).unwrap(), + SignedDecimal::from_str("0.000000000000000043").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(-4321i128, 20).unwrap(), + SignedDecimal::from_str("-0.000000000000000043").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(6789i128, 20).unwrap(), + SignedDecimal::from_str("0.000000000000000067").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 38).unwrap(), + SignedDecimal::from_str("1.701411834604692317").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 39).unwrap(), + SignedDecimal::from_str("0.170141183460469231").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 45).unwrap(), + SignedDecimal::from_str("0.000000170141183460").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 51).unwrap(), + SignedDecimal::from_str("0.000000000000170141").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 56).unwrap(), + SignedDecimal::from_str("0.000000000000000001").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 57).unwrap(), + SignedDecimal::from_str("0.000000000000000000").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, u32::MAX).unwrap(), + SignedDecimal::from_str("0.000000000000000000").unwrap() + ); + + // Can be used with max value + let max = SignedDecimal::MAX; + assert_eq!( + SignedDecimal::from_atomics(max.atomics(), max.decimal_places()).unwrap(), + max + ); + + // Can be used with min value + let min = SignedDecimal::MIN; + assert_eq!( + SignedDecimal::from_atomics(min.atomics(), min.decimal_places()).unwrap(), + min + ); + + // Overflow is only possible with digits < 18 + let result = SignedDecimal::from_atomics(i128::MAX, 17); + assert_eq!(result.unwrap_err(), SignedDecimalRangeExceeded); + } + + #[test] + fn signed_decimal_from_ratio_works() { + // 1.0 + assert_eq!( + SignedDecimal::from_ratio(1i128, 1i128), + SignedDecimal::one() + ); + assert_eq!( + SignedDecimal::from_ratio(53i128, 53i128), + SignedDecimal::one() + ); + assert_eq!( + SignedDecimal::from_ratio(125i128, 125i128), + SignedDecimal::one() + ); + + // -1.0 + assert_eq!( + SignedDecimal::from_ratio(-1i128, 1i128), + SignedDecimal::negative_one() + ); + assert_eq!( + SignedDecimal::from_ratio(-53i128, 53i128), + SignedDecimal::negative_one() + ); + assert_eq!( + SignedDecimal::from_ratio(125i128, -125i128), + SignedDecimal::negative_one() + ); + + // 1.5 + assert_eq!( + SignedDecimal::from_ratio(3i128, 2i128), + SignedDecimal::percent(150) + ); + assert_eq!( + SignedDecimal::from_ratio(150i128, 100i128), + SignedDecimal::percent(150) + ); + assert_eq!( + SignedDecimal::from_ratio(333i128, 222i128), + SignedDecimal::percent(150) + ); + + // 0.125 + assert_eq!( + SignedDecimal::from_ratio(1i64, 8i64), + SignedDecimal::permille(125) + ); + assert_eq!( + SignedDecimal::from_ratio(125i64, 1000i64), + SignedDecimal::permille(125) + ); + + // -0.125 + assert_eq!( + SignedDecimal::from_ratio(-1i64, 8i64), + SignedDecimal::permille(-125) + ); + assert_eq!( + SignedDecimal::from_ratio(125i64, -1000i64), + SignedDecimal::permille(-125) + ); + + // 1/3 (result floored) + assert_eq!( + SignedDecimal::from_ratio(1i64, 3i64), + SignedDecimal(Int128::from(333_333_333_333_333_333i128)) + ); + + // 2/3 (result floored) + assert_eq!( + SignedDecimal::from_ratio(2i64, 3i64), + SignedDecimal(Int128::from(666_666_666_666_666_666i128)) + ); + + // large inputs + assert_eq!( + SignedDecimal::from_ratio(0i128, i128::MAX), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::from_ratio(i128::MAX, i128::MAX), + SignedDecimal::one() + ); + // 170141183460469231731 is the largest integer <= SignedDecimal::MAX + assert_eq!( + SignedDecimal::from_ratio(170141183460469231731i128, 1i128), + SignedDecimal::from_str("170141183460469231731").unwrap() + ); + } + + #[test] + #[should_panic(expected = "Denominator must not be zero")] + fn signed_decimal_from_ratio_panics_for_zero_denominator() { + SignedDecimal::from_ratio(1i128, 0i128); + } + + #[test] + #[should_panic(expected = "Multiplication overflow")] + fn signed_decimal_from_ratio_panics_for_mul_overflow() { + SignedDecimal::from_ratio(i128::MAX, 1i128); + } + + #[test] + fn signed_decimal_checked_from_ratio_does_not_panic() { + assert_eq!( + SignedDecimal::checked_from_ratio(1i128, 0i128), + Err(CheckedFromRatioError::DivideByZero) + ); + + assert_eq!( + SignedDecimal::checked_from_ratio(i128::MAX, 1i128), + Err(CheckedFromRatioError::Overflow) + ); + } + + #[test] + fn signed_decimal_implements_fraction() { + let fraction = SignedDecimal::from_str("1234.567").unwrap(); + assert_eq!( + fraction.numerator(), + Int128::from(1_234_567_000_000_000_000_000i128) + ); + assert_eq!( + fraction.denominator(), + Int128::from(1_000_000_000_000_000_000i128) + ); + + let fraction = SignedDecimal::from_str("-1234.567").unwrap(); + assert_eq!( + fraction.numerator(), + Int128::from(-1_234_567_000_000_000_000_000i128) + ); + assert_eq!( + fraction.denominator(), + Int128::from(1_000_000_000_000_000_000i128) + ); + } + + #[test] + fn signed_decimal_from_str_works() { + // Integers + assert_eq!( + SignedDecimal::from_str("0").unwrap(), + SignedDecimal::percent(0) + ); + assert_eq!( + SignedDecimal::from_str("1").unwrap(), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::from_str("5").unwrap(), + SignedDecimal::percent(500) + ); + assert_eq!( + SignedDecimal::from_str("42").unwrap(), + SignedDecimal::percent(4200) + ); + assert_eq!( + SignedDecimal::from_str("000").unwrap(), + SignedDecimal::percent(0) + ); + assert_eq!( + SignedDecimal::from_str("001").unwrap(), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::from_str("005").unwrap(), + SignedDecimal::percent(500) + ); + assert_eq!( + SignedDecimal::from_str("0042").unwrap(), + SignedDecimal::percent(4200) + ); + + // Positive decimals + assert_eq!( + SignedDecimal::from_str("1.0").unwrap(), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::from_str("1.5").unwrap(), + SignedDecimal::percent(150) + ); + assert_eq!( + SignedDecimal::from_str("0.5").unwrap(), + SignedDecimal::percent(50) + ); + assert_eq!( + SignedDecimal::from_str("0.123").unwrap(), + SignedDecimal::permille(123) + ); + + assert_eq!( + SignedDecimal::from_str("40.00").unwrap(), + SignedDecimal::percent(4000) + ); + assert_eq!( + SignedDecimal::from_str("04.00").unwrap(), + SignedDecimal::percent(400) + ); + assert_eq!( + SignedDecimal::from_str("00.40").unwrap(), + SignedDecimal::percent(40) + ); + assert_eq!( + SignedDecimal::from_str("00.04").unwrap(), + SignedDecimal::percent(4) + ); + // Negative decimals + assert_eq!( + SignedDecimal::from_str("-00.04").unwrap(), + SignedDecimal::percent(-4) + ); + assert_eq!( + SignedDecimal::from_str("-00.40").unwrap(), + SignedDecimal::percent(-40) + ); + assert_eq!( + SignedDecimal::from_str("-04.00").unwrap(), + SignedDecimal::percent(-400) + ); + + // Can handle DECIMAL_PLACES fractional digits + assert_eq!( + SignedDecimal::from_str("7.123456789012345678").unwrap(), + SignedDecimal(Int128::from(7123456789012345678i128)) + ); + assert_eq!( + SignedDecimal::from_str("7.999999999999999999").unwrap(), + SignedDecimal(Int128::from(7999999999999999999i128)) + ); + + // Works for documented max value + assert_eq!( + SignedDecimal::from_str("170141183460469231731.687303715884105727").unwrap(), + SignedDecimal::MAX + ); + // Works for documented min value + assert_eq!( + SignedDecimal::from_str("-170141183460469231731.687303715884105728").unwrap(), + SignedDecimal::MIN + ); + assert_eq!( + SignedDecimal::from_str("-1").unwrap(), + SignedDecimal::negative_one() + ); + } + + #[test] + fn signed_decimal_from_str_errors_for_broken_whole_part() { + let expected_err = StdError::generic_err("Error parsing whole"); + assert_eq!(SignedDecimal::from_str("").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str(" ").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str("-").unwrap_err(), expected_err); + } + + #[test] + fn signed_decimal_from_str_errors_for_broken_fractional_part() { + let expected_err = StdError::generic_err("Error parsing fractional"); + assert_eq!(SignedDecimal::from_str("1.").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str("1. ").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str("1.e").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str("1.2e3").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str("1.-2").unwrap_err(), expected_err); + } + + #[test] + fn signed_decimal_from_str_errors_for_more_than_18_fractional_digits() { + let expected_err = StdError::generic_err("Cannot parse more than 18 fractional digits"); + assert_eq!( + SignedDecimal::from_str("7.1234567890123456789").unwrap_err(), + expected_err + ); + // No special rules for trailing zeros. This could be changed but adds gas cost for the happy path. + assert_eq!( + SignedDecimal::from_str("7.1230000000000000000").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_from_str_errors_for_invalid_number_of_dots() { + let expected_err = StdError::generic_err("Unexpected number of dots"); + assert_eq!(SignedDecimal::from_str("1.2.3").unwrap_err(), expected_err); + assert_eq!( + SignedDecimal::from_str("1.2.3.4").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_from_str_errors_for_more_than_max_value() { + let expected_err = StdError::generic_err("Value too big"); + // Integer + assert_eq!( + SignedDecimal::from_str("170141183460469231732").unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal::from_str("-170141183460469231732").unwrap_err(), + expected_err + ); + + // SignedDecimal + assert_eq!( + SignedDecimal::from_str("170141183460469231732.0").unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal::from_str("170141183460469231731.687303715884105728").unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal::from_str("-170141183460469231731.687303715884105729").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_conversions_work() { + // signed decimal to signed decimal + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::MAX).unwrap_err(), + SignedDecimalRangeExceeded + ); + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::MIN).unwrap_err(), + SignedDecimalRangeExceeded + ); + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::zero()).unwrap(), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::one()).unwrap(), + SignedDecimal::one() + ); + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::percent(50)).unwrap(), + SignedDecimal::percent(50) + ); + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::percent(-200)).unwrap(), + SignedDecimal::percent(-200) + ); + + // unsigned to signed decimal + assert_eq!( + SignedDecimal::try_from(Decimal::MAX).unwrap_err(), + SignedDecimalRangeExceeded + ); + let max = Decimal::raw(SignedDecimal::MAX.atomics().i128() as u128); + let too_big = max + Decimal::raw(1); + assert_eq!( + SignedDecimal::try_from(too_big).unwrap_err(), + SignedDecimalRangeExceeded + ); + assert_eq!( + SignedDecimal::try_from(Decimal::zero()).unwrap(), + SignedDecimal::zero() + ); + assert_eq!(SignedDecimal::try_from(max).unwrap(), SignedDecimal::MAX); + } + + #[test] + fn signed_decimal_atomics_works() { + let zero = SignedDecimal::zero(); + let one = SignedDecimal::one(); + let half = SignedDecimal::percent(50); + let two = SignedDecimal::percent(200); + let max = SignedDecimal::MAX; + let neg_half = SignedDecimal::percent(-50); + let neg_two = SignedDecimal::percent(-200); + let min = SignedDecimal::MIN; + + assert_eq!(zero.atomics(), Int128::new(0)); + assert_eq!(one.atomics(), Int128::new(1000000000000000000)); + assert_eq!(half.atomics(), Int128::new(500000000000000000)); + assert_eq!(two.atomics(), Int128::new(2000000000000000000)); + assert_eq!(max.atomics(), Int128::MAX); + assert_eq!(neg_half.atomics(), Int128::new(-500000000000000000)); + assert_eq!(neg_two.atomics(), Int128::new(-2000000000000000000)); + assert_eq!(min.atomics(), Int128::MIN); + } + + #[test] + fn signed_decimal_decimal_places_works() { + let zero = SignedDecimal::zero(); + let one = SignedDecimal::one(); + let half = SignedDecimal::percent(50); + let two = SignedDecimal::percent(200); + let max = SignedDecimal::MAX; + let neg_one = SignedDecimal::negative_one(); + + assert_eq!(zero.decimal_places(), 18); + assert_eq!(one.decimal_places(), 18); + assert_eq!(half.decimal_places(), 18); + assert_eq!(two.decimal_places(), 18); + assert_eq!(max.decimal_places(), 18); + assert_eq!(neg_one.decimal_places(), 18); + } + + #[test] + fn signed_decimal_is_zero_works() { + assert!(SignedDecimal::zero().is_zero()); + assert!(SignedDecimal::percent(0).is_zero()); + assert!(SignedDecimal::permille(0).is_zero()); + + assert!(!SignedDecimal::one().is_zero()); + assert!(!SignedDecimal::percent(123).is_zero()); + assert!(!SignedDecimal::permille(-1234).is_zero()); + } + + #[test] + fn signed_decimal_inv_works() { + // d = 0 + assert_eq!(SignedDecimal::zero().inv(), None); + + // d == 1 + assert_eq!(SignedDecimal::one().inv(), Some(SignedDecimal::one())); + + // d == -1 + assert_eq!( + SignedDecimal::negative_one().inv(), + Some(SignedDecimal::negative_one()) + ); + + // d > 1 exact + assert_eq!( + SignedDecimal::from_str("2").unwrap().inv(), + Some(SignedDecimal::from_str("0.5").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("20").unwrap().inv(), + Some(SignedDecimal::from_str("0.05").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("200").unwrap().inv(), + Some(SignedDecimal::from_str("0.005").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("2000").unwrap().inv(), + Some(SignedDecimal::from_str("0.0005").unwrap()) + ); + + // d > 1 rounded + assert_eq!( + SignedDecimal::from_str("3").unwrap().inv(), + Some(SignedDecimal::from_str("0.333333333333333333").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("6").unwrap().inv(), + Some(SignedDecimal::from_str("0.166666666666666666").unwrap()) + ); + + // d < 1 exact + assert_eq!( + SignedDecimal::from_str("0.5").unwrap().inv(), + Some(SignedDecimal::from_str("2").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("0.05").unwrap().inv(), + Some(SignedDecimal::from_str("20").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("0.005").unwrap().inv(), + Some(SignedDecimal::from_str("200").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("0.0005").unwrap().inv(), + Some(SignedDecimal::from_str("2000").unwrap()) + ); + + // d < 0 + assert_eq!( + SignedDecimal::from_str("-0.5").unwrap().inv(), + Some(SignedDecimal::from_str("-2").unwrap()) + ); + // d < 0 rounded + assert_eq!( + SignedDecimal::from_str("-3").unwrap().inv(), + Some(SignedDecimal::from_str("-0.333333333333333333").unwrap()) + ); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_add_works() { + let value = SignedDecimal::one() + SignedDecimal::percent(50); // 1.5 + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL * Int128::from(3u8) / Int128::from(2u8) + ); + + assert_eq!( + SignedDecimal::percent(5) + SignedDecimal::percent(4), + SignedDecimal::percent(9) + ); + assert_eq!( + SignedDecimal::percent(5) + SignedDecimal::zero(), + SignedDecimal::percent(5) + ); + assert_eq!( + SignedDecimal::zero() + SignedDecimal::zero(), + SignedDecimal::zero() + ); + // negative numbers + assert_eq!( + SignedDecimal::percent(-5) + SignedDecimal::percent(-4), + SignedDecimal::percent(-9) + ); + assert_eq!( + SignedDecimal::percent(-5) + SignedDecimal::percent(4), + SignedDecimal::percent(-1) + ); + assert_eq!( + SignedDecimal::percent(5) + SignedDecimal::percent(-4), + SignedDecimal::percent(1) + ); + + // works for refs + let a = SignedDecimal::percent(15); + let b = SignedDecimal::percent(25); + let expected = SignedDecimal::percent(40); + assert_eq!(a + b, expected); + assert_eq!(&a + b, expected); + assert_eq!(a + &b, expected); + assert_eq!(&a + &b, expected); + } + + #[test] + #[should_panic] + fn signed_decimal_add_overflow_panics() { + let _value = SignedDecimal::MAX + SignedDecimal::percent(50); + } + + #[test] + fn signed_decimal_add_assign_works() { + let mut a = SignedDecimal::percent(30); + a += SignedDecimal::percent(20); + assert_eq!(a, SignedDecimal::percent(50)); + + // works for refs + let mut a = SignedDecimal::percent(15); + let b = SignedDecimal::percent(3); + let expected = SignedDecimal::percent(18); + a += &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_sub_works() { + let value = SignedDecimal::one() - SignedDecimal::percent(50); // 0.5 + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(2u8) + ); + + assert_eq!( + SignedDecimal::percent(9) - SignedDecimal::percent(4), + SignedDecimal::percent(5) + ); + assert_eq!( + SignedDecimal::percent(16) - SignedDecimal::zero(), + SignedDecimal::percent(16) + ); + assert_eq!( + SignedDecimal::percent(16) - SignedDecimal::percent(16), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::zero() - SignedDecimal::zero(), + SignedDecimal::zero() + ); + + // negative numbers + assert_eq!( + SignedDecimal::percent(-5) - SignedDecimal::percent(-4), + SignedDecimal::percent(-1) + ); + assert_eq!( + SignedDecimal::percent(-5) - SignedDecimal::percent(4), + SignedDecimal::percent(-9) + ); + assert_eq!( + SignedDecimal::percent(500) - SignedDecimal::percent(-4), + SignedDecimal::percent(504) + ); + + // works for refs + let a = SignedDecimal::percent(13); + let b = SignedDecimal::percent(6); + let expected = SignedDecimal::percent(7); + assert_eq!(a - b, expected); + assert_eq!(&a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn signed_decimal_sub_overflow_panics() { + let _value = SignedDecimal::MIN - SignedDecimal::percent(50); + } + + #[test] + fn signed_decimal_sub_assign_works() { + let mut a = SignedDecimal::percent(20); + a -= SignedDecimal::percent(2); + assert_eq!(a, SignedDecimal::percent(18)); + + // works for refs + let mut a = SignedDecimal::percent(33); + let b = SignedDecimal::percent(13); + let expected = SignedDecimal::percent(20); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_implements_mul() { + let one = SignedDecimal::one(); + let two = one + one; + let half = SignedDecimal::percent(50); + + // 1*x and x*1 + assert_eq!(one * SignedDecimal::percent(0), SignedDecimal::percent(0)); + assert_eq!(one * SignedDecimal::percent(1), SignedDecimal::percent(1)); + assert_eq!(one * SignedDecimal::percent(10), SignedDecimal::percent(10)); + assert_eq!( + one * SignedDecimal::percent(100), + SignedDecimal::percent(100) + ); + assert_eq!( + one * SignedDecimal::percent(1000), + SignedDecimal::percent(1000) + ); + assert_eq!(one * SignedDecimal::MAX, SignedDecimal::MAX); + assert_eq!(SignedDecimal::percent(0) * one, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) * one, SignedDecimal::percent(1)); + assert_eq!(SignedDecimal::percent(10) * one, SignedDecimal::percent(10)); + assert_eq!( + SignedDecimal::percent(100) * one, + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::percent(1000) * one, + SignedDecimal::percent(1000) + ); + assert_eq!(SignedDecimal::MAX * one, SignedDecimal::MAX); + assert_eq!(SignedDecimal::percent(-1) * one, SignedDecimal::percent(-1)); + assert_eq!( + one * SignedDecimal::percent(-10), + SignedDecimal::percent(-10) + ); + + // double + assert_eq!(two * SignedDecimal::percent(0), SignedDecimal::percent(0)); + assert_eq!(two * SignedDecimal::percent(1), SignedDecimal::percent(2)); + assert_eq!(two * SignedDecimal::percent(10), SignedDecimal::percent(20)); + assert_eq!( + two * SignedDecimal::percent(100), + SignedDecimal::percent(200) + ); + assert_eq!( + two * SignedDecimal::percent(1000), + SignedDecimal::percent(2000) + ); + assert_eq!(SignedDecimal::percent(0) * two, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) * two, SignedDecimal::percent(2)); + assert_eq!(SignedDecimal::percent(10) * two, SignedDecimal::percent(20)); + assert_eq!( + SignedDecimal::percent(100) * two, + SignedDecimal::percent(200) + ); + assert_eq!( + SignedDecimal::percent(1000) * two, + SignedDecimal::percent(2000) + ); + assert_eq!(SignedDecimal::percent(-1) * two, SignedDecimal::percent(-2)); + assert_eq!( + two * SignedDecimal::new(Int128::MIN / Int128::new(2)), + SignedDecimal::MIN + ); + + // half + assert_eq!(half * SignedDecimal::percent(0), SignedDecimal::percent(0)); + assert_eq!(half * SignedDecimal::percent(1), SignedDecimal::permille(5)); + assert_eq!(half * SignedDecimal::percent(10), SignedDecimal::percent(5)); + assert_eq!( + half * SignedDecimal::percent(100), + SignedDecimal::percent(50) + ); + assert_eq!( + half * SignedDecimal::percent(1000), + SignedDecimal::percent(500) + ); + assert_eq!(SignedDecimal::percent(0) * half, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) * half, SignedDecimal::permille(5)); + assert_eq!(SignedDecimal::percent(10) * half, SignedDecimal::percent(5)); + assert_eq!( + SignedDecimal::percent(100) * half, + SignedDecimal::percent(50) + ); + assert_eq!( + SignedDecimal::percent(1000) * half, + SignedDecimal::percent(500) + ); + + // Move left + let a = dec("123.127726548762582"); + assert_eq!(a * dec("1"), dec("123.127726548762582")); + assert_eq!(a * dec("10"), dec("1231.27726548762582")); + assert_eq!(a * dec("100"), dec("12312.7726548762582")); + assert_eq!(a * dec("1000"), dec("123127.726548762582")); + assert_eq!(a * dec("1000000"), dec("123127726.548762582")); + assert_eq!(a * dec("1000000000"), dec("123127726548.762582")); + assert_eq!(a * dec("1000000000000"), dec("123127726548762.582")); + assert_eq!(a * dec("1000000000000000"), dec("123127726548762582")); + assert_eq!(a * dec("1000000000000000000"), dec("123127726548762582000")); + assert_eq!(dec("1") * a, dec("123.127726548762582")); + assert_eq!(dec("10") * a, dec("1231.27726548762582")); + assert_eq!(dec("100") * a, dec("12312.7726548762582")); + assert_eq!(dec("1000") * a, dec("123127.726548762582")); + assert_eq!(dec("1000000") * a, dec("123127726.548762582")); + assert_eq!(dec("1000000000") * a, dec("123127726548.762582")); + assert_eq!(dec("1000000000000") * a, dec("123127726548762.582")); + assert_eq!(dec("1000000000000000") * a, dec("123127726548762582")); + assert_eq!(dec("1000000000000000000") * a, dec("123127726548762582000")); + assert_eq!( + dec("-1000000000000000000") * a, + dec("-123127726548762582000") + ); + + // Move right + let max = SignedDecimal::MAX; + assert_eq!( + max * dec("1.0"), + dec("170141183460469231731.687303715884105727") + ); + assert_eq!( + max * dec("0.1"), + dec("17014118346046923173.168730371588410572") + ); + assert_eq!( + max * dec("0.01"), + dec("1701411834604692317.316873037158841057") + ); + assert_eq!( + max * dec("0.001"), + dec("170141183460469231.731687303715884105") + ); + assert_eq!( + max * dec("0.000001"), + dec("170141183460469.231731687303715884") + ); + assert_eq!( + max * dec("0.000000001"), + dec("170141183460.469231731687303715") + ); + assert_eq!( + max * dec("0.000000000001"), + dec("170141183.460469231731687303") + ); + assert_eq!( + max * dec("0.000000000000001"), + dec("170141.183460469231731687") + ); + assert_eq!( + max * dec("0.000000000000000001"), + dec("170.141183460469231731") + ); + + // works for refs + let a = SignedDecimal::percent(20); + let b = SignedDecimal::percent(30); + let expected = SignedDecimal::percent(6); + assert_eq!(a * b, expected); + assert_eq!(&a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn signed_decimal_mul_assign_works() { + let mut a = SignedDecimal::percent(15); + a *= SignedDecimal::percent(60); + assert_eq!(a, SignedDecimal::percent(9)); + + // works for refs + let mut a = SignedDecimal::percent(50); + let b = SignedDecimal::percent(20); + a *= &b; + assert_eq!(a, SignedDecimal::percent(10)); + } + + #[test] + #[should_panic(expected = "attempt to multiply with overflow")] + fn signed_decimal_mul_overflow_panics() { + let _value = SignedDecimal::MAX * SignedDecimal::percent(101); + } + + #[test] + fn signed_decimal_checked_mul() { + let test_data = [ + (SignedDecimal::zero(), SignedDecimal::zero()), + (SignedDecimal::zero(), SignedDecimal::one()), + (SignedDecimal::one(), SignedDecimal::zero()), + (SignedDecimal::percent(10), SignedDecimal::zero()), + (SignedDecimal::percent(10), SignedDecimal::percent(5)), + (SignedDecimal::MAX, SignedDecimal::one()), + ( + SignedDecimal::MAX / Int128::new(2), + SignedDecimal::percent(200), + ), + (SignedDecimal::permille(6), SignedDecimal::permille(13)), + (SignedDecimal::permille(-6), SignedDecimal::permille(0)), + (SignedDecimal::MAX, SignedDecimal::negative_one()), + ]; + + // The regular core::ops::Mul is our source of truth for these tests. + for (x, y) in test_data.into_iter() { + assert_eq!(x * y, x.checked_mul(y).unwrap()); + } + } + + #[test] + fn signed_decimal_checked_mul_overflow() { + assert_eq!( + SignedDecimal::MAX.checked_mul(SignedDecimal::percent(200)), + Err(OverflowError { + operation: OverflowOperation::Mul, + operand1: SignedDecimal::MAX.to_string(), + operand2: SignedDecimal::percent(200).to_string(), + }) + ); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_implements_div() { + let one = SignedDecimal::one(); + let two = one + one; + let half = SignedDecimal::percent(50); + + // 1/x and x/1 + assert_eq!( + one / SignedDecimal::percent(1), + SignedDecimal::percent(10_000) + ); + assert_eq!( + one / SignedDecimal::percent(10), + SignedDecimal::percent(1_000) + ); + assert_eq!( + one / SignedDecimal::percent(100), + SignedDecimal::percent(100) + ); + assert_eq!( + one / SignedDecimal::percent(1000), + SignedDecimal::percent(10) + ); + assert_eq!(SignedDecimal::percent(0) / one, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) / one, SignedDecimal::percent(1)); + assert_eq!(SignedDecimal::percent(10) / one, SignedDecimal::percent(10)); + assert_eq!( + SignedDecimal::percent(100) / one, + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::percent(1000) / one, + SignedDecimal::percent(1000) + ); + assert_eq!( + one / SignedDecimal::percent(-1), + SignedDecimal::percent(-10_000) + ); + assert_eq!( + one / SignedDecimal::percent(-10), + SignedDecimal::percent(-1_000) + ); + + // double + assert_eq!( + two / SignedDecimal::percent(1), + SignedDecimal::percent(20_000) + ); + assert_eq!( + two / SignedDecimal::percent(10), + SignedDecimal::percent(2_000) + ); + assert_eq!( + two / SignedDecimal::percent(100), + SignedDecimal::percent(200) + ); + assert_eq!( + two / SignedDecimal::percent(1000), + SignedDecimal::percent(20) + ); + assert_eq!(SignedDecimal::percent(0) / two, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) / two, dec("0.005")); + assert_eq!(SignedDecimal::percent(10) / two, SignedDecimal::percent(5)); + assert_eq!( + SignedDecimal::percent(100) / two, + SignedDecimal::percent(50) + ); + assert_eq!( + SignedDecimal::percent(1000) / two, + SignedDecimal::percent(500) + ); + assert_eq!( + two / SignedDecimal::percent(-1), + SignedDecimal::percent(-20_000) + ); + assert_eq!( + SignedDecimal::percent(-10000) / two, + SignedDecimal::percent(-5000) + ); + + // half + assert_eq!( + half / SignedDecimal::percent(1), + SignedDecimal::percent(5_000) + ); + assert_eq!( + half / SignedDecimal::percent(10), + SignedDecimal::percent(500) + ); + assert_eq!( + half / SignedDecimal::percent(100), + SignedDecimal::percent(50) + ); + assert_eq!( + half / SignedDecimal::percent(1000), + SignedDecimal::percent(5) + ); + assert_eq!(SignedDecimal::percent(0) / half, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) / half, SignedDecimal::percent(2)); + assert_eq!( + SignedDecimal::percent(10) / half, + SignedDecimal::percent(20) + ); + assert_eq!( + SignedDecimal::percent(100) / half, + SignedDecimal::percent(200) + ); + assert_eq!( + SignedDecimal::percent(1000) / half, + SignedDecimal::percent(2000) + ); + + // Move right + let a = dec("123127726548762582"); + assert_eq!(a / dec("1"), dec("123127726548762582")); + assert_eq!(a / dec("10"), dec("12312772654876258.2")); + assert_eq!(a / dec("100"), dec("1231277265487625.82")); + assert_eq!(a / dec("1000"), dec("123127726548762.582")); + assert_eq!(a / dec("1000000"), dec("123127726548.762582")); + assert_eq!(a / dec("1000000000"), dec("123127726.548762582")); + assert_eq!(a / dec("1000000000000"), dec("123127.726548762582")); + assert_eq!(a / dec("1000000000000000"), dec("123.127726548762582")); + assert_eq!(a / dec("1000000000000000000"), dec("0.123127726548762582")); + assert_eq!(dec("1") / a, dec("0.000000000000000008")); + assert_eq!(dec("10") / a, dec("0.000000000000000081")); + assert_eq!(dec("100") / a, dec("0.000000000000000812")); + assert_eq!(dec("1000") / a, dec("0.000000000000008121")); + assert_eq!(dec("1000000") / a, dec("0.000000000008121647")); + assert_eq!(dec("1000000000") / a, dec("0.000000008121647560")); + assert_eq!(dec("1000000000000") / a, dec("0.000008121647560868")); + assert_eq!(dec("1000000000000000") / a, dec("0.008121647560868164")); + assert_eq!(dec("1000000000000000000") / a, dec("8.121647560868164773")); + // negative + let a = dec("-123127726548762582"); + assert_eq!(a / dec("1"), dec("-123127726548762582")); + assert_eq!(a / dec("10"), dec("-12312772654876258.2")); + assert_eq!(a / dec("100"), dec("-1231277265487625.82")); + assert_eq!(a / dec("1000"), dec("-123127726548762.582")); + assert_eq!(a / dec("1000000"), dec("-123127726548.762582")); + assert_eq!(a / dec("1000000000"), dec("-123127726.548762582")); + assert_eq!(a / dec("1000000000000"), dec("-123127.726548762582")); + assert_eq!(a / dec("1000000000000000"), dec("-123.127726548762582")); + assert_eq!(a / dec("1000000000000000000"), dec("-0.123127726548762582")); + assert_eq!(dec("1") / a, dec("-0.000000000000000008")); + + // Move left + let a = dec("0.123127726548762582"); + assert_eq!(a / dec("1.0"), dec("0.123127726548762582")); + assert_eq!(a / dec("0.1"), dec("1.23127726548762582")); + assert_eq!(a / dec("0.01"), dec("12.3127726548762582")); + assert_eq!(a / dec("0.001"), dec("123.127726548762582")); + assert_eq!(a / dec("0.000001"), dec("123127.726548762582")); + assert_eq!(a / dec("0.000000001"), dec("123127726.548762582")); + assert_eq!(a / dec("0.000000000001"), dec("123127726548.762582")); + assert_eq!(a / dec("0.000000000000001"), dec("123127726548762.582")); + assert_eq!(a / dec("0.000000000000000001"), dec("123127726548762582")); + // negative + let a = dec("-0.123127726548762582"); + assert_eq!(a / dec("1.0"), dec("-0.123127726548762582")); + assert_eq!(a / dec("0.1"), dec("-1.23127726548762582")); + assert_eq!(a / dec("0.01"), dec("-12.3127726548762582")); + assert_eq!(a / dec("0.001"), dec("-123.127726548762582")); + assert_eq!(a / dec("0.000001"), dec("-123127.726548762582")); + assert_eq!(a / dec("0.000000001"), dec("-123127726.548762582")); + + assert_eq!( + SignedDecimal::percent(15) / SignedDecimal::percent(60), + SignedDecimal::percent(25) + ); + + // works for refs + let a = SignedDecimal::percent(100); + let b = SignedDecimal::percent(20); + let expected = SignedDecimal::percent(500); + assert_eq!(a / b, expected); + assert_eq!(&a / b, expected); + assert_eq!(a / &b, expected); + assert_eq!(&a / &b, expected); + } + + #[test] + fn signed_decimal_div_assign_works() { + let mut a = SignedDecimal::percent(15); + a /= SignedDecimal::percent(20); + assert_eq!(a, SignedDecimal::percent(75)); + + // works for refs + let mut a = SignedDecimal::percent(50); + let b = SignedDecimal::percent(20); + a /= &b; + assert_eq!(a, SignedDecimal::percent(250)); + } + + #[test] + #[should_panic(expected = "Division failed - multiplication overflow")] + fn signed_decimal_div_overflow_panics() { + let _value = SignedDecimal::MAX / SignedDecimal::percent(10); + } + + #[test] + #[should_panic(expected = "Division failed - denominator must not be zero")] + fn signed_decimal_div_by_zero_panics() { + let _value = SignedDecimal::one() / SignedDecimal::zero(); + } + + #[test] + fn signed_decimal_int128_division() { + // a/b + let left = SignedDecimal::percent(150); // 1.5 + let right = Int128::new(3); + assert_eq!(left / right, SignedDecimal::percent(50)); + + // negative + let left = SignedDecimal::percent(-150); // -1.5 + let right = Int128::new(3); + assert_eq!(left / right, SignedDecimal::percent(-50)); + + // 0/a + let left = SignedDecimal::zero(); + let right = Int128::new(300); + assert_eq!(left / right, SignedDecimal::zero()); + } + + #[test] + #[should_panic] + fn signed_decimal_int128_divide_by_zero() { + let left = SignedDecimal::percent(150); // 1.5 + let right = Int128::new(0); + let _result = left / right; + } + + #[test] + fn signed_decimal_int128_div_assign() { + // a/b + let mut dec = SignedDecimal::percent(150); // 1.5 + dec /= Int128::new(3); + assert_eq!(dec, SignedDecimal::percent(50)); + + // 0/a + let mut dec = SignedDecimal::zero(); + dec /= Int128::new(300); + assert_eq!(dec, SignedDecimal::zero()); + } + + #[test] + #[should_panic] + fn signed_decimal_int128_div_assign_by_zero() { + // a/0 + let mut dec = SignedDecimal::percent(50); + dec /= Int128::new(0); + } + + #[test] + fn signed_decimal_checked_pow() { + for exp in 0..10 { + assert_eq!( + SignedDecimal::one().checked_pow(exp).unwrap(), + SignedDecimal::one() + ); + } + + // This case is mathematically undefined but we ensure consistency with Rust standard types + // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=20df6716048e77087acd40194b233494 + assert_eq!( + SignedDecimal::zero().checked_pow(0).unwrap(), + SignedDecimal::one() + ); + + for exp in 1..10 { + assert_eq!( + SignedDecimal::zero().checked_pow(exp).unwrap(), + SignedDecimal::zero() + ); + } + + for exp in 1..10 { + assert_eq!( + SignedDecimal::negative_one().checked_pow(exp).unwrap(), + // alternates between 1 and -1 + if exp % 2 == 0 { + SignedDecimal::one() + } else { + SignedDecimal::negative_one() + } + ) + } + + for num in &[ + SignedDecimal::percent(50), + SignedDecimal::percent(99), + SignedDecimal::percent(200), + ] { + assert_eq!(num.checked_pow(0).unwrap(), SignedDecimal::one()) + } + + assert_eq!( + SignedDecimal::percent(20).checked_pow(2).unwrap(), + SignedDecimal::percent(4) + ); + + assert_eq!( + SignedDecimal::percent(20).checked_pow(3).unwrap(), + SignedDecimal::permille(8) + ); + + assert_eq!( + SignedDecimal::percent(200).checked_pow(4).unwrap(), + SignedDecimal::percent(1600) + ); + + assert_eq!( + SignedDecimal::percent(200).checked_pow(4).unwrap(), + SignedDecimal::percent(1600) + ); + + assert_eq!( + SignedDecimal::percent(700).checked_pow(5).unwrap(), + SignedDecimal::percent(1680700) + ); + + assert_eq!( + SignedDecimal::percent(700).checked_pow(8).unwrap(), + SignedDecimal::percent(576480100) + ); + + assert_eq!( + SignedDecimal::percent(700).checked_pow(10).unwrap(), + SignedDecimal::percent(28247524900) + ); + + assert_eq!( + SignedDecimal::percent(120).checked_pow(123).unwrap(), + SignedDecimal(5486473221892422150877397607i128.into()) + ); + + assert_eq!( + SignedDecimal::percent(10).checked_pow(2).unwrap(), + SignedDecimal(10000000000000000i128.into()) + ); + + assert_eq!( + SignedDecimal::percent(10).checked_pow(18).unwrap(), + SignedDecimal(1i128.into()) + ); + + let decimals = [ + SignedDecimal::percent(-50), + SignedDecimal::percent(-99), + SignedDecimal::percent(-200), + ]; + let exponents = [1, 2, 3, 4, 5, 8, 10]; + + for d in decimals { + for e in exponents { + // use multiplication as source of truth + let mut mul = Ok(d); + for _ in 1..e { + mul = mul.and_then(|mul| mul.checked_mul(d)); + } + assert_eq!(mul, d.checked_pow(e)); + } + } + } + + #[test] + fn signed_decimal_checked_pow_overflow() { + assert_eq!( + SignedDecimal::MAX.checked_pow(2), + Err(OverflowError { + operation: OverflowOperation::Pow, + operand1: SignedDecimal::MAX.to_string(), + operand2: "2".to_string(), + }) + ); + } + + #[test] + fn signed_decimal_to_string() { + // Integers + assert_eq!(SignedDecimal::zero().to_string(), "0"); + assert_eq!(SignedDecimal::one().to_string(), "1"); + assert_eq!(SignedDecimal::percent(500).to_string(), "5"); + assert_eq!(SignedDecimal::percent(-500).to_string(), "-5"); + + // SignedDecimals + assert_eq!(SignedDecimal::percent(125).to_string(), "1.25"); + assert_eq!(SignedDecimal::percent(42638).to_string(), "426.38"); + assert_eq!(SignedDecimal::percent(3).to_string(), "0.03"); + assert_eq!(SignedDecimal::permille(987).to_string(), "0.987"); + assert_eq!(SignedDecimal::percent(-125).to_string(), "-1.25"); + assert_eq!(SignedDecimal::percent(-42638).to_string(), "-426.38"); + assert_eq!(SignedDecimal::percent(-3).to_string(), "-0.03"); + assert_eq!(SignedDecimal::permille(-987).to_string(), "-0.987"); + + assert_eq!( + SignedDecimal(Int128::from(1i128)).to_string(), + "0.000000000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(10i128)).to_string(), + "0.00000000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(100i128)).to_string(), + "0.0000000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(1000i128)).to_string(), + "0.000000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(10000i128)).to_string(), + "0.00000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(100000i128)).to_string(), + "0.0000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(1000000i128)).to_string(), + "0.000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(10000000i128)).to_string(), + "0.00000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(100000000i128)).to_string(), + "0.0000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(1000000000i128)).to_string(), + "0.000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(10000000000i128)).to_string(), + "0.00000001" + ); + assert_eq!( + SignedDecimal(Int128::from(100000000000i128)).to_string(), + "0.0000001" + ); + assert_eq!( + SignedDecimal(Int128::from(10000000000000i128)).to_string(), + "0.00001" + ); + assert_eq!( + SignedDecimal(Int128::from(100000000000000i128)).to_string(), + "0.0001" + ); + assert_eq!( + SignedDecimal(Int128::from(1000000000000000i128)).to_string(), + "0.001" + ); + assert_eq!( + SignedDecimal(Int128::from(10000000000000000i128)).to_string(), + "0.01" + ); + assert_eq!( + SignedDecimal(Int128::from(100000000000000000i128)).to_string(), + "0.1" + ); + assert_eq!( + SignedDecimal(Int128::from(-1i128)).to_string(), + "-0.000000000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(-100000000000000i128)).to_string(), + "-0.0001" + ); + assert_eq!( + SignedDecimal(Int128::from(-100000000000000000i128)).to_string(), + "-0.1" + ); + } + + #[test] + fn signed_decimal_iter_sum() { + let items = vec![ + SignedDecimal::zero(), + SignedDecimal(Int128::from(2i128)), + SignedDecimal(Int128::from(2i128)), + SignedDecimal(Int128::from(-2i128)), + ]; + assert_eq!( + items.iter().sum::(), + SignedDecimal(Int128::from(2i128)) + ); + assert_eq!( + items.into_iter().sum::(), + SignedDecimal(Int128::from(2i128)) + ); + + let empty: Vec = vec![]; + assert_eq!(SignedDecimal::zero(), empty.iter().sum::()); + } + + #[test] + fn signed_decimal_serialize() { + assert_eq!(to_json_vec(&SignedDecimal::zero()).unwrap(), br#""0""#); + assert_eq!(to_json_vec(&SignedDecimal::one()).unwrap(), br#""1""#); + assert_eq!( + to_json_vec(&SignedDecimal::percent(8)).unwrap(), + br#""0.08""# + ); + assert_eq!( + to_json_vec(&SignedDecimal::percent(87)).unwrap(), + br#""0.87""# + ); + assert_eq!( + to_json_vec(&SignedDecimal::percent(876)).unwrap(), + br#""8.76""# + ); + assert_eq!( + to_json_vec(&SignedDecimal::percent(8765)).unwrap(), + br#""87.65""# + ); + assert_eq!( + to_json_vec(&SignedDecimal::percent(-87654)).unwrap(), + br#""-876.54""# + ); + assert_eq!( + to_json_vec(&SignedDecimal::negative_one()).unwrap(), + br#""-1""# + ); + assert_eq!( + to_json_vec(&-SignedDecimal::percent(8)).unwrap(), + br#""-0.08""# + ); + } + + #[test] + fn signed_decimal_deserialize() { + assert_eq!( + from_json::(br#""0""#).unwrap(), + SignedDecimal::zero() + ); + assert_eq!( + from_json::(br#""1""#).unwrap(), + SignedDecimal::one() + ); + assert_eq!( + from_json::(br#""000""#).unwrap(), + SignedDecimal::zero() + ); + assert_eq!( + from_json::(br#""001""#).unwrap(), + SignedDecimal::one() + ); + + assert_eq!( + from_json::(br#""0.08""#).unwrap(), + SignedDecimal::percent(8) + ); + assert_eq!( + from_json::(br#""0.87""#).unwrap(), + SignedDecimal::percent(87) + ); + assert_eq!( + from_json::(br#""8.76""#).unwrap(), + SignedDecimal::percent(876) + ); + assert_eq!( + from_json::(br#""87.65""#).unwrap(), + SignedDecimal::percent(8765) + ); + + // negative numbers + assert_eq!( + from_json::(br#""-0""#).unwrap(), + SignedDecimal::zero() + ); + assert_eq!( + from_json::(br#""-1""#).unwrap(), + SignedDecimal::negative_one() + ); + assert_eq!( + from_json::(br#""-001""#).unwrap(), + SignedDecimal::negative_one() + ); + assert_eq!( + from_json::(br#""-0.08""#).unwrap(), + SignedDecimal::percent(-8) + ); + } + + #[test] + fn signed_decimal_abs_diff_works() { + let a = SignedDecimal::percent(285); + let b = SignedDecimal::percent(200); + let expected = Decimal::percent(85); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let a = SignedDecimal::percent(-200); + let b = SignedDecimal::percent(200); + let expected = Decimal::percent(400); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let a = SignedDecimal::percent(-200); + let b = SignedDecimal::percent(-240); + let expected = Decimal::percent(40); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_rem_works() { + // 4.02 % 1.11 = 0.69 + assert_eq!( + SignedDecimal::percent(402) % SignedDecimal::percent(111), + SignedDecimal::percent(69) + ); + + // 15.25 % 4 = 3.25 + assert_eq!( + SignedDecimal::percent(1525) % SignedDecimal::percent(400), + SignedDecimal::percent(325) + ); + + // -20.25 % 5 = -25 + assert_eq!( + SignedDecimal::percent(-2025) % SignedDecimal::percent(500), + SignedDecimal::percent(-25) + ); + + let a = SignedDecimal::percent(318); + let b = SignedDecimal::percent(317); + let expected = SignedDecimal::percent(1); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + fn signed_decimal_rem_assign_works() { + let mut a = SignedDecimal::percent(17673); + a %= SignedDecimal::percent(2362); + assert_eq!(a, SignedDecimal::percent(1139)); // 176.73 % 23.62 = 11.39 + + let mut a = SignedDecimal::percent(4262); + let b = SignedDecimal::percent(1270); + a %= &b; + assert_eq!(a, SignedDecimal::percent(452)); // 42.62 % 12.7 = 4.52 + + let mut a = SignedDecimal::percent(-4262); + let b = SignedDecimal::percent(1270); + a %= &b; + assert_eq!(a, SignedDecimal::percent(-452)); // -42.62 % 12.7 = -4.52 + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn signed_decimal_rem_panics_for_zero() { + let _ = SignedDecimal::percent(777) % SignedDecimal::zero(); + } + + #[test] + fn signed_decimal_checked_methods() { + // checked add + assert_eq!( + SignedDecimal::percent(402) + .checked_add(SignedDecimal::percent(111)) + .unwrap(), + SignedDecimal::percent(513) + ); + assert!(matches!( + SignedDecimal::MAX.checked_add(SignedDecimal::percent(1)), + Err(OverflowError { .. }) + )); + assert!(matches!( + SignedDecimal::MIN.checked_add(SignedDecimal::percent(-1)), + Err(OverflowError { .. }) + )); + + // checked sub + assert_eq!( + SignedDecimal::percent(1111) + .checked_sub(SignedDecimal::percent(111)) + .unwrap(), + SignedDecimal::percent(1000) + ); + assert_eq!( + SignedDecimal::zero() + .checked_sub(SignedDecimal::percent(1)) + .unwrap(), + SignedDecimal::percent(-1) + ); + assert!(matches!( + SignedDecimal::MIN.checked_sub(SignedDecimal::percent(1)), + Err(OverflowError { .. }) + )); + assert!(matches!( + SignedDecimal::MAX.checked_sub(SignedDecimal::percent(-1)), + Err(OverflowError { .. }) + )); + + // checked div + assert_eq!( + SignedDecimal::percent(30) + .checked_div(SignedDecimal::percent(200)) + .unwrap(), + SignedDecimal::percent(15) + ); + assert_eq!( + SignedDecimal::percent(88) + .checked_div(SignedDecimal::percent(20)) + .unwrap(), + SignedDecimal::percent(440) + ); + assert!(matches!( + SignedDecimal::MAX.checked_div(SignedDecimal::zero()), + Err(CheckedFromRatioError::DivideByZero {}) + )); + assert!(matches!( + SignedDecimal::MAX.checked_div(SignedDecimal::percent(1)), + Err(CheckedFromRatioError::Overflow {}) + )); + assert_eq!( + SignedDecimal::percent(-88) + .checked_div(SignedDecimal::percent(20)) + .unwrap(), + SignedDecimal::percent(-440) + ); + assert_eq!( + SignedDecimal::percent(-88) + .checked_div(SignedDecimal::percent(-20)) + .unwrap(), + SignedDecimal::percent(440) + ); + + // checked rem + assert_eq!( + SignedDecimal::percent(402) + .checked_rem(SignedDecimal::percent(111)) + .unwrap(), + SignedDecimal::percent(69) + ); + assert_eq!( + SignedDecimal::percent(1525) + .checked_rem(SignedDecimal::percent(400)) + .unwrap(), + SignedDecimal::percent(325) + ); + assert_eq!( + SignedDecimal::percent(-1525) + .checked_rem(SignedDecimal::percent(400)) + .unwrap(), + SignedDecimal::percent(-325) + ); + assert_eq!( + SignedDecimal::percent(-1525) + .checked_rem(SignedDecimal::percent(-400)) + .unwrap(), + SignedDecimal::percent(-325) + ); + assert!(matches!( + SignedDecimal::MAX.checked_rem(SignedDecimal::zero()), + Err(DivideByZeroError { .. }) + )); + } + + #[test] + fn signed_decimal_pow_works() { + assert_eq!( + SignedDecimal::percent(200).pow(2), + SignedDecimal::percent(400) + ); + assert_eq!( + SignedDecimal::percent(-200).pow(2), + SignedDecimal::percent(400) + ); + assert_eq!( + SignedDecimal::percent(-200).pow(3), + SignedDecimal::percent(-800) + ); + assert_eq!( + SignedDecimal::percent(200).pow(10), + SignedDecimal::percent(102400) + ); + } + + #[test] + #[should_panic] + fn signed_decimal_pow_overflow_panics() { + _ = SignedDecimal::MAX.pow(2u32); + } + + #[test] + fn signed_decimal_saturating_works() { + assert_eq!( + SignedDecimal::percent(200).saturating_add(SignedDecimal::percent(200)), + SignedDecimal::percent(400) + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_add(SignedDecimal::percent(200)), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_add(SignedDecimal::percent(-200)), + SignedDecimal::percent(-400) + ); + assert_eq!( + SignedDecimal::MAX.saturating_add(SignedDecimal::percent(200)), + SignedDecimal::MAX + ); + assert_eq!( + SignedDecimal::MIN.saturating_add(SignedDecimal::percent(-1)), + SignedDecimal::MIN + ); + assert_eq!( + SignedDecimal::percent(200).saturating_sub(SignedDecimal::percent(100)), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_sub(SignedDecimal::percent(100)), + SignedDecimal::percent(-300) + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_sub(SignedDecimal::percent(-100)), + SignedDecimal::percent(-100) + ); + assert_eq!( + SignedDecimal::zero().saturating_sub(SignedDecimal::percent(200)), + SignedDecimal::from_str("-2").unwrap() + ); + assert_eq!( + SignedDecimal::MIN.saturating_sub(SignedDecimal::percent(200)), + SignedDecimal::MIN + ); + assert_eq!( + SignedDecimal::MAX.saturating_sub(SignedDecimal::percent(-200)), + SignedDecimal::MAX + ); + assert_eq!( + SignedDecimal::percent(200).saturating_mul(SignedDecimal::percent(50)), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_mul(SignedDecimal::percent(50)), + SignedDecimal::percent(-100) + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_mul(SignedDecimal::percent(-50)), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::MAX.saturating_mul(SignedDecimal::percent(200)), + SignedDecimal::MAX + ); + assert_eq!( + SignedDecimal::MIN.saturating_mul(SignedDecimal::percent(200)), + SignedDecimal::MIN + ); + assert_eq!( + SignedDecimal::MIN.saturating_mul(SignedDecimal::percent(-200)), + SignedDecimal::MAX + ); + assert_eq!( + SignedDecimal::percent(400).saturating_pow(2u32), + SignedDecimal::percent(1600) + ); + assert_eq!(SignedDecimal::MAX.saturating_pow(2u32), SignedDecimal::MAX); + assert_eq!(SignedDecimal::MAX.saturating_pow(3u32), SignedDecimal::MAX); + assert_eq!(SignedDecimal::MIN.saturating_pow(2u32), SignedDecimal::MAX); + assert_eq!(SignedDecimal::MIN.saturating_pow(3u32), SignedDecimal::MIN); + } + + #[test] + fn signed_decimal_rounding() { + assert_eq!(SignedDecimal::one().floor(), SignedDecimal::one()); + assert_eq!(SignedDecimal::percent(150).floor(), SignedDecimal::one()); + assert_eq!(SignedDecimal::percent(199).floor(), SignedDecimal::one()); + assert_eq!( + SignedDecimal::percent(200).floor(), + SignedDecimal::percent(200) + ); + assert_eq!(SignedDecimal::percent(99).floor(), SignedDecimal::zero()); + assert_eq!( + SignedDecimal(Int128::from(1i128)).floor(), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal(Int128::from(-1i128)).floor(), + SignedDecimal::negative_one() + ); + assert_eq!( + SignedDecimal::permille(-1234).floor(), + SignedDecimal::percent(-200) + ); + + assert_eq!(SignedDecimal::one().ceil(), SignedDecimal::one()); + assert_eq!( + SignedDecimal::percent(150).ceil(), + SignedDecimal::percent(200) + ); + assert_eq!( + SignedDecimal::percent(199).ceil(), + SignedDecimal::percent(200) + ); + assert_eq!(SignedDecimal::percent(99).ceil(), SignedDecimal::one()); + assert_eq!( + SignedDecimal(Int128::from(1i128)).ceil(), + SignedDecimal::one() + ); + assert_eq!( + SignedDecimal(Int128::from(-1i128)).ceil(), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::permille(-1234).ceil(), + SignedDecimal::negative_one() + ); + + assert_eq!(SignedDecimal::one().trunc(), SignedDecimal::one()); + assert_eq!(SignedDecimal::percent(150).trunc(), SignedDecimal::one()); + assert_eq!(SignedDecimal::percent(199).trunc(), SignedDecimal::one()); + assert_eq!( + SignedDecimal::percent(200).trunc(), + SignedDecimal::percent(200) + ); + assert_eq!(SignedDecimal::percent(99).trunc(), SignedDecimal::zero()); + assert_eq!( + SignedDecimal(Int128::from(1i128)).trunc(), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal(Int128::from(-1i128)).trunc(), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::permille(-1234).trunc(), + SignedDecimal::negative_one() + ); + } + + #[test] + #[should_panic(expected = "attempt to ceil with overflow")] + fn signed_decimal_ceil_panics() { + let _ = SignedDecimal::MAX.ceil(); + } + + #[test] + #[should_panic(expected = "attempt to floor with overflow")] + fn signed_decimal_floor_panics() { + let _ = SignedDecimal::MIN.floor(); + } + + #[test] + fn signed_decimal_checked_ceil() { + assert_eq!( + SignedDecimal::percent(199).checked_ceil(), + Ok(SignedDecimal::percent(200)) + ); + assert_eq!(SignedDecimal::MAX.checked_ceil(), Err(RoundUpOverflowError)); + } + + #[test] + fn signed_decimal_checked_floor() { + assert_eq!( + SignedDecimal::percent(199).checked_floor(), + Ok(SignedDecimal::one()) + ); + assert_eq!( + SignedDecimal::percent(-199).checked_floor(), + Ok(SignedDecimal::percent(-200)) + ); + assert_eq!( + SignedDecimal::MIN.checked_floor(), + Err(RoundDownOverflowError) + ); + assert_eq!( + SignedDecimal::negative_one().checked_floor(), + Ok(SignedDecimal::negative_one()) + ); + } + + #[test] + fn signed_decimal_to_int_floor_works() { + let d = SignedDecimal::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(12)); + let d = SignedDecimal::from_str("12.345").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(12)); + let d = SignedDecimal::from_str("12.999").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(12)); + let d = SignedDecimal::from_str("0.98451384").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(0)); + let d = SignedDecimal::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(-13)); + let d = SignedDecimal::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(-13)); + let d = SignedDecimal::from_str("75.0").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(75)); + let d = SignedDecimal::from_str("0.0001").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(0)); + let d = SignedDecimal::from_str("0.0").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(0)); + let d = SignedDecimal::from_str("-0.0").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(0)); + let d = SignedDecimal::from_str("-0.0001").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(-1)); + let d = SignedDecimal::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(-75)); + let d = SignedDecimal::MAX; + assert_eq!(d.to_int_floor(), Int128::new(170141183460469231731)); + let d = SignedDecimal::MIN; + assert_eq!(d.to_int_floor(), Int128::new(-170141183460469231732)); + } + + #[test] + fn signed_decimal_to_int_ceil_works() { + let d = SignedDecimal::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(13)); + let d = SignedDecimal::from_str("12.345").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(13)); + let d = SignedDecimal::from_str("12.999").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(13)); + let d = SignedDecimal::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(-12)); + let d = SignedDecimal::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(-12)); + + let d = SignedDecimal::from_str("75.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(75)); + let d = SignedDecimal::from_str("0.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(0)); + let d = SignedDecimal::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(-75)); + + let d = SignedDecimal::MAX; + assert_eq!(d.to_int_ceil(), Int128::new(170141183460469231732)); + let d = SignedDecimal::MIN; + assert_eq!(d.to_int_ceil(), Int128::new(-170141183460469231731)); + } + + #[test] + fn signed_decimal_to_int_trunc_works() { + let d = SignedDecimal::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(12)); + let d = SignedDecimal::from_str("12.345").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(12)); + let d = SignedDecimal::from_str("12.999").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(12)); + let d = SignedDecimal::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(-12)); + let d = SignedDecimal::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(-12)); + + let d = SignedDecimal::from_str("75.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(75)); + let d = SignedDecimal::from_str("0.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(0)); + let d = SignedDecimal::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(-75)); + + let d = SignedDecimal::MAX; + assert_eq!(d.to_int_trunc(), Int128::new(170141183460469231731)); + let d = SignedDecimal::MIN; + assert_eq!(d.to_int_trunc(), Int128::new(-170141183460469231731)); + } + + #[test] + fn signed_decimal_neg_works() { + assert_eq!(-SignedDecimal::percent(50), SignedDecimal::percent(-50)); + assert_eq!(-SignedDecimal::one(), SignedDecimal::negative_one()); + } + + #[test] + fn signed_decimal_partial_eq() { + let test_cases = [ + ("1", "1", true), + ("0.5", "0.5", true), + ("0.5", "0.51", false), + ("0", "0.00000", true), + ("-1", "-1", true), + ("-0.5", "-0.5", true), + ("-0.5", "0.5", false), + ("-0.5", "-0.51", false), + ("-0", "-0.00000", true), + ] + .into_iter() + .map(|(lhs, rhs, expected)| (dec(lhs), dec(rhs), expected)); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } + + #[test] + fn signed_decimal_implements_debug() { + let decimal = SignedDecimal::from_str("123.45").unwrap(); + assert_eq!(format!("{decimal:?}"), "SignedDecimal(123.45)"); + + let test_cases = ["5", "5.01", "42", "0", "2", "-0.000001"]; + for s in test_cases { + let decimal = SignedDecimal::from_str(s).unwrap(); + let expected = format!("SignedDecimal({s})"); + assert_eq!(format!("{decimal:?}"), expected); + } + } + + #[test] + fn signed_decimal_can_be_instantiated_from_decimal256() { + let d: SignedDecimal = Decimal256::zero().try_into().unwrap(); + assert_eq!(d, SignedDecimal::zero()); + } + + #[test] + fn signed_decimal_may_fail_when_instantiated_from_decimal256() { + let err = >::try_into(Decimal256::MAX).unwrap_err(); + assert_eq!("SignedDecimalRangeExceeded", format!("{err:?}")); + assert_eq!("SignedDecimal range exceeded", format!("{err}")); + } + + #[test] + fn signed_decimal_can_be_serialized_and_deserialized() { + // properly deserialized + let value: SignedDecimal = serde_json::from_str(r#""123""#).unwrap(); + assert_eq!(SignedDecimal::from_str("123").unwrap(), value); + + // properly serialized + let value = SignedDecimal::from_str("456").unwrap(); + assert_eq!(r#""456""#, serde_json::to_string(&value).unwrap()); + + // invalid: not a string encoded decimal + assert_eq!( + "invalid type: integer `123`, expected string-encoded decimal at line 1 column 3", + serde_json::from_str::("123") + .err() + .unwrap() + .to_string() + ); + + // invalid: not properly defined signed decimal value + assert_eq!( + "Error parsing decimal '1.e': Generic error: Error parsing fractional at line 1 column 5", + serde_json::from_str::(r#""1.e""#) + .err() + .unwrap() + .to_string() + ); + } + + #[test] + fn signed_decimal_has_defined_json_schema() { + let schema = schema_for!(SignedDecimal); + assert_eq!( + "SignedDecimal", + schema.schema.metadata.unwrap().title.unwrap() + ); + } +} diff --git a/packages/std/src/math/signed_decimal_256.rs b/packages/std/src/math/signed_decimal_256.rs new file mode 100644 index 000000000..9468729c0 --- /dev/null +++ b/packages/std/src/math/signed_decimal_256.rs @@ -0,0 +1,3273 @@ +use core::cmp::Ordering; +use core::fmt::{self, Write}; +use core::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, +}; +use core::str::FromStr; +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use thiserror::Error; + +use crate::errors::{ + CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, + OverflowOperation, RoundDownOverflowError, RoundUpOverflowError, StdError, +}; +use crate::{forward_ref_partial_eq, Decimal, Decimal256, Int512, SignedDecimal}; + +use super::Fraction; +use super::Int256; + +/// A signed fixed-point decimal value with 18 fractional digits, +/// i.e. SignedDecimal256(1_000_000_000_000_000_000) == 1.0 +/// +/// The greatest possible value that can be represented is +/// 57896044618658097711785492504343953926634992332820282019728.792003956564819967 +/// (which is (2^255 - 1) / 10^18) +/// and the smallest is +/// -57896044618658097711785492504343953926634992332820282019728.792003956564819968 +/// (which is -2^255 / 10^18). +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct SignedDecimal256(#[schemars(with = "String")] Int256); + +forward_ref_partial_eq!(SignedDecimal256, SignedDecimal256); + +#[derive(Error, Debug, PartialEq, Eq)] +#[error("SignedDecimal256 range exceeded")] +pub struct SignedDecimal256RangeExceeded; + +impl SignedDecimal256 { + const DECIMAL_FRACTIONAL: Int256 = // 1*10**18 + Int256::from_i128(1_000_000_000_000_000_000); + const DECIMAL_FRACTIONAL_SQUARED: Int256 = // 1*10**36 + Int256::from_i128(1_000_000_000_000_000_000_000_000_000_000_000_000); + + /// The number of decimal places. Since decimal types are fixed-point rather than + /// floating-point, this is a constant. + pub const DECIMAL_PLACES: u32 = 18; // This needs to be an even number. + + /// The largest value that can be represented by this signed decimal type. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// assert_eq!( + /// SignedDecimal256::MAX.to_string(), + /// "57896044618658097711785492504343953926634992332820282019728.792003956564819967" + /// ); + /// ``` + pub const MAX: Self = Self(Int256::MAX); + + /// The smallest value that can be represented by this signed decimal type. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// assert_eq!( + /// SignedDecimal256::MIN.to_string(), + /// "-57896044618658097711785492504343953926634992332820282019728.792003956564819968" + /// ); + /// ``` + pub const MIN: Self = Self(Int256::MIN); + + /// Creates a SignedDecimal256(value) + /// This is equivalent to `SignedDecimal256::from_atomics(value, 18)` but usable in a const context. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal256, Int256}; + /// assert_eq!(SignedDecimal256::new(Int256::one()).to_string(), "0.000000000000000001"); + /// ``` + pub const fn new(value: Int256) -> Self { + Self(value) + } + + /// Creates a SignedDecimal256(Int256(value)) + /// This is equivalent to `SignedDecimal256::from_atomics(value, 18)` but usable in a const context. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// assert_eq!(SignedDecimal256::raw(1234i128).to_string(), "0.000000000000001234"); + /// ``` + pub const fn raw(value: i128) -> Self { + Self(Int256::from_i128(value)) + } + + /// Create a 1.0 SignedDecimal256 + #[inline] + pub const fn one() -> Self { + Self(Self::DECIMAL_FRACTIONAL) + } + + /// Create a -1.0 SignedDecimal256 + #[inline] + pub const fn negative_one() -> Self { + // -DECIMAL_FRATIONAL + Self(Int256::from_i128(-1_000_000_000_000_000_000)) + } + + /// Create a 0.0 SignedDecimal256 + #[inline] + pub const fn zero() -> Self { + Self(Int256::zero()) + } + + /// Convert x% into SignedDecimal256 + pub fn percent(x: i64) -> Self { + Self(((x as i128) * 10_000_000_000_000_000).into()) + } + + /// Convert permille (x/1000) into SignedDecimal256 + pub fn permille(x: i64) -> Self { + Self(((x as i128) * 1_000_000_000_000_000).into()) + } + + /// Convert basis points (x/10000) into SignedDecimal256 + pub fn bps(x: i64) -> Self { + Self(((x as i128) * 100_000_000_000_000).into()) + } + + /// Creates a signed decimal from a number of atomic units and the number + /// of decimal places. The inputs will be converted internally to form + /// a signed decimal with 18 decimal places. So the input 123 and 2 will create + /// the decimal 1.23. + /// + /// Using 18 decimal places is slightly more efficient than other values + /// as no internal conversion is necessary. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal256, Int256}; + /// let a = SignedDecimal256::from_atomics(Int256::from(1234), 3).unwrap(); + /// assert_eq!(a.to_string(), "1.234"); + /// + /// let a = SignedDecimal256::from_atomics(1234i128, 0).unwrap(); + /// assert_eq!(a.to_string(), "1234"); + /// + /// let a = SignedDecimal256::from_atomics(1i64, 18).unwrap(); + /// assert_eq!(a.to_string(), "0.000000000000000001"); + /// + /// let a = SignedDecimal256::from_atomics(-1i64, 18).unwrap(); + /// assert_eq!(a.to_string(), "-0.000000000000000001"); + /// ``` + pub fn from_atomics( + atomics: impl Into, + decimal_places: u32, + ) -> Result { + let atomics = atomics.into(); + let ten = Int256::from(10u64); + Ok(match decimal_places.cmp(&(Self::DECIMAL_PLACES)) { + Ordering::Less => { + let digits = (Self::DECIMAL_PLACES) - decimal_places; // No overflow because decimal_places < DECIMAL_PLACES + let factor = ten.checked_pow(digits).unwrap(); // Safe because digits <= 17 + Self( + atomics + .checked_mul(factor) + .map_err(|_| SignedDecimal256RangeExceeded)?, + ) + } + Ordering::Equal => Self(atomics), + Ordering::Greater => { + let digits = decimal_places - (Self::DECIMAL_PLACES); // No overflow because decimal_places > DECIMAL_PLACES + if let Ok(factor) = ten.checked_pow(digits) { + Self(atomics.checked_div(factor).unwrap()) // Safe because factor cannot be zero + } else { + // In this case `factor` exceeds the Int256 range. + // Any Int256 `x` divided by `factor` with `factor > Int256::MAX` is 0. + // Try e.g. Python3: `(2**128-1) // 2**128` + Self(Int256::zero()) + } + } + }) + } + + /// Returns the ratio (numerator / denominator) as a SignedDecimal256 + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// assert_eq!( + /// SignedDecimal256::from_ratio(1, 3).to_string(), + /// "0.333333333333333333" + /// ); + /// ``` + pub fn from_ratio(numerator: impl Into, denominator: impl Into) -> Self { + match SignedDecimal256::checked_from_ratio(numerator, denominator) { + Ok(value) => value, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => panic!("Multiplication overflow"), + } + } + + /// Returns the ratio (numerator / denominator) as a SignedDecimal256 + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal256, CheckedFromRatioError}; + /// assert_eq!( + /// SignedDecimal256::checked_from_ratio(1, 3).unwrap().to_string(), + /// "0.333333333333333333" + /// ); + /// assert_eq!( + /// SignedDecimal256::checked_from_ratio(1, 0), + /// Err(CheckedFromRatioError::DivideByZero) + /// ); + /// ``` + pub fn checked_from_ratio( + numerator: impl Into, + denominator: impl Into, + ) -> Result { + let numerator: Int256 = numerator.into(); + let denominator: Int256 = denominator.into(); + match numerator.checked_multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator) { + Ok(ratio) => { + // numerator * DECIMAL_FRACTIONAL / denominator + Ok(SignedDecimal256(ratio)) + } + Err(CheckedMultiplyRatioError::Overflow) => Err(CheckedFromRatioError::Overflow), + Err(CheckedMultiplyRatioError::DivideByZero) => { + Err(CheckedFromRatioError::DivideByZero) + } + } + } + + /// Returns `true` if the number is 0 + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0.is_zero() + } + + /// Returns `true` if the number is negative (< 0) + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.is_negative() + } + + /// A decimal is an integer of atomic units plus a number that specifies the + /// position of the decimal dot. So any decimal can be expressed as two numbers. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal256, Int256}; + /// # use core::str::FromStr; + /// // Value with whole and fractional part + /// let a = SignedDecimal256::from_str("1.234").unwrap(); + /// assert_eq!(a.decimal_places(), 18); + /// assert_eq!(a.atomics(), Int256::from(1234000000000000000i128)); + /// + /// // Smallest possible value + /// let b = SignedDecimal256::from_str("0.000000000000000001").unwrap(); + /// assert_eq!(b.decimal_places(), 18); + /// assert_eq!(b.atomics(), Int256::from(1)); + /// ``` + #[must_use] + #[inline] + pub const fn atomics(&self) -> Int256 { + self.0 + } + + /// The number of decimal places. This is a constant value for now + /// but this could potentially change as the type evolves. + /// + /// See also [`SignedDecimal256::atomics()`]. + #[must_use] + #[inline] + pub const fn decimal_places(&self) -> u32 { + Self::DECIMAL_PLACES + } + + /// Rounds value by truncating the decimal places. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// # use core::str::FromStr; + /// assert!(SignedDecimal256::from_str("0.6").unwrap().trunc().is_zero()); + /// assert_eq!(SignedDecimal256::from_str("-5.8").unwrap().trunc().to_string(), "-5"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn trunc(&self) -> Self { + Self((self.0 / Self::DECIMAL_FRACTIONAL) * Self::DECIMAL_FRACTIONAL) + } + + /// Rounds value down after decimal places. Panics on overflow. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// # use core::str::FromStr; + /// assert!(SignedDecimal256::from_str("0.6").unwrap().floor().is_zero()); + /// assert_eq!(SignedDecimal256::from_str("-5.2").unwrap().floor().to_string(), "-6"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn floor(&self) -> Self { + match self.checked_floor() { + Ok(value) => value, + Err(_) => panic!("attempt to floor with overflow"), + } + } + + /// Rounds value down after decimal places. + pub fn checked_floor(&self) -> Result { + if self.is_negative() { + let truncated = self.trunc(); + + if truncated != self { + truncated + .checked_sub(SignedDecimal256::one()) + .map_err(|_| RoundDownOverflowError) + } else { + Ok(truncated) + } + } else { + Ok(self.trunc()) + } + } + + /// Rounds value up after decimal places. Panics on overflow. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// # use core::str::FromStr; + /// assert_eq!(SignedDecimal256::from_str("0.2").unwrap().ceil(), SignedDecimal256::one()); + /// assert_eq!(SignedDecimal256::from_str("-5.8").unwrap().ceil().to_string(), "-5"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn ceil(&self) -> Self { + match self.checked_ceil() { + Ok(value) => value, + Err(_) => panic!("attempt to ceil with overflow"), + } + } + + /// Rounds value up after decimal places. Returns OverflowError on overflow. + pub fn checked_ceil(&self) -> Result { + let floor = self.floor(); + if floor == self { + Ok(floor) + } else { + floor + .checked_add(SignedDecimal256::one()) + .map_err(|_| RoundUpOverflowError) + } + } + + /// Computes `self + other`, returning an `OverflowError` if an overflow occurred. + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Add, self, other)) + } + + /// Computes `self - other`, returning an `OverflowError` if an overflow occurred. + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + /// Multiplies one `SignedDecimal256` by another, returning an `OverflowError` if an overflow occurred. + pub fn checked_mul(self, other: Self) -> Result { + let result_as_int512 = + self.numerator().full_mul(other.numerator()) / Int512::from(Self::DECIMAL_FRACTIONAL); + result_as_int512 + .try_into() + .map(Self) + .map_err(|_| OverflowError { + operation: OverflowOperation::Mul, + operand1: self.to_string(), + operand2: other.to_string(), + }) + } + + /// Raises a value to the power of `exp`, panics if an overflow occurred. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => panic!("Multiplication overflow"), + } + } + + /// Raises a value to the power of `exp`, returning an `OverflowError` if an overflow occurred. + pub fn checked_pow(self, exp: u32) -> Result { + // This uses the exponentiation by squaring algorithm: + // https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Basic_method + + fn inner(mut x: SignedDecimal256, mut n: u32) -> Result { + if n == 0 { + return Ok(SignedDecimal256::one()); + } + + let mut y = SignedDecimal256::one(); + + while n > 1 { + if n % 2 == 0 { + x = x.checked_mul(x)?; + n /= 2; + } else { + y = x.checked_mul(y)?; + x = x.checked_mul(x)?; + n = (n - 1) / 2; + } + } + + Ok(x * y) + } + + inner(self, exp).map_err(|_| OverflowError { + operation: OverflowOperation::Pow, + operand1: self.to_string(), + operand2: exp.to_string(), + }) + } + + pub fn checked_div(self, other: Self) -> Result { + SignedDecimal256::checked_from_ratio(self.numerator(), other.numerator()) + } + + /// Computes `self % other`, returning an `DivideByZeroError` if `other == 0`. + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .map_err(|_| DivideByZeroError::new(self)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs_diff(self, other: Self) -> Decimal256 { + Decimal256::new(self.0.abs_diff(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + match self.checked_mul(other) { + Ok(value) => value, + Err(_) => { + // both negative or both positive results in positive number, otherwise negative + if self.is_negative() == other.is_negative() { + Self::MAX + } else { + Self::MIN + } + } + } + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => { + // odd exponent of negative number results in negative number + // everything else results in positive number + if self.is_negative() && exp % 2 == 1 { + Self::MIN + } else { + Self::MAX + } + } + } + } + + /// Converts this decimal to a signed integer by rounding down + /// to the next integer, e.g. 22.5 becomes 22 and -1.2 becomes -2. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal256, Int256}; + /// + /// let d = SignedDecimal256::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_floor(), Int256::from(12)); + /// + /// let d = SignedDecimal256::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_floor(), Int256::from(-13)); + /// + /// let d = SignedDecimal256::from_str("-0.05").unwrap(); + /// assert_eq!(d.to_int_floor(), Int256::from(-1)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_floor(self) -> Int256 { + if self.is_negative() { + // Using `x.to_int_floor() = -(-x).to_int_ceil()` for a negative `x`, + // but avoiding overflow by implementing the formula from `to_int_ceil` directly. + let x = self.0; + let y = Self::DECIMAL_FRACTIONAL; + // making sure not to negate `x`, as this would overflow + -Int256::one() - ((-Int256::one() - x) / y) + } else { + self.to_int_trunc() + } + } + + /// Converts this decimal to a signed integer by truncating + /// the fractional part, e.g. 22.5 becomes 22. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal256, Int256}; + /// + /// let d = SignedDecimal256::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int256::from(12)); + /// + /// let d = SignedDecimal256::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int256::from(-12)); + /// + /// let d = SignedDecimal256::from_str("75.0").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int256::from(75)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_trunc(self) -> Int256 { + self.0 / Self::DECIMAL_FRACTIONAL + } + + /// Converts this decimal to a signed integer by rounding up + /// to the next integer, e.g. 22.3 becomes 23 and -1.2 becomes -1. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal256, Int256}; + /// + /// let d = SignedDecimal256::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int256::from(13)); + /// + /// let d = SignedDecimal256::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int256::from(-12)); + /// + /// let d = SignedDecimal256::from_str("75.0").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int256::from(75)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_ceil(self) -> Int256 { + if self.is_negative() { + self.to_int_trunc() + } else { + // Using `q = 1 + ((x - 1) / y); // if x != 0` with unsigned integers x, y, q + // from https://stackoverflow.com/a/2745086/2013738. We know `x + y` CAN overflow. + let x = self.0; + let y = Self::DECIMAL_FRACTIONAL; + if x.is_zero() { + Int256::zero() + } else { + Int256::one() + ((x - Int256::one()) / y) + } + } + } +} + +impl Fraction for SignedDecimal256 { + #[inline] + fn numerator(&self) -> Int256 { + self.0 + } + + #[inline] + fn denominator(&self) -> Int256 { + Self::DECIMAL_FRACTIONAL + } + + /// Returns the multiplicative inverse `1/d` for decimal `d`. + /// + /// If `d` is zero, none is returned. + fn inv(&self) -> Option { + if self.is_zero() { + None + } else { + // Let self be p/q with p = self.0 and q = DECIMAL_FRACTIONAL. + // Now we calculate the inverse a/b = q/p such that b = DECIMAL_FRACTIONAL. Then + // `a = DECIMAL_FRACTIONAL*DECIMAL_FRACTIONAL / self.0`. + Some(SignedDecimal256(Self::DECIMAL_FRACTIONAL_SQUARED / self.0)) + } + } +} + +impl Neg for SignedDecimal256 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl From for SignedDecimal256 { + fn from(value: SignedDecimal) -> Self { + Self::new(value.atomics().into()) + } +} + +impl From for SignedDecimal256 { + fn from(value: Decimal) -> Self { + Self::new(value.atomics().into()) + } +} + +impl TryFrom for SignedDecimal256 { + type Error = SignedDecimal256RangeExceeded; + + fn try_from(value: Decimal256) -> Result { + value + .atomics() + .try_into() + .map(SignedDecimal256) + .map_err(|_| SignedDecimal256RangeExceeded) + } +} + +impl FromStr for SignedDecimal256 { + type Err = StdError; + + /// Converts the decimal string to a SignedDecimal256 + /// Possible inputs: "1.23", "1", "000012", "1.123000000", "-1.12300" + /// Disallowed: "", ".23" + /// + /// This never performs any kind of rounding. + /// More than DECIMAL_PLACES fractional digits, even zeros, result in an error. + fn from_str(input: &str) -> Result { + let mut parts_iter = input.split('.'); + + let whole_part = parts_iter.next().unwrap(); // split always returns at least one element + let is_neg = whole_part.starts_with('-'); + + let whole = whole_part + .parse::() + .map_err(|_| StdError::generic_err("Error parsing whole"))?; + let mut atomics = whole + .checked_mul(Self::DECIMAL_FRACTIONAL) + .map_err(|_| StdError::generic_err("Value too big"))?; + + if let Some(fractional_part) = parts_iter.next() { + let fractional = fractional_part + .parse::() // u64 is enough for 18 decimal places + .map_err(|_| StdError::generic_err("Error parsing fractional"))?; + let exp = (Self::DECIMAL_PLACES.checked_sub(fractional_part.len() as u32)).ok_or_else( + || { + StdError::generic_err(format!( + "Cannot parse more than {} fractional digits", + Self::DECIMAL_PLACES + )) + }, + )?; + debug_assert!(exp <= Self::DECIMAL_PLACES); + let fractional_factor = Int256::from(10i128.pow(exp)); + + // This multiplication can't overflow because + // fractional < 10^DECIMAL_PLACES && fractional_factor <= 10^DECIMAL_PLACES + let fractional_part = Int256::from(fractional) + .checked_mul(fractional_factor) + .unwrap(); + + // for negative numbers, we need to subtract the fractional part + atomics = if is_neg { + atomics.checked_sub(fractional_part) + } else { + atomics.checked_add(fractional_part) + } + .map_err(|_| StdError::generic_err("Value too big"))?; + } + + if parts_iter.next().is_some() { + return Err(StdError::generic_err("Unexpected number of dots")); + } + + Ok(SignedDecimal256(atomics)) + } +} + +impl fmt::Display for SignedDecimal256 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let whole = (self.0) / Self::DECIMAL_FRACTIONAL; + let fractional = (self.0).checked_rem(Self::DECIMAL_FRACTIONAL).unwrap(); + + if fractional.is_zero() { + write!(f, "{whole}") + } else { + let fractional_string = format!( + "{:0>padding$}", + fractional.abs(), // fractional should always be printed as positive + padding = Self::DECIMAL_PLACES as usize + ); + if self.is_negative() { + f.write_char('-')?; + } + write!( + f, + "{whole}.{fractional}", + whole = whole.abs(), + fractional = fractional_string.trim_end_matches('0') + ) + } + } +} + +impl fmt::Debug for SignedDecimal256 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SignedDecimal256({self})") + } +} + +impl Add for SignedDecimal256 { + type Output = Self; + + fn add(self, other: Self) -> Self { + SignedDecimal256(self.0 + other.0) + } +} +forward_ref_binop!(impl Add, add for SignedDecimal256, SignedDecimal256); + +impl AddAssign for SignedDecimal256 { + fn add_assign(&mut self, rhs: SignedDecimal256) { + *self = *self + rhs; + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for SignedDecimal256, SignedDecimal256); + +impl Sub for SignedDecimal256 { + type Output = Self; + + fn sub(self, other: Self) -> Self { + SignedDecimal256(self.0 - other.0) + } +} +forward_ref_binop!(impl Sub, sub for SignedDecimal256, SignedDecimal256); + +impl SubAssign for SignedDecimal256 { + fn sub_assign(&mut self, rhs: SignedDecimal256) { + *self = *self - rhs; + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for SignedDecimal256, SignedDecimal256); + +impl Mul for SignedDecimal256 { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn mul(self, other: Self) -> Self { + // SignedDecimal256s are fractions. We can multiply two decimals a and b + // via + // (a.numerator() * b.numerator()) / (a.denominator() * b.denominator()) + // = (a.numerator() * b.numerator()) / a.denominator() / b.denominator() + + let result_as_int512 = + self.numerator().full_mul(other.numerator()) / Int512::from(Self::DECIMAL_FRACTIONAL); + match result_as_int512.try_into() { + Ok(result) => Self(result), + Err(_) => panic!("attempt to multiply with overflow"), + } + } +} +forward_ref_binop!(impl Mul, mul for SignedDecimal256, SignedDecimal256); + +impl MulAssign for SignedDecimal256 { + fn mul_assign(&mut self, rhs: SignedDecimal256) { + *self = *self * rhs; + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for SignedDecimal256, SignedDecimal256); + +impl Div for SignedDecimal256 { + type Output = Self; + + fn div(self, other: Self) -> Self { + match SignedDecimal256::checked_from_ratio(self.numerator(), other.numerator()) { + Ok(ratio) => ratio, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Division failed - denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => { + panic!("Division failed - multiplication overflow") + } + } + } +} +forward_ref_binop!(impl Div, div for SignedDecimal256, SignedDecimal256); + +impl DivAssign for SignedDecimal256 { + fn div_assign(&mut self, rhs: SignedDecimal256) { + *self = *self / rhs; + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for SignedDecimal256, SignedDecimal256); + +impl Div for SignedDecimal256 { + type Output = Self; + + fn div(self, rhs: Int256) -> Self::Output { + SignedDecimal256(self.0 / rhs) + } +} + +impl DivAssign for SignedDecimal256 { + fn div_assign(&mut self, rhs: Int256) { + self.0 /= rhs; + } +} + +impl Rem for SignedDecimal256 { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for SignedDecimal256, SignedDecimal256); + +impl RemAssign for SignedDecimal256 { + fn rem_assign(&mut self, rhs: SignedDecimal256) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for SignedDecimal256, SignedDecimal256); + +impl core::iter::Sum for SignedDecimal256 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +/// Serializes as a decimal string +impl Serialize for SignedDecimal256 { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +/// Deserializes as a base64 string +impl<'de> Deserialize<'de> for SignedDecimal256 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(SignedDecimal256Visitor) + } +} + +struct SignedDecimal256Visitor; + +impl<'de> de::Visitor<'de> for SignedDecimal256Visitor { + type Value = SignedDecimal256; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded decimal") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + match SignedDecimal256::from_str(v) { + Ok(d) => Ok(d), + Err(e) => Err(E::custom(format!("Error parsing decimal '{v}': {e}"))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_json, to_json_vec}; + use schemars::schema_for; + + fn dec(input: &str) -> SignedDecimal256 { + SignedDecimal256::from_str(input).unwrap() + } + + #[test] + fn signed_decimal_256_new() { + let expected = Int256::from(300i128); + assert_eq!(SignedDecimal256::new(expected).0, expected); + + let expected = Int256::from(-300i128); + assert_eq!(SignedDecimal256::new(expected).0, expected); + } + + #[test] + fn signed_decimal_256_raw() { + let value = 300i128; + assert_eq!(SignedDecimal256::raw(value).0, Int256::from(value)); + + let value = -300i128; + assert_eq!(SignedDecimal256::raw(value).0, Int256::from(value)); + } + + #[test] + fn signed_decimal_256_one() { + let value = SignedDecimal256::one(); + assert_eq!(value.0, SignedDecimal256::DECIMAL_FRACTIONAL); + } + + #[test] + fn signed_decimal_256_zero() { + let value = SignedDecimal256::zero(); + assert!(value.0.is_zero()); + } + + #[test] + fn signed_decimal_256_percent() { + let value = SignedDecimal256::percent(50); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(2u8) + ); + + let value = SignedDecimal256::percent(-50); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(-2i8) + ); + } + + #[test] + fn signed_decimal_256_permille() { + let value = SignedDecimal256::permille(125); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(8u8) + ); + + let value = SignedDecimal256::permille(-125); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(-8i8) + ); + } + + #[test] + fn signed_decimal_256_bps() { + let value = SignedDecimal256::bps(125); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(80u8) + ); + + let value = SignedDecimal256::bps(-125); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(-80i8) + ); + } + + #[test] + fn signed_decimal_256_from_atomics_works() { + let one = SignedDecimal256::one(); + let two = one + one; + let neg_one = SignedDecimal256::negative_one(); + + assert_eq!(SignedDecimal256::from_atomics(1i128, 0).unwrap(), one); + assert_eq!(SignedDecimal256::from_atomics(10i128, 1).unwrap(), one); + assert_eq!(SignedDecimal256::from_atomics(100i128, 2).unwrap(), one); + assert_eq!(SignedDecimal256::from_atomics(1000i128, 3).unwrap(), one); + assert_eq!( + SignedDecimal256::from_atomics(1000000000000000000i128, 18).unwrap(), + one + ); + assert_eq!( + SignedDecimal256::from_atomics(10000000000000000000i128, 19).unwrap(), + one + ); + assert_eq!( + SignedDecimal256::from_atomics(100000000000000000000i128, 20).unwrap(), + one + ); + + assert_eq!(SignedDecimal256::from_atomics(2i128, 0).unwrap(), two); + assert_eq!(SignedDecimal256::from_atomics(20i128, 1).unwrap(), two); + assert_eq!(SignedDecimal256::from_atomics(200i128, 2).unwrap(), two); + assert_eq!(SignedDecimal256::from_atomics(2000i128, 3).unwrap(), two); + assert_eq!( + SignedDecimal256::from_atomics(2000000000000000000i128, 18).unwrap(), + two + ); + assert_eq!( + SignedDecimal256::from_atomics(20000000000000000000i128, 19).unwrap(), + two + ); + assert_eq!( + SignedDecimal256::from_atomics(200000000000000000000i128, 20).unwrap(), + two + ); + + assert_eq!(SignedDecimal256::from_atomics(-1i128, 0).unwrap(), neg_one); + assert_eq!(SignedDecimal256::from_atomics(-10i128, 1).unwrap(), neg_one); + assert_eq!( + SignedDecimal256::from_atomics(-100000000000000000000i128, 20).unwrap(), + neg_one + ); + + // Cuts decimal digits (20 provided but only 18 can be stored) + assert_eq!( + SignedDecimal256::from_atomics(4321i128, 20).unwrap(), + SignedDecimal256::from_str("0.000000000000000043").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(-4321i128, 20).unwrap(), + SignedDecimal256::from_str("-0.000000000000000043").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(6789i128, 20).unwrap(), + SignedDecimal256::from_str("0.000000000000000067").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 38).unwrap(), + SignedDecimal256::from_str("1.701411834604692317").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 39).unwrap(), + SignedDecimal256::from_str("0.170141183460469231").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 45).unwrap(), + SignedDecimal256::from_str("0.000000170141183460").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 51).unwrap(), + SignedDecimal256::from_str("0.000000000000170141").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 56).unwrap(), + SignedDecimal256::from_str("0.000000000000000001").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 57).unwrap(), + SignedDecimal256::from_str("0.000000000000000000").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, u32::MAX).unwrap(), + SignedDecimal256::from_str("0.000000000000000000").unwrap() + ); + + // Can be used with max value + let max = SignedDecimal256::MAX; + assert_eq!( + SignedDecimal256::from_atomics(max.atomics(), max.decimal_places()).unwrap(), + max + ); + + // Can be used with min value + let min = SignedDecimal256::MIN; + assert_eq!( + SignedDecimal256::from_atomics(min.atomics(), min.decimal_places()).unwrap(), + min + ); + + // Overflow is only possible with digits < 18 + let result = SignedDecimal256::from_atomics(Int256::MAX, 17); + assert_eq!(result.unwrap_err(), SignedDecimal256RangeExceeded); + } + + #[test] + fn signed_decimal_256_from_ratio_works() { + // 1.0 + assert_eq!( + SignedDecimal256::from_ratio(1i128, 1i128), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::from_ratio(53i128, 53i128), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::from_ratio(125i128, 125i128), + SignedDecimal256::one() + ); + + // -1.0 + assert_eq!( + SignedDecimal256::from_ratio(-1i128, 1i128), + SignedDecimal256::negative_one() + ); + assert_eq!( + SignedDecimal256::from_ratio(-53i128, 53i128), + SignedDecimal256::negative_one() + ); + assert_eq!( + SignedDecimal256::from_ratio(125i128, -125i128), + SignedDecimal256::negative_one() + ); + + // 1.5 + assert_eq!( + SignedDecimal256::from_ratio(3i128, 2i128), + SignedDecimal256::percent(150) + ); + assert_eq!( + SignedDecimal256::from_ratio(150i128, 100i128), + SignedDecimal256::percent(150) + ); + assert_eq!( + SignedDecimal256::from_ratio(333i128, 222i128), + SignedDecimal256::percent(150) + ); + + // 0.125 + assert_eq!( + SignedDecimal256::from_ratio(1i64, 8i64), + SignedDecimal256::permille(125) + ); + assert_eq!( + SignedDecimal256::from_ratio(125i64, 1000i64), + SignedDecimal256::permille(125) + ); + + // -0.125 + assert_eq!( + SignedDecimal256::from_ratio(-1i64, 8i64), + SignedDecimal256::permille(-125) + ); + assert_eq!( + SignedDecimal256::from_ratio(125i64, -1000i64), + SignedDecimal256::permille(-125) + ); + + // 1/3 (result floored) + assert_eq!( + SignedDecimal256::from_ratio(1i64, 3i64), + SignedDecimal256(Int256::from(333_333_333_333_333_333i128)) + ); + + // 2/3 (result floored) + assert_eq!( + SignedDecimal256::from_ratio(2i64, 3i64), + SignedDecimal256(Int256::from(666_666_666_666_666_666i128)) + ); + + // large inputs + assert_eq!( + SignedDecimal256::from_ratio(0i128, i128::MAX), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::from_ratio(i128::MAX, i128::MAX), + SignedDecimal256::one() + ); + // 170141183460469231731 is the largest integer <= SignedDecimal256::MAX + assert_eq!( + SignedDecimal256::from_ratio(170141183460469231731i128, 1i128), + SignedDecimal256::from_str("170141183460469231731").unwrap() + ); + } + + #[test] + #[should_panic(expected = "Denominator must not be zero")] + fn signed_decimal_256_from_ratio_panics_for_zero_denominator() { + SignedDecimal256::from_ratio(1i128, 0i128); + } + + #[test] + #[should_panic(expected = "Multiplication overflow")] + fn signed_decimal_256_from_ratio_panics_for_mul_overflow() { + SignedDecimal256::from_ratio(Int256::MAX, 1i128); + } + + #[test] + fn signed_decimal_256_checked_from_ratio_does_not_panic() { + assert_eq!( + SignedDecimal256::checked_from_ratio(1i128, 0i128), + Err(CheckedFromRatioError::DivideByZero) + ); + + assert_eq!( + SignedDecimal256::checked_from_ratio(Int256::MAX, 1i128), + Err(CheckedFromRatioError::Overflow) + ); + } + + #[test] + fn signed_decimal_256_implements_fraction() { + let fraction = SignedDecimal256::from_str("1234.567").unwrap(); + assert_eq!( + fraction.numerator(), + Int256::from(1_234_567_000_000_000_000_000i128) + ); + assert_eq!( + fraction.denominator(), + Int256::from(1_000_000_000_000_000_000i128) + ); + + let fraction = SignedDecimal256::from_str("-1234.567").unwrap(); + assert_eq!( + fraction.numerator(), + Int256::from(-1_234_567_000_000_000_000_000i128) + ); + assert_eq!( + fraction.denominator(), + Int256::from(1_000_000_000_000_000_000i128) + ); + } + + #[test] + fn signed_decimal_256_from_str_works() { + // Integers + assert_eq!( + SignedDecimal256::from_str("0").unwrap(), + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::from_str("1").unwrap(), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::from_str("5").unwrap(), + SignedDecimal256::percent(500) + ); + assert_eq!( + SignedDecimal256::from_str("42").unwrap(), + SignedDecimal256::percent(4200) + ); + assert_eq!( + SignedDecimal256::from_str("000").unwrap(), + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::from_str("001").unwrap(), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::from_str("005").unwrap(), + SignedDecimal256::percent(500) + ); + assert_eq!( + SignedDecimal256::from_str("0042").unwrap(), + SignedDecimal256::percent(4200) + ); + + // Positive decimals + assert_eq!( + SignedDecimal256::from_str("1.0").unwrap(), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::from_str("1.5").unwrap(), + SignedDecimal256::percent(150) + ); + assert_eq!( + SignedDecimal256::from_str("0.5").unwrap(), + SignedDecimal256::percent(50) + ); + assert_eq!( + SignedDecimal256::from_str("0.123").unwrap(), + SignedDecimal256::permille(123) + ); + + assert_eq!( + SignedDecimal256::from_str("40.00").unwrap(), + SignedDecimal256::percent(4000) + ); + assert_eq!( + SignedDecimal256::from_str("04.00").unwrap(), + SignedDecimal256::percent(400) + ); + assert_eq!( + SignedDecimal256::from_str("00.40").unwrap(), + SignedDecimal256::percent(40) + ); + assert_eq!( + SignedDecimal256::from_str("00.04").unwrap(), + SignedDecimal256::percent(4) + ); + // Negative decimals + assert_eq!( + SignedDecimal256::from_str("-00.04").unwrap(), + SignedDecimal256::percent(-4) + ); + assert_eq!( + SignedDecimal256::from_str("-00.40").unwrap(), + SignedDecimal256::percent(-40) + ); + assert_eq!( + SignedDecimal256::from_str("-04.00").unwrap(), + SignedDecimal256::percent(-400) + ); + + // Can handle DECIMAL_PLACES fractional digits + assert_eq!( + SignedDecimal256::from_str("7.123456789012345678").unwrap(), + SignedDecimal256(Int256::from(7123456789012345678i128)) + ); + assert_eq!( + SignedDecimal256::from_str("7.999999999999999999").unwrap(), + SignedDecimal256(Int256::from(7999999999999999999i128)) + ); + + // Works for documented max value + assert_eq!( + SignedDecimal256::from_str( + "57896044618658097711785492504343953926634992332820282019728.792003956564819967" + ) + .unwrap(), + SignedDecimal256::MAX + ); + // Works for documented min value + assert_eq!( + SignedDecimal256::from_str( + "-57896044618658097711785492504343953926634992332820282019728.792003956564819968" + ) + .unwrap(), + SignedDecimal256::MIN + ); + assert_eq!( + SignedDecimal256::from_str("-1").unwrap(), + SignedDecimal256::negative_one() + ); + } + + #[test] + fn signed_decimal_256_from_str_errors_for_broken_whole_part() { + let expected_err = StdError::generic_err("Error parsing whole"); + assert_eq!(SignedDecimal256::from_str("").unwrap_err(), expected_err); + assert_eq!(SignedDecimal256::from_str(" ").unwrap_err(), expected_err); + assert_eq!(SignedDecimal256::from_str("-").unwrap_err(), expected_err); + } + + #[test] + fn signed_decimal_256_from_str_errors_for_broken_fractional_part() { + let expected_err = StdError::generic_err("Error parsing fractional"); + assert_eq!(SignedDecimal256::from_str("1.").unwrap_err(), expected_err); + assert_eq!(SignedDecimal256::from_str("1. ").unwrap_err(), expected_err); + assert_eq!(SignedDecimal256::from_str("1.e").unwrap_err(), expected_err); + assert_eq!( + SignedDecimal256::from_str("1.2e3").unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal256::from_str("1.-2").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_256_from_str_errors_for_more_than_18_fractional_digits() { + let expected_err = StdError::generic_err("Cannot parse more than 18 fractional digits"); + assert_eq!( + SignedDecimal256::from_str("7.1234567890123456789").unwrap_err(), + expected_err + ); + // No special rules for trailing zeros. This could be changed but adds gas cost for the happy path. + assert_eq!( + SignedDecimal256::from_str("7.1230000000000000000").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_256_from_str_errors_for_invalid_number_of_dots() { + let expected_err = StdError::generic_err("Unexpected number of dots"); + assert_eq!( + SignedDecimal256::from_str("1.2.3").unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal256::from_str("1.2.3.4").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_256_from_str_errors_for_more_than_max_value() { + let expected_err = StdError::generic_err("Value too big"); + // Integer + assert_eq!( + SignedDecimal256::from_str( + "57896044618658097711785492504343953926634992332820282019729", + ) + .unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal256::from_str( + "-57896044618658097711785492504343953926634992332820282019729", + ) + .unwrap_err(), + expected_err + ); + + // SignedDecimal256 + assert_eq!( + SignedDecimal256::from_str( + "57896044618658097711785492504343953926634992332820282019729.0", + ) + .unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal256::from_str( + "57896044618658097711785492504343953926634992332820282019728.792003956564819968", + ) + .unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal256::from_str( + "-57896044618658097711785492504343953926634992332820282019728.792003956564819969", + ) + .unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_256_conversions_work() { + assert_eq!( + SignedDecimal256::from(SignedDecimal::zero()), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::from(SignedDecimal::one()), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::from(SignedDecimal::percent(50)), + SignedDecimal256::percent(50) + ); + assert_eq!( + SignedDecimal256::from(SignedDecimal::MAX), + SignedDecimal256::new(Int256::from_i128(i128::MAX)) + ); + assert_eq!( + SignedDecimal256::from(SignedDecimal::percent(-50)), + SignedDecimal256::percent(-50) + ); + assert_eq!( + SignedDecimal256::from(SignedDecimal::MIN), + SignedDecimal256::new(Int256::from_i128(i128::MIN)) + ); + } + + #[test] + fn signed_decimal_256_atomics_works() { + let zero = SignedDecimal256::zero(); + let one = SignedDecimal256::one(); + let half = SignedDecimal256::percent(50); + let two = SignedDecimal256::percent(200); + let max = SignedDecimal256::MAX; + let neg_half = SignedDecimal256::percent(-50); + let neg_two = SignedDecimal256::percent(-200); + let min = SignedDecimal256::MIN; + + assert_eq!(zero.atomics(), Int256::from(0)); + assert_eq!(one.atomics(), Int256::from(1000000000000000000i128)); + assert_eq!(half.atomics(), Int256::from(500000000000000000i128)); + assert_eq!(two.atomics(), Int256::from(2000000000000000000i128)); + assert_eq!(max.atomics(), Int256::MAX); + assert_eq!(neg_half.atomics(), Int256::from(-500000000000000000i128)); + assert_eq!(neg_two.atomics(), Int256::from(-2000000000000000000i128)); + assert_eq!(min.atomics(), Int256::MIN); + } + + #[test] + fn signed_decimal_256_decimal_places_works() { + let zero = SignedDecimal256::zero(); + let one = SignedDecimal256::one(); + let half = SignedDecimal256::percent(50); + let two = SignedDecimal256::percent(200); + let max = SignedDecimal256::MAX; + let neg_one = SignedDecimal256::negative_one(); + + assert_eq!(zero.decimal_places(), 18); + assert_eq!(one.decimal_places(), 18); + assert_eq!(half.decimal_places(), 18); + assert_eq!(two.decimal_places(), 18); + assert_eq!(max.decimal_places(), 18); + assert_eq!(neg_one.decimal_places(), 18); + } + + #[test] + fn signed_decimal_256_is_zero_works() { + assert!(SignedDecimal256::zero().is_zero()); + assert!(SignedDecimal256::percent(0).is_zero()); + assert!(SignedDecimal256::permille(0).is_zero()); + + assert!(!SignedDecimal256::one().is_zero()); + assert!(!SignedDecimal256::percent(123).is_zero()); + assert!(!SignedDecimal256::permille(-1234).is_zero()); + } + + #[test] + fn signed_decimal_256_inv_works() { + // d = 0 + assert_eq!(SignedDecimal256::zero().inv(), None); + + // d == 1 + assert_eq!(SignedDecimal256::one().inv(), Some(SignedDecimal256::one())); + + // d == -1 + assert_eq!( + SignedDecimal256::negative_one().inv(), + Some(SignedDecimal256::negative_one()) + ); + + // d > 1 exact + assert_eq!( + SignedDecimal256::from_str("2").unwrap().inv(), + Some(SignedDecimal256::from_str("0.5").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("20").unwrap().inv(), + Some(SignedDecimal256::from_str("0.05").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("200").unwrap().inv(), + Some(SignedDecimal256::from_str("0.005").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("2000").unwrap().inv(), + Some(SignedDecimal256::from_str("0.0005").unwrap()) + ); + + // d > 1 rounded + assert_eq!( + SignedDecimal256::from_str("3").unwrap().inv(), + Some(SignedDecimal256::from_str("0.333333333333333333").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("6").unwrap().inv(), + Some(SignedDecimal256::from_str("0.166666666666666666").unwrap()) + ); + + // d < 1 exact + assert_eq!( + SignedDecimal256::from_str("0.5").unwrap().inv(), + Some(SignedDecimal256::from_str("2").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("0.05").unwrap().inv(), + Some(SignedDecimal256::from_str("20").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("0.005").unwrap().inv(), + Some(SignedDecimal256::from_str("200").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("0.0005").unwrap().inv(), + Some(SignedDecimal256::from_str("2000").unwrap()) + ); + + // d < 0 + assert_eq!( + SignedDecimal256::from_str("-0.5").unwrap().inv(), + Some(SignedDecimal256::from_str("-2").unwrap()) + ); + // d < 0 rounded + assert_eq!( + SignedDecimal256::from_str("-3").unwrap().inv(), + Some(SignedDecimal256::from_str("-0.333333333333333333").unwrap()) + ); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_256_add_works() { + let value = SignedDecimal256::one() + SignedDecimal256::percent(50); // 1.5 + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL * Int256::from(3u8) / Int256::from(2u8) + ); + + assert_eq!( + SignedDecimal256::percent(5) + SignedDecimal256::percent(4), + SignedDecimal256::percent(9) + ); + assert_eq!( + SignedDecimal256::percent(5) + SignedDecimal256::zero(), + SignedDecimal256::percent(5) + ); + assert_eq!( + SignedDecimal256::zero() + SignedDecimal256::zero(), + SignedDecimal256::zero() + ); + // negative numbers + assert_eq!( + SignedDecimal256::percent(-5) + SignedDecimal256::percent(-4), + SignedDecimal256::percent(-9) + ); + assert_eq!( + SignedDecimal256::percent(-5) + SignedDecimal256::percent(4), + SignedDecimal256::percent(-1) + ); + assert_eq!( + SignedDecimal256::percent(5) + SignedDecimal256::percent(-4), + SignedDecimal256::percent(1) + ); + + // works for refs + let a = SignedDecimal256::percent(15); + let b = SignedDecimal256::percent(25); + let expected = SignedDecimal256::percent(40); + assert_eq!(a + b, expected); + assert_eq!(&a + b, expected); + assert_eq!(a + &b, expected); + assert_eq!(&a + &b, expected); + } + + #[test] + #[should_panic] + fn signed_decimal_256_add_overflow_panics() { + let _value = SignedDecimal256::MAX + SignedDecimal256::percent(50); + } + + #[test] + fn signed_decimal_256_add_assign_works() { + let mut a = SignedDecimal256::percent(30); + a += SignedDecimal256::percent(20); + assert_eq!(a, SignedDecimal256::percent(50)); + + // works for refs + let mut a = SignedDecimal256::percent(15); + let b = SignedDecimal256::percent(3); + let expected = SignedDecimal256::percent(18); + a += &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_256_sub_works() { + let value = SignedDecimal256::one() - SignedDecimal256::percent(50); // 0.5 + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(2u8) + ); + + assert_eq!( + SignedDecimal256::percent(9) - SignedDecimal256::percent(4), + SignedDecimal256::percent(5) + ); + assert_eq!( + SignedDecimal256::percent(16) - SignedDecimal256::zero(), + SignedDecimal256::percent(16) + ); + assert_eq!( + SignedDecimal256::percent(16) - SignedDecimal256::percent(16), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::zero() - SignedDecimal256::zero(), + SignedDecimal256::zero() + ); + + // negative numbers + assert_eq!( + SignedDecimal256::percent(-5) - SignedDecimal256::percent(-4), + SignedDecimal256::percent(-1) + ); + assert_eq!( + SignedDecimal256::percent(-5) - SignedDecimal256::percent(4), + SignedDecimal256::percent(-9) + ); + assert_eq!( + SignedDecimal256::percent(500) - SignedDecimal256::percent(-4), + SignedDecimal256::percent(504) + ); + + // works for refs + let a = SignedDecimal256::percent(13); + let b = SignedDecimal256::percent(6); + let expected = SignedDecimal256::percent(7); + assert_eq!(a - b, expected); + assert_eq!(&a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn signed_decimal_256_sub_overflow_panics() { + let _value = SignedDecimal256::MIN - SignedDecimal256::percent(50); + } + + #[test] + fn signed_decimal_256_sub_assign_works() { + let mut a = SignedDecimal256::percent(20); + a -= SignedDecimal256::percent(2); + assert_eq!(a, SignedDecimal256::percent(18)); + + // works for refs + let mut a = SignedDecimal256::percent(33); + let b = SignedDecimal256::percent(13); + let expected = SignedDecimal256::percent(20); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_256_implements_mul() { + let one = SignedDecimal256::one(); + let two = one + one; + let half = SignedDecimal256::percent(50); + + // 1*x and x*1 + assert_eq!( + one * SignedDecimal256::percent(0), + SignedDecimal256::percent(0) + ); + assert_eq!( + one * SignedDecimal256::percent(1), + SignedDecimal256::percent(1) + ); + assert_eq!( + one * SignedDecimal256::percent(10), + SignedDecimal256::percent(10) + ); + assert_eq!( + one * SignedDecimal256::percent(100), + SignedDecimal256::percent(100) + ); + assert_eq!( + one * SignedDecimal256::percent(1000), + SignedDecimal256::percent(1000) + ); + assert_eq!(one * SignedDecimal256::MAX, SignedDecimal256::MAX); + assert_eq!( + SignedDecimal256::percent(0) * one, + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::percent(1) * one, + SignedDecimal256::percent(1) + ); + assert_eq!( + SignedDecimal256::percent(10) * one, + SignedDecimal256::percent(10) + ); + assert_eq!( + SignedDecimal256::percent(100) * one, + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::percent(1000) * one, + SignedDecimal256::percent(1000) + ); + assert_eq!(SignedDecimal256::MAX * one, SignedDecimal256::MAX); + assert_eq!( + SignedDecimal256::percent(-1) * one, + SignedDecimal256::percent(-1) + ); + assert_eq!( + one * SignedDecimal256::percent(-10), + SignedDecimal256::percent(-10) + ); + + // double + assert_eq!( + two * SignedDecimal256::percent(0), + SignedDecimal256::percent(0) + ); + assert_eq!( + two * SignedDecimal256::percent(1), + SignedDecimal256::percent(2) + ); + assert_eq!( + two * SignedDecimal256::percent(10), + SignedDecimal256::percent(20) + ); + assert_eq!( + two * SignedDecimal256::percent(100), + SignedDecimal256::percent(200) + ); + assert_eq!( + two * SignedDecimal256::percent(1000), + SignedDecimal256::percent(2000) + ); + assert_eq!( + SignedDecimal256::percent(0) * two, + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::percent(1) * two, + SignedDecimal256::percent(2) + ); + assert_eq!( + SignedDecimal256::percent(10) * two, + SignedDecimal256::percent(20) + ); + assert_eq!( + SignedDecimal256::percent(100) * two, + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(1000) * two, + SignedDecimal256::percent(2000) + ); + assert_eq!( + SignedDecimal256::percent(-1) * two, + SignedDecimal256::percent(-2) + ); + assert_eq!( + two * SignedDecimal256::new(Int256::MIN / Int256::from(2)), + SignedDecimal256::MIN + ); + + // half + assert_eq!( + half * SignedDecimal256::percent(0), + SignedDecimal256::percent(0) + ); + assert_eq!( + half * SignedDecimal256::percent(1), + SignedDecimal256::permille(5) + ); + assert_eq!( + half * SignedDecimal256::percent(10), + SignedDecimal256::percent(5) + ); + assert_eq!( + half * SignedDecimal256::percent(100), + SignedDecimal256::percent(50) + ); + assert_eq!( + half * SignedDecimal256::percent(1000), + SignedDecimal256::percent(500) + ); + assert_eq!( + SignedDecimal256::percent(0) * half, + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::percent(1) * half, + SignedDecimal256::permille(5) + ); + assert_eq!( + SignedDecimal256::percent(10) * half, + SignedDecimal256::percent(5) + ); + assert_eq!( + SignedDecimal256::percent(100) * half, + SignedDecimal256::percent(50) + ); + assert_eq!( + SignedDecimal256::percent(1000) * half, + SignedDecimal256::percent(500) + ); + + // Move left + let a = dec("123.127726548762582"); + assert_eq!(a * dec("1"), dec("123.127726548762582")); + assert_eq!(a * dec("10"), dec("1231.27726548762582")); + assert_eq!(a * dec("100"), dec("12312.7726548762582")); + assert_eq!(a * dec("1000"), dec("123127.726548762582")); + assert_eq!(a * dec("1000000"), dec("123127726.548762582")); + assert_eq!(a * dec("1000000000"), dec("123127726548.762582")); + assert_eq!(a * dec("1000000000000"), dec("123127726548762.582")); + assert_eq!(a * dec("1000000000000000"), dec("123127726548762582")); + assert_eq!(a * dec("1000000000000000000"), dec("123127726548762582000")); + assert_eq!(dec("1") * a, dec("123.127726548762582")); + assert_eq!(dec("10") * a, dec("1231.27726548762582")); + assert_eq!(dec("100") * a, dec("12312.7726548762582")); + assert_eq!(dec("1000") * a, dec("123127.726548762582")); + assert_eq!(dec("1000000") * a, dec("123127726.548762582")); + assert_eq!(dec("1000000000") * a, dec("123127726548.762582")); + assert_eq!(dec("1000000000000") * a, dec("123127726548762.582")); + assert_eq!(dec("1000000000000000") * a, dec("123127726548762582")); + assert_eq!(dec("1000000000000000000") * a, dec("123127726548762582000")); + assert_eq!( + dec("-1000000000000000000") * a, + dec("-123127726548762582000") + ); + + // Move right + let max = SignedDecimal256::MAX; + assert_eq!( + max * dec("1.0"), + dec("57896044618658097711785492504343953926634992332820282019728.792003956564819967") + ); + assert_eq!( + max * dec("0.1"), + dec("5789604461865809771178549250434395392663499233282028201972.879200395656481996") + ); + assert_eq!( + max * dec("0.01"), + dec("578960446186580977117854925043439539266349923328202820197.287920039565648199") + ); + assert_eq!( + max * dec("0.001"), + dec("57896044618658097711785492504343953926634992332820282019.728792003956564819") + ); + assert_eq!( + max * dec("0.000001"), + dec("57896044618658097711785492504343953926634992332820282.019728792003956564") + ); + assert_eq!( + max * dec("0.000000001"), + dec("57896044618658097711785492504343953926634992332820.282019728792003956") + ); + assert_eq!( + max * dec("0.000000000001"), + dec("57896044618658097711785492504343953926634992332.820282019728792003") + ); + assert_eq!( + max * dec("0.000000000000001"), + dec("57896044618658097711785492504343953926634992.332820282019728792") + ); + assert_eq!( + max * dec("0.000000000000000001"), + dec("57896044618658097711785492504343953926634.992332820282019728") + ); + + // works for refs + let a = SignedDecimal256::percent(20); + let b = SignedDecimal256::percent(30); + let expected = SignedDecimal256::percent(6); + assert_eq!(a * b, expected); + assert_eq!(&a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn signed_decimal_256_mul_assign_works() { + let mut a = SignedDecimal256::percent(15); + a *= SignedDecimal256::percent(60); + assert_eq!(a, SignedDecimal256::percent(9)); + + // works for refs + let mut a = SignedDecimal256::percent(50); + let b = SignedDecimal256::percent(20); + a *= &b; + assert_eq!(a, SignedDecimal256::percent(10)); + } + + #[test] + #[should_panic(expected = "attempt to multiply with overflow")] + fn signed_decimal_256_mul_overflow_panics() { + let _value = SignedDecimal256::MAX * SignedDecimal256::percent(101); + } + + #[test] + fn signed_decimal_256_checked_mul() { + let test_data = [ + (SignedDecimal256::zero(), SignedDecimal256::zero()), + (SignedDecimal256::zero(), SignedDecimal256::one()), + (SignedDecimal256::one(), SignedDecimal256::zero()), + (SignedDecimal256::percent(10), SignedDecimal256::zero()), + (SignedDecimal256::percent(10), SignedDecimal256::percent(5)), + (SignedDecimal256::MAX, SignedDecimal256::one()), + ( + SignedDecimal256::MAX / Int256::from(2), + SignedDecimal256::percent(200), + ), + ( + SignedDecimal256::permille(6), + SignedDecimal256::permille(13), + ), + ( + SignedDecimal256::permille(-6), + SignedDecimal256::permille(0), + ), + (SignedDecimal256::MAX, SignedDecimal256::negative_one()), + ]; + + // The regular core::ops::Mul is our source of truth for these tests. + for (x, y) in test_data.into_iter() { + assert_eq!(x * y, x.checked_mul(y).unwrap()); + } + } + + #[test] + fn signed_decimal_256_checked_mul_overflow() { + assert_eq!( + SignedDecimal256::MAX.checked_mul(SignedDecimal256::percent(200)), + Err(OverflowError { + operation: OverflowOperation::Mul, + operand1: SignedDecimal256::MAX.to_string(), + operand2: SignedDecimal256::percent(200).to_string(), + }) + ); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_256_implements_div() { + let one = SignedDecimal256::one(); + let two = one + one; + let half = SignedDecimal256::percent(50); + + // 1/x and x/1 + assert_eq!( + one / SignedDecimal256::percent(1), + SignedDecimal256::percent(10_000) + ); + assert_eq!( + one / SignedDecimal256::percent(10), + SignedDecimal256::percent(1_000) + ); + assert_eq!( + one / SignedDecimal256::percent(100), + SignedDecimal256::percent(100) + ); + assert_eq!( + one / SignedDecimal256::percent(1000), + SignedDecimal256::percent(10) + ); + assert_eq!( + SignedDecimal256::percent(0) / one, + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::percent(1) / one, + SignedDecimal256::percent(1) + ); + assert_eq!( + SignedDecimal256::percent(10) / one, + SignedDecimal256::percent(10) + ); + assert_eq!( + SignedDecimal256::percent(100) / one, + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::percent(1000) / one, + SignedDecimal256::percent(1000) + ); + assert_eq!( + one / SignedDecimal256::percent(-1), + SignedDecimal256::percent(-10_000) + ); + assert_eq!( + one / SignedDecimal256::percent(-10), + SignedDecimal256::percent(-1_000) + ); + + // double + assert_eq!( + two / SignedDecimal256::percent(1), + SignedDecimal256::percent(20_000) + ); + assert_eq!( + two / SignedDecimal256::percent(10), + SignedDecimal256::percent(2_000) + ); + assert_eq!( + two / SignedDecimal256::percent(100), + SignedDecimal256::percent(200) + ); + assert_eq!( + two / SignedDecimal256::percent(1000), + SignedDecimal256::percent(20) + ); + assert_eq!( + SignedDecimal256::percent(0) / two, + SignedDecimal256::percent(0) + ); + assert_eq!(SignedDecimal256::percent(1) / two, dec("0.005")); + assert_eq!( + SignedDecimal256::percent(10) / two, + SignedDecimal256::percent(5) + ); + assert_eq!( + SignedDecimal256::percent(100) / two, + SignedDecimal256::percent(50) + ); + assert_eq!( + SignedDecimal256::percent(1000) / two, + SignedDecimal256::percent(500) + ); + assert_eq!( + two / SignedDecimal256::percent(-1), + SignedDecimal256::percent(-20_000) + ); + assert_eq!( + SignedDecimal256::percent(-10000) / two, + SignedDecimal256::percent(-5000) + ); + + // half + assert_eq!( + half / SignedDecimal256::percent(1), + SignedDecimal256::percent(5_000) + ); + assert_eq!( + half / SignedDecimal256::percent(10), + SignedDecimal256::percent(500) + ); + assert_eq!( + half / SignedDecimal256::percent(100), + SignedDecimal256::percent(50) + ); + assert_eq!( + half / SignedDecimal256::percent(1000), + SignedDecimal256::percent(5) + ); + assert_eq!( + SignedDecimal256::percent(0) / half, + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::percent(1) / half, + SignedDecimal256::percent(2) + ); + assert_eq!( + SignedDecimal256::percent(10) / half, + SignedDecimal256::percent(20) + ); + assert_eq!( + SignedDecimal256::percent(100) / half, + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(1000) / half, + SignedDecimal256::percent(2000) + ); + + // Move right + let a = dec("123127726548762582"); + assert_eq!(a / dec("1"), dec("123127726548762582")); + assert_eq!(a / dec("10"), dec("12312772654876258.2")); + assert_eq!(a / dec("100"), dec("1231277265487625.82")); + assert_eq!(a / dec("1000"), dec("123127726548762.582")); + assert_eq!(a / dec("1000000"), dec("123127726548.762582")); + assert_eq!(a / dec("1000000000"), dec("123127726.548762582")); + assert_eq!(a / dec("1000000000000"), dec("123127.726548762582")); + assert_eq!(a / dec("1000000000000000"), dec("123.127726548762582")); + assert_eq!(a / dec("1000000000000000000"), dec("0.123127726548762582")); + assert_eq!(dec("1") / a, dec("0.000000000000000008")); + assert_eq!(dec("10") / a, dec("0.000000000000000081")); + assert_eq!(dec("100") / a, dec("0.000000000000000812")); + assert_eq!(dec("1000") / a, dec("0.000000000000008121")); + assert_eq!(dec("1000000") / a, dec("0.000000000008121647")); + assert_eq!(dec("1000000000") / a, dec("0.000000008121647560")); + assert_eq!(dec("1000000000000") / a, dec("0.000008121647560868")); + assert_eq!(dec("1000000000000000") / a, dec("0.008121647560868164")); + assert_eq!(dec("1000000000000000000") / a, dec("8.121647560868164773")); + // negative + let a = dec("-123127726548762582"); + assert_eq!(a / dec("1"), dec("-123127726548762582")); + assert_eq!(a / dec("10"), dec("-12312772654876258.2")); + assert_eq!(a / dec("100"), dec("-1231277265487625.82")); + assert_eq!(a / dec("1000"), dec("-123127726548762.582")); + assert_eq!(a / dec("1000000"), dec("-123127726548.762582")); + assert_eq!(a / dec("1000000000"), dec("-123127726.548762582")); + assert_eq!(a / dec("1000000000000"), dec("-123127.726548762582")); + assert_eq!(a / dec("1000000000000000"), dec("-123.127726548762582")); + assert_eq!(a / dec("1000000000000000000"), dec("-0.123127726548762582")); + assert_eq!(dec("1") / a, dec("-0.000000000000000008")); + + // Move left + let a = dec("0.123127726548762582"); + assert_eq!(a / dec("1.0"), dec("0.123127726548762582")); + assert_eq!(a / dec("0.1"), dec("1.23127726548762582")); + assert_eq!(a / dec("0.01"), dec("12.3127726548762582")); + assert_eq!(a / dec("0.001"), dec("123.127726548762582")); + assert_eq!(a / dec("0.000001"), dec("123127.726548762582")); + assert_eq!(a / dec("0.000000001"), dec("123127726.548762582")); + assert_eq!(a / dec("0.000000000001"), dec("123127726548.762582")); + assert_eq!(a / dec("0.000000000000001"), dec("123127726548762.582")); + assert_eq!(a / dec("0.000000000000000001"), dec("123127726548762582")); + // negative + let a = dec("-0.123127726548762582"); + assert_eq!(a / dec("1.0"), dec("-0.123127726548762582")); + assert_eq!(a / dec("0.1"), dec("-1.23127726548762582")); + assert_eq!(a / dec("0.01"), dec("-12.3127726548762582")); + assert_eq!(a / dec("0.001"), dec("-123.127726548762582")); + assert_eq!(a / dec("0.000001"), dec("-123127.726548762582")); + assert_eq!(a / dec("0.000000001"), dec("-123127726.548762582")); + + assert_eq!( + SignedDecimal256::percent(15) / SignedDecimal256::percent(60), + SignedDecimal256::percent(25) + ); + + // works for refs + let a = SignedDecimal256::percent(100); + let b = SignedDecimal256::percent(20); + let expected = SignedDecimal256::percent(500); + assert_eq!(a / b, expected); + assert_eq!(&a / b, expected); + assert_eq!(a / &b, expected); + assert_eq!(&a / &b, expected); + } + + #[test] + fn signed_decimal_256_div_assign_works() { + let mut a = SignedDecimal256::percent(15); + a /= SignedDecimal256::percent(20); + assert_eq!(a, SignedDecimal256::percent(75)); + + // works for refs + let mut a = SignedDecimal256::percent(50); + let b = SignedDecimal256::percent(20); + a /= &b; + assert_eq!(a, SignedDecimal256::percent(250)); + } + + #[test] + #[should_panic(expected = "Division failed - multiplication overflow")] + fn signed_decimal_256_div_overflow_panics() { + let _value = SignedDecimal256::MAX / SignedDecimal256::percent(10); + } + + #[test] + #[should_panic(expected = "Division failed - denominator must not be zero")] + fn signed_decimal_256_div_by_zero_panics() { + let _value = SignedDecimal256::one() / SignedDecimal256::zero(); + } + + #[test] + fn signed_decimal_256_int128_division() { + // a/b + let left = SignedDecimal256::percent(150); // 1.5 + let right = Int256::from(3); + assert_eq!(left / right, SignedDecimal256::percent(50)); + + // negative + let left = SignedDecimal256::percent(-150); // -1.5 + let right = Int256::from(3); + assert_eq!(left / right, SignedDecimal256::percent(-50)); + + // 0/a + let left = SignedDecimal256::zero(); + let right = Int256::from(300); + assert_eq!(left / right, SignedDecimal256::zero()); + } + + #[test] + #[should_panic] + fn signed_decimal_256_int128_divide_by_zero() { + let left = SignedDecimal256::percent(150); // 1.5 + let right = Int256::from(0); + let _result = left / right; + } + + #[test] + fn signed_decimal_256_int128_div_assign() { + // a/b + let mut dec = SignedDecimal256::percent(150); // 1.5 + dec /= Int256::from(3); + assert_eq!(dec, SignedDecimal256::percent(50)); + + // 0/a + let mut dec = SignedDecimal256::zero(); + dec /= Int256::from(300); + assert_eq!(dec, SignedDecimal256::zero()); + } + + #[test] + #[should_panic] + fn signed_decimal_256_int128_div_assign_by_zero() { + // a/0 + let mut dec = SignedDecimal256::percent(50); + dec /= Int256::from(0); + } + + #[test] + fn signed_decimal_256_checked_pow() { + for exp in 0..10 { + assert_eq!( + SignedDecimal256::one().checked_pow(exp).unwrap(), + SignedDecimal256::one() + ); + } + + // This case is mathematically undefined but we ensure consistency with Rust standard types + // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=20df6716048e77087acd40194b233494 + assert_eq!( + SignedDecimal256::zero().checked_pow(0).unwrap(), + SignedDecimal256::one() + ); + + for exp in 1..10 { + assert_eq!( + SignedDecimal256::zero().checked_pow(exp).unwrap(), + SignedDecimal256::zero() + ); + } + + for exp in 1..10 { + assert_eq!( + SignedDecimal256::negative_one().checked_pow(exp).unwrap(), + // alternates between 1 and -1 + if exp % 2 == 0 { + SignedDecimal256::one() + } else { + SignedDecimal256::negative_one() + } + ) + } + + for num in &[ + SignedDecimal256::percent(50), + SignedDecimal256::percent(99), + SignedDecimal256::percent(200), + ] { + assert_eq!(num.checked_pow(0).unwrap(), SignedDecimal256::one()) + } + + assert_eq!( + SignedDecimal256::percent(20).checked_pow(2).unwrap(), + SignedDecimal256::percent(4) + ); + + assert_eq!( + SignedDecimal256::percent(20).checked_pow(3).unwrap(), + SignedDecimal256::permille(8) + ); + + assert_eq!( + SignedDecimal256::percent(200).checked_pow(4).unwrap(), + SignedDecimal256::percent(1600) + ); + + assert_eq!( + SignedDecimal256::percent(200).checked_pow(4).unwrap(), + SignedDecimal256::percent(1600) + ); + + assert_eq!( + SignedDecimal256::percent(700).checked_pow(5).unwrap(), + SignedDecimal256::percent(1680700) + ); + + assert_eq!( + SignedDecimal256::percent(700).checked_pow(8).unwrap(), + SignedDecimal256::percent(576480100) + ); + + assert_eq!( + SignedDecimal256::percent(700).checked_pow(10).unwrap(), + SignedDecimal256::percent(28247524900) + ); + + assert_eq!( + SignedDecimal256::percent(120).checked_pow(123).unwrap(), + SignedDecimal256(5486473221892422150877397607i128.into()) + ); + + assert_eq!( + SignedDecimal256::percent(10).checked_pow(2).unwrap(), + SignedDecimal256(10000000000000000i128.into()) + ); + + assert_eq!( + SignedDecimal256::percent(10).checked_pow(18).unwrap(), + SignedDecimal256(1i128.into()) + ); + + let decimals = [ + SignedDecimal256::percent(-50), + SignedDecimal256::percent(-99), + SignedDecimal256::percent(-200), + ]; + let exponents = [1, 2, 3, 4, 5, 8, 10]; + + for d in decimals { + for e in exponents { + // use multiplication as source of truth + let mut mul = Ok(d); + for _ in 1..e { + mul = mul.and_then(|mul| mul.checked_mul(d)); + } + assert_eq!(mul, d.checked_pow(e)); + } + } + } + + #[test] + fn signed_decimal_256_checked_pow_overflow() { + assert_eq!( + SignedDecimal256::MAX.checked_pow(2), + Err(OverflowError { + operation: OverflowOperation::Pow, + operand1: SignedDecimal256::MAX.to_string(), + operand2: "2".to_string(), + }) + ); + } + + #[test] + fn signed_decimal_256_to_string() { + // Integers + assert_eq!(SignedDecimal256::zero().to_string(), "0"); + assert_eq!(SignedDecimal256::one().to_string(), "1"); + assert_eq!(SignedDecimal256::percent(500).to_string(), "5"); + assert_eq!(SignedDecimal256::percent(-500).to_string(), "-5"); + + // SignedDecimal256s + assert_eq!(SignedDecimal256::percent(125).to_string(), "1.25"); + assert_eq!(SignedDecimal256::percent(42638).to_string(), "426.38"); + assert_eq!(SignedDecimal256::percent(3).to_string(), "0.03"); + assert_eq!(SignedDecimal256::permille(987).to_string(), "0.987"); + assert_eq!(SignedDecimal256::percent(-125).to_string(), "-1.25"); + assert_eq!(SignedDecimal256::percent(-42638).to_string(), "-426.38"); + assert_eq!(SignedDecimal256::percent(-3).to_string(), "-0.03"); + assert_eq!(SignedDecimal256::permille(-987).to_string(), "-0.987"); + + assert_eq!( + SignedDecimal256(Int256::from(1i128)).to_string(), + "0.000000000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10i128)).to_string(), + "0.00000000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(100i128)).to_string(), + "0.0000000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(1000i128)).to_string(), + "0.000000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10000i128)).to_string(), + "0.00000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(100000i128)).to_string(), + "0.0000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(1000000i128)).to_string(), + "0.000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10000000i128)).to_string(), + "0.00000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(100000000i128)).to_string(), + "0.0000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(1000000000i128)).to_string(), + "0.000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10000000000i128)).to_string(), + "0.00000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(100000000000i128)).to_string(), + "0.0000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10000000000000i128)).to_string(), + "0.00001" + ); + assert_eq!( + SignedDecimal256(Int256::from(100000000000000i128)).to_string(), + "0.0001" + ); + assert_eq!( + SignedDecimal256(Int256::from(1000000000000000i128)).to_string(), + "0.001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10000000000000000i128)).to_string(), + "0.01" + ); + assert_eq!( + SignedDecimal256(Int256::from(100000000000000000i128)).to_string(), + "0.1" + ); + assert_eq!( + SignedDecimal256(Int256::from(-1i128)).to_string(), + "-0.000000000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(-100000000000000i128)).to_string(), + "-0.0001" + ); + assert_eq!( + SignedDecimal256(Int256::from(-100000000000000000i128)).to_string(), + "-0.1" + ); + } + + #[test] + fn signed_decimal_256_iter_sum() { + let items = vec![ + SignedDecimal256::zero(), + SignedDecimal256(Int256::from(2i128)), + SignedDecimal256(Int256::from(2i128)), + SignedDecimal256(Int256::from(-2i128)), + ]; + assert_eq!( + items.iter().sum::(), + SignedDecimal256(Int256::from(2i128)) + ); + assert_eq!( + items.into_iter().sum::(), + SignedDecimal256(Int256::from(2i128)) + ); + + let empty: Vec = vec![]; + assert_eq!( + SignedDecimal256::zero(), + empty.iter().sum::() + ); + } + + #[test] + fn signed_decimal_256_serialize() { + assert_eq!(to_json_vec(&SignedDecimal256::zero()).unwrap(), br#""0""#); + assert_eq!(to_json_vec(&SignedDecimal256::one()).unwrap(), br#""1""#); + assert_eq!( + to_json_vec(&SignedDecimal256::percent(8)).unwrap(), + br#""0.08""# + ); + assert_eq!( + to_json_vec(&SignedDecimal256::percent(87)).unwrap(), + br#""0.87""# + ); + assert_eq!( + to_json_vec(&SignedDecimal256::percent(876)).unwrap(), + br#""8.76""# + ); + assert_eq!( + to_json_vec(&SignedDecimal256::percent(8765)).unwrap(), + br#""87.65""# + ); + assert_eq!( + to_json_vec(&SignedDecimal256::percent(-87654)).unwrap(), + br#""-876.54""# + ); + assert_eq!( + to_json_vec(&SignedDecimal256::negative_one()).unwrap(), + br#""-1""# + ); + assert_eq!( + to_json_vec(&-SignedDecimal256::percent(8)).unwrap(), + br#""-0.08""# + ); + } + + #[test] + fn signed_decimal_256_deserialize() { + assert_eq!( + from_json::(br#""0""#).unwrap(), + SignedDecimal256::zero() + ); + assert_eq!( + from_json::(br#""1""#).unwrap(), + SignedDecimal256::one() + ); + assert_eq!( + from_json::(br#""000""#).unwrap(), + SignedDecimal256::zero() + ); + assert_eq!( + from_json::(br#""001""#).unwrap(), + SignedDecimal256::one() + ); + + assert_eq!( + from_json::(br#""0.08""#).unwrap(), + SignedDecimal256::percent(8) + ); + assert_eq!( + from_json::(br#""0.87""#).unwrap(), + SignedDecimal256::percent(87) + ); + assert_eq!( + from_json::(br#""8.76""#).unwrap(), + SignedDecimal256::percent(876) + ); + assert_eq!( + from_json::(br#""87.65""#).unwrap(), + SignedDecimal256::percent(8765) + ); + + // negative numbers + assert_eq!( + from_json::(br#""-0""#).unwrap(), + SignedDecimal256::zero() + ); + assert_eq!( + from_json::(br#""-1""#).unwrap(), + SignedDecimal256::negative_one() + ); + assert_eq!( + from_json::(br#""-001""#).unwrap(), + SignedDecimal256::negative_one() + ); + assert_eq!( + from_json::(br#""-0.08""#).unwrap(), + SignedDecimal256::percent(-8) + ); + } + + #[test] + fn signed_decimal_256_abs_diff_works() { + let a = SignedDecimal256::percent(285); + let b = SignedDecimal256::percent(200); + let expected = Decimal256::percent(85); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let a = SignedDecimal256::percent(-200); + let b = SignedDecimal256::percent(200); + let expected = Decimal256::percent(400); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let a = SignedDecimal256::percent(-200); + let b = SignedDecimal256::percent(-240); + let expected = Decimal256::percent(40); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_256_rem_works() { + // 4.02 % 1.11 = 0.69 + assert_eq!( + SignedDecimal256::percent(402) % SignedDecimal256::percent(111), + SignedDecimal256::percent(69) + ); + + // 15.25 % 4 = 3.25 + assert_eq!( + SignedDecimal256::percent(1525) % SignedDecimal256::percent(400), + SignedDecimal256::percent(325) + ); + + // -20.25 % 5 = -25 + assert_eq!( + SignedDecimal256::percent(-2025) % SignedDecimal256::percent(500), + SignedDecimal256::percent(-25) + ); + + let a = SignedDecimal256::percent(318); + let b = SignedDecimal256::percent(317); + let expected = SignedDecimal256::percent(1); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + fn signed_decimal_256_rem_assign_works() { + let mut a = SignedDecimal256::percent(17673); + a %= SignedDecimal256::percent(2362); + assert_eq!(a, SignedDecimal256::percent(1139)); // 176.73 % 23.62 = 11.39 + + let mut a = SignedDecimal256::percent(4262); + let b = SignedDecimal256::percent(1270); + a %= &b; + assert_eq!(a, SignedDecimal256::percent(452)); // 42.62 % 12.7 = 4.52 + + let mut a = SignedDecimal256::percent(-4262); + let b = SignedDecimal256::percent(1270); + a %= &b; + assert_eq!(a, SignedDecimal256::percent(-452)); // -42.62 % 12.7 = -4.52 + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn signed_decimal_256_rem_panics_for_zero() { + let _ = SignedDecimal256::percent(777) % SignedDecimal256::zero(); + } + + #[test] + fn signed_decimal_256_checked_methods() { + // checked add + assert_eq!( + SignedDecimal256::percent(402) + .checked_add(SignedDecimal256::percent(111)) + .unwrap(), + SignedDecimal256::percent(513) + ); + assert!(matches!( + SignedDecimal256::MAX.checked_add(SignedDecimal256::percent(1)), + Err(OverflowError { .. }) + )); + assert!(matches!( + SignedDecimal256::MIN.checked_add(SignedDecimal256::percent(-1)), + Err(OverflowError { .. }) + )); + + // checked sub + assert_eq!( + SignedDecimal256::percent(1111) + .checked_sub(SignedDecimal256::percent(111)) + .unwrap(), + SignedDecimal256::percent(1000) + ); + assert_eq!( + SignedDecimal256::zero() + .checked_sub(SignedDecimal256::percent(1)) + .unwrap(), + SignedDecimal256::percent(-1) + ); + assert!(matches!( + SignedDecimal256::MIN.checked_sub(SignedDecimal256::percent(1)), + Err(OverflowError { .. }) + )); + assert!(matches!( + SignedDecimal256::MAX.checked_sub(SignedDecimal256::percent(-1)), + Err(OverflowError { .. }) + )); + + // checked div + assert_eq!( + SignedDecimal256::percent(30) + .checked_div(SignedDecimal256::percent(200)) + .unwrap(), + SignedDecimal256::percent(15) + ); + assert_eq!( + SignedDecimal256::percent(88) + .checked_div(SignedDecimal256::percent(20)) + .unwrap(), + SignedDecimal256::percent(440) + ); + assert!(matches!( + SignedDecimal256::MAX.checked_div(SignedDecimal256::zero()), + Err(CheckedFromRatioError::DivideByZero {}) + )); + assert!(matches!( + SignedDecimal256::MAX.checked_div(SignedDecimal256::percent(1)), + Err(CheckedFromRatioError::Overflow {}) + )); + assert_eq!( + SignedDecimal256::percent(-88) + .checked_div(SignedDecimal256::percent(20)) + .unwrap(), + SignedDecimal256::percent(-440) + ); + assert_eq!( + SignedDecimal256::percent(-88) + .checked_div(SignedDecimal256::percent(-20)) + .unwrap(), + SignedDecimal256::percent(440) + ); + + // checked rem + assert_eq!( + SignedDecimal256::percent(402) + .checked_rem(SignedDecimal256::percent(111)) + .unwrap(), + SignedDecimal256::percent(69) + ); + assert_eq!( + SignedDecimal256::percent(1525) + .checked_rem(SignedDecimal256::percent(400)) + .unwrap(), + SignedDecimal256::percent(325) + ); + assert_eq!( + SignedDecimal256::percent(-1525) + .checked_rem(SignedDecimal256::percent(400)) + .unwrap(), + SignedDecimal256::percent(-325) + ); + assert_eq!( + SignedDecimal256::percent(-1525) + .checked_rem(SignedDecimal256::percent(-400)) + .unwrap(), + SignedDecimal256::percent(-325) + ); + assert!(matches!( + SignedDecimal256::MAX.checked_rem(SignedDecimal256::zero()), + Err(DivideByZeroError { .. }) + )); + } + + #[test] + fn signed_decimal_256_pow_works() { + assert_eq!( + SignedDecimal256::percent(200).pow(2), + SignedDecimal256::percent(400) + ); + assert_eq!( + SignedDecimal256::percent(-200).pow(2), + SignedDecimal256::percent(400) + ); + assert_eq!( + SignedDecimal256::percent(-200).pow(3), + SignedDecimal256::percent(-800) + ); + assert_eq!( + SignedDecimal256::percent(200).pow(10), + SignedDecimal256::percent(102400) + ); + } + + #[test] + #[should_panic] + fn signed_decimal_256_pow_overflow_panics() { + _ = SignedDecimal256::MAX.pow(2u32); + } + + #[test] + fn signed_decimal_256_saturating_works() { + assert_eq!( + SignedDecimal256::percent(200).saturating_add(SignedDecimal256::percent(200)), + SignedDecimal256::percent(400) + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_add(SignedDecimal256::percent(200)), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_add(SignedDecimal256::percent(-200)), + SignedDecimal256::percent(-400) + ); + assert_eq!( + SignedDecimal256::MAX.saturating_add(SignedDecimal256::percent(200)), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::MIN.saturating_add(SignedDecimal256::percent(-1)), + SignedDecimal256::MIN + ); + assert_eq!( + SignedDecimal256::percent(200).saturating_sub(SignedDecimal256::percent(100)), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_sub(SignedDecimal256::percent(100)), + SignedDecimal256::percent(-300) + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_sub(SignedDecimal256::percent(-100)), + SignedDecimal256::percent(-100) + ); + assert_eq!( + SignedDecimal256::zero().saturating_sub(SignedDecimal256::percent(200)), + SignedDecimal256::from_str("-2").unwrap() + ); + assert_eq!( + SignedDecimal256::MIN.saturating_sub(SignedDecimal256::percent(200)), + SignedDecimal256::MIN + ); + assert_eq!( + SignedDecimal256::MAX.saturating_sub(SignedDecimal256::percent(-200)), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::percent(200).saturating_mul(SignedDecimal256::percent(50)), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_mul(SignedDecimal256::percent(50)), + SignedDecimal256::percent(-100) + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_mul(SignedDecimal256::percent(-50)), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::MAX.saturating_mul(SignedDecimal256::percent(200)), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::MIN.saturating_mul(SignedDecimal256::percent(200)), + SignedDecimal256::MIN + ); + assert_eq!( + SignedDecimal256::MIN.saturating_mul(SignedDecimal256::percent(-200)), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::percent(400).saturating_pow(2u32), + SignedDecimal256::percent(1600) + ); + assert_eq!( + SignedDecimal256::MAX.saturating_pow(2u32), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::MAX.saturating_pow(3u32), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::MIN.saturating_pow(2u32), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::MIN.saturating_pow(3u32), + SignedDecimal256::MIN + ); + } + + #[test] + fn signed_decimal_256_rounding() { + assert_eq!(SignedDecimal256::one().floor(), SignedDecimal256::one()); + assert_eq!( + SignedDecimal256::percent(150).floor(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::percent(199).floor(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::percent(200).floor(), + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(99).floor(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256(Int256::from(1i128)).floor(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256(Int256::from(-1i128)).floor(), + SignedDecimal256::negative_one() + ); + assert_eq!( + SignedDecimal256::permille(-1234).floor(), + SignedDecimal256::percent(-200) + ); + + assert_eq!(SignedDecimal256::one().ceil(), SignedDecimal256::one()); + assert_eq!( + SignedDecimal256::percent(150).ceil(), + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(199).ceil(), + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(99).ceil(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256(Int256::from(1i128)).ceil(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256(Int256::from(-1i128)).ceil(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::permille(-1234).ceil(), + SignedDecimal256::negative_one() + ); + + assert_eq!(SignedDecimal256::one().trunc(), SignedDecimal256::one()); + assert_eq!( + SignedDecimal256::percent(150).trunc(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::percent(199).trunc(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::percent(200).trunc(), + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(99).trunc(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256(Int256::from(1i128)).trunc(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256(Int256::from(-1i128)).trunc(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::permille(-1234).trunc(), + SignedDecimal256::negative_one() + ); + } + + #[test] + #[should_panic(expected = "attempt to ceil with overflow")] + fn signed_decimal_256_ceil_panics() { + let _ = SignedDecimal256::MAX.ceil(); + } + + #[test] + #[should_panic(expected = "attempt to floor with overflow")] + fn signed_decimal_256_floor_panics() { + let _ = SignedDecimal256::MIN.floor(); + } + + #[test] + fn signed_decimal_256_checked_ceil() { + assert_eq!( + SignedDecimal256::percent(199).checked_ceil(), + Ok(SignedDecimal256::percent(200)) + ); + assert_eq!( + SignedDecimal256::MAX.checked_ceil(), + Err(RoundUpOverflowError) + ); + } + + #[test] + fn signed_decimal_256_checked_floor() { + assert_eq!( + SignedDecimal256::percent(199).checked_floor(), + Ok(SignedDecimal256::one()) + ); + assert_eq!( + SignedDecimal256::percent(-199).checked_floor(), + Ok(SignedDecimal256::percent(-200)) + ); + assert_eq!( + SignedDecimal256::MIN.checked_floor(), + Err(RoundDownOverflowError) + ); + assert_eq!( + SignedDecimal256::negative_one().checked_floor(), + Ok(SignedDecimal256::negative_one()) + ); + } + + #[test] + fn signed_decimal_256_to_int_floor_works() { + let d = SignedDecimal256::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(12)); + let d = SignedDecimal256::from_str("12.345").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(12)); + let d = SignedDecimal256::from_str("12.999").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(12)); + let d = SignedDecimal256::from_str("0.98451384").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(0)); + let d = SignedDecimal256::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(-13)); + let d = SignedDecimal256::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(-13)); + let d = SignedDecimal256::from_str("0.0001").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(0)); + let d = SignedDecimal256::from_str("75.0").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(75)); + let d = SignedDecimal256::from_str("0.0").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(0)); + let d = SignedDecimal256::from_str("-0.0").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(0)); + let d = SignedDecimal256::from_str("-0.0001").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(-1)); + let d = SignedDecimal256::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(-75)); + + let d = SignedDecimal256::MAX; + assert_eq!( + d.to_int_floor(), + Int256::from_str("57896044618658097711785492504343953926634992332820282019728") + .unwrap() + ); + let d = SignedDecimal256::MIN; + assert_eq!( + d.to_int_floor(), + Int256::from_str("-57896044618658097711785492504343953926634992332820282019729") + .unwrap() + ); + } + + #[test] + fn signed_decimal_256_to_int_ceil_works() { + let d = SignedDecimal256::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(13)); + let d = SignedDecimal256::from_str("12.345").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(13)); + let d = SignedDecimal256::from_str("12.999").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(13)); + let d = SignedDecimal256::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(-12)); + let d = SignedDecimal256::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(-12)); + + let d = SignedDecimal256::from_str("75.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(75)); + let d = SignedDecimal256::from_str("0.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(0)); + let d = SignedDecimal256::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(-75)); + + let d = SignedDecimal256::MAX; + assert_eq!( + d.to_int_ceil(), + Int256::from_str("57896044618658097711785492504343953926634992332820282019729") + .unwrap() + ); + let d = SignedDecimal256::MIN; + assert_eq!( + d.to_int_ceil(), + Int256::from_str("-57896044618658097711785492504343953926634992332820282019728") + .unwrap() + ); + } + + #[test] + fn signed_decimal_256_to_int_trunc_works() { + let d = SignedDecimal256::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(12)); + let d = SignedDecimal256::from_str("12.345").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(12)); + let d = SignedDecimal256::from_str("12.999").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(12)); + let d = SignedDecimal256::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(-12)); + let d = SignedDecimal256::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(-12)); + + let d = SignedDecimal256::from_str("75.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(75)); + let d = SignedDecimal256::from_str("0.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(0)); + let d = SignedDecimal256::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(-75)); + + let d = SignedDecimal256::MAX; + assert_eq!( + d.to_int_trunc(), + Int256::from_str("57896044618658097711785492504343953926634992332820282019728") + .unwrap() + ); + let d = SignedDecimal256::MIN; + assert_eq!( + d.to_int_trunc(), + Int256::from_str("-57896044618658097711785492504343953926634992332820282019728") + .unwrap() + ); + } + + #[test] + fn signed_decimal_256_neg_works() { + assert_eq!( + -SignedDecimal256::percent(50), + SignedDecimal256::percent(-50) + ); + assert_eq!(-SignedDecimal256::one(), SignedDecimal256::negative_one()); + } + + #[test] + fn signed_decimal_256_partial_eq() { + let test_cases = [ + ("1", "1", true), + ("0.5", "0.5", true), + ("0.5", "0.51", false), + ("0", "0.00000", true), + ("-1", "-1", true), + ("-0.5", "-0.5", true), + ("-0.5", "0.5", false), + ("-0.5", "-0.51", false), + ("-0", "-0.00000", true), + ] + .into_iter() + .map(|(lhs, rhs, expected)| (dec(lhs), dec(rhs), expected)); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } + + #[test] + fn signed_decimal_256_implements_debug() { + let decimal = SignedDecimal256::from_str("123.45").unwrap(); + assert_eq!(format!("{decimal:?}"), "SignedDecimal256(123.45)"); + + let test_cases = ["5", "5.01", "42", "0", "2", "-0.000001"]; + for s in test_cases { + let decimal = SignedDecimal256::from_str(s).unwrap(); + let expected = format!("SignedDecimal256({s})"); + assert_eq!(format!("{decimal:?}"), expected); + } + } + + #[test] + fn signed_decimal_256_can_be_instantiated_from_decimal() { + let d: SignedDecimal256 = Decimal::one().into(); + assert_eq!(d, SignedDecimal256::one()); + } + + #[test] + fn signed_decimal_256_can_be_instantiated_from_decimal_256() { + let d: SignedDecimal256 = Decimal256::zero().try_into().unwrap(); + assert_eq!(d, SignedDecimal256::zero()); + } + + #[test] + fn signed_decimal_256_may_fail_when_instantiated_from_decimal_256() { + let err = >::try_into(Decimal256::MAX).unwrap_err(); + assert_eq!("SignedDecimal256RangeExceeded", format!("{err:?}")); + assert_eq!("SignedDecimal256 range exceeded", format!("{err}")); + } + + #[test] + fn signed_decimal_256_can_be_serialized_and_deserialized() { + // properly deserialized + let value: SignedDecimal256 = serde_json::from_str(r#""123""#).unwrap(); + assert_eq!(SignedDecimal256::from_str("123").unwrap(), value); + + // properly serialized + let value = SignedDecimal256::from_str("456").unwrap(); + assert_eq!(r#""456""#, serde_json::to_string(&value).unwrap()); + + // invalid: not a string encoded decimal + assert_eq!( + "invalid type: integer `123`, expected string-encoded decimal at line 1 column 3", + serde_json::from_str::("123") + .err() + .unwrap() + .to_string() + ); + + // invalid: not properly defined signed decimal value + assert_eq!( + "Error parsing decimal '1.e': Generic error: Error parsing fractional at line 1 column 5", + serde_json::from_str::(r#""1.e""#) + .err() + .unwrap() + .to_string() + ); + } + + #[test] + fn signed_decimal_256_has_defined_json_schema() { + let schema = schema_for!(SignedDecimal256); + assert_eq!( + "SignedDecimal256", + schema.schema.metadata.unwrap().title.unwrap() + ); + } +} diff --git a/packages/std/src/math/uint128.rs b/packages/std/src/math/uint128.rs index 0aebf6b99..28081a947 100644 --- a/packages/std/src/math/uint128.rs +++ b/packages/std/src/math/uint128.rs @@ -15,9 +15,13 @@ use crate::errors::{ OverflowOperation, StdError, }; use crate::{ - forward_ref_partial_eq, impl_mul_fraction, ConversionOverflowError, Fraction, Uint256, Uint64, + forward_ref_partial_eq, impl_mul_fraction, Fraction, Int128, Int256, Int512, Int64, Uint256, + Uint64, }; +use super::conversion::forward_try_from; +use super::num_consts::NumConsts; + /// A thin wrapper around u128 that is using strings for JSON encoding/decoding, /// such that the full u128 range can be used for clients that convert JSON numbers to floats, /// like JavaScript and jq. @@ -269,6 +273,13 @@ impl Uint128 { } } +impl NumConsts for Uint128 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + impl_mul_fraction!(Uint128); // `From` is implemented manually instead of @@ -312,15 +323,13 @@ impl From for Uint128 { } } -impl TryFrom for Uint64 { - type Error = ConversionOverflowError; +forward_try_from!(Uint128, Uint64); - fn try_from(value: Uint128) -> Result { - Ok(Uint64::new(value.0.try_into().map_err(|_| { - ConversionOverflowError::new("Uint128", "Uint64", value.to_string()) - })?)) - } -} +// Int to Uint +forward_try_from!(Int64, Uint128); +forward_try_from!(Int128, Uint128); +forward_try_from!(Int256, Uint128); +forward_try_from!(Int512, Uint128); impl TryFrom<&str> for Uint128 { type Error = StdError; @@ -607,7 +616,8 @@ where #[cfg(test)] mod tests { use crate::errors::CheckedMultiplyFractionError::{ConversionOverflow, DivideByZero}; - use crate::{from_slice, to_vec, Decimal}; + use crate::math::conversion::test_try_from_int_to_uint; + use crate::{from_json, to_json_vec, ConversionOverflowError, Decimal}; use super::*; @@ -677,6 +687,26 @@ mod tests { assert!(result.is_err()); } + #[test] + fn uint128_try_from_signed_works() { + test_try_from_int_to_uint::("Int64", "Uint128"); + test_try_from_int_to_uint::("Int128", "Uint128"); + test_try_from_int_to_uint::("Int256", "Uint128"); + test_try_from_int_to_uint::("Int512", "Uint128"); + } + + #[test] + fn uint128_try_into() { + assert!(Uint64::try_from(Uint128::MAX).is_err()); + + assert_eq!(Uint64::try_from(Uint128::zero()), Ok(Uint64::zero())); + + assert_eq!( + Uint64::try_from(Uint128::from(42u64)), + Ok(Uint64::from(42u64)) + ); + } + #[test] fn uint128_implements_display() { let a = Uint128(12345); @@ -759,9 +789,9 @@ mod tests { #[test] fn uint128_json() { let orig = Uint128(1234567890987654321); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Uint128 = from_slice(&serialized).unwrap(); + let parsed: Uint128 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } diff --git a/packages/std/src/math/uint256.rs b/packages/std/src/math/uint256.rs index 6422dee76..a4a672ad8 100644 --- a/packages/std/src/math/uint256.rs +++ b/packages/std/src/math/uint256.rs @@ -13,12 +13,18 @@ use crate::errors::{ CheckedMultiplyFractionError, CheckedMultiplyRatioError, ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, StdError, }; -use crate::{forward_ref_partial_eq, impl_mul_fraction, Fraction, Uint128, Uint512, Uint64}; +use crate::{ + forward_ref_partial_eq, impl_mul_fraction, Fraction, Int128, Int256, Int512, Int64, Uint128, + Uint512, Uint64, +}; /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. use bnum::types::U256; +use super::conversion::{forward_try_from, try_from_int_to_uint}; +use super::num_consts::NumConsts; + /// An implementation of u256 that is using strings for JSON encoding/decoding, /// such that the full u256 range can be used for clients that convert JSON numbers to floats, /// like JavaScript and jq. @@ -335,6 +341,13 @@ impl Uint256 { } } +impl NumConsts for Uint256 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + impl_mul_fraction!(Uint256); impl From for Uint256 { @@ -379,15 +392,14 @@ impl From for Uint256 { } } -impl TryFrom for Uint128 { - type Error = ConversionOverflowError; +forward_try_from!(Uint256, Uint128); +forward_try_from!(Uint256, Uint64); - fn try_from(value: Uint256) -> Result { - Ok(Uint128::new(value.0.try_into().map_err(|_| { - ConversionOverflowError::new("Uint256", "Uint128", value.to_string()) - })?)) - } -} +// Int to Uint +try_from_int_to_uint!(Int64, Uint256); +try_from_int_to_uint!(Int128, Uint256); +try_from_int_to_uint!(Int256, Uint256); +try_from_int_to_uint!(Int512, Uint256); impl TryFrom<&str> for Uint256 { type Error = StdError; @@ -667,7 +679,8 @@ where mod tests { use super::*; use crate::errors::CheckedMultiplyFractionError::{ConversionOverflow, DivideByZero}; - use crate::{from_slice, to_vec, Decimal, Decimal256}; + use crate::math::conversion::test_try_from_int_to_uint; + use crate::{from_json, to_json_vec, Decimal, Decimal256}; #[test] fn size_of_works() { @@ -1062,6 +1075,32 @@ mod tests { assert!(result.is_err()); } + #[test] + fn uint256_try_from_signed_works() { + test_try_from_int_to_uint::("Int64", "Uint256"); + test_try_from_int_to_uint::("Int128", "Uint256"); + test_try_from_int_to_uint::("Int256", "Uint256"); + test_try_from_int_to_uint::("Int512", "Uint256"); + } + + #[test] + fn uint256_try_into() { + assert!(Uint64::try_from(Uint256::MAX).is_err()); + assert!(Uint128::try_from(Uint256::MAX).is_err()); + + assert_eq!(Uint64::try_from(Uint256::zero()), Ok(Uint64::zero())); + assert_eq!(Uint128::try_from(Uint256::zero()), Ok(Uint128::zero())); + + assert_eq!( + Uint64::try_from(Uint256::from(42u64)), + Ok(Uint64::from(42u64)) + ); + assert_eq!( + Uint128::try_from(Uint256::from(42u128)), + Ok(Uint128::from(42u128)) + ); + } + #[test] fn uint256_convert_to_uint128() { let source = Uint256::from(42u128); @@ -1267,9 +1306,9 @@ mod tests { #[test] fn uint256_json() { let orig = Uint256::from(1234567890987654321u128); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Uint256 = from_slice(&serialized).unwrap(); + let parsed: Uint256 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } diff --git a/packages/std/src/math/uint512.rs b/packages/std/src/math/uint512.rs index c4dd9e3f5..30be022a6 100644 --- a/packages/std/src/math/uint512.rs +++ b/packages/std/src/math/uint512.rs @@ -11,12 +11,15 @@ use serde::{de, ser, Deserialize, Deserializer, Serialize}; use crate::errors::{ ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, StdError, }; -use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint64}; +use crate::{forward_ref_partial_eq, Int128, Int256, Int512, Int64, Uint128, Uint256, Uint64}; /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. use bnum::types::U512; +use super::conversion::{forward_try_from, try_from_int_to_uint}; +use super::num_consts::NumConsts; + /// An implementation of u512 that is using strings for JSON encoding/decoding, /// such that the full u512 range can be used for clients that convert JSON numbers to floats, /// like JavaScript and jq. @@ -300,6 +303,13 @@ impl Uint512 { } } +impl NumConsts for Uint512 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + impl From for Uint512 { fn from(val: Uint256) -> Self { let mut bytes = [0u8; 64]; @@ -370,15 +380,14 @@ impl TryFrom for Uint256 { } } -impl TryFrom for Uint128 { - type Error = ConversionOverflowError; +forward_try_from!(Uint512, Uint128); +forward_try_from!(Uint512, Uint64); - fn try_from(value: Uint512) -> Result { - Ok(Uint128::new(value.0.try_into().map_err(|_| { - ConversionOverflowError::new("Uint512", "Uint128", value.to_string()) - })?)) - } -} +// Int to Uint +try_from_int_to_uint!(Int64, Uint512); +try_from_int_to_uint!(Int128, Uint512); +try_from_int_to_uint!(Int256, Uint512); +try_from_int_to_uint!(Int512, Uint512); impl TryFrom<&str> for Uint512 { type Error = StdError; @@ -637,7 +646,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, math::conversion::test_try_from_int_to_uint, to_json_vec}; #[test] fn size_of_works() { @@ -748,6 +757,38 @@ mod tests { assert!(result.is_err()); } + #[test] + fn uint512_try_from_signed_works() { + test_try_from_int_to_uint::("Int64", "Uint512"); + test_try_from_int_to_uint::("Int128", "Uint512"); + test_try_from_int_to_uint::("Int256", "Uint512"); + test_try_from_int_to_uint::("Int512", "Uint512"); + } + + #[test] + fn uint512_try_into() { + assert!(Uint64::try_from(Uint512::MAX).is_err()); + assert!(Uint128::try_from(Uint512::MAX).is_err()); + assert!(Uint256::try_from(Uint512::MAX).is_err()); + + assert_eq!(Uint64::try_from(Uint512::zero()), Ok(Uint64::zero())); + assert_eq!(Uint128::try_from(Uint512::zero()), Ok(Uint128::zero())); + assert_eq!(Uint256::try_from(Uint512::zero()), Ok(Uint256::zero())); + + assert_eq!( + Uint64::try_from(Uint512::from(42u64)), + Ok(Uint64::from(42u64)) + ); + assert_eq!( + Uint128::try_from(Uint512::from(42u128)), + Ok(Uint128::from(42u128)) + ); + assert_eq!( + Uint256::try_from(Uint512::from(42u128)), + Ok(Uint256::from(42u128)) + ); + } + #[test] fn uint512_convert_to_uint128() { let source = Uint512::from(42u128); @@ -971,9 +1012,9 @@ mod tests { #[test] fn uint512_json() { let orig = Uint512::from(1234567890987654321u128); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Uint512 = from_slice(&serialized).unwrap(); + let parsed: Uint512 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } diff --git a/packages/std/src/math/uint64.rs b/packages/std/src/math/uint64.rs index f405b2364..f168f893c 100644 --- a/packages/std/src/math/uint64.rs +++ b/packages/std/src/math/uint64.rs @@ -12,7 +12,12 @@ use crate::errors::{ CheckedMultiplyFractionError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, OverflowOperation, StdError, }; -use crate::{forward_ref_partial_eq, impl_mul_fraction, Fraction, Uint128}; +use crate::{ + forward_ref_partial_eq, impl_mul_fraction, Fraction, Int128, Int256, Int512, Int64, Uint128, +}; + +use super::conversion::forward_try_from; +use super::num_consts::NumConsts; /// A thin wrapper around u64 that is using strings for JSON encoding/decoding, /// such that the full u64 range can be used for clients that convert JSON numbers to floats, @@ -262,6 +267,13 @@ impl Uint64 { } } +impl NumConsts for Uint64 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + impl_mul_fraction!(Uint64); // `From` is implemented manually instead of @@ -269,6 +281,7 @@ impl_mul_fraction!(Uint64); // of the conflict with `TryFrom<&str>` as described here // https://stackoverflow.com/questions/63136970/how-do-i-work-around-the-upstream-crates-may-add-a-new-impl-of-trait-error +// uint to Uint impl From for Uint64 { fn from(val: u64) -> Self { Uint64(val) @@ -293,6 +306,12 @@ impl From for Uint64 { } } +// Int to Uint +forward_try_from!(Int64, Uint64); +forward_try_from!(Int128, Uint64); +forward_try_from!(Int256, Uint64); +forward_try_from!(Int512, Uint64); + impl TryFrom<&str> for Uint64 { type Error = StdError; @@ -559,7 +578,8 @@ where mod tests { use super::*; use crate::errors::CheckedMultiplyFractionError::{ConversionOverflow, DivideByZero}; - use crate::{from_slice, to_vec, ConversionOverflowError}; + use crate::math::conversion::test_try_from_int_to_uint; + use crate::{from_json, to_json_vec, ConversionOverflowError}; #[test] fn size_of_works() { @@ -618,6 +638,14 @@ mod tests { assert!(result.is_err()); } + #[test] + fn uint64_try_from_signed_works() { + test_try_from_int_to_uint::("Int64", "Uint64"); + test_try_from_int_to_uint::("Int128", "Uint64"); + test_try_from_int_to_uint::("Int256", "Uint64"); + test_try_from_int_to_uint::("Int512", "Uint64"); + } + #[test] fn uint64_implements_display() { let a = Uint64(12345); @@ -682,9 +710,9 @@ mod tests { #[test] fn uint64_json() { let orig = Uint64(1234567890987654321); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Uint64 = from_slice(&serialized).unwrap(); + let parsed: Uint64 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } diff --git a/packages/std/src/query/wasm.rs b/packages/std/src/query/wasm.rs index 9487ff592..a80008e29 100644 --- a/packages/std/src/query/wasm.rs +++ b/packages/std/src/query/wasm.rs @@ -94,14 +94,14 @@ impl QueryResponseType for CodeInfoResponse {} #[cfg(test)] mod tests { use super::*; - use crate::to_binary; + use crate::to_json_binary; #[test] fn wasm_query_contract_info_serialization() { let query = WasmQuery::ContractInfo { contract_addr: "aabbccdd456".into(), }; - let json = to_binary(&query).unwrap(); + let json = to_json_binary(&query).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"contract_info":{"contract_addr":"aabbccdd456"}}"#, @@ -112,7 +112,7 @@ mod tests { #[cfg(feature = "cosmwasm_1_2")] fn wasm_query_code_info_serialization() { let query = WasmQuery::CodeInfo { code_id: 70 }; - let json = to_binary(&query).unwrap(); + let json = to_json_binary(&query).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"code_info":{"code_id":70}}"#, @@ -128,7 +128,7 @@ mod tests { pinned: true, ibc_port: Some("wasm.123".to_string()), }; - let json = to_binary(&response).unwrap(); + let json = to_json_binary(&response).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"code_id":67,"creator":"jane","admin":"king","pinned":true,"ibc_port":"wasm.123"}"#, @@ -148,7 +148,7 @@ mod tests { ) .unwrap(), }; - let json = to_binary(&response).unwrap(); + let json = to_json_binary(&response).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"code_id":67,"creator":"jane","checksum":"f7bb7b18fb01bbf425cf4ed2cd4b7fb26a019a7fc75a4dc87e8a0b768c501f00"}"#, diff --git a/packages/std/src/results/contract_result.rs b/packages/std/src/results/contract_result.rs index 343da57c8..a55c32e3c 100644 --- a/packages/std/src/results/contract_result.rs +++ b/packages/std/src/results/contract_result.rs @@ -88,59 +88,59 @@ impl From> for Result { #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec, Response, StdError, StdResult}; + use crate::{from_json, to_json_vec, Response, StdError, StdResult}; #[test] fn contract_result_serialization_works() { let result = ContractResult::Ok(12); - assert_eq!(&to_vec(&result).unwrap(), b"{\"ok\":12}"); + assert_eq!(&to_json_vec(&result).unwrap(), b"{\"ok\":12}"); let result = ContractResult::Ok("foo"); - assert_eq!(&to_vec(&result).unwrap(), b"{\"ok\":\"foo\"}"); + assert_eq!(&to_json_vec(&result).unwrap(), b"{\"ok\":\"foo\"}"); let result: ContractResult = ContractResult::Ok(Response::default()); assert_eq!( - to_vec(&result).unwrap(), + to_json_vec(&result).unwrap(), br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"# ); let result: ContractResult = ContractResult::Err("broken".to_string()); - assert_eq!(&to_vec(&result).unwrap(), b"{\"error\":\"broken\"}"); + assert_eq!(&to_json_vec(&result).unwrap(), b"{\"error\":\"broken\"}"); } #[test] fn contract_result_deserialization_works() { - let result: ContractResult = from_slice(br#"{"ok":12}"#).unwrap(); + let result: ContractResult = from_json(br#"{"ok":12}"#).unwrap(); assert_eq!(result, ContractResult::Ok(12)); - let result: ContractResult = from_slice(br#"{"ok":"foo"}"#).unwrap(); + let result: ContractResult = from_json(br#"{"ok":"foo"}"#).unwrap(); assert_eq!(result, ContractResult::Ok("foo".to_string())); let result: ContractResult = - from_slice(br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#) + from_json(br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#) .unwrap(); assert_eq!(result, ContractResult::Ok(Response::default())); - let result: ContractResult = from_slice(br#"{"error":"broken"}"#).unwrap(); + let result: ContractResult = from_json(br#"{"error":"broken"}"#).unwrap(); assert_eq!(result, ContractResult::Err("broken".to_string())); // ignores whitespace - let result: ContractResult = from_slice(b" {\n\t \"ok\": 5898\n} ").unwrap(); + let result: ContractResult = from_json(b" {\n\t \"ok\": 5898\n} ").unwrap(); assert_eq!(result, ContractResult::Ok(5898)); // fails for additional attributes - let parse: StdResult> = from_slice(br#"{"unrelated":321,"ok":4554}"#); + let parse: StdResult> = from_json(br#"{"unrelated":321,"ok":4554}"#); match parse.unwrap_err() { StdError::ParseErr { .. } => {} err => panic!("Unexpected error: {err:?}"), } - let parse: StdResult> = from_slice(br#"{"ok":4554,"unrelated":321}"#); + let parse: StdResult> = from_json(br#"{"ok":4554,"unrelated":321}"#); match parse.unwrap_err() { StdError::ParseErr { .. } => {} err => panic!("Unexpected error: {err:?}"), } let parse: StdResult> = - from_slice(br#"{"ok":4554,"error":"What's up now?"}"#); + from_json(br#"{"ok":4554,"error":"What's up now?"}"#); match parse.unwrap_err() { StdError::ParseErr { .. } => {} err => panic!("Unexpected error: {err:?}"), diff --git a/packages/std/src/results/cosmos_msg.rs b/packages/std/src/results/cosmos_msg.rs index 1a8bd0eb6..dc886ab88 100644 --- a/packages/std/src/results/cosmos_msg.rs +++ b/packages/std/src/results/cosmos_msg.rs @@ -8,7 +8,7 @@ use crate::coin::Coin; use crate::errors::StdResult; #[cfg(feature = "stargate")] use crate::ibc::IbcMsg; -use crate::serde::to_binary; +use crate::serde::to_json_binary; #[cfg(all(feature = "stargate", feature = "cosmwasm_1_2"))] use crate::Decimal; @@ -336,7 +336,7 @@ pub fn wasm_instantiate( funds: Vec, label: String, ) -> StdResult { - let payload = to_binary(msg)?; + let payload = to_json_binary(msg)?; Ok(WasmMsg::Instantiate { admin: None, code_id, @@ -352,7 +352,7 @@ pub fn wasm_execute( msg: &impl Serialize, funds: Vec, ) -> StdResult { - let payload = to_binary(msg)?; + let payload = to_json_binary(msg)?; Ok(WasmMsg::Execute { contract_addr: contract_addr.into(), msg: payload, @@ -427,7 +427,7 @@ mod tests { funds: vec![], label: "my instance".to_string(), }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"instantiate":{"admin":"king","code_id":7897,"msg":"eyJjbGFpbSI6e319","funds":[],"label":"my instance"}}"#, @@ -441,7 +441,7 @@ mod tests { funds: vec![], label: "my instance".to_string(), }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"instantiate":{"admin":null,"code_id":7897,"msg":"eyJjbGFpbSI6e319","funds":[],"label":"my instance"}}"#, @@ -455,7 +455,7 @@ mod tests { funds: vec![coin(321, "stones")], label: "my instance".to_string(), }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"instantiate":{"admin":null,"code_id":7897,"msg":"eyJjbGFpbSI6e319","funds":[{"denom":"stones","amount":"321"}],"label":"my instance"}}"#, @@ -472,7 +472,7 @@ mod tests { funds: vec![coin(321, "stones")], salt: Binary::from_base64("UkOVazhiwoo=").unwrap(), }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"instantiate2":{"admin":null,"code_id":7897,"label":"my instance","msg":"eyJjbGFpbSI6e319","funds":[{"denom":"stones","amount":"321"}],"salt":"UkOVazhiwoo="}}"#, @@ -486,7 +486,7 @@ mod tests { // FundCommunityPool let fund_coins = vec![coin(200, "feathers"), coin(200, "stones")]; let fund_msg = DistributionMsg::FundCommunityPool { amount: fund_coins }; - let fund_json = to_binary(&fund_msg).unwrap(); + let fund_json = to_json_binary(&fund_msg).unwrap(); assert_eq!( String::from_utf8_lossy(&fund_json), r#"{"fund_community_pool":{"amount":[{"denom":"feathers","amount":"200"},{"denom":"stones","amount":"200"}]}}"#, @@ -496,7 +496,7 @@ mod tests { let set_msg = DistributionMsg::SetWithdrawAddress { address: String::from("withdrawer"), }; - let set_json = to_binary(&set_msg).unwrap(); + let set_json = to_json_binary(&set_msg).unwrap(); assert_eq!( String::from_utf8_lossy(&set_json), r#"{"set_withdraw_address":{"address":"withdrawer"}}"#, @@ -506,7 +506,7 @@ mod tests { let withdraw_msg = DistributionMsg::WithdrawDelegatorReward { validator: String::from("fancyoperator"), }; - let withdraw_json = to_binary(&withdraw_msg).unwrap(); + let withdraw_json = to_json_binary(&withdraw_msg).unwrap(); assert_eq!( String::from_utf8_lossy(&withdraw_json), r#"{"withdraw_delegator_reward":{"validator":"fancyoperator"}}"# @@ -522,7 +522,7 @@ mod tests { let msg = WasmMsg::Execute { contract_addr: "joe".to_string(), - msg: to_binary(&ExecuteMsg::Mint { + msg: to_json_binary(&ExecuteMsg::Mint { coin: coin(10, "BTC"), }) .unwrap(), @@ -557,7 +557,7 @@ mod tests { proposal_id: 4, vote: VoteOption::NoWithVeto, }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"vote":{"proposal_id":4,"vote":"no_with_veto"}}"#, @@ -584,7 +584,7 @@ mod tests { ], }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"vote_weighted":{"proposal_id":25,"options":[{"option":"yes","weight":"0.25"},{"option":"no","weight":"0.25"},{"option":"abstain","weight":"0.5"}]}}"#, diff --git a/packages/std/src/results/empty.rs b/packages/std/src/results/empty.rs index 56ccf3586..91e3b581b 100644 --- a/packages/std/src/results/empty.rs +++ b/packages/std/src/results/empty.rs @@ -14,7 +14,7 @@ pub struct Empty {} mod tests { use super::*; - use crate::serde::{from_slice, to_vec}; + use crate::serde::{from_json, to_json_vec}; #[test] fn empty_can_be_instantiated() { @@ -25,13 +25,13 @@ mod tests { #[test] fn empty_can_be_instantiated_serialized_and_deserialized() { let instance = Empty {}; - let serialized = to_vec(&instance).unwrap(); + let serialized = to_json_vec(&instance).unwrap(); assert_eq!(serialized, b"{}"); - let deserialized: Empty = from_slice(b"{}").unwrap(); + let deserialized: Empty = from_json(b"{}").unwrap(); assert_eq!(deserialized, instance); - let deserialized: Empty = from_slice(b"{\"stray\":\"data\"}").unwrap(); + let deserialized: Empty = from_json(b"{\"stray\":\"data\"}").unwrap(); assert_eq!(deserialized, instance); } } diff --git a/packages/std/src/results/events.rs b/packages/std/src/results/events.rs index 195cd9161..0ecf17edc 100644 --- a/packages/std/src/results/events.rs +++ b/packages/std/src/results/events.rs @@ -9,7 +9,7 @@ use crate::forward_ref_partial_eq; /// which then get magically converted to bytes for Tendermint somewhere between /// the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK. /// -/// [*Cosmos SDK* event]: https://docs.cosmos.network/main/core/events.html +/// [*Cosmos SDK* event]: https://docs.cosmos.network/main/learn/advanced/events /// [*Cosmos SDK* StringEvent]: https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[non_exhaustive] @@ -21,7 +21,7 @@ pub struct Event { /// /// You can learn more about these from [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub attributes: Vec, } diff --git a/packages/std/src/results/response.rs b/packages/std/src/results/response.rs index 88f04e5c8..09295b7ee 100644 --- a/packages/std/src/results/response.rs +++ b/packages/std/src/results/response.rs @@ -72,14 +72,14 @@ pub struct Response { /// /// More info about events (and their attributes) can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub attributes: Vec, /// Extra, custom events separate from the main `wasm` one. These will have /// `wasm-` prepended to the type. /// /// More info about events can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub events: Vec, /// The binary payload to include in the response. pub data: Option, @@ -242,7 +242,9 @@ mod tests { use super::super::BankMsg; use super::*; use crate::results::submessages::{ReplyOn, UNUSED_MSG_ID}; - use crate::{attr, coins, from_slice, to_vec, Addr, Coin, ContractResult, Event, IntoEvent}; + use crate::{ + attr, coins, from_json, to_json_vec, Addr, Coin, ContractResult, Event, IntoEvent, + }; #[test] fn response_add_attributes_works() { @@ -315,8 +317,8 @@ mod tests { events: vec![], data: Some(Binary::from([0xAA, 0xBB])), }; - let serialized = to_vec(&original).expect("encode contract result"); - let deserialized: Response = from_slice(&serialized).expect("decode contract result"); + let serialized = to_json_vec(&original).expect("encode contract result"); + let deserialized: Response = from_json(serialized).expect("decode contract result"); assert_eq!(deserialized, original); } diff --git a/packages/std/src/results/submessages.rs b/packages/std/src/results/submessages.rs index f854e7832..7aa7d7ce7 100644 --- a/packages/std/src/results/submessages.rs +++ b/packages/std/src/results/submessages.rs @@ -206,7 +206,7 @@ pub type SubMsgExecutionResponse = SubMsgResponse; #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec, StdError, StdResult}; + use crate::{from_json, to_json_vec, StdError, StdResult}; #[test] fn sub_msg_result_serialization_works() { @@ -215,7 +215,7 @@ mod tests { events: vec![], }); assert_eq!( - &to_vec(&result).unwrap(), + &to_json_vec(&result).unwrap(), br#"{"ok":{"events":[],"data":null}}"# ); @@ -224,17 +224,17 @@ mod tests { events: vec![Event::new("wasm").add_attribute("fo", "ba")], }); assert_eq!( - &to_vec(&result).unwrap(), + &to_json_vec(&result).unwrap(), br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"# ); let result: SubMsgResult = SubMsgResult::Err("broken".to_string()); - assert_eq!(&to_vec(&result).unwrap(), b"{\"error\":\"broken\"}"); + assert_eq!(&to_json_vec(&result).unwrap(), b"{\"error\":\"broken\"}"); } #[test] fn sub_msg_result_deserialization_works() { - let result: SubMsgResult = from_slice(br#"{"ok":{"events":[],"data":null}}"#).unwrap(); + let result: SubMsgResult = from_json(br#"{"ok":{"events":[],"data":null}}"#).unwrap(); assert_eq!( result, SubMsgResult::Ok(SubMsgResponse { @@ -243,7 +243,7 @@ mod tests { }) ); - let result: SubMsgResult = from_slice( + let result: SubMsgResult = from_json( br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#).unwrap(); assert_eq!( result, @@ -253,16 +253,16 @@ mod tests { }) ); - let result: SubMsgResult = from_slice(br#"{"error":"broken"}"#).unwrap(); + let result: SubMsgResult = from_json(br#"{"error":"broken"}"#).unwrap(); assert_eq!(result, SubMsgResult::Err("broken".to_string())); // fails for additional attributes - let parse: StdResult = from_slice(br#"{"unrelated":321,"error":"broken"}"#); + let parse: StdResult = from_json(br#"{"unrelated":321,"error":"broken"}"#); match parse.unwrap_err() { StdError::ParseErr { .. } => {} err => panic!("Unexpected error: {err:?}"), } - let parse: StdResult = from_slice(br#"{"error":"broken","unrelated":321}"#); + let parse: StdResult = from_json(br#"{"error":"broken","unrelated":321}"#); match parse.unwrap_err() { StdError::ParseErr { .. } => {} err => panic!("Unexpected error: {err:?}"), diff --git a/packages/std/src/serde.rs b/packages/std/src/serde.rs index 5b258fd1f..23793698b 100644 --- a/packages/std/src/serde.rs +++ b/packages/std/src/serde.rs @@ -8,26 +8,62 @@ use serde::{de::DeserializeOwned, Serialize}; use crate::binary::Binary; use crate::errors::{StdError, StdResult}; +#[deprecated = "use from_json instead"] pub fn from_slice(value: &[u8]) -> StdResult { - serde_json_wasm::from_slice(value).map_err(|e| StdError::parse_err(type_name::(), e)) + from_json(value) } +#[deprecated = "use from_json instead"] pub fn from_binary(value: &Binary) -> StdResult { - from_slice(value.as_slice()) + from_json(value) } +/// Deserializes the given JSON bytes to a data structure. +/// +/// Errors if the input is not valid JSON or cannot be deserialized to the given type. +pub fn from_json(value: impl AsRef<[u8]>) -> StdResult { + serde_json_wasm::from_slice(value.as_ref()) + .map_err(|e| StdError::parse_err(type_name::(), e)) +} + +#[deprecated = "use to_json_vec instead"] pub fn to_vec(data: &T) -> StdResult> where T: Serialize + ?Sized, { - serde_json_wasm::to_vec(data).map_err(|e| StdError::serialize_err(type_name::(), e)) + to_json_vec(data) } +#[deprecated = "use to_json_binary instead"] pub fn to_binary(data: &T) -> StdResult where T: Serialize + ?Sized, { - to_vec(data).map(Binary) + to_json_binary(data) +} + +/// Serializes the given data structure as a JSON byte vector. +pub fn to_json_vec(data: &T) -> StdResult> +where + T: Serialize + ?Sized, +{ + serde_json_wasm::to_vec(data).map_err(|e| StdError::serialize_err(type_name::(), e)) +} + +/// Serializes the given data structure as a JSON string. +pub fn to_json_string(data: &T) -> StdResult +where + T: Serialize + ?Sized, +{ + serde_json_wasm::to_string(data).map_err(|e| StdError::serialize_err(type_name::(), e)) +} + +/// Serializes the given data structure as JSON bytes. +pub fn to_json_binary(data: &T) -> StdResult +where + T: Serialize + ?Sized, +{ + to_json_vec(data).map(Binary) } #[cfg(test)] @@ -51,9 +87,9 @@ mod tests { } #[test] - fn to_vec_works() { + fn to_json_vec_works() { let msg = SomeMsg::Refund {}; - let serialized = to_vec(&msg).unwrap(); + let serialized = to_json_vec(&msg).unwrap(); assert_eq!(serialized, br#"{"refund":{}}"#); let msg = SomeMsg::ReleaseAll { @@ -62,7 +98,7 @@ mod tests { time: 9007199254740999, // Number.MAX_SAFE_INTEGER + 7 karma: -17, }; - let serialized = String::from_utf8(to_vec(&msg).unwrap()).unwrap(); + let serialized = String::from_utf8(to_json_vec(&msg).unwrap()).unwrap(); assert_eq!( serialized, r#"{"release_all":{"image":"foo","amount":42,"time":9007199254740999,"karma":-17}}"# @@ -70,50 +106,55 @@ mod tests { } #[test] - fn from_slice_works() { - let deserialized: SomeMsg = from_slice(br#"{"refund":{}}"#).unwrap(); + fn from_json_works() { + let deserialized: SomeMsg = from_json(br#"{"refund":{}}"#).unwrap(); assert_eq!(deserialized, SomeMsg::Refund {}); - let deserialized: SomeMsg = from_slice( + let expected = SomeMsg::ReleaseAll { + image: "foo".to_string(), + amount: 42, + time: 18446744073709551615, + karma: -17, + }; + // &[u8] + let deserialized: SomeMsg = from_json( br#"{"release_all":{"image":"foo","amount":42,"time":18446744073709551615,"karma":-17}}"#, ) .unwrap(); - assert_eq!( - deserialized, - SomeMsg::ReleaseAll { - image: "foo".to_string(), - amount: 42, - time: 18446744073709551615, - karma: -17 - } - ); + assert_eq!(deserialized, expected); + + // &str + let deserialized: SomeMsg = from_json( + r#"{"release_all":{"image":"foo","amount":42,"time":18446744073709551615,"karma":-17}}"#, + ) + .unwrap(); + assert_eq!(deserialized, expected); } #[test] - fn from_slice_or_binary() { + fn from_json_or_binary() { let msg = SomeMsg::Refund {}; - let serialized: Binary = to_binary(&msg).unwrap(); + let serialized: Binary = to_json_binary(&msg).unwrap(); - let parse_binary: SomeMsg = from_binary(&serialized).unwrap(); + let parse_binary: SomeMsg = from_json(&serialized).unwrap(); assert_eq!(parse_binary, msg); - let parse_slice: SomeMsg = from_slice(&serialized).unwrap(); + let parse_slice: SomeMsg = from_json(serialized.as_slice()).unwrap(); assert_eq!(parse_slice, msg); } #[test] - fn to_vec_works_for_special_chars() { + fn to_json_vec_works_for_special_chars() { let msg = SomeMsg::Cowsay { text: "foo\"bar\\\"bla".to_string(), }; - let serialized = String::from_utf8(to_vec(&msg).unwrap()).unwrap(); + let serialized = String::from_utf8(to_json_vec(&msg).unwrap()).unwrap(); assert_eq!(serialized, r#"{"cowsay":{"text":"foo\"bar\\\"bla"}}"#); } #[test] - fn from_slice_works_for_special_chars() { - let deserialized: SomeMsg = - from_slice(br#"{"cowsay":{"text":"foo\"bar\\\"bla"}}"#).unwrap(); + fn from_json_works_for_special_chars() { + let deserialized: SomeMsg = from_json(br#"{"cowsay":{"text":"foo\"bar\\\"bla"}}"#).unwrap(); assert_eq!( deserialized, SomeMsg::Cowsay { @@ -121,4 +162,23 @@ mod tests { } ); } + + #[test] + fn to_json_string_works() { + let msg = SomeMsg::Refund {}; + let serialized = to_json_string(&msg).unwrap(); + assert_eq!(serialized, r#"{"refund":{}}"#); + + let msg = SomeMsg::ReleaseAll { + image: "foo".to_string(), + amount: 42, + time: 9007199254740999, // Number.MAX_SAFE_INTEGER + 7 + karma: -17, + }; + let serialized = to_json_string(&msg).unwrap(); + assert_eq!( + serialized, + r#"{"release_all":{"image":"foo","amount":42,"time":9007199254740999,"karma":-17}}"# + ); + } } diff --git a/packages/std/src/stdack.rs b/packages/std/src/stdack.rs index e933257a0..64c711ef5 100644 --- a/packages/std/src/stdack.rs +++ b/packages/std/src/stdack.rs @@ -2,7 +2,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::binary::Binary; -use crate::to_binary; +use crate::to_json_binary; /// This is a standard IBC acknowledgement type. IBC application are free /// to use any acknowledgement format they want. However, for compatibility @@ -94,7 +94,7 @@ impl StdAck { // We need a non-failing StdAck -> Binary conversion to allow using StdAck in // `impl Into` arguments. // Pretty sure this cannot fail. If that changes we can create a non-failing implementation here. - to_binary(&self).unwrap() + to_json_binary(&self).unwrap() } pub fn unwrap(self) -> Binary { diff --git a/packages/std/src/testing/assertions.rs b/packages/std/src/testing/assertions.rs index d74736007..fa3ab6c7f 100644 --- a/packages/std/src/testing/assertions.rs +++ b/packages/std/src/testing/assertions.rs @@ -82,6 +82,7 @@ pub fn assert_hash_works_impl(left: T, right: T, panic_msg } // Check clone + #[allow(clippy::redundant_clone)] let clone = left.clone(); if left != clone { do_panic("assertion failed: `left == left.clone()`", panic_msg); diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index f7cec5430..c49f85338 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -1,10 +1,12 @@ use alloc::collections::BTreeMap; +use bech32::{encode, ToBase32, Variant}; use core::marker::PhantomData; #[cfg(feature = "cosmwasm_1_3")] use core::ops::Bound; use serde::de::DeserializeOwned; #[cfg(feature = "stargate")] use serde::Serialize; +use sha2::{Digest, Sha256}; #[cfg(feature = "cosmwasm_1_3")] use std::collections::BTreeSet; use std::collections::HashMap; @@ -36,7 +38,7 @@ use crate::query::{ #[cfg(feature = "cosmwasm_1_3")] use crate::query::{DelegatorWithdrawAddressResponse, DistributionQuery}; use crate::results::{ContractResult, Empty, SystemResult}; -use crate::serde::{from_slice, to_binary}; +use crate::serde::{from_json, to_json_binary}; use crate::storage::MemoryStorage; use crate::timestamp::Timestamp; use crate::traits::{Api, Querier, QuerierResult}; @@ -108,20 +110,26 @@ const CANONICAL_LENGTH: usize = 90; // n = 45 const SHUFFLES_ENCODE: usize = 10; const SHUFFLES_DECODE: usize = 2; -// MockPrecompiles zero pads all human addresses to make them fit the canonical_length +/// Default prefix used when creating Bech32 encoded address. +const BECH32_PREFIX: &str = "cosmwasm"; + +// MockApi zero pads all human addresses to make them fit the canonical_length // it trims off zeros for the reverse operation. -// not really smart, but allows us to see a difference (and consistent length for canonical adddresses) +// not really smart, but allows us to see a difference (and consistent length for canonical addresses) #[derive(Copy, Clone)] pub struct MockApi { /// Length of canonical addresses created with this API. Contracts should not make any assumptions /// what this value is. canonical_length: usize, + /// Prefix used for creating addresses in Bech32 encoding. + bech32_prefix: &'static str, } impl Default for MockApi { fn default() -> Self { MockApi { canonical_length: CANONICAL_LENGTH, + bech32_prefix: BECH32_PREFIX, } } } @@ -154,7 +162,7 @@ impl Api for MockApi { )); } - // mimicks formats like hex or bech32 where different casings are valid for one address + // mimics formats like hex or bech32 where different casings are valid for one address let normalized = input.to_lowercase(); let mut out = Vec::from(normalized); @@ -250,6 +258,55 @@ impl Api for MockApi { } } +impl MockApi { + /// Returns [MockApi] with Bech32 prefix set to provided value. + /// + /// Bech32 prefix must not be empty. + /// + /// # Example + /// + /// ``` + /// # use cosmwasm_std::Addr; + /// # use cosmwasm_std::testing::MockApi; + /// # + /// let mock_api = MockApi::default().with_prefix("juno"); + /// let addr = mock_api.addr_make("creator"); + /// + /// assert_eq!(addr.to_string(), "juno1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqsksmtyp"); + /// ``` + pub fn with_prefix(mut self, prefix: &'static str) -> Self { + self.bech32_prefix = prefix; + self + } + + /// Returns an address built from provided input string. + /// + /// # Example + /// + /// ``` + /// # use cosmwasm_std::Addr; + /// # use cosmwasm_std::testing::MockApi; + /// # + /// let mock_api = MockApi::default(); + /// let addr = mock_api.addr_make("creator"); + /// + /// assert_eq!(addr.to_string(), "cosmwasm1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqs8s7vcp"); + /// ``` + /// + /// # Panics + /// + /// This function panics when generating a valid address is not possible, + /// especially when Bech32 prefix set in function [with_prefix](Self::with_prefix) is empty. + /// + pub fn addr_make(&self, input: &str) -> Addr { + let digest = Sha256::digest(input).to_vec(); + match encode(self.bech32_prefix, digest.to_base32(), Variant::Bech32) { + Ok(address) => Addr::unchecked(address), + Err(reason) => panic!("Generating address failed with reason: {reason}"), + } + } +} + /// Returns a default enviroment with height, time, chain_id, and contract address /// You can submit as is to most contracts, or modify height/time if you want to /// test for expiration. @@ -366,7 +423,7 @@ pub fn mock_ibc_packet_recv( ) -> StdResult { Ok(IbcPacketReceiveMsg::new( IbcPacket { - data: to_binary(data)?, + data: to_json_binary(data)?, src: IbcEndpoint { port_id: "their-port".to_string(), channel_id: "channel-1234".to_string(), @@ -393,7 +450,7 @@ pub fn mock_ibc_packet_recv( #[cfg(feature = "stargate")] fn mock_ibc_packet(my_channel_id: &str, data: &impl Serialize) -> StdResult { Ok(IbcPacket { - data: to_binary(data)?, + data: to_json_binary(data)?, src: IbcEndpoint { port_id: "their-port".to_string(), channel_id: my_channel_id.into(), @@ -568,7 +625,7 @@ impl Default for MockQuerier { impl Querier for MockQuerier { fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { - let request: QueryRequest = match from_slice(bin_request) { + let request: QueryRequest = match from_json(bin_request) { Ok(v) => v, Err(e) => { return SystemResult::Err(SystemError::InvalidRequest { @@ -724,7 +781,7 @@ impl BankQuerier { denom: denom.to_string(), }, }; - to_binary(&bank_res).into() + to_json_binary(&bank_res).into() } BankQuery::Balance { address, denom } => { // proper error on not found, serialize result on found @@ -739,14 +796,14 @@ impl BankQuerier { denom: denom.to_string(), }, }; - to_binary(&bank_res).into() + to_json_binary(&bank_res).into() } BankQuery::AllBalances { address } => { // proper error on not found, serialize result on found let bank_res = AllBalanceResponse { amount: self.balances.get(address).cloned().unwrap_or_default(), }; - to_binary(&bank_res).into() + to_json_binary(&bank_res).into() } #[cfg(feature = "cosmwasm_1_3")] BankQuery::DenomMetadata { denom } => { @@ -756,7 +813,7 @@ impl BankQuerier { let metadata_res = DenomMetadataResponse { metadata: m.clone(), }; - to_binary(&metadata_res).into() + to_json_binary(&metadata_res).into() } None => return SystemResult::Err(SystemError::Unknown {}), } @@ -799,7 +856,7 @@ impl BankQuerier { }; let metadata_res = AllDenomMetadataResponse { metadata, next_key }; - to_binary(&metadata_res).into() + to_json_binary(&metadata_res).into() } }; // system result is always ok in the mock implementation @@ -844,7 +901,7 @@ impl IbcQuerier { }) .cloned(); let res = ChannelResponse { channel }; - to_binary(&res).into() + to_json_binary(&res).into() } IbcQuery::ListChannels { port_id } => { let channels = self @@ -857,13 +914,13 @@ impl IbcQuerier { .cloned() .collect(); let res = ListChannelsResponse { channels }; - to_binary(&res).into() + to_json_binary(&res).into() } IbcQuery::PortId {} => { let res = PortIdResponse { port_id: self.port_id.clone(), }; - to_binary(&res).into() + to_json_binary(&res).into() } }; // system result is always ok in the mock implementation @@ -895,13 +952,13 @@ impl StakingQuerier { let res = BondedDenomResponse { denom: self.denom.clone(), }; - to_binary(&res).into() + to_json_binary(&res).into() } StakingQuery::AllValidators {} => { let res = AllValidatorsResponse { validators: self.validators.clone(), }; - to_binary(&res).into() + to_json_binary(&res).into() } StakingQuery::Validator { address } => { let validator: Option = self @@ -910,7 +967,7 @@ impl StakingQuerier { .find(|validator| validator.address == *address) .cloned(); let res = ValidatorResponse { validator }; - to_binary(&res).into() + to_json_binary(&res).into() } StakingQuery::AllDelegations { delegator } => { let delegations: Vec<_> = self @@ -921,7 +978,7 @@ impl StakingQuerier { .map(|d| d.into()) .collect(); let res = AllDelegationsResponse { delegations }; - to_binary(&res).into() + to_json_binary(&res).into() } StakingQuery::Delegation { delegator, @@ -934,7 +991,7 @@ impl StakingQuerier { let res = DelegationResponse { delegation: delegation.cloned(), }; - to_binary(&res).into() + to_json_binary(&res).into() } }; // system result is always ok in the mock implementation @@ -1022,7 +1079,7 @@ impl DistributionQuerier { .unwrap_or(delegator_address), ), }; - to_binary(&res).into() + to_json_binary(&res).into() } #[cfg(feature = "cosmwasm_1_4")] DistributionQuery::DelegationRewards { @@ -1037,7 +1094,7 @@ impl DistributionQuerier { .cloned() .unwrap_or_default(), }; - to_binary(&res).into() + to_json_binary(&res).into() } #[cfg(feature = "cosmwasm_1_4")] DistributionQuery::DelegationTotalRewards { delegator_address } => { @@ -1063,7 +1120,7 @@ impl DistributionQuerier { .collect(), rewards: validator_rewards, }; - to_binary(&res).into() + to_json_binary(&res).into() } #[cfg(feature = "cosmwasm_1_4")] DistributionQuery::DelegatorValidators { delegator_address } => { @@ -1074,7 +1131,7 @@ impl DistributionQuerier { .map(|set| set.iter().cloned().collect()) .unwrap_or_default(), }; - to_binary(&res).into() + to_json_binary(&res).into() } }; // system result is always ok in the mock implementation @@ -1116,7 +1173,7 @@ mod tests { use super::*; #[cfg(feature = "cosmwasm_1_3")] use crate::DenomUnit; - use crate::{coin, coins, from_binary, to_binary, ContractInfoResponse, Response}; + use crate::{coin, coins, from_json, to_json_binary, ContractInfoResponse, Response}; #[cfg(feature = "staking")] use crate::{Decimal, Delegation}; use hex_literal::hex; @@ -1449,7 +1506,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: SupplyResponse = from_binary(&elf).unwrap(); + let res: SupplyResponse = from_json(elf).unwrap(); assert_eq!(res.amount, coin(444, "ELF")); let fly = bank @@ -1458,7 +1515,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: SupplyResponse = from_binary(&fly).unwrap(); + let res: SupplyResponse = from_json(fly).unwrap(); assert_eq!(res.amount, coin(777, "FLY")); // if a denom does not exist, should return zero amount, instead of throwing an error @@ -1468,7 +1525,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: SupplyResponse = from_binary(&atom).unwrap(); + let res: SupplyResponse = from_json(atom).unwrap(); assert_eq!(res.amount, coin(0, "ATOM")); } @@ -1482,7 +1539,7 @@ mod tests { .query(&BankQuery::AllBalances { address: addr }) .unwrap() .unwrap(); - let res: AllBalanceResponse = from_binary(&all).unwrap(); + let res: AllBalanceResponse = from_json(all).unwrap(); assert_eq!(&res.amount, &balance); } @@ -1500,7 +1557,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&fly).unwrap(); + let res: BalanceResponse = from_json(fly).unwrap(); assert_eq!(res.amount, coin(777, "FLY")); // missing denom @@ -1511,7 +1568,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&miss).unwrap(); + let res: BalanceResponse = from_json(miss).unwrap(); assert_eq!(res.amount, coin(0, "MISS")); } @@ -1528,7 +1585,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: AllBalanceResponse = from_binary(&all).unwrap(); + let res: AllBalanceResponse = from_json(all).unwrap(); assert_eq!(res.amount, vec![]); // any denom on balances on empty account is empty coin @@ -1539,7 +1596,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&miss).unwrap(); + let res: BalanceResponse = from_json(miss).unwrap(); assert_eq!(res.amount, coin(0, "ELF")); } @@ -1577,7 +1634,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: AllDenomMetadataResponse = from_binary(&res).unwrap(); + let res: AllDenomMetadataResponse = from_json(res).unwrap(); assert_eq!(res.metadata.len(), 10); assert!(res.next_key.is_some()); @@ -1592,7 +1649,7 @@ mod tests { }) .unwrap() .unwrap(); - let res2: AllDenomMetadataResponse = from_binary(&res2).unwrap(); + let res2: AllDenomMetadataResponse = from_json(res2).unwrap(); assert_eq!(res2.metadata.len(), 10); assert_ne!(res.metadata.last(), res2.metadata.first()); // should have no overlap @@ -1611,7 +1668,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: AllDenomMetadataResponse = from_binary(&res).unwrap(); + let res: AllDenomMetadataResponse = from_json(res).unwrap(); assert_eq!(res.metadata.len(), 100); assert!(res.next_key.is_none(), "no more data should be available"); assert_eq!(res.metadata[0].symbol, "FOO99", "should have been reversed"); @@ -1626,7 +1683,7 @@ mod tests { }) .unwrap() .unwrap(); - let more_res: AllDenomMetadataResponse = from_binary(&more_res).unwrap(); + let more_res: AllDenomMetadataResponse = from_json(more_res).unwrap(); assert_eq!( more_res.metadata, res.metadata, "should be same as previous query" @@ -1644,7 +1701,7 @@ mod tests { }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegatorWithdrawAddressResponse = from_binary(&res).unwrap(); + let res: DelegatorWithdrawAddressResponse = from_json(res).unwrap(); assert_eq!(res.withdraw_address, "withdraw0"); let query = DistributionQuery::DelegatorWithdrawAddress { @@ -1652,7 +1709,7 @@ mod tests { }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegatorWithdrawAddressResponse = from_binary(&res).unwrap(); + let res: DelegatorWithdrawAddressResponse = from_json(res).unwrap(); assert_eq!(res.withdraw_address, "addr1"); } @@ -1667,7 +1724,7 @@ mod tests { }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegatorValidatorsResponse = from_binary(&res).unwrap(); + let res: DelegatorValidatorsResponse = from_json(res).unwrap(); assert_eq!(res.validators, ["valoper1", "valoper2"]); let query = DistributionQuery::DelegatorValidators { @@ -1675,7 +1732,7 @@ mod tests { }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegatorValidatorsResponse = from_binary(&res).unwrap(); + let res: DelegatorValidatorsResponse = from_json(res).unwrap(); assert_eq!(res.validators, ([] as [String; 0])); } @@ -1697,7 +1754,7 @@ mod tests { validator_address: "valoper0".to_string(), }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegationRewardsResponse = from_binary(&res).unwrap(); + let res: DelegationRewardsResponse = from_json(res).unwrap(); assert_eq!(res.rewards, valoper0_rewards); // delegator does not exist @@ -1706,7 +1763,7 @@ mod tests { validator_address: "valoper0".to_string(), }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegationRewardsResponse = from_binary(&res).unwrap(); + let res: DelegationRewardsResponse = from_json(res).unwrap(); assert_eq!(res.rewards.len(), 0); // validator does not exist @@ -1715,7 +1772,7 @@ mod tests { validator_address: "valopernonexistent".to_string(), }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegationRewardsResponse = from_binary(&res).unwrap(); + let res: DelegationRewardsResponse = from_json(res).unwrap(); assert_eq!(res.rewards.len(), 0); // add one more validator @@ -1727,7 +1784,7 @@ mod tests { delegator_address: "addr0".to_string(), }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegationTotalRewardsResponse = from_binary(&res).unwrap(); + let res: DelegationTotalRewardsResponse = from_json(res).unwrap(); assert_eq!( res.rewards, vec![ @@ -1768,7 +1825,7 @@ mod tests { port_id: Some("my_port".to_string()), }; let raw = ibc.query(query).unwrap().unwrap(); - let chan: ChannelResponse = from_binary(&raw).unwrap(); + let chan: ChannelResponse = from_json(raw).unwrap(); assert_eq!(chan.channel, Some(chan1)); } @@ -1798,7 +1855,7 @@ mod tests { port_id: Some("myport".to_string()), }; let raw = ibc.query(query).unwrap().unwrap(); - let chan: ChannelResponse = from_binary(&raw).unwrap(); + let chan: ChannelResponse = from_json(raw).unwrap(); assert_eq!(chan.channel, Some(chan1)); } @@ -1816,7 +1873,7 @@ mod tests { port_id: None, }; let raw = ibc.query(query).unwrap().unwrap(); - let chan: ChannelResponse = from_binary(&raw).unwrap(); + let chan: ChannelResponse = from_json(raw).unwrap(); assert_eq!(chan.channel, None); } @@ -1833,7 +1890,7 @@ mod tests { port_id: Some("my_port".to_string()), }; let raw = ibc.query(query).unwrap().unwrap(); - let res: ListChannelsResponse = from_binary(&raw).unwrap(); + let res: ListChannelsResponse = from_json(raw).unwrap(); assert_eq!(res.channels, vec![chan1, chan2]); } @@ -1848,7 +1905,7 @@ mod tests { // query channels matching "myport" (should be none) let query = &IbcQuery::ListChannels { port_id: None }; let raw = ibc.query(query).unwrap().unwrap(); - let res: ListChannelsResponse = from_binary(&raw).unwrap(); + let res: ListChannelsResponse = from_json(raw).unwrap(); assert_eq!(res.channels, vec![]); } @@ -1862,7 +1919,7 @@ mod tests { // query channels matching "myport" (should be none) let query = &IbcQuery::PortId {}; let raw = ibc.query(query).unwrap().unwrap(); - let res: PortIdResponse = from_binary(&raw).unwrap(); + let res: PortIdResponse = from_json(raw).unwrap(); assert_eq!(res.port_id, "myport"); } @@ -1889,7 +1946,7 @@ mod tests { .query(&StakingQuery::AllValidators {}) .unwrap() .unwrap(); - let vals: AllValidatorsResponse = from_binary(&raw).unwrap(); + let vals: AllValidatorsResponse = from_json(raw).unwrap(); assert_eq!(vals.validators, vec![val1, val2]); } @@ -1920,7 +1977,7 @@ mod tests { .query(&StakingQuery::Validator { address: address1 }) .unwrap() .unwrap(); - let res: ValidatorResponse = from_binary(&raw).unwrap(); + let res: ValidatorResponse = from_json(raw).unwrap(); assert_eq!(res.validator, Some(val1)); // query 2 @@ -1928,7 +1985,7 @@ mod tests { .query(&StakingQuery::Validator { address: address2 }) .unwrap() .unwrap(); - let res: ValidatorResponse = from_binary(&raw).unwrap(); + let res: ValidatorResponse = from_json(raw).unwrap(); assert_eq!(res.validator, Some(val2)); // query non-existent @@ -1938,7 +1995,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: ValidatorResponse = from_binary(&raw).unwrap(); + let res: ValidatorResponse = from_json(raw).unwrap(); assert_eq!(res.validator, None); } @@ -1954,7 +2011,7 @@ mod tests { }) .unwrap() .unwrap(); - let dels: AllDelegationsResponse = from_binary(&raw).unwrap(); + let dels: AllDelegationsResponse = from_json(raw).unwrap(); dels.delegations } @@ -1972,7 +2029,7 @@ mod tests { }) .unwrap() .unwrap(); - let dels: DelegationResponse = from_binary(&raw).unwrap(); + let dels: DelegationResponse = from_json(raw).unwrap(); dels.delegation } @@ -2137,14 +2194,14 @@ mod tests { if *contract_addr == constract1 { #[derive(Deserialize)] struct MyMsg {} - let _msg: MyMsg = match from_binary(msg) { + let _msg: MyMsg = match from_json(msg) { Ok(msg) => msg, Err(err) => { return SystemResult::Ok(ContractResult::Err(err.to_string())) } }; let response: Response = Response::new().set_data(b"good"); - SystemResult::Ok(ContractResult::Ok(to_binary(&response).unwrap())) + SystemResult::Ok(ContractResult::Ok(to_json_binary(&response).unwrap())) } else { SystemResult::Err(SystemError::NoSuchContract { addr: contract_addr.clone(), @@ -2160,7 +2217,7 @@ mod tests { pinned: false, ibc_port: None, }; - SystemResult::Ok(ContractResult::Ok(to_binary(&response).unwrap())) + SystemResult::Ok(ContractResult::Ok(to_json_binary(&response).unwrap())) } else { SystemResult::Err(SystemError::NoSuchContract { addr: contract_addr.clone(), @@ -2180,7 +2237,7 @@ mod tests { ) .unwrap(), }; - SystemResult::Ok(ContractResult::Ok(to_binary(&response).unwrap())) + SystemResult::Ok(ContractResult::Ok(to_json_binary(&response).unwrap())) } else { SystemResult::Err(SystemError::NoSuchCode { code_id }) } @@ -2271,4 +2328,31 @@ mod tests { assert_eq!(digit_sum(&[255, 1]), 256); } + + #[test] + fn making_an_address_works() { + let mock_api = MockApi::default(); + + assert_eq!( + mock_api.addr_make("creator").to_string(), + "cosmwasm1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqs8s7vcp", + ); + + assert_eq!( + mock_api.addr_make("").to_string(), + "cosmwasm1uwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2sly4xh9", + ); + + let mock_api = MockApi::default().with_prefix("juno"); + assert_eq!( + mock_api.addr_make("creator").to_string(), + "juno1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqsksmtyp", + ); + } + + #[test] + #[should_panic(expected = "Generating address failed with reason: invalid length")] + fn making_an_address_with_empty_prefix_should_panic() { + MockApi::default().with_prefix("").addr_make("creator"); + } } diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs index 618c57f64..8286b714d 100644 --- a/packages/std/src/traits.rs +++ b/packages/std/src/traits.rs @@ -28,7 +28,7 @@ use crate::query::{ DistributionQuery, }; use crate::results::{ContractResult, Empty, SystemResult}; -use crate::serde::{from_binary, to_binary, to_vec}; +use crate::serde::{from_json, to_json_binary, to_json_vec}; use crate::ContractInfoResponse; #[cfg(feature = "cosmwasm_1_3")] use crate::{DenomMetadata, PageRequest}; @@ -244,7 +244,7 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { /// one level. Only use this if you don't need to check the SystemError /// eg. If you don't differentiate between contract missing and contract returned error pub fn query(&self, request: &QueryRequest) -> StdResult { - let raw = to_vec(request).map_err(|serialize_err| { + let raw = to_json_vec(request).map_err(|serialize_err| { StdError::generic_err(format!("Serializing QueryRequest: {serialize_err}")) })?; match self.raw_query(&raw) { @@ -254,7 +254,7 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err( format!("Querier contract error: {contract_err}"), )), - SystemResult::Ok(ContractResult::Ok(value)) => from_binary(&value), + SystemResult::Ok(ContractResult::Ok(value)) => from_json(value), } } @@ -380,7 +380,7 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { ) -> StdResult { let request = WasmQuery::Smart { contract_addr: contract_addr.into(), - msg: to_binary(msg)?, + msg: to_json_binary(msg)?, } .into(); self.query(&request) @@ -406,7 +406,7 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { .into(); // we cannot use query, as it will try to parse the binary data, when we just want to return it, // so a bit of code copy here... - let raw = to_vec(&request).map_err(|serialize_err| { + let raw = to_json_vec(&request).map_err(|serialize_err| { StdError::generic_err(format!("Serializing QueryRequest: {serialize_err}")) })?; match self.raw_query(&raw) { @@ -504,7 +504,7 @@ mod tests { use super::*; use crate::testing::MockQuerier; - use crate::{coins, from_slice, Uint128}; + use crate::{coins, from_json, Uint128}; // this is a simple demo helper to prove we can use it fn demo_helper(_querier: &dyn Querier) -> u64 { @@ -537,10 +537,10 @@ mod tests { }); let raw = wrapper - .raw_query(&to_vec(&query).unwrap()) + .raw_query(&to_json_vec(&query).unwrap()) .unwrap() .unwrap(); - let balance: BalanceResponse = from_slice(&raw).unwrap(); + let balance: BalanceResponse = from_json(raw).unwrap(); assert_eq!(balance.amount.amount, Uint128::new(5)); } @@ -583,7 +583,7 @@ mod tests { if q == &(WasmQuery::ContractInfo { contract_addr: ACCT.to_string(), }) { - SystemResult::Ok(ContractResult::Ok(to_binary(&mock_resp()).unwrap())) + SystemResult::Ok(ContractResult::Ok(to_json_binary(&mock_resp()).unwrap())) } else { SystemResult::Err(crate::SystemError::NoSuchContract { addr: ACCT.to_string(), @@ -614,7 +614,7 @@ mod tests { if q == &(WasmQuery::ContractInfo { contract_addr: ACCT.to_string(), }) { - SystemResult::Ok(ContractResult::Ok(to_binary(&mock_resp()).unwrap())) + SystemResult::Ok(ContractResult::Ok(to_json_binary(&mock_resp()).unwrap())) } else { SystemResult::Err(crate::SystemError::NoSuchContract { addr: ACCT.to_string(), diff --git a/packages/std/src/uuid.rs b/packages/std/src/uuid.rs index 41798b408..b718d7fc0 100644 --- a/packages/std/src/uuid.rs +++ b/packages/std/src/uuid.rs @@ -4,7 +4,7 @@ use std::ops::Deref; use std::str::FromStr; use uuid as raw_uuid; -use crate::{from_slice, to_vec}; +use crate::{from_json, to_json_vec}; use crate::{Api, Env, StdResult, Storage}; /// Uuid Provides a Uuid that can be used deterministically. @@ -41,13 +41,16 @@ const CONTRACT_UUID_SEQ_NUM_KEY: &[u8] = b"contract_uuid_seq_num"; pub fn new_uuid(env: &Env, storage: &mut dyn Storage, api: &dyn Api) -> StdResult { let raw_seq_num = storage.get(CONTRACT_UUID_SEQ_NUM_KEY); let seq_num: u16 = match raw_seq_num { - Some(data) => from_slice(&data).unwrap(), + Some(data) => from_json(data).unwrap(), None => 0, }; let next_seq_num: u16 = seq_num.wrapping_add(1); let uuid_name = format!("{} {} {}", env.contract.address, env.block.height, seq_num); - storage.set(CONTRACT_UUID_SEQ_NUM_KEY, &(to_vec(&next_seq_num).unwrap())); + storage.set( + CONTRACT_UUID_SEQ_NUM_KEY, + &(to_json_vec(&next_seq_num).unwrap()), + ); Uuid::new_v5( api, @@ -78,7 +81,7 @@ impl FromStr for Uuid { mod tests { use crate::testing::{mock_env, MockApi, MockStorage}; use crate::{new_uuid, Uuid}; - use crate::{to_vec, Addr, Storage}; + use crate::{to_json_vec, Addr, Storage}; use std::str::FromStr; use uuid as raw_uuid; @@ -126,7 +129,10 @@ mod tests { env.contract.address = Addr::unchecked("link1qyqszqgpqyqszqgpqyqszqgpqyqszqgp8apuk5"); env.block.height = u64::MAX; let stor: &mut dyn Storage = &mut storage; - stor.set(CONTRACT_UUID_SEQ_NUM_KEY, &(to_vec(&u16::MAX).unwrap())); + stor.set( + CONTRACT_UUID_SEQ_NUM_KEY, + &(to_json_vec(&u16::MAX).unwrap()), + ); let uuid = new_uuid(&env, &mut storage, &api); assert!(uuid.is_ok()); diff --git a/packages/storage/README.md b/packages/storage/README.md index 9309fa209..13320a543 100644 --- a/packages/storage/README.md +++ b/packages/storage/README.md @@ -1,3 +1,9 @@ +# WARNING: Unmaintained + +This crate is no longer maintained and should not be used anymore. For an +alternative, please check out +[cw-storage-plus](https://crates.io/crates/cw-storage-plus) + # cosmwasm-storage _Forked from [CosmWasm/cosmwasm-storage](https://github.com/CosmWasm/cosmwasm/tree/main/packages/storage)_ @@ -8,10 +14,14 @@ boilerplate. ## Contents -- [PrefixedStorage](#prefixed-storage) -- [TypedStorage](#typed-storage) -- [Bucket](#bucket) -- [Singleton](#singleton) +- [WARNING: Unmaintained](#warning-unmaintained) +- [cosmwasm-storage](#cosmwasm-storage) + - [Contents](#contents) + - [Prefixed Storage](#prefixed-storage) + - [Typed Storage](#typed-storage) + - [Bucket](#bucket) + - [Singleton](#singleton) + - [License](#license) ### Prefixed Storage diff --git a/packages/storage/src/bucket.rs b/packages/storage/src/bucket.rs index a870dfac4..81c7a65c3 100644 --- a/packages/storage/src/bucket.rs +++ b/packages/storage/src/bucket.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use cosmwasm_std::{ storage_keys::{to_length_prefixed, to_length_prefixed_nested}, - to_vec, StdError, StdResult, Storage, + to_json_vec, StdError, StdResult, Storage, }; #[cfg(feature = "iterator")] use cosmwasm_std::{Order, Record}; @@ -72,7 +72,7 @@ where /// save will serialize the model and store, returns an error on serialization issues pub fn save(&mut self, key: &[u8], data: &T) -> StdResult<()> { - set_with_prefix(self.storage, &self.prefix, key, &to_vec(data)?); + set_with_prefix(self.storage, &self.prefix, key, &to_json_vec(data)?); Ok(()) } @@ -399,7 +399,7 @@ mod tests { return Err(StdError::generic_err("Current age is negative").into()); } if data.age > 10 { - to_vec(&data)?; // Uses From to convert StdError to MyError + to_json_vec(&data)?; // Uses From to convert StdError to MyError } data.age += 1; Ok(data) diff --git a/packages/storage/src/singleton.rs b/packages/storage/src/singleton.rs index 3d5e5c956..41fbda7d6 100644 --- a/packages/storage/src/singleton.rs +++ b/packages/storage/src/singleton.rs @@ -1,7 +1,7 @@ use serde::{de::DeserializeOwned, ser::Serialize}; use std::marker::PhantomData; -use cosmwasm_std::{storage_keys::to_length_prefixed, to_vec, StdError, StdResult, Storage}; +use cosmwasm_std::{storage_keys::to_length_prefixed, to_json_vec, StdError, StdResult, Storage}; use crate::type_helpers::{may_deserialize, must_deserialize}; @@ -58,7 +58,7 @@ where /// save will serialize the model and store, returns an error on serialization issues pub fn save(&mut self, data: &T) -> StdResult<()> { - self.storage.set(&self.key, &to_vec(data)?); + self.storage.set(&self.key, &to_json_vec(data)?); Ok(()) } @@ -307,7 +307,7 @@ mod tests { return Err(StdError::generic_err("broken stuff").into()); // Uses Into to convert StdError to MyError } if c.max_tokens > 10 { - to_vec(&c)?; // Uses From to convert StdError to MyError + to_json_vec(&c)?; // Uses From to convert StdError to MyError } c.max_tokens += 20; Ok(c) diff --git a/packages/storage/src/type_helpers.rs b/packages/storage/src/type_helpers.rs index 233d2b146..98855de1b 100644 --- a/packages/storage/src/type_helpers.rs +++ b/packages/storage/src/type_helpers.rs @@ -36,7 +36,7 @@ pub(crate) fn deserialize_kv(kv: Record>) -> StdRes #[cfg(test)] mod tests { use super::*; - use cosmwasm_std::{to_vec, StdError}; + use cosmwasm_std::{to_json_vec, StdError}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -51,7 +51,7 @@ mod tests { name: "Maria".to_string(), age: 42, }; - let value = to_vec(&person).unwrap(); + let value = to_json_vec(&person).unwrap(); let may_parse: Option = may_deserialize(&Some(value)).unwrap(); assert_eq!(may_parse, Some(person)); @@ -69,7 +69,7 @@ mod tests { name: "Maria".to_string(), age: 42, }; - let value = to_vec(&person).unwrap(); + let value = to_json_vec(&person).unwrap(); let loaded = Some(value); let parsed: Person = must_deserialize(&loaded).unwrap(); diff --git a/packages/vm/Cargo.toml b/packages/vm/Cargo.toml index 200c109b6..1a251fb06 100644 --- a/packages/vm/Cargo.toml +++ b/packages/vm/Cargo.toml @@ -36,7 +36,7 @@ bench = false [dependencies] bytes = "1.4.0" # need a higher version than the one required by Wasmer for the Bytes -> Vec implementation -clru = "0.4.0" +clru = "0.6.1" crc32fast = "1.3.2" # Uses the path when built locally; uses the given version from crates.io when published cosmwasm-std = { path = "../std", version = "1.1.9+0.8.1", default-features = false } @@ -48,8 +48,8 @@ serde = { version = "1.0.103", default-features = false, features = ["derive", " serde_json = "1.0.40" sha2 = "0.10.3" thiserror = "1.0.26" -wasmer = { version = "=4.1.2", default-features = false, features = ["cranelift", "singlepass"] } -wasmer-middlewares = "=4.1.2" +wasmer = { version = "=4.2.2", default-features = false, features = ["cranelift", "singlepass"] } +wasmer-middlewares = "=4.2.2" # Dependencies that we do not use ourself. We add those entries # to bump the min version of them. diff --git a/packages/vm/benches/main.rs b/packages/vm/benches/main.rs index e0911e380..afefe5d3c 100644 --- a/packages/vm/benches/main.rs +++ b/packages/vm/benches/main.rs @@ -318,7 +318,6 @@ pub fn bench_instance_threads(c: &mut Criterion) { let checksum = random_checksum(); thread::spawn(move || { - let checksum = checksum; // Perform measurement internally let t = SystemTime::now(); black_box( diff --git a/packages/vm/examples/multi_threaded_cache.rs b/packages/vm/examples/multi_threaded_cache.rs index 7be56be40..fd8c145f2 100644 --- a/packages/vm/examples/multi_threaded_cache.rs +++ b/packages/vm/examples/multi_threaded_cache.rs @@ -51,7 +51,6 @@ pub fn main() { let cache = Arc::clone(&cache); threads.push(thread::spawn(move || { - let checksum = checksum; let mut instance = cache .get_instance(&checksum, mock_backend(&[]), DEFAULT_INSTANCE_OPTIONS) .unwrap(); diff --git a/packages/vm/src/cache.rs b/packages/vm/src/cache.rs index 28fa20d3b..071b77413 100644 --- a/packages/vm/src/cache.rs +++ b/packages/vm/src/cache.rs @@ -202,6 +202,7 @@ where pub fn save_wasm_unchecked(&self, wasm: &[u8]) -> VmResult { // We need a new engine for each Wasm -> module compilation due to the metering middleware. let compiling_engine = make_compiling_engine(None); + // This module cannot be executed directly as it was not created with the runtime engine let module = compile(&compiling_engine, wasm)?; let mut cache = self.inner.lock().unwrap(); @@ -291,11 +292,22 @@ where // Re-compile from original Wasm bytecode let wasm = self.load_wasm_with_path(&cache.wasm_path, checksum)?; cache.stats.misses = cache.stats.misses.saturating_add(1); - // Module will run with a different engine, so we can set memory limit to None - let engine = make_compiling_engine(None); - let module = compile(&engine, &wasm)?; - // Store into the fs cache too - let module_size = cache.fs_cache.store(checksum, &module)?; + { + // Module will run with a different engine, so we can set memory limit to None + let compiling_engine = make_compiling_engine(None); + // This module cannot be executed directly as it was not created with the runtime engine + let module = compile(&compiling_engine, &wasm)?; + cache.fs_cache.store(checksum, &module)?; + } + + // This time we'll hit the file-system cache. + let Some((module, module_size)) = cache.fs_cache.load(checksum, &cache.runtime_engine)? + else { + return Err(VmError::generic_err( + "Can't load module from file system cache after storing it to file system cache (pin)", + )); + }; + cache .pinned_memory_cache .store(checksum, module, module_size) @@ -377,11 +389,21 @@ where // stored the old module format. let wasm = self.load_wasm_with_path(&cache.wasm_path, checksum)?; cache.stats.misses = cache.stats.misses.saturating_add(1); - // Module will run with a different engine, so we can set memory limit to None - let engine = make_compiling_engine(None); - let module = compile(&engine, &wasm)?; - let module_size = cache.fs_cache.store(checksum, &module)?; + { + // Module will run with a different engine, so we can set memory limit to None + let compiling_engine = make_compiling_engine(None); + // This module cannot be executed directly as it was not created with the runtime engine + let module = compile(&compiling_engine, &wasm)?; + cache.fs_cache.store(checksum, &module)?; + } + // This time we'll hit the file-system cache. + let Some((module, module_size)) = cache.fs_cache.load(checksum, &cache.runtime_engine)? + else { + return Err(VmError::generic_err( + "Can't load module from file system cache after storing it to file system cache (get_module)", + )); + }; cache .memory_cache .store(checksum, module.clone(), module_size)?; @@ -532,6 +554,30 @@ mod tests { } } + /// Takes an instance and executes it + fn test_hackatom_instance_execution(instance: &mut Instance) + where + A: BackendApi + 'static, + S: Storage + 'static, + Q: Querier + 'static, + { + // instantiate + let info = mock_info("creator", &coins(1000, "earth")); + let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; + let response = call_instantiate::<_, _, _, Empty>(instance, &mock_env(), &info, msg) + .unwrap() + .unwrap(); + assert_eq!(response.messages.len(), 0); + + // execute + let info = mock_info("verifies", &coins(15, "earth")); + let msg = br#"{"release":{}}"#; + let response = call_execute::<_, _, _, Empty>(instance, &mock_env(), &info, msg) + .unwrap() + .unwrap(); + assert_eq!(response.messages.len(), 1); + } + #[test] fn new_base_dir_will_be_created() { let my_base_dir = TempDir::new() @@ -854,7 +900,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 1); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let res = @@ -873,7 +919,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 1); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let res = @@ -894,7 +940,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 2); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let res = @@ -919,7 +965,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 1); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let response = @@ -947,7 +993,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 1); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let response = @@ -977,7 +1023,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 2); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let response = @@ -996,6 +1042,27 @@ mod tests { } } + #[test] + fn call_execute_on_recompiled_contract() { + let options = make_testing_options(); + let cache = unsafe { Cache::new(options.clone()).unwrap() }; + let checksum = cache.save_wasm(CONTRACT).unwrap(); + + // Remove compiled module from disk + remove_dir_all(options.base_dir.join(CACHE_DIR).join(MODULES_DIR)).unwrap(); + + // Recompiles the Wasm (miss on all caches) + let backend = mock_backend(&[]); + let mut instance = cache + .get_instance(&checksum, backend, TESTING_OPTIONS) + .unwrap(); + assert_eq!(cache.stats().hits_pinned_memory_cache, 0); + assert_eq!(cache.stats().hits_memory_cache, 0); + assert_eq!(cache.stats().hits_fs_cache, 0); + assert_eq!(cache.stats().misses, 1); + test_hackatom_instance_execution(&mut instance); + } + #[test] fn use_multiple_cached_instances_of_same_contract() { let cache = unsafe { Cache::new(make_testing_options()).unwrap() }; @@ -1005,7 +1072,7 @@ mod tests { let backend1 = mock_backend(&[]); let backend2 = mock_backend(&[]); - // init instance 1 + // instantiate instance 1 let mut instance = cache .get_instance(&checksum, backend1, TESTING_OPTIONS) .unwrap(); @@ -1017,7 +1084,7 @@ mod tests { assert_eq!(msgs.len(), 0); let backend1 = instance.recycle().unwrap(); - // init instance 2 + // instantiate instance 2 let mut instance = cache .get_instance(&checksum, backend2, TESTING_OPTIONS) .unwrap(); @@ -1230,13 +1297,14 @@ mod tests { // check not pinned let backend = mock_backend(&[]); - let _instance = cache + let mut instance = cache .get_instance(&checksum, backend, TESTING_OPTIONS) .unwrap(); assert_eq!(cache.stats().hits_pinned_memory_cache, 0); assert_eq!(cache.stats().hits_memory_cache, 0); assert_eq!(cache.stats().hits_fs_cache, 1); assert_eq!(cache.stats().misses, 0); + test_hackatom_instance_execution(&mut instance); // first pin hits file system cache cache.pin(&checksum).unwrap(); @@ -1254,26 +1322,28 @@ mod tests { // check pinned let backend = mock_backend(&[]); - let _instance = cache + let mut instance = cache .get_instance(&checksum, backend, TESTING_OPTIONS) .unwrap(); assert_eq!(cache.stats().hits_pinned_memory_cache, 1); assert_eq!(cache.stats().hits_memory_cache, 0); assert_eq!(cache.stats().hits_fs_cache, 2); assert_eq!(cache.stats().misses, 0); + test_hackatom_instance_execution(&mut instance); // unpin cache.unpin(&checksum).unwrap(); // verify unpinned let backend = mock_backend(&[]); - let _instance = cache + let mut instance = cache .get_instance(&checksum, backend, TESTING_OPTIONS) .unwrap(); assert_eq!(cache.stats().hits_pinned_memory_cache, 1); assert_eq!(cache.stats().hits_memory_cache, 1); assert_eq!(cache.stats().hits_fs_cache, 2); assert_eq!(cache.stats().misses, 0); + test_hackatom_instance_execution(&mut instance); // unpin again has no effect cache.unpin(&checksum).unwrap(); @@ -1302,13 +1372,14 @@ mod tests { // After the compilation in pin, the module can be used from pinned memory cache let backend = mock_backend(&[]); - let _ = cache + let mut instance = cache .get_instance(&checksum, backend, TESTING_OPTIONS) .unwrap(); assert_eq!(cache.stats().hits_pinned_memory_cache, 1); assert_eq!(cache.stats().hits_memory_cache, 0); assert_eq!(cache.stats().hits_fs_cache, 0); assert_eq!(cache.stats().misses, 1); + test_hackatom_instance_execution(&mut instance); } #[test] diff --git a/packages/vm/src/calls.rs b/packages/vm/src/calls.rs index dc24a11a6..5828b0210 100644 --- a/packages/vm/src/calls.rs +++ b/packages/vm/src/calls.rs @@ -590,11 +590,15 @@ where #[cfg(test)] mod tests { use super::*; - use crate::testing::{mock_env, mock_info, mock_instance}; - use cosmwasm_std::{coins, Empty}; + use crate::testing::{ + mock_env, mock_info, mock_instance, mock_instance_with_options, MockInstanceOptions, + }; + use cosmwasm_std::{coins, from_json, to_json_string, Empty}; + use sha2::{Digest, Sha256}; static CONTRACT: &[u8] = include_bytes!("../testdata/hackatom.wasm"); static CYBERPUNK: &[u8] = include_bytes!("../testdata/cyberpunk.wasm"); + static FLOATY2: &[u8] = include_bytes!("../testdata/floaty_2.0.wasm"); #[test] fn call_instantiate_works() { @@ -734,6 +738,86 @@ mod tests { assert_eq!(query_response.as_slice(), b"{\"verifier\":\"verifies\"}"); } + #[test] + fn float_instrs_are_deterministic() { + #[derive(Debug, serde::Serialize, serde::Deserialize)] + #[serde(rename_all = "snake_case")] + pub enum Value { + U32(u32), + U64(u64), + F32(u32), + F64(u64), + } + + let mut instance = mock_instance_with_options( + FLOATY2, + MockInstanceOptions { + gas_limit: u64::MAX, + memory_limit: None, + ..Default::default() + }, + ); + + // init + let info = mock_info("creator", &[]); + call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{}"#) + .unwrap() + .unwrap(); + + // query instructions + let msg = br#"{"instructions":{}}"#; + let contract_result = call_query(&mut instance, &mock_env(), msg) + .unwrap() + .unwrap(); + let instructions: Vec = from_json(contract_result).unwrap(); + // little sanity check + assert_eq!(instructions.len(), 70); + + const RUNS_PER_INSTRUCTION: u64 = 150; + let mut hasher = Sha256::new(); + for instr in &instructions { + for seed in 0..RUNS_PER_INSTRUCTION { + // query some input values for the instruction + let args: Vec = from_json( + &call_query( + &mut instance, + &mock_env(), + format!( + r#"{{"random_args_for":{{ "instruction": "{instr}", "seed": {seed}}}}}"# + ) + .as_bytes(), + ) + .unwrap() + .unwrap(), + ) + .unwrap(); + + // build the run message + let args = to_json_string(&args).unwrap(); + let msg: String = format!( + r#"{{"run":{{ + "instruction": "{instr}", + "args": {args} + }}}}"# + ); + // run the instruction + // this might throw a runtime error (e.g. if the instruction traps) + let result = match call_query(&mut instance, &mock_env(), msg.as_bytes()) { + Ok(ContractResult::Ok(r)) => format!("{:?}", from_json::(&r).unwrap()), + Err(VmError::RuntimeErr { msg, .. }) => msg, + e => panic!("unexpected error: {e:?}"), + }; + // add the result to the hash + hasher.update(format!("{instr}{seed}{result}").as_bytes()); + } + } + let hash = Digest::finalize(hasher); + assert_eq!( + hex::encode(hash.as_slice()), + "95f70fa6451176ab04a9594417a047a1e4d8e2ff809609b8f81099496bee2393" + ); + } + #[cfg(feature = "stargate")] mod ibc { use super::*; diff --git a/packages/vm/src/environment.rs b/packages/vm/src/environment.rs index 121759ddf..f78dffbc0 100644 --- a/packages/vm/src/environment.rs +++ b/packages/vm/src/environment.rs @@ -372,7 +372,7 @@ impl Environment { /// Creates a MemoryView. /// This must be short living and not be used after the memory was grown. - pub fn memory<'a>(&self, store: &'a mut impl AsStoreMut) -> MemoryView<'a> { + pub fn memory<'a>(&self, store: &'a impl AsStoreMut) -> MemoryView<'a> { self.memory .as_ref() .expect("Memory is not set. This is a bug in the lifecycle.") @@ -458,7 +458,7 @@ mod tests { use crate::testing::{MockApi, MockQuerier, MockStorage}; use crate::wasm_backend::{compile, make_compiling_engine}; use cosmwasm_std::{ - coins, from_binary, to_vec, AllBalanceResponse, BankQuery, Empty, QueryRequest, + coins, from_json, to_json_vec, AllBalanceResponse, BankQuery, Empty, QueryRequest, }; use wasmer::{imports, Function, Instance as WasmerInstance, Store}; @@ -898,13 +898,13 @@ mod tests { address: INIT_ADDR.to_string(), }); let (result, _gas_info) = - querier.query_raw(&to_vec(&req).unwrap(), DEFAULT_QUERY_GAS_LIMIT); + querier.query_raw(&to_json_vec(&req).unwrap(), DEFAULT_QUERY_GAS_LIMIT); Ok(result.unwrap()) }) .unwrap() .unwrap() .unwrap(); - let balance: AllBalanceResponse = from_binary(&res).unwrap(); + let balance: AllBalanceResponse = from_json(res).unwrap(); assert_eq!(balance.amount, coins(INIT_AMOUNT, INIT_DENOM)); } diff --git a/packages/vm/src/imports.rs b/packages/vm/src/imports.rs index 87b722f71..5353192d8 100644 --- a/packages/vm/src/imports.rs +++ b/packages/vm/src/imports.rs @@ -46,11 +46,11 @@ const MAX_LENGTH_QUERY_CHAIN_REQUEST: usize = 64 * KI; /// Length of a serialized Ed25519 signature const MAX_LENGTH_ED25519_SIGNATURE: usize = 64; /// Max length of a Ed25519 message in bytes. -/// This is an arbitrary value, for performance / memory contraints. If you need to verify larger +/// This is an arbitrary value, for performance / memory constraints. If you need to verify larger /// messages, let us know. const MAX_LENGTH_ED25519_MESSAGE: usize = 128 * 1024; /// Max number of batch Ed25519 messages / signatures / public_keys. -/// This is an arbitrary value, for performance / memory contraints. If you need to batch-verify a +/// This is an arbitrary value, for performance / memory constraints. If you need to batch-verify a /// larger number of signatures, let us know. const MAX_COUNT_ED25519_BATCH: usize = 256; /// Max count of a inputs for sha1. @@ -83,7 +83,7 @@ pub fn do_db_read VmResult { let (data, mut store) = env.data_and_store_mut(); - let key = read_region(&data.memory(&mut store), key_ptr, MAX_LENGTH_DB_KEY)?; + let key = read_region(&data.memory(&store), key_ptr, MAX_LENGTH_DB_KEY)?; let (result, gas_info) = data.with_storage_from_context::<_, _>(|store| Ok(store.get(&key)))?; process_gas_info(data, &mut store, gas_info)?; @@ -108,8 +108,25 @@ pub fn do_db_write VmError { + if let VmError::CommunicationErr { + source: CommunicationError::RegionLengthTooBig { length, max_length }, + .. + } = e + { + VmError::generic_err(format!( + "{kind} too big. Tried to write {length} bytes to storage, limit is {max_length}." + )) + } else { + e + } + } + + let key = read_region(&data.memory(&store), key_ptr, MAX_LENGTH_DB_KEY) + .map_err(|e| convert_error(e, "Key"))?; + let value = read_region(&data.memory(&store), value_ptr, MAX_LENGTH_DB_VALUE) + .map_err(|e| convert_error(e, "Value"))?; let (result, gas_info) = data.with_storage_from_context::<_, _>(|store| Ok(store.set(&key, &value)))?; @@ -129,7 +146,7 @@ pub fn do_db_remove(|store| Ok(store.remove(&key)))?; @@ -145,11 +162,7 @@ pub fn do_addr_validate VmResult { let (data, mut store) = env.data_and_store_mut(); - let source_data = read_region( - &data.memory(&mut store), - source_ptr, - MAX_LENGTH_HUMAN_ADDRESS, - )?; + let source_data = read_region(&data.memory(&store), source_ptr, MAX_LENGTH_HUMAN_ADDRESS)?; if source_data.is_empty() { return write_to_contract(data, &mut store, b"Input is empty"); } @@ -193,11 +206,7 @@ pub fn do_addr_canonicalize VmResult { let (data, mut store) = env.data_and_store_mut(); - let source_data = read_region( - &data.memory(&mut store), - source_ptr, - MAX_LENGTH_HUMAN_ADDRESS, - )?; + let source_data = read_region(&data.memory(&store), source_ptr, MAX_LENGTH_HUMAN_ADDRESS)?; if source_data.is_empty() { return write_to_contract(data, &mut store, b"Input is empty"); } @@ -211,11 +220,7 @@ pub fn do_addr_canonicalize { - write_region( - &data.memory(&mut store), - destination_ptr, - canonical.as_slice(), - )?; + write_region(&data.memory(&store), destination_ptr, canonical.as_slice())?; Ok(0) } Err(BackendError::UserErr { msg, .. }) => { @@ -233,7 +238,7 @@ pub fn do_addr_humanize { - write_region(&data.memory(&mut store), destination_ptr, human.as_bytes())?; + write_region(&data.memory(&store), destination_ptr, human.as_bytes())?; Ok(0) } Err(BackendError::UserErr { msg, .. }) => { @@ -266,9 +271,9 @@ pub fn do_secp256k1_verify VmResult { let (data, mut store) = env.data_and_store_mut(); - let hash = read_region(&data.memory(&mut store), hash_ptr, MESSAGE_HASH_MAX_LEN)?; - let signature = read_region(&data.memory(&mut store), signature_ptr, ECDSA_SIGNATURE_LEN)?; - let pubkey = read_region(&data.memory(&mut store), pubkey_ptr, ECDSA_PUBKEY_MAX_LEN)?; + let hash = read_region(&data.memory(&store), hash_ptr, MESSAGE_HASH_MAX_LEN)?; + let signature = read_region(&data.memory(&store), signature_ptr, ECDSA_SIGNATURE_LEN)?; + let pubkey = read_region(&data.memory(&store), pubkey_ptr, ECDSA_PUBKEY_MAX_LEN)?; let gas_info = GasInfo::with_cost(data.gas_config.secp256k1_verify_cost); process_gas_info(data, &mut store, gas_info)?; @@ -307,8 +312,8 @@ pub fn do_secp256k1_recover_pubkey< ) -> VmResult { let (data, mut store) = env.data_and_store_mut(); - let hash = read_region(&data.memory(&mut store), hash_ptr, MESSAGE_HASH_MAX_LEN)?; - let signature = read_region(&data.memory(&mut store), signature_ptr, ECDSA_SIGNATURE_LEN)?; + let hash = read_region(&data.memory(&store), hash_ptr, MESSAGE_HASH_MAX_LEN)?; + let signature = read_region(&data.memory(&store), signature_ptr, ECDSA_SIGNATURE_LEN)?; let recover_param: u8 = match recover_param.try_into() { Ok(rp) => rp, Err(_) => return Ok((CryptoError::invalid_recovery_param().code() as u64) << 32), @@ -350,16 +355,16 @@ pub fn do_ed25519_verify>, message_ptr: u32, ) -> VmResult<()> { - let (data, mut store) = env.data_and_store_mut(); + let (data, store) = env.data_and_store_mut(); - let message_data = read_region(&data.memory(&mut store), message_ptr, MAX_LENGTH_ABORT)?; + let message_data = read_region(&data.memory(&store), message_ptr, MAX_LENGTH_ABORT)?; let msg = String::from_utf8_lossy(&message_data); Err(VmError::aborted(msg)) } @@ -532,7 +537,7 @@ pub fn do_query_chain VmResult { let (data, mut store) = env.data_and_store_mut(); - let start = maybe_read_region(&data.memory(&mut store), start_ptr, MAX_LENGTH_DB_KEY)?; - let end = maybe_read_region(&data.memory(&mut store), end_ptr, MAX_LENGTH_DB_KEY)?; + let start = maybe_read_region(&data.memory(&store), start_ptr, MAX_LENGTH_DB_KEY)?; + let end = maybe_read_region(&data.memory(&store), end_ptr, MAX_LENGTH_DB_KEY)?; let order: Order = order .try_into() .map_err(|_| CommunicationError::invalid_order(order))?; @@ -668,8 +673,8 @@ fn to_low_half(data: u32) -> u64 { mod tests { use super::*; use cosmwasm_std::{ - coins, from_binary, AllBalanceResponse, BankQuery, Binary, Empty, QueryRequest, - SystemError, SystemResult, WasmQuery, + coins, from_json, AllBalanceResponse, BankQuery, Binary, Empty, QueryRequest, SystemError, + SystemResult, WasmQuery, }; use hex_literal::hex; use std::ptr::NonNull; @@ -770,7 +775,7 @@ mod tests { fn leave_default_data( fe_mut: &mut FunctionEnvMut>, ) { - let (env, mut _store) = fe_mut.data_and_store_mut(); + let (env, _store) = fe_mut.data_and_store_mut(); // create some mock data let mut storage = MockStorage::new(); @@ -791,12 +796,12 @@ mod tests { .call_function1(&mut store, "allocate", &[(data.len() as u32).into()]) .unwrap(); let region_ptr = ref_to_u32(&result).unwrap(); - write_region(&env.memory(&mut store), region_ptr, data).expect("error writing"); + write_region(&env.memory(&store), region_ptr, data).expect("error writing"); region_ptr } fn create_empty( - wasmer_instance: &mut WasmerInstance, + wasmer_instance: &WasmerInstance, fe_mut: &mut FunctionEnvMut>, capacity: u32, ) -> u32 { @@ -816,9 +821,9 @@ mod tests { fe_mut: &mut FunctionEnvMut>, region_ptr: u32, ) -> Vec { - let (env, mut store) = fe_mut.data_and_store_mut(); + let (env, store) = fe_mut.data_and_store_mut(); - read_region(&env.memory(&mut store), region_ptr, 5000).unwrap() + read_region(&env.memory(&store), region_ptr, 5000).unwrap() } #[test] @@ -944,25 +949,14 @@ mod tests { let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); - let key_ptr = write_data(&mut fe_mut, &vec![4u8; 300 * 1024]); + const KEY_SIZE: usize = 300 * 1024; + let key_ptr = write_data(&mut fe_mut, &vec![4u8; KEY_SIZE]); let value_ptr = write_data(&mut fe_mut, b"new value"); leave_default_data(&mut fe_mut); let result = do_db_write(fe_mut, key_ptr, value_ptr); - match result.unwrap_err() { - VmError::CommunicationErr { - source: - CommunicationError::RegionLengthTooBig { - length, max_length, .. - }, - .. - } => { - assert_eq!(length, 300 * 1024); - assert_eq!(max_length, MAX_LENGTH_DB_KEY); - } - err => panic!("unexpected error: {err:?}"), - }; + assert_eq!(result.unwrap_err().to_string(), format!("Generic error: Key too big. Tried to write {KEY_SIZE} bytes to storage, limit is {MAX_LENGTH_DB_KEY}.")); } #[test] @@ -971,25 +965,14 @@ mod tests { let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); + const VAL_SIZE: usize = 300 * 1024; let key_ptr = write_data(&mut fe_mut, b"new storage key"); - let value_ptr = write_data(&mut fe_mut, &vec![5u8; 300 * 1024]); + let value_ptr = write_data(&mut fe_mut, &vec![5u8; VAL_SIZE]); leave_default_data(&mut fe_mut); let result = do_db_write(fe_mut, key_ptr, value_ptr); - match result.unwrap_err() { - VmError::CommunicationErr { - source: - CommunicationError::RegionLengthTooBig { - length, max_length, .. - }, - .. - } => { - assert_eq!(length, 300 * 1024); - assert_eq!(max_length, MAX_LENGTH_DB_VALUE); - } - err => panic!("unexpected error: {err:?}"), - }; + assert_eq!(result.unwrap_err().to_string(), format!("Generic error: Value too big. Tried to write {VAL_SIZE} bytes to storage, limit is {MAX_LENGTH_DB_VALUE}.")); } #[test] @@ -1060,7 +1043,7 @@ mod tests { leave_default_data(&mut fe_mut); - // Note: right now we cannot differnetiate between an existent and a non-existent key + // Note: right now we cannot differentiate between an existent and a non-existent key do_db_remove(fe_mut.as_mut(), key_ptr).unwrap(); let value = fe_mut @@ -1215,12 +1198,12 @@ mod tests { #[test] fn do_addr_canonicalize_works() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let api = MockApi::default(); let source_ptr = write_data(&mut fe_mut, b"foo"); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, api.canonical_length() as u32); + let dest_ptr = create_empty(&instance, &mut fe_mut, api.canonical_length() as u32); leave_default_data(&mut fe_mut); @@ -1234,13 +1217,13 @@ mod tests { #[test] fn do_addr_canonicalize_reports_invalid_input_back_to_contract() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr1 = write_data(&mut fe_mut, b"fo\x80o"); // invalid UTF-8 (fo�o) let source_ptr2 = write_data(&mut fe_mut, b""); // empty let source_ptr3 = write_data(&mut fe_mut, b"addressexceedingaddressspacesuperlongreallylongiamensuringthatitislongerthaneverything"); // too long - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 70); + let dest_ptr = create_empty(&instance, &mut fe_mut, 70); leave_default_data(&mut fe_mut); @@ -1266,11 +1249,11 @@ mod tests { #[test] fn do_addr_canonicalize_fails_for_broken_backend() { let api = MockApi::new_failing("Temporarily unavailable"); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, b"foo"); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 7); + let dest_ptr = create_empty(&instance, &mut fe_mut, 7); leave_default_data(&mut fe_mut); @@ -1287,11 +1270,11 @@ mod tests { #[test] fn do_addr_canonicalize_fails_for_large_inputs() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, &[61; 333]); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 8); + let dest_ptr = create_empty(&instance, &mut fe_mut, 8); leave_default_data(&mut fe_mut); @@ -1314,11 +1297,11 @@ mod tests { #[test] fn do_addr_canonicalize_fails_for_small_destination_region() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, b"foo"); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 7); + let dest_ptr = create_empty(&instance, &mut fe_mut, 7); leave_default_data(&mut fe_mut); @@ -1338,13 +1321,13 @@ mod tests { #[test] fn do_addr_humanize_works() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let api = MockApi::default(); let source_data = vec![0x22; api.canonical_length()]; let source_ptr = write_data(&mut fe_mut, &source_data); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 70); + let dest_ptr = create_empty(&instance, &mut fe_mut, 70); leave_default_data(&mut fe_mut); @@ -1356,11 +1339,11 @@ mod tests { #[test] fn do_addr_humanize_reports_invalid_input_back_to_contract() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, b"foo"); // too short - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 70); + let dest_ptr = create_empty(&instance, &mut fe_mut, 70); leave_default_data(&mut fe_mut); @@ -1373,11 +1356,11 @@ mod tests { #[test] fn do_addr_humanize_fails_for_broken_backend() { let api = MockApi::new_failing("Temporarily unavailable"); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, b"foo\0\0\0\0\0"); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 70); + let dest_ptr = create_empty(&instance, &mut fe_mut, 70); leave_default_data(&mut fe_mut); @@ -1394,11 +1377,11 @@ mod tests { #[test] fn do_addr_humanize_fails_for_input_too_long() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, &[61; 65]); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 70); + let dest_ptr = create_empty(&instance, &mut fe_mut, 70); leave_default_data(&mut fe_mut); @@ -1421,13 +1404,13 @@ mod tests { #[test] fn do_addr_humanize_fails_for_destination_region_too_small() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let api = MockApi::default(); let source_data = vec![0x22; api.canonical_length()]; let source_ptr = write_data(&mut fe_mut, &source_data); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 2); + let dest_ptr = create_empty(&instance, &mut fe_mut, 2); leave_default_data(&mut fe_mut); @@ -1447,7 +1430,7 @@ mod tests { #[test] fn do_secp256k1_verify_works() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1466,7 +1449,7 @@ mod tests { #[test] fn do_secp256k1_verify_wrong_hash_verify_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let mut hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1487,7 +1470,7 @@ mod tests { #[test] fn do_secp256k1_verify_larger_hash_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let mut hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1512,7 +1495,7 @@ mod tests { #[test] fn do_secp256k1_verify_shorter_hash_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let mut hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1533,7 +1516,7 @@ mod tests { #[test] fn do_secp256k1_verify_wrong_sig_verify_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1554,7 +1537,7 @@ mod tests { #[test] fn do_secp256k1_verify_larger_sig_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1579,7 +1562,7 @@ mod tests { #[test] fn do_secp256k1_verify_shorter_sig_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1600,7 +1583,7 @@ mod tests { #[test] fn do_secp256k1_verify_wrong_pubkey_format_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1621,7 +1604,7 @@ mod tests { #[test] fn do_secp256k1_verify_wrong_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1642,7 +1625,7 @@ mod tests { #[test] fn do_secp256k1_verify_larger_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1667,7 +1650,7 @@ mod tests { #[test] fn do_secp256k1_verify_shorter_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1688,7 +1671,7 @@ mod tests { #[test] fn do_secp256k1_verify_empty_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1707,7 +1690,7 @@ mod tests { #[test] fn do_secp256k1_verify_wrong_data_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = vec![0x22; MESSAGE_HASH_MAX_LEN]; @@ -1726,7 +1709,7 @@ mod tests { #[test] fn do_secp256k1_recover_pubkey_works() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); // https://gist.github.com/webmaster128/130b628d83621a33579751846699ed15 @@ -1749,7 +1732,7 @@ mod tests { #[test] fn do_ed25519_verify_works() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1768,7 +1751,7 @@ mod tests { #[test] fn do_ed25519_verify_wrong_msg_verify_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let mut msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1789,7 +1772,7 @@ mod tests { #[test] fn do_ed25519_verify_larger_msg_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let mut msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1814,7 +1797,7 @@ mod tests { #[test] fn do_ed25519_verify_wrong_sig_verify_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1835,7 +1818,7 @@ mod tests { #[test] fn do_ed25519_verify_larger_sig_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1860,7 +1843,7 @@ mod tests { #[test] fn do_ed25519_verify_shorter_sig_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1881,7 +1864,7 @@ mod tests { #[test] fn do_ed25519_verify_wrong_pubkey_verify_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1902,7 +1885,7 @@ mod tests { #[test] fn do_ed25519_verify_larger_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1927,7 +1910,7 @@ mod tests { #[test] fn do_ed25519_verify_shorter_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1948,7 +1931,7 @@ mod tests { #[test] fn do_ed25519_verify_empty_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1967,7 +1950,7 @@ mod tests { #[test] fn do_ed25519_verify_wrong_data_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = vec![0x22; MESSAGE_HASH_MAX_LEN]; @@ -1992,7 +1975,7 @@ mod tests { let request: QueryRequest = QueryRequest::Bank(BankQuery::AllBalances { address: INIT_ADDR.to_string(), }); - let request_data = cosmwasm_std::to_vec(&request).unwrap(); + let request_data = cosmwasm_std::to_json_vec(&request).unwrap(); let request_ptr = write_data(&mut fe_mut, &request_data); leave_default_data(&mut fe_mut); @@ -2000,11 +1983,10 @@ mod tests { let response_ptr = do_query_chain(fe_mut.as_mut(), request_ptr).unwrap(); let response = force_read(&mut fe_mut, response_ptr); - let query_result: cosmwasm_std::QuerierResult = - cosmwasm_std::from_slice(&response).unwrap(); + let query_result: cosmwasm_std::QuerierResult = cosmwasm_std::from_json(response).unwrap(); let query_result_inner = query_result.unwrap(); let query_result_inner_inner = query_result_inner.unwrap(); - let parsed_again: AllBalanceResponse = from_binary(&query_result_inner_inner).unwrap(); + let parsed_again: AllBalanceResponse = from_json(query_result_inner_inner).unwrap(); assert_eq!(parsed_again.amount, coins(INIT_AMOUNT, INIT_DENOM)); } @@ -2022,8 +2004,7 @@ mod tests { let response_ptr = do_query_chain(fe_mut.as_mut(), request_ptr).unwrap(); let response = force_read(&mut fe_mut, response_ptr); - let query_result: cosmwasm_std::QuerierResult = - cosmwasm_std::from_slice(&response).unwrap(); + let query_result: cosmwasm_std::QuerierResult = cosmwasm_std::from_json(response).unwrap(); match query_result { SystemResult::Ok(_) => panic!("This must not succeed"), SystemResult::Err(SystemError::InvalidRequest { request: err, .. }) => { @@ -2043,7 +2024,7 @@ mod tests { contract_addr: String::from("non-existent"), msg: Binary::from(b"{}" as &[u8]), }); - let request_data = cosmwasm_std::to_vec(&request).unwrap(); + let request_data = cosmwasm_std::to_json_vec(&request).unwrap(); let request_ptr = write_data(&mut fe_mut, &request_data); leave_default_data(&mut fe_mut); @@ -2051,8 +2032,7 @@ mod tests { let response_ptr = do_query_chain(fe_mut.as_mut(), request_ptr).unwrap(); let response = force_read(&mut fe_mut, response_ptr); - let query_result: cosmwasm_std::QuerierResult = - cosmwasm_std::from_slice(&response).unwrap(); + let query_result: cosmwasm_std::QuerierResult = cosmwasm_std::from_json(response).unwrap(); match query_result { SystemResult::Ok(_) => panic!("This must not succeed"), SystemResult::Err(SystemError::NoSuchContract { addr }) => { diff --git a/packages/vm/src/instance.rs b/packages/vm/src/instance.rs index a616ee63a..0da0ef950 100644 --- a/packages/vm/src/instance.rs +++ b/packages/vm/src/instance.rs @@ -350,7 +350,7 @@ where /// Returns the features required by this contract. /// /// This is not needed for production because we can do static analysis - /// on the Wasm file before instatiation to obtain this information. It's + /// on the Wasm file before instantiation to obtain this information. It's /// only kept because it can be handy for integration testing. pub fn required_capabilities(&self) -> HashSet { required_capabilities_from_module(self._inner.module()) @@ -362,9 +362,9 @@ where /// (https://github.com/WebAssembly/design/issues/1300#issuecomment-573867836). pub fn memory_pages(&mut self) -> usize { let mut fe_mut = self.fe.clone().into_mut(&mut self.store); - let (env, mut store) = fe_mut.data_and_store_mut(); + let (env, store) = fe_mut.data_and_store_mut(); - env.memory(&mut store).size().0 as _ + env.memory(&store).size().0 as _ } /// Returns the currently remaining gas. @@ -449,17 +449,17 @@ where /// Copies all data described by the Region at the given pointer from Wasm to the caller. pub(crate) fn read_memory(&mut self, region_ptr: u32, max_length: usize) -> VmResult> { let mut fe_mut = self.fe.clone().into_mut(&mut self.store); - let (env, mut store) = fe_mut.data_and_store_mut(); + let (env, store) = fe_mut.data_and_store_mut(); - read_region(&env.memory(&mut store), region_ptr, max_length) + read_region(&env.memory(&store), region_ptr, max_length) } /// Copies data to the memory region that was created before using allocate. pub(crate) fn write_memory(&mut self, region_ptr: u32, data: &[u8]) -> VmResult<()> { let mut fe_mut = self.fe.clone().into_mut(&mut self.store); - let (env, mut store) = fe_mut.data_and_store_mut(); + let (env, store) = fe_mut.data_and_store_mut(); - write_region(&env.memory(&mut store), region_ptr, data)?; + write_region(&env.memory(&store), region_ptr, data)?; Ok(()) } @@ -524,8 +524,7 @@ mod tests { mock_instance_with_options, MockInstanceOptions, }; use cosmwasm_std::{ - coin, coins, from_binary, AllBalanceResponse, BalanceResponse, BankQuery, Empty, - QueryRequest, + coin, coins, from_json, AllBalanceResponse, BalanceResponse, BankQuery, Empty, QueryRequest, }; use wasmer::{FunctionEnv, FunctionEnvMut}; @@ -980,7 +979,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let BalanceResponse { amount } = from_binary(&response).unwrap(); + let BalanceResponse { amount } = from_json(response).unwrap(); assert_eq!(amount.amount.u128(), 8000); assert_eq!(amount.denom, "silver"); Ok(()) @@ -1001,7 +1000,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let AllBalanceResponse { amount } = from_binary(&response).unwrap(); + let AllBalanceResponse { amount } = from_json(response).unwrap(); assert_eq!(amount.len(), 2); assert_eq!(amount[0].amount.u128(), 10000); assert_eq!(amount[0].denom, "gold"); @@ -1013,7 +1012,7 @@ mod tests { .unwrap(); } - /// This is needed for writing intagration tests in which the balance of a contract changes over time + /// This is needed for writing integration tests in which the balance of a contract changes over time. #[test] fn with_querier_allows_updating_balances() { let rich_addr = String::from("foobar"); @@ -1036,7 +1035,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let BalanceResponse { amount } = from_binary(&response).unwrap(); + let BalanceResponse { amount } = from_json(response).unwrap(); assert_eq!(amount.amount.u128(), 500); Ok(()) }) @@ -1065,7 +1064,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let BalanceResponse { amount } = from_binary(&response).unwrap(); + let BalanceResponse { amount } = from_json(response).unwrap(); assert_eq!(amount.amount.u128(), 8000); Ok(()) }) diff --git a/packages/vm/src/modules/file_system_cache.rs b/packages/vm/src/modules/file_system_cache.rs index 12c84a0ae..9008c038b 100644 --- a/packages/vm/src/modules/file_system_cache.rs +++ b/packages/vm/src/modules/file_system_cache.rs @@ -49,7 +49,10 @@ use crate::modules::current_wasmer_module_version; /// - **v7**:
/// New version because of Wasmer 2.3.0 -> 4 upgrade. /// This internally changes how rkyv is used for module serialization, making compatibility unlikely. -const MODULE_SERIALIZATION_VERSION: &str = "v7"; +/// - **v8**:
+/// New version because of Wasmer 4.1.2 -> 4.2.2 upgrade. +/// Module compatibility between Wasmer versions is not guaranteed. +const MODULE_SERIALIZATION_VERSION: &str = "v8"; /// Representation of a directory that contains compiled Wasm artifacts. pub struct FileSystemCache { @@ -118,6 +121,14 @@ impl FileSystemCache { self.unchecked_modules = unchecked; } + /// Returns the path to the serialized module with the given checksum. + fn module_file(&self, checksum: &Checksum) -> PathBuf { + let mut path = self.modules_path.clone(); + path.push(checksum.to_hex()); + path.set_extension("module"); + path + } + /// Loads a serialized module from the file system and returns a module (i.e. artifact + store), /// along with the size of the serialized module. pub fn load( @@ -125,8 +136,7 @@ impl FileSystemCache { checksum: &Checksum, engine: &impl AsEngineRef, ) -> VmResult> { - let filename = checksum.to_hex(); - let file_path = self.modules_path.join(filename); + let file_path = self.module_file(checksum); let result = if self.unchecked_modules { unsafe { Module::deserialize_from_file_unchecked(engine, &file_path) } @@ -155,8 +165,7 @@ impl FileSystemCache { mkdir_p(&self.modules_path) .map_err(|_e| VmError::cache_err("Error creating modules directory"))?; - let filename = checksum.to_hex(); - let path = self.modules_path.join(filename); + let path = self.module_file(checksum); module .serialize_to_file(&path) .map_err(|e| VmError::cache_err(format!("Error writing module to disk: {e}")))?; @@ -168,8 +177,7 @@ impl FileSystemCache { /// /// Returns true if the file existed and false if the file did not exist. pub fn remove(&mut self, checksum: &Checksum) -> VmResult { - let filename = checksum.to_hex(); - let file_path = self.modules_path.join(filename); + let file_path = self.module_file(checksum); if file_path.exists() { fs::remove_file(file_path) @@ -287,7 +295,7 @@ mod tests { cache.store(&checksum, &module).unwrap(); let mut globber = glob::glob(&format!( - "{}/v7-wasmer4/**/{}", + "{}/v8-wasmer5/**/{}.module", tmp_dir.path().to_string_lossy(), checksum )) @@ -365,9 +373,9 @@ mod tests { assert_eq!( p.as_os_str(), if cfg!(windows) { - "modules\\v7-wasmer17\\x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" + "modules\\v8-wasmer17\\x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" } else { - "modules/v7-wasmer17/x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" + "modules/v8-wasmer17/x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" } ); } diff --git a/packages/vm/src/modules/versioning.rs b/packages/vm/src/modules/versioning.rs index 1911852b3..f48d7198b 100644 --- a/packages/vm/src/modules/versioning.rs +++ b/packages/vm/src/modules/versioning.rs @@ -51,6 +51,6 @@ mod tests { #[test] fn current_wasmer_module_version_works() { let version = current_wasmer_module_version(); - assert_eq!(version, 4); + assert_eq!(version, 5); } } diff --git a/packages/vm/src/testing/querier.rs b/packages/vm/src/testing/querier.rs index e35217862..af43432a3 100644 --- a/packages/vm/src/testing/querier.rs +++ b/packages/vm/src/testing/querier.rs @@ -2,7 +2,7 @@ use serde::de::DeserializeOwned; use cosmwasm_std::testing::{MockQuerier as StdMockQuerier, MockQuerierCustomHandlerResult}; use cosmwasm_std::{ - to_binary, to_vec, Binary, Coin, ContractResult, CustomQuery, Empty, Querier as _, + to_json_binary, to_json_vec, Binary, Coin, ContractResult, CustomQuery, Empty, Querier as _, QueryRequest, SystemError, SystemResult, }; @@ -73,7 +73,7 @@ impl Querier for MockQuerier { GAS_COST_QUERY_FLAT + (GAS_COST_QUERY_REQUEST_MULTIPLIER * (bin_request.len() as u64)) + (GAS_COST_QUERY_RESPONSE_MULTIPLIER - * (to_binary(&response).unwrap().len() as u64)), + * (to_json_binary(&response).unwrap().len() as u64)), ); // In a production implementation, this should stop the query execution in the middle of the computation. @@ -94,7 +94,7 @@ impl MockQuerier { gas_limit: u64, ) -> BackendResult>> { // encode the request, then call raw_query - let request_binary = match to_vec(request) { + let request_binary = match to_json_vec(request) { Ok(raw) => raw, Err(err) => { let gas_info = GasInfo::with_externally_used(err.to_string().len() as u64); @@ -114,7 +114,7 @@ impl MockQuerier { #[cfg(test)] mod tests { use super::*; - use cosmwasm_std::{coin, from_binary, AllBalanceResponse, BalanceResponse, BankQuery, Empty}; + use cosmwasm_std::{coin, from_json, AllBalanceResponse, BalanceResponse, BankQuery, Empty}; const DEFAULT_QUERY_GAS_LIMIT: u64 = 300_000; @@ -148,7 +148,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let res: AllBalanceResponse = from_binary(&all).unwrap(); + let res: AllBalanceResponse = from_json(all).unwrap(); assert_eq!(&res.amount, &balance); } @@ -172,7 +172,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&fly).unwrap(); + let res: BalanceResponse = from_json(fly).unwrap(); assert_eq!(res.amount, coin(777, "FLY")); // missing denom @@ -189,7 +189,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&miss).unwrap(); + let res: BalanceResponse = from_json(miss).unwrap(); assert_eq!(res.amount, coin(0, "MISS")); } @@ -212,7 +212,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let res: AllBalanceResponse = from_binary(&all).unwrap(); + let res: AllBalanceResponse = from_json(all).unwrap(); assert_eq!(res.amount, vec![]); // any denom on balances on empty account is empty coin @@ -229,7 +229,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&miss).unwrap(); + let res: BalanceResponse = from_json(miss).unwrap(); assert_eq!(res.amount, coin(0, "ELF")); } } diff --git a/packages/vm/src/wasm_backend/compile.rs b/packages/vm/src/wasm_backend/compile.rs index 38fd4966d..d06a1ae05 100644 --- a/packages/vm/src/wasm_backend/compile.rs +++ b/packages/vm/src/wasm_backend/compile.rs @@ -16,9 +16,8 @@ mod tests { static CONTRACT: &[u8] = include_bytes!("../../testdata/floaty.wasm"); #[test] - fn contract_with_floats_fails_check() { + fn contract_with_floats_passes_check() { let engine = make_compiling_engine(None); - let err = compile(&engine, CONTRACT).unwrap_err(); - assert!(err.to_string().contains("Float operator detected:")); + assert!(compile(&engine, CONTRACT).is_ok()); } } diff --git a/packages/vm/src/wasm_backend/engine.rs b/packages/vm/src/wasm_backend/engine.rs index 0761e5968..25eceee93 100644 --- a/packages/vm/src/wasm_backend/engine.rs +++ b/packages/vm/src/wasm_backend/engine.rs @@ -54,6 +54,7 @@ pub fn make_compiling_engine(memory_limit: Option) -> Engine { #[cfg(not(feature = "cranelift"))] let mut compiler = Singlepass::default(); + compiler.canonicalize_nans(true); compiler.push_middleware(deterministic); compiler.push_middleware(metering); let mut engine = Engine::from(compiler); diff --git a/packages/vm/src/wasm_backend/gatekeeper.rs b/packages/vm/src/wasm_backend/gatekeeper.rs index 42cbc4b3f..f3646326a 100644 --- a/packages/vm/src/wasm_backend/gatekeeper.rs +++ b/packages/vm/src/wasm_backend/gatekeeper.rs @@ -57,7 +57,7 @@ impl Gatekeeper { impl Default for Gatekeeper { fn default() -> Self { Self::new(GatekeeperConfig { - allow_floats: false, + allow_floats: true, allow_feature_bulk_memory_operations: false, allow_feature_reference_types: false, allow_feature_simd: false, @@ -727,7 +727,7 @@ mod tests { } #[test] - fn parser_floats_are_not_supported() { + fn parser_floats_are_supported() { let wasm = wat::parse_str( r#" (module @@ -744,10 +744,7 @@ mod tests { compiler.push_middleware(deterministic); let store = Store::new(compiler); let result = Module::new(&store, wasm); - assert!(result - .unwrap_err() - .to_string() - .contains("Float operator detected:")); + assert!(result.is_ok()); } #[test] diff --git a/packages/vm/testdata/floaty_2.0.wasm b/packages/vm/testdata/floaty_2.0.wasm new file mode 100755 index 000000000..3dd07a913 Binary files /dev/null and b/packages/vm/testdata/floaty_2.0.wasm differ