diff --git a/.circleci/config.yml b/.circleci/config.yml index e50be631dd..d0ee3dcb84 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -86,7 +86,7 @@ workflows: # Add your branch here if benchmarking matters to your work - fix-benchmarking - w3 - - coverage + # - coverage # disabled temporarily because Rust version is too low deploy: jobs: - build_and_upload_devcontracts: diff --git a/CHANGELOG.md b/CHANGELOG.md index 3636d5af41..1f818a6feb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,60 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [1.3.0-rc.0] - 2023-07-03 + +### Fixed + +- cosmwasm-vm: Add missing cache stats increment when calling `pin`. + +### Added + +- cosmwasm-std: Implement `BankQuery::AllDenomMetadata` to allow querying all + the denom metadata and `BankQuery::DenomMetadata` to query a specific one. In + order to use this query in a contract, the `cosmwasm_1_3` feature needs to be + enabled for the `cosmwasm_std` dependency. This makes the contract + incompatible with chains running anything lower than CosmWasm `1.3.0`. + ([#1647]) +- cosmwasm-std: Add `DistributionQuery::DelegatorWithdrawAddress`. Also needs + the `cosmwasm_1_3` feature (see above). ([#1593]) +- cosmwasm-std: Add `DistributionMsg::FundCommunityPool`. Also needs the + `cosmwasm_1_3` feature (see above). ([#1747]) +- cosmwasm-std: Add `FromStr` impl for `Coin`. ([#1684]) +- cosmwasm-std: Add `Coins` helper to handle multiple coins. ([#1687]) +- cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have + been checked before. This is useful for state-sync where we know the Wasm code + was checked when it was first uploaded. ([#1635]) +- cosmwasm-vm: Allow sign extension Wasm opcodes in static validation. This + allows contracts to be compiled with Rust 1.70.0 and above. ([#1727]) +- cosmwasm-std: Add trait functions `Storage::range_keys` and + `Storage::range_values`. The default implementations just use + `Storage::range`. Later this can be implemented more efficiently. ([#1748]) +- cosmwasm-std: Add `Int64`, `Int128`, `Int256` and `Int512` signed integer + types. ([#1718]) + +[#1593]: https://github.com/CosmWasm/cosmwasm/pull/1593 +[#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635 +[#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647 +[#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 +[#1687]: https://github.com/CosmWasm/cosmwasm/pull/1687 +[#1718]: https://github.com/CosmWasm/cosmwasm/pull/1718 +[#1727]: https://github.com/CosmWasm/cosmwasm/issues/1727 +[#1747]: https://github.com/CosmWasm/cosmwasm/pull/1747 +[#1748]: https://github.com/CosmWasm/cosmwasm/pull/1748 + +### Changed + +- cosmwasm-vm: Add checks for table section of Wasm blob ([#1631]). +- cosmwasm-vm: Limit number of imports during static validation ([#1629]). +- cosmwasm-vm: Add target (triple + CPU features) into the module cache + directory to avoid using modules compiled for a different system. Bump + `MODULE_SERIALIZATION_VERSION` to "v6". ([#1664]) +- cosmwasm-vm: Add `.wasm` extension to stored wasm files ([#1686]). + +[#1629]: https://github.com/CosmWasm/cosmwasm/pull/1629 +[#1631]: https://github.com/CosmWasm/cosmwasm/pull/1631 +[#1664]: https://github.com/CosmWasm/cosmwasm/pull/1664 +[#1686]: https://github.com/CosmWasm/cosmwasm/pull/1686 ### Added @@ -1773,7 +1826,7 @@ Some main points: All future Changelog entries will reference this base -[unreleased]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.7...HEAD +[1.3.0-rc.0]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.7...v1.3.0-rc.0 [1.2.7]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.6...v1.2.7 [1.2.6]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.5...v1.2.6 [1.2.5]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.4...v1.2.5 diff --git a/Cargo.lock b/Cargo.lock index 81191e1fff..0071b3775a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,6 +211,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bstr" version = "1.5.0" @@ -411,7 +417,7 @@ dependencies = [ [[package]] name = "cosmwasm-check" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "anyhow", "assert_cmd", @@ -424,7 +430,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "criterion", @@ -443,7 +449,7 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "syn 1.0.109", @@ -451,7 +457,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "anyhow", "cosmwasm-schema-derive", @@ -466,7 +472,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -475,9 +481,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "chrono", "cosmwasm-crypto", "cosmwasm-derive", @@ -492,12 +499,11 @@ dependencies = [ "serde_json", "sha2 0.10.6", "thiserror", - "uint", ] [[package]] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "serde", @@ -505,7 +511,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -716,12 +722,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.9" @@ -2015,12 +2015,6 @@ 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" @@ -2175,18 +2169,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock index b4398eeab9..e7afa66cbb 100644 --- a/contracts/burner/Cargo.lock +++ b/contracts/burner/Cargo.lock @@ -97,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -187,7 +193,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -198,14 +204,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -216,7 +222,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -225,9 +231,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -238,12 +245,11 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -419,12 +425,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1410,12 +1410,6 @@ 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" @@ -1518,18 +1512,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock index 8830044d6c..bdf5047943 100644 --- a/contracts/crypto-verify/Cargo.lock +++ b/contracts/crypto-verify/Cargo.lock @@ -97,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -176,7 +182,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -187,14 +193,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -205,7 +211,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -214,9 +220,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -227,12 +234,11 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -408,12 +414,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1453,12 +1453,6 @@ 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" @@ -1561,18 +1555,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock index acf7eb5ad8..486f71732f 100644 --- a/contracts/cyberpunk/Cargo.lock +++ b/contracts/cyberpunk/Cargo.lock @@ -120,6 +120,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -205,7 +211,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -216,14 +222,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -234,7 +240,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -243,9 +249,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -256,12 +263,11 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -437,12 +443,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1537,12 +1537,6 @@ 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" @@ -1658,18 +1652,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock index 00d8db842e..7e88d1d538 100644 --- a/contracts/floaty/Cargo.lock +++ b/contracts/floaty/Cargo.lock @@ -97,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -176,7 +182,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -187,14 +193,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -205,7 +211,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -214,9 +220,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -227,12 +234,11 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -408,12 +414,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1411,12 +1411,6 @@ 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" @@ -1519,18 +1513,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock index 266b9315bd..2c8549bc2f 100644 --- a/contracts/hackatom/Cargo.lock +++ b/contracts/hackatom/Cargo.lock @@ -97,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -176,7 +182,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -187,14 +193,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -205,7 +211,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -214,9 +220,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -227,12 +234,11 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -408,12 +414,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1412,12 +1412,6 @@ 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" @@ -1520,18 +1514,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock index 4c187f7720..b8765ec7a5 100644 --- a/contracts/ibc-reflect-send/Cargo.lock +++ b/contracts/ibc-reflect-send/Cargo.lock @@ -97,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -176,7 +182,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -187,14 +193,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -205,7 +211,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -214,9 +220,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -227,12 +234,11 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -408,12 +414,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1410,12 +1410,6 @@ 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" @@ -1518,18 +1512,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock index 97100109ea..7c3927f3ba 100644 --- a/contracts/ibc-reflect/Cargo.lock +++ b/contracts/ibc-reflect/Cargo.lock @@ -97,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -176,7 +182,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -187,14 +193,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -205,7 +211,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -214,9 +220,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -227,12 +234,11 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -408,12 +414,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1410,12 +1410,6 @@ 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" @@ -1518,18 +1512,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock index 030268b6a2..2d39bd5af8 100644 --- a/contracts/queue/Cargo.lock +++ b/contracts/queue/Cargo.lock @@ -97,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -176,7 +182,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -187,14 +193,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -205,7 +211,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -214,9 +220,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -227,12 +234,11 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -408,12 +414,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1410,12 +1410,6 @@ 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" @@ -1518,18 +1512,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock index e5d5e5186e..742fb8f919 100644 --- a/contracts/reflect/Cargo.lock +++ b/contracts/reflect/Cargo.lock @@ -97,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -176,7 +182,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -187,14 +193,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -205,7 +211,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -214,9 +220,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -227,12 +234,11 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -408,12 +414,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1411,12 +1411,6 @@ 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" @@ -1519,18 +1513,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock index 9677d21968..eb507bdb22 100644 --- a/contracts/staking/Cargo.lock +++ b/contracts/staking/Cargo.lock @@ -97,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -176,7 +182,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -187,14 +193,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -205,7 +211,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -214,9 +220,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -227,12 +234,11 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -408,12 +414,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1438,12 +1438,6 @@ dependencies = [ "snafu", ] -[[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" @@ -1546,18 +1540,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock index cbf31b6a61..2bf601883e 100644 --- a/contracts/virus/Cargo.lock +++ b/contracts/virus/Cargo.lock @@ -97,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -176,7 +182,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -187,14 +193,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -205,7 +211,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -214,9 +220,10 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -227,12 +234,11 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", @@ -408,12 +414,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1399,12 +1399,6 @@ 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" @@ -1507,18 +1501,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/docs/CAPABILITIES-BUILT-IN.md b/docs/CAPABILITIES-BUILT-IN.md index d822971807..2f2268e4e7 100644 --- a/docs/CAPABILITIES-BUILT-IN.md +++ b/docs/CAPABILITIES-BUILT-IN.md @@ -15,6 +15,7 @@ might define others. CosmWasm `1.1.0` or higher support this. - `cosmwasm_1_2` enables the `GovMsg::VoteWeighted` and `WasmMsg::Instantiate2` messages. Only chains running CosmWasm `1.2.0` or higher support this. -- `cosmwasm_1_3` enables the `BankQuery::AllDenomMetadata` and - `BankQuery::DenomMetadata` queries. Only chains running CosmWasm `1.3.0` or - higher support this. +- `cosmwasm_1_3` enables the `BankQuery::AllDenomMetadata`, + `BankQuery::DenomMetadata` and `DistributionQuery::DelegatorWithdrawAddress` + queries, as well as `DistributionMsg::FundCommunityPool`. Only chains running + CosmWasm `1.3.0` or higher support this. diff --git a/packages/check/Cargo.toml b/packages/check/Cargo.toml index ed6c920cfc..88300df196 100644 --- a/packages/check/Cargo.toml +++ b/packages/check/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-check" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Mauro Lacy "] edition = "2021" description = "A CLI tool for verifying CosmWasm smart contracts" @@ -11,8 +11,8 @@ license = "Apache-2.0" anyhow = "1.0.57" clap = "4" colored = "2" -cosmwasm-vm = { path = "../vm", version = "1.2.7" } -cosmwasm-std = { path = "../std", version = "1.2.7" } +cosmwasm-vm = { path = "../vm", version = "1.3.0-rc.0" } +cosmwasm-std = { path = "../std", version = "1.3.0-rc.0" } [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 diff --git a/packages/crypto/Cargo.toml b/packages/crypto/Cargo.toml index e5e16090a3..c1a3d9b102 100644 --- a/packages/crypto/Cargo.toml +++ b/packages/crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Mauro Lacy "] edition = "2021" description = "Crypto bindings for cosmwasm contracts" diff --git a/packages/derive/Cargo.toml b/packages/derive/Cargo.toml index c6ab3d563e..68212e10a3 100644 --- a/packages/derive/Cargo.toml +++ b/packages/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Simon Warta "] edition = "2021" description = "A package for auto-generated code used for CosmWasm contract development. This is shipped as part of cosmwasm-std. Do not use directly." diff --git a/packages/schema-derive/Cargo.toml b/packages/schema-derive/Cargo.toml index 07b76b970d..ccf9e6ce09 100644 --- a/packages/schema-derive/Cargo.toml +++ b/packages/schema-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Tomasz Kurcz "] edition = "2021" description = "Derive macros for cosmwasm-schema" diff --git a/packages/schema/Cargo.toml b/packages/schema/Cargo.toml index 153beae691..7394ecb894 100644 --- a/packages/schema/Cargo.toml +++ b/packages/schema/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Simon Warta ", "Ethan Frey "] edition = "2021" description = "A dev-dependency for CosmWasm contracts to generate JSON Schema files." @@ -8,7 +8,7 @@ repository = "https://github.com/CosmWasm/cosmwasm/tree/main/packages/schema" license = "Apache-2.0" [dependencies] -cosmwasm-schema-derive = { version = "=1.2.7", path = "../schema-derive" } +cosmwasm-schema-derive = { version = "=1.3.0-rc.0", path = "../schema-derive" } schemars = "0.8.3" serde = "1.0" serde_json = "1.0.40" @@ -16,6 +16,6 @@ thiserror = "1.0.26" [dev-dependencies] anyhow = "1.0.57" -cosmwasm-std = { version = "1.2.7", path = "../std" } +cosmwasm-std = { version = "1.3.0-rc.0", path = "../std" } semver = "1" tempfile = "3" diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index af772d64e4..0c34b84a5c 100644 --- a/packages/std/Cargo.toml +++ b/packages/std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Ethan Frey "] edition = "2021" description = "Standard library for Wasm based smart contracts on Cosmos blockchains" @@ -45,7 +45,7 @@ cosmwasm_1_3 = ["cosmwasm_1_2"] [dependencies] base64 = "0.13.0" -cosmwasm-derive = { path = "../derive", version = "1.2.7" } +cosmwasm-derive = { path = "../derive", version = "1.3.0-rc.0" } derivative = "2" forward_ref = "1" hex = "0.4" @@ -54,10 +54,10 @@ sha2 = "0.10.3" serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } serde-json-wasm = { version = "0.5.0" } thiserror = "1.0.26" -uint = "0.9.3" +bnum = "0.7.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -cosmwasm-crypto = { path = "../crypto", version = "1.2.7" } +cosmwasm-crypto = { path = "../crypto", version = "1.3.0-rc.0" } [dev-dependencies] cosmwasm-schema = { path = "../schema" } diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs new file mode 100644 index 0000000000..1548cfffad --- /dev/null +++ b/packages/std/src/coins.rs @@ -0,0 +1,392 @@ +use std::collections::BTreeMap; +use std::fmt; +use std::str::FromStr; + +use crate::{errors::CoinsError, Coin, StdError, StdResult, Uint128}; +use crate::{OverflowError, OverflowOperation}; + +/// A collection of coins, similar to Cosmos SDK's `sdk.Coins` struct. +/// +/// Differently from `sdk.Coins`, which is a vector of `sdk.Coin`, here we +/// implement Coins as a BTreeMap that maps from coin denoms to amounts. +/// This has a number of advantages: +/// +/// - coins are naturally sorted alphabetically by denom +/// - duplicate denoms are automatically removed +/// - cheaper for searching/inserting/deleting: O(log(n)) compared to O(n) +#[derive(Clone, Default, Debug, PartialEq, Eq)] +pub struct Coins(BTreeMap); + +/// Casting a Vec to Coins. +/// The Vec can be out of order, but must not contain duplicate denoms. +/// If you want to sum up duplicates, create an empty instance using `Coins::default` and +/// use `Coins::add` to add your coins. +impl TryFrom> for Coins { + type Error = CoinsError; + + fn try_from(vec: Vec) -> Result { + let mut map = BTreeMap::new(); + for Coin { amount, denom } in vec { + if amount.is_zero() { + continue; + } + + // if the insertion returns a previous value, we have a duplicate denom + if map.insert(denom, amount).is_some() { + return Err(CoinsError::DuplicateDenom); + } + } + + Ok(Self(map)) + } +} + +impl TryFrom<&[Coin]> for Coins { + type Error = CoinsError; + + fn try_from(slice: &[Coin]) -> Result { + slice.to_vec().try_into() + } +} + +impl From for Coins { + fn from(value: Coin) -> Self { + let mut coins = Coins::default(); + // this can never overflow (because there are no coins in there yet), so we can unwrap + coins.add(value).unwrap(); + coins + } +} + +impl TryFrom<[Coin; N]> for Coins { + type Error = CoinsError; + + fn try_from(slice: [Coin; N]) -> Result { + slice.to_vec().try_into() + } +} + +impl From for Vec { + fn from(value: Coins) -> Self { + value.into_vec() + } +} + +impl FromStr for Coins { + type Err = StdError; + + fn from_str(s: &str) -> StdResult { + if s.is_empty() { + return Ok(Self::default()); + } + + Ok(s.split(',') + .map(Coin::from_str) + .collect::, _>>()? + .try_into()?) + } +} + +impl fmt::Display for Coins { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = self + .0 + .iter() + .map(|(denom, amount)| format!("{amount}{denom}")) + .collect::>() + .join(","); + write!(f, "{s}") + } +} + +impl Coins { + /// Conversion to Vec, while NOT consuming the original object + pub fn to_vec(&self) -> Vec { + self.0 + .iter() + .map(|(denom, amount)| Coin { + denom: denom.clone(), + amount: *amount, + }) + .collect() + } + + /// Conversion to Vec, consuming the original object + pub fn into_vec(self) -> Vec { + self.0 + .into_iter() + .map(|(denom, amount)| Coin { denom, amount }) + .collect() + } + + /// Returns the number of different denoms in this collection. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns `true` if this collection contains no coins. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Returns the denoms as a vector of strings. + /// The vector is guaranteed to not contain duplicates and sorted alphabetically. + pub fn denoms(&self) -> Vec { + self.0.keys().cloned().collect() + } + + /// Returns the amount of the given denom or zero if the denom is not present. + pub fn amount_of(&self, denom: &str) -> Uint128 { + self.0.get(denom).copied().unwrap_or_else(Uint128::zero) + } + + /// Returns the amount of the given denom if and only if this collection contains only + /// the given denom. Otherwise `None` is returned. + /// + /// # Examples + /// + /// ```rust + /// use cosmwasm_std::{Coin, Coins, coin}; + /// + /// let coins: Coins = [coin(100, "uatom")].try_into().unwrap(); + /// assert_eq!(coins.contains_only("uatom").unwrap().u128(), 100); + /// assert_eq!(coins.contains_only("uluna"), None); + /// ``` + /// + /// ```rust + /// use cosmwasm_std::{Coin, Coins, coin}; + /// + /// let coins: Coins = [coin(100, "uatom"), coin(200, "uusd")].try_into().unwrap(); + /// assert_eq!(coins.contains_only("uatom"), None); + /// ``` + pub fn contains_only(&self, denom: &str) -> Option { + if self.len() == 1 { + self.0.get(denom).copied() + } else { + None + } + } + + /// Adds the given coin to this `Coins` instance. + /// Errors in case of overflow. + pub fn add(&mut self, coin: Coin) -> StdResult<()> { + if coin.amount.is_zero() { + return Ok(()); + } + + let amount = self.0.entry(coin.denom).or_insert_with(Uint128::zero); + *amount = amount.checked_add(coin.amount)?; + Ok(()) + } + + /// Subtracts the given coin from this `Coins` instance. + /// Errors in case of overflow or if the denom is not present. + pub fn sub(&mut self, coin: Coin) -> StdResult<()> { + match self.0.get_mut(&coin.denom) { + Some(v) => { + *v = v.checked_sub(coin.amount)?; + // make sure to remove zero coin + if v.is_zero() { + self.0.remove(&coin.denom); + } + } + None => { + // ignore zero subtraction + if coin.amount.is_zero() { + return Ok(()); + } + return Err(OverflowError::new( + OverflowOperation::Sub, + Uint128::zero(), + coin.amount, + ) + .into()); + } + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::coin; + + /// Sort a Vec by denom alphabetically + fn sort_by_denom(vec: &mut [Coin]) { + vec.sort_by(|a, b| a.denom.cmp(&b.denom)); + } + + /// Returns a mockup Vec. In this example, the coins are not in order + fn mock_vec() -> Vec { + vec![ + coin(12345, "uatom"), + coin(69420, "ibc/1234ABCD"), + coin(88888, "factory/osmo1234abcd/subdenom"), + ] + } + + /// Return a mockup Coins that contains the same coins as in `mock_vec` + fn mock_coins() -> Coins { + let mut coins = Coins::default(); + for coin in mock_vec() { + coins.add(coin).unwrap(); + } + coins + } + + #[test] + fn converting_vec() { + let mut vec = mock_vec(); + let coins = mock_coins(); + + // &[Coin] --> Coins + assert_eq!(Coins::try_from(vec.as_slice()).unwrap(), coins); + // Vec --> Coins + assert_eq!(Coins::try_from(vec.clone()).unwrap(), coins); + + sort_by_denom(&mut vec); + + // &Coins --> Vec + // NOTE: the returned vec should be sorted + assert_eq!(coins.to_vec(), vec); + // Coins --> Vec + // NOTE: the returned vec should be sorted + assert_eq!(coins.into_vec(), vec); + } + + #[test] + fn converting_str() { + // not in order + let s1 = "88888factory/osmo1234abcd/subdenom,12345uatom,69420ibc/1234ABCD"; + // in order + let s2 = "88888factory/osmo1234abcd/subdenom,69420ibc/1234ABCD,12345uatom"; + + let invalid = "12345uatom,noamount"; + + let coins = mock_coins(); + + // &str --> Coins + // NOTE: should generate the same Coins, regardless of input order + assert_eq!(Coins::from_str(s1).unwrap(), coins); + assert_eq!(Coins::from_str(s2).unwrap(), coins); + assert_eq!(Coins::from_str("").unwrap(), Coins::default()); + + // Coins --> String + // NOTE: the generated string should be sorted + assert_eq!(coins.to_string(), s2); + assert_eq!(Coins::default().to_string(), ""); + assert_eq!( + Coins::from_str(invalid).unwrap_err().to_string(), + "Generic error: Parsing Coin: Missing amount or non-digit characters in amount" + ); + } + + #[test] + fn handling_duplicates() { + // create a Vec that contains duplicate denoms + let mut vec = mock_vec(); + vec.push(coin(67890, "uatom")); + + let err = Coins::try_from(vec).unwrap_err(); + assert_eq!(err, CoinsError::DuplicateDenom); + } + + #[test] + fn handling_zero_amount() { + // create a Vec that contains zero amounts + let mut vec = mock_vec(); + vec[0].amount = Uint128::zero(); + + let coins = Coins::try_from(vec).unwrap(); + assert_eq!(coins.len(), 2); + assert_ne!(coins.amount_of("ibc/1234ABCD"), Uint128::zero()); + assert_ne!( + coins.amount_of("factory/osmo1234abcd/subdenom"), + Uint128::zero() + ); + + // adding a coin with zero amount should not be added + let mut coins = Coins::default(); + coins.add(coin(0, "uusd")).unwrap(); + assert!(coins.is_empty()); + } + + #[test] + fn length() { + let coins = Coins::default(); + assert_eq!(coins.len(), 0); + assert!(coins.is_empty()); + + let coins = mock_coins(); + assert_eq!(coins.len(), 3); + assert!(!coins.is_empty()); + } + + #[test] + fn add_coin() { + let mut coins = mock_coins(); + + // existing denom + coins.add(coin(12345, "uatom")).unwrap(); + assert_eq!(coins.len(), 3); + assert_eq!(coins.amount_of("uatom").u128(), 24690); + + // new denom + coins.add(coin(123, "uusd")).unwrap(); + assert_eq!(coins.len(), 4); + + // zero amount + coins.add(coin(0, "uusd")).unwrap(); + assert_eq!(coins.amount_of("uusd").u128(), 123); + + // zero amount, new denom + coins.add(coin(0, "utest")).unwrap(); + assert_eq!(coins.len(), 4); + } + + #[test] + fn sub_coins() { + let mut coins: Coins = coin(12345, "uatom").into(); + + // sub more than available + let err = coins.sub(coin(12346, "uatom")).unwrap_err(); + assert!(matches!(err, StdError::Overflow { .. })); + + // sub non-existent denom + let err = coins.sub(coin(12345, "uusd")).unwrap_err(); + assert!(matches!(err, StdError::Overflow { .. })); + + // partial sub + coins.sub(coin(1, "uatom")).unwrap(); + assert_eq!(coins.len(), 1); + assert_eq!(coins.amount_of("uatom").u128(), 12344); + + // full sub + coins.sub(coin(12344, "uatom")).unwrap(); + assert!(coins.is_empty()); + + // sub zero, existing denom + coins.sub(coin(0, "uusd")).unwrap(); + assert!(coins.is_empty()); + let mut coins: Coins = coin(12345, "uatom").into(); + + // sub zero, non-existent denom + coins.sub(coin(0, "uatom")).unwrap(); + assert_eq!(coins.len(), 1); + assert_eq!(coins.amount_of("uatom").u128(), 12345); + } + + #[test] + fn coin_to_coins() { + // zero coin results in empty collection + let coins: Coins = coin(0, "uusd").into(); + assert!(coins.is_empty()); + + // happy path + let coins = Coins::from(coin(12345, "uatom")); + assert_eq!(coins.len(), 1); + assert_eq!(coins.amount_of("uatom").u128(), 12345); + } +} diff --git a/packages/std/src/errors/mod.rs b/packages/std/src/errors/mod.rs index 5535479bdf..54422007d3 100644 --- a/packages/std/src/errors/mod.rs +++ b/packages/std/src/errors/mod.rs @@ -6,8 +6,8 @@ mod verification_error; pub use recover_pubkey_error::RecoverPubkeyError; pub use std_error::{ CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError, - CoinFromStrError, ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, - RoundUpOverflowError, StdError, StdResult, + CoinFromStrError, CoinsError, ConversionOverflowError, DivideByZeroError, DivisionError, + OverflowError, OverflowOperation, 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 e77d0c0f1f..af18d282ea 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -556,6 +556,15 @@ impl DivideByZeroError { } } +#[derive(Error, Debug, PartialEq, Eq)] +pub enum DivisionError { + #[error("Divide by zero")] + DivideByZero, + + #[error("Overflow in division")] + Overflow, +} + #[derive(Error, Debug, PartialEq, Eq)] pub enum CheckedMultiplyFractionError { #[error("{0}")] @@ -590,6 +599,18 @@ pub enum CheckedFromRatioError { #[error("Round up operation failed because of overflow")] pub struct RoundUpOverflowError; +#[derive(Error, Debug, PartialEq, Eq)] +pub enum CoinsError { + #[error("Duplicate denom")] + DuplicateDenom, +} + +impl From for StdError { + fn from(value: CoinsError) -> Self { + Self::generic_err(format!("Creating Coins: {}", value)) + } +} + #[derive(Error, Debug, PartialEq, Eq)] pub enum CoinFromStrError { #[error("Missing denominator")] @@ -608,7 +629,7 @@ impl From for CoinFromStrError { impl From for StdError { fn from(value: CoinFromStrError) -> Self { - Self::generic_err(format!("Parsing Coin: {value}")) + Self::generic_err(format!("Parsing Coin: {}", value)) } } diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index d2e1568836..0f26389b38 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -7,6 +7,7 @@ mod addresses; mod assertions; mod binary; mod coin; +mod coins; mod conversion; mod deps; mod errors; @@ -38,6 +39,7 @@ pub mod storage_keys; pub use crate::addresses::{instantiate2_address, Addr, CanonicalAddr, Instantiate2AddressError}; pub use crate::binary::Binary; pub use crate::coin::{coin, coins, has_coins, Coin}; +pub use crate::coins::Coins; pub use crate::deps::{Deps, DepsMut, OwnedDeps}; pub use crate::errors::{ CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError, @@ -55,8 +57,8 @@ pub use crate::ibc::{ #[cfg(feature = "iterator")] pub use crate::iterator::{Order, Record}; pub use crate::math::{ - Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Isqrt, Uint128, - Uint256, Uint512, Uint64, + Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int128, Int256, + Int512, Int64, Isqrt, Uint128, Uint256, Uint512, Uint64, }; pub use crate::metadata::{DenomMetadata, DenomUnit}; pub use crate::never::Never; diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index b3df870ad7..2b98a82ce6 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -2070,7 +2070,7 @@ mod tests { } #[test] - #[should_panic(expected = "division by zero")] + #[should_panic(expected = "divisor of zero")] fn decimal256_rem_panics_for_zero() { let _ = Decimal256::percent(777) % Decimal256::zero(); } diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs new file mode 100644 index 0000000000..e30aea7914 --- /dev/null +++ b/packages/std/src/math/int128.rs @@ -0,0 +1,1104 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, Int64, Uint128, Uint64}; + +/// 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, +/// like JavaScript and jq. +/// +/// # Examples +/// +/// Use `from` to create instances of this and `i128` to get the value out: +/// +/// ``` +/// # use cosmwasm_std::Int128; +/// let a = Int128::from(258i128); +/// assert_eq!(a.i128(), 258); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int128(#[schemars(with = "String")] i128); + +forward_ref_partial_eq!(Int128, Int128); + +impl Int128 { + pub const MAX: Int128 = Int128(i128::MAX); + pub const MIN: Int128 = Int128(i128::MIN); + + /// Creates a Int128(value). + /// + /// This method is less flexible than `from` but can be called in a const context. + #[inline] + pub const fn new(value: i128) -> Self { + Self(value) + } + + /// Creates a Int128(0) + #[inline] + pub const fn zero() -> Self { + Int128(0) + } + + /// Creates a Int128(1) + #[inline] + pub const fn one() -> Self { + Self(1) + } + + /// Returns a copy of the internal data + pub const fn i128(&self) -> i128 { + self.0 + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 16]) -> Self { + Self(i128::from_be_bytes(data)) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 16]) -> Self { + Self(i128::from_le_bytes(data)) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 16] { + self.0.to_be_bytes() + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 16] { + self.0.to_le_bytes() + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0 == 0 + } + + #[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)) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 128 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 128 { + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + Self(self.0.wrapping_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + Self(self.0.wrapping_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + Self(self.0.wrapping_pow(other)) + } + + #[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 { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + Self(self.0.saturating_pow(exp)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Uint128 { + Uint128(self.0.abs_diff(other.0)) + } +} + +impl From for Int128 { + fn from(val: Uint64) -> Self { + val.u64().into() + } +} + +impl From for Int128 { + fn from(val: u64) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: u32) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: u16) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: u8) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: Int64) -> Self { + val.i64().into() + } +} + +impl From for Int128 { + fn from(val: i128) -> Self { + Int128(val) + } +} + +impl From for Int128 { + fn from(val: i64) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: i32) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: i16) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: i8) -> Self { + Int128(val.into()) + } +} + +impl TryFrom<&str> for Int128 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int128 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match s.parse::() { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing Int128: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int128) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int128 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Add for Int128 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int128(self.0.checked_add(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Add, add for Int128, Int128); + +impl Sub for Int128 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int128(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int128, Int128); + +impl SubAssign for Int128 { + fn sub_assign(&mut self, rhs: Int128) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int128, Int128); + +impl Div for Int128 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Div, div for Int128, Int128); + +impl Rem for Int128 { + 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 Int128, Int128); + +impl Not for Int128 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Neg for Int128 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl RemAssign for Int128 { + fn rem_assign(&mut self, rhs: Int128) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int128, Int128); + +impl Mul for Int128 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int128, Int128); + +impl MulAssign for Int128 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int128, Int128); + +impl Shr for Int128 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int128", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shr, shr for Int128, u32); + +impl Shl for Int128 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int128", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shl, shl for Int128, u32); + +impl AddAssign for Int128 { + fn add_assign(&mut self, rhs: Int128) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for Int128, Int128); + +impl DivAssign for Int128 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Int128, Int128); + +impl ShrAssign for Int128 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int128, u32); + +impl ShlAssign for Int128 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int128, u32); + +impl Serialize for Int128 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int128 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int128Visitor) + } +} + +struct Int128Visitor; + +impl<'de> de::Visitor<'de> for Int128Visitor { + type Value = Int128; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int128::try_from(v).map_err(|e| E::custom(format!("invalid Int128 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int128 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 16); + } + + #[test] + fn int128_from_be_bytes_works() { + let num = Int128::from_be_bytes([1; 16]); + let a: [u8; 16] = num.to_be_bytes(); + assert_eq!(a, [1; 16]); + + let be_bytes = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Int128::from_be_bytes(be_bytes); + let resulting_bytes: [u8; 16] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int128_new_works() { + let num = Int128::new(222); + assert_eq!(num.i128(), 222); + + let num = Int128::new(-222); + assert_eq!(num.i128(), -222); + + let num = Int128::new(i128::MAX); + assert_eq!(num.i128(), i128::MAX); + + let num = Int128::new(i128::MIN); + assert_eq!(num.i128(), i128::MIN); + } + + #[test] + fn int128_zero_works() { + let zero = Int128::zero(); + assert_eq!(zero.to_be_bytes(), [0; 16]); + } + + #[test] + fn uint128_one_works() { + let one = Int128::one(); + let mut one_be = [0; 16]; + one_be[15] = 1; + + assert_eq!(one.to_be_bytes(), one_be); + } + + #[test] + fn int128_endianness() { + let be_bytes = [ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let le_bytes = [ + 3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]; + + // These should all be the same. + let num1 = Int128::from_be_bytes(be_bytes); + let num2 = Int128::from_le_bytes(le_bytes); + assert_eq!(num1, Int128::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + } + + #[test] + fn int128_convert_from() { + let a = Int128::from(5i128); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u64); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u32); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u16); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u8); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(-5i128); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i64); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i32); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i16); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i8); + assert_eq!(a.0, i128::from(-5i32)); + + let result = Int128::try_from("34567"); + assert_eq!(result.unwrap().0, "34567".parse::().unwrap()); + + let result = Int128::try_from("1.23"); + assert!(result.is_err()); + } + + #[test] + fn int128_implements_display() { + let a = Int128::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int128::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + + let a = Int128::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int128_display_padding_works() { + let a = Int128::from(123u64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int128::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + } + + #[test] + fn int128_to_be_bytes_works() { + assert_eq!(Int128::zero().to_be_bytes(), [0; 16]); + + let mut max = [0xff; 16]; + max[0] = 0x7f; + assert_eq!(Int128::MAX.to_be_bytes(), max); + + let mut one = [0; 16]; + one[15] = 1; + assert_eq!(Int128::from(1i128).to_be_bytes(), one); + // Python: `[b for b in (70141183460469231731687303715884018880).to_bytes(16, "big")]` + assert_eq!( + Int128::from(70141183460469231731687303715884018880i128).to_be_bytes(), + [52, 196, 179, 87, 165, 121, 59, 133, 246, 117, 221, 191, 255, 254, 172, 192] + ); + assert_eq!( + Int128::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78]) + .to_be_bytes(), + [17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78] + ); + } + + #[test] + fn int128_to_le_bytes_works() { + assert_eq!(Int128::zero().to_le_bytes(), [0; 16]); + + let mut max = [0xff; 16]; + max[15] = 0x7f; + assert_eq!(Int128::MAX.to_le_bytes(), max); + + let mut one = [0; 16]; + one[0] = 1; + assert_eq!(Int128::from(1i128).to_le_bytes(), one); + // Python: `[b for b in (70141183460469231731687303715884018880).to_bytes(16, "little")]` + assert_eq!( + Int128::from(70141183460469231731687303715884018880i128).to_le_bytes(), + [192, 172, 254, 255, 191, 221, 117, 246, 133, 59, 121, 165, 87, 179, 196, 52] + ); + assert_eq!( + Int128::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78]) + .to_le_bytes(), + [78, 67, 21, 33, 38, 0, 91, 58, 200, 123, 67, 87, 32, 23, 4, 17] + ); + } + + #[test] + fn int128_is_zero_works() { + assert!(Int128::zero().is_zero()); + assert!(Int128(i128::from(0u32)).is_zero()); + + assert!(!Int128::from(1u32).is_zero()); + assert!(!Int128::from(123u32).is_zero()); + assert!(!Int128::from(-123i32).is_zero()); + } + + #[test] + fn int128_wrapping_methods() { + // wrapping_add + assert_eq!( + Int128::from(2u32).wrapping_add(Int128::from(2u32)), + Int128::from(4u32) + ); // non-wrapping + assert_eq!(Int128::MAX.wrapping_add(Int128::from(1u32)), Int128::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int128::from(7u32).wrapping_sub(Int128::from(5u32)), + Int128::from(2u32) + ); // non-wrapping + assert_eq!(Int128::MIN.wrapping_sub(Int128::from(1u32)), Int128::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int128::from(3u32).wrapping_mul(Int128::from(2u32)), + Int128::from(6u32) + ); // non-wrapping + assert_eq!( + Int128::MAX.wrapping_mul(Int128::from(2u32)), + Int128::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int128::from(2u32).wrapping_pow(3), Int128::from(8u32)); // non-wrapping + assert_eq!(Int128::MAX.wrapping_pow(2), Int128::from(1u32)); // wrapping + } + + #[test] + fn int128_json() { + let orig = Int128::from(1234567890987654321i128); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int128 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int128_compare() { + let a = Int128::from(12345u32); + let b = Int128::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int128::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_math() { + let a = Int128::from(-12345i32); + let b = Int128::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int128::from(11111u32)); + assert_eq!(a + &b, Int128::from(11111u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int128::from(35801u32)); + assert_eq!(b - &a, Int128::from(35801u32)); + + // test += with owned and reference right hand side + let mut c = Int128::from(300000u32); + c += b; + assert_eq!(c, Int128::from(323456u32)); + let mut d = Int128::from(300000u32); + d += &b; + assert_eq!(d, Int128::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int128::from(300000u32); + c -= b; + assert_eq!(c, Int128::from(276544u32)); + let mut d = Int128::from(300000u32); + d -= &b; + assert_eq!(d, Int128::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int128::from(-35801i32)); + } + + #[test] + #[should_panic] + fn int128_add_overflow_panics() { + let _ = Int128::MAX + Int128::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_sub_works() { + assert_eq!(Int128::from(2u32) - Int128::from(1u32), Int128::from(1u32)); + assert_eq!(Int128::from(2u32) - Int128::from(0u32), Int128::from(2u32)); + assert_eq!(Int128::from(2u32) - Int128::from(2u32), Int128::from(0u32)); + assert_eq!(Int128::from(2u32) - Int128::from(3u32), Int128::from(-1i32)); + + // works for refs + let a = Int128::from(10u32); + let b = Int128::from(3u32); + let expected = Int128::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int128_sub_overflow_panics() { + let _ = Int128::MIN + Int128::one() - Int128::from(2u32); + } + + #[test] + fn int128_sub_assign_works() { + let mut a = Int128::from(14u32); + a -= Int128::from(2u32); + assert_eq!(a, Int128::from(12u32)); + + // works for refs + let mut a = Int128::from(10u32); + let b = Int128::from(3u32); + let expected = Int128::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_mul_works() { + assert_eq!(Int128::from(2u32) * Int128::from(3u32), Int128::from(6u32)); + assert_eq!(Int128::from(2u32) * Int128::zero(), Int128::zero()); + + // works for refs + let a = Int128::from(11u32); + let b = Int128::from(3u32); + let expected = Int128::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int128_mul_assign_works() { + let mut a = Int128::from(14u32); + a *= Int128::from(2u32); + assert_eq!(a, Int128::from(28u32)); + + // works for refs + let mut a = Int128::from(10u32); + let b = Int128::from(3u32); + a *= &b; + assert_eq!(a, Int128::from(30u32)); + } + + #[test] + fn int128_pow_works() { + assert_eq!(Int128::from(2u32).pow(2), Int128::from(4u32)); + assert_eq!(Int128::from(2u32).pow(10), Int128::from(1024u32)); + } + + #[test] + #[should_panic] + fn int128_pow_overflow_panics() { + _ = Int128::MAX.pow(2u32); + } + + #[test] + fn int128_shr_works() { + let original = Int128::from_be_bytes([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, + ]); + + let shifted = Int128::from_be_bytes([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, + ]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int128_shr_overflow_panics() { + let _ = Int128::from(1u32) >> 128u32; + } + + #[test] + fn sum_works() { + let nums = vec![ + Int128::from(17u32), + Int128::from(123u32), + Int128::from(540u32), + Int128::from(82u32), + ]; + let expected = Int128::from(762u32); + + let sum_as_ref: Int128 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int128 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int128_methods() { + // checked_* + assert!(matches!( + Int128::MAX.checked_add(Int128::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int128::from(1u32).checked_add(Int128::from(1u32)), + Ok(Int128::from(2u32)), + ); + assert!(matches!( + Int128::MIN.checked_sub(Int128::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int128::from(2u32).checked_sub(Int128::from(1u32)), + Ok(Int128::from(1u32)), + ); + assert!(matches!( + Int128::MAX.checked_mul(Int128::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int128::from(2u32).checked_mul(Int128::from(2u32)), + Ok(Int128::from(4u32)), + ); + assert!(matches!( + Int128::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int128::from(2u32).checked_pow(3u32), Ok(Int128::from(8u32)),); + assert_eq!( + Int128::MAX.checked_div(Int128::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int128::from(6u32).checked_div(Int128::from(2u32)), + Ok(Int128::from(3u32)), + ); + assert_eq!( + Int128::MAX.checked_div_euclid(Int128::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int128::from(6u32).checked_div_euclid(Int128::from(2u32)), + Ok(Int128::from(3u32)), + ); + assert_eq!( + Int128::from(7u32).checked_div_euclid(Int128::from(2u32)), + Ok(Int128::from(3u32)), + ); + assert!(matches!( + Int128::MAX.checked_rem(Int128::from(0u32)), + Err(DivideByZeroError { .. }) + )); + // checked_* with negative numbers + assert_eq!( + Int128::from(-12i32).checked_div(Int128::from(10i32)), + Ok(Int128::from(-1i32)), + ); + assert_eq!( + Int128::from(-2i32).checked_pow(3u32), + Ok(Int128::from(-8i32)), + ); + assert_eq!( + Int128::from(-6i32).checked_mul(Int128::from(-7i32)), + Ok(Int128::from(42i32)), + ); + assert_eq!( + Int128::from(-2i32).checked_add(Int128::from(3i32)), + Ok(Int128::from(1i32)), + ); + assert_eq!( + Int128::from(-1i32).checked_div_euclid(Int128::from(-2i32)), + Ok(Int128::from(1u32)), + ); + + // saturating_* + assert_eq!(Int128::MAX.saturating_add(Int128::from(1u32)), Int128::MAX); + assert_eq!(Int128::MIN.saturating_sub(Int128::from(1u32)), Int128::MIN); + assert_eq!(Int128::MAX.saturating_mul(Int128::from(2u32)), Int128::MAX); + assert_eq!(Int128::from(4u32).saturating_pow(2u32), Int128::from(16u32)); + assert_eq!(Int128::MAX.saturating_pow(2u32), Int128::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_implements_rem() { + let a = Int128::from(10u32); + assert_eq!(a % Int128::from(10u32), Int128::zero()); + assert_eq!(a % Int128::from(2u32), Int128::zero()); + assert_eq!(a % Int128::from(1u32), Int128::zero()); + assert_eq!(a % Int128::from(3u32), Int128::from(1u32)); + assert_eq!(a % Int128::from(4u32), Int128::from(2u32)); + + assert_eq!( + Int128::from(-12i32) % Int128::from(10i32), + Int128::from(-2i32) + ); + assert_eq!( + Int128::from(12i32) % Int128::from(-10i32), + Int128::from(2i32) + ); + assert_eq!( + Int128::from(-12i32) % Int128::from(-10i32), + Int128::from(-2i32) + ); + + // works for refs + let a = Int128::from(10u32); + let b = Int128::from(3u32); + let expected = Int128::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int128_rem_panics_for_zero() { + let _ = Int128::from(10u32) % Int128::zero(); + } + + #[test] + fn int128_rem_assign_works() { + let mut a = Int128::from(30u32); + a %= Int128::from(4u32); + assert_eq!(a, Int128::from(2u32)); + + // works for refs + let mut a = Int128::from(25u32); + let b = Int128::from(6u32); + a %= &b; + assert_eq!(a, Int128::from(1u32)); + } + + #[test] + fn int128_shr() { + let x: Int128 = 0x4000_0000_0000_0000_0000_0000_0000_0000i128.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!( + x >> 1, + Int128::from(0x2000_0000_0000_0000_0000_0000_0000_0000i128) + ); + assert_eq!( + x >> 4, + Int128::from(0x0400_0000_0000_0000_0000_0000_0000_0000i128) + ); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int128::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int128::one() + ); + } + + #[test] + fn int128_shl() { + let x: Int128 = 0x0800_0000_0000_0000_0000_0000_0000_0000i128.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!( + x << 1, + Int128::from(0x1000_0000_0000_0000_0000_0000_0000_0000i128) + ); + assert_eq!( + x << 4, + Int128::from(0x0800_0000_0000_0000_0000_0000_0000_0000i128 << 4) + ); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int128::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int128::MIN + ); + } + + #[test] + fn int128_abs_diff_works() { + let a = Int128::from(42u32); + let b = Int128::from(5u32); + let expected = Uint128::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let c = Int128::from(-5i32); + assert_eq!(b.abs_diff(c), Uint128::from(10u32)); + assert_eq!(c.abs_diff(b), Uint128::from(10u32)); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int128_neg_min_panics() { + _ = -Int128::MIN; + } + + #[test] + fn int128_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (u64, u64, bool)| { + (Int128::from(lhs), Int128::from(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); + } + } +} diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs new file mode 100644 index 0000000000..937cf8df80 --- /dev/null +++ b/packages/std/src/math/int256.rs @@ -0,0 +1,1189 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, Int128, 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::{I256, U256}; + +/// 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. +/// +/// # Examples +/// +/// Use `from` to create instances out of primitive uint types or `new` to provide big +/// endian bytes: +/// +/// ``` +/// # use cosmwasm_std::Int256; +/// let a = Int256::from(258u128); +/// let b = Int256::new([ +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, +/// ]); +/// assert_eq!(a, b); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int256(#[schemars(with = "String")] I256); + +forward_ref_partial_eq!(Int256, Int256); + +impl Int256 { + pub const MAX: Int256 = Int256(I256::MAX); + pub const MIN: Int256 = Int256(I256::MIN); + + /// Creates a Int256(value) from a big endian representation. It's just an alias for + /// `from_be_bytes`. + #[inline] + pub const fn new(value: [u8; 32]) -> Self { + Self::from_be_bytes(value) + } + + /// Creates a Int256(0) + #[inline] + pub const fn zero() -> Self { + Int256(I256::ZERO) + } + + /// Creates a Int256(1) + #[inline] + pub const fn one() -> Self { + Self(I256::ONE) + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 32]) -> Self { + let words: [u64; 4] = [ + u64::from_le_bytes([ + data[31], data[30], data[29], data[28], data[27], data[26], data[25], data[24], + ]), + u64::from_le_bytes([ + data[23], data[22], data[21], data[20], data[19], data[18], data[17], data[16], + ]), + u64::from_le_bytes([ + data[15], data[14], data[13], data[12], data[11], data[10], data[9], data[8], + ]), + u64::from_le_bytes([ + data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], + ]), + ]; + Self(I256::from_bits(U256::from_digits(words))) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 32]) -> Self { + let words: [u64; 4] = [ + u64::from_le_bytes([ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]), + u64::from_le_bytes([ + data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], + ]), + u64::from_le_bytes([ + data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23], + ]), + u64::from_le_bytes([ + data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], + ]), + ]; + Self(I256::from_bits(U256::from_digits(words))) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 32] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 32] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[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)) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 256 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 256 { + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + Self(self.0.wrapping_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + Self(self.0.wrapping_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + Self(self.0.wrapping_pow(other)) + } + + #[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 { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + Self(self.0.saturating_pow(exp)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Uint256 { + Uint256(self.0.abs_diff(other.0)) + } +} + +impl From for Int256 { + fn from(val: Uint128) -> Self { + val.u128().into() + } +} + +impl From for Int256 { + fn from(val: Uint64) -> Self { + val.u64().into() + } +} + +impl From for Int256 { + fn from(val: u128) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u64) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u32) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u16) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u8) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: Int128) -> Self { + val.i128().into() + } +} + +impl From for Int256 { + fn from(val: Int64) -> Self { + val.i64().into() + } +} + +impl From for Int256 { + fn from(val: i128) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i64) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i32) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i16) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i8) -> Self { + Int256(val.into()) + } +} + +impl TryFrom<&str> for Int256 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int256 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match I256::from_str_radix(s, 10) { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing Int256: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int256) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int256 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // The inner type doesn't work as expected with padding, so we + // work around that. Remove this code when the upstream padding is fixed. + let unpadded = self.0.to_string(); + let numeric = unpadded.strip_prefix('-').unwrap_or(&unpadded); + + f.pad_integral(self >= &Self::zero(), "", numeric) + } +} + +impl Add for Int256 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int256(self.0.checked_add(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Add, add for Int256, Int256); + +impl Sub for Int256 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int256(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int256, Int256); + +impl SubAssign for Int256 { + fn sub_assign(&mut self, rhs: Int256) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int256, Int256); + +impl Div for Int256 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Div, div for Int256, Int256); + +impl Rem for Int256 { + 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 Int256, Int256); + +impl Not for Int256 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Neg for Int256 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl RemAssign for Int256 { + fn rem_assign(&mut self, rhs: Int256) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int256, Int256); + +impl Mul for Int256 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int256, Int256); + +impl MulAssign for Int256 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int256, Int256); + +impl Shr for Int256 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int256", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shr, shr for Int256, u32); + +impl Shl for Int256 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int256", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shl, shl for Int256, u32); + +impl AddAssign for Int256 { + fn add_assign(&mut self, rhs: Int256) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for Int256, Int256); + +impl DivAssign for Int256 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Int256, Int256); + +impl ShrAssign for Int256 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int256, u32); + +impl ShlAssign for Int256 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int256, u32); + +impl Serialize for Int256 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int256 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int256Visitor) + } +} + +struct Int256Visitor; + +impl<'de> de::Visitor<'de> for Int256Visitor { + type Value = Int256; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int256::try_from(v).map_err(|e| E::custom(format!("invalid Int256 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int256 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 32); + } + + #[test] + fn int256_new_works() { + let num = Int256::new([1; 32]); + let a: [u8; 32] = num.to_be_bytes(); + assert_eq!(a, [1; 32]); + + let be_bytes = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Int256::new(be_bytes); + let resulting_bytes: [u8; 32] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int256_zero_works() { + let zero = Int256::zero(); + assert_eq!(zero.to_be_bytes(), [0; 32]); + } + + #[test] + fn uint256_one_works() { + let one = Int256::one(); + let mut one_be = [0; 32]; + one_be[31] = 1; + + assert_eq!(one.to_be_bytes(), one_be); + } + + #[test] + fn int256_endianness() { + let be_bytes = [ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let le_bytes = [ + 3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]; + + // These should all be the same. + let num1 = Int256::new(be_bytes); + let num2 = Int256::from_be_bytes(be_bytes); + let num3 = Int256::from_le_bytes(le_bytes); + assert_eq!(num1, Int256::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + assert_eq!(num1, num3); + } + + #[test] + fn int256_convert_from() { + let a = Int256::from(5u128); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u64); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u32); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u16); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u8); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(-5i128); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i64); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i32); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i16); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i8); + assert_eq!(a.0, I256::from(-5i32)); + + let result = Int256::try_from("34567"); + assert_eq!( + result.unwrap().0, + I256::from_str_radix("34567", 10).unwrap() + ); + + let result = Int256::try_from("1.23"); + assert!(result.is_err()); + } + + #[test] + fn int256_implements_display() { + let a = Int256::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int256::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + + let a = Int256::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int256_display_padding_works() { + let a = Int256::from(123u64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int256::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + } + + #[test] + fn int256_to_be_bytes_works() { + assert_eq!(Int256::zero().to_be_bytes(), [0; 32]); + + let mut max = [0xff; 32]; + max[0] = 0x7f; + assert_eq!(Int256::MAX.to_be_bytes(), max); + + let mut one = [0; 32]; + one[31] = 1; + assert_eq!(Int256::from(1u128).to_be_bytes(), one); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(32, "big")]` + assert_eq!( + Int256::from(240282366920938463463374607431768124608u128).to_be_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 196, 179, 87, 165, 121, 59, + 133, 246, 117, 221, 191, 255, 254, 172, 192 + ] + ); + assert_eq!( + Int256::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240 + ]) + .to_be_bytes(), + [ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240 + ] + ); + } + + #[test] + fn int256_to_le_bytes_works() { + assert_eq!(Int256::zero().to_le_bytes(), [0; 32]); + + let mut max = [0xff; 32]; + max[31] = 0x7f; + assert_eq!(Int256::MAX.to_le_bytes(), max); + + let mut one = [0; 32]; + one[0] = 1; + assert_eq!(Int256::from(1u128).to_le_bytes(), one); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(64, "little")]` + assert_eq!( + Int256::from(240282366920938463463374607431768124608u128).to_le_bytes(), + [ + 192, 172, 254, 255, 191, 221, 117, 246, 133, 59, 121, 165, 87, 179, 196, 180, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ] + ); + assert_eq!( + Int256::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240 + ]) + .to_le_bytes(), + [ + 240, 150, 115, 200, 240, 2, 233, 42, 7, 192, 201, 211, 54, 65, 76, 87, 78, 67, 21, + 33, 38, 0, 91, 58, 200, 123, 67, 87, 32, 23, 4, 17 + ] + ); + } + + #[test] + fn int256_is_zero_works() { + assert!(Int256::zero().is_zero()); + assert!(Int256(I256::from(0u32)).is_zero()); + + assert!(!Int256::from(1u32).is_zero()); + assert!(!Int256::from(123u32).is_zero()); + assert!(!Int256::from(-123i32).is_zero()); + } + + #[test] + fn int256_wrapping_methods() { + // wrapping_add + assert_eq!( + Int256::from(2u32).wrapping_add(Int256::from(2u32)), + Int256::from(4u32) + ); // non-wrapping + assert_eq!(Int256::MAX.wrapping_add(Int256::from(1u32)), Int256::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int256::from(7u32).wrapping_sub(Int256::from(5u32)), + Int256::from(2u32) + ); // non-wrapping + assert_eq!(Int256::MIN.wrapping_sub(Int256::from(1u32)), Int256::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int256::from(3u32).wrapping_mul(Int256::from(2u32)), + Int256::from(6u32) + ); // non-wrapping + assert_eq!( + Int256::MAX.wrapping_mul(Int256::from(2u32)), + Int256::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int256::from(2u32).wrapping_pow(3), Int256::from(8u32)); // non-wrapping + assert_eq!(Int256::MAX.wrapping_pow(2), Int256::from(1u32)); // wrapping + } + + #[test] + fn int256_json() { + let orig = Int256::from(1234567890987654321u128); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int256 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int256_compare() { + let a = Int256::from(12345u32); + let b = Int256::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int256::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_math() { + let a = Int256::from(-12345i32); + let b = Int256::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int256::from(11111u32)); + assert_eq!(a + &b, Int256::from(11111u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int256::from(35801u32)); + assert_eq!(b - &a, Int256::from(35801u32)); + + // test += with owned and reference right hand side + let mut c = Int256::from(300000u32); + c += b; + assert_eq!(c, Int256::from(323456u32)); + let mut d = Int256::from(300000u32); + d += &b; + assert_eq!(d, Int256::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int256::from(300000u32); + c -= b; + assert_eq!(c, Int256::from(276544u32)); + let mut d = Int256::from(300000u32); + d -= &b; + assert_eq!(d, Int256::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int256::from(-35801i32)); + } + + #[test] + #[should_panic] + fn int256_add_overflow_panics() { + let _ = Int256::MAX + Int256::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_sub_works() { + assert_eq!(Int256::from(2u32) - Int256::from(1u32), Int256::from(1u32)); + assert_eq!(Int256::from(2u32) - Int256::from(0u32), Int256::from(2u32)); + assert_eq!(Int256::from(2u32) - Int256::from(2u32), Int256::from(0u32)); + assert_eq!(Int256::from(2u32) - Int256::from(3u32), Int256::from(-1i32)); + + // works for refs + let a = Int256::from(10u32); + let b = Int256::from(3u32); + let expected = Int256::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int256_sub_overflow_panics() { + let _ = Int256::MIN + Int256::one() - Int256::from(2u32); + } + + #[test] + fn int256_sub_assign_works() { + let mut a = Int256::from(14u32); + a -= Int256::from(2u32); + assert_eq!(a, Int256::from(12u32)); + + // works for refs + let mut a = Int256::from(10u32); + let b = Int256::from(3u32); + let expected = Int256::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_mul_works() { + assert_eq!(Int256::from(2u32) * Int256::from(3u32), Int256::from(6u32)); + assert_eq!(Int256::from(2u32) * Int256::zero(), Int256::zero()); + + // works for refs + let a = Int256::from(11u32); + let b = Int256::from(3u32); + let expected = Int256::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int256_mul_assign_works() { + let mut a = Int256::from(14u32); + a *= Int256::from(2u32); + assert_eq!(a, Int256::from(28u32)); + + // works for refs + let mut a = Int256::from(10u32); + let b = Int256::from(3u32); + a *= &b; + assert_eq!(a, Int256::from(30u32)); + } + + #[test] + fn int256_pow_works() { + assert_eq!(Int256::from(2u32).pow(2), Int256::from(4u32)); + assert_eq!(Int256::from(2u32).pow(10), Int256::from(1024u32)); + } + + #[test] + #[should_panic] + fn int256_pow_overflow_panics() { + _ = Int256::MAX.pow(2u32); + } + + #[test] + fn int256_shr_works() { + let original = Int256::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, + ]); + + let shifted = Int256::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, + ]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int256_shr_overflow_panics() { + let _ = Int256::from(1u32) >> 256u32; + } + + #[test] + fn sum_works() { + let nums = vec![ + Int256::from(17u32), + Int256::from(123u32), + Int256::from(540u32), + Int256::from(82u32), + ]; + let expected = Int256::from(762u32); + + let sum_as_ref: Int256 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int256 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int256_methods() { + // checked_* + assert!(matches!( + Int256::MAX.checked_add(Int256::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int256::from(1u32).checked_add(Int256::from(1u32)), + Ok(Int256::from(2u32)), + ); + assert!(matches!( + Int256::MIN.checked_sub(Int256::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int256::from(2u32).checked_sub(Int256::from(1u32)), + Ok(Int256::from(1u32)), + ); + assert!(matches!( + Int256::MAX.checked_mul(Int256::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int256::from(2u32).checked_mul(Int256::from(2u32)), + Ok(Int256::from(4u32)), + ); + assert!(matches!( + Int256::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int256::from(2u32).checked_pow(3u32), Ok(Int256::from(8u32)),); + assert_eq!( + Int256::MAX.checked_div(Int256::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int256::from(6u32).checked_div(Int256::from(2u32)), + Ok(Int256::from(3u32)), + ); + assert_eq!( + Int256::MAX.checked_div_euclid(Int256::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int256::from(6u32).checked_div_euclid(Int256::from(2u32)), + Ok(Int256::from(3u32)), + ); + assert_eq!( + Int256::from(7u32).checked_div_euclid(Int256::from(2u32)), + Ok(Int256::from(3u32)), + ); + assert!(matches!( + Int256::MAX.checked_rem(Int256::from(0u32)), + Err(DivideByZeroError { .. }) + )); + // checked_* with negative numbers + assert_eq!( + Int256::from(-12i32).checked_div(Int256::from(10i32)), + Ok(Int256::from(-1i32)), + ); + assert_eq!( + Int256::from(-2i32).checked_pow(3u32), + Ok(Int256::from(-8i32)), + ); + assert_eq!( + Int256::from(-6i32).checked_mul(Int256::from(-7i32)), + Ok(Int256::from(42i32)), + ); + assert_eq!( + Int256::from(-2i32).checked_add(Int256::from(3i32)), + Ok(Int256::from(1i32)), + ); + assert_eq!( + Int256::from(-1i32).checked_div_euclid(Int256::from(-2i32)), + Ok(Int256::from(1u32)), + ); + + // saturating_* + assert_eq!(Int256::MAX.saturating_add(Int256::from(1u32)), Int256::MAX); + assert_eq!(Int256::MIN.saturating_sub(Int256::from(1u32)), Int256::MIN); + assert_eq!(Int256::MAX.saturating_mul(Int256::from(2u32)), Int256::MAX); + assert_eq!(Int256::from(4u32).saturating_pow(2u32), Int256::from(16u32)); + assert_eq!(Int256::MAX.saturating_pow(2u32), Int256::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_implements_rem() { + let a = Int256::from(10u32); + assert_eq!(a % Int256::from(10u32), Int256::zero()); + assert_eq!(a % Int256::from(2u32), Int256::zero()); + assert_eq!(a % Int256::from(1u32), Int256::zero()); + assert_eq!(a % Int256::from(3u32), Int256::from(1u32)); + assert_eq!(a % Int256::from(4u32), Int256::from(2u32)); + + assert_eq!( + Int256::from(-12i32) % Int256::from(10i32), + Int256::from(-2i32) + ); + assert_eq!( + Int256::from(12i32) % Int256::from(-10i32), + Int256::from(2i32) + ); + assert_eq!( + Int256::from(-12i32) % Int256::from(-10i32), + Int256::from(-2i32) + ); + + // works for refs + let a = Int256::from(10u32); + let b = Int256::from(3u32); + let expected = Int256::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int256_rem_panics_for_zero() { + let _ = Int256::from(10u32) % Int256::zero(); + } + + #[test] + fn int256_rem_assign_works() { + let mut a = Int256::from(30u32); + a %= Int256::from(4u32); + assert_eq!(a, Int256::from(2u32)); + + // works for refs + let mut a = Int256::from(25u32); + let b = Int256::from(6u32); + a %= &b; + assert_eq!(a, Int256::from(1u32)); + } + + #[test] + fn int256_shr() { + let x: Int256 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!( + x >> 1, + Int256::from(0x4000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x >> 4, + Int256::from(0x0800_0000_0000_0000_0000_0000_0000_0000u128) + ); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int256::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int256::one() + ); + } + + #[test] + fn int256_shl() { + let x: Int256 = 0x0800_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!( + x << 1, + Int256::from(0x1000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x << 4, + Int256::from(0x8000_0000_0000_0000_0000_0000_0000_0000u128) + ); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int256::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int256::MIN + ); + } + + #[test] + fn int256_abs_diff_works() { + let a = Int256::from(42u32); + let b = Int256::from(5u32); + let expected = Uint256::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let c = Int256::from(-5i32); + assert_eq!(b.abs_diff(c), Uint256::from(10u32)); + assert_eq!(c.abs_diff(b), Uint256::from(10u32)); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int256_neg_min_panics() { + _ = -Int256::MIN; + } + + #[test] + fn int256_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (u64, u64, bool)| { + (Int256::from(lhs), Int256::from(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); + } + } +} diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs new file mode 100644 index 0000000000..980b81ee5c --- /dev/null +++ b/packages/std/src/math/int512.rs @@ -0,0 +1,1242 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, 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}; + +/// 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. +/// +/// # Examples +/// +/// Use `from` to create instances out of primitive uint types or `new` to provide big +/// endian bytes: +/// +/// ``` +/// # use cosmwasm_std::Int512; +/// let a = Int512::from(258u128); +/// let b = Int512::new([ +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, +/// ]); +/// assert_eq!(a, b); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int512(#[schemars(with = "String")] I512); + +forward_ref_partial_eq!(Int512, Int512); + +impl Int512 { + pub const MAX: Int512 = Int512(I512::MAX); + pub const MIN: Int512 = Int512(I512::MIN); + + /// Creates a Int512(value) from a big endian representation. It's just an alias for + /// `from_be_bytes`. + #[inline] + pub const fn new(value: [u8; 64]) -> Self { + Self::from_be_bytes(value) + } + + /// Creates a Int512(0) + #[inline] + pub const fn zero() -> Self { + Int512(I512::ZERO) + } + + /// Creates a Int512(1) + #[inline] + pub const fn one() -> Self { + Self(I512::ONE) + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 64]) -> Self { + let words: [u64; 8] = [ + u64::from_le_bytes([ + data[63], data[62], data[61], data[60], data[59], data[58], data[57], data[56], + ]), + u64::from_le_bytes([ + data[55], data[54], data[53], data[52], data[51], data[50], data[49], data[48], + ]), + u64::from_le_bytes([ + data[47], data[46], data[45], data[44], data[43], data[42], data[41], data[40], + ]), + u64::from_le_bytes([ + data[39], data[38], data[37], data[36], data[35], data[34], data[33], data[32], + ]), + u64::from_le_bytes([ + data[31], data[30], data[29], data[28], data[27], data[26], data[25], data[24], + ]), + u64::from_le_bytes([ + data[23], data[22], data[21], data[20], data[19], data[18], data[17], data[16], + ]), + u64::from_le_bytes([ + data[15], data[14], data[13], data[12], data[11], data[10], data[9], data[8], + ]), + u64::from_le_bytes([ + data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], + ]), + ]; + Self(I512::from_bits(U512::from_digits(words))) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 64]) -> Self { + let words: [u64; 8] = [ + u64::from_le_bytes([ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]), + u64::from_le_bytes([ + data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], + ]), + u64::from_le_bytes([ + data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23], + ]), + u64::from_le_bytes([ + data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], + ]), + u64::from_le_bytes([ + data[32], data[33], data[34], data[35], data[36], data[37], data[38], data[39], + ]), + u64::from_le_bytes([ + data[40], data[41], data[42], data[43], data[44], data[45], data[46], data[47], + ]), + u64::from_le_bytes([ + data[48], data[49], data[50], data[51], data[52], data[53], data[54], data[55], + ]), + u64::from_le_bytes([ + data[56], data[57], data[58], data[59], data[60], data[61], data[62], data[63], + ]), + ]; + Self(I512::from_bits(U512::from_digits(words))) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 64] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[7].to_be_bytes(), + words[6].to_be_bytes(), + words[5].to_be_bytes(), + words[4].to_be_bytes(), + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 64] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), + words[4].to_le_bytes(), + words[5].to_le_bytes(), + words[6].to_le_bytes(), + words[7].to_le_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[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)) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 512 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 512 { + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + Self(self.0.wrapping_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + Self(self.0.wrapping_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + Self(self.0.wrapping_pow(other)) + } + + #[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 { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + Self(self.0.saturating_pow(exp)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Uint512 { + Uint512(self.0.abs_diff(other.0)) + } +} + +impl From for Int512 { + fn from(val: Uint256) -> Self { + let mut bytes = [0u8; 64]; + bytes[32..].copy_from_slice(&val.to_be_bytes()); + + Self::from_be_bytes(bytes) + } +} + +impl From for Int512 { + fn from(val: Uint128) -> Self { + val.u128().into() + } +} + +impl From for Int512 { + fn from(val: Uint64) -> Self { + val.u64().into() + } +} + +impl From for Int512 { + fn from(val: u128) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u64) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u32) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u16) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u8) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i128) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i64) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i32) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i16) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i8) -> Self { + Int512(val.into()) + } +} + +impl TryFrom<&str> for Int512 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int512 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match I512::from_str_radix(s, 10) { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing Int512: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int512) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int512 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // The inner type doesn't work as expected with padding, so we + // work around that. Remove this code when the upstream padding is fixed. + let unpadded = self.0.to_string(); + let numeric = unpadded.strip_prefix('-').unwrap_or(&unpadded); + + f.pad_integral(self >= &Self::zero(), "", numeric) + } +} + +impl Add for Int512 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int512(self.0.checked_add(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Add, add for Int512, Int512); + +impl Sub for Int512 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int512(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int512, Int512); + +impl SubAssign for Int512 { + fn sub_assign(&mut self, rhs: Int512) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int512, Int512); + +impl Div for Int512 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Div, div for Int512, Int512); + +impl Rem for Int512 { + 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 Int512, Int512); + +impl Not for Int512 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Neg for Int512 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl RemAssign for Int512 { + fn rem_assign(&mut self, rhs: Int512) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int512, Int512); + +impl Mul for Int512 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int512, Int512); + +impl MulAssign for Int512 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int512, Int512); + +impl Shr for Int512 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int512", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shr, shr for Int512, u32); + +impl Shl for Int512 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int512", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shl, shl for Int512, u32); + +impl AddAssign for Int512 { + fn add_assign(&mut self, rhs: Int512) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for Int512, Int512); + +impl DivAssign for Int512 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Int512, Int512); + +impl ShrAssign for Int512 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int512, u32); + +impl ShlAssign for Int512 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int512, u32); + +impl Serialize for Int512 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int512 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int512Visitor) + } +} + +struct Int512Visitor; + +impl<'de> de::Visitor<'de> for Int512Visitor { + type Value = Int512; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int512::try_from(v).map_err(|e| E::custom(format!("invalid Int512 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int512 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 64); + } + + #[test] + fn int512_new_works() { + let num = Int512::new([1; 64]); + let a: [u8; 64] = num.to_be_bytes(); + assert_eq!(a, [1; 64]); + + let be_bytes = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Int512::new(be_bytes); + let resulting_bytes: [u8; 64] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int512_zero_works() { + let zero = Int512::zero(); + assert_eq!(zero.to_be_bytes(), [0; 64]); + } + + #[test] + fn uint512_one_works() { + let one = Int512::one(); + let mut one_be = [0; 64]; + one_be[63] = 1; + + assert_eq!(one.to_be_bytes(), one_be); + } + + #[test] + fn int512_endianness() { + let be_bytes = [ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let le_bytes = [ + 3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]; + + // These should all be the same. + let num1 = Int512::new(be_bytes); + let num2 = Int512::from_be_bytes(be_bytes); + let num3 = Int512::from_le_bytes(le_bytes); + assert_eq!(num1, Int512::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + assert_eq!(num1, num3); + } + + #[test] + fn int512_convert_from() { + let a = Int512::from(5u128); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u64); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u32); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u16); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u8); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(-5i128); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i64); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i32); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i16); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i8); + assert_eq!(a.0, I512::from(-5i32)); + + let result = Int512::try_from("34567"); + assert_eq!( + result.unwrap().0, + I512::from_str_radix("34567", 10).unwrap() + ); + + let result = Int512::try_from("1.23"); + assert!(result.is_err()); + } + + #[test] + fn int512_implements_display() { + let a = Int512::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int512::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + + let a = Int512::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int512_display_padding_works() { + let a = Int512::from(123u64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int512::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + } + + #[test] + fn int512_to_be_bytes_works() { + assert_eq!(Int512::zero().to_be_bytes(), [0; 64]); + + let mut max = [0xff; 64]; + max[0] = 0x7f; + assert_eq!(Int512::MAX.to_be_bytes(), max); + + let mut one = [0; 64]; + one[63] = 1; + assert_eq!(Int512::from(1u128).to_be_bytes(), one); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(64, "big")]` + assert_eq!( + Int512::from(240282366920938463463374607431768124608u128).to_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, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 196, 179, 87, 165, + 121, 59, 133, 246, 117, 221, 191, 255, 254, 172, 192 + ] + ); + assert_eq!( + Int512::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240, 218, 88, 106, 45, 208, 134, + 238, 119, 85, 22, 14, 88, 166, 195, 154, 73, 64, 10, 44, 59, 13, 22, 47, 12, 99, 8, + 252, 96, 230, 187, 38, 29 + ]) + .to_be_bytes(), + [ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240, 218, 88, 106, 45, 208, 134, + 238, 119, 85, 22, 14, 88, 166, 195, 154, 73, 64, 10, 44, 59, 13, 22, 47, 12, 99, 8, + 252, 96, 230, 187, 38, 29 + ] + ); + } + + #[test] + fn int512_to_le_bytes_works() { + assert_eq!(Int512::zero().to_le_bytes(), [0; 64]); + + let mut max = [0xff; 64]; + max[63] = 0x7f; + assert_eq!(Int512::MAX.to_le_bytes(), max); + + let mut one = [0; 64]; + one[0] = 1; + assert_eq!(Int512::from(1u128).to_le_bytes(), one); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(64, "little")]` + assert_eq!( + Int512::from(240282366920938463463374607431768124608u128).to_le_bytes(), + [ + 192, 172, 254, 255, 191, 221, 117, 246, 133, 59, 121, 165, 87, 179, 196, 180, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + assert_eq!( + Int512::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240, 218, 88, 106, 45, 208, 134, + 238, 119, 85, 22, 14, 88, 166, 195, 154, 73, 64, 10, 44, 59, 13, 22, 47, 12, 99, 8, + 252, 96, 230, 187, 38, 29 + ]) + .to_le_bytes(), + [ + 29, 38, 187, 230, 96, 252, 8, 99, 12, 47, 22, 13, 59, 44, 10, 64, 73, 154, 195, + 166, 88, 14, 22, 85, 119, 238, 134, 208, 45, 106, 88, 218, 240, 150, 115, 200, 240, + 2, 233, 42, 7, 192, 201, 211, 54, 65, 76, 87, 78, 67, 21, 33, 38, 0, 91, 58, 200, + 123, 67, 87, 32, 23, 4, 17 + ] + ); + } + + #[test] + fn int512_is_zero_works() { + assert!(Int512::zero().is_zero()); + assert!(Int512(I512::from(0u32)).is_zero()); + + assert!(!Int512::from(1u32).is_zero()); + assert!(!Int512::from(123u32).is_zero()); + assert!(!Int512::from(-123i32).is_zero()); + } + + #[test] + fn int512_wrapping_methods() { + // wrapping_add + assert_eq!( + Int512::from(2u32).wrapping_add(Int512::from(2u32)), + Int512::from(4u32) + ); // non-wrapping + assert_eq!(Int512::MAX.wrapping_add(Int512::from(1u32)), Int512::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int512::from(7u32).wrapping_sub(Int512::from(5u32)), + Int512::from(2u32) + ); // non-wrapping + assert_eq!(Int512::MIN.wrapping_sub(Int512::from(1u32)), Int512::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int512::from(3u32).wrapping_mul(Int512::from(2u32)), + Int512::from(6u32) + ); // non-wrapping + assert_eq!( + Int512::MAX.wrapping_mul(Int512::from(2u32)), + Int512::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int512::from(2u32).wrapping_pow(3), Int512::from(8u32)); // non-wrapping + assert_eq!(Int512::MAX.wrapping_pow(2), Int512::from(1u32)); // wrapping + } + + #[test] + fn int512_json() { + let orig = Int512::from(1234567890987654321u128); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int512 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int512_compare() { + let a = Int512::from(12345u32); + let b = Int512::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int512::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_math() { + let a = Int512::from(-12345i32); + let b = Int512::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int512::from(11111u32)); + assert_eq!(a + &b, Int512::from(11111u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int512::from(35801u32)); + assert_eq!(b - &a, Int512::from(35801u32)); + + // test += with owned and reference right hand side + let mut c = Int512::from(300000u32); + c += b; + assert_eq!(c, Int512::from(323456u32)); + let mut d = Int512::from(300000u32); + d += &b; + assert_eq!(d, Int512::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int512::from(300000u32); + c -= b; + assert_eq!(c, Int512::from(276544u32)); + let mut d = Int512::from(300000u32); + d -= &b; + assert_eq!(d, Int512::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int512::from(-35801i32)); + } + + #[test] + #[should_panic] + fn int512_add_overflow_panics() { + let _ = Int512::MAX + Int512::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_sub_works() { + assert_eq!(Int512::from(2u32) - Int512::from(1u32), Int512::from(1u32)); + assert_eq!(Int512::from(2u32) - Int512::from(0u32), Int512::from(2u32)); + assert_eq!(Int512::from(2u32) - Int512::from(2u32), Int512::from(0u32)); + assert_eq!(Int512::from(2u32) - Int512::from(3u32), Int512::from(-1i32)); + + // works for refs + let a = Int512::from(10u32); + let b = Int512::from(3u32); + let expected = Int512::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int512_sub_overflow_panics() { + let _ = Int512::MIN + Int512::one() - Int512::from(2u32); + } + + #[test] + fn int512_sub_assign_works() { + let mut a = Int512::from(14u32); + a -= Int512::from(2u32); + assert_eq!(a, Int512::from(12u32)); + + // works for refs + let mut a = Int512::from(10u32); + let b = Int512::from(3u32); + let expected = Int512::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_mul_works() { + assert_eq!(Int512::from(2u32) * Int512::from(3u32), Int512::from(6u32)); + assert_eq!(Int512::from(2u32) * Int512::zero(), Int512::zero()); + + // works for refs + let a = Int512::from(11u32); + let b = Int512::from(3u32); + let expected = Int512::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int512_mul_assign_works() { + let mut a = Int512::from(14u32); + a *= Int512::from(2u32); + assert_eq!(a, Int512::from(28u32)); + + // works for refs + let mut a = Int512::from(10u32); + let b = Int512::from(3u32); + a *= &b; + assert_eq!(a, Int512::from(30u32)); + } + + #[test] + fn int512_pow_works() { + assert_eq!(Int512::from(2u32).pow(2), Int512::from(4u32)); + assert_eq!(Int512::from(2u32).pow(10), Int512::from(1024u32)); + } + + #[test] + #[should_panic] + fn int512_pow_overflow_panics() { + _ = Int512::MAX.pow(2u32); + } + + #[test] + fn int512_shr_works() { + let original = Int512::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, + ]); + + let shifted = Int512::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, + ]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int512_shr_overflow_panics() { + let _ = Int512::from(1u32) >> 512u32; + } + + #[test] + fn sum_works() { + let nums = vec![ + Int512::from(17u32), + Int512::from(123u32), + Int512::from(540u32), + Int512::from(82u32), + ]; + let expected = Int512::from(762u32); + + let sum_as_ref: Int512 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int512 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int512_methods() { + // checked_* + assert!(matches!( + Int512::MAX.checked_add(Int512::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int512::from(1u32).checked_add(Int512::from(1u32)), + Ok(Int512::from(2u32)), + ); + assert!(matches!( + Int512::MIN.checked_sub(Int512::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int512::from(2u32).checked_sub(Int512::from(1u32)), + Ok(Int512::from(1u32)), + ); + assert!(matches!( + Int512::MAX.checked_mul(Int512::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int512::from(2u32).checked_mul(Int512::from(2u32)), + Ok(Int512::from(4u32)), + ); + assert!(matches!( + Int512::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int512::from(2u32).checked_pow(3u32), Ok(Int512::from(8u32)),); + assert_eq!( + Int512::MAX.checked_div(Int512::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int512::from(6u32).checked_div(Int512::from(2u32)), + Ok(Int512::from(3u32)), + ); + assert_eq!( + Int512::MAX.checked_div_euclid(Int512::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int512::from(6u32).checked_div_euclid(Int512::from(2u32)), + Ok(Int512::from(3u32)), + ); + assert_eq!( + Int512::from(7u32).checked_div_euclid(Int512::from(2u32)), + Ok(Int512::from(3u32)), + ); + assert!(matches!( + Int512::MAX.checked_rem(Int512::from(0u32)), + Err(DivideByZeroError { .. }) + )); + // checked_* with negative numbers + assert_eq!( + Int512::from(-12i32).checked_div(Int512::from(10i32)), + Ok(Int512::from(-1i32)), + ); + assert_eq!( + Int512::from(-2i32).checked_pow(3u32), + Ok(Int512::from(-8i32)), + ); + assert_eq!( + Int512::from(-6i32).checked_mul(Int512::from(-7i32)), + Ok(Int512::from(42i32)), + ); + assert_eq!( + Int512::from(-2i32).checked_add(Int512::from(3i32)), + Ok(Int512::from(1i32)), + ); + assert_eq!( + Int512::from(-1i32).checked_div_euclid(Int512::from(-2i32)), + Ok(Int512::from(1u32)), + ); + + // saturating_* + assert_eq!(Int512::MAX.saturating_add(Int512::from(1u32)), Int512::MAX); + assert_eq!(Int512::MIN.saturating_sub(Int512::from(1u32)), Int512::MIN); + assert_eq!(Int512::MAX.saturating_mul(Int512::from(2u32)), Int512::MAX); + assert_eq!(Int512::from(4u32).saturating_pow(2u32), Int512::from(16u32)); + assert_eq!(Int512::MAX.saturating_pow(2u32), Int512::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_implements_rem() { + let a = Int512::from(10u32); + assert_eq!(a % Int512::from(10u32), Int512::zero()); + assert_eq!(a % Int512::from(2u32), Int512::zero()); + assert_eq!(a % Int512::from(1u32), Int512::zero()); + assert_eq!(a % Int512::from(3u32), Int512::from(1u32)); + assert_eq!(a % Int512::from(4u32), Int512::from(2u32)); + + assert_eq!( + Int512::from(-12i32) % Int512::from(10i32), + Int512::from(-2i32) + ); + assert_eq!( + Int512::from(12i32) % Int512::from(-10i32), + Int512::from(2i32) + ); + assert_eq!( + Int512::from(-12i32) % Int512::from(-10i32), + Int512::from(-2i32) + ); + + // works for refs + let a = Int512::from(10u32); + let b = Int512::from(3u32); + let expected = Int512::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int512_rem_panics_for_zero() { + let _ = Int512::from(10u32) % Int512::zero(); + } + + #[test] + fn int512_rem_assign_works() { + let mut a = Int512::from(30u32); + a %= Int512::from(4u32); + assert_eq!(a, Int512::from(2u32)); + + // works for refs + let mut a = Int512::from(25u32); + let b = Int512::from(6u32); + a %= &b; + assert_eq!(a, Int512::from(1u32)); + } + + #[test] + fn int512_shr() { + let x: Int512 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!( + x >> 1, + Int512::from(0x4000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x >> 4, + Int512::from(0x0800_0000_0000_0000_0000_0000_0000_0000u128) + ); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int512::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int512::one() + ); + } + + #[test] + fn int512_shl() { + let x: Int512 = 0x0800_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!( + x << 1, + Int512::from(0x1000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x << 4, + Int512::from(0x8000_0000_0000_0000_0000_0000_0000_0000u128) + ); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int512::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int512::MIN + ); + } + + #[test] + fn int512_abs_diff_works() { + let a = Int512::from(42u32); + let b = Int512::from(5u32); + let expected = Uint512::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let c = Int512::from(-5i32); + assert_eq!(b.abs_diff(c), Uint512::from(10u32)); + assert_eq!(c.abs_diff(b), Uint512::from(10u32)); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int512_neg_min_panics() { + _ = -Int512::MIN; + } + + #[test] + fn int512_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (u64, u64, bool)| { + (Int512::from(lhs), Int512::from(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); + } + } +} diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs new file mode 100644 index 0000000000..8cfbc756c6 --- /dev/null +++ b/packages/std/src/math/int64.rs @@ -0,0 +1,1047 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, Uint64}; + +/// 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, +/// like JavaScript and jq. +/// +/// # Examples +/// +/// Use `from` to create instances of this and `i64` to get the value out: +/// +/// ``` +/// # use cosmwasm_std::Int64; +/// let a = Int64::from(258i64); +/// assert_eq!(a.i64(), 258); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int64(#[schemars(with = "String")] i64); + +forward_ref_partial_eq!(Int64, Int64); + +impl Int64 { + pub const MAX: Int64 = Int64(i64::MAX); + pub const MIN: Int64 = Int64(i64::MIN); + + /// Creates a Int64(value). + /// + /// This method is less flexible than `from` but can be called in a const context. + #[inline] + pub const fn new(value: i64) -> Self { + Self(value) + } + + /// Creates a Int64(0) + #[inline] + pub const fn zero() -> Self { + Int64(0) + } + + /// Creates a Int64(1) + #[inline] + pub const fn one() -> Self { + Self(1) + } + + /// Returns a copy of the internal data + pub const fn i64(&self) -> i64 { + self.0 + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 8]) -> Self { + Self(i64::from_be_bytes(data)) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 8]) -> Self { + Self(i64::from_le_bytes(data)) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 8] { + self.0.to_be_bytes() + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 8] { + self.0.to_le_bytes() + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0 == 0 + } + + #[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)) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 64 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 64 { + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + Self(self.0.wrapping_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + Self(self.0.wrapping_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + Self(self.0.wrapping_pow(other)) + } + + #[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 { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + Self(self.0.saturating_pow(exp)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Uint64 { + Uint64(self.0.abs_diff(other.0)) + } +} + +impl From for Int64 { + fn from(val: u32) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: u16) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: u8) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: i64) -> Self { + Int64(val) + } +} + +impl From for Int64 { + fn from(val: i32) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: i16) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: i8) -> Self { + Int64(val.into()) + } +} + +impl TryFrom<&str> for Int64 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int64 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match s.parse::() { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing Int64: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int64) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int64 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Add for Int64 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int64(self.0.checked_add(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Add, add for Int64, Int64); + +impl Sub for Int64 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int64(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int64, Int64); + +impl SubAssign for Int64 { + fn sub_assign(&mut self, rhs: Int64) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int64, Int64); + +impl Div for Int64 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Div, div for Int64, Int64); + +impl Rem for Int64 { + 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 Int64, Int64); + +impl Not for Int64 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Neg for Int64 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl RemAssign for Int64 { + fn rem_assign(&mut self, rhs: Int64) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int64, Int64); + +impl Mul for Int64 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int64, Int64); + +impl MulAssign for Int64 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int64, Int64); + +impl Shr for Int64 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int64", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shr, shr for Int64, u32); + +impl Shl for Int64 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int64", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shl, shl for Int64, u32); + +impl AddAssign for Int64 { + fn add_assign(&mut self, rhs: Int64) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for Int64, Int64); + +impl DivAssign for Int64 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Int64, Int64); + +impl ShrAssign for Int64 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int64, u32); + +impl ShlAssign for Int64 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int64, u32); + +impl Serialize for Int64 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int64 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int64Visitor) + } +} + +struct Int64Visitor; + +impl<'de> de::Visitor<'de> for Int64Visitor { + type Value = Int64; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int64::try_from(v).map_err(|e| E::custom(format!("invalid Int64 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int64 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 8); + } + + #[test] + fn int64_from_be_bytes_works() { + let num = Int64::from_be_bytes([1; 8]); + let a: [u8; 8] = num.to_be_bytes(); + assert_eq!(a, [1; 8]); + + let be_bytes = [0u8, 222u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; + let num = Int64::from_be_bytes(be_bytes); + let resulting_bytes: [u8; 8] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int64_new_works() { + let num = Int64::new(222); + assert_eq!(num.i64(), 222); + + let num = Int64::new(-222); + assert_eq!(num.i64(), -222); + + let num = Int64::new(i64::MAX); + assert_eq!(num.i64(), i64::MAX); + + let num = Int64::new(i64::MIN); + assert_eq!(num.i64(), i64::MIN); + } + + #[test] + fn int64_zero_works() { + let zero = Int64::zero(); + assert_eq!(zero.to_be_bytes(), [0; 8]); + } + + #[test] + fn uint64_one_works() { + let one = Int64::one(); + let mut one_be = [0; 8]; + one_be[7] = 1; + + assert_eq!(one.to_be_bytes(), one_be); + } + + #[test] + fn int64_endianness() { + let be_bytes = [0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; + let le_bytes = [3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8]; + + // These should all be the same. + let num1 = Int64::from_be_bytes(be_bytes); + let num2 = Int64::from_le_bytes(le_bytes); + assert_eq!(num1, Int64::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + } + + #[test] + fn int64_convert_from() { + let a = Int64::from(5i64); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5i64); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5u32); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5u16); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5u8); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(-5i64); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i64); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i32); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i16); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i8); + assert_eq!(a.0, i64::from(-5i32)); + + let result = Int64::try_from("34567"); + assert_eq!(result.unwrap().0, "34567".parse::().unwrap()); + + let result = Int64::try_from("1.23"); + assert!(result.is_err()); + } + + #[test] + fn int64_implements_display() { + let a = Int64::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int64::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + + let a = Int64::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int64_display_padding_works() { + let a = Int64::from(123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int64::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + } + + #[test] + fn int64_to_be_bytes_works() { + assert_eq!(Int64::zero().to_be_bytes(), [0; 8]); + + let mut max = [0xff; 8]; + max[0] = 0x7f; + assert_eq!(Int64::MAX.to_be_bytes(), max); + + let mut one = [0; 8]; + one[7] = 1; + assert_eq!(Int64::from(1i64).to_be_bytes(), one); + // Python: `[b for b in (8535972485454015680).to_bytes(8, "big")]` + assert_eq!( + Int64::from(8535972485454015680i64).to_be_bytes(), + [118, 117, 221, 191, 255, 254, 172, 192] + ); + assert_eq!( + Int64::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200]).to_be_bytes(), + [17, 4, 23, 32, 87, 67, 123, 200] + ); + } + + #[test] + fn int64_to_le_bytes_works() { + assert_eq!(Int64::zero().to_le_bytes(), [0; 8]); + + let mut max = [0xff; 8]; + max[7] = 0x7f; + assert_eq!(Int64::MAX.to_le_bytes(), max); + + let mut one = [0; 8]; + one[0] = 1; + assert_eq!(Int64::from(1i64).to_le_bytes(), one); + // Python: `[b for b in (8535972485454015680).to_bytes(8, "little")]` + assert_eq!( + Int64::from(8535972485454015680i64).to_le_bytes(), + [192, 172, 254, 255, 191, 221, 117, 118] + ); + assert_eq!( + Int64::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200]).to_le_bytes(), + [200, 123, 67, 87, 32, 23, 4, 17] + ); + } + + #[test] + fn int64_is_zero_works() { + assert!(Int64::zero().is_zero()); + assert!(Int64(i64::from(0u32)).is_zero()); + + assert!(!Int64::from(1u32).is_zero()); + assert!(!Int64::from(123u32).is_zero()); + assert!(!Int64::from(-123i32).is_zero()); + } + + #[test] + fn int64_wrapping_methods() { + // wrapping_add + assert_eq!( + Int64::from(2u32).wrapping_add(Int64::from(2u32)), + Int64::from(4u32) + ); // non-wrapping + assert_eq!(Int64::MAX.wrapping_add(Int64::from(1u32)), Int64::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int64::from(7u32).wrapping_sub(Int64::from(5u32)), + Int64::from(2u32) + ); // non-wrapping + assert_eq!(Int64::MIN.wrapping_sub(Int64::from(1u32)), Int64::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int64::from(3u32).wrapping_mul(Int64::from(2u32)), + Int64::from(6u32) + ); // non-wrapping + assert_eq!( + Int64::MAX.wrapping_mul(Int64::from(2u32)), + Int64::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int64::from(2u32).wrapping_pow(3), Int64::from(8u32)); // non-wrapping + assert_eq!(Int64::MAX.wrapping_pow(2), Int64::from(1u32)); // wrapping + } + + #[test] + fn int64_json() { + let orig = Int64::from(1234567890987654321i64); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int64 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int64_compare() { + let a = Int64::from(12345u32); + let b = Int64::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int64::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_math() { + let a = Int64::from(-12345i32); + let b = Int64::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int64::from(11111u32)); + assert_eq!(a + &b, Int64::from(11111u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int64::from(35801u32)); + assert_eq!(b - &a, Int64::from(35801u32)); + + // test += with owned and reference right hand side + let mut c = Int64::from(300000u32); + c += b; + assert_eq!(c, Int64::from(323456u32)); + let mut d = Int64::from(300000u32); + d += &b; + assert_eq!(d, Int64::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int64::from(300000u32); + c -= b; + assert_eq!(c, Int64::from(276544u32)); + let mut d = Int64::from(300000u32); + d -= &b; + assert_eq!(d, Int64::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int64::from(-35801i32)); + } + + #[test] + #[should_panic] + fn int64_add_overflow_panics() { + let _ = Int64::MAX + Int64::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_sub_works() { + assert_eq!(Int64::from(2u32) - Int64::from(1u32), Int64::from(1u32)); + assert_eq!(Int64::from(2u32) - Int64::from(0u32), Int64::from(2u32)); + assert_eq!(Int64::from(2u32) - Int64::from(2u32), Int64::from(0u32)); + assert_eq!(Int64::from(2u32) - Int64::from(3u32), Int64::from(-1i32)); + + // works for refs + let a = Int64::from(10u32); + let b = Int64::from(3u32); + let expected = Int64::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int64_sub_overflow_panics() { + let _ = Int64::MIN + Int64::one() - Int64::from(2u32); + } + + #[test] + fn int64_sub_assign_works() { + let mut a = Int64::from(14u32); + a -= Int64::from(2u32); + assert_eq!(a, Int64::from(12u32)); + + // works for refs + let mut a = Int64::from(10u32); + let b = Int64::from(3u32); + let expected = Int64::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_mul_works() { + assert_eq!(Int64::from(2u32) * Int64::from(3u32), Int64::from(6u32)); + assert_eq!(Int64::from(2u32) * Int64::zero(), Int64::zero()); + + // works for refs + let a = Int64::from(11u32); + let b = Int64::from(3u32); + let expected = Int64::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int64_mul_assign_works() { + let mut a = Int64::from(14u32); + a *= Int64::from(2u32); + assert_eq!(a, Int64::from(28u32)); + + // works for refs + let mut a = Int64::from(10u32); + let b = Int64::from(3u32); + a *= &b; + assert_eq!(a, Int64::from(30u32)); + } + + #[test] + fn int64_pow_works() { + assert_eq!(Int64::from(2u32).pow(2), Int64::from(4u32)); + assert_eq!(Int64::from(2u32).pow(10), Int64::from(1024u32)); + } + + #[test] + #[should_panic] + fn int64_pow_overflow_panics() { + _ = Int64::MAX.pow(2u32); + } + + #[test] + fn int64_shr_works() { + let original = Int64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8]); + + let shifted = Int64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int64_shr_overflow_panics() { + let _ = Int64::from(1u32) >> 64u32; + } + + #[test] + fn sum_works() { + let nums = vec![ + Int64::from(17u32), + Int64::from(123u32), + Int64::from(540u32), + Int64::from(82u32), + ]; + let expected = Int64::from(762u32); + + let sum_as_ref: Int64 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int64 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int64_methods() { + // checked_* + assert!(matches!( + Int64::MAX.checked_add(Int64::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int64::from(1u32).checked_add(Int64::from(1u32)), + Ok(Int64::from(2u32)), + ); + assert!(matches!( + Int64::MIN.checked_sub(Int64::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int64::from(2u32).checked_sub(Int64::from(1u32)), + Ok(Int64::from(1u32)), + ); + assert!(matches!( + Int64::MAX.checked_mul(Int64::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int64::from(2u32).checked_mul(Int64::from(2u32)), + Ok(Int64::from(4u32)), + ); + assert!(matches!( + Int64::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int64::from(2u32).checked_pow(3u32), Ok(Int64::from(8u32)),); + assert_eq!( + Int64::MAX.checked_div(Int64::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int64::from(6u32).checked_div(Int64::from(2u32)), + Ok(Int64::from(3u32)), + ); + assert_eq!( + Int64::MAX.checked_div_euclid(Int64::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int64::from(6u32).checked_div_euclid(Int64::from(2u32)), + Ok(Int64::from(3u32)), + ); + assert_eq!( + Int64::from(7u32).checked_div_euclid(Int64::from(2u32)), + Ok(Int64::from(3u32)), + ); + assert!(matches!( + Int64::MAX.checked_rem(Int64::from(0u32)), + Err(DivideByZeroError { .. }) + )); + // checked_* with negative numbers + assert_eq!( + Int64::from(-12i32).checked_div(Int64::from(10i32)), + Ok(Int64::from(-1i32)), + ); + assert_eq!(Int64::from(-2i32).checked_pow(3u32), Ok(Int64::from(-8i32)),); + assert_eq!( + Int64::from(-6i32).checked_mul(Int64::from(-7i32)), + Ok(Int64::from(42i32)), + ); + assert_eq!( + Int64::from(-2i32).checked_add(Int64::from(3i32)), + Ok(Int64::from(1i32)), + ); + assert_eq!( + Int64::from(-1i32).checked_div_euclid(Int64::from(-2i32)), + Ok(Int64::from(1u32)), + ); + + // saturating_* + assert_eq!(Int64::MAX.saturating_add(Int64::from(1u32)), Int64::MAX); + assert_eq!(Int64::MIN.saturating_sub(Int64::from(1u32)), Int64::MIN); + assert_eq!(Int64::MAX.saturating_mul(Int64::from(2u32)), Int64::MAX); + assert_eq!(Int64::from(4u32).saturating_pow(2u32), Int64::from(16u32)); + assert_eq!(Int64::MAX.saturating_pow(2u32), Int64::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_implements_rem() { + let a = Int64::from(10u32); + assert_eq!(a % Int64::from(10u32), Int64::zero()); + assert_eq!(a % Int64::from(2u32), Int64::zero()); + assert_eq!(a % Int64::from(1u32), Int64::zero()); + assert_eq!(a % Int64::from(3u32), Int64::from(1u32)); + assert_eq!(a % Int64::from(4u32), Int64::from(2u32)); + + assert_eq!(Int64::from(-12i32) % Int64::from(10i32), Int64::from(-2i32)); + assert_eq!(Int64::from(12i32) % Int64::from(-10i32), Int64::from(2i32)); + assert_eq!( + Int64::from(-12i32) % Int64::from(-10i32), + Int64::from(-2i32) + ); + + // works for refs + let a = Int64::from(10u32); + let b = Int64::from(3u32); + let expected = Int64::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int64_rem_panics_for_zero() { + let _ = Int64::from(10u32) % Int64::zero(); + } + + #[test] + fn int64_rem_assign_works() { + let mut a = Int64::from(30u32); + a %= Int64::from(4u32); + assert_eq!(a, Int64::from(2u32)); + + // works for refs + let mut a = Int64::from(25u32); + let b = Int64::from(6u32); + a %= &b; + assert_eq!(a, Int64::from(1u32)); + } + + #[test] + fn int64_shr() { + let x: Int64 = 0x4000_0000_0000_0000i64.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!(x >> 1, Int64::from(0x2000_0000_0000_0000i64)); + assert_eq!(x >> 4, Int64::from(0x0400_0000_0000_0000i64)); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int64::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int64::one() + ); + } + + #[test] + fn int64_shl() { + let x: Int64 = 0x0800_0000_0000_0000i64.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!(x << 1, Int64::from(0x1000_0000_0000_0000i64)); + assert_eq!(x << 4, Int64::from(0x0800_0000_0000_0000i64 << 4)); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int64::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int64::MIN + ); + } + + #[test] + fn int64_abs_diff_works() { + let a = Int64::from(42u32); + let b = Int64::from(5u32); + let expected = Uint64::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let c = Int64::from(-5i32); + assert_eq!(b.abs_diff(c), Uint64::from(10u32)); + assert_eq!(c.abs_diff(b), Uint64::from(10u32)); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int64_neg_min_panics() { + _ = -Int64::MIN; + } + + #[test] + fn int64_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (i64, i64, bool)| { + (Int64::from(lhs), Int64::from(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); + } + } +} diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index 72e55d9f1d..782276af37 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -1,6 +1,10 @@ mod decimal; mod decimal256; mod fraction; +mod int128; +mod int256; +mod int512; +mod int64; mod isqrt; mod uint128; mod uint256; @@ -10,6 +14,10 @@ mod uint64; pub use decimal::{Decimal, DecimalRangeExceeded}; pub use decimal256::{Decimal256, Decimal256RangeExceeded}; pub use fraction::Fraction; +pub use int128::Int128; +pub use int256::Int256; +pub use int512::Int512; +pub use int64::Int64; pub use isqrt::Isqrt; pub use uint128::Uint128; pub use uint256::Uint256; @@ -64,14 +72,31 @@ mod tests { { } + trait SignedImpl<'a>: IntImpl<'a> + Neg {} + impl AllImpl<'_> for Uint64 {} impl AllImpl<'_> for Uint128 {} impl AllImpl<'_> for Uint256 {} impl AllImpl<'_> for Uint512 {} + impl AllImpl<'_> for Int64 {} + impl AllImpl<'_> for Int128 {} + impl AllImpl<'_> for Int256 {} + impl AllImpl<'_> for Int512 {} + + impl IntImpl<'_> for Int64 {} + impl IntImpl<'_> for Int128 {} + impl IntImpl<'_> for Int256 {} + impl IntImpl<'_> for Int512 {} impl IntImpl<'_> for Uint64 {} impl IntImpl<'_> for Uint128 {} impl IntImpl<'_> for Uint256 {} impl IntImpl<'_> for Uint512 {} + impl AllImpl<'_> for Decimal {} impl AllImpl<'_> for Decimal256 {} + + impl SignedImpl<'_> for Int64 {} + impl SignedImpl<'_> for Int128 {} + impl SignedImpl<'_> for Int256 {} + impl SignedImpl<'_> for Int512 {} } diff --git a/packages/std/src/math/uint128.rs b/packages/std/src/math/uint128.rs index 6733dfc0c0..5bcaed78dc 100644 --- a/packages/std/src/math/uint128.rs +++ b/packages/std/src/math/uint128.rs @@ -37,7 +37,7 @@ use crate::{ /// assert_eq!(c.u128(), 70); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint128(#[schemars(with = "String")] u128); +pub struct Uint128(#[schemars(with = "String")] pub(crate) u128); forward_ref_partial_eq!(Uint128, Uint128); diff --git a/packages/std/src/math/uint256.rs b/packages/std/src/math/uint256.rs index d0d7fe94c9..9f71573c40 100644 --- a/packages/std/src/math/uint256.rs +++ b/packages/std/src/math/uint256.rs @@ -14,18 +14,9 @@ use crate::errors::{ }; use crate::{forward_ref_partial_eq, impl_mul_fraction, Fraction, Uint128, Uint512, Uint64}; -/// This module is purely a workaround that lets us ignore lints for all the code -/// the `construct_uint!` macro generates. -#[allow(clippy::all)] -mod uints { - uint::construct_uint! { - pub struct U256(4); - } -} - /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. -use uints::U256; +use bnum::types::U256; /// 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, @@ -48,13 +39,13 @@ use uints::U256; /// assert_eq!(a, b); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint256(#[schemars(with = "String")] U256); +pub struct Uint256(#[schemars(with = "String")] pub(crate) U256); forward_ref_partial_eq!(Uint256, Uint256); impl Uint256 { pub const MAX: Uint256 = Uint256(U256::MAX); - pub const MIN: Uint256 = Uint256(U256::zero()); + pub const MIN: Uint256 = Uint256(U256::ZERO); /// Creates a Uint256(value) from a big endian representation. It's just an alias for /// [`Uint256::from_be_bytes`]. @@ -67,16 +58,13 @@ impl Uint256 { /// Creates a Uint256(0) #[inline] pub const fn zero() -> Self { - Uint256(U256::zero()) + Self(U256::ZERO) } /// Creates a Uint256(1) #[inline] pub const fn one() -> Self { - Self::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, 0, 0, 0, 0, 0, - 0, 0, 1, - ]) + Self(U256::ONE) } #[must_use] @@ -95,7 +83,7 @@ impl Uint256 { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], ]), ]; - Self(U256(words)) + Self(U256::from_digits(words)) } #[must_use] @@ -114,7 +102,7 @@ impl Uint256 { data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], ]), ]; - Uint256(U256(words)) + Self(U256::from_digits(words)) } /// A conversion from `u128` that, unlike the one provided by the `From` trait, @@ -140,11 +128,12 @@ impl Uint256 { /// Returns a copy of the number as big endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_be_bytes(self) -> [u8; 32] { + let words = self.0.digits(); let words = [ - (self.0).0[3].to_be_bytes(), - (self.0).0[2].to_be_bytes(), - (self.0).0[1].to_be_bytes(), - (self.0).0[0].to_be_bytes(), + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } } @@ -152,25 +141,24 @@ impl Uint256 { /// Returns a copy of the number as little endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_le_bytes(self) -> [u8; 32] { + let words = self.0.digits(); let words = [ - (self.0).0[0].to_le_bytes(), - (self.0).0[1].to_le_bytes(), - (self.0).0[2].to_le_bytes(), - (self.0).0[3].to_le_bytes(), + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } } #[must_use] pub const fn is_zero(&self) -> bool { - let words = (self.0).0; - words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 + self.0.is_zero() } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { - let res = self.0.pow(exp.into()); - Self(res) + Self(self.0.pow(exp)) } /// Returns `self * numerator / denominator`. @@ -257,7 +245,7 @@ impl Uint256 { pub fn checked_pow(self, exp: u32) -> Result { self.0 - .checked_pow(exp.into()) + .checked_pow(exp) .map(Self) .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) } @@ -299,29 +287,25 @@ impl Uint256 { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_add(other.0); - Self(value) + Self(self.0.wrapping_add(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_sub(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_sub(other.0); - Self(value) + Self(self.0.wrapping_sub(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_mul(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_mul(other.0); - Self(value) + Self(self.0.wrapping_mul(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_pow(self, other: u32) -> Self { - let (value, _did_overflow) = self.0.overflowing_pow(other.into()); - Self(value) + Self(self.0.wrapping_pow(other)) } #[must_use = "this returns the result of the operation, without modifying the original"] @@ -341,19 +325,12 @@ impl Uint256 { #[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(_) => Self::MAX, - } + Self(self.0.saturating_pow(exp)) } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn abs_diff(self, other: Self) -> Self { - if self < other { - other - self - } else { - self - other - } + Self(self.0.abs_diff(other.0)) } } @@ -427,7 +404,7 @@ impl FromStr for Uint256 { return Err(StdError::generic_err("Parsing u256: received empty string")); } - match U256::from_dec_str(s) { + match U256::from_str_radix(s, 10) { Ok(u) => Ok(Uint256(u)), Err(e) => Err(StdError::generic_err(format!("Parsing u256: {e}"))), } @@ -1046,22 +1023,25 @@ mod tests { #[test] fn uint256_convert_from() { let a = Uint256::from(5u128); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u64); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u32); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u16); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u8); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let result = Uint256::try_from("34567"); - assert_eq!(result.unwrap().0, U256::from_dec_str("34567").unwrap()); + assert_eq!( + result.unwrap().0, + U256::from_str_radix("34567", 10).unwrap() + ); let result = Uint256::try_from("1.23"); assert!(result.is_err()); @@ -1221,7 +1201,7 @@ mod tests { #[test] fn uint256_is_zero_works() { assert!(Uint256::zero().is_zero()); - assert!(Uint256(U256::from(0)).is_zero()); + assert!(Uint256(U256::from(0u32)).is_zero()); assert!(!Uint256::from(1u32).is_zero()); assert!(!Uint256::from(123u32).is_zero()); @@ -1642,7 +1622,7 @@ mod tests { } #[test] - #[should_panic(expected = "division by zero")] + #[should_panic(expected = "divisor of zero")] fn uint256_rem_panics_for_zero() { let _ = Uint256::from(10u32) % Uint256::zero(); } diff --git a/packages/std/src/math/uint512.rs b/packages/std/src/math/uint512.rs index a9eb33e08e..5d581e05c7 100644 --- a/packages/std/src/math/uint512.rs +++ b/packages/std/src/math/uint512.rs @@ -3,8 +3,8 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use std::fmt; use std::ops::{ - Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, - Sub, SubAssign, + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, }; use std::str::FromStr; @@ -13,18 +13,9 @@ use crate::errors::{ }; use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint64}; -/// This module is purely a workaround that lets us ignore lints for all the code -/// the `construct_uint!` macro generates. -#[allow(clippy::all)] -mod uints { - uint::construct_uint! { - pub struct U512(8); - } -} - /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. -use uints::U512; +use bnum::types::U512; /// 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, @@ -51,16 +42,16 @@ use uints::U512; /// assert_eq!(a, b); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint512(#[schemars(with = "String")] U512); +pub struct Uint512(#[schemars(with = "String")] pub(crate) U512); forward_ref_partial_eq!(Uint512, Uint512); impl Uint512 { pub const MAX: Uint512 = Uint512(U512::MAX); - pub const MIN: Uint512 = Uint512(U512::zero()); + pub const MIN: Uint512 = Uint512(U512::ZERO); /// Creates a Uint512(value) from a big endian representation. It's just an alias for - /// `from_big_endian`. + /// `from_be_bytes`. pub const fn new(value: [u8; 64]) -> Self { Self::from_be_bytes(value) } @@ -68,17 +59,13 @@ impl Uint512 { /// Creates a Uint512(0) #[inline] pub const fn zero() -> Self { - Uint512(U512::zero()) + Uint512(U512::ZERO) } /// Creates a Uint512(1) #[inline] pub const fn one() -> Self { - Self::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, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - ]) + Self(U512::ONE) } #[must_use] @@ -109,7 +96,7 @@ impl Uint512 { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], ]), ]; - Self(U512(words)) + Self(U512::from_digits(words)) } #[must_use] @@ -140,7 +127,7 @@ impl Uint512 { data[56], data[57], data[58], data[59], data[60], data[61], data[62], data[63], ]), ]; - Self(U512(words)) + Self(U512::from_digits(words)) } /// A conversion from `Uint256` that, unlike the one provided by the `From` trait, @@ -161,15 +148,16 @@ impl Uint512 { /// Returns a copy of the number as big endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_be_bytes(self) -> [u8; 64] { + let words = self.0.digits(); let words = [ - (self.0).0[7].to_be_bytes(), - (self.0).0[6].to_be_bytes(), - (self.0).0[5].to_be_bytes(), - (self.0).0[4].to_be_bytes(), - (self.0).0[3].to_be_bytes(), - (self.0).0[2].to_be_bytes(), - (self.0).0[1].to_be_bytes(), - (self.0).0[0].to_be_bytes(), + words[7].to_be_bytes(), + words[6].to_be_bytes(), + words[5].to_be_bytes(), + words[4].to_be_bytes(), + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } } @@ -177,36 +165,28 @@ impl Uint512 { /// Returns a copy of the number as little endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_le_bytes(self) -> [u8; 64] { + let words = self.0.digits(); let words = [ - (self.0).0[0].to_le_bytes(), - (self.0).0[1].to_le_bytes(), - (self.0).0[2].to_le_bytes(), - (self.0).0[3].to_le_bytes(), - (self.0).0[4].to_le_bytes(), - (self.0).0[5].to_le_bytes(), - (self.0).0[6].to_le_bytes(), - (self.0).0[7].to_le_bytes(), + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), + words[4].to_le_bytes(), + words[5].to_le_bytes(), + words[6].to_le_bytes(), + words[7].to_le_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } } #[must_use] pub const fn is_zero(&self) -> bool { - let words = (self.0).0; - words[0] == 0 - && words[1] == 0 - && words[2] == 0 - && words[3] == 0 - && words[4] == 0 - && words[5] == 0 - && words[6] == 0 - && words[7] == 0 + self.0.is_zero() } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { - let res = self.0.pow(exp.into()); - Self(res) + Self(self.0.pow(exp)) } pub fn checked_add(self, other: Self) -> Result { @@ -232,7 +212,7 @@ impl Uint512 { pub fn checked_pow(self, exp: u32) -> Result { self.0 - .checked_pow(exp.into()) + .checked_pow(exp) .map(Self) .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) } @@ -256,11 +236,10 @@ impl Uint512 { } pub fn checked_shr(self, other: u32) -> Result { - if other >= 512 { - return Err(OverflowError::new(OverflowOperation::Shr, self, other)); - } - - Ok(Self(self.0.shr(other))) + self.0 + .checked_shr(other) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Shr, self, other)) } pub fn checked_shl(self, other: u32) -> Result { @@ -274,29 +253,25 @@ impl Uint512 { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_add(other.0); - Self(value) + Self(self.0.wrapping_add(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_sub(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_sub(other.0); - Self(value) + Self(self.0.wrapping_sub(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_mul(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_mul(other.0); - Self(value) + Self(self.0.wrapping_mul(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_pow(self, other: u32) -> Self { - let (value, _did_overflow) = self.0.overflowing_pow(other.into()); - Self(value) + Self(self.0.wrapping_pow(other)) } #[must_use = "this returns the result of the operation, without modifying the original"] @@ -316,19 +291,12 @@ impl Uint512 { #[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(_) => Self::MAX, - } + Self(self.0.saturating_pow(exp)) } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn abs_diff(self, other: Self) -> Self { - if self < other { - other - self - } else { - self - other - } + Self(self.0.abs_diff(other.0)) } } @@ -424,7 +392,7 @@ impl FromStr for Uint512 { type Err = StdError; fn from_str(s: &str) -> Result { - match U512::from_dec_str(s) { + match U512::from_str_radix(s, 10) { Ok(u) => Ok(Self(u)), Err(e) => Err(StdError::generic_err(format!("Parsing u512: {e}"))), } @@ -508,6 +476,14 @@ impl Rem for Uint512 { } forward_ref_binop!(impl Rem, rem for Uint512, Uint512); +impl Not for Uint512 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + impl RemAssign for Uint512 { fn rem_assign(&mut self, rhs: Uint512) { *self = *self % rhs; @@ -742,22 +718,25 @@ mod tests { #[test] fn uint512_convert_from() { let a = Uint512::from(5u128); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u64); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u32); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u16); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u8); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let result = Uint512::try_from("34567"); - assert_eq!(result.unwrap().0, U512::from_dec_str("34567").unwrap()); + assert_eq!( + result.unwrap().0, + U512::from_str_radix("34567", 10).unwrap() + ); let result = Uint512::try_from("1.23"); assert!(result.is_err()); @@ -935,7 +914,7 @@ mod tests { #[test] fn uint512_is_zero_works() { assert!(Uint512::zero().is_zero()); - assert!(Uint512(U512::from(0)).is_zero()); + assert!(Uint512(U512::from(0u32)).is_zero()); assert!(!Uint512::from(1u32).is_zero()); assert!(!Uint512::from(123u32).is_zero()); @@ -1300,7 +1279,7 @@ mod tests { } #[test] - #[should_panic(expected = "division by zero")] + #[should_panic(expected = "divisor of zero")] fn uint512_rem_panics_for_zero() { let _ = Uint512::from(10u32) % Uint512::zero(); } diff --git a/packages/std/src/math/uint64.rs b/packages/std/src/math/uint64.rs index cea6cd37ff..734b7a248d 100644 --- a/packages/std/src/math/uint64.rs +++ b/packages/std/src/math/uint64.rs @@ -30,7 +30,7 @@ use crate::{forward_ref_partial_eq, impl_mul_fraction, Fraction, Uint128}; /// assert_eq!(b.u64(), 70); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint64(#[schemars(with = "String")] u64); +pub struct Uint64(#[schemars(with = "String")] pub(crate) u64); forward_ref_partial_eq!(Uint64, Uint64); diff --git a/packages/std/src/query/distribution.rs b/packages/std/src/query/distribution.rs new file mode 100644 index 0000000000..ffe5772322 --- /dev/null +++ b/packages/std/src/query/distribution.rs @@ -0,0 +1,22 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use super::query_response::QueryResponseType; + +#[non_exhaustive] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum DistributionQuery { + // https://github.com/cosmos/cosmos-sdk/blob/4f6f6c00021f4b5ee486bbb71ae2071a8ceb47c9/x/distribution/types/query.pb.go#L792-L795 + DelegatorWithdrawAddress { delegator_address: String }, +} + +// https://github.com/cosmos/cosmos-sdk/blob/4f6f6c00021f4b5ee486bbb71ae2071a8ceb47c9/x/distribution/types/query.pb.go#L832-L835 +#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +#[non_exhaustive] +pub struct DelegatorWithdrawAddressResponse { + pub withdraw_address: String, +} + +impl QueryResponseType for DelegatorWithdrawAddressResponse {} diff --git a/packages/std/src/query/mod.rs b/packages/std/src/query/mod.rs index c88e209b8f..5401f9145d 100644 --- a/packages/std/src/query/mod.rs +++ b/packages/std/src/query/mod.rs @@ -6,6 +6,7 @@ use crate::Binary; use crate::Empty; mod bank; +mod distribution; mod ibc; mod query_response; mod staking; @@ -16,6 +17,8 @@ pub use bank::SupplyResponse; pub use bank::{AllBalanceResponse, BalanceResponse, BankQuery}; #[cfg(feature = "cosmwasm_1_3")] pub use bank::{AllDenomMetadataResponse, DenomMetadataResponse}; +#[cfg(feature = "cosmwasm_1_3")] +pub use distribution::{DelegatorWithdrawAddressResponse, DistributionQuery}; #[cfg(feature = "stargate")] pub use ibc::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdResponse}; #[cfg(feature = "staking")] @@ -35,6 +38,8 @@ pub enum QueryRequest { Custom(C), #[cfg(feature = "staking")] Staking(StakingQuery), + #[cfg(feature = "cosmwasm_1_3")] + Distribution(DistributionQuery), /// A Stargate query is encoded the same way as abci_query, with path and protobuf encoded request data. /// The format is defined in [ADR-21](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-021-protobuf-query-encoding.md). /// The response is protobuf encoded data directly without a JSON response wrapper. @@ -108,3 +113,10 @@ impl From for QueryRequest { QueryRequest::Ibc(msg) } } + +#[cfg(feature = "cosmwasm_1_3")] +impl From for QueryRequest { + fn from(msg: DistributionQuery) -> Self { + QueryRequest::Distribution(msg) + } +} diff --git a/packages/std/src/results/cosmos_msg.rs b/packages/std/src/results/cosmos_msg.rs index 2b483a59cc..68dbd4f732 100644 --- a/packages/std/src/results/cosmos_msg.rs +++ b/packages/std/src/results/cosmos_msg.rs @@ -111,6 +111,13 @@ pub enum DistributionMsg { /// The `validator_address` validator: String, }, + /// This is translated to a [[MsgFundCommunityPool](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#LL69C1-L76C2). + /// `depositor` is automatically filled with the current contract's address. + #[cfg(feature = "cosmwasm_1_3")] + FundCommunityPool { + /// The amount to spend + amount: Vec, + }, } fn binary_to_string(data: &Binary, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { @@ -463,6 +470,39 @@ mod tests { } } + #[test] + #[cfg(feature = "cosmwasm_1_3")] + fn msg_distribution_serializes_to_correct_json() { + // 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(); + assert_eq!( + String::from_utf8_lossy(&fund_json), + r#"{"fund_community_pool":{"amount":[{"denom":"feathers","amount":"200"},{"denom":"stones","amount":"200"}]}}"#, + ); + + // SetWithdrawAddress + let set_msg = DistributionMsg::SetWithdrawAddress { + address: String::from("withdrawer"), + }; + let set_json = to_binary(&set_msg).unwrap(); + assert_eq!( + String::from_utf8_lossy(&set_json), + r#"{"set_withdraw_address":{"address":"withdrawer"}}"#, + ); + + // WithdrawDelegatorRewards + let withdraw_msg = DistributionMsg::WithdrawDelegatorReward { + validator: String::from("fancyoperator"), + }; + let withdraw_json = to_binary(&withdraw_msg).unwrap(); + assert_eq!( + String::from_utf8_lossy(&withdraw_json), + r#"{"withdraw_delegator_reward":{"validator":"fancyoperator"}}"# + ); + } + #[test] fn wasm_msg_debug_decodes_binary_string_when_possible() { #[cosmwasm_schema::cw_serde] diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index 375d0c86dc..69d3281dd1 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -28,6 +28,8 @@ use crate::query::{ AllDelegationsResponse, AllValidatorsResponse, BondedDenomResponse, DelegationResponse, FullDelegation, StakingQuery, Validator, ValidatorResponse, }; +#[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::storage::MemoryStorage; @@ -443,6 +445,8 @@ pub struct MockQuerier { bank: BankQuerier, #[cfg(feature = "staking")] staking: StakingQuerier, + #[cfg(feature = "cosmwasm_1_3")] + distribution: DistributionQuerier, wasm: WasmQuerier, #[cfg(feature = "stargate")] ibc: IbcQuerier, @@ -457,6 +461,8 @@ impl MockQuerier { pub fn new(balances: &[(&str, &[Coin])]) -> Self { MockQuerier { bank: BankQuerier::new(balances), + #[cfg(feature = "cosmwasm_1_3")] + distribution: DistributionQuerier::default(), #[cfg(feature = "staking")] staking: StakingQuerier::default(), wasm: WasmQuerier::default(), @@ -484,6 +490,33 @@ impl MockQuerier { self.bank.set_denom_metadata(denom_metadata); } + #[cfg(feature = "cosmwasm_1_3")] + pub fn set_withdraw_address( + &mut self, + delegator_address: impl Into, + withdraw_address: impl Into, + ) { + self.distribution + .set_withdraw_address(delegator_address, withdraw_address); + } + + /// Sets multiple withdraw addresses. + /// + /// This allows passing multiple tuples of `(delegator_address, withdraw_address)`. + /// It does not overwrite existing entries. + #[cfg(feature = "cosmwasm_1_3")] + pub fn set_withdraw_addresses( + &mut self, + withdraw_addresses: impl IntoIterator, impl Into)>, + ) { + self.distribution.set_withdraw_addresses(withdraw_addresses); + } + + #[cfg(feature = "cosmwasm_1_3")] + pub fn clear_withdraw_addresses(&mut self) { + self.distribution.clear_withdraw_addresses(); + } + #[cfg(feature = "staking")] pub fn update_staking( &mut self, @@ -543,6 +576,10 @@ impl MockQuerier { QueryRequest::Custom(custom_query) => (*self.custom_handler)(custom_query), #[cfg(feature = "staking")] QueryRequest::Staking(staking_query) => self.staking.query(staking_query), + #[cfg(feature = "cosmwasm_1_3")] + QueryRequest::Distribution(distribution_query) => { + self.distribution.query(distribution_query) + } QueryRequest::Wasm(msg) => self.wasm.query(msg), #[cfg(feature = "stargate")] QueryRequest::Stargate { .. } => SystemResult::Err(SystemError::UnsupportedRequest { @@ -893,6 +930,62 @@ impl StakingQuerier { } } +#[cfg(feature = "cosmwasm_1_3")] +#[derive(Clone, Default)] +pub struct DistributionQuerier { + withdraw_addresses: HashMap, +} + +#[cfg(feature = "cosmwasm_1_3")] +impl DistributionQuerier { + pub fn new(withdraw_addresses: HashMap) -> Self { + DistributionQuerier { withdraw_addresses } + } + + pub fn set_withdraw_address( + &mut self, + delegator_address: impl Into, + withdraw_address: impl Into, + ) { + self.withdraw_addresses + .insert(delegator_address.into(), withdraw_address.into()); + } + + /// Sets multiple withdraw addresses. + /// + /// This allows passing multiple tuples of `(delegator_address, withdraw_address)`. + /// It does not overwrite existing entries. + pub fn set_withdraw_addresses( + &mut self, + withdraw_addresses: impl IntoIterator, impl Into)>, + ) { + for (d, w) in withdraw_addresses { + self.set_withdraw_address(d, w); + } + } + + pub fn clear_withdraw_addresses(&mut self) { + self.withdraw_addresses.clear(); + } + + pub fn query(&self, request: &DistributionQuery) -> QuerierResult { + let contract_result: ContractResult = match request { + DistributionQuery::DelegatorWithdrawAddress { delegator_address } => { + let res = DelegatorWithdrawAddressResponse { + withdraw_address: self + .withdraw_addresses + .get(delegator_address) + .unwrap_or(delegator_address) + .clone(), + }; + to_binary(&res).into() + } + }; + // system result is always ok in the mock implementation + SystemResult::Ok(contract_result) + } +} + pub fn digit_sum(input: &[u8]) -> usize { input.iter().fold(0, |sum, val| sum + (*val as usize)) } @@ -1428,6 +1521,29 @@ mod tests { ); } + #[cfg(feature = "cosmwasm_1_3")] + #[test] + fn distribution_querier_delegator_withdraw_address() { + let mut distribution = DistributionQuerier::default(); + distribution.set_withdraw_address("addr0", "withdraw0"); + + let query = DistributionQuery::DelegatorWithdrawAddress { + delegator_address: "addr0".to_string(), + }; + + let res = distribution.query(&query).unwrap().unwrap(); + let res: DelegatorWithdrawAddressResponse = from_binary(&res).unwrap(); + assert_eq!(res.withdraw_address, "withdraw0"); + + let query = DistributionQuery::DelegatorWithdrawAddress { + delegator_address: "addr1".to_string(), + }; + + let res = distribution.query(&query).unwrap().unwrap(); + let res: DelegatorWithdrawAddressResponse = from_binary(&res).unwrap(); + assert_eq!(res.withdraw_address, "addr1"); + } + #[cfg(feature = "stargate")] #[test] fn ibc_querier_channel_existing() { diff --git a/packages/std/src/testing/mod.rs b/packages/std/src/testing/mod.rs index 657aada5de..550486a0b6 100644 --- a/packages/std/src/testing/mod.rs +++ b/packages/std/src/testing/mod.rs @@ -9,6 +9,8 @@ mod shuffle; pub use assertions::assert_approx_eq_impl; +#[cfg(feature = "cosmwasm_1_3")] +pub use mock::DistributionQuerier; #[cfg(feature = "staking")] pub use mock::StakingQuerier; pub use mock::{ diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs index c0442d0ed9..3b3812307b 100644 --- a/packages/std/src/traits.rs +++ b/packages/std/src/traits.rs @@ -21,7 +21,10 @@ use crate::query::{ DelegationResponse, FullDelegation, StakingQuery, Validator, ValidatorResponse, }; #[cfg(feature = "cosmwasm_1_3")] -use crate::query::{AllDenomMetadataResponse, DenomMetadataResponse}; +use crate::query::{ + AllDenomMetadataResponse, DelegatorWithdrawAddressResponse, DenomMetadataResponse, + DistributionQuery, +}; use crate::results::{ContractResult, Empty, SystemResult}; use crate::serde::{from_binary, to_binary, to_vec}; use crate::ContractInfoResponse; @@ -38,12 +41,11 @@ pub trait Storage { /// is not great yet and might not be possible in all backends. But we're trying to get there. fn get(&self, key: &[u8]) -> Option>; - #[cfg(feature = "iterator")] /// Allows iteration over a set of key/value pairs, either forwards or backwards. /// /// The bound `start` is inclusive and `end` is exclusive. - /// /// If `start` is lexicographically greater than or equal to `end`, an empty range is described, mo matter of the order. + #[cfg(feature = "iterator")] fn range<'a>( &'a self, start: Option<&[u8]>, @@ -51,6 +53,40 @@ pub trait Storage { order: Order, ) -> Box + 'a>; + /// Allows iteration over a set of keys, either forwards or backwards. + /// + /// The bound `start` is inclusive and `end` is exclusive. + /// If `start` is lexicographically greater than or equal to `end`, an empty range is described, mo matter of the order. + /// + /// The default implementation uses [`Storage::range`] and discards the values. More efficient + /// implementations might be possible depending on the storage. + #[cfg(feature = "iterator")] + fn range_keys<'a>( + &'a self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box> + 'a> { + Box::new(self.range(start, end, order).map(|(k, _v)| k)) + } + + /// Allows iteration over a set of values, either forwards or backwards. + /// + /// The bound `start` is inclusive and `end` is exclusive. + /// If `start` is lexicographically greater than or equal to `end`, an empty range is described, mo matter of the order. + /// + /// The default implementation uses [`Storage::range`] and discards the keys. More efficient implementations + /// might be possible depending on the storage. + #[cfg(feature = "iterator")] + fn range_values<'a>( + &'a self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box> + 'a> { + Box::new(self.range(start, end, order).map(|(_k, v)| v)) + } + fn set(&mut self, key: &[u8], value: &[u8]); /// Removes a database entry at `key`. @@ -242,6 +278,19 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { Ok(res.amount) } + #[cfg(feature = "cosmwasm_1_3")] + pub fn query_delegator_withdraw_address( + &self, + delegator: impl Into, + ) -> StdResult { + let request = DistributionQuery::DelegatorWithdrawAddress { + delegator_address: delegator.into(), + } + .into(); + let res: DelegatorWithdrawAddressResponse = self.query(&request)?; + Ok(res.withdraw_address) + } + #[cfg(feature = "cosmwasm_1_3")] pub fn query_denom_metadata(&self, denom: impl Into) -> StdResult { let request = BankQuery::DenomMetadata { diff --git a/packages/storage/Cargo.toml b/packages/storage/Cargo.toml index b5dbbab45a..270906d7e9 100644 --- a/packages/storage/Cargo.toml +++ b/packages/storage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Ethan Frey "] edition = "2021" description = "CosmWasm library with useful helpers for Storage patterns" @@ -16,5 +16,5 @@ iterator = ["cosmwasm-std/iterator"] [dependencies] # Uses the path when built locally; uses the given version from crates.io when published -cosmwasm-std = { path = "../std", version = "1.2.7", default-features = false } +cosmwasm-std = { path = "../std", version = "1.3.0-rc.0", default-features = false } serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } diff --git a/packages/vm/Cargo.toml b/packages/vm/Cargo.toml index 3c36b5b33b..a27b8b27a8 100644 --- a/packages/vm/Cargo.toml +++ b/packages/vm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Ethan Frey "] edition = "2021" description = "VM bindings to run cosmwams contracts" @@ -39,11 +39,11 @@ bytes = "1.4.0" # need a higher version than the one required by Wasmer for the clru = "0.4.0" 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.2.7", default-features = false } -cosmwasm-crypto = { path = "../crypto", version = "1.2.7" } +cosmwasm-std = { path = "../std", version = "1.3.0-rc.0", default-features = false } +cosmwasm-crypto = { path = "../crypto", version = "1.3.0-rc.0" } derivative = "2" hex = "0.4" -parity-wasm = "0.45" +parity-wasm = { version = "0.45", features = ["sign_ext"] } schemars = "0.8.3" serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } serde_json = "1.0.40" diff --git a/packages/vm/README.md b/packages/vm/README.md index 7fc48e561f..a50ae52130 100644 --- a/packages/vm/README.md +++ b/packages/vm/README.md @@ -75,6 +75,17 @@ docker run --rm -v "$(pwd)":/code \ && cp artifacts/floaty.wasm packages/vm/testdata/floaty_1.2.wasm ``` +The `cyberpunk_rust170.wasm` for +https://github.com/CosmWasm/cosmwasm/issues/1727 is built as follows +(non-reproducible): + +```sh +cd contracts/cyberpunk +rm -r target +RUSTFLAGS='-C link-arg=-s' cargo build --release --lib --target wasm32-unknown-unknown --locked +cp target/wasm32-unknown-unknown/release/cyberpunk.wasm ../../packages/vm/testdata/cyberpunk_rust170.wasm +``` + ## Testing By default, this repository is built and tested with the singlepass backend. You diff --git a/packages/vm/src/cache.rs b/packages/vm/src/cache.rs index 38772a2728..872ec5f19f 100644 --- a/packages/vm/src/cache.rs +++ b/packages/vm/src/cache.rs @@ -280,6 +280,7 @@ where // Re-compile from original Wasm bytecode let code = self.load_wasm_with_path(&cache.wasm_path, checksum)?; + cache.stats.misses = cache.stats.misses.saturating_add(1); let (engine, module) = compile(&code, &[])?; // Store into the fs cache too let module_size = cache.fs_cache.store(checksum, &module)?; @@ -470,7 +471,7 @@ mod tests { use crate::errors::VmError; use crate::testing::{mock_backend, mock_env, mock_info, MockApi, MockQuerier, MockStorage}; use cosmwasm_std::{coins, Empty}; - use std::fs::{create_dir_all, OpenOptions}; + use std::fs::{create_dir_all, remove_dir_all, OpenOptions}; use std::io::Write; use tempfile::TempDir; @@ -794,6 +795,36 @@ mod tests { assert_eq!(cache.stats().misses, 0); } + #[test] + fn get_instance_recompiles_module() { + 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(); + + // The first get_instance recompiles the Wasm (miss) + let backend = mock_backend(&[]); + let _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); + + // The second get_instance finds the module in cache (hit) + let backend = mock_backend(&[]); + let _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, 1); + assert_eq!(cache.stats().hits_fs_cache, 0); + assert_eq!(cache.stats().misses, 1); + } + #[test] fn call_instantiate_on_cached_contract() { let cache = unsafe { Cache::new(make_testing_options()).unwrap() }; @@ -1238,6 +1269,34 @@ mod tests { cache.unpin(&non_id).unwrap(); } + #[test] + fn pin_recompiles_module() { + let options = make_testing_options(); + let cache: 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(); + + // Pin misses, forcing a re-compile of the module + cache.pin(&checksum).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); + + // After the compilation in pin, the module can be used from pinned memory cache + let backend = mock_backend(&[]); + let _ = 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] fn loading_without_extension_works() { let tmp_dir = TempDir::new().unwrap(); diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index a5284a6e2e..0e399e086a 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -260,6 +260,7 @@ mod tests { static CONTRACT_0_14: &[u8] = include_bytes!("../testdata/hackatom_0.14.wasm"); static CONTRACT_0_15: &[u8] = include_bytes!("../testdata/hackatom_0.15.wasm"); static CONTRACT: &[u8] = include_bytes!("../testdata/hackatom.wasm"); + static CONTRACT_RUST_170: &[u8] = include_bytes!("../testdata/cyberpunk_rust170.wasm"); fn default_capabilities() -> HashSet { ["staking".to_string()].into_iter().collect() @@ -271,6 +272,12 @@ mod tests { check_wasm(CONTRACT, &default_capabilities()).unwrap(); } + #[test] + fn check_wasm_allows_sign_ext() { + // See https://github.com/CosmWasm/cosmwasm/issues/1727 + check_wasm(CONTRACT_RUST_170, &default_capabilities()).unwrap(); + } + #[test] fn check_wasm_old_contract() { match check_wasm(CONTRACT_0_15, &default_capabilities()) { diff --git a/packages/vm/src/modules/file_system_cache.rs b/packages/vm/src/modules/file_system_cache.rs index f2298c8f6e..d3c9679417 100644 --- a/packages/vm/src/modules/file_system_cache.rs +++ b/packages/vm/src/modules/file_system_cache.rs @@ -1,8 +1,7 @@ use std::fs; use std::hash::Hash; use std::io; -use std::path::Path; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use thiserror::Error; use wasmer::{AsEngineRef, DeserializeError, Module, Target}; @@ -45,7 +44,9 @@ use crate::modules::current_wasmer_module_version; /// A change in memory layout of some types in Rust [std] caused /// [issues with module deserialization](https://github.com/CosmWasm/wasmvm/issues/426). /// To work around this, the version was bumped to "v5" here to invalidate these corrupt caches. -const MODULE_SERIALIZATION_VERSION: &str = "v5"; +/// - **v6**:
+/// Version for cosmwasm_vm 1.3+ which adds a sub-folder with the target identier for the modules. +const MODULE_SERIALIZATION_VERSION: &str = "v6"; /// Representation of a directory that contains compiled Wasm artifacts. pub struct FileSystemCache { @@ -279,7 +280,7 @@ mod tests { cache.store(&checksum, &module).unwrap(); let mut globber = glob::glob(&format!( - "{}/v5-wasmer4/**/{}", + "{}/v6-wasmer4/**/{}", tmp_dir.path().to_string_lossy(), checksum )) @@ -357,9 +358,9 @@ mod tests { assert_eq!( p.as_os_str(), if cfg!(windows) { - "modules\\v5-wasmer17\\x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" + "modules\\v6-wasmer17\\x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" } else { - "modules/v5-wasmer17/x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" + "modules/v6-wasmer17/x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" } ); } diff --git a/packages/vm/src/wasm_backend/gatekeeper.rs b/packages/vm/src/wasm_backend/gatekeeper.rs index 3062e3950b..bc9c770c94 100644 --- a/packages/vm/src/wasm_backend/gatekeeper.rs +++ b/packages/vm/src/wasm_backend/gatekeeper.rs @@ -198,13 +198,17 @@ impl FunctionMiddleware for FunctionGatekeeper { | Operator::I64Rotl | Operator::I64Rotr | Operator::I32WrapI64 + // Those are part of the MVP + // https://github.com/bytecodealliance/wasm-tools/blob/wasmparser-0.107.0/crates/wasmparser/src/lib.rs#L287-L288 + | Operator::I64ExtendI32S + | Operator::I64ExtendI32U + // Sign-extension + // https://github.com/bytecodealliance/wasm-tools/blob/wasmparser-0.107.0/crates/wasmparser/src/lib.rs#L307-L311 | Operator::I32Extend8S | Operator::I32Extend16S | Operator::I64Extend8S | Operator::I64Extend16S - | Operator::I64ExtendI32S - | Operator::I64Extend32S - | Operator::I64ExtendI32U => { + | Operator::I64Extend32S => { state.push_operator(operator); Ok(()) } diff --git a/packages/vm/testdata/cyberpunk_rust170.wasm b/packages/vm/testdata/cyberpunk_rust170.wasm new file mode 100755 index 0000000000..3bd0b471c0 Binary files /dev/null and b/packages/vm/testdata/cyberpunk_rust170.wasm differ