diff --git a/CHANGELOG-pre1.0.0.md b/CHANGELOG-pre1.0.0.md new file mode 100644 index 000000000..36b2a3bb6 --- /dev/null +++ b/CHANGELOG-pre1.0.0.md @@ -0,0 +1,1363 @@ +# CHANGELOG of versions before 1.0.0 + +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). + +## [1.0.0-rc.0] - 2022-05-05 + +### Fixed + +- cosmwasm-std: Upgrade `serde-json-wasm` to 0.4.0 to fix u128/i128 + serialization of `to_vec`/`to_binary` in some cases ([#1297]). + +[#1297]: https://github.com/CosmWasm/cosmwasm/pull/1297 + +### Added + +- cosmwasm-std: Implement `checked_multiply_ratio` for + `Uint64`/`Uint128`/`Uint256` +- cosmwasm-std: Implement `checked_from_ratio` for `Decimal`/`Decimal256` +- cosmwasm-std: Implement `Div`/`DivAssign` for `Decimal`/`Decimal256`. +- cosmwasm-vm: Add feature `allow_interface_version_7` to run CosmWasm 0.16 + contracts in modern hosts. Be careful if you consider using this! + +### Changed + +- all: Updated Rust edition to 2021 +- cosmwasm-std: Rename `SubMsgExecutionResponse` to `SubMsgResponse`. +- cosmwasm-crypto: Update dependency `k256` to ^0.10.4. +- cosmwasm-vm: `BackendError` was changed to `non_exhaustive` for future + extension; `BackendError` now implements `PartialEq` for easier test code; the + `msg` in `BackendError::Unknown` became non-optional because it was always + set; the argument in `BackendError::unknown`/`::user_err` was change to + `impl Into` to avoid unnecessary clones. + +### Deprecated + +- cosmwasm-std: `SubMsgExecutionResponse` is deprecated in favor of the new + `SubMsgResponse`. + +### Removed + +- cosmwasm-std: Remove `Pair` which was previously deprecated. Use `Record` + instead. ([#1282]) + +[#1282]: https://github.com/CosmWasm/cosmwasm/issues/1282 + +## [1.0.0-beta8] - 2022-04-06 + +### Added + +- cosmwasm-std: Implement `MulAssign` for `Decimal`/`Decimal256`. +- cosmwasm-std: Implement `is_zero`/`atomics`/`decimal_places` as const for Uint + and Decimal types. +- cosmwasm-std: Implement `new` and `raw` const constructors for + `Decimal`/`Decimal256`. + +### Changed + +- all: Drop support for Rust versions lower than 1.56.1. +- cosmwasm-std: `MockQuerier` now supports adding custom behaviour for handling + Wasm queries via `MockQuerier::update_wasm` ([#1050]). + +[#1050]: https://github.com/CosmWasm/cosmwasm/pull/1050 + +### Fixed + +- cosmwasm-std: `Api::addr_validate` now requires inputs to be normalized. +- cosmwasm-vm: The `addr_validate` import now requires inputs to be normalized. + +## [1.0.0-beta7] - 2022-03-22 + +### Added + +- cosmwasm-std: Implement `Decimal{,256}::checked_mul` and + `Decimal{,256}::checked_pow`. +- cosmwasm-std: Implement `Sub`/`SubAssign` for `Uint64`. +- cosmwasm-std: Implement `Mul`/`MulAssign` for `Uint64`. +- cosmwasm-std: Implement `RemAssign` for + `Uint64`/`Uint128`/`Uint256`/`Uint512`. +- cosmwasm-std: Implement `pow`/`checked_pow` for `Uint64`/`Uint128`/`Uint512`. +- cosmwasm-std: Implement `SubAssign`/`AddAssign` for `Decimal`/`Decimal256`. +- cosmwasm-crypto: Upgrade ed25519-zebra to version 3. + +### Changed + +- cosmwasm-vm: Upgrade Wasmer to 2.2.1. + +## [1.0.0-beta6] - 2022-03-07 + +### Added + +- cosmwasm-std: Implement `ops::Rem` for `Uint{64,128,256,512}`. + +### Changed + +- cosmwasm-std: Change type of `Reply::result` from `ContractResult` to the new + `SubMsgResult`. Both types are equal when serialized but `ContractResult` is + documented to be the result of a contract execution, which is not the case + here. ([#1232]) +- cosmwasm-vm: Upgrade Wasmer to 2.2.0 and bump `MODULE_SERIALIZATION_VERSION` + to "v3-wasmer1". ([#1224]) + +[#1224]: https://github.com/CosmWasm/cosmwasm/pull/1224 +[#1232]: https://github.com/CosmWasm/cosmwasm/pull/1232 + +## [1.0.0-beta5] - 2022-02-08 + +### Changed + +- all: Drop support for Rust versions lower than 1.54.0. +- cosmwasm-std: The `Debug` implementation of `Binary` now produces a hex string + instead of a list of bytes ([#1199]). +- cosmwasm-std: Pin uint version to 0.9.1 in order to maintain a reasonably low + MSRV. +- cosmwasm-std: Add missing `Isqrt` export ([#1214]). + +[#1199]: https://github.com/CosmWasm/cosmwasm/issues/1199 +[#1214]: https://github.com/CosmWasm/cosmwasm/issues/1214 + +### Fixed + +- cosmwasm-vm: Fix `AddAssign` implementation of `GasInfo`. +- cosmwasm-vm: Bump `MODULE_SERIALIZATION_VERSION` to "v2" because the module + serialization format changed between Wasmer 2.0.0 and 2.1.x. + +## [1.0.0-beta4] - 2021-12-23 + +### Changed + +- cosmwasm-vm: `wasmer` version bumped `2.1.0 -> 2.1.1` + +### Fixed + +- cosmwasm-vm: Remove system-dependent stacktrace from `VmError::RuntimeErr` + (fixes CWA-2021-003). + +## [1.0.0-beta3] + +### Added + +- cosmwasm-std: New const methods `Uint64::to_be_bytes`/`::to_le_bytes`. +- cosmwasm-vm: The check_contracts tool now has a `--supported-features` option + that defaults to "iterator,staking,stargate". +- cosmwasm-vm: The default `singlepass` compiler is now supported on 64-bit + Windows. +- cosmwasm-std: Add missing `DivideByZeroError` export. +- cosmwasm-std: Implement `std::iter::Sum` for `Decimal` and `Decimal256`. + +### Changed + +- all: Drop support for Rust versions lower than 1.53.0. +- cosmwasm-std: The balance argument from `mock_dependencies` was removed. + Remove `&[]` if you don't need a contract balance or use the new + `mock_dependencies_with_balance` if you need a balance. +- cosmwasm-vm: Unlock cache mutex before module instantiation. +- cosmwasm-vm: `wasmer` version bumped `2.0.0 -> 2.1.0` + +### Removed + +- cosmwasm-std: Remove the macros `create_entry_points` and + `create_entry_points_with_migration` in favour of the new, more flexible entry + point system introduced in CosmWasm 0.14. + +## [1.0.0-beta] - 2021-10-11 + +### Added + +- cosmwasm-std: Add new `WasmQuery::ContractInfo` variant to get metadata about + the contract, like `code_id` and `admin`. +- cosmwasm-std: New field `Env::transaction` containing info of the transaction + the contract call was executed in. +- cosmwasm-std: Implement `ops::Mul` for `Decimal` and `Decimal256`. +- cosmwasm-std: New const methods `Uint128::to_be_bytes`/`::to_le_bytes`. +- cosmwasm-std: New const conversion methods `Uint256::from_uint128` and + `Uint512::from_uint256`. +- cosmwasm-std: New getters `Decimal{,256}::atomics()` and + `Decimal{,256}::decimal_places()`. +- cosmwasm-std: New constructors `Decimal{,256}::from_atomics`. +- cosmwasm-std: New `Uint128::checked_pow`. +- cosmwasm-std: New macros `ensure!`, `ensure_eq!` and `ensure_ne!` allow + requirement checking that return errors instead of panicking ([#1103]). + +[#1103]: https://github.com/CosmWasm/cosmwasm/issues/1103 + +### Changed + +- cosmwasm-std: Make `iterator` a required feature if the `iterator` feature + flag is set (enabled by default). +- cosmwasm-vm: Increase `MAX_LENGTH_HUMAN_ADDRESS` from 90 to 256 in order to + support longer address formats than bech32. +- cosmwasm-std: Make `CustomQuery` a subtrait of `Clone`, i.e. types that + implement `CustomQuery` need to be `Clone`able. +- cosmwasm-std: Add generic for custom query type to `QuerierWrapper`, `Deps`, + `DepsMut` and `OwnedDeps`. Merge `QuerierWrapper::custom_query` into the now + fully typed `QuerierWrapper::query`. +- cosmwasm-std: Add generic type `Q` for the custom query request type to + `do_instantiate`, `do_execute`, `do_migrate`, `do_sudo`, `do_reply`, + `do_query`, `ibc_channel_open`, `ibc_channel_connect`, `ibc_channel_close`, + `ibc_packet_receive`, `ibc_packet_ack` and `ibc_packet_timeout`. +- cosmwasm-std: In `Decimal` change `Fraction` to `Fraction`, + such that `Decimal::numerator` and `::denominator` now return `Uint128`. +- cosmwasm-std: Make methods `Uint256::to_be_bytes`/`::to_le_bytes` const. +- cosmwasm-std: Make methods `Uint512::to_be_bytes`/`::to_le_bytes` const. +- cosmwasm-std: Make method `Uint512::from_le_bytes` const. +- cosmwasm-std: Rename `Pair` to `Record`. `Pair` is now an alias for `Record` + and deprecated. ([#1108]) +- cosmwasm-vm: Bump required marker export `interface_version_7` to + `interface_version_8`. +- cosmwasm-vm: Increase cost per Wasm operation from 1 to 150_000 and adjust + crypto API gas cost based on the target of 1 Teragas per millisecond. +- cosmwasm-std: Deprecate the macros `create_entry_points` and + `create_entry_points_with_migration` in favour of the new, more flexible entry + point system introduced in CosmWasm 0.14. + +### Removed + +- cosmwasm-std: Remove `HumanAddr` (deprecated since 0.14). Use `String` + instead. +- cosmwasm-std: Remove `KV` (deprecated since 0.14). Use `Pair` instead. + +[#1108]: https://github.com/CosmWasm/cosmwasm/issues/1108 + +## [0.16.2] - 2021-09-07 + +### Added + +- cosmwasm-std: Implement `Mul` and `MulAssign` for `Uint128`. +- cosmwasm-std: Implement `FromStr` for `Uint128`, `Uint256`, and `Uint512`. +- cosmwasm-std: Make `Uint256::from_le_bytes`, `::from_be_bytes` and `::new` + const. +- cosmwasm-std: Added the `Decimal256` type with 18 decimal places. + +### Changed + +- cosmwasm-std: Implement `Decimal::from_ratio` using full uint128 + multiplication to support a wider range of input values. +- cosmwasm-std: `Decimal::from_ratio` now accepts any types that implement + `Into` rather than `Into`. +- cosmwasm-crypto: Update dependency `k256` to ^0.9.6. +- cosmwasm-std: Add enum cases `Shl` to `OverflowOperation` (breaking; [#1071]). + +[#1071]: https://github.com/CosmWasm/cosmwasm/pull/1071 + +### Fixed + +- cosmwasm-std: Fixed a bug where `Uint*` types wouldn't handle formatting + options when formatted with `std::fmt::Display`. + +## [0.16.1] - 2021-08-31 + +### Added + +- cosmwasm-std: Added `From` and `From<&Addr>` conversions for + `Cow`. +- cosmwasm-std: Added new `Uint256` and `Uint512` types. +- cosmwasm-std: Added implementations of `Isqrt` (integer square root) for + `Uint64`, `Uint128`, `Uint256`, and `Uint512`. +- cosmwasm-std: Exposed `Uint{64, 128, 256}::full_mul` for full multiplication + that cannot overflow. + +### Changed + +- cosmwasm-std: In `ExternalApi::addr_validate` and `::addr_canonicalize` do not + send too long inputs to VM to avoid terminating contract execution. Errors are + returned instead now. +- cosmwasm-std: Add enum cases `Shr` to `OverflowOperation` (breaking; [#1059]). + +[#1059]: https://github.com/CosmWasm/cosmwasm/pull/1059 + +## [0.16.0] - 2021-08-05 + +### Added + +- cosmwasm-std: Added the `IbcChannelOpenMsg`, `IbcChannelConnectMsg`, + `IbcChannelCloseMsg`, `IbcPacketReceiveMsg`, `IbcPacketAckMsg`, and + `IbcPacketTimeoutMsg` types for use with corresponding IBC entrypoints. +- cosmwasm-std::testing: New mocking helpers for IBC channel msg types: + `mock_ibc_channel_open_init`, `mock_ibc_channel_open_try`, + `mock_ibc_channel_connect_ack`, `mock_ibc_channel_connect_confirm`, + `mock_ibc_channel_close_init`, `mock_ibc_channel_close_confirm`. +- cosmwasm-std::testing: Added `mock_ibc_packet_timeout` since + `mock_ibc_packet_ack` is no longer usable for creating mock data for + `ibc_packet_timeout`. +- cosmwasm-std: New `Attribute::new` constructor that does the same thing as + `attr`. +- cosmwasm-std::testing: Added `mock_wasm_attr` when you really need to create + an `Attribute` with a key starting with `_` in test code. +- cosmwasm-std: Renamed `IBCAcknowledgementWithPacket` -> `IbcPacketAckMsg` to + remove an unneeded level of indirection. +- cosmwasm-std: Added `Event::add_attributes` for bulk adding attributes to an + `Event` struct. +- cosmwasm-std: Added `Addr::into_string` for explicit conversion + +### Changed + +- cosmwasm-vm: The `Checksum::to_hex` function signature was changed from + `to_hex(&self) -> String` to `to_hex(self) -> String`. +- cosmwasm-std: The `attr` function now accepts types that implement + `Into` rather than `ToString`. +- cosmwasm-std, cosmwasm-vm, cosmwasm-storage: The `iterator` feature is now + enabled by default. +- cosmwasm-std: Make `MockApi::canonical_length` private. +- cosmwasm-vm: Make `MockApi::canonical_length` private. +- cosmwasm-vm: Bump required marker export `interface_version_6` to + `interface_version_7`. +- cosmwasm-std, cosmwasm-vm: Entrypoints `ibc_channel_open`, + `ibc_channel_connect`, `ibc_channel_close`, `ibc_packet_receive`, + `ibc_packet_ack`, `ibc_packet_timeout` now each accept a corresponding `Msg` + value that wraps around channels, packets and acknowledgements. +- cosmwasm-std/cosmwasm-vm: Increase canonical address lengths up to 64 bytes. +- cosmwasm-std/cosmwasm-vm: In `MockApi`, increase max length of supported human + addresses from 24 bytes to 54 bytes by using a longer canonical + representation. This allows you to insert typical bech32 addresses in tests. + ([#995]) +- cosmwasm-std::testing: `mock_ibc_packet_recv` function now returns an + `IbcPacketReceiveMsg`, `mock_ibc_packet_ack` requires an acknowledgement to be + passed and returns an `IbcPacketAckMsg`. +- cosmwasm-std: `IbcBasicResponse` and `IbcReceiveResponse` now both support + custom events via the `events` field. +- cosmwasm-std: `attr` (and `Attribute::new`) will now panic in debug builds if + the attribute's key starts with an underscore. These names are reserved and + could cause problems further down the line. +- cosmwasm-std: `Response`, `IbcBasicResponse` and `IbcReceiveResponse` can no + longer be constructed using struct literals. Use constructors like + `Response::new` to construct empty structs and appropriate builder-style + methods to set fields (`response.add_message`, `response.set_data`, etc). +- cosmwasm-std: `Event`, `IbcChannel`, `IbcPacket`, `IbcAcknowledgement` have + been marked `non_exhaustive` (can't be constructed using a struct literal by + downstream code). +- cosmwasm-std: `Event::attr` has been renamed to `Event::add_attribute` for + consistency with other types like `Response`. +- cosmwasm-vm: `Instance::required_features` changed from a property to a getter + method. +- cosmwasm-vm: Add `required_features` field to `AnalysisReport` which is + returned by `Cache::analyze`. +- cosmwasm-vm: The VM now checks that exactly one `interface_version_*` marker + export is set. For `interface_version_5` and `interface_version_6` (CosmWasm + 0.14–0.15) more specific error messages were added. + +[#995]: https://github.com/CosmWasm/cosmwasm/pull/995 + +### Removed + +- cosmwasm-std::testing: `mock_ibc_channel` is now private. Use + `mock_ibc_channel_open`, `mock_ibc_channel_connect`, or + `mock_ibc_channel_close` instead. + +## [0.15.2] - 2021-07-21 + +### Fixed + +- cosmwasm-std: Export `VoteOption` as a top-level type. + +## [0.15.1] - 2021-07-20 + +### Fixed + +- cosmwasm-std: Export `GovMsg` as a top-level type of the crate. + +## [0.15.0] - 2021-06-24 + +### Added + +- cosmwasm-std: Implement `Sub` and `SubAssign` for `Uint128` +- cosmwasm-std: Implement custom events for contract execution results +- cosmwasm-std: Add `CosmosMsg::Gov` for voting on governance proposals. +- cosmwasm-storage: Implement `Storage` for `PrefixedStorage` and + `ReadonlyPrefixedStorage`. NOTE: Calling `set` or `remove` on + `ReadonlyPrefixedStorage` will panic! + +### Removed + +- cosmwasm-std: Make `Uint128` inner field private ([#905]) +- cosmwasm-std: Remove `Context` - deprecated in previous release +- cosmwasm-std: Remove `HandleResponse`, `InitResponse`, and `MigrateResponse` - + deprecated in previous release +- cosmwasm-crypto: Remove `ed25519::MESSAGE_MAX_LEN`, `ed25519::BATCH_MAX_LEN` + and message length verification as this should not be a concern of + `cosmwasm-crypto`. + +[#905]: https://github.com/CosmWasm/cosmwasm/issues/905 + +### Changed + +- cosmwasm-std: Rename the `send` function parameter to `funds` in `WasmMsg` for + consistency with the wasmd message types. +- cosmwasm-vm: Increase read limit of contract execution results from 100,000 + bytes to 64 MiB. JSON deserializers should have their own limit to protect + against large deserializations. +- cosmwasm-vm: Create `VmError::DeserializationLimitExceeded`; Add limit + argument to `from_slice`; Increase deserialization limit of contract execution + results from 100,000 bytes to 256 KiB. This probably only affects internal + testing as well as integration tests of smart contracts. +- cosmwasm-vm: More accurate error messages for op codes related to bulk memory + operations, reference types, SIMD and the Threads extension. +- cosmwasm-vm: Update `wasmer` to `2.0.0` +- cosmwasm-vm: ED25519 message length and batch length limits are now hardcoded + in `cosmwasm-vm` itself instead of being imported from `cosmwasm-crypto`. +- cosmwasm-vm: Filesystem storage layout now distinguishes clearly between state + and cache. +- cosmwasm-std: Add enum case `ReplyOn::Never`; Remove default implementation of + `ReplyOn` as there is no natural default case anymore ([#961]). +- cosmwasm-std: Merge `messages` and `submessages` into one list, using + `ReplyOn::Never` to model the "fire and forget" semantics ([#961]). +- cosmwasm-std: Add `SubMsg` constructors: `::new()`, `::reply_on_error()`, + `::reply_on_success()`, `::reply_always()`; Add `with_gas_limit` to add a gas + limit to any those constructors ([#961]). +- cosmwasm-std: Change `Event`'s constructor - it no longer takes a vector of + attributes and instead constructs an empty one +- cosmwasm-std: Rename `Event.kind` to `Event.ty`. +- cosmwasm-std: Rename `SubcallResponse` to `SubMsgExecutionResponse`. +- contracts: Rename `ReflectSubCall` to `ReflectSubMsg` and `SubCallResult` to + `SubCallMsg` in the `reflect` contract. +- cosmwasm-std: Rename the `subcall` module to `submessages`. +- cosmwasm-vm: Bump required marker export `cosmwasm_vm_version_5` to + `interface_version_6`. +- cosmwasm-std: `IbcAcknowledgement` is renamed to + `IbcAcknowledgementWithPacket` as it contains both data elements. ([#975]) +- cosmwasm-std: `IbcAcknowledgementWithPacket.acknowledgement` is no longer + simply `Binary`, but a new `IbcAcknowledgement` structure, which contains one + field - `data: Binary`. This change was made to allow us to handle future + changes to IBC in a non-contract-breaking way. ([#975]) + +[#961]: https://github.com/CosmWasm/cosmwasm/pull/961 +[#975]: https://github.com/CosmWasm/cosmwasm/pull/975 + +### Fixed + +- comswasm-vm: Whitelisted the `i64.extend32_s` operation. + +## [0.14.1] - 2021-06-14 + +### Added + +- cosmwasm-std: Add `Timestamp::minus_seconds` and `::minus_nanos`. +- cosmwasm-std: Add `Addr::as_bytes` +- cosmwasm-std: Implement `std::ops::Sub` for `math::Decimal` +- cosmwasm-std: Add `Timestamp::seconds` and `Timestamp::subsec_nanos`. +- cosmwasm-std: Implement division for `Decimal / Uint128` +- cosmwasm-std: Add `math::Decimal::sqrt` + +### Fixed + +- cosmwasm-std: Fix `Uint64::multiply_ratio` and `Uint128::multiply_ratio` so + that internal multiplication cannot cause an unnecessary overflow. ([#920]) + +[#920]: https://github.com/CosmWasm/cosmwasm/issues/920 + +## [0.14.0] - 2021-05-03 + +### Added + +- cosmwasm-crypto: Add `ed25519_batch_verify`, EdDSA ed25519 batch signature + verification scheme for Tendermint signatures and public keys formats. + ([#788]) +- cosmwasm-crypto: Add `ed25519_verify`, EdDSA ed25519 signature verification + scheme for Tendermint signature and public key formats. ([#771]) +- cosmwasm-crypto: New crypto-related crate. Add `secp256k1_verify`, ECDSA + secp256k1 signature verification scheme for Cosmos signature and public key + formats. ([#780]) +- cosmwasm-vm: Add PinnedMemoryCache. ([#696]) +- cosmwasm-vm: The new `Cache::analyze` provides a static analyzis of the Wasm + bytecode. This is used to tell the caller if the contract exposes IBC entry + points. ([#736]) +- cosmwasm-vm: Added new `stargate` feature flag to enable new stargate and ibc + features ([#692], [#716]) +- cosmwasm-vm: (requires `stargate`) call into 6 new ibc entry points if exposed + by contract ([#692], [#716]) +- cosmwasm-std: Added new `stargate` feature flag to enable new stargate and ibc + features ([#692], [#706]) +- cosmwasm-std: (requires `stargate`) Added new `CosmosMsg::Stargate` message + type to dispatch protobuf-encoded message (contract must know proto schema) + ([#706]) +- cosmwasm-std: (requires `stargate`) Added new `QueryRequest::Stargate` message + type to dispatch protobuf-encoded queries (contract must know proto schema for + request and response) ([#706]) +- cosmwasm-std: (requires `stargate`) Added new `CosmosMsg::Ibc(IbcMsg)` message + type to use ibctransfer app or send raw ics packets (if contract has ibc entry + points) ([#692], [#710]) +- cosmwasm-std: Add mutable helper methods to `InitResponse`, `MigrateResponse` + and `HandleResponse` which make `Context` obsolete. +- contracts: added new `ibc-reflect` contract that receives channels and assigns + each an account to redispatch. Similar idea to ICS27/Interchain Accounts (but + different implementation) ([#692], [#711], [#714]) +- cosmwasm-std: Added new `WasmMsg::Migrate` variant that allows one contract + (eg. multisig) be the admin and migrate another contract ([#768]) +- cosmwasm-std: Added optional `system` entry point that can only be called by + native (blockchain) modules to expose admin functionality if desired. ([#793]) +- cosmwasm-std: Add extra field `submessages` to `Response`, such that you can + get a callback from these messages after their execution (success or failure). + ([#796]) +- cosmwasm-std: Added `reply` entry point that will receive all callbacks from + submessages dispatched by this contract. This is only required if contract + returns "submessages" (above). ([#796]) +- cosmwasm-std: Implement `From for String`, `From for u128` + as well as `From for Uint128`. +- cosmwasm-std: Create new address type `Addr`. This is human readable (like + `HumanAddr`) but is immutable and always contains a valid address ([#802]). +- cosmwasm-vm: Add import `addr_validate` ([#802]). +- cosmwasm-std: Add `BankMsg::Burn` variant when you want the tokens to + disappear ([#860]) +- cosmwasm-std: Create `Fraction` trait to represent a fraction `p`/`q` with + integers `p` and `q`. `Decimal` now implements `Fraction`, which + provides public getters `::numerator()` and `::denominator()`. +- cosmwasm-std: Add `Decimal::inv` that returns `1/d` for decimal `d`. +- cosmwasm-vm: Add `Cache::metrics` to expose internal data for monitoring + purposes ([#763]). +- cosmwasm-std: Implement `PartialOrd` and `Ord` for `Binary` using the same + lexicographical ordering as implemented by `Vec`. +- cosmwasm-std: Implement `PartialOrd` and `Ord` for `Addr` using the same + lexicographical ordering as implemented by `String`. +- cosmwasm-std: Added new `WasmMsg::UpdateAdmin` variant that allows an admin + contract (eg. multisig) to set another admin ([#900]) +- cosmwasm-std: Added new `WasmMsg::ClearAdmin` variant that allows an admin + contract (eg. multisig) to clear the admin, to prevent future migrations + ([#900]) +- cosmwasm-std: Implement `Display for Coin` ([#901]). +- cosmwasm-std: Create `Uint64` analogously to `Uint128` with string + serialization allowing the use of the full uint64 range in JSON clients that + use float numbers, such as JavaScript and jq. +- cosmwasm-std: Create const functions `Uint64::new` and `Uint128::new` to + create instances in a const context. + +[#692]: https://github.com/CosmWasm/cosmwasm/issues/692 +[#706]: https://github.com/CosmWasm/cosmwasm/pull/706 +[#710]: https://github.com/CosmWasm/cosmwasm/pull/710 +[#711]: https://github.com/CosmWasm/cosmwasm/pull/711 +[#714]: https://github.com/CosmWasm/cosmwasm/pull/714 +[#716]: https://github.com/CosmWasm/cosmwasm/pull/716 +[#763]: https://github.com/CosmWasm/cosmwasm/issues/763 +[#768]: https://github.com/CosmWasm/cosmwasm/pull/768 +[#793]: https://github.com/CosmWasm/cosmwasm/pull/793 +[#796]: https://github.com/CosmWasm/cosmwasm/pull/796 +[#802]: https://github.com/CosmWasm/cosmwasm/pull/802 +[#860]: https://github.com/CosmWasm/cosmwasm/pull/860 +[#900]: https://github.com/CosmWasm/cosmwasm/pull/900 +[#901]: https://github.com/CosmWasm/cosmwasm/pull/901 + +### Changed + +- contracts: Rename `HandleMsg` to `ExecuteMsg`. +- all: Rename `handle` entry point to `execute`. +- all: Rename `init` entry point to `instantiate`. +- all: Rename `system` entry point to `sudo`. +- all: Drop support for Rust versions lower than 1.51.0. +- all: The `query` and `execute` entry points are now optional. It is still + highly recommended to implement and expose them in almost any use case though. +- all: Change the encoding of the key/value region of the `db_next` import to a + more generic encoding that supports an arbitrary number of sections. This + encoding can then be reused for other multi value regions. +- all: Remove the `info: MessageInfo` argument from the `migrate` entry point + ([#690]). +- cosmwasm-std: Remove `from_address` from `BankMsg::Send`, as it always sends + from the contract address, and this is consistent with other `CosmosMsg` + variants. +- cosmwasm-std: Remove the previously deprecated `InitResult`, `HandleResult`, + `MigrateResult` and `QueryResult` in order to make error type explicit and + encourage migration to custom errors. +- cosmwasm-std: Add a `data` field to `InitResponse` the same way as in + `MigrateResponse` and `HandleResponse`. +- cosmwasm-std: Rename `MessageInfo::sent_funds` to `MessageInfo::funds`. +- cosmwasm-std: Merge response types `InitResponse`, `HandleResponse` and + `MigrateResponse` into the new `Response`. +- cosmwasm-std: Remove `Default` implementation from `HumanAddr`, + `CanonicalAddr`, `ContractInfo`, `MessageInfo`, `BlockInfo` and `Env`. If you + need one of those, you're probably doing something wrong. +- cosmwasm-std: Make `label` in `WasmMsg::Instantiate` non-optional to better + match the Go/database format. +- cosmwasm-std: Add new field `admin` to `WasmMsg::Instantiate` to fully support + `MsgInstantiateContract` from `x/wasm` ([#861]). +- cosmwasm-std: `Binary::to_array` is now generic over the array length instead + of the output type. As a consequence the obsolete type `ByteArray` was + removed. The array length is not restricted to 0-64 anymore. +- cosmwasm-std: Use const generics to implement `From<&[u8; LENGTH]> for Binary` + and `From<[u8; LENGTH]> for Binary`, such that the array length is not + restricted to 0-64 anymore. +- cosmwasm-vm: Avoid serialization of Modules in `InMemoryCache`, for + performance. Also, remove `memory_limit` from `InstanceOptions`, and define it + instead at `Cache` level (same memory limit for all cached instances). + ([#697]) +- cosmwasm-std: Rename type `KV` to `Pair` in order to comply to naming + convention as enforced by clippy rule `upper_case_acronyms` from Rust 1.51.0 + on. +- cosmwasm-std: `ContractInfo::address` and `MessageInfo::sender` are now of + type `Addr`. The value of those fields is created by the host and thus valid. +- cosmwasm-vm: Bump required marker export `cosmwasm_vm_version_4` to + `interface_version_5`. +- cosmwasm-vm: Rename trait `Api` to `BackendApi` to better express this is the + API provided by the VM's backend (i.e. the blockchain). +- cosmwasm-vm: Rename imports to `addr_canonicalize` and `addr_humanize` + ([#802]). +- cosmwasm-vm: Replace types `HumanAddr`/`CanonicalAddr` with + `&str`/`String`/`&[u8]`/`Vec` in the methods of `BackendApi`. The address + types belong in the contract development and the backend operates on raw + strings and binary anyways. +- contracts: `reflect` contract requires `stargate` feature and supports + redispatching `Stargate` and `IbcMsg::Transfer` messages ([#692]) +- cosmwasm-std: The arithmetic methods of `Uint128` got a huge overhaul, making + them more consistent with the behaviour of the Rust primitive types. Thank you + [@yihuang] for bringing this up and for the great implementation. ([#853]) + 1. `Uint128` got the new functions `checked_add`, `checked_sub`, + `checked_mul`, `checked_div`, `checked_div_euclid`, `checked_rem`, + `wrapping_add`, `wrapping_sub`, `wrapping_mul`, `wrapping_pow`, + `saturating_add`, `saturating_sub`, `saturating_mul` and `saturating_pow` + which match their equivalent in [u128] except that instead of `Option` the + checked methods return a `Result` with an `OverflowError` or + `DivideByZeroError` that carries a few debug information and can directly + be converted to `StdError`/`StdResult` by using the `?` operator. + 2. `StdError::Underflow` and `StdError::underflow` were removed in favour of + `StdError::Overflow`. `StdError::DivideByZeroError` was added. + 3. The `-` operator (`impl ops::Sub for Uint128`) was removed + because it returned a `StdResult` instead of panicking in the case of an + overflow. This behaviour was inconsistent with `+` and the Rust standard + library. Please use the explicit `*_sub` methods introduced above. In a + couple of releases from now, we want to introduce the operator again with + panicking overflow behaviour ([#858]). +- cosmwasm-std: Replace `HumanAddr` with `String` in `BankQuery`, `StakingQuery` + and `WasmQuery` query requests ([#802]). +- cosmwasm-std: In staking query response types `Delegation`, `FullDelegation` + and `Validator` the validator address fields were changed from `HumanAddr` to + `String`. The new `Addr` type cannot be used here because it only supports + standard account addresses via `Api::addr_*` ([#871]). +- cosmwasm-std: Change address types in `BankMsg`, `IbcMsg` and `WasmMsg` from + `HumanAddr` to `String` ([#802]). +- cosmwasm-std: `Api::addr_humanize` now returns `Addr` instead of `HumanAddr` + ([#802]). +- cosmwasm-std: Hide `StakingMsg`, `CosmosMsg::Staking`, + `AllDelegationsResponse`, `BondedDenomResponse`, `Delegation`, + `FullDelegation`, `StakingQuery`, `Validator`, `ValidatorsResponse` and + `testing::StakingQuerier` behind the `staking` feature flag to make those only + available in contracts built for PoS chains. +- cosmwasm-std: Remove `StakingMsg::Withdraw` in favour of + `DistributionMsg::SetWithdrawAddress` and + `DistributionMsg::WithdrawDelegatorReward` ([#848]). +- cosmwasm-std: Rename `StakingQuery::Validators`, `ValidatorsResponse` and + `QuerierWrapper::query_validators` to `StakingQuery::AllValidators`, + `AllValidatorsResponse` and `QuerierWrapper.query_all_validators`. Add + `StakingQuery::Validator`, `ValidatorResponse` and + `QuerierWrapper::query_validator` to allow querying a single validator. + ([#879]) +- cosmwasm-schema: Make first argument non-mutable in `export_schema_with_title` + for consistency with `export_schema`. +- cosmwasm-std: The block time in `BlockInfo::time` is now a `Timestamp`. + `BlockInfo::time_nanos` was removed. + +[#696]: https://github.com/CosmWasm/cosmwasm/issues/696 +[#697]: https://github.com/CosmWasm/cosmwasm/issues/697 +[#736]: https://github.com/CosmWasm/cosmwasm/pull/736 +[#690]: https://github.com/CosmWasm/cosmwasm/issues/690 +[@yihuang]: https://github.com/yihuang +[#853]: https://github.com/CosmWasm/cosmwasm/pull/853 +[#858]: https://github.com/CosmWasm/cosmwasm/issues/858 +[u128]: https://doc.rust-lang.org/std/primitive.u128.html +[#802]: https://github.com/CosmWasm/cosmwasm/pull/802 +[#871]: https://github.com/CosmWasm/cosmwasm/issues/871 +[#861]: https://github.com/CosmWasm/cosmwasm/issues/861 +[#848]: https://github.com/CosmWasm/cosmwasm/issues/848 +[#879]: https://github.com/CosmWasm/cosmwasm/pull/879 + +### Deprecated + +- cosmwasm-std: `InitResponse`, `MigrateResponse` and `HandleResponse` are + deprecated in favour of the new `Response`. +- cosmwasm-std: `Context` is deprecated in favour of the new mutable helpers in + `Response`. +- cosmwasm-std: `HumanAddr` is not much more than an alias to `String` and it + does not provide significant safety advantages. With CosmWasm 0.14, we now use + `String` when there was `HumanAddr` before. There is also the new `Addr`, + which holds a validated immutable human readable address. ([#802]) + +[#802]: https://github.com/CosmWasm/cosmwasm/pull/802 + +## [0.13.2] - 2021-01-14 + +## Changed + +- cosmwasm-vm: Update Wasmer to 1.0.1. + +## [0.13.1] - 2021-01-12 + +### Added + +- cosmwasm-std: Add the new `#[entry_point]` macro attribute that serves as an + alternative implementation to `cosmwasm_std::create_entry_points!(contract)` + and `cosmwasm_std::create_entry_points_with_migration!(contract)`. Both ways + are supported in the 0.13 series. + +## [0.13.0] – 2021-01-06 + +## Added + +- cosmwasm-std: Extend binary to array support to 64 bytes. + +## Changed + +- all: Drop support for Rust versions lower than 1.47.0. +- cosmwasm-std: Remove `cosmwasm_std::testing::MockApi::new`. Use + `MockApi::default` instead. +- cosmwasm-vm: Upgrade Wasmer to 1.0 and adapt all the internal workings + accordingly. +- cosmwasm-vm: Export method `cosmwasm_vm::Cache::stats` and response type + `Stats`. +- cosmwasm-vm: Remove `cosmwasm_vm::testing::MockApi::new`. Use + `MockApi::default` instead. +- cosmwasm-vm: Convert field `Instance::api` to a method. +- cosmwasm-vm: Change order of generic arguments for consistency in `Instance`, + `Cache` and `Backend` to always match ``. +- cosmwasm-vm: Remove `Instance::get_memory_size`. Use `Instance::memory_pages` + instead. + +## 0.12.2 (2020-12-14) + +**cosmwasm-std** + +- `StdError` now implements `PartialEq` (ignoring backtrace if any). This allows + simpler `assert_eq!()` when testing error conditions (rather than match + statements as now). + +## 0.12.1 (2020-12-09) + +**cosmwasm-std** + +- Deprecate `InitResult`, `HandleResult`, `MigrateResult` and `QueryResult` in + order to make error type explicit and encourage migration to custom errors. +- Implement `Deref` for `QuerierWrapper`, such that `QuerierWrapper` behaves + like a smart pointer to `Querier` allowing you to access `Querier` methods + directly. + +## 0.12.0 (2020-11-19) + +**cosmwasm-std** + +- Remove the previously deprecated `StdError::Unauthorized`. Contract specific + errors should be implemented using custom error types now (see + [migration guide](./MIGRATING.md) 0.10 -> 0.11). +- Use dependency `thiserror` instead of `snafu` to implement `StdError`. Along + with this change, the `backtraces` feature now requires Rust nightly. +- Rename `StdError::ParseErr::source` to `StdError::ParseErr::source_type` and + `StdError::SerializeErr::target` to `StdError::SerializeErr::target_type` to + work around speacial treatment of the field name `source` in thiserror. +- Rename `Extern` to `Deps` to unify naming. +- Simplify ownership of calling `handle`, etc. with `Deps` and `DepsMut` struct + that just contains references (`DepsMut` has `&mut Storage` otherwise the + same) +- Remove unused `Deps::change_querier`. If you need this or similar + functionality, create a new struct with the right querier. +- Remove `ReadonlyStorage`. You can just use `Storage` everywhere. And use + `&Storage` to provide readonly access. This was only needed to let + `PrefixedStorage`/`ReadonlyPrefixedStorage` implement the common interface, + which is something we don't need. + +**cosmwasm-storage** + +- `PrefixedStorage`/`ReadonlyPrefixedStorage` do not implement the + `Storage`/`ReadonlyStorage` traits anymore. If you need nested prefixes, you + need to construct them directly via `PrefixedStorage::multilevel` and + `ReadonlyPrefixedStorage::multilevel`. +- Remove unused `TypedStorage`. If you need this or similar functionality, you + probably want to use `Bucket` or `Singleton`. If you really need it, please + copy the v0.11 code into your project. +- Remove `StorageTransaction` along with `transactional` and `RepLog`. This has + not been used actively for contract development and is now maintained in a + contract testing framework. + +**cosmwasm-vm** + +- Remove `Storage::range` and `StorageIterator`. The storage implementation is + now responsible for maintaining iterators internally and make them accessible + via the new `Storage::scan` and `Storage::next` methods. +- Add `FfiError::IteratorDoesNotExist`. Looking at this, `FfiError` should + probably be renamed to something that includes before, on and behind the FFI + boundary to Go. +- `MockStorage` now implements the new `Storage` trait and has an additional + `MockStorage::all` for getting all elements of an iterator in tests. +- Remove unused `Extern::change_querier`. If you need this or similar + functionality, create a new struct with the right querier. +- Let `Instance::from_code` and `CosmCache::get_instance` take options as an + `InstanceOptions` struct. This contains `gas_limit` and `print_debug` for now + and can easily be extended. `cosmwasm_vm::testing::mock_instance_options` can + be used for creating such a struct in integration tests. +- Make `FileSystemCache` crate internal. This should be used via `CosmCache`. +- Fix return type of `FileSystemCache::load` to `VmResult>` in + order to differentiate missing files from errors. +- Add in-memory caching for recently used Wasm modules. +- Rename `CosmCache` to just `cosmwasm_vm::Cache` and add `CacheOptions` to + configure it. +- Rename `Extern` to `Backend`. +- Rename `mock_dependencies` to `mock_backend` and + `mock_dependencies_with_balances` to `mock_backend_with_balances`. +- Rename `FfiError`/`FfiResult` to `BackendError`/`BackendResult` and adapt + `VmError` accordingly. + +## 0.11.2 (2020-10-26) + +**cosmwasm-std** + +- Implement `From` and `From` + for `StdError`. +- Generalize denom argument from `&str` to `S: Into` in `coin`, `coins` + and `Coin::new`. +- Implement `PartialEq` between `Binary` and `Vec`/`&[u8]`. +- Add missing `PartialEq` implementations between `HumanAddr` and `str`/`&str`. +- Add `Binary::to_array`, which allows you to copy binary content into a + fixed-length `u8` array. This is especially useful for creating integers from + binary data. + +## 0.11.1 (2020-10-12) + +**cosmwasm-std** + +- Implement `Hash` and `Eq` for `Binary` to allow using `Binary` in `HashSet` + and `HashMap`. +- Implement `Hash` and `Eq` for `CanonicalAddr` to allow using `CanonicalAddr` + in `HashSet` and `HashMap`. +- Implement `Add`, `AddAssign` and `Sub` with references on the right hand side + for `Uint128`. +- Implement `Sum` and `Sum<&'a Uint128>` for `Uint128`. + +## 0.11.0 (2020-10-08) + +**all** + +- Drop support for Rust versions lower than 1.45.2. +- The serialization of the result from `init`/`migrate`/`handle`/`query` changed + in an incompatible way. See the new `ContractResult` and `SystemResult` types + and their documentation. +- Pass `Env` into `query` as well. As this doesn't have `MessageInfo`, we + removed `MessageInfo` from `Env` and pass that as a separate argument to + `init`, `handle`, and `query`. See the example + [type definitions in the README](README.md#implementing-the-smart-contract) to + see how to update your contract exports (just add one extra arg each). + +**cosmwasm-std** + +- Add `time_nanos` to `BlockInfo` allowing access to high precision block times. +- Change `FullDelegation::accumulated_rewards` from `Coin` to `Vec`. +- Rename `InitResponse::log`, `MigrateResponse::log` and `HandleResponse::log` + to `InitResponse::attributes`, `MigrateResponse::attributes` and + `HandleResponse::attributes`. +- Rename `LogAttribute` to `Attribute`. +- Rename `log` to `attr`. +- Rename `Context::add_log` to `Context::add_attribute`. +- Add `Api::debug` for emitting debug messages during development. +- Fix error type for response parsing errors in `ExternalQuerier::raw_query`. + This was `Ok(Err(StdError::ParseErr))` instead of + `Err(SystemError::InvalidResponse)`, implying an error created in the target + contract. +- Deprecate `StdError::Unauthorized` and `StdError::unauthorized` in favour of + custom errors. From now on `StdError` should only be created by the standard + library and should only contain cases the standard library needs. +- Let `impl Display for CanonicalAddr` use upper case hex instead of base64. + This also affects `CanonicalAddr::to_string`. +- Create trait `CustomQuery` for the generic argument in + `QueryRequest`. This allows us to provide + `impl From for QueryRequest` for any custom query. +- Implement `From for Vec`. +- Implement `From for Vec`. +- Add `Binary::into_vec` and `CanonicalAddr::into_vec`. +- The `canonical_length` argument was removed from `mock_dependencies`, + `mock_dependencies_with_balances`. In the now deprecated `MockApi::new`, the + argument is unused. Contracts should not need to set this value and usually + should not make assumptions about the value. +- The canonical address encoding in `MockApi::canonical_address` and + `MockApi::human_address` was changed to an unpredictable representation of + non-standard length that aims to destroy most of the input structure. + +**cosmwasm-storage** + +- Change order of arguments such that `storage` is always first followed by + namespace in `Bucket::new`, `Bucket::multilevel`, `ReadonlyBucket::new`, + `ReadonlyBucket::multilevel`, `bucket` and `bucket_read`. +- Change order of arguments such that `storage` is always first followed by + namespace in `PrefixedStorage::new`, `PrefixedStorage::multilevel`, + `ReadonlyPrefixedStorage::new`, `ReadonlyPrefixedStorage::multilevel`, + `prefixed` and `prefixed_read`. + +**cosmwasm-vm** + +- `CosmCache::new`, `Instance::from_code` and `Instance::from_module` now take + an additional argument to enable/disable printing debug logs from contracts. +- Bump required export `cosmwasm_vm_version_3` to `cosmwasm_vm_version_4`. +- The `canonical_length` argument was removed from `mock_dependencies`, + `mock_dependencies_with_balances` and `MockApi::new_failing`. In the now + deprecated `MockApi::new`, the argument is unused. Contracts should not need + to set this value and usually should not make assumptions about the value. +- The canonical address encoding in `MockApi::canonical_address` and + `MockApi::human_address` was changed to an unpredictable representation of + non-standard length that aims to destroy most of the input structure. + +## 0.10.1 (2020-08-25) + +**cosmwasm-std** + +- Fix bug where `ExternalStorage.range()` would cause VM error if either lower + or upper bound was set + ([#508](https://github.com/CosmWasm/cosmwasm/issues/508)) + +## 0.10.0 (2020-07-30) + +**all** + +- Drop support for Rust versions lower than 1.44.1. + +**cosmwasm-std** + +- Remove error helpers `generic_err`, `invalid_base64`, `invalid_utf8`, + `not_found`, `parse_err`, `serialize_err`, `underflow`, `unauthorized` in + favour of `StdError::generic_err` and friends. +- Implement `From<&[u8; $N]> for Binary` and `From<[u8; $N]> for Binary` for all + `$N <= 32`. +- Add `Context` object that can be used to build Init/Handle/Migrate response + via `add_log`, `add_message`, `set_data` and then convert to the proper type + via `into` or `try_into`. Option to simplify response construction. +- Env uses `HumanAddr` for `message.sender` and `contract_address` +- Remove `Api` argument from `mock_env` +- Implement `From<&[u8]>` and `From>` for `CanonicalAddr` + +**cosmwasm-vm** + +- Remove unused cache size argument from `CosmCache`. +- `set_gas_limit` now panics if the given gas limit exceeds the max. supported + value. +- Increase the max. supported value for gas limit from 10_000_000_000 to + 0x7FFFFFFFFFFFFFFF. +- Add checks to `get_region` for failing early when the contract sends a Region + pointer to the VM that is not backed by a plausible Region. This helps + development of standard libraries. +- Create dedicated `RegionValidationError` and `RegionValidationResult`. +- `Api::human_address` and `Api::canonical_address` now return a pair of return + data and gas usage. +- Remove `NextItem` in favour of a more advanced `FfiResult`, which is used + to store the return result and the gas information consistently across all + APIs. `FfiResult` was changed to `(Result, GasInfo)`. +- Create error type `FfiError::InvalidUtf8` for the cases where the backend + sends invalid UTF-8 in places that expect strings. +- Remove `FfiError::Other` in favour of `FfiError::UserErr` and + `FfiError::Unknown`. +- The `canonicalize_address` and `humanize_address` imports now report user + errors to the contract. +- Bump `cosmwasm_vm_version_2` to `cosmwasm_vm_version_3`. +- `Querier::raw_query` and `QuerierResult` were removed in favour of the new + `Querier::query_raw`, which includes a gas limit parameter for the query. + +## 0.9.4 (2020-07-16) + +**cosmwasm-vm** + +- Add `Instance::create_gas_report` returning a gas report including the + original limit, the remaining gas and the internally/externally used gas. + +## 0.9.3 (2020-07-08) + +**cosmwasm-storage** + +- Add `.remove()` method to `Bucket` and `Singleton`. + +## 0.9.2 (2020-06-29) + +- Downgrade wasmer to 0.17.0. + +## 0.9.1 (2020-06-25) + +**cosmwasm-std** + +- Replace type `Never` with `Empty` because enums with no cases cannot be + expressed in valid JSON Schema. + +## 0.9.0 (2020-06-25) + +Note: this version contains an API bug and should not be used (see +https://github.com/CosmWasm/cosmwasm/issues/451). + +**all** + +- Upgrade wasmer to 0.17.1. +- Drop support for Rust versions lower than 1.43.1 + +**cosmwasm-std** + +- `ReadonlyStorage::get` and all its implementations now return + `Option>`. +- `ReadonlyStorage::range` and all its implementations now always succeed and + return an iterator instead of a result. This is now an iterator over + `Option` instead of `Option>`. +- `Storage::{set, remove}` and all their implementations no longer have a return + value. Previously they returned `StdResult<()>`. +- Trait `Querier` is not `Clone` and `Send` anymore. +- `consume_region` panics on null pointers and returns `Vec` instead of + `StdResult>`. +- Added contract migration mechanism. Contracts can now optionally export a + `migrate` function with the following definition: + ```rust + extern "C" fn migrate(env_ptr: u32, msg_ptr: u32) -> u32; + ``` +- InitResponse no longer has a data field. We always return the contract address + in the data field in the blockchain and don't allow you to override. `handle` + can still make use of the field. +- Rename `MockQuerier::with_staking` to `MockQuerier::update_staking` to match + `::update_balance`. +- The obsolete `StdError::NullPointer` and `null_pointer` were removed. +- Error creator functions are now in type itself, e.g. + `StdError::invalid_base64` instead of `invalid_base64`. The free functions are + deprecated and will be removed before 1.0. + +**cosmwasm-storage** + +- Remove `transactional_deps`. Use `transactional` that just provides a + transactional storage instead. +- `get_with_prefix` returns `Option>` instead of + `StdResult>>`. +- `set_with_prefix` and `remove_with_prefix` return nothing instead of + `StdResult<()>`. +- `RepLog::commit` no longer returns any value (always succeeds). +- `Op::apply` no longer returns any value (always succeeds). + +**cosmwasm-vm** + +- The export `allocate` must not return 0 as a valid address. The contract is + responsible for avoiding this offset in the linear memory. +- The import `db_read` now allocates memory for the return value as part of the + call and returns a pointer to the value as `u32`. The return value 0 means + _key does not exist_. +- The import `db_next` now allocates a memory region for the return key and + value as part of the call and returns a pointer to the region as `u32`. The + data in the region is stored in the format `value || key || keylen`. As + before, an empty key means _no more value_. +- Remove `Instance::get_gas` in favour of `Instance::get_gas_left`. +- All calls from the VM layer to the chain layer also return the amount of gas + used on success. (This is represented by replacing the return value with + `(value, used_gas)`). Gas usage across the system is then tracked in the VM + layer, which allows us to halt the contract during an import, as soon as we + can prove that we used all allocated gas. +- Remove instance caching, which is disabled since 0.8.1 as it is not stable. + Remove `CosmCache::store_instance`; you can not call `Instance::recylce` + directly to get back the external dependencies. +- Rename `MockQuerier::with_staking` to `MockQuerier::update_staking` to match + `::update_balance`. +- Instead of panicking, `read_region`/`write_region`/`get_region`/`set_region` + now return a new `CommunicationError::DerefErr` when dereferencing a pointer + provided by the contract fails. +- `FfiError::set_message` was removed because errors should be immutable. Use + `FfiError::other` to create an error with the desired error message. +- The import implementation of `db_scan` now errors instead of returning an + error code for an invalid order value. The return type was changed to `u32`. +- Remove `StorageIteratorItem` in favour of the new types `StorageIterator` and + `NextItem`. `StorageIterator` is a custom iterator type that does not + implement Rust's `Iterator` trait, allowing it to communicate the used gas + value of the last `next` call to the VM. +- Don't report any `VmError` back to the contract in `canonicalize_address` and + `humanize_address`. Only invalid inputs should be reported. +- Move error cases `VmError::RegionLengthTooBig` and `VmError::RegionTooSmall` + into `CommunicationError`. +- In the `canonicalize_address` implementation, invalid UTF-8 inputs now result + in `CommunicationError::InvalidUtf8`, which is not reported back to the + contract. A standard library should ensure this never happens by correctly + encoding string input values. +- Merge trait `ReadonlyStorage` into `Storage`. +- The imports `canonicalize_address` and `humanize_address` now return a memory + address to an error `Region`. If this address is 0, the call succeeded. +- Bump `cosmwasm_vm_version_1` to `cosmwasm_vm_version_2`. + +## 0.8.1 (2020-06-08) + +**cosmwasm-std** + +- The arguments of `log` changed from `&str` to `ToString`, allowing to pass + various types like `String`, `HumanAddr`, `Uint128` or primitive integers + directly. +- Add `From>` and `Into>` implementations for `Binary` for + zero-copy conversions. + +**cosmwasm-vm** + +- Deprecated `Instance::get_gas` in favour of `Instance::get_gas_left`. The old + method will remain available for a while but will issue a deprecation warning + when used. +- Disable instance caching by treating every cache size as 0. Instance caching + is not safe as the same Wasm memory is reused across multiple executions. +- The storage of an `Instance` can now be set into readonly mode, which is + checked by the writing storage imports `db_write` and `db_remove`. Read-only + mode is off by default for backwards compatibility. `call_query_raw` now sets + the instance's storage to readonly. +- The new error case `VmError::WriteAccessDenied` is returned when a contract + calls an import that potentially writes to storage during a query. + +## 0.8.0 (2020-05-25) + +**all** + +- Upgrade schemars to 0.7. +- Upgrade wasmer to 0.17. +- Update snafu to 0.6. +- Minimal supported Rust version is 1.41. +- Split `Region.len` into `Region.capacity` and `Region.length`, where the new + capacity is the number of bytes available and `length` is the number of bytes + used. This is a breaking change in the contract-vm interface, which requires + the same memory layout of the `Region` struct on both sides. +- Add `remove` method to `Storage` trait. +- (feature-flagged) Add `range` method to `ReadonlyStorage` trait. This returns + an iterator that covers all or a subset of the items in the db ordered + ascending or descending by key. +- Add new feature flag `iterator` to both packages to enable `range` + functionality. This is used to allow potential porting to chains that use + Merkle Tries (which don't allow iterating over ranges). +- All serialized JSON types now use snake_case mappings for names. This means + enum fields like `ChangeOwner` will map to `change_owner` in the underlying + JSON, not `changeowner`. This is a breaking change for the clients. +- Public interface between contract and runtime no longer uses `String` to + represent an error, but rather serializes `ApiError` as a rich JSON error. +- Return value from `env.write_db` and `env.remove_db` to allow error reporting. +- Query responses are now required to contain valid JSON. +- Renamed all `*_db` wasm imports to `db_*` +- Merge `cw-storage` repo as subpackage, now `cosmwasm-storage` +- Add iterator support to `cosmwasm-storage` +- `Coin.amount` is now `Uint128` rather than `String`. Uint128 serializes as a + string in JSON, but parses into a u128 data in memory. It also has some + operator overloads to allow easy math operations on `Coin` types, as well as + enforcing valid amounts. +- `Env` no longer has a `contract.balance` element. If you need this info, + please use the `Querier` to get this info. As of Cosmos-SDK 0.39 this needs + extra storage queries to get the balance, so we only do those queries when + needed. +- `Env.message.sent_funds` is a `Vec` not `Option>`. We will + normalize the go response in `go-cosmwasm` before sending it to the contract. +- `Env.message.signer` was renamed to `Env.message.sender`. +- `Env.block.{height,time}` are now `u64` rather than `i64`. + +**cosmwasm-schema** + +- This new crate now contains the implementations for generating JSON Schema + files from interface types. It exposes the functions `export_schema`, + `export_schema_with_title`, and `schema_for`. + +**cosmwasm-std** + +- Make all symbols from `cosmwasm::memory` crate internal, as those symbols are + not needed by users of the library. +- Rename `cosmwasm::mock::dependencies` -> `cosmwasm::mock::mock_dependencies` + to differentiate between testing and production `External`. +- Export all symbols from `cosmwasm::mock` as the new non-wasm32 module + `cosmwasm::testing`. Export all remaining symbols at top level (e.g. + `use cosmwasm::traits::{Api, Storage};` + `use cosmwasm::encoding::Binary;` + becomes `use cosmwasm::{Api, Binary, Storage};`). +- Rename package `cosmwasm` to `cosmwasm-std`. +- The export `allocate` does not zero-fill the allocated memory anymore. +- Add `remove_db` to the required imports of a contract. +- (feature-flagged) add `scan_db` and `next_db` callbacks from wasm contract to + VM. +- `serde::{from_slice, to_vec}` return `cosmwasm_std::Result`, no more need to + use `.context(...)` when calling these functions +- Split `Response` into `InitResponse` and `HandleResponse`; split + `ContractResult` into `InitResult` and `HandleResult`. +- Create explicit `QueryResponse`, analogue to `InitResponse` and + `HandleResponse`. +- The exports `cosmwasm_vm_version_1`, `allocate` and `deallocate` are now + private and can only be called via the Wasm export. Make sure to `use` + `cosmwasm_std` at least once in the contract to pull in the C exports. +- Add `Querier` trait and `QueryRequest` for query callbacks from the contract, + along with `SystemError` type for the runtime rejecting messages. +- `QueryRequest` takes a generic `Custom(T)` type that is passed opaquely to the + end consumer (`wasmd` or integration test stubs), allowing custom queries to + native code. +- `{Init,Handle,Query}Result` are now just aliases for a concrete `ApiResult` + type. +- Support results up to 128 KiB in `ExternalStorage.get`. +- The `Storage` trait's `.get`, `.set` and `.remove` now return a `Result` to + allow propagation of errors. +- Move `transactional`, `transactional_deps`, `RepLog`, `StorageTransaction` + into crate `cosmwasm-storage`. +- Rename `Result` to `StdResult` to differentiate between the auto-`use`d + `core::result::Result`. Fix error argument to `Error`. +- Complete overhaul of `Error` into `StdError`: + - The `StdError` enum can now be serialized and deserialized (losing its + backtrace), which allows us to pass them over the Wasm/VM boundary. This + allows using fully structured errors in e.g. integration tests. + - Auto generated snafu error constructor structs like `NotFound`/`ParseErr`/… + have been intenalized in favour of error generation helpers like + `not_found`/`parse_err`/… + - All error generator functions now return errors instead of results. + - Error cases don't contain `source` fields anymore. Instead source errors are + converted to standard types like `String`. For this reason, both + `snafu::ResultExt` and `snafu::OptionExt` cannot be used anymore. + - Backtraces became optional. Use `RUST_BACKTRACE=1` to enable them. + - `Utf8Err`/`Utf8StringErr` merged into `StdError::InvalidUtf8` + - `Base64Err` renamed into `StdError::InvalidBase64` + - `ContractErr`/`DynContractErr` merged into `StdError::GeneralErr` + - The unused `ValidationErr` was removed + - `StdError` is now + [non_exhaustive](https://doc.rust-lang.org/1.35.0/unstable-book/language-features/non-exhaustive.html), + making new error cases non-breaking changes. +- `ExternalStorage.get` now returns an empty vector if a storage entry exists + but has an empty value. Before, this was normalized to `None`. +- Reorganize `CosmosMsg` enum types. They are now split by modules: + `CosmosMsg::Bank(BankMsg)`, `CosmosMsg::Custom(T)`, `CosmosMsg::Wasm(WasmMsg)` +- CosmosMsg is now generic over the content of `Custom` variant. This allows + blockchains to support custom native calls in their Cosmos-SDK apps and + developers to make use of them in CosmWasm apps without forking the + `cosmwasm-vm` and `go-cosmwasm` runtime. +- Add `staking` feature flag to expose new `StakingMsg` types under `CosmosMsg` + and new `StakingRequest` types under `QueryRequest`. +- Add support for mocking-out staking queries via `MockQuerier.with_staking` +- `from_slice`/`from_binary` now require result type to be `DeserializeOwned`, + i.e. the result must not contain references such as `&str`. + +**cosmwasm-vm** + +- Make `Instance.memory`/`.allocate`/`.deallocate`/`.func` crate internal. A + user of the VM must not access the instance's memory directly. +- The imports `env.canonicalize_address`, `env.humanize_address` and + `env.read_db` don't return the number of bytes written anymore. This value is + now available in the resulting regions. Negative return values are errors, 0 + is success and values greater than 0 are reserved for future use. +- Change the required interface version guard export from `cosmwasm_api_0_6` to + `cosmwasm_vm_version_1`. +- Provide implementations for `remove_db` and (feature-flagged) `scan_db` and + `next_db` +- Provide custom `serde::{from_slice, to_vec}` implementation separate from + `cosmwasm_std`, so we can return cosmwasm-vm specific `Result` (only used + internally). +- `call_{init,handle,query}` and the `cosmwasm_vm::testing` wrappers return + standard `Result` types now, eg. `Result`. +- Add length limit when reading memory from the instance to protect against + malicious contracts creating overly large `Region`s. +- Add `Instance.get_memory_size`, giving you the peak memory consumption of an + instance. +- Remove `cosmwasm_vm::errors::CacheExt`. +- Move `cosmwasm_vm::errors::{Error, Result}` to + `cosmwasm_vm::{VmError, VmResult}` and remove generic error type from result. +- The import `db_read` now returns an error code if the storage key does not + exist. The latest standard library converts this error code back to a `None` + value. This allows differentiating non-existent and empty storage entries. +- Make `Instance::from_module`, `::from_wasmer` and `::recycle` crate-internal. +- Create explicit, public `Checksum` type to identify Wasm blobs. +- `CosmCache::new` now takes supported features as an argument. +- Rename `VmError::RegionTooSmallErr` to `VmError::RegionTooSmall`. +- Rename `VmError::RegionLengthTooBigErr` to `VmError::RegionLengthTooBig`. +- Change property types to owned string in `VmError::UninitializedContextData`, + `VmError::ConversionErr`, `VmError::ParseErr` and `VmError::SerializeErr`. +- Remove `VmError::IoErr` in favour of `VmError::CacheErr`. +- Simplify `VmError::CompileErr`, `VmError::ResolveErr` and + `VmError::WasmerRuntimeErr` to just hold a string with the details instead of + the source error. +- Remove `VmError::WasmerErr` in favour of the new `VmError::InstantiationErr`. +- The snafu error builders from `VmError` are now private, i.e. callers can only + use the errors, not create them. +- `VmError` is now `#[non_exhaustive]`. +- Split `VmError::RuntimeErr` in `VmError::BackendErr` and + `VmError::GenericErr`; rename `VmError::WasmerRuntimeErr` to + `VmError::RuntimeErr`. +- Add `Instance.with_querier` analogue to `Instance.with_storage`. + +## 0.7.2 (2020-03-23) + +**cosmwasm** + +- Fix JSON schema type of `Binary` from int array (wrong) to string (right). +- Make `Extern` not `Clone`able anymore. Before cloning led to copying the data + for mock storage and copying a stateless bridge for the external storage, + which are different semantics. +- Remove public `cosmwasm::imports::dependencies`. A user of this library does + not need to call this explicitely. Dependencies are created internally and + passed as an argument in `exports::do_init`, `exports::do_handle` and + `exports::do_query`. +- Make `ExternalStorage` not `Clone`able anymore. This does not copy any data, + so a clone could lead to unexpected results. + +## 0.7.1 (2020-03-11) + +**cosmwasm_vm** + +- Avoid unnecessary panic when checking corrupted wasm file. +- Support saving the same wasm to cache multiple times. + +## 0.7.0 (2020-02-26) + +**cosmwasm** + +- Rename `Slice` to `Region` to simplify differentiation between Wasm memory + region and serde's `from_slice` +- Rename `Params` to `Env`, `mock_params` to `mock_env` for clearer naming (this + is information on the execution environment) +- `Response.log` is not a vector of key/value pairs that can later be indexed by + Tendermint. + +**cosmwasm_vm** + +- Remove export `cosmwasm_vm::read_memory`. Using this indicates an + architectural flaw, since this is a method for host to guest communication + inside the VM and not needed for users of the VM. +- Create new type `cosmwasm_vm:errors::Error::RegionTooSmallErr`. +- Change return type of `cosmwasm_vm::write_memory` to `Result` to + make it harder to forget handling errors. +- Fix missing error propagation in `do_canonical_address`, `do_human_address` + and `allocate`. +- Update error return codes in import `c_read`. +- Rename imports `c_read`/`c_write` to `read_db`/`write_db`. +- Rename imports `c_canonical_address`/`c_human_address` to + `canonicalize_address`/`humanize_address`. +- Add `cosmwasm_vm::testing::test_io` for basic memory allocation/deallocation + testing between host and guest. +- Make `ValidationErr.msg` a dynamic `String` including relevant runtime + information. +- Remove export `check_api_compatibility`. The VM will take care of calling it. +- Let `check_api_compatibility` check imports by fully qualified identifier + `.`. +- Make gas limit immutable in `cosmwasm_vm::instance::Instance`. It is passed + once at construction time and cannot publicly be manipulated anymore. +- Remove `take_storage`/`leave_storage` from `cosmwasm_vm::Instance`. + +## 0.6 + +[Define canonical address callbacks](https://github.com/confio/cosmwasm/issues/73) + +- Use `&[u8]` for addresses in params +- Allow contracts to resolve human readable addresses (`&str`) in their json + into a fixed-size binary representation +- Provide mocks for unit testing and integration tests + +- Separate out `Storage` from `ReadOnlyStorage` as separate traits + +## 0.5 + +### 0.5.2 + +This is the first documented and supported implementation. It contains the basic +feature set. `init` and `handle` supported for modules and can return messages. +A stub implementation of `query` is done, which is likely to be deprecated soon. +Some main points: + +- The build-system and unit/integration-test setup is all stabilized. +- Cosmwasm-vm supports singlepass and cranelift backends, and caches modules on + disk and instances in memory (lru cache). +- JSON Schema output works + +All future Changelog entries will reference this base + +[1.0.0-rc.0]: + https://github.com/CosmWasm/cosmwasm/compare/v1.0.0-beta8...v1.0.0-rc.0 +[1.0.0-beta8]: + https://github.com/CosmWasm/cosmwasm/compare/v1.0.0-beta7...v1.0.0-beta8 +[1.0.0-beta7]: + https://github.com/CosmWasm/cosmwasm/compare/v1.0.0-beta6...v1.0.0-beta7 +[1.0.0-beta6]: + https://github.com/CosmWasm/cosmwasm/compare/v1.0.0-beta5...v1.0.0-beta6 +[1.0.0-beta5]: + https://github.com/CosmWasm/cosmwasm/compare/v1.0.0-beta4...v1.0.0-beta5 +[1.0.0-beta4]: + https://github.com/CosmWasm/cosmwasm/compare/v1.0.0-beta3...v1.0.0-beta4 +[1.0.0-beta3]: + https://github.com/CosmWasm/cosmwasm/compare/v1.0.0-beta...v1.0.0-beta3 +[1.0.0-beta]: https://github.com/CosmWasm/cosmwasm/compare/v0.16.2...v1.0.0-beta +[0.16.2]: https://github.com/CosmWasm/cosmwasm/compare/v0.16.1...v0.16.2 +[0.16.1]: https://github.com/CosmWasm/cosmwasm/compare/v0.16.0...v0.16.1 +[0.16.0]: https://github.com/CosmWasm/cosmwasm/compare/v0.15.2...v0.16.0 +[0.15.2]: https://github.com/CosmWasm/cosmwasm/compare/v0.15.1...v0.15.2 +[0.15.1]: https://github.com/CosmWasm/cosmwasm/compare/v0.15.0...v0.15.1 +[0.15.0]: https://github.com/CosmWasm/cosmwasm/compare/v0.14.1...v0.15.0 +[0.14.1]: https://github.com/CosmWasm/cosmwasm/compare/v0.14.0...v0.14.1 +[0.14.0]: https://github.com/CosmWasm/cosmwasm/compare/v0.13.1...v0.14.0 +[0.13.2]: https://github.com/CosmWasm/cosmwasm/compare/v0.13.1...v0.13.2 +[0.13.1]: https://github.com/CosmWasm/cosmwasm/compare/v0.13.0...v0.13.1 +[0.13.0]: https://github.com/CosmWasm/cosmwasm/compare/v0.12.0...v0.13.0 diff --git a/Cargo.lock b/Cargo.lock index 02e6cb366..050c81d0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,7 +59,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ - "anstyle 1.0.4", + "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", @@ -67,12 +67,6 @@ dependencies = [ "utf8parse", ] -[[package]] -name = "anstyle" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" - [[package]] name = "anstyle" version = "1.0.4" @@ -103,7 +97,7 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ - "anstyle 1.0.4", + "anstyle", "windows-sys 0.48.0", ] @@ -121,11 +115,11 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "assert_cmd" -version = "2.0.10" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0b2340f55d9661d76793b2bfc2eb0e62689bd79d067a95707ea762afd5e9dd" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" dependencies = [ - "anstyle 0.3.5", + "anstyle", "bstr", "doc-comment", "predicates", @@ -184,6 +178,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -368,7 +368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ "anstream", - "anstyle 1.0.4", + "anstyle", "clap_lex 0.6.0", "strsim", ] @@ -390,9 +390,9 @@ checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "colorchoice" @@ -455,6 +455,7 @@ version = "1.1.9+0.8.1" dependencies = [ "criterion", "digest 0.10.7", + "ecdsa", "ed25519-zebra", "english-numbers", "hex", @@ -508,6 +509,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "chrono", "cosmwasm-crypto", @@ -523,6 +525,7 @@ dependencies = [ "serde-json-wasm", "serde_json", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -728,6 +731,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -902,7 +915,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -1362,6 +1375,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1518,7 +1540,7 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" dependencies = [ - "anstyle 1.0.4", + "anstyle", "difflib", "float-cmp", "itertools 0.11.0", @@ -1861,6 +1883,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "semver" version = "1.0.20" @@ -1972,6 +2000,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -2016,6 +2054,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.10.0" @@ -2342,9 +2386,9 @@ dependencies = [ [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -2355,6 +2399,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -2370,19 +2415,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -2393,9 +2442,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -2412,9 +2461,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -2431,9 +2480,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -2443,9 +2492,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -2454,9 +2503,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -2470,14 +2519,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/MIGRATING.md b/MIGRATING.md index d06877b40..e48b84d37 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -4,6 +4,22 @@ This guide explains what is needed to upgrade contracts when migrating over major releases of `cosmwasm`. Note that you can also view the [complete CHANGELOG](./CHANGELOG.md) to understand the differences. +## 1.4.x -> 1.5.0 + +- Update `cosmwasm-*` dependencies in Cargo.toml (skip the ones you don't use): + + ``` + [dependencies] + cosmwasm-std = "1.5.0" + cosmwasm-storage = "1.5.0" + # ... + + [dev-dependencies] + cosmwasm-schema = "1.5.0" + cosmwasm-vm = "1.5.0" + # ... + ``` + ## 1.3.x -> 1.4.0 - Update `cosmwasm-*` dependencies in Cargo.toml (skip the ones you don't use): diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock index e8b46cf4e..1a2e92d86 100644 --- a/contracts/burner/Cargo.lock +++ b/contracts/burner/Cargo.lock @@ -73,6 +73,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -183,9 +189,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -217,6 +223,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -259,6 +266,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -269,6 +277,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -421,6 +430,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -583,7 +602,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -926,6 +945,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1261,6 +1289,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1366,6 +1400,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1410,6 +1454,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1655,9 +1705,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1668,6 +1718,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1683,19 +1734,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1706,9 +1761,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1725,9 +1780,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1744,9 +1799,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1756,9 +1811,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1767,9 +1822,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1783,14 +1838,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock index 1e05ea5d6..dd62cc869 100644 --- a/contracts/crypto-verify/Cargo.lock +++ b/contracts/crypto-verify/Cargo.lock @@ -73,6 +73,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -588,7 +607,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -946,6 +965,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1297,6 +1325,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1412,6 +1446,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1456,6 +1500,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1701,9 +1751,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1714,6 +1764,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1729,19 +1780,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1752,9 +1807,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1771,9 +1826,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1790,9 +1845,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1802,9 +1857,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1813,9 +1868,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1829,14 +1884,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/crypto-verify/src/contract.rs b/contracts/crypto-verify/src/contract.rs index b2917a90c..41fe87920 100644 --- a/contracts/crypto-verify/src/contract.rs +++ b/contracts/crypto-verify/src/contract.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, + entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, StdError, StdResult, Uint128, }; use sha2::{Digest, Sha256}; @@ -32,7 +32,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { message, signature, public_key, - } => to_binary(&query_verify_cosmos( + } => to_json_binary(&query_verify_cosmos( deps, &message.0, &signature.0, @@ -42,7 +42,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { message, signature, signer_address, - } => to_binary(&query_verify_ethereum_text( + } => to_json_binary(&query_verify_ethereum_text( deps, &message, &signature, @@ -60,14 +60,14 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { r, s, v, - } => to_binary(&query_verify_ethereum_transaction( + } => to_json_binary(&query_verify_ethereum_transaction( deps, from, to, nonce, gas_limit, gas_price, value, data, chain_id, r, s, v, )?), QueryMsg::VerifyTendermintSignature { message, signature, public_key, - } => to_binary(&query_verify_tendermint( + } => to_json_binary(&query_verify_tendermint( deps, &message.0, &signature.0, @@ -77,13 +77,13 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { messages, signatures, public_keys, - } => to_binary(&query_verify_tendermint_batch( + } => to_json_binary(&query_verify_tendermint_batch( deps, &messages, &signatures, &public_keys, )?), - QueryMsg::ListVerificationSchemes {} => to_binary(&query_list_verifications(deps)?), + QueryMsg::ListVerificationSchemes {} => to_json_binary(&query_list_verifications(deps)?), } } @@ -218,7 +218,7 @@ mod tests { mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, }; use cosmwasm_std::{ - from_slice, Binary, OwnedDeps, RecoverPubkeyError, StdError, VerificationError, + from_json, Binary, OwnedDeps, RecoverPubkeyError, StdError, VerificationError, }; use hex_literal::hex; @@ -274,7 +274,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -296,7 +296,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: false }); } @@ -339,7 +339,7 @@ mod tests { signer_address: signer_address.into(), }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -360,7 +360,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: false }); } @@ -380,7 +380,7 @@ mod tests { signer_address: signer_address.into(), }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: false }); // Broken signature @@ -447,7 +447,7 @@ mod tests { v, }; let raw = query(deps.as_ref(), mock_env(), msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -475,7 +475,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -508,7 +508,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -542,7 +542,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -573,7 +573,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: false }); } @@ -626,7 +626,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: true }); } @@ -648,7 +648,7 @@ mod tests { }; let raw = query(deps.as_ref(), mock_env(), verify_msg).unwrap(); - let res: VerifyResponse = from_slice(&raw).unwrap(); + let res: VerifyResponse = from_json(raw).unwrap(); assert_eq!(res, VerifyResponse { verifies: false }); } @@ -683,7 +683,7 @@ mod tests { let query_msg = QueryMsg::ListVerificationSchemes {}; let raw = query(deps.as_ref(), mock_env(), query_msg).unwrap(); - let res: ListVerificationsResponse = from_slice(&raw).unwrap(); + let res: ListVerificationsResponse = from_json(raw).unwrap(); assert_eq!( res, diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock index b4bad366c..8d716d400 100644 --- a/contracts/cyberpunk/Cargo.lock +++ b/contracts/cyberpunk/Cargo.lock @@ -91,6 +91,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -207,9 +213,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -247,6 +253,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -289,6 +296,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64 0.21.5", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -299,6 +307,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -451,6 +460,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -625,7 +644,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -990,6 +1009,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1350,6 +1378,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1455,6 +1489,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1499,6 +1543,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1757,9 +1807,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1770,6 +1820,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1785,19 +1836,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1808,9 +1863,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1827,9 +1882,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1846,9 +1901,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1858,9 +1913,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1869,9 +1924,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1885,14 +1940,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/cyberpunk/src/contract.rs b/contracts/cyberpunk/src/contract.rs index 0374a06a8..2084f630d 100644 --- a/contracts/cyberpunk/src/contract.rs +++ b/contracts/cyberpunk/src/contract.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, to_binary, Api, DenomMetadata, Deps, DepsMut, Empty, Env, MessageInfo, + entry_point, to_json_binary, Api, DenomMetadata, Deps, DepsMut, Empty, Env, MessageInfo, PageRequest, QueryResponse, Response, StdError, StdResult, WasmMsg, }; @@ -94,7 +94,7 @@ fn execute_memory_loop() -> Result { fn execute_message_loop(env: Env) -> Result { let resp = Response::new().add_message(WasmMsg::Execute { contract_addr: env.contract.address.into(), - msg: to_binary(&ExecuteMsg::MessageLoop {})?, + msg: to_json_binary(&ExecuteMsg::MessageLoop {})?, funds: vec![], }); Ok(resp) @@ -148,7 +148,7 @@ fn execute_unreachable() -> Result { } fn execute_mirror_env(env: Env) -> Result { - Ok(Response::new().set_data(to_binary(&env)?)) + Ok(Response::new().set_data(to_json_binary(&env)?)) } fn execute_debug(api: &dyn Api) -> Result { @@ -183,9 +183,9 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { use QueryMsg::*; match msg { - MirrorEnv {} => to_binary(&query_mirror_env(env)), - Denoms {} => to_binary(&query_denoms(deps)?), - Denom { denom } => to_binary(&query_denom(deps, denom)?), + MirrorEnv {} => to_json_binary(&query_mirror_env(env)), + Denoms {} => to_json_binary(&query_denoms(deps)?), + Denom { denom } => to_json_binary(&query_denom(deps, denom)?), } } @@ -226,7 +226,7 @@ mod tests { use cosmwasm_std::testing::{ mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, }; - use cosmwasm_std::{from_binary, DenomMetadata, DenomUnit, OwnedDeps}; + use cosmwasm_std::{from_json, DenomMetadata, DenomUnit, OwnedDeps}; fn setup() -> OwnedDeps { let mut deps = mock_dependencies(); @@ -274,12 +274,12 @@ mod tests { ); let symbols: Vec = - from_binary(&query(deps.as_ref(), mock_env(), QueryMsg::Denoms {}).unwrap()).unwrap(); + from_json(query(deps.as_ref(), mock_env(), QueryMsg::Denoms {}).unwrap()).unwrap(); assert_eq!(symbols.len(), 98); - let denom: DenomMetadata = from_binary( - &query( + let denom: DenomMetadata = from_json( + query( deps.as_ref(), mock_env(), QueryMsg::Denom { diff --git a/contracts/cyberpunk/tests/integration.rs b/contracts/cyberpunk/tests/integration.rs index 8abdc74f5..539a53eb2 100644 --- a/contracts/cyberpunk/tests/integration.rs +++ b/contracts/cyberpunk/tests/integration.rs @@ -17,7 +17,7 @@ //! }); //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) -use cosmwasm_std::{from_binary, Empty, Env, Response}; +use cosmwasm_std::{from_json, Empty, Env, Response}; use cosmwasm_vm::testing::{ execute, instantiate, mock_env, mock_info, mock_instance, mock_instance_with_gas_limit, query, }; @@ -152,13 +152,13 @@ fn test_env() { ) .unwrap(); - let received_env: Env = from_binary(&res.data.unwrap()).unwrap(); + let received_env: Env = from_json(res.data.unwrap()).unwrap(); assert_eq!(received_env, env); let env = mock_env(); let received_env: Env = - from_binary(&query(&mut deps, env.clone(), QueryMsg::MirrorEnv {}).unwrap()).unwrap(); + from_json(query(&mut deps, env.clone(), QueryMsg::MirrorEnv {}).unwrap()).unwrap(); assert_eq!(received_env, env); } diff --git a/contracts/floaty/.vscode/settings.json b/contracts/floaty/.vscode/settings.json new file mode 100644 index 000000000..11fee5dc5 --- /dev/null +++ b/contracts/floaty/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "rust-analyzer.cargo.target": "wasm32-unknown-unknown", + "rust-analyzer.cargo.extraEnv": { + "RUSTFLAGS": "-C target-feature=+nontrapping-fptoint" + } +} diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock index 10871f95b..dbc77095d 100644 --- a/contracts/floaty/Cargo.lock +++ b/contracts/floaty/Cargo.lock @@ -73,6 +73,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -572,7 +591,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -693,9 +712,9 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cosmwasm-vm", + "rand_chacha", "schemars", "serde", - "thiserror", ] [[package]] @@ -927,6 +946,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1016,6 +1044,12 @@ dependencies = [ "spki", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1084,6 +1118,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -1262,6 +1306,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1367,6 +1417,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1411,6 +1471,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1656,9 +1722,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1669,6 +1735,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1684,19 +1751,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1707,9 +1778,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1726,9 +1797,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1745,9 +1816,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1757,9 +1828,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1768,9 +1839,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1784,14 +1855,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/floaty/Cargo.toml b/contracts/floaty/Cargo.toml index 0aee91625..e78bf0017 100644 --- a/contracts/floaty/Cargo.toml +++ b/contracts/floaty/Cargo.toml @@ -35,7 +35,7 @@ cosmwasm-schema = { path = "../../packages/schema" } cosmwasm-std = { path = "../../packages/std" } schemars = "0.8.3" serde = { version = "1.0.103", default-features = false, features = ["derive"] } -thiserror = "1.0.26" +rand_chacha = { version = "0.3.1", default-features = false } [dev-dependencies] cosmwasm-vm = { path = "../../packages/vm", default-features = false, features = ["iterator"] } diff --git a/contracts/floaty/README.md b/contracts/floaty/README.md new file mode 100644 index 000000000..921205651 --- /dev/null +++ b/contracts/floaty/README.md @@ -0,0 +1,13 @@ +# Floaty Contract + +This contract contains all WebAssembly floating point instructions. It is used +for testing the floating point support. + +In order to compile it, you need a nightly version of Rust and enable the +`nontrapping-fptoint` target-feature. This allows the usage of +[some more conversion instructions](https://github.com/WebAssembly/spec/blob/main/proposals/nontrapping-float-to-int-conversion/Overview.md). +To do this, run: + +```sh +RUSTFLAGS="-C link-arg=-s -C target-feature=+nontrapping-fptoint" cargo wasm +``` diff --git a/contracts/floaty/schema/floaty.json b/contracts/floaty/schema/floaty.json index 62f697a59..cd80c1433 100644 --- a/contracts/floaty/schema/floaty.json +++ b/contracts/floaty/schema/floaty.json @@ -5,53 +5,50 @@ "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "InstantiateMsg", - "type": "object", - "required": [ - "beneficiary", - "verifier" - ], - "properties": { - "beneficiary": { - "type": "string" - }, - "verifier": { - "type": "string" - } - }, - "additionalProperties": false + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" }, - "execute": { + "execute": null, + "query": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ExecuteMsg", + "title": "QueryMsg", "oneOf": [ { - "description": "Releasing all funds in the contract to the beneficiary. This is the only \"proper\" action of this demo contract.", + "description": "Returns valid random arguments for the given instruction", "type": "object", "required": [ - "release" + "random_args_for" ], "properties": { - "release": { + "random_args_for": { "type": "object", + "required": [ + "instruction", + "seed" + ], + "properties": { + "instruction": { + "type": "string" + }, + "seed": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, "additionalProperties": false } }, "additionalProperties": false - } - ] - }, - "query": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", - "oneOf": [ + }, { - "description": "returns a human-readable representation of the verifier use to ensure query path works in integration tests", + "description": "Returns a list of all instructions", "type": "object", "required": [ - "verifier" + "instructions" ], "properties": { - "verifier": { + "instructions": { "type": "object", "additionalProperties": false } @@ -59,19 +56,26 @@ "additionalProperties": false }, { - "description": "This returns cosmwasm_std::AllBalanceResponse to demo use of the querier", + "description": "Runs the given instruction with the given arguments and returns the result", "type": "object", "required": [ - "other_balance" + "run" ], "properties": { - "other_balance": { + "run": { "type": "object", "required": [ - "address" + "args", + "instruction" ], "properties": { - "address": { + "args": { + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + "instruction": { "type": "string" } }, @@ -80,62 +84,212 @@ }, "additionalProperties": false } - ] + ], + "definitions": { + "Value": { + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + } + } }, "migrate": null, "sudo": null, "responses": { - "other_balance": { + "instructions": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "AllBalanceResponse", - "type": "object", - "required": [ - "amount" - ], - "properties": { - "amount": { - "description": "Returns all non-zero coins held by this account.", - "type": "array", - "items": { - "$ref": "#/definitions/Coin" - } - } + "title": "Array_of_String", + "type": "array", + "items": { + "type": "string" + } + }, + "random_args_for": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_Value", + "type": "array", + "items": { + "$ref": "#/definitions/Value" }, "definitions": { - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" + "Value": { + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false }, - "denom": { - "type": "string" + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false } - } - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" + ] } } }, - "verifier": { + "run": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "VerifierResponse", - "type": "object", - "required": [ - "verifier" - ], - "properties": { - "verifier": { - "type": "string" + "title": "Value", + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false } - }, - "additionalProperties": false + ] } } } diff --git a/contracts/floaty/schema/raw/execute.json b/contracts/floaty/schema/raw/execute.json index 76967fd17..bcd478d67 100644 --- a/contracts/floaty/schema/raw/execute.json +++ b/contracts/floaty/schema/raw/execute.json @@ -1,20 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ExecuteMsg", - "oneOf": [ - { - "description": "Releasing all funds in the contract to the beneficiary. This is the only \"proper\" action of this demo contract.", - "type": "object", - "required": [ - "release" - ], - "properties": { - "release": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" } diff --git a/contracts/floaty/schema/raw/instantiate.json b/contracts/floaty/schema/raw/instantiate.json index 8639103d3..5f6dfaf43 100644 --- a/contracts/floaty/schema/raw/instantiate.json +++ b/contracts/floaty/schema/raw/instantiate.json @@ -1,18 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "InstantiateMsg", - "type": "object", - "required": [ - "beneficiary", - "verifier" - ], - "properties": { - "beneficiary": { - "type": "string" - }, - "verifier": { - "type": "string" - } - }, - "additionalProperties": false + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" } diff --git a/contracts/floaty/schema/raw/query.json b/contracts/floaty/schema/raw/query.json index b96c3dfb8..376f32793 100644 --- a/contracts/floaty/schema/raw/query.json +++ b/contracts/floaty/schema/raw/query.json @@ -3,13 +3,41 @@ "title": "QueryMsg", "oneOf": [ { - "description": "returns a human-readable representation of the verifier use to ensure query path works in integration tests", + "description": "Returns valid random arguments for the given instruction", "type": "object", "required": [ - "verifier" + "random_args_for" ], "properties": { - "verifier": { + "random_args_for": { + "type": "object", + "required": [ + "instruction", + "seed" + ], + "properties": { + "instruction": { + "type": "string" + }, + "seed": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns a list of all instructions", + "type": "object", + "required": [ + "instructions" + ], + "properties": { + "instructions": { "type": "object", "additionalProperties": false } @@ -17,19 +45,26 @@ "additionalProperties": false }, { - "description": "This returns cosmwasm_std::AllBalanceResponse to demo use of the querier", + "description": "Runs the given instruction with the given arguments and returns the result", "type": "object", "required": [ - "other_balance" + "run" ], "properties": { - "other_balance": { + "run": { "type": "object", "required": [ - "address" + "args", + "instruction" ], "properties": { - "address": { + "args": { + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + "instruction": { "type": "string" } }, @@ -38,5 +73,67 @@ }, "additionalProperties": false } - ] + ], + "definitions": { + "Value": { + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + } + } } diff --git a/contracts/floaty/schema/raw/response_to_instructions.json b/contracts/floaty/schema/raw/response_to_instructions.json new file mode 100644 index 000000000..4290cb1a2 --- /dev/null +++ b/contracts/floaty/schema/raw/response_to_instructions.json @@ -0,0 +1,8 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_String", + "type": "array", + "items": { + "type": "string" + } +} diff --git a/contracts/floaty/schema/raw/response_to_random_args_for.json b/contracts/floaty/schema/raw/response_to_random_args_for.json new file mode 100644 index 000000000..4b9c97bed --- /dev/null +++ b/contracts/floaty/schema/raw/response_to_random_args_for.json @@ -0,0 +1,70 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_Value", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + }, + "definitions": { + "Value": { + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/contracts/floaty/schema/raw/response_to_run.json b/contracts/floaty/schema/raw/response_to_run.json new file mode 100644 index 000000000..08c5730ab --- /dev/null +++ b/contracts/floaty/schema/raw/response_to_run.json @@ -0,0 +1,62 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Value", + "oneOf": [ + { + "type": "object", + "required": [ + "u32" + ], + "properties": { + "u32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "u64" + ], + "properties": { + "u64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f32" + ], + "properties": { + "f32": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "f64" + ], + "properties": { + "f64": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/floaty/src/bin/schema.rs b/contracts/floaty/src/bin/schema.rs index b0a8a02b0..6a19141a5 100644 --- a/contracts/floaty/src/bin/schema.rs +++ b/contracts/floaty/src/bin/schema.rs @@ -1,11 +1,11 @@ use cosmwasm_schema::write_api; -use floaty::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cosmwasm_std::Empty; +use floaty::msg::QueryMsg; fn main() { write_api! { - instantiate: InstantiateMsg, + instantiate: Empty, query: QueryMsg, - execute: ExecuteMsg, } } diff --git a/contracts/floaty/src/contract.rs b/contracts/floaty/src/contract.rs index bff239789..9f7e8e4bf 100644 --- a/contracts/floaty/src/contract.rs +++ b/contracts/floaty/src/contract.rs @@ -1,259 +1,46 @@ use cosmwasm_std::{ - entry_point, from_slice, to_binary, to_vec, AllBalanceResponse, BankMsg, Deps, DepsMut, Env, - Event, MessageInfo, QueryResponse, Response, StdError, StdResult, + entry_point, to_json_binary, Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response, + StdResult, }; +use rand_chacha::rand_core::SeedableRng; -use crate::errors::HackError; -use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, VerifierResponse}; -use crate::state::{State, CONFIG_KEY}; +#[cfg(target_arch = "wasm32")] +use crate::instructions::run_instruction; +use crate::{ + instructions::{random_args_for, Value, FLOAT_INSTRUCTIONS}, + msg::QueryMsg, +}; #[entry_point] pub fn instantiate( - deps: DepsMut, + _deps: DepsMut, _env: Env, - info: MessageInfo, - msg: InstantiateMsg, -) -> Result { - deps.api.debug("here we go 🚀"); - - deps.storage.set( - CONFIG_KEY, - &to_vec(&State { - verifier: deps.api.addr_validate(&msg.verifier)?, - beneficiary: deps.api.addr_validate(&msg.beneficiary)?, - funder: info.sender, - })?, - ); - - // This adds some unrelated event attribute for testing purposes - Ok(Response::new().add_attribute("Let the", "hacking begin")) + _info: MessageInfo, + _msg: Empty, +) -> Result { + Ok(Response::default()) } #[entry_point] -pub fn execute( - deps: DepsMut, - env: Env, - info: MessageInfo, - _msg: ExecuteMsg, -) -> Result { - let data = deps - .storage - .get(CONFIG_KEY) - .ok_or_else(|| StdError::not_found("State"))?; - let state: State = from_slice(&data)?; - - if info.sender == state.verifier { - let to_addr = state.beneficiary; - let balance = deps.querier.query_all_balances(env.contract.address)?; - - let mut fl = balance[0].amount.u128() as f64; - fl *= 0.3; - - let resp = Response::new() - .add_attribute("action", "release") - .add_attribute("destination", to_addr.clone()) - .add_attribute("foo", fl.to_string()) - .add_event(Event::new("hackatom").add_attribute("action", "release")) - .add_message(BankMsg::Send { - to_address: to_addr.into(), - amount: balance, - }) - .set_data([0xF0, 0x0B, 0xAA]); - Ok(resp) - } else { - Err(HackError::Unauthorized {}) - } -} - -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { +pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Verifier {} => to_binary(&query_verifier(deps)?), - QueryMsg::OtherBalance { address } => to_binary(&query_other_balance(deps, address)?), + QueryMsg::RandomArgsFor { instruction, seed } => { + let mut rng = rand_chacha::ChaChaRng::seed_from_u64(seed); + to_json_binary(&random_args_for(&instruction, &mut rng)) + } + QueryMsg::Instructions {} => to_json_binary(&FLOAT_INSTRUCTIONS.to_vec()), + QueryMsg::Run { instruction, args } => to_json_binary(&query_run(&instruction, args)?), } } -fn query_verifier(deps: Deps) -> StdResult { - let data = deps - .storage - .get(CONFIG_KEY) - .ok_or_else(|| StdError::not_found("State"))?; - let state: State = from_slice(&data)?; - Ok(VerifierResponse { - verifier: state.verifier.into(), - }) -} - -fn query_other_balance(deps: Deps, address: String) -> StdResult { - let amount = deps.querier.query_all_balances(address)?; - Ok(AllBalanceResponse { amount }) -} - -#[cfg(test)] -mod tests { - use super::*; - use cosmwasm_std::testing::{ - mock_dependencies, mock_dependencies_with_balances, mock_env, mock_info, MOCK_CONTRACT_ADDR, - }; - use cosmwasm_std::Api as _; - // import trait Storage to get access to read - use cosmwasm_std::{attr, coins, Addr, Storage, SubMsg}; - - #[test] - fn proper_initialization() { - let mut deps = mock_dependencies(); - - let verifier = String::from("verifies"); - let beneficiary = String::from("benefits"); - let creator = String::from("creator"); - let expected_state = State { - verifier: deps.api.addr_validate(&verifier).unwrap(), - beneficiary: deps.api.addr_validate(&beneficiary).unwrap(), - funder: deps.api.addr_validate(&creator).unwrap(), - }; - - let msg = InstantiateMsg { - verifier, - beneficiary, - }; - let info = mock_info(creator.as_str(), &[]); - let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); - assert_eq!(res.messages.len(), 0); - assert_eq!(res.attributes.len(), 1); - assert_eq!(res.attributes[0].key, "Let the"); - assert_eq!(res.attributes[0].value, "hacking begin"); - - // it worked, let's check the state - let data = deps.storage.get(CONFIG_KEY).expect("no data stored"); - let state: State = from_slice(&data).unwrap(); - assert_eq!(state, expected_state); - } - - #[test] - fn instantiate_and_query() { - let mut deps = mock_dependencies(); - - let verifier = String::from("verifies"); - let beneficiary = String::from("benefits"); - let creator = String::from("creator"); - let msg = InstantiateMsg { - verifier: verifier.clone(), - beneficiary, - }; - let info = mock_info(&creator, &[]); - let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // now let's query - let query_response = query_verifier(deps.as_ref()).unwrap(); - assert_eq!(query_response.verifier, verifier); - } - - #[test] - fn querier_callbacks_work() { - let rich_addr = String::from("foobar"); - let rich_balance = coins(10000, "gold"); - let deps = mock_dependencies_with_balances(&[(&rich_addr, &rich_balance)]); - - // querying with balance gets the balance - let bal = query_other_balance(deps.as_ref(), rich_addr).unwrap(); - assert_eq!(bal.amount, rich_balance); - - // querying other accounts gets none - let bal = query_other_balance(deps.as_ref(), String::from("someone else")).unwrap(); - assert_eq!(bal.amount, vec![]); - } - - #[test] - fn execute_release_works() { - let mut deps = mock_dependencies(); - - // initialize the store - let creator = String::from("creator"); - let verifier = String::from("verifies"); - let beneficiary = String::from("benefits"); - - let instantiate_msg = InstantiateMsg { - verifier: verifier.clone(), - beneficiary: beneficiary.clone(), - }; - let init_amount = coins(1000, "earth"); - let init_info = mock_info(&creator, &init_amount); - let init_res = instantiate(deps.as_mut(), mock_env(), init_info, instantiate_msg).unwrap(); - assert_eq!(init_res.messages.len(), 0); - - // balance changed in init - deps.querier.update_balance(MOCK_CONTRACT_ADDR, init_amount); - - // beneficiary can release it - let execute_info = mock_info(verifier.as_str(), &[]); - let execute_res = execute( - deps.as_mut(), - mock_env(), - execute_info, - ExecuteMsg::Release {}, - ) - .unwrap(); - assert_eq!(execute_res.messages.len(), 1); - let msg = execute_res.messages.get(0).expect("no message"); - assert_eq!( - msg, - &SubMsg::new(BankMsg::Send { - to_address: beneficiary, - amount: coins(1000, "earth"), - }), - ); - assert_eq!( - execute_res.attributes, - vec![ - attr("action", "release"), - attr("destination", "benefits"), - attr("foo", "300") - ], - ); - assert_eq!(execute_res.data, Some(vec![0xF0, 0x0B, 0xAA].into())); - } - - #[test] - fn execute_release_fails_for_wrong_sender() { - let mut deps = mock_dependencies(); - - // initialize the store - let creator = String::from("creator"); - let verifier = String::from("verifies"); - let beneficiary = String::from("benefits"); - - let instantiate_msg = InstantiateMsg { - verifier: verifier.clone(), - beneficiary: beneficiary.clone(), - }; - let init_amount = coins(1000, "earth"); - let init_info = mock_info(&creator, &init_amount); - let init_res = instantiate(deps.as_mut(), mock_env(), init_info, instantiate_msg).unwrap(); - assert_eq!(init_res.messages.len(), 0); - - // balance changed in init - deps.querier.update_balance(MOCK_CONTRACT_ADDR, init_amount); - - // beneficiary cannot release it - let execute_info = mock_info(beneficiary.as_str(), &[]); - let execute_res = execute( - deps.as_mut(), - mock_env(), - execute_info, - ExecuteMsg::Release {}, - ); - assert_eq!(execute_res.unwrap_err(), HackError::Unauthorized {}); +#[cfg_attr(not(target_arch = "wasm32"), allow(unused_variables))] +fn query_run(instruction: &str, args: Vec) -> StdResult { + #[cfg(not(target_arch = "wasm32"))] + panic!(); - // state should not change - let data = deps.storage.get(CONFIG_KEY).expect("no data stored"); - let state: State = from_slice(&data).unwrap(); - assert_eq!( - state, - State { - verifier: Addr::unchecked(verifier), - beneficiary: Addr::unchecked(beneficiary), - funder: Addr::unchecked(creator), - } - ); + #[cfg(target_arch = "wasm32")] + { + let result = run_instruction(instruction, &args); + Ok(result) } } diff --git a/contracts/floaty/src/errors.rs b/contracts/floaty/src/errors.rs deleted file mode 100644 index 7b46b00b6..000000000 --- a/contracts/floaty/src/errors.rs +++ /dev/null @@ -1,12 +0,0 @@ -use cosmwasm_std::StdError; -use thiserror::Error; - -#[derive(Error, Debug, PartialEq)] -pub enum HackError { - #[error("{0}")] - /// this is needed so we can use `bucket.load(...)?` and have it auto-converted to the custom error - Std(#[from] StdError), - // this is whatever we want - #[error("Unauthorized")] - Unauthorized {}, -} diff --git a/contracts/floaty/src/floats.rs b/contracts/floaty/src/floats.rs new file mode 100644 index 000000000..ccbb06ade --- /dev/null +++ b/contracts/floaty/src/floats.rs @@ -0,0 +1,212 @@ +use rand_chacha::rand_core::RngCore; + +pub const INF_32: u32 = 0x7f800000; +pub const NEG_INF_32: u32 = 0xff800000; +pub const INF_64: u64 = 0x7ff0000000000000; +pub const NEG_INF_64: u64 = 0xfff0000000000000; + +const EXPONENT_MASK_32: u32 = 0x7f800000; +const EXPONENT_MASK_64: u64 = 0x7ff0000000000000; +const SIGN_MASK_32: u32 = 0x80000000; +const SIGN_MASK_64: u64 = 0x8000000000000000; +const MANTISSA_MASK_32: u32 = 0x007fffff; +const MANTISSA_MASK_64: u64 = 0x000fffffffffffff; + +/// Returns a random `f32`. +/// +/// We want to cover all classes of floats: NaNs, subnormals, infinities, and normal floats. +/// Because of that, we don't just generate a random `u32` and convert it to an `f32` +/// (that would make e.g. infinities highly unlikely) +/// Instead, we give each of these classes a probability of 25% and +/// then generate a random pattern within that class +pub fn random_f32(rng: &mut impl RngCore) -> f32 { + let decider = rng.next_u32(); + let bits = match decider % 4 { + 0 => { + // 25% chance of being a NaN + random_nan_32(rng) + } + 1 => { + // 25% chance of being a subnormal + random_subnormal_32(rng) + } + 2 => { + // 25% chance of being an infinite + if decider % 2 == 0 { + INF_32 + } else { + NEG_INF_32 + } + } + 3 => { + // 25% chance of being a random bit pattern + rng.next_u32() + } + _ => unreachable!(), + }; + f32::from_bits(bits) +} + +/// Returns a random `f64`. +/// +/// See [`random_f32`] for more details. +pub fn random_f64(rng: &mut impl RngCore) -> f64 { + let decider = rng.next_u64(); + let bits = match decider % 4 { + 0 => { + // 25% chance of being a NaN + random_nan_64(rng) + } + 1 => { + // 25% chance of being a subnormal + random_subnormal_64(rng) + } + 2 => { + // 25% chance of being an infinite + if decider % 2 == 0 { + INF_64 + } else { + NEG_INF_64 + } + } + 3 => { + // 25% chance of being a random bit pattern + rng.next_u64() + } + _ => unreachable!(), + }; + f64::from_bits(bits) +} + +/// Returns bits for a random NaN +pub fn random_nan_32(rng: &mut impl RngCore) -> u32 { + let mut rnd = rng.next_u32(); + if rnd == 0 { + // we don't want to return an infinity, so we just set the last bit to 1 + rnd = 1; + } + // Set the exponent to all 1s and remaining bits random + EXPONENT_MASK_32 | rnd +} + +/// Returns bits for a random NaN +pub fn random_nan_64(rng: &mut impl RngCore) -> u64 { + let mut rnd = rng.next_u64(); + if rnd == 0 { + // we don't want to return an infinity, so we just set the last bit to 1 + rnd = 1; + } + // Set the exponent to all 1s and remaining bits random + EXPONENT_MASK_64 | rnd +} + +/// Returns bits for a random subnormal +pub fn random_subnormal_32(rng: &mut impl RngCore) -> u32 { + // Set the exponent to all 0s and remaining bits random + let res = rng.next_u32() & (SIGN_MASK_32 | MANTISSA_MASK_32); + + if res == 0 { + // we don't want to return a zero, so we just return a fixed subnormal + SIGN_MASK_32 | MANTISSA_MASK_32 + } else { + res + } +} + +/// Returns bits for a random subnormal +pub fn random_subnormal_64(rng: &mut impl RngCore) -> u64 { + // Set the exponent to all 0s and remaining bits random + let res = rng.next_u64() & (SIGN_MASK_64 | MANTISSA_MASK_64); + + if res == 0 { + // we don't want to return a zero, so we just return a fixed subnormal + SIGN_MASK_64 | MANTISSA_MASK_64 + } else { + res + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Class { + Normal, + Subnormal, + Zero, + Infinite, + NaN, +} + +pub trait Classifier { + fn classify(&self) -> Class; +} + +impl Classifier for u32 { + fn classify(&self) -> Class { + let exponent = self & EXPONENT_MASK_32; + let mantissa = self & MANTISSA_MASK_32; + + match (exponent, mantissa) { + (0, 0) => Class::Zero, + (0, _) => Class::Subnormal, + (EXPONENT_MASK_32, 0) => Class::Infinite, + (EXPONENT_MASK_32, _) => Class::NaN, + _ => Class::Normal, + } + } +} + +impl Classifier for u64 { + fn classify(&self) -> Class { + let exponent = self & EXPONENT_MASK_64; + let mantissa = self & MANTISSA_MASK_64; + + match (exponent, mantissa) { + (0, 0) => Class::Zero, + (0, _) => Class::Subnormal, + (EXPONENT_MASK_64, 0) => Class::Infinite, + (EXPONENT_MASK_64, _) => Class::NaN, + _ => Class::Normal, + } + } +} + +#[cfg(test)] +mod tests { + use rand_chacha::rand_core::SeedableRng; + + use super::*; + + #[test] + fn test_constants() { + assert_eq!(INF_32, f32::INFINITY.to_bits()); + assert_eq!(NEG_INF_32, f32::NEG_INFINITY.to_bits()); + assert_eq!(INF_64, f64::INFINITY.to_bits()); + assert_eq!(NEG_INF_64, f64::NEG_INFINITY.to_bits()); + } + + #[test] + fn test_classify() { + // for 32-bit floats + assert_eq!((-0f32).to_bits().classify(), Class::Zero); + assert_eq!(0u32.classify(), Class::Zero); + assert_eq!(1f32.to_bits().classify(), Class::Normal); + assert_eq!(INF_32.classify(), Class::Infinite); + assert_eq!(NEG_INF_32.classify(), Class::Infinite); + + // for 64-bit floats + assert_eq!((-0f64).to_bits().classify(), Class::Zero); + assert_eq!(0u64.classify(), Class::Zero); + assert_eq!(1f64.to_bits().classify(), Class::Normal); + assert_eq!(INF_64.classify(), Class::Infinite); + assert_eq!(NEG_INF_64.classify(), Class::Infinite); + + // random floats + let mut rng = rand_chacha::ChaChaRng::seed_from_u64(123456); + for _ in 0..1000 { + assert_eq!(random_subnormal_32(&mut rng).classify(), Class::Subnormal); + assert_eq!(random_nan_32(&mut rng).classify(), Class::NaN); + + assert_eq!(random_subnormal_64(&mut rng).classify(), Class::Subnormal); + assert_eq!(random_nan_64(&mut rng).classify(), Class::NaN); + } + } +} diff --git a/contracts/floaty/src/instructions.rs b/contracts/floaty/src/instructions.rs new file mode 100644 index 000000000..d26433085 --- /dev/null +++ b/contracts/floaty/src/instructions.rs @@ -0,0 +1,555 @@ +use cosmwasm_schema::cw_serde; +use rand_chacha::rand_core::RngCore; + +use crate::floats::{random_f32, random_f64}; + +/// Not intended for direct usage +#[cfg_attr(not(target_arch = "wasm32"), allow(unused_macros))] +macro_rules! run_instr { + ($instr:expr, $input:expr, $input_ty:ty, $return_ty:ty) => {{ + let input: $input_ty = $input; + let ret: $return_ty; + unsafe { + core::arch::asm!("local.get {0}", $instr, "local.set {1}", in(local) input, out(local) ret) + }; + ret + }}; + ($instr:expr, $input1:expr, $input1_ty:ty, $input2:expr, $input2_ty:ty, $return_ty:ty) => {{ + let input1: $input1_ty = $input1; + let input2: $input2_ty = $input2; + let ret: $return_ty; + unsafe { + core::arch::asm!("local.get {0}", "local.get {1}", $instr, "local.set {2}", in(local) input1, in(local) input2, out(local) ret) + }; + ret + }}; +} +#[cfg_attr(not(target_arch = "wasm32"), allow(unused_imports))] +pub(crate) use run_instr; + +/// Helper to run a single WebAssembly instruction in a type-safe way +#[cfg_attr(not(target_arch = "wasm32"), allow(unused_macros))] +macro_rules! run { + ("f32.eq", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.eq", $input1, f32, $input2, f32, u32) + }; + ("f32.ne", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.ne", $input1, f32, $input2, f32, u32) + }; + ("f32.lt", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.lt", $input1, f32, $input2, f32, u32) + }; + ("f32.gt", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.gt", $input1, f32, $input2, f32, u32) + }; + ("f32.le", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.le", $input1, f32, $input2, f32, u32) + }; + ("f32.ge", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.ge", $input1, f32, $input2, f32, u32) + }; + ("f64.eq", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.eq", $input1, f64, $input2, f64, u32) + }; + ("f64.ne", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.ne", $input1, f64, $input2, f64, u32) + }; + ("f64.lt", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.lt", $input1, f64, $input2, f64, u32) + }; + ("f64.gt", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.gt", $input1, f64, $input2, f64, u32) + }; + ("f64.le", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.le", $input1, f64, $input2, f64, u32) + }; + ("f64.ge", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.ge", $input1, f64, $input2, f64, u32) + }; + // + ("f32.abs", $input:expr) => { + $crate::instructions::run_instr!("f32.abs", $input, f32, f32) + }; + ("f32.neg", $input:expr) => { + $crate::instructions::run_instr!("f32.neg", $input, f32, f32) + }; + ("f32.ceil", $input:expr) => { + $crate::instructions::run_instr!("f32.ceil", $input, f32, f32) + }; + ("f32.floor", $input:expr) => { + $crate::instructions::run_instr!("f32.floor", $input, f32, f32) + }; + ("f32.trunc", $input:expr) => { + $crate::instructions::run_instr!("f32.trunc", $input, f32, f32) + }; + ("f32.nearest", $input:expr) => { + $crate::instructions::run_instr!("f32.nearest", $input, f32, f32) + }; + ("f32.sqrt", $input:expr) => { + $crate::instructions::run_instr!("f32.sqrt", $input, f32, f32) + }; + ("f32.add", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.add", $input1, f32, $input2, f32, f32) + }; + ("f32.sub", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.sub", $input1, f32, $input2, f32, f32) + }; + ("f32.mul", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.mul", $input1, f32, $input2, f32, f32) + }; + ("f32.div", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.div", $input1, f32, $input2, f32, f32) + }; + ("f32.min", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.min", $input1, f32, $input2, f32, f32) + }; + ("f32.max", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.max", $input1, f32, $input2, f32, f32) + }; + ("f32.copysign", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f32.copysign", $input1, f32, $input2, f32, f32) + }; + ("f64.abs", $input:expr) => { + $crate::instructions::run_instr!("f64.abs", $input, f64, f64) + }; + ("f64.neg", $input:expr) => { + $crate::instructions::run_instr!("f64.neg", $input, f64, f64) + }; + ("f64.ceil", $input:expr) => { + $crate::instructions::run_instr!("f64.ceil", $input, f64, f64) + }; + ("f64.floor", $input:expr) => { + $crate::instructions::run_instr!("f64.floor", $input, f64, f64) + }; + ("f64.trunc", $input:expr) => { + $crate::instructions::run_instr!("f64.trunc", $input, f64, f64) + }; + ("f64.nearest", $input:expr) => { + $crate::instructions::run_instr!("f64.nearest", $input, f64, f64) + }; + ("f64.sqrt", $input:expr) => { + $crate::instructions::run_instr!("f64.sqrt", $input, f64, f64) + }; + ("f64.add", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.add", $input1, f64, $input2, f64, f64) + }; + ("f64.sub", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.sub", $input1, f64, $input2, f64, f64) + }; + ("f64.mul", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.mul", $input1, f64, $input2, f64, f64) + }; + ("f64.div", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.div", $input1, f64, $input2, f64, f64) + }; + ("f64.min", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.min", $input1, f64, $input2, f64, f64) + }; + ("f64.max", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.max", $input1, f64, $input2, f64, f64) + }; + ("f64.copysign", $input1:expr, $input2:expr) => { + $crate::instructions::run_instr!("f64.copysign", $input1, f64, $input2, f64, f64) + }; + // + ("i32.trunc_f32_s", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_f32_s", $input, f32, i32) + }; + ("i32.trunc_f32_u", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_f32_u", $input, f32, u32) + }; + ("i32.trunc_f64_s", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_f64_s", $input, f64, i32) + }; + ("i32.trunc_f64_u", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_f64_u", $input, f64, u32) + }; + // + ("i64.trunc_f32_s", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_f32_s", $input, f32, i64) + }; + ("i64.trunc_f32_u", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_f32_u", $input, f32, u64) + }; + ("i64.trunc_f64_s", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_f64_s", $input, f64, i64) + }; + ("i64.trunc_f64_u", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_f64_u", $input, f64, u64) + }; + // + ("f32.convert_i32_s", $input:expr) => { + $crate::instructions::run_instr!("f32.convert_i32_s", $input, i32, f32) + }; + ("f32.convert_i32_u", $input:expr) => { + $crate::instructions::run_instr!("f32.convert_i32_u", $input, u32, f32) + }; + ("f32.convert_i64_s", $input:expr) => { + $crate::instructions::run_instr!("f32.convert_i64_s", $input, i64, f32) + }; + ("f32.convert_i64_u", $input:expr) => { + $crate::instructions::run_instr!("f32.convert_i64_u", $input, u64, f32) + }; + ("f32.demote_f64", $input:expr) => { + $crate::instructions::run_instr!("f32.demote_f64", $input, f64, f32) + }; + ("f64.convert_i32_s", $input:expr) => { + $crate::instructions::run_instr!("f64.convert_i32_s", $input, i32, f64) + }; + ("f64.convert_i32_u", $input:expr) => { + $crate::instructions::run_instr!("f64.convert_i32_u", $input, u32, f64) + }; + ("f64.convert_i64_s", $input:expr) => { + $crate::instructions::run_instr!("f64.convert_i64_s", $input, i64, f64) + }; + ("f64.convert_i64_u", $input:expr) => { + $crate::instructions::run_instr!("f64.convert_i64_u", $input, u64, f64) + }; + ("f64.promote_f32", $input:expr) => { + $crate::instructions::run_instr!("f64.promote_f32", $input, f32, f64) + }; + // + ("i32.reinterpret_f32", $input:expr) => { + $crate::instructions::run_instr!("i32.reinterpret_f32", $input, f32, i32) + }; + ("i64.reinterpret_f64", $input:expr) => { + $crate::instructions::run_instr!("i64.reinterpret_f64", $input, f64, i64) + }; + ("f32.reinterpret_i32", $input:expr) => { + $crate::instructions::run_instr!("f32.reinterpret_i32", $input, u32, f32) + }; + ("f64.reinterpret_i64", $input:expr) => { + $crate::instructions::run_instr!("f64.reinterpret_i64", $input, u64, f64) + }; + // + ("i32.trunc_sat_f32_s", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_sat_f32_s", $input, f32, i32) + }; + ("i32.trunc_sat_f32_u", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_sat_f32_u", $input, f32, u32) + }; + ("i32.trunc_sat_f64_s", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_sat_f64_s", $input, f64, i32) + }; + ("i32.trunc_sat_f64_u", $input:expr) => { + $crate::instructions::run_instr!("i32.trunc_sat_f64_u", $input, f64, u32) + }; + ("i64.trunc_sat_f32_s", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_sat_f32_s", $input, f32, i64) + }; + ("i64.trunc_sat_f32_u", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_sat_f32_u", $input, f32, u64) + }; + ("i64.trunc_sat_f64_s", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_sat_f64_s", $input, f64, i64) + }; + ("i64.trunc_sat_f64_u", $input:expr) => { + $crate::instructions::run_instr!("i64.trunc_sat_f64_u", $input, f64, u64) + }; +} +#[cfg_attr(not(target_arch = "wasm32"), allow(unused_imports))] +pub(crate) use run; + +#[cw_serde] +pub enum Value { + U32(u32), + U64(u64), + F32(u32), + F64(u64), +} + +impl Value { + pub fn u32(&self) -> u32 { + match self { + Self::U32(x) => *x, + v => panic!("expected u32, got {:?}", v), + } + } + + pub fn u64(&self) -> u64 { + match self { + Self::U64(x) => *x, + v => panic!("expected u64, got {:?}", v), + } + } + + pub fn f32(&self) -> f32 { + match self { + Self::F32(x) => f32::from_bits(*x), + v => panic!("expected f32, got {:?}", v), + } + } + + pub fn f64(&self) -> f64 { + match self { + Self::F64(x) => f64::from_bits(*x), + v => panic!("expected f64, got {:?}", v), + } + } +} + +/// Runs the given instruction with random inputs +#[cfg(target_arch = "wasm32")] +pub fn run_instruction(instr: &str, args: &[Value]) -> Value { + use Value::*; + + let arg1 = || args.get(0).unwrap(); + let arg2 = || args.get(0).unwrap(); + + match instr { + "f32.eq" => U32(run!("f32.eq", arg1().f32(), arg2().f32())), + "f32.ne" => U32(run!("f32.ne", arg1().f32(), arg2().f32())), + "f32.lt" => U32(run!("f32.lt", arg1().f32(), arg2().f32())), + "f32.gt" => U32(run!("f32.gt", arg1().f32(), arg2().f32())), + "f32.le" => U32(run!("f32.le", arg1().f32(), arg2().f32())), + "f32.ge" => U32(run!("f32.ge", arg1().f32(), arg2().f32())), + "f64.eq" => U32(run!("f64.eq", arg1().f64(), arg2().f64())), + "f64.ne" => U32(run!("f64.ne", arg1().f64(), arg2().f64())), + "f64.lt" => U32(run!("f64.lt", arg1().f64(), arg2().f64())), + "f64.gt" => U32(run!("f64.gt", arg1().f64(), arg2().f64())), + "f64.le" => U32(run!("f64.le", arg1().f64(), arg2().f64())), + "f64.ge" => U32(run!("f64.ge", arg1().f64(), arg2().f64())), + // + "f32.abs" => U32(run!("f32.abs", arg1().f32()).to_bits()), + "f32.neg" => U32(run!("f32.neg", arg1().f32()).to_bits()), + "f32.ceil" => U32(run!("f32.ceil", arg1().f32()).to_bits()), + "f32.floor" => U32(run!("f32.floor", arg1().f32()).to_bits()), + "f32.trunc" => U32(run!("f32.trunc", arg1().f32()).to_bits()), + "f32.nearest" => U32(run!("f32.nearest", arg1().f32()).to_bits()), + "f32.sqrt" => U32(run!("f32.sqrt", arg1().f32()).to_bits()), + "f32.add" => U32(run!("f32.add", arg1().f32(), arg2().f32()).to_bits()), + "f32.sub" => U32(run!("f32.sub", arg1().f32(), arg2().f32()).to_bits()), + "f32.mul" => U32(run!("f32.mul", arg1().f32(), arg2().f32()).to_bits()), + "f32.div" => U32(run!("f32.div", arg1().f32(), arg2().f32()).to_bits()), + "f32.min" => U32(run!("f32.min", arg1().f32(), arg2().f32()).to_bits()), + "f32.max" => U32(run!("f32.max", arg1().f32(), arg2().f32()).to_bits()), + "f32.copysign" => U32(run!("f32.copysign", arg1().f32(), arg2().f32()).to_bits()), + "f64.abs" => U64(run!("f64.abs", arg1().f64()).to_bits()), + "f64.neg" => U64(run!("f64.neg", arg1().f64()).to_bits()), + "f64.ceil" => U64(run!("f64.ceil", arg1().f64()).to_bits()), + "f64.floor" => U64(run!("f64.floor", arg1().f64()).to_bits()), + "f64.trunc" => U64(run!("f64.trunc", arg1().f64()).to_bits()), + "f64.nearest" => U64(run!("f64.nearest", arg1().f64()).to_bits()), + "f64.sqrt" => U64(run!("f64.sqrt", arg1().f64()).to_bits()), + "f64.add" => U64(run!("f64.add", arg1().f64(), arg2().f64()).to_bits()), + "f64.sub" => U64(run!("f64.sub", arg1().f64(), arg2().f64()).to_bits()), + "f64.mul" => U64(run!("f64.mul", arg1().f64(), arg2().f64()).to_bits()), + "f64.div" => U64(run!("f64.div", arg1().f64(), arg2().f64()).to_bits()), + "f64.min" => U64(run!("f64.min", arg1().f64(), arg2().f64()).to_bits()), + "f64.max" => U64(run!("f64.max", arg1().f64(), arg2().f64()).to_bits()), + "f64.copysign" => U64(run!("f64.copysign", arg1().f64(), arg2().f64()).to_bits()), + // + "i32.trunc_f32_s" => U32(run!("i32.trunc_f32_s", arg1().f32()) as u32), + "i32.trunc_f32_u" => U32(run!("i32.trunc_f32_u", arg1().f32())), + "i32.trunc_f64_s" => U32(run!("i32.trunc_f64_s", arg1().f64()) as u32), + "i32.trunc_f64_u" => U32(run!("i32.trunc_f64_u", arg1().f64())), + // + "i64.trunc_f32_s" => U64(run!("i64.trunc_f32_s", arg1().f32()) as u64), + "i64.trunc_f32_u" => U64(run!("i64.trunc_f32_u", arg1().f32())), + "i64.trunc_f64_s" => U64(run!("i64.trunc_f64_s", arg1().f64()) as u64), + "i64.trunc_f64_u" => U64(run!("i64.trunc_f64_u", arg1().f64())), + // + "f32.convert_i32_s" => U32(run!("f32.convert_i32_s", arg1().u32() as i32).to_bits()), + "f32.convert_i32_u" => U32(run!("f32.convert_i32_u", arg1().u32()).to_bits()), + "f32.convert_i64_s" => U32(run!("f32.convert_i64_s", arg1().u64() as i64).to_bits()), + "f32.convert_i64_u" => U32(run!("f32.convert_i64_u", arg1().u64()).to_bits()), + "f32.demote_f64" => U32(run!("f32.demote_f64", arg1().f64()).to_bits()), + "f64.convert_i32_s" => U64(run!("f64.convert_i32_s", arg1().u32() as i32).to_bits()), + "f64.convert_i32_u" => U64(run!("f64.convert_i32_u", arg1().u32()).to_bits()), + "f64.convert_i64_s" => U64(run!("f64.convert_i64_s", arg1().u64() as i64).to_bits()), + "f64.convert_i64_u" => U64(run!("f64.convert_i64_u", arg1().u64()).to_bits()), + "f64.promote_f32" => U64(run!("f64.promote_f32", arg1().f32()).to_bits()), + // + "i32.reinterpret_f32" => U32(run!("i32.reinterpret_f32", arg1().f32()) as u32), + "i64.reinterpret_f64" => U64(run!("i64.reinterpret_f64", arg1().f64()) as u64), + "f32.reinterpret_i32" => U32(run!("f32.reinterpret_i32", arg1().u32()).to_bits() as u32), + "f64.reinterpret_i64" => U64(run!("f64.reinterpret_i64", arg1().u64()).to_bits() as u64), + // + "i32.trunc_sat_f32_s" => U32(run!("i32.trunc_sat_f32_s", arg1().f32()) as u32), + "i32.trunc_sat_f32_u" => U32(run!("i32.trunc_sat_f32_u", arg1().f32()) as u32), + "i32.trunc_sat_f64_s" => U32(run!("i32.trunc_sat_f64_s", arg1().f64()) as u32), + "i32.trunc_sat_f64_u" => U32(run!("i32.trunc_sat_f64_u", arg1().f64()) as u32), + "i64.trunc_sat_f32_s" => U64(run!("i64.trunc_sat_f32_s", arg1().f32()) as u64), + "i64.trunc_sat_f32_u" => U64(run!("i64.trunc_sat_f32_u", arg1().f32()) as u64), + "i64.trunc_sat_f64_s" => U64(run!("i64.trunc_sat_f64_s", arg1().f64()) as u64), + "i64.trunc_sat_f64_u" => U64(run!("i64.trunc_sat_f64_u", arg1().f64()) as u64), + _ => panic!("unknown instruction: {}", instr), + } +} + +pub fn random_args_for(instr: &str, rng: &mut impl RngCore) -> Vec { + let a = random_f32(rng); + let b = random_f32(rng); + let c = random_f64(rng); + let d = random_f64(rng); + let e = rng.next_u32(); + let f = rng.next_u64(); + + use Value::*; + + let f32x2 = vec![F32(a.to_bits()), F32(b.to_bits())]; + let f64x2 = vec![F64(c.to_bits()), F64(d.to_bits())]; + let f32 = vec![F32(a.to_bits())]; + let f64 = vec![F64(c.to_bits())]; + let u32 = vec![U32(e)]; + let u64 = vec![U64(f)]; + + match instr { + "f32.eq" => f32x2, + "f32.ne" => f32x2, + "f32.lt" => f32x2, + "f32.gt" => f32x2, + "f32.le" => f32x2, + "f32.ge" => f32x2, + "f64.eq" => f64x2, + "f64.ne" => f64x2, + "f64.lt" => f64x2, + "f64.gt" => f64x2, + "f64.le" => f64x2, + "f64.ge" => f64x2, + // + "f32.abs" => f32, + "f32.neg" => f32, + "f32.ceil" => f32, + "f32.floor" => f32, + "f32.trunc" => f32, + "f32.nearest" => f32, + "f32.sqrt" => f32, + "f32.add" => f32x2, + "f32.sub" => f32x2, + "f32.mul" => f32x2, + "f32.div" => f32x2, + "f32.min" => f32x2, + "f32.max" => f32x2, + "f32.copysign" => f32x2, + "f64.abs" => f64, + "f64.neg" => f64, + "f64.ceil" => f64, + "f64.floor" => f64, + "f64.trunc" => f64, + "f64.nearest" => f64, + "f64.sqrt" => f64, + "f64.add" => f64x2, + "f64.sub" => f64x2, + "f64.mul" => f64x2, + "f64.div" => f64x2, + "f64.min" => f64x2, + "f64.max" => f64x2, + "f64.copysign" => f64x2, + // + "i32.trunc_f32_s" => f32, + "i32.trunc_f32_u" => f32, + "i32.trunc_f64_s" => f64, + "i32.trunc_f64_u" => f64, + // + "i64.trunc_f32_s" => f32, + "i64.trunc_f32_u" => f32, + "i64.trunc_f64_s" => f64, + "i64.trunc_f64_u" => f64, + // + "f32.convert_i32_s" => u32, + "f32.convert_i32_u" => u32, + "f32.convert_i64_s" => u64, + "f32.convert_i64_u" => u64, + "f32.demote_f64" => f64, + "f64.convert_i32_s" => u32, + "f64.convert_i32_u" => u32, + "f64.convert_i64_s" => u64, + "f64.convert_i64_u" => u64, + "f64.promote_f32" => f32, + // + "i32.reinterpret_f32" => f32, + "i64.reinterpret_f64" => f64, + "f32.reinterpret_i32" => u32, + "f64.reinterpret_i64" => u64, + // + "i32.trunc_sat_f32_s" => f32, + "i32.trunc_sat_f32_u" => f32, + "i32.trunc_sat_f64_s" => f64, + "i32.trunc_sat_f64_u" => f64, + "i64.trunc_sat_f32_s" => f32, + "i64.trunc_sat_f32_u" => f32, + "i64.trunc_sat_f64_s" => f64, + "i64.trunc_sat_f64_u" => f64, + _ => panic!("unknown instruction: {}", instr), + } +} + +pub const FLOAT_INSTRUCTIONS: [&str; 70] = [ + "f32.eq", + "f32.ne", + "f32.lt", + "f32.gt", + "f32.le", + "f32.ge", + "f64.eq", + "f64.ne", + "f64.lt", + "f64.gt", + "f64.le", + "f64.ge", + // + "f32.abs", + "f32.neg", + "f32.ceil", + "f32.floor", + "f32.trunc", + "f32.nearest", + "f32.sqrt", + "f32.add", + "f32.sub", + "f32.mul", + "f32.div", + "f32.min", + "f32.max", + "f32.copysign", + "f64.abs", + "f64.neg", + "f64.ceil", + "f64.floor", + "f64.trunc", + "f64.nearest", + "f64.sqrt", + "f64.add", + "f64.sub", + "f64.mul", + "f64.div", + "f64.min", + "f64.max", + "f64.copysign", + // + "i32.trunc_f32_s", + "i32.trunc_f32_u", + "i32.trunc_f64_s", + "i32.trunc_f64_u", + // + "i64.trunc_f32_s", + "i64.trunc_f32_u", + "i64.trunc_f64_s", + "i64.trunc_f64_u", + // + "f32.convert_i32_s", + "f32.convert_i32_u", + "f32.convert_i64_s", + "f32.convert_i64_u", + "f32.demote_f64", + "f64.convert_i32_s", + "f64.convert_i32_u", + "f64.convert_i64_s", + "f64.convert_i64_u", + "f64.promote_f32", + // + "i32.reinterpret_f32", + "i64.reinterpret_f64", + "f32.reinterpret_i32", + "f64.reinterpret_i64", + // + "i32.trunc_sat_f32_s", + "i32.trunc_sat_f32_u", + "i32.trunc_sat_f64_s", + "i32.trunc_sat_f64_u", + "i64.trunc_sat_f32_s", + "i64.trunc_sat_f32_u", + "i64.trunc_sat_f64_s", + "i64.trunc_sat_f64_u", +]; diff --git a/contracts/floaty/src/lib.rs b/contracts/floaty/src/lib.rs index 0573cc96c..ba67016d3 100644 --- a/contracts/floaty/src/lib.rs +++ b/contracts/floaty/src/lib.rs @@ -1,4 +1,7 @@ +#![cfg_attr(target_arch = "wasm32", feature(asm_experimental_arch))] + pub mod contract; -mod errors; +pub(crate) mod floats; +mod instructions; pub mod msg; pub mod state; diff --git a/contracts/floaty/src/msg.rs b/contracts/floaty/src/msg.rs index caa8fc4e3..6baa9a1ef 100644 --- a/contracts/floaty/src/msg.rs +++ b/contracts/floaty/src/msg.rs @@ -1,30 +1,26 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -#[cw_serde] -pub struct InstantiateMsg { - pub verifier: String, - pub beneficiary: String, -} +use crate::instructions::Value; #[cw_serde] -pub enum ExecuteMsg { - /// Releasing all funds in the contract to the beneficiary. This is the only "proper" action of this demo contract. - Release {}, +pub enum ValueType { + Float, + Int, } #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { - /// returns a human-readable representation of the verifier - /// use to ensure query path works in integration tests - #[returns(VerifierResponse)] - Verifier {}, - /// This returns cosmwasm_std::AllBalanceResponse to demo use of the querier - #[returns(cosmwasm_std::AllBalanceResponse)] - OtherBalance { address: String }, -} - -#[cw_serde] -pub struct VerifierResponse { - pub verifier: String, + /// Returns valid random arguments for the given instruction + #[returns(Vec)] + RandomArgsFor { instruction: String, seed: u64 }, + /// Returns a list of all instructions + #[returns(Vec)] + Instructions {}, + /// Runs the given instruction with the given arguments and returns the result + #[returns(Value)] + Run { + instruction: String, + args: Vec, + }, } diff --git a/contracts/floaty/tests/integration.rs b/contracts/floaty/tests/integration.rs index 5d3de04e5..593ae1d1e 100644 --- a/contracts/floaty/tests/integration.rs +++ b/contracts/floaty/tests/integration.rs @@ -6,7 +6,6 @@ static WASM: &[u8] = include_bytes!("../target/wasm32-unknown-unknown/release/fl // static WASM: &[u8] = include_bytes!("../contract.wasm"); #[test] -#[should_panic(expected = "Float operator detected")] -fn instantiate_fails() { - let mut _deps = mock_instance(WASM, &[]); +fn validation_succeeds() { + mock_instance(WASM, &[]); } diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock index ca1919229..7f95aa7ef 100644 --- a/contracts/hackatom/Cargo.lock +++ b/contracts/hackatom/Cargo.lock @@ -73,6 +73,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -572,7 +591,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -928,6 +947,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1263,6 +1291,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1368,6 +1402,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1412,6 +1456,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1657,9 +1707,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1670,6 +1720,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1685,19 +1736,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1708,9 +1763,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1727,9 +1782,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1746,9 +1801,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1758,9 +1813,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1769,9 +1824,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1785,14 +1840,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/hackatom/src/contract.rs b/contracts/hackatom/src/contract.rs index 6de12ab53..f62ffcb31 100644 --- a/contracts/hackatom/src/contract.rs +++ b/contracts/hackatom/src/contract.rs @@ -1,7 +1,7 @@ use sha2::{Digest, Sha256}; use cosmwasm_std::{ - entry_point, from_slice, to_binary, to_vec, Addr, AllBalanceResponse, Api, BankMsg, + entry_point, from_json, to_json_binary, to_json_vec, Addr, AllBalanceResponse, Api, BankMsg, CanonicalAddr, Deps, DepsMut, Env, Event, MessageInfo, QueryRequest, QueryResponse, Response, StdError, StdResult, WasmMsg, WasmQuery, }; @@ -24,7 +24,7 @@ pub fn instantiate( deps.storage.set( CONFIG_KEY, - &to_vec(&State { + &to_json_vec(&State { verifier: deps.api.addr_validate(&msg.verifier)?, beneficiary: deps.api.addr_validate(&msg.beneficiary)?, funder: info.sender, @@ -41,9 +41,9 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result Result Result { fn do_message_loop(env: Env) -> Result { let resp = Response::new().add_message(WasmMsg::Execute { contract_addr: env.contract.address.into(), - msg: to_binary(&ExecuteMsg::MessageLoop {})?, + msg: to_json_binary(&ExecuteMsg::MessageLoop {})?, funds: vec![], }); Ok(resp) @@ -248,12 +248,12 @@ fn do_user_errors_in_api_calls(api: &dyn Api) -> Result { #[entry_point] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Verifier {} => to_binary(&query_verifier(deps)?), - QueryMsg::OtherBalance { address } => to_binary(&query_other_balance(deps, address)?), + QueryMsg::Verifier {} => to_json_binary(&query_verifier(deps)?), + QueryMsg::OtherBalance { address } => to_json_binary(&query_other_balance(deps, address)?), QueryMsg::Recurse { depth, work } => { - to_binary(&query_recurse(deps, depth, work, env.contract.address)?) + to_json_binary(&query_recurse(deps, depth, work, env.contract.address)?) } - QueryMsg::GetInt {} => to_binary(&query_int()), + QueryMsg::GetInt {} => to_json_binary(&query_int()), } } @@ -262,7 +262,7 @@ fn query_verifier(deps: Deps) -> StdResult { .storage .get(CONFIG_KEY) .ok_or_else(|| StdError::not_found("State"))?; - let state: State = from_slice(&data)?; + let state: State = from_json(data)?; Ok(VerifierResponse { verifier: state.verifier.into(), }) @@ -293,7 +293,7 @@ fn query_recurse(deps: Deps, depth: u32, work: u32, contract: Addr) -> StdResult }; let query = QueryRequest::Wasm(WasmQuery::Smart { contract_addr: contract.into(), - msg: to_binary(&req)?, + msg: to_json_binary(&req)?, }); deps.querier.query(&query) } @@ -336,7 +336,7 @@ mod tests { // it worked, let's check the state let data = deps.storage.get(CONFIG_KEY).expect("no data stored"); - let state: State = from_slice(&data).unwrap(); + let state: State = from_json(data).unwrap(); assert_eq!(state, expected_state); } @@ -514,7 +514,7 @@ mod tests { // state should not change let data = deps.storage.get(CONFIG_KEY).expect("no data stored"); - let state: State = from_slice(&data).unwrap(); + let state: State = from_json(data).unwrap(); assert_eq!( state, State { diff --git a/contracts/hackatom/tests/integration.rs b/contracts/hackatom/tests/integration.rs index 10f77d091..890309ffd 100644 --- a/contracts/hackatom/tests/integration.rs +++ b/contracts/hackatom/tests/integration.rs @@ -18,7 +18,7 @@ //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) use cosmwasm_std::{ - assert_approx_eq, coins, from_binary, to_vec, Addr, AllBalanceResponse, BankMsg, Binary, + assert_approx_eq, coins, from_json, to_json_vec, Addr, AllBalanceResponse, BankMsg, Binary, ContractResult, Empty, Response, SubMsg, }; use cosmwasm_vm::{ @@ -183,7 +183,7 @@ fn querier_callbacks_work() { // querying with balance gets the balance let query_msg = QueryMsg::OtherBalance { address: rich_addr }; let query_response = query(&mut deps, mock_env(), query_msg).unwrap(); - let bal: AllBalanceResponse = from_binary(&query_response).unwrap(); + let bal: AllBalanceResponse = from_json(query_response).unwrap(); assert_eq!(bal.amount, rich_balance); // querying other accounts gets none @@ -191,7 +191,7 @@ fn querier_callbacks_work() { address: String::from("someone else"), }; let query_response = query(&mut deps, mock_env(), query_msg).unwrap(); - let bal: AllBalanceResponse = from_binary(&query_response).unwrap(); + let bal: AllBalanceResponse = from_json(query_response).unwrap(); assert_eq!(bal.amount, vec![]); } @@ -322,7 +322,7 @@ fn execute_cpu_loop() { &mut deps, &mock_env(), &execute_info, - &to_vec(&ExecuteMsg::CpuLoop {}).unwrap(), + &to_json_vec(&ExecuteMsg::CpuLoop {}).unwrap(), ); assert!(execute_res.is_err()); assert_eq!(deps.get_gas_left(), 0); @@ -344,7 +344,7 @@ fn execute_storage_loop() { &mut deps, &mock_env(), &execute_info, - &to_vec(&ExecuteMsg::StorageLoop {}).unwrap(), + &to_json_vec(&ExecuteMsg::StorageLoop {}).unwrap(), ); assert!(execute_res.is_err()); assert_eq!(deps.get_gas_left(), 0); @@ -366,7 +366,7 @@ fn execute_memory_loop() { &mut deps, &mock_env(), &execute_info, - &to_vec(&ExecuteMsg::MemoryLoop {}).unwrap(), + &to_json_vec(&ExecuteMsg::MemoryLoop {}).unwrap(), ); assert!(execute_res.is_err()); assert_eq!(deps.get_gas_left(), 0); @@ -448,7 +448,7 @@ fn execute_panic() { &mut deps, &mock_env(), &execute_info, - &to_vec(&ExecuteMsg::Panic {}).unwrap(), + &to_json_vec(&ExecuteMsg::Panic {}).unwrap(), ); match execute_res.unwrap_err() { VmError::RuntimeErr { msg, .. } => { diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock index 970cf0c26..826fc3e90 100644 --- a/contracts/ibc-reflect-send/Cargo.lock +++ b/contracts/ibc-reflect-send/Cargo.lock @@ -73,6 +73,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -572,7 +591,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -926,6 +945,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1261,6 +1289,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1366,6 +1400,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1410,6 +1454,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1655,9 +1705,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1668,6 +1718,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1683,19 +1734,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1706,9 +1761,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1725,9 +1780,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1744,9 +1799,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1756,9 +1811,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1767,9 +1822,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1783,14 +1838,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/ibc-reflect-send/src/contract.rs b/contracts/ibc-reflect-send/src/contract.rs index 50d074f3a..23adc82b8 100644 --- a/contracts/ibc-reflect-send/src/contract.rs +++ b/contracts/ibc-reflect-send/src/contract.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, to_binary, CosmosMsg, Deps, DepsMut, Env, IbcMsg, MessageInfo, QueryResponse, + entry_point, to_json_binary, CosmosMsg, Deps, DepsMut, Env, IbcMsg, MessageInfo, QueryResponse, Response, StdError, StdResult, }; @@ -79,7 +79,7 @@ pub fn handle_send_msgs( let packet = PacketMsg::Dispatch { msgs }; let msg = IbcMsg::SendPacket { channel_id, - data: to_binary(&packet)?, + data: to_json_binary(&packet)?, timeout: env.block.time.plus_seconds(PACKET_LIFETIME).into(), }; @@ -107,7 +107,7 @@ pub fn handle_check_remote_balance( let packet = PacketMsg::Balances {}; let msg = IbcMsg::SendPacket { channel_id, - data: to_binary(&packet)?, + data: to_json_binary(&packet)?, timeout: env.block.time.plus_seconds(PACKET_LIFETIME).into(), }; @@ -168,9 +168,9 @@ pub fn handle_send_funds( #[entry_point] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Admin {} => to_binary(&query_admin(deps)?), - QueryMsg::Account { channel_id } => to_binary(&query_account(deps, channel_id)?), - QueryMsg::ListAccounts {} => to_binary(&query_list_accounts(deps)?), + QueryMsg::Admin {} => to_json_binary(&query_admin(deps)?), + QueryMsg::Account { channel_id } => to_json_binary(&query_account(deps, channel_id)?), + QueryMsg::ListAccounts {} => to_json_binary(&query_list_accounts(deps)?), } } diff --git a/contracts/ibc-reflect-send/src/ibc.rs b/contracts/ibc-reflect-send/src/ibc.rs index 634fed3e8..6faabd5c1 100644 --- a/contracts/ibc-reflect-send/src/ibc.rs +++ b/contracts/ibc-reflect-send/src/ibc.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, from_slice, to_binary, DepsMut, Env, IbcBasicResponse, IbcChannelCloseMsg, + entry_point, from_json, to_json_binary, DepsMut, Env, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcMsg, IbcOrder, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, Never, StdError, StdResult, }; @@ -59,7 +59,7 @@ pub fn ibc_channel_connect( let packet = PacketMsg::WhoAmI {}; let msg = IbcMsg::SendPacket { channel_id: channel_id.clone(), - data: to_binary(&packet)?, + data: to_json_binary(&packet)?, timeout: env.block.time.plus_seconds(PACKET_LIFETIME).into(), }; @@ -108,18 +108,18 @@ pub fn ibc_packet_ack( // which local channel was this packet send from let caller = msg.original_packet.src.channel_id; // we need to parse the ack based on our request - let packet: PacketMsg = from_slice(&msg.original_packet.data)?; + let packet: PacketMsg = from_json(msg.original_packet.data)?; match packet { PacketMsg::Dispatch { .. } => { - let res: AcknowledgementMsg = from_slice(&msg.acknowledgement.data)?; + let res: AcknowledgementMsg = from_json(msg.acknowledgement.data)?; acknowledge_dispatch(deps, caller, res) } PacketMsg::WhoAmI {} => { - let res: AcknowledgementMsg = from_slice(&msg.acknowledgement.data)?; + let res: AcknowledgementMsg = from_json(msg.acknowledgement.data)?; acknowledge_who_am_i(deps, caller, res) } PacketMsg::Balances {} => { - let res: AcknowledgementMsg = from_slice(&msg.acknowledgement.data)?; + let res: AcknowledgementMsg = from_json(msg.acknowledgement.data)?; acknowledge_balances(deps, env, caller, res) } } @@ -305,7 +305,7 @@ mod tests { channel_id: channel_id.into(), }; let r = query(deps.as_ref(), mock_env(), q).unwrap(); - let acct: AccountResponse = from_slice(&r).unwrap(); + let acct: AccountResponse = from_json(r).unwrap(); assert!(acct.remote_addr.is_none()); assert!(acct.remote_balance.is_empty()); assert_eq!(0, acct.last_update_time.nanos()); @@ -319,7 +319,7 @@ mod tests { channel_id: channel_id.into(), }; let r = query(deps.as_ref(), mock_env(), q).unwrap(); - let acct: AccountResponse = from_slice(&r).unwrap(); + let acct: AccountResponse = from_json(r).unwrap(); assert_eq!(acct.remote_addr.unwrap(), remote_addr); assert!(acct.remote_balance.is_empty()); assert_eq!(0, acct.last_update_time.nanos()); diff --git a/contracts/ibc-reflect-send/src/state.rs b/contracts/ibc-reflect-send/src/state.rs index 062ff187e..44f78f4e0 100644 --- a/contracts/ibc-reflect-send/src/state.rs +++ b/contracts/ibc-reflect-send/src/state.rs @@ -1,9 +1,9 @@ use serde::{Deserialize, Serialize}; use cosmwasm_std::{ - from_slice, + from_json, storage_keys::{namespace_with_key, to_length_prefixed}, - to_vec, Addr, Coin, Order, StdError, StdResult, Storage, Timestamp, + to_json_vec, Addr, Coin, Order, StdError, StdResult, Storage, Timestamp, }; pub const KEY_CONFIG: &[u8] = b"config"; @@ -33,7 +33,7 @@ pub struct AccountData { pub fn may_load_account(storage: &dyn Storage, id: &str) -> StdResult> { storage .get(&namespace_with_key(&[PREFIX_ACCOUNTS], id.as_bytes())) - .map(|v| from_slice(&v)) + .map(from_json) .transpose() } @@ -44,7 +44,7 @@ pub fn load_account(storage: &dyn Storage, id: &str) -> StdResult { pub fn save_account(storage: &mut dyn Storage, id: &str, account: &AccountData) -> StdResult<()> { storage.set( &namespace_with_key(&[PREFIX_ACCOUNTS], id.as_bytes()), - &to_vec(account)?, + &to_json_vec(account)?, ); Ok(()) } @@ -63,7 +63,7 @@ pub fn range_accounts( .map(|(key, val)| { Ok(( String::from_utf8(key[PREFIX_ACCOUNTS.len() + 2..].to_vec())?, - from_slice(&val)?, + from_json(val)?, )) }) } @@ -72,10 +72,10 @@ pub fn load_config(storage: &dyn Storage) -> StdResult { storage .get(&to_length_prefixed(KEY_CONFIG)) .ok_or_else(|| StdError::not_found("config")) - .and_then(|v| from_slice(&v)) + .and_then(from_json) } pub fn save_config(storage: &mut dyn Storage, item: &Config) -> StdResult<()> { - storage.set(&to_length_prefixed(KEY_CONFIG), &to_vec(item)?); + storage.set(&to_length_prefixed(KEY_CONFIG), &to_json_vec(item)?); Ok(()) } diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock index 71dbd710d..a2a9ffbcf 100644 --- a/contracts/ibc-reflect/Cargo.lock +++ b/contracts/ibc-reflect/Cargo.lock @@ -73,6 +73,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -572,7 +591,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -926,6 +945,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1261,6 +1289,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1366,6 +1400,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1410,6 +1454,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1655,9 +1705,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1668,6 +1718,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1683,19 +1734,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1706,9 +1761,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1725,9 +1780,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1744,9 +1799,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1756,9 +1811,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1767,9 +1822,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1783,14 +1838,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/ibc-reflect/src/contract.rs b/contracts/ibc-reflect/src/contract.rs index 1af5d90e9..7d8efb44c 100644 --- a/contracts/ibc-reflect/src/contract.rs +++ b/contracts/ibc-reflect/src/contract.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ - entry_point, from_slice, to_binary, wasm_execute, BankMsg, Binary, CosmosMsg, Deps, DepsMut, - Empty, Env, Event, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannelCloseMsg, + entry_point, from_json, to_json_binary, wasm_execute, BankMsg, Binary, CosmosMsg, Deps, + DepsMut, Empty, Env, Event, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, IbcOrder, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, MessageInfo, Never, QueryResponse, Reply, Response, StdError, StdResult, SubMsg, SubMsgResponse, SubMsgResult, @@ -91,8 +91,8 @@ pub fn handle_init_callback(deps: DepsMut, response: SubMsgResponse) -> StdResul #[entry_point] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Account { channel_id } => to_binary(&query_account(deps, channel_id)?), - QueryMsg::ListAccounts {} => to_binary(&query_list_accounts(deps)?), + QueryMsg::Account { channel_id } => to_json_binary(&query_account(deps, channel_id)?), + QueryMsg::ListAccounts {} => to_json_binary(&query_list_accounts(deps)?), } } @@ -224,7 +224,7 @@ pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> StdResult { // this encode an error or error message into a proper acknowledgement to the recevier fn encode_ibc_error(msg: impl Into) -> Binary { // this cannot error, unwrap to keep the interface simple - to_binary(&AcknowledgementMsg::<()>::Error(msg.into())).unwrap() + to_json_binary(&AcknowledgementMsg::<()>::Error(msg.into())).unwrap() } #[entry_point] @@ -241,7 +241,7 @@ pub fn ibc_packet_receive( let packet = msg.packet; // which local channel did this packet come on let caller = packet.dest.channel_id; - let msg: PacketMsg = from_slice(&packet.data)?; + let msg: PacketMsg = from_json(packet.data)?; match msg { PacketMsg::Dispatch { msgs } => receive_dispatch(deps, caller, msgs), PacketMsg::WhoAmI {} => receive_who_am_i(deps, caller), @@ -267,7 +267,7 @@ fn receive_who_am_i(deps: DepsMut, caller: String) -> StdResult StdResult::Ok(()))?; + let acknowledgement = to_json_binary(&AcknowledgementMsg::::Ok(()))?; // create the message to re-dispatch to the reflect contract let reflect_msg = ReflectExecuteMsg::ReflectMsg { msgs }; let wasm_msg = wasm_execute(reflect_addr, &reflect_msg, vec![])?; @@ -322,7 +322,7 @@ fn execute_error(text: String) -> StdResult { } fn execute_return_msgs(msgs: Vec) -> StdResult { - let acknowledgement = to_binary(&AcknowledgementMsg::::Ok(()))?; + let acknowledgement = to_json_binary(&AcknowledgementMsg::::Ok(()))?; Ok(IbcReceiveResponse::new() .set_ack(acknowledgement) @@ -358,7 +358,7 @@ mod tests { mock_ibc_channel_open_init, mock_ibc_channel_open_try, mock_ibc_packet_recv, mock_info, mock_wasmd_attr, MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR, }; - use cosmwasm_std::{attr, coin, coins, from_slice, BankMsg, OwnedDeps, WasmMsg}; + use cosmwasm_std::{attr, coin, coins, from_json, BankMsg, OwnedDeps, WasmMsg}; const CREATOR: &str = "creator"; // code id of the reflect contract @@ -482,7 +482,7 @@ mod tests { // no accounts set yet let raw = query(deps.as_ref(), mock_env(), QueryMsg::ListAccounts {}).unwrap(); - let res: ListAccountsResponse = from_slice(&raw).unwrap(); + let res: ListAccountsResponse = from_json(raw).unwrap(); assert_eq!(0, res.accounts.len()); // fake a reply and ensure this works @@ -497,7 +497,7 @@ mod tests { // ensure this is now registered let raw = query(deps.as_ref(), mock_env(), QueryMsg::ListAccounts {}).unwrap(); - let res: ListAccountsResponse = from_slice(&raw).unwrap(); + let res: ListAccountsResponse = from_json(raw).unwrap(); assert_eq!(1, res.accounts.len()); assert_eq!( &res.accounts[0], @@ -516,7 +516,7 @@ mod tests { }, ) .unwrap(); - let res: AccountResponse = from_slice(&raw).unwrap(); + let res: AccountResponse = from_json(raw).unwrap(); assert_eq!(res.account.unwrap(), REFLECT_ADDR); } @@ -546,7 +546,7 @@ mod tests { res.events[0] ); // acknowledgement is an error - let ack: AcknowledgementMsg = from_slice(&res.acknowledgement).unwrap(); + let ack: AcknowledgementMsg = from_json(res.acknowledgement).unwrap(); assert_eq!( ack.unwrap_err(), "invalid packet: account channel-123 not found" @@ -560,7 +560,7 @@ mod tests { let res = ibc_packet_receive(deps.as_mut(), mock_env(), msg).unwrap(); // assert app-level success - let ack: AcknowledgementMsg<()> = from_slice(&res.acknowledgement).unwrap(); + let ack: AcknowledgementMsg<()> = from_json(res.acknowledgement).unwrap(); ack.unwrap(); // and we dispatch the BankMsg via submessage @@ -577,7 +577,7 @@ mod tests { assert_eq!(account, contract_addr.as_str()); assert_eq!(0, funds.len()); // parse the message - should callback with proper channel_id - let rmsg: ReflectExecuteMsg = from_slice(msg).unwrap(); + let rmsg: ReflectExecuteMsg = from_json(msg).unwrap(); assert_eq!( rmsg, ReflectExecuteMsg::ReflectMsg { @@ -597,7 +597,7 @@ mod tests { // we didn't dispatch anything assert_eq!(0, res.messages.len()); // acknowledgement is an error - let ack: AcknowledgementMsg = from_slice(&res.acknowledgement).unwrap(); + let ack: AcknowledgementMsg = from_json(res.acknowledgement).unwrap(); assert_eq!(ack.unwrap_err(), "invalid packet: Error parsing into type ibc_reflect::msg::PacketMsg: unknown variant `reflect_code_id`, expected one of `dispatch`, `who_am_i`, `balances`, `panic`, `return_err`, `return_msgs`"); } @@ -616,7 +616,7 @@ mod tests { // channel should be listed and have balance let raw = query(deps.as_ref(), mock_env(), QueryMsg::ListAccounts {}).unwrap(); - let res: ListAccountsResponse = from_slice(&raw).unwrap(); + let res: ListAccountsResponse = from_json(raw).unwrap(); assert_eq!(1, res.accounts.len()); let balance = deps.as_ref().querier.query_all_balances(account).unwrap(); assert_eq!(funds, balance); @@ -632,7 +632,7 @@ mod tests { }) = &res.messages[0].msg { assert_eq!(contract_addr.as_str(), account); - let reflect: ReflectExecuteMsg = from_slice(msg).unwrap(); + let reflect: ReflectExecuteMsg = from_json(msg).unwrap(); match reflect { ReflectExecuteMsg::ReflectMsg { msgs } => { assert_eq!(1, msgs.len()); @@ -652,7 +652,7 @@ mod tests { // and removes the account lookup let raw = query(deps.as_ref(), mock_env(), QueryMsg::ListAccounts {}).unwrap(); - let res: ListAccountsResponse = from_slice(&raw).unwrap(); + let res: ListAccountsResponse = from_json(raw).unwrap(); assert_eq!(0, res.accounts.len()); } } diff --git a/contracts/ibc-reflect/src/state.rs b/contracts/ibc-reflect/src/state.rs index bbb1fdaaa..112f0227d 100644 --- a/contracts/ibc-reflect/src/state.rs +++ b/contracts/ibc-reflect/src/state.rs @@ -1,9 +1,9 @@ use std::any::type_name; use cosmwasm_std::{ - from_slice, + from_json, storage_keys::{namespace_with_key, to_length_prefixed}, - to_vec, Addr, Order, StdError, StdResult, Storage, + to_json_vec, Addr, Order, StdError, StdResult, Storage, }; use schemars::JsonSchema; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -22,7 +22,7 @@ pub struct Config { pub fn may_load_account(storage: &dyn Storage, id: &str) -> StdResult> { storage .get(&namespace_with_key(&[PREFIX_ACCOUNTS], id.as_bytes())) - .map(|v| from_slice(&v)) + .map(from_json) .transpose() } @@ -33,7 +33,7 @@ pub fn load_account(storage: &dyn Storage, id: &str) -> StdResult { pub fn save_account(storage: &mut dyn Storage, id: &str, account: &Addr) -> StdResult<()> { storage.set( &namespace_with_key(&[PREFIX_ACCOUNTS], id.as_bytes()), - &to_vec(account)?, + &to_json_vec(account)?, ); Ok(()) } @@ -52,7 +52,7 @@ pub fn range_accounts( .map(|(key, val)| { Ok(( String::from_utf8(key[PREFIX_ACCOUNTS.len() + 2..].to_vec())?, - from_slice(&val)?, + from_json(val)?, )) }) } @@ -61,10 +61,10 @@ pub fn load_item(storage: &dyn Storage, key: &[u8]) -> StdR storage .get(&to_length_prefixed(key)) .ok_or_else(|| StdError::not_found(type_name::())) - .and_then(|v| from_slice(&v)) + .and_then(from_json) } pub fn save_item(storage: &mut dyn Storage, key: &[u8], item: &T) -> StdResult<()> { - storage.set(&to_length_prefixed(key), &to_vec(item)?); + storage.set(&to_length_prefixed(key), &to_json_vec(item)?); Ok(()) } diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock index d37444497..6004c45c9 100644 --- a/contracts/queue/Cargo.lock +++ b/contracts/queue/Cargo.lock @@ -73,6 +73,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -572,7 +591,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -915,6 +934,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1261,6 +1289,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1366,6 +1400,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1410,6 +1454,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1655,9 +1705,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1668,6 +1718,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1683,19 +1734,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1706,9 +1761,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1725,9 +1780,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1744,9 +1799,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1756,9 +1811,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1767,9 +1822,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1783,14 +1838,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/queue/src/contract.rs b/contracts/queue/src/contract.rs index 63c1769ec..536921a4e 100644 --- a/contracts/queue/src/contract.rs +++ b/contracts/queue/src/contract.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ - entry_point, from_slice, to_binary, to_vec, Binary, Deps, DepsMut, Empty, Env, MessageInfo, - Order, QueryResponse, Response, StdResult, Storage, + entry_point, from_json, to_json_binary, to_json_vec, Binary, Deps, DepsMut, Empty, Env, + MessageInfo, Order, QueryResponse, Response, StdResult, Storage, }; use crate::msg::{ @@ -51,7 +51,7 @@ fn enqueue(storage: &mut dyn Storage, value: i32) -> StdResult<()> { (last_key + 1).to_be_bytes() } }; - let new_value = to_vec(&Item { value })?; + let new_value = to_json_vec(&Item { value })?; storage.set(&new_key, &new_value); Ok(()) @@ -92,11 +92,11 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult StdResult { match msg { - QueryMsg::Count {} => to_binary(&query_count(deps)), - QueryMsg::Sum {} => to_binary(&query_sum(deps)?), - QueryMsg::Reducer {} => to_binary(&query_reducer(deps)?), - QueryMsg::List {} => to_binary(&query_list(deps)), - QueryMsg::OpenIterators { count } => to_binary(&query_open_iterators(deps, count)), + QueryMsg::Count {} => to_json_binary(&query_count(deps)), + QueryMsg::Sum {} => to_json_binary(&query_sum(deps)?), + QueryMsg::Reducer {} => to_json_binary(&query_reducer(deps)?), + QueryMsg::List {} => to_json_binary(&query_list(deps)), + QueryMsg::OpenIterators { count } => to_json_binary(&query_open_iterators(deps, count)), } } @@ -112,7 +112,7 @@ fn query_sum(deps: Deps) -> StdResult { let values: StdResult> = deps .storage .range_values(None, None, Order::Ascending) - .map(|v| from_slice(&v)) + .map(from_json) .collect(); let sum = values?.iter().fold(0, |s, v| s + v.value); Ok(SumResponse { sum }) @@ -124,7 +124,7 @@ fn query_reducer(deps: Deps) -> StdResult { for val in deps .storage .range_values(None, None, Order::Ascending) - .map(|v| from_slice::(&v)) + .map(|v| from_json::(&v)) { // this returns error on parse error let my_val = val?.value; @@ -134,7 +134,7 @@ fn query_reducer(deps: Deps) -> StdResult { .range_values(None, None, Order::Ascending) // get value. ignore parse errors, just count as 0 .map(|v| { - from_slice::(&v) + from_json::(&v) .map(|v| v.value) .expect("error in item") }) @@ -181,7 +181,7 @@ mod tests { use cosmwasm_std::testing::{ mock_dependencies_with_balance, mock_env, mock_info, MockApi, MockQuerier, MockStorage, }; - use cosmwasm_std::{coins, from_binary, OwnedDeps}; + use cosmwasm_std::{coins, from_json, OwnedDeps}; /// Instantiates a contract with no elements fn create_contract() -> (OwnedDeps, MessageInfo) { @@ -270,7 +270,7 @@ mod tests { // ensure we popped properly assert!(res.data.is_some()); let data = res.data.unwrap(); - let state: Item = from_slice(data.as_slice()).unwrap(); + let state: Item = from_json(data.as_slice()).unwrap(); assert_eq!(state.value, 25); assert_eq!(get_count(deps.as_ref()), 1); @@ -340,7 +340,7 @@ mod tests { let query_msg = QueryMsg::List {}; let ids: ListResponse = - from_binary(&query(deps.as_ref(), mock_env(), query_msg).unwrap()).unwrap(); + from_json(query(deps.as_ref(), mock_env(), query_msg).unwrap()).unwrap(); assert_eq!(ids.empty, Vec::::new()); assert_eq!(ids.early, vec![0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]); assert_eq!(ids.late, vec![0x20, 0x21, 0x22, 0x23, 0x24]); diff --git a/contracts/queue/tests/integration.rs b/contracts/queue/tests/integration.rs index 98a5b21f5..b0a86ae1d 100644 --- a/contracts/queue/tests/integration.rs +++ b/contracts/queue/tests/integration.rs @@ -17,7 +17,7 @@ //! }); //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) -use cosmwasm_std::{from_binary, from_slice, MessageInfo, Response}; +use cosmwasm_std::{from_json, MessageInfo, Response}; use cosmwasm_vm::{ testing::{ execute, instantiate, migrate, mock_env, mock_info, mock_instance_with_gas_limit, query, @@ -48,13 +48,13 @@ fn create_contract() -> (Instance, MessageInf fn get_count(deps: &mut Instance) -> u32 { let data = query(deps, mock_env(), QueryMsg::Count {}).unwrap(); - let res: CountResponse = from_binary(&data).unwrap(); + let res: CountResponse = from_json(data).unwrap(); res.count } fn get_sum(deps: &mut Instance) -> i32 { let data = query(deps, mock_env(), QueryMsg::Sum {}).unwrap(); - let res: SumResponse = from_binary(&data).unwrap(); + let res: SumResponse = from_json(data).unwrap(); res.sum } @@ -128,7 +128,7 @@ fn push_and_pop() { // ensure we popped properly assert!(res.data.is_some()); let data = res.data.unwrap(); - let item: Item = from_slice(data.as_slice()).unwrap(); + let item: Item = from_json(data).unwrap(); assert_eq!(item.value, 25); assert_eq!(get_count(&mut deps), 1); @@ -169,7 +169,7 @@ fn push_and_reduce() { assert_eq!(get_count(&mut deps), 4); assert_eq!(get_sum(&mut deps), 130); let data = query(&mut deps, mock_env(), QueryMsg::Reducer {}).unwrap(); - let counters = from_binary::(&data).unwrap().counters; + let counters = from_json::(&data).unwrap().counters; assert_eq!(counters, vec![(40, 85), (15, 125), (85, 0), (-10, 140)]); } @@ -223,7 +223,7 @@ fn query_list() { // since we count up to 0x20 in early, we get early and late both with data let query_msg = QueryMsg::List {}; - let ids: ListResponse = from_binary(&query(&mut deps, mock_env(), query_msg).unwrap()).unwrap(); + let ids: ListResponse = from_json(query(&mut deps, mock_env(), query_msg).unwrap()).unwrap(); assert_eq!(ids.empty, Vec::::new()); assert_eq!(ids.early, vec![0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]); assert_eq!(ids.late, vec![0x20, 0x21, 0x22, 0x23, 0x24]); diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock index ac573e8ab..4cebcec76 100644 --- a/contracts/reflect/Cargo.lock +++ b/contracts/reflect/Cargo.lock @@ -73,6 +73,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -572,7 +591,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -915,6 +934,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1262,6 +1290,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.103" @@ -1367,6 +1401,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1411,6 +1455,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1656,9 +1706,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1669,6 +1719,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1684,19 +1735,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1707,9 +1762,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1726,9 +1781,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1745,9 +1800,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1757,9 +1812,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1768,9 +1823,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1784,14 +1839,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/reflect/schema/raw/response_to_sub_msg_result.json b/contracts/reflect/schema/raw/response_to_sub_msg_result.json index e7a26e26f..d3b29ce66 100644 --- a/contracts/reflect/schema/raw/response_to_sub_msg_result.json +++ b/contracts/reflect/schema/raw/response_to_sub_msg_result.json @@ -40,7 +40,7 @@ "type": "string" }, "Event": { - "description": "A full [*Cosmos SDK* event].\n\nThis version uses string attributes (similar to [*Cosmos SDK* StringEvent]), which then get magically converted to bytes for Tendermint somewhere between the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK.\n\n[*Cosmos SDK* event]: https://docs.cosmos.network/main/core/events.html [*Cosmos SDK* StringEvent]: https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70", + "description": "A full [*Cosmos SDK* event].\n\nThis version uses string attributes (similar to [*Cosmos SDK* StringEvent]), which then get magically converted to bytes for Tendermint somewhere between the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK.\n\n[*Cosmos SDK* event]: https://docs.cosmos.network/main/learn/advanced/events [*Cosmos SDK* StringEvent]: https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70", "type": "object", "required": [ "attributes", @@ -48,7 +48,7 @@ ], "properties": { "attributes": { - "description": "The attributes to be included in the event.\n\nYou can learn more about these from [*Cosmos SDK* docs].\n\n[*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html", + "description": "The attributes to be included in the event.\n\nYou can learn more about these from [*Cosmos SDK* docs].\n\n[*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events", "type": "array", "items": { "$ref": "#/definitions/Attribute" diff --git a/contracts/reflect/schema/reflect.json b/contracts/reflect/schema/reflect.json index 169f5943d..5f1737f41 100644 --- a/contracts/reflect/schema/reflect.json +++ b/contracts/reflect/schema/reflect.json @@ -1911,7 +1911,7 @@ "type": "string" }, "Event": { - "description": "A full [*Cosmos SDK* event].\n\nThis version uses string attributes (similar to [*Cosmos SDK* StringEvent]), which then get magically converted to bytes for Tendermint somewhere between the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK.\n\n[*Cosmos SDK* event]: https://docs.cosmos.network/main/core/events.html [*Cosmos SDK* StringEvent]: https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70", + "description": "A full [*Cosmos SDK* event].\n\nThis version uses string attributes (similar to [*Cosmos SDK* StringEvent]), which then get magically converted to bytes for Tendermint somewhere between the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK.\n\n[*Cosmos SDK* event]: https://docs.cosmos.network/main/learn/advanced/events [*Cosmos SDK* StringEvent]: https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70", "type": "object", "required": [ "attributes", @@ -1919,7 +1919,7 @@ ], "properties": { "attributes": { - "description": "The attributes to be included in the event.\n\nYou can learn more about these from [*Cosmos SDK* docs].\n\n[*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html", + "description": "The attributes to be included in the event.\n\nYou can learn more about these from [*Cosmos SDK* docs].\n\n[*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events", "type": "array", "items": { "$ref": "#/definitions/Attribute" diff --git a/contracts/reflect/src/contract.rs b/contracts/reflect/src/contract.rs index 2213bd4b9..fbe9076ec 100644 --- a/contracts/reflect/src/contract.rs +++ b/contracts/reflect/src/contract.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ - entry_point, to_binary, to_vec, Binary, ContractResult, CosmosMsg, Deps, DepsMut, Env, - MessageInfo, QueryRequest, QueryResponse, Reply, Response, StdError, StdResult, SubMsg, + entry_point, to_json_binary, to_json_vec, Binary, ContractResult, CosmosMsg, Deps, DepsMut, + Env, MessageInfo, QueryRequest, QueryResponse, Reply, Response, StdError, StdResult, SubMsg, SystemResult, }; @@ -119,11 +119,11 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Owner {} => to_binary(&query_owner(deps)?), - QueryMsg::Capitalized { text } => to_binary(&query_capitalized(deps, text)?), - QueryMsg::Chain { request } => to_binary(&query_chain(deps, &request)?), - QueryMsg::Raw { contract, key } => to_binary(&query_raw(deps, contract, key)?), - QueryMsg::SubMsgResult { id } => to_binary(&query_subcall(deps, id)?), + QueryMsg::Owner {} => to_json_binary(&query_owner(deps)?), + QueryMsg::Capitalized { text } => to_json_binary(&query_capitalized(deps, text)?), + QueryMsg::Chain { request } => to_json_binary(&query_chain(deps, &request)?), + QueryMsg::Raw { contract, key } => to_json_binary(&query_raw(deps, contract, key)?), + QueryMsg::SubMsgResult { id } => to_json_binary(&query_subcall(deps, id)?), } } @@ -149,7 +149,7 @@ fn query_chain( deps: Deps, request: &QueryRequest, ) -> StdResult { - let raw = to_vec(request).map_err(|serialize_err| { + let raw = to_json_vec(request).map_err(|serialize_err| { StdError::generic_err(format!("Serializing QueryRequest: {serialize_err}")) })?; match deps.querier.raw_query(&raw) { @@ -176,8 +176,8 @@ mod tests { use crate::testing::mock_dependencies_with_custom_querier; use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ - coin, coins, from_binary, AllBalanceResponse, BankMsg, BankQuery, Binary, Event, - StakingMsg, StdError, SubMsgResponse, SubMsgResult, + coin, coins, from_json, AllBalanceResponse, BankMsg, BankQuery, Binary, Event, StakingMsg, + StdError, SubMsgResponse, SubMsgResult, }; #[test] @@ -365,7 +365,7 @@ mod tests { text: "demo one".to_string(), }; let response = query(deps.as_ref(), mock_env(), msg).unwrap(); - let value: CapitalizedResponse = from_binary(&response).unwrap(); + let value: CapitalizedResponse = from_json(response).unwrap(); assert_eq!(value.text, "DEMO ONE"); } @@ -381,8 +381,8 @@ mod tests { .into(), }; let response = query(deps.as_ref(), mock_env(), msg).unwrap(); - let outer: ChainResponse = from_binary(&response).unwrap(); - let inner: AllBalanceResponse = from_binary(&outer.data).unwrap(); + let outer: ChainResponse = from_json(response).unwrap(); + let inner: AllBalanceResponse = from_json(outer.data).unwrap(); assert_eq!(inner.amount, coins(123, "ucosm")); // with custom query @@ -390,8 +390,8 @@ mod tests { request: SpecialQuery::Ping {}.into(), }; let response = query(deps.as_ref(), mock_env(), msg).unwrap(); - let outer: ChainResponse = from_binary(&response).unwrap(); - let inner: SpecialResponse = from_binary(&outer.data).unwrap(); + let outer: ChainResponse = from_json(response).unwrap(); + let inner: SpecialResponse = from_json(outer.data).unwrap(); assert_eq!(inner.msg, "pong"); } @@ -452,7 +452,7 @@ mod tests { // query for the real id let raw = query(deps.as_ref(), mock_env(), QueryMsg::SubMsgResult { id }).unwrap(); - let qres: Reply = from_binary(&raw).unwrap(); + let qres: Reply = from_json(raw).unwrap(); assert_eq!(qres.id, id); let result = qres.result.unwrap(); assert_eq!(result.data, Some(data)); diff --git a/contracts/reflect/src/state.rs b/contracts/reflect/src/state.rs index 417c76315..2fc752b22 100644 --- a/contracts/reflect/src/state.rs +++ b/contracts/reflect/src/state.rs @@ -2,9 +2,9 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use cosmwasm_std::{ - from_slice, + from_json, storage_keys::{namespace_with_key, to_length_prefixed}, - to_vec, Addr, Reply, StdError, StdResult, Storage, + to_json_vec, Addr, Reply, StdError, StdResult, Storage, }; const CONFIG_KEY: &[u8] = b"config"; @@ -19,13 +19,13 @@ pub fn load_reply(storage: &dyn Storage, id: u64) -> StdResult { storage .get(&namespace_with_key(&[RESULT_PREFIX], &id.to_be_bytes())) .ok_or_else(|| StdError::not_found(format!("reply {id}"))) - .and_then(|v| from_slice(&v)) + .and_then(from_json) } pub fn save_reply(storage: &mut dyn Storage, id: u64, reply: &Reply) -> StdResult<()> { storage.set( &namespace_with_key(&[RESULT_PREFIX], &id.to_be_bytes()), - &to_vec(reply)?, + &to_json_vec(reply)?, ); Ok(()) } @@ -38,10 +38,10 @@ pub fn load_config(storage: &dyn Storage) -> StdResult { storage .get(&to_length_prefixed(CONFIG_KEY)) .ok_or_else(|| StdError::not_found("config")) - .and_then(|v| from_slice(&v)) + .and_then(from_json) } pub fn save_config(storage: &mut dyn Storage, item: &State) -> StdResult<()> { - storage.set(&to_length_prefixed(CONFIG_KEY), &to_vec(item)?); + storage.set(&to_length_prefixed(CONFIG_KEY), &to_json_vec(item)?); Ok(()) } diff --git a/contracts/reflect/src/testing.rs b/contracts/reflect/src/testing.rs index 8f6ce714b..f46dac69e 100644 --- a/contracts/reflect/src/testing.rs +++ b/contracts/reflect/src/testing.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use crate::msg::{SpecialQuery, SpecialResponse}; use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR}; -use cosmwasm_std::{to_binary, Binary, Coin, ContractResult, OwnedDeps, SystemResult}; +use cosmwasm_std::{to_json_binary, Binary, Coin, ContractResult, OwnedDeps, SystemResult}; /// A drop-in replacement for cosmwasm_std::testing::mock_dependencies /// this uses our CustomQuerier. @@ -26,18 +26,18 @@ pub fn custom_query_execute(query: &SpecialQuery) -> ContractResult { SpecialQuery::Ping {} => "pong".to_string(), SpecialQuery::Capitalized { text } => text.to_uppercase(), }; - to_binary(&SpecialResponse { msg }).into() + to_json_binary(&SpecialResponse { msg }).into() } #[cfg(test)] mod tests { use super::*; - use cosmwasm_std::{from_binary, QuerierWrapper, QueryRequest}; + use cosmwasm_std::{from_json, QuerierWrapper, QueryRequest}; #[test] fn custom_query_execute_ping() { let res = custom_query_execute(&SpecialQuery::Ping {}).unwrap(); - let response: SpecialResponse = from_binary(&res).unwrap(); + let response: SpecialResponse = from_json(res).unwrap(); assert_eq!(response.msg, "pong"); } @@ -47,7 +47,7 @@ mod tests { text: "fOObaR".to_string(), }) .unwrap(); - let response: SpecialResponse = from_binary(&res).unwrap(); + let response: SpecialResponse = from_json(res).unwrap(); assert_eq!(response.msg, "FOOBAR"); } diff --git a/contracts/reflect/tests/integration.rs b/contracts/reflect/tests/integration.rs index 4df7685dc..1333ad53b 100644 --- a/contracts/reflect/tests/integration.rs +++ b/contracts/reflect/tests/integration.rs @@ -18,9 +18,9 @@ //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) use cosmwasm_std::{ - coin, coins, from_binary, BankMsg, BankQuery, Binary, Coin, ContractResult, Event, - QueryRequest, Reply, Response, StakingMsg, SubMsg, SubMsgResponse, SubMsgResult, - SupplyResponse, SystemResult, + coin, coins, from_json, BankMsg, BankQuery, Binary, Coin, ContractResult, Event, QueryRequest, + Reply, Response, StakingMsg, SubMsg, SubMsgResponse, SubMsgResult, SupplyResponse, + SystemResult, }; use cosmwasm_vm::{ testing::{ @@ -83,7 +83,7 @@ fn proper_initialization() { // it worked, let's query the state let res = query(&mut deps, mock_env(), QueryMsg::Owner {}).unwrap(); - let value: OwnerResponse = from_binary(&res).unwrap(); + let value: OwnerResponse = from_json(res).unwrap(); assert_eq!("creator", value.owner.as_str()); } @@ -159,7 +159,7 @@ fn transfer() { // should change state assert_eq!(0, res.messages.len()); let res = query(&mut deps, mock_env(), QueryMsg::Owner {}).unwrap(); - let value: OwnerResponse = from_binary(&res).unwrap(); + let value: OwnerResponse = from_json(res).unwrap(); assert_eq!("friend", value.owner.as_str()); } @@ -203,8 +203,8 @@ fn supply_query() { ) .unwrap(); - let res: ChainResponse = from_binary(&res).unwrap(); - let res: SupplyResponse = from_binary(&res.data).unwrap(); + let res: ChainResponse = from_json(res).unwrap(); + let res: SupplyResponse = from_json(res.data).unwrap(); assert_eq!(res.amount, coin(25, "OSMO")); } @@ -225,7 +225,7 @@ fn dispatch_custom_query() { }, ) .unwrap(); - let value: CapitalizedResponse = from_binary(&res).unwrap(); + let value: CapitalizedResponse = from_json(res).unwrap(); assert_eq!(value.text, "DEMO ONE"); } @@ -282,7 +282,7 @@ fn reply_and_query() { // query for the real id let raw = query(&mut deps, mock_env(), QueryMsg::SubMsgResult { id }).unwrap(); - let qres: Reply = from_binary(&raw).unwrap(); + let qres: Reply = from_json(raw).unwrap(); assert_eq!(qres.id, id); let result = qres.result.unwrap(); assert_eq!(result.data, Some(data)); diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock index a5d1f152f..9b73aa565 100644 --- a/contracts/staking/Cargo.lock +++ b/contracts/staking/Cargo.lock @@ -73,6 +73,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.3.2" @@ -172,9 +178,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -206,6 +212,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -248,6 +255,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -258,6 +266,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", + "static_assertions", "thiserror", "uuid", ] @@ -410,6 +419,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -578,7 +597,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] @@ -921,6 +940,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1256,6 +1284,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.190" @@ -1361,6 +1395,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1438,6 +1482,12 @@ dependencies = [ "snafu", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -1683,9 +1733,9 @@ checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1696,6 +1746,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1711,19 +1762,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1734,9 +1789,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1753,9 +1808,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1772,9 +1827,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1784,9 +1839,9 @@ dependencies = [ [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1795,9 +1850,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1811,14 +1866,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/staking/src/contract.rs b/contracts/staking/src/contract.rs index 19295f8ef..72b20818e 100644 --- a/contracts/staking/src/contract.rs +++ b/contracts/staking/src/contract.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - coin, entry_point, to_binary, BankMsg, Decimal, Deps, DepsMut, DistributionMsg, Env, + coin, entry_point, to_json_binary, BankMsg, Decimal, Deps, DepsMut, DistributionMsg, Env, MessageInfo, QuerierWrapper, QueryResponse, Response, StakingMsg, StdError, StdResult, Uint128, WasmMsg, }; @@ -289,7 +289,7 @@ pub fn claim(deps: DepsMut, env: Env, info: MessageInfo) -> StdResult pub fn reinvest(deps: DepsMut, env: Env, _info: MessageInfo) -> StdResult { let contract_addr = env.contract.address; let invest: InvestmentInfo = load_item(deps.storage, KEY_INVESTMENT)?; - let msg = to_binary(&ExecuteMsg::_BondAllTokens {})?; + let msg = to_json_binary(&ExecuteMsg::_BondAllTokens {})?; // and bond them to the validator let res = Response::new() @@ -349,10 +349,10 @@ pub fn _bond_all_tokens( #[entry_point] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::TokenInfo {} => to_binary(&query_token_info(deps)?), - QueryMsg::Investment {} => to_binary(&query_investment(deps)?), - QueryMsg::Balance { address } => to_binary(&query_balance(deps, &address)?), - QueryMsg::Claims { address } => to_binary(&query_claims(deps, &address)?), + QueryMsg::TokenInfo {} => to_json_binary(&query_token_info(deps)?), + QueryMsg::Investment {} => to_json_binary(&query_investment(deps)?), + QueryMsg::Balance { address } => to_json_binary(&query_balance(deps, &address)?), + QueryMsg::Claims { address } => to_json_binary(&query_claims(deps, &address)?), } } diff --git a/contracts/staking/src/state.rs b/contracts/staking/src/state.rs index 40e875db2..6af667021 100644 --- a/contracts/staking/src/state.rs +++ b/contracts/staking/src/state.rs @@ -4,9 +4,9 @@ use schemars::JsonSchema; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use cosmwasm_std::{ - from_slice, + from_json, storage_keys::{namespace_with_key, to_length_prefixed}, - to_vec, Addr, CanonicalAddr, Decimal, StdError, StdResult, Storage, Uint128, + to_json_vec, Addr, CanonicalAddr, Decimal, StdError, StdResult, Storage, Uint128, }; pub const KEY_INVESTMENT: &[u8] = b"invest"; @@ -23,7 +23,7 @@ pub fn may_load_map( ) -> StdResult> { storage .get(&namespace_with_key(&[prefix], key)) - .map(|v| from_slice(&v)) + .map(from_json) .transpose() } @@ -33,7 +33,7 @@ pub fn save_map( key: &CanonicalAddr, value: Uint128, ) -> StdResult<()> { - storage.set(&namespace_with_key(&[prefix], key), &to_vec(&value)?); + storage.set(&namespace_with_key(&[prefix], key), &to_json_vec(&value)?); Ok(()) } @@ -85,11 +85,11 @@ pub fn load_item(storage: &dyn Storage, key: &[u8]) -> StdR storage .get(&to_length_prefixed(key)) .ok_or_else(|| StdError::not_found(type_name::())) - .and_then(|v| from_slice(&v)) + .and_then(from_json) } pub fn save_item(storage: &mut dyn Storage, key: &[u8], item: &T) -> StdResult<()> { - storage.set(&to_length_prefixed(key), &to_vec(item)?); + storage.set(&to_length_prefixed(key), &to_json_vec(item)?); Ok(()) } diff --git a/contracts/staking/tests/integration.rs b/contracts/staking/tests/integration.rs index cec0aba12..cca65a9db 100644 --- a/contracts/staking/tests/integration.rs +++ b/contracts/staking/tests/integration.rs @@ -17,7 +17,7 @@ //! }); //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) -use cosmwasm_std::{coin, from_binary, ContractResult, Decimal, Response, Uint128, Validator}; +use cosmwasm_std::{coin, from_json, ContractResult, Decimal, Response, Uint128, Validator}; use cosmwasm_vm::testing::{ instantiate, mock_backend, mock_env, mock_info, mock_instance_options, query, }; @@ -106,7 +106,7 @@ fn proper_initialization() { // token info is proper let res = query(&mut deps, mock_env(), QueryMsg::TokenInfo {}).unwrap(); - let token: TokenInfoResponse = from_binary(&res).unwrap(); + let token: TokenInfoResponse = from_json(res).unwrap(); assert_eq!(&token.name, &msg.name); assert_eq!(&token.symbol, &msg.symbol); assert_eq!(token.decimals, msg.decimals); @@ -120,7 +120,7 @@ fn proper_initialization() { }, ) .unwrap(); - let bal: BalanceResponse = from_binary(&res).unwrap(); + let bal: BalanceResponse = from_json(res).unwrap(); assert_eq!(bal.balance, Uint128::new(0)); // no claims @@ -132,12 +132,12 @@ fn proper_initialization() { }, ) .unwrap(); - let claim: ClaimsResponse = from_binary(&res).unwrap(); + let claim: ClaimsResponse = from_json(res).unwrap(); assert_eq!(claim.claims, Uint128::new(0)); // investment info correct let res = query(&mut deps, mock_env(), QueryMsg::Investment {}).unwrap(); - let invest: InvestmentResponse = from_binary(&res).unwrap(); + let invest: InvestmentResponse = from_json(res).unwrap(); assert_eq!(&invest.owner, &creator); assert_eq!(&invest.validator, &msg.validator); assert_eq!(invest.exit_tax, msg.exit_tax); diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock index 591d75b54..1feb6d879 100644 --- a/contracts/virus/Cargo.lock +++ b/contracts/virus/Cargo.lock @@ -73,6 +73,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bitflags" version = "1.2.1" @@ -128,7 +134,7 @@ checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -157,9 +163,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clru" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ff76ca0691bd91c1b0b5b987e5cf93b21ec810ad96665c5a569c60846dd93" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] name = "const-oid" @@ -191,6 +197,7 @@ name = "cosmwasm-crypto" version = "1.1.9+0.8.1" dependencies = [ "digest 0.10.7", + "ecdsa", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -205,7 +212,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -225,7 +232,7 @@ version = "1.1.9+0.8.1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -233,6 +240,7 @@ name = "cosmwasm-std" version = "1.1.9+0.8.1" dependencies = [ "base64", + "bech32", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -243,6 +251,7 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.3", + "static_assertions", "thiserror", "uuid", ] @@ -414,6 +423,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.8" @@ -461,9 +480,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.0" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -471,27 +490,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.0" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim", - "syn", + "syn 2.0.37", ] [[package]] name = "darling_macro" -version = "0.13.0" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.37", ] [[package]] @@ -525,7 +543,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -567,7 +585,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -578,7 +596,7 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.0", ] [[package]] @@ -652,28 +670,28 @@ checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "enumset" -version = "1.0.7" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e76129da36102af021b8e5000dab2c1c30dbef85c1e482beeff8da5dde0e0b0" +checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.5.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6451128aa6655d880755345d085494cf7561a6bee7c8dc821e5d77e6d267ecd4" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] @@ -932,6 +950,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.6.4" @@ -1041,7 +1068,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -1058,9 +1085,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -1082,14 +1109,14 @@ checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1212,7 +1239,7 @@ checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1248,7 +1275,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 1.0.109", ] [[package]] @@ -1277,6 +1304,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.126" @@ -1314,7 +1347,7 @@ checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1325,7 +1358,7 @@ checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1382,6 +1415,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shared-buffer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + [[package]] name = "signature" version = "2.1.0" @@ -1427,10 +1470,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "strsim" -version = "0.10.0" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "subtle" @@ -1449,6 +1492,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "target-lexicon" version = "0.12.7" @@ -1472,7 +1526,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1510,7 +1564,7 @@ checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1621,7 +1675,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -1645,7 +1699,7 @@ checksum = "c5020cfa87c7cecefef118055d44e3c1fc122c7ec25701d528ee458a0b45f38f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1666,7 +1720,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1679,9 +1733,9 @@ checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wasmer" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7142dbb91ede83cc0aef2301fa75fcc7e0c9e5a7d5358e3c4f3a7249fe9ce8" +checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" dependencies = [ "bytes", "cfg-if", @@ -1692,6 +1746,7 @@ dependencies = [ "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", "wasm-bindgen", @@ -1707,19 +1762,23 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b99c70711ec7631b602a9fc95577c40df21e8f3916159c9d80c3fb4f77abdc" +checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" dependencies = [ "backtrace", + "bytes", "cfg-if", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.0", "more-asserts", "region", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1730,9 +1789,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aef2ef35513a04fed54de9a7dc9c469d4742a5c2e378a5f7e2a79b1327e3bd" +checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1749,9 +1808,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfd019aa98b19fea0fb1d8db9b539145c9416d183ce4cda4e8e024b2c890aac" +checksum = "ebaa865b40ffb3351b03dab9fe9930a5248c25daebd55b464b79b862d9b55ccd" dependencies = [ "byteorder", "dynasm", @@ -1768,21 +1827,21 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bb1425c9e4dc3e2d3aacd6e82e22e27a3127379e0d09bcbdf25ff376229162" +checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "wasmer-middlewares" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc08fb8e2e1511f1d69302d7406ace6c0ec0c90e103f8c0a5aa81ecb9fe81f" +checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" dependencies = [ "wasmer", "wasmer-types", @@ -1791,9 +1850,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e32ed799fa8c0d96ca9615d9ea8006857a0f0c18e7c2ed8082bd5c63a9ea70" +checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" dependencies = [ "bytecheck", "enum-iterator", @@ -1807,14 +1866,15 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.1.2" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0847513cb176b5d62a6f65d6ae474594935e726a10e9e3387177d9cbf8b8cda0" +checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" dependencies = [ "backtrace", "cc", "cfg-if", "corosensei", + "crossbeam-queue", "dashmap", "derivative", "enum-iterator", diff --git a/contracts/virus/src/contract.rs b/contracts/virus/src/contract.rs index 202881b34..0a1235751 100644 --- a/contracts/virus/src/contract.rs +++ b/contracts/virus/src/contract.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, instantiate2_address, to_binary, Attribute, Binary, CodeInfoResponse, + entry_point, instantiate2_address, to_json_binary, Attribute, Binary, CodeInfoResponse, ContractInfoResponse, DepsMut, Env, MessageInfo, Response, StdResult, WasmMsg, }; @@ -72,7 +72,7 @@ pub fn execute_spread( admin: None, code_id, label, - msg: to_binary(&InstantiateMsg {})?, + msg: to_json_binary(&InstantiateMsg {})?, funds: vec![], salt, }); @@ -80,7 +80,7 @@ pub fn execute_spread( // we know the address of the newly instantiated contract, so let's execute it right away msgs.push(WasmMsg::Execute { contract_addr: address.into(), - msg: to_binary(&ExecuteMsg::Spread { + msg: to_json_binary(&ExecuteMsg::Spread { parent_path: path, levels: levels - 1, })?, diff --git a/devtools/check_contracts_full.sh b/devtools/check_contracts_full.sh index 51fd97732..ef0a1c3f9 100755 --- a/devtools/check_contracts_full.sh +++ b/devtools/check_contracts_full.sh @@ -2,6 +2,7 @@ set -o errexit -o nounset -o pipefail command -v shellcheck >/dev/null && shellcheck "$0" +GLOBIGNORE="contracts/floaty/" for contract_dir in contracts/*/; do ( cd "$contract_dir" diff --git a/devtools/check_contracts_medium.sh b/devtools/check_contracts_medium.sh index 265b4d2f4..5e796eaef 100755 --- a/devtools/check_contracts_medium.sh +++ b/devtools/check_contracts_medium.sh @@ -2,6 +2,7 @@ set -o errexit -o nounset -o pipefail command -v shellcheck >/dev/null && shellcheck "$0" +GLOBIGNORE="contracts/floaty/" for contract_dir in contracts/*/; do ( cd "$contract_dir" diff --git a/packages/check/Cargo.toml b/packages/check/Cargo.toml index 6c4ad1608..0136fa7e7 100644 --- a/packages/check/Cargo.toml +++ b/packages/check/Cargo.toml @@ -15,5 +15,5 @@ cosmwasm-vm = { path = "../vm", version = "1.1.9+0.8.1" } cosmwasm-std = { path = "../std", version = "1.1.9+0.8.1" } [dev-dependencies] -assert_cmd = "=2.0.10" # 2.0.11+ requires Rust 1.65.0 which we currently don't want to make the minimum if possible +assert_cmd = "2.0.12" predicates = "3" diff --git a/packages/check/tests/cosmwasm_check_tests.rs b/packages/check/tests/cosmwasm_check_tests.rs index 0c4301d68..751ac2ae0 100644 --- a/packages/check/tests/cosmwasm_check_tests.rs +++ b/packages/check/tests/cosmwasm_check_tests.rs @@ -27,16 +27,13 @@ fn invalid_contract_check() -> Result<(), Box> { } #[test] -fn invalid_contract_check_float_operator() -> Result<(), Box> { +fn valid_contract_check_float_operator() -> Result<(), Box> { let mut cmd = Command::cargo_bin("cosmwasm-check")?; cmd.arg("../vm/testdata/floaty.wasm"); cmd.assert() - .failure() - .stdout(predicate::str::contains("Float operator detected")) - .stdout(predicate::str::contains( - "The use of floats is not supported", - )); + .success() + .stdout(predicate::str::contains("pass")); Ok(()) } diff --git a/packages/crypto/Cargo.toml b/packages/crypto/Cargo.toml index 270ea96c2..2ff89c004 100644 --- a/packages/crypto/Cargo.toml +++ b/packages/crypto/Cargo.toml @@ -25,6 +25,8 @@ digest = "0.10" rand_core = { version = "0.6", features = ["getrandom"] } thiserror = "1.0.38" sha-1 = "0.9.8" +# Not used directly, but needed to bump transitive dependency, see: https://github.com/CosmWasm/cosmwasm/pull/1899 for details. +ecdsa = "0.16.2" [dev-dependencies] criterion = "0.4" diff --git a/packages/go-gen/src/schema.rs b/packages/go-gen/src/schema.rs index 9d9a3fa86..496d0954c 100644 --- a/packages/go-gen/src/schema.rs +++ b/packages/go-gen/src/schema.rs @@ -88,18 +88,18 @@ pub fn schema_object_type( pub fn nullable_type(subschemas: &[Schema]) -> Result, anyhow::Error> { let (found_null, nullable_type): (bool, Option<&SchemaObject>) = subschemas .iter() - .fold(Ok((false, None)), |result: Result<_>, subschema| { - result.and_then(|(nullable, not_null)| { + .try_fold( + (false, None), + |(nullable, not_null), subschema| -> Result<_> { let subschema = subschema.object()?; if is_null(subschema) { Ok((true, not_null)) } else { Ok((nullable, Some(subschema))) } - }) - }) + }, + ) .context("failed to get anyOf subschemas")?; - Ok(if found_null { nullable_type } else { None }) } diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index 2f143b44c..13b4d8f11 100644 --- a/packages/std/Cargo.toml +++ b/packages/std/Cargo.toml @@ -9,7 +9,7 @@ license = "Apache-2.0" readme = "README.md" [package.metadata.docs.rs] -features = ["abort", "stargate", "staking", "ibc3", "cosmwasm_1_3"] +features = ["abort", "stargate", "staking", "ibc3", "cosmwasm_1_4"] [features] default = ["iterator", "abort"] @@ -57,19 +57,27 @@ forward_ref = "1" hex = "0.4" schemars = "0.8.3" sha2 = "0.10.3" -serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } +serde = { version = "1.0.103", default-features = false, features = [ + "derive", + "alloc", +] } serde-json-wasm = { version = "0.5.0" } thiserror = "1.0.26" bnum = "0.8.0" uuid = { version = "1.0.0", features = ["v5", "serde"] } +static_assertions = "1.1.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] +bech32 = "0.9.1" cosmwasm-crypto = { path = "../crypto", version = "1.1.9+0.8.1" } [dev-dependencies] cosmwasm-schema = { path = "../schema" } # The chrono dependency is only used in an example, which Rust compiles for us. If this causes trouble, remove it. -chrono = { version = "0.4", default-features = false, features = ["alloc", "std"] } +chrono = { version = "0.4", default-features = false, features = [ + "alloc", + "std", +] } crc32fast = "1.3.2" hex-literal = "0.3.1" serde_json = "1.0.81" diff --git a/packages/std/src/addresses.rs b/packages/std/src/addresses.rs index 39bc8bc06..f05702e4d 100644 --- a/packages/std/src/addresses.rs +++ b/packages/std/src/addresses.rs @@ -167,7 +167,7 @@ impl<'a> From<&'a Addr> for Cow<'a, Addr> { /// /// The safe way to obtain a valid `CanonicalAddr` is using `Api::addr_canonicalize`. In /// addition to that there are many unsafe ways to convert any binary data into an instance. -/// So the type shoud be treated as a marker to express the intended data type, not as +/// So the type should be treated as a marker to express the intended data type, not as /// a validity guarantee of any sort. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, JsonSchema)] pub struct CanonicalAddr(pub Binary); @@ -358,7 +358,7 @@ pub fn instantiate2_address( } /// The instantiate2 address derivation implementation. This API is used for -/// testing puposes only. The `msg` field is discouraged and should not be used. +/// testing purposes only. The `msg` field is discouraged and should not be used. /// Use [`instantiate2_address`]. #[doc(hidden)] fn instantiate2_address_impl( @@ -751,7 +751,7 @@ mod tests { } #[test] - fn instantiate2_address_impl_works_for_cosmjs_testvectors() { + fn instantiate2_address_impl_works_for_cosmjs_test_vectors() { // Test data from https://github.com/cosmos/cosmjs/pull/1253 const COSMOS_ED25519_TESTS_JSON: &str = "./testdata/instantiate2_addresses.json"; diff --git a/packages/std/src/binary.rs b/packages/std/src/binary.rs index ec5954aeb..bb65eea8d 100644 --- a/packages/std/src/binary.rs +++ b/packages/std/src/binary.rs @@ -253,7 +253,7 @@ mod tests { use super::*; use crate::assert_hash_works; use crate::errors::StdError; - use crate::serde::{from_slice, to_vec}; + use crate::serde::{from_json, to_json_vec}; #[test] fn to_array_works() { @@ -450,8 +450,8 @@ mod tests { fn serialization_works() { let binary = Binary(vec![0u8, 187, 61, 11, 250, 0]); - let json = to_vec(&binary).unwrap(); - let deserialized: Binary = from_slice(&json).unwrap(); + let json = to_json_vec(&binary).unwrap(); + let deserialized: Binary = from_json(json).unwrap(); assert_eq!(binary, deserialized); } @@ -462,16 +462,16 @@ mod tests { // this is the binary behind above string let expected = vec![0u8, 187, 61, 11, 250, 0]; - let serialized = to_vec(&b64_str).unwrap(); - let deserialized: Binary = from_slice(&serialized).unwrap(); + let serialized = to_json_vec(&b64_str).unwrap(); + let deserialized: Binary = from_json(serialized).unwrap(); assert_eq!(expected, deserialized.as_slice()); } #[test] fn deserialize_from_invalid_string() { let invalid_str = "**BAD!**"; - let serialized = to_vec(&invalid_str).unwrap(); - let res = from_slice::(&serialized); + let serialized = to_json_vec(&invalid_str).unwrap(); + let res = from_json::(&serialized); assert!(res.is_err()); } diff --git a/packages/std/src/errors/mod.rs b/packages/std/src/errors/mod.rs index 7bee00461..4f80de8f5 100644 --- a/packages/std/src/errors/mod.rs +++ b/packages/std/src/errors/mod.rs @@ -9,7 +9,8 @@ pub use recover_pubkey_error::RecoverPubkeyError; pub use std_error::{ CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError, CoinFromStrError, CoinsError, ConversionOverflowError, DivideByZeroError, DivisionError, - OverflowError, OverflowOperation, RoundUpOverflowError, StdError, StdResult, + OverflowError, OverflowOperation, RoundDownOverflowError, RoundUpOverflowError, StdError, + StdResult, }; pub use system_error::SystemError; pub use verification_error::VerificationError; diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index 9a9f2f58c..884a81bbc 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -635,6 +635,10 @@ pub enum CheckedFromRatioError { #[error("Round up operation failed because of overflow")] pub struct RoundUpOverflowError; +#[derive(Error, Debug, PartialEq, Eq)] +#[error("Round down operation failed because of overflow")] +pub struct RoundDownOverflowError; + #[derive(Error, Debug, PartialEq, Eq)] pub enum CoinsError { #[error("Duplicate denom")] @@ -920,7 +924,7 @@ mod tests { } #[test] - fn from_std_string_fromutf8error_works() { + fn from_std_string_from_utf8error_works() { let error: StdError = String::from_utf8(b"Hello \xF0\x90\x80World".to_vec()) .unwrap_err() .into(); diff --git a/packages/std/src/errors/system_error.rs b/packages/std/src/errors/system_error.rs index 98dc6f670..8d560c413 100644 --- a/packages/std/src/errors/system_error.rs +++ b/packages/std/src/errors/system_error.rs @@ -7,7 +7,7 @@ use crate::Binary; /// /// This is used on return values for Querier as a nested result: Result, SystemError> /// The first wrap (SystemError) will trigger if the contract address doesn't exist, -/// the QueryRequest is malformated, etc. The second wrap will be an error message from +/// the QueryRequest is malformed, etc. The second wrap will be an error message from /// the contract itself. /// /// Such errors are only created by the VM. The error type is defined in the standard library, to ensure @@ -69,7 +69,7 @@ impl core::fmt::Display for SystemError { #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, to_json_vec}; #[test] fn system_error_no_such_contract_serialization() { @@ -78,14 +78,14 @@ mod tests { }; // ser - let json = to_vec(&err).unwrap(); + let json = to_json_vec(&err).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"no_such_contract":{"addr":"gibtsnicht"}}"#, ); // de - let err: SystemError = from_slice(br#"{"no_such_contract":{"addr":"nada"}}"#).unwrap(); + let err: SystemError = from_json(br#"{"no_such_contract":{"addr":"nada"}}"#).unwrap(); assert_eq!( err, SystemError::NoSuchContract { @@ -99,14 +99,14 @@ mod tests { let err = SystemError::NoSuchCode { code_id: 13 }; // ser - let json = to_vec(&err).unwrap(); + let json = to_json_vec(&err).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"no_such_code":{"code_id":13}}"#, ); // de - let err: SystemError = from_slice(br#"{"no_such_code":{"code_id":987}}"#).unwrap(); + let err: SystemError = from_json(br#"{"no_such_code":{"code_id":987}}"#).unwrap(); assert_eq!(err, SystemError::NoSuchCode { code_id: 987 },); } } diff --git a/packages/std/src/exports.rs b/packages/std/src/exports.rs index 8f057712d..c745bc82e 100644 --- a/packages/std/src/exports.rs +++ b/packages/std/src/exports.rs @@ -25,7 +25,7 @@ use crate::memory::{alloc, consume_region, release_buffer, Region}; use crate::panic::install_panic_handler; use crate::query::CustomQuery; use crate::results::{ContractResult, QueryResponse, Reply, Response}; -use crate::serde::{from_slice, to_vec}; +use crate::serde::{from_json, to_json_vec}; use crate::types::Env; use crate::{CustomMsg, Deps, DepsMut, MessageInfo}; @@ -120,7 +120,7 @@ where info_ptr as *mut Region, msg_ptr as *mut Region, ); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -150,7 +150,7 @@ where info_ptr as *mut Region, msg_ptr as *mut Region, ); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -174,7 +174,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_migrate(migrate_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -198,7 +198,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_sudo(sudo_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -221,7 +221,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_reply(reply_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -243,7 +243,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_query(query_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -266,7 +266,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_channel_open(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -291,7 +291,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_channel_connect(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -316,7 +316,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_channel_close(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -342,7 +342,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_packet_receive(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -368,7 +368,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_packet_ack(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -395,7 +395,7 @@ where #[cfg(feature = "abort")] install_panic_handler(); let res = _do_ibc_packet_timeout(contract_fn, env_ptr as *mut Region, msg_ptr as *mut Region); - let v = to_vec(&res).unwrap(); + let v = to_json_vec(&res).unwrap(); release_buffer(v) as u32 } @@ -415,9 +415,9 @@ where let info: Vec = unsafe { consume_region(info_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let info: MessageInfo = try_into_contract_result!(from_slice(&info)); - let msg: M = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let info: MessageInfo = try_into_contract_result!(from_json(info)); + let msg: M = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); instantiate_fn(deps.as_mut(), env, info, msg).into() @@ -439,9 +439,9 @@ where let info: Vec = unsafe { consume_region(info_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let info: MessageInfo = try_into_contract_result!(from_slice(&info)); - let msg: M = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let info: MessageInfo = try_into_contract_result!(from_json(info)); + let msg: M = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); execute_fn(deps.as_mut(), env, info, msg).into() @@ -461,8 +461,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: M = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: M = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); migrate_fn(deps.as_mut(), env, msg).into() @@ -482,8 +482,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: M = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: M = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); sudo_fn(deps.as_mut(), env, msg).into() @@ -502,8 +502,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: Reply = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: Reply = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); reply_fn(deps.as_mut(), env, msg).into() @@ -522,8 +522,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: M = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: M = try_into_contract_result!(from_json(msg)); let deps = make_dependencies(); query_fn(deps.as_ref(), env, msg).into() @@ -542,8 +542,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcChannelOpenMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcChannelOpenMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() @@ -563,8 +563,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcChannelConnectMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcChannelConnectMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() @@ -584,8 +584,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcChannelCloseMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcChannelCloseMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() @@ -605,8 +605,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcPacketReceiveMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcPacketReceiveMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() @@ -626,8 +626,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcPacketAckMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcPacketAckMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() @@ -647,8 +647,8 @@ where let env: Vec = unsafe { consume_region(env_ptr) }; let msg: Vec = unsafe { consume_region(msg_ptr) }; - let env: Env = try_into_contract_result!(from_slice(&env)); - let msg: IbcPacketTimeoutMsg = try_into_contract_result!(from_slice(&msg)); + let env: Env = try_into_contract_result!(from_json(env)); + let msg: IbcPacketTimeoutMsg = try_into_contract_result!(from_json(msg)); let mut deps = make_dependencies(); contract_fn(deps.as_mut(), env, msg).into() diff --git a/packages/std/src/hex_binary.rs b/packages/std/src/hex_binary.rs index 0def99b36..aeb30b4c4 100644 --- a/packages/std/src/hex_binary.rs +++ b/packages/std/src/hex_binary.rs @@ -247,7 +247,7 @@ impl<'de> de::Visitor<'de> for HexVisitor { mod tests { use super::*; - use crate::{assert_hash_works, from_slice, to_vec, StdError}; + use crate::{assert_hash_works, from_json, to_json_vec, StdError}; #[test] fn from_hex_works() { @@ -376,7 +376,7 @@ mod tests { } #[test] - fn from_slice_works() { + fn from_json_works() { let original: &[u8] = &[0u8, 187, 61, 11, 250, 0]; let binary: HexBinary = original.into(); assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]); @@ -515,8 +515,8 @@ mod tests { fn serialization_works() { let binary = HexBinary(vec![0u8, 187, 61, 11, 250, 0]); - let json = to_vec(&binary).unwrap(); - let deserialized: HexBinary = from_slice(&json).unwrap(); + let json = to_json_vec(&binary).unwrap(); + let deserialized: HexBinary = from_json(json).unwrap(); assert_eq!(binary, deserialized); } @@ -527,16 +527,16 @@ mod tests { // this is the binary behind above string let expected = vec![0u8, 187, 61, 11, 250, 0]; - let serialized = to_vec(&hex).unwrap(); - let deserialized: HexBinary = from_slice(&serialized).unwrap(); + let serialized = to_json_vec(&hex).unwrap(); + let deserialized: HexBinary = from_json(serialized).unwrap(); assert_eq!(expected, deserialized.as_slice()); } #[test] fn deserialize_from_invalid_string() { let invalid_str = "**BAD!**"; - let serialized = to_vec(&invalid_str).unwrap(); - let res = from_slice::(&serialized); + let serialized = to_json_vec(&invalid_str).unwrap(); + let res = from_json::(&serialized); assert!(res.is_err()); } diff --git a/packages/std/src/ibc.rs b/packages/std/src/ibc.rs index dab621b71..1ad33e366 100644 --- a/packages/std/src/ibc.rs +++ b/packages/std/src/ibc.rs @@ -11,7 +11,7 @@ use crate::binary::Binary; use crate::coin::Coin; use crate::errors::StdResult; use crate::results::{Attribute, CosmosMsg, Empty, Event, SubMsg}; -use crate::serde::to_binary; +use crate::serde::to_json_binary; use crate::timestamp::Timestamp; /// These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts @@ -240,7 +240,7 @@ impl IbcAcknowledgement { pub fn encode_json(data: &impl Serialize) -> StdResult { Ok(IbcAcknowledgement { - data: to_binary(data)?, + data: to_json_binary(data)?, }) } } @@ -492,14 +492,14 @@ pub struct IbcBasicResponse { /// /// More info about events (and their attributes) can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub attributes: Vec, /// Extra, custom events separate from the main `wasm` one. These will have /// `wasm-` prepended to the type. /// /// More info about events can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub events: Vec, } @@ -637,14 +637,14 @@ pub struct IbcReceiveResponse { /// /// More info about events (and their attributes) can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub attributes: Vec, /// Extra, custom events separate from the main `wasm` one. These will have /// `wasm-` prepended to the type. /// /// More info about events can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub events: Vec, } diff --git a/packages/std/src/imports.rs b/packages/std/src/imports.rs index 3e4e81b4b..a8772509a 100644 --- a/packages/std/src/imports.rs +++ b/packages/std/src/imports.rs @@ -10,7 +10,7 @@ use crate::results::SystemResult; #[cfg(feature = "iterator")] use crate::sections::decode_sections2; use crate::sections::encode_sections; -use crate::serde::from_slice; +use crate::serde::from_json; use crate::traits::{Api, Querier, QuerierResult, Storage}; #[cfg(feature = "iterator")] use crate::{ @@ -523,7 +523,7 @@ impl Querier for ExternalQuerier { let response_ptr = unsafe { query_chain(request_ptr) }; let response = unsafe { consume_region(response_ptr as *mut Region) }; - from_slice(&response).unwrap_or_else(|parsing_err| { + from_json(&response).unwrap_or_else(|parsing_err| { SystemResult::Err(SystemError::InvalidResponse { error: parsing_err.to_string(), response: response.into(), diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index e0589d8f7..d5abb95dd 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -62,7 +62,8 @@ pub use crate::ibc::{ pub use crate::iterator::{Order, Record}; pub use crate::math::{ Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int128, Int256, - Int512, Int64, Isqrt, Uint128, Uint256, Uint512, Uint64, + Int512, Int64, Isqrt, SignedDecimal, SignedDecimal256, SignedDecimal256RangeExceeded, + SignedDecimalRangeExceeded, Uint128, Uint256, Uint512, Uint64, }; pub use crate::metadata::{DenomMetadata, DenomUnit}; pub use crate::never::Never; @@ -89,7 +90,11 @@ pub use crate::results::{ pub use crate::results::{DistributionMsg, StakingMsg}; #[cfg(feature = "stargate")] pub use crate::results::{GovMsg, VoteOption}; -pub use crate::serde::{from_binary, from_slice, to_binary, to_vec}; +#[allow(deprecated)] +pub use crate::serde::{ + from_binary, from_json, from_slice, to_binary, to_json_binary, to_json_string, to_json_vec, + to_vec, +}; pub use crate::stdack::StdAck; pub use crate::storage::MemoryStorage; pub use crate::timestamp::Timestamp; diff --git a/packages/std/src/math/conversion.rs b/packages/std/src/math/conversion.rs new file mode 100644 index 000000000..240cbf865 --- /dev/null +++ b/packages/std/src/math/conversion.rs @@ -0,0 +1,390 @@ +/// Grows a big endian signed integer to a bigger size. +/// See +pub const fn grow_be_int( + input: [u8; INPUT_SIZE], +) -> [u8; OUTPUT_SIZE] { + debug_assert!(INPUT_SIZE <= OUTPUT_SIZE); + // check if sign bit is set + let mut output = if input[0] & 0b10000000 != 0 { + // negative number is filled up with 1s + [0b11111111u8; OUTPUT_SIZE] + } else { + [0u8; OUTPUT_SIZE] + }; + let mut i = 0; + + // copy input to the end of output + // copy_from_slice is not const, so we have to do this manually + while i < INPUT_SIZE { + output[OUTPUT_SIZE - INPUT_SIZE + i] = input[i]; + i += 1; + } + output +} + +/// Shrinks a big endian signed integer to a smaller size. +/// This is the opposite operation of sign extension. +pub fn shrink_be_int( + input: [u8; INPUT_SIZE], +) -> Option<[u8; OUTPUT_SIZE]> { + debug_assert!(INPUT_SIZE >= OUTPUT_SIZE); + + // check bounds + if input[0] & 0b10000000 != 0 { + // a negative number should start with only 1s, otherwise it's too small + for i in &input[0..(INPUT_SIZE - OUTPUT_SIZE)] { + if *i != 0b11111111u8 { + return None; + } + } + // the sign bit also has to be 1 + if input[INPUT_SIZE - OUTPUT_SIZE] & 0b10000000 == 0 { + return None; + } + } else { + // a positive number should start with only 0s, otherwise it's too large + for i in &input[0..(INPUT_SIZE - OUTPUT_SIZE)] { + if *i != 0u8 { + return None; + } + } + // the sign bit also has to be 0 + if input[INPUT_SIZE - OUTPUT_SIZE] & 0b10000000 != 0 { + return None; + } + } + + // Now, we can just copy the last bytes + let mut output = [0u8; OUTPUT_SIZE]; + output.copy_from_slice(&input[(INPUT_SIZE - OUTPUT_SIZE)..]); + Some(output) +} + +/// Helper macro to implement `TryFrom` for a type that is just a wrapper around another type. +/// This can be used for all our integer conversions where `bnum` implements `TryFrom`. +macro_rules! forward_try_from { + ($input: ty, $output: ty) => { + impl TryFrom<$input> for $output { + type Error = $crate::ConversionOverflowError; + + fn try_from(value: $input) -> Result { + value + .0 + .try_into() + .map(Self) + .map_err(|_| Self::Error::new(stringify!($input), stringify!($output), value)) + } + } + }; +} +pub(crate) use forward_try_from; + +/// Helper macro to implement `TryFrom` for a conversion from a bigger signed int to a smaller one. +/// This is needed because `bnum` does not implement `TryFrom` for those conversions +/// because of limitations of const generics. +macro_rules! try_from_int_to_int { + ($input: ty, $output: ty) => { + // statically assert that the input is bigger than the output + static_assertions::const_assert!( + core::mem::size_of::<$input>() > core::mem::size_of::<$output>() + ); + impl TryFrom<$input> for $output { + type Error = $crate::ConversionOverflowError; + + fn try_from(value: $input) -> Result { + $crate::math::conversion::shrink_be_int(value.to_be_bytes()) + .ok_or_else(|| Self::Error::new(stringify!($input), stringify!($output), value)) + .map(Self::from_be_bytes) + } + } + }; +} +pub(crate) use try_from_int_to_int; + +/// Helper macro to implement `TryFrom` for a conversion from a unsigned int to a smaller or +/// equal sized signed int. +/// This is needed because `bnum` does not implement `TryFrom` for all of those conversions. +macro_rules! try_from_uint_to_int { + ($input: ty, $output: ty) => { + // statically assert that... + // input is unsigned + static_assertions::const_assert_eq!(stringify!($input).as_bytes()[0], b'U'); + // output is signed + static_assertions::const_assert_eq!(stringify!($output).as_bytes()[0], b'I'); + // input is bigger than output (otherwise we would not need a `TryFrom` impl) + static_assertions::const_assert!( + core::mem::size_of::<$input>() >= core::mem::size_of::<$output>() + ); + + impl TryFrom<$input> for $output { + type Error = $crate::ConversionOverflowError; + + fn try_from(value: $input) -> Result { + use bnum::prelude::As; + // $input::MAX has to be bigger than $output::MAX, so we can just cast it + if value.0 > Self::MAX.0.as_() { + return Err(Self::Error::new( + stringify!($input), + stringify!($output), + value, + )); + } + + // at this point we know it fits + Ok(Self(value.0.as_())) + } + } + }; +} +pub(crate) use try_from_uint_to_int; + +#[cfg(test)] +pub(crate) fn test_try_from_uint_to_int(input_type: &'static str, output_type: &'static str) +where + I: super::num_consts::NumConsts + + From + + Copy + + TryFrom + + core::fmt::Debug + + core::ops::Add, + O: TryFrom + + From + + super::num_consts::NumConsts + + core::cmp::PartialEq + + core::fmt::Debug, + String: From, +{ + let v = I::MAX; + assert_eq!( + O::try_from(v), + Err(crate::ConversionOverflowError::new( + input_type, + output_type, + v + )), + "input::MAX value should not fit" + ); + + let max = I::try_from(O::MAX).unwrap(); + assert_eq!(O::try_from(max), Ok(O::MAX), "output::MAX value should fit"); + + // but $output::MAX + 1 should not fit + let v = max + I::ONE; + assert_eq!( + O::try_from(v), + Err(crate::ConversionOverflowError::new( + input_type, + output_type, + v + )), + "output::MAX + 1 should not fit" + ); + + // zero should work + let v = I::ZERO; + assert_eq!(O::try_from(v), Ok(O::ZERO), "zero should fit"); + + // 42 should work + assert_eq!( + O::try_from(I::from(42u32)), + Ok(O::from(42u32)), + "42 should fit" + ) +} + +#[cfg(test)] +pub(crate) fn test_try_from_int_to_uint(input_type: &'static str, output_type: &'static str) +where + I: super::num_consts::NumConsts + + From + + Copy + + TryFrom + + core::fmt::Debug + + core::ops::Add, + O: TryFrom + + From + + super::num_consts::NumConsts + + core::cmp::PartialEq + + core::fmt::Debug, + String: From, + >::Error: std::fmt::Debug, +{ + if core::mem::size_of::() <= core::mem::size_of::() { + // if the input type is smaller than the output type, then `I::MAX` should fit into `O` + let v = I::MAX; + assert_eq!( + O::try_from(v), + Ok(O::try_from(v).unwrap()), + "input::MAX value should fit" + ); + } else { + // if the input is bigger than the output, then `I::MAX` should not fit into `O` + let v = I::MAX; + assert_eq!( + O::try_from(v), + Err(crate::ConversionOverflowError::new( + input_type, + output_type, + v + )), + "input::MAX value should not fit" + ); + // but `O::MAX` should fit + let max = I::try_from(O::MAX).unwrap(); + assert_eq!( + O::try_from(max), + Ok(O::try_from(max).unwrap()), + "output::MAX value should fit" + ); + // while `O::MAX + 1` should not + let v = max + I::ONE; + assert_eq!( + O::try_from(v), + Err(crate::ConversionOverflowError::new( + input_type, + output_type, + v + )), + "output::MAX + 1 should not fit" + ); + } + + // negative numbers should fail + let v = I::from(-42i32); + assert_eq!( + O::try_from(v), + Err(crate::ConversionOverflowError::new( + input_type, + output_type, + v + )), + "negative numbers should not fit" + ); + + // zero should work + let v = I::ZERO; + assert_eq!(O::try_from(v), Ok(O::ZERO), "zero should fit"); + + // 42 should work + assert_eq!( + O::try_from(I::from(42i32)), + Ok(O::from(42u32)), + "42 should fit" + ) +} + +/// Helper macro to implement `TryFrom` for a conversion from a signed int to an unsigned int. +/// This is needed because `bnum` does not implement `TryFrom` for all of those conversions. +macro_rules! try_from_int_to_uint { + ($input: ty, $output: ty) => { + // statically assert that... + // input is signed + static_assertions::const_assert_eq!(stringify!($input).as_bytes()[0], b'I'); + // output is unsigned + static_assertions::const_assert_eq!(stringify!($output).as_bytes()[0], b'U'); + + impl TryFrom<$input> for $output { + type Error = ConversionOverflowError; + + fn try_from(value: $input) -> Result { + use bnum::prelude::As; + // if $input::MAX is smaller than $output::MAX, we only need to check the sign + if core::mem::size_of::<$input>() <= core::mem::size_of::<$output>() { + if value.is_negative() { + return Err(ConversionOverflowError::new( + stringify!($input), + stringify!($output), + value, + )); + } + + // otherwise we can just cast it + Ok(Self(value.0.as_())) + } else { + // $output::MAX is smaller than $input::MAX. + // If it is negative or too big, we error. + // We can safely cast $output::MAX to $input size + if value.is_negative() || value.0 > <$output>::MAX.0.as_() { + return Err(ConversionOverflowError::new( + stringify!($input), + stringify!($output), + value, + )); + } + + // at this point we know it fits + Ok(Self(value.0.as_())) + } + } + } + }; +} +pub(crate) use try_from_int_to_uint; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn grow_be_int_works() { + // test against rust std's integers + let i32s = [i32::MIN, -1, 0, 1, 42, i32::MAX]; + for i in i32s { + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i64).to_be_bytes()); + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i128).to_be_bytes()); + } + let i8s = [i8::MIN, -1, 0, 1, 42, i8::MAX]; + for i in i8s { + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i16).to_be_bytes()); + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i32).to_be_bytes()); + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i64).to_be_bytes()); + assert_eq!(grow_be_int(i.to_be_bytes()), (i as i128).to_be_bytes()); + } + } + + #[test] + fn shrink_be_int_works() { + // test against rust std's integers + let i32s = [-42, -1, 0i32, 1, 42]; + for i in i32s { + assert_eq!( + shrink_be_int(i.to_be_bytes()), + Some((i as i16).to_be_bytes()) + ); + assert_eq!( + shrink_be_int(i.to_be_bytes()), + Some((i as i8).to_be_bytes()) + ); + } + // these should be too big to fit into an i16 or i8 + let oob = [ + i32::MIN, + i32::MIN + 10, + i32::MIN + 1234, + i32::MAX - 1234, + i32::MAX - 10, + i32::MAX, + ]; + for i in oob { + // 32 -> 16 bit + assert_eq!(shrink_be_int::<4, 2>(i.to_be_bytes()), None); + // 32 -> 8 bit + assert_eq!(shrink_be_int::<4, 1>(i.to_be_bytes()), None); + } + + // compare against whole i16 range + for i in i16::MIN..=i16::MAX { + let cast = i as i8 as i16; + if i == cast { + // if the cast is lossless, `shrink_be_int` should get the same result + assert_eq!( + shrink_be_int::<2, 1>(i.to_be_bytes()), + Some((i as i8).to_be_bytes()) + ); + } else { + // otherwise, we should get None + assert_eq!(shrink_be_int::<2, 1>(i.to_be_bytes()), None); + } + } + } +} diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index b8de3e90b..12837bc21 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -11,7 +11,7 @@ use crate::errors::{ CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, OverflowOperation, RoundUpOverflowError, StdError, }; -use crate::{forward_ref_partial_eq, Decimal256}; +use crate::{forward_ref_partial_eq, Decimal256, SignedDecimal, SignedDecimal256}; use super::Fraction; use super::Isqrt; @@ -289,7 +289,7 @@ impl Decimal { .try_into() .map(Self) .map_err(|_| OverflowError { - operation: crate::OverflowOperation::Mul, + operation: OverflowOperation::Mul, operand1: self.to_string(), operand2: other.to_string(), }) @@ -331,7 +331,7 @@ impl Decimal { } inner(self, exp).map_err(|_| OverflowError { - operation: crate::OverflowOperation::Pow, + operation: OverflowOperation::Pow, operand1: self.to_string(), operand2: exp.to_string(), }) @@ -435,7 +435,7 @@ impl Decimal { /// let d = Decimal::from_str("75.0").unwrap(); /// assert_eq!(d.to_uint_floor(), Uint128::new(75)); /// ``` - #[must_use] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_uint_floor(self) -> Uint128 { self.0 / Self::DECIMAL_FRACTIONAL } @@ -458,7 +458,7 @@ impl Decimal { /// let d = Decimal::from_str("75.0").unwrap(); /// assert_eq!(d.to_uint_ceil(), Uint128::new(75)); /// ``` - #[must_use] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_uint_ceil(self) -> Uint128 { // Using `q = 1 + ((x - 1) / y); // if x != 0` with unsigned integers x, y, q // from https://stackoverflow.com/a/2745086/2013738. We know `x + y` CAN overflow. @@ -510,6 +510,30 @@ impl TryFrom for Decimal { } } +impl TryFrom for Decimal { + type Error = DecimalRangeExceeded; + + fn try_from(value: SignedDecimal) -> Result { + value + .atomics() + .try_into() + .map(Decimal) + .map_err(|_| DecimalRangeExceeded) + } +} + +impl TryFrom for Decimal { + type Error = DecimalRangeExceeded; + + fn try_from(value: SignedDecimal256) -> Result { + value + .atomics() + .try_into() + .map(Decimal) + .map_err(|_| DecimalRangeExceeded) + } +} + impl FromStr for Decimal { type Err = StdError; @@ -781,7 +805,7 @@ impl<'de> de::Visitor<'de> for DecimalVisitor { #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, to_json_vec}; fn dec(input: &str) -> Decimal { Decimal::from_str(input).unwrap() @@ -845,6 +869,34 @@ mod tests { ); } + #[test] + fn decimal_try_from_signed_works() { + assert_eq!( + Decimal::try_from(SignedDecimal::MAX).unwrap(), + Decimal::raw(SignedDecimal::MAX.atomics().i128() as u128) + ); + assert_eq!( + Decimal::try_from(SignedDecimal::zero()).unwrap(), + Decimal::zero() + ); + assert_eq!( + Decimal::try_from(SignedDecimal::one()).unwrap(), + Decimal::one() + ); + assert_eq!( + Decimal::try_from(SignedDecimal::percent(50)).unwrap(), + Decimal::percent(50) + ); + assert_eq!( + Decimal::try_from(SignedDecimal::negative_one()), + Err(DecimalRangeExceeded) + ); + assert_eq!( + Decimal::try_from(SignedDecimal::MIN), + Err(DecimalRangeExceeded) + ); + } + #[test] fn decimal_from_atomics_works() { let one = Decimal::one(); @@ -1069,7 +1121,7 @@ mod tests { } #[test] - fn decimal_from_str_errors_for_broken_fractinal_part() { + fn decimal_from_str_errors_for_broken_fractional_part() { match Decimal::from_str("1.").unwrap_err() { StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), e => panic!("Unexpected error: {e:?}"), @@ -1485,7 +1537,7 @@ mod tests { assert_eq!( Decimal::MAX.checked_mul(Decimal::percent(200)), Err(OverflowError { - operation: crate::OverflowOperation::Mul, + operation: OverflowOperation::Mul, operand1: Decimal::MAX.to_string(), operand2: Decimal::percent(200).to_string(), }) @@ -1727,7 +1779,7 @@ mod tests { assert_eq!(Decimal::one().checked_pow(exp).unwrap(), Decimal::one()); } - // This case is mathematically undefined but we ensure consistency with Rust stdandard types + // This case is mathematically undefined but we ensure consistency with Rust standard types // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=20df6716048e77087acd40194b233494 assert_eq!(Decimal::zero().checked_pow(0).unwrap(), Decimal::one()); @@ -1799,7 +1851,7 @@ mod tests { assert_eq!( Decimal::MAX.checked_pow(2), Err(OverflowError { - operation: crate::OverflowOperation::Pow, + operation: OverflowOperation::Pow, operand1: Decimal::MAX.to_string(), operand2: "2".to_string(), }) @@ -1908,35 +1960,35 @@ mod tests { #[test] fn decimal_serialize() { - assert_eq!(to_vec(&Decimal::zero()).unwrap(), br#""0""#); - assert_eq!(to_vec(&Decimal::one()).unwrap(), br#""1""#); - assert_eq!(to_vec(&Decimal::percent(8)).unwrap(), br#""0.08""#); - assert_eq!(to_vec(&Decimal::percent(87)).unwrap(), br#""0.87""#); - assert_eq!(to_vec(&Decimal::percent(876)).unwrap(), br#""8.76""#); - assert_eq!(to_vec(&Decimal::percent(8765)).unwrap(), br#""87.65""#); + assert_eq!(to_json_vec(&Decimal::zero()).unwrap(), br#""0""#); + assert_eq!(to_json_vec(&Decimal::one()).unwrap(), br#""1""#); + assert_eq!(to_json_vec(&Decimal::percent(8)).unwrap(), br#""0.08""#); + assert_eq!(to_json_vec(&Decimal::percent(87)).unwrap(), br#""0.87""#); + assert_eq!(to_json_vec(&Decimal::percent(876)).unwrap(), br#""8.76""#); + assert_eq!(to_json_vec(&Decimal::percent(8765)).unwrap(), br#""87.65""#); } #[test] fn decimal_deserialize() { - assert_eq!(from_slice::(br#""0""#).unwrap(), Decimal::zero()); - assert_eq!(from_slice::(br#""1""#).unwrap(), Decimal::one()); - assert_eq!(from_slice::(br#""000""#).unwrap(), Decimal::zero()); - assert_eq!(from_slice::(br#""001""#).unwrap(), Decimal::one()); + assert_eq!(from_json::(br#""0""#).unwrap(), Decimal::zero()); + assert_eq!(from_json::(br#""1""#).unwrap(), Decimal::one()); + assert_eq!(from_json::(br#""000""#).unwrap(), Decimal::zero()); + assert_eq!(from_json::(br#""001""#).unwrap(), Decimal::one()); assert_eq!( - from_slice::(br#""0.08""#).unwrap(), + from_json::(br#""0.08""#).unwrap(), Decimal::percent(8) ); assert_eq!( - from_slice::(br#""0.87""#).unwrap(), + from_json::(br#""0.87""#).unwrap(), Decimal::percent(87) ); assert_eq!( - from_slice::(br#""8.76""#).unwrap(), + from_json::(br#""8.76""#).unwrap(), Decimal::percent(876) ); assert_eq!( - from_slice::(br#""87.65""#).unwrap(), + from_json::(br#""87.65""#).unwrap(), Decimal::percent(8765) ); } diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index f3fea968c..f0db608d2 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -11,7 +11,7 @@ use crate::errors::{ CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, OverflowOperation, RoundUpOverflowError, StdError, }; -use crate::{forward_ref_partial_eq, Decimal, Uint512}; +use crate::{forward_ref_partial_eq, Decimal, SignedDecimal, SignedDecimal256, Uint512}; use super::Fraction; use super::Isqrt; @@ -33,15 +33,9 @@ pub struct Decimal256RangeExceeded; impl Decimal256 { const DECIMAL_FRACTIONAL: Uint256 = // 1*10**18 - Uint256::from_be_bytes([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 224, 182, - 179, 167, 100, 0, 0, - ]); + Uint256::from_u128(1_000_000_000_000_000_000); const DECIMAL_FRACTIONAL_SQUARED: Uint256 = // 1*10**36 - Uint256::from_be_bytes([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 151, 206, 123, 201, 7, 21, 179, - 75, 159, 16, 0, 0, 0, 0, - ]); + Uint256::from_u128(1_000_000_000_000_000_000_000_000_000_000_000_000); /// The number of decimal places. Since decimal types are fixed-point rather than /// floating-point, this is a constant. @@ -304,7 +298,7 @@ impl Decimal256 { .try_into() .map(Self) .map_err(|_| OverflowError { - operation: crate::OverflowOperation::Mul, + operation: OverflowOperation::Mul, operand1: self.to_string(), operand2: other.to_string(), }) @@ -346,7 +340,7 @@ impl Decimal256 { } inner(self, exp).map_err(|_| OverflowError { - operation: crate::OverflowOperation::Pow, + operation: OverflowOperation::Pow, operand1: self.to_string(), operand2: exp.to_string(), }) @@ -454,7 +448,7 @@ impl Decimal256 { /// let d = Decimal256::from_str("75.0").unwrap(); /// assert_eq!(d.to_uint_floor(), Uint256::from(75u64)); /// ``` - #[must_use] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_uint_floor(self) -> Uint256 { self.0 / Self::DECIMAL_FRACTIONAL } @@ -477,7 +471,7 @@ impl Decimal256 { /// let d = Decimal256::from_str("75.0").unwrap(); /// assert_eq!(d.to_uint_ceil(), Uint256::from(75u64)); /// ``` - #[must_use] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_uint_ceil(self) -> Uint256 { // Using `q = 1 + ((x - 1) / y); // if x != 0` with unsigned integers x, y, q // from https://stackoverflow.com/a/2745086/2013738. We know `x + y` CAN overflow. @@ -525,6 +519,30 @@ impl From for Decimal256 { } } +impl TryFrom for Decimal256 { + type Error = Decimal256RangeExceeded; + + fn try_from(value: SignedDecimal) -> Result { + value + .atomics() + .try_into() + .map(Decimal256) + .map_err(|_| Decimal256RangeExceeded) + } +} + +impl TryFrom for Decimal256 { + type Error = Decimal256RangeExceeded; + + fn try_from(value: SignedDecimal256) -> Result { + value + .atomics() + .try_into() + .map(Decimal256) + .map_err(|_| Decimal256RangeExceeded) + } +} + impl FromStr for Decimal256 { type Err = StdError; @@ -797,7 +815,7 @@ impl<'de> de::Visitor<'de> for Decimal256Visitor { mod tests { use super::*; use crate::errors::StdError; - use crate::{from_slice, to_vec}; + use crate::{from_json, to_json_vec}; fn dec(input: &str) -> Decimal256 { Decimal256::from_str(input).unwrap() @@ -1145,7 +1163,7 @@ mod tests { } #[test] - fn decimal256_from_str_errors_for_broken_fractinal_part() { + fn decimal256_from_str_errors_for_broken_fractional_part() { match Decimal256::from_str("1.").unwrap_err() { StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), e => panic!("Unexpected error: {e:?}"), @@ -1581,7 +1599,7 @@ mod tests { assert_eq!( Decimal256::MAX.checked_mul(Decimal256::percent(200)), Err(OverflowError { - operation: crate::OverflowOperation::Mul, + operation: OverflowOperation::Mul, operand1: Decimal256::MAX.to_string(), operand2: Decimal256::percent(200).to_string(), }) @@ -1830,7 +1848,7 @@ mod tests { ); } - // This case is mathematically undefined but we ensure consistency with Rust stdandard types + // This case is mathematically undefined but we ensure consistency with Rust standard types // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=20df6716048e77087acd40194b233494 assert_eq!( Decimal256::zero().checked_pow(0).unwrap(), @@ -1908,7 +1926,7 @@ mod tests { assert_eq!( Decimal256::MAX.checked_pow(2), Err(OverflowError { - operation: crate::OverflowOperation::Pow, + operation: OverflowOperation::Pow, operand1: Decimal256::MAX.to_string(), operand2: "2".to_string(), }) @@ -2020,47 +2038,53 @@ mod tests { #[test] fn decimal256_serialize() { - assert_eq!(to_vec(&Decimal256::zero()).unwrap(), br#""0""#); - assert_eq!(to_vec(&Decimal256::one()).unwrap(), br#""1""#); - assert_eq!(to_vec(&Decimal256::percent(8)).unwrap(), br#""0.08""#); - assert_eq!(to_vec(&Decimal256::percent(87)).unwrap(), br#""0.87""#); - assert_eq!(to_vec(&Decimal256::percent(876)).unwrap(), br#""8.76""#); - assert_eq!(to_vec(&Decimal256::percent(8765)).unwrap(), br#""87.65""#); + assert_eq!(to_json_vec(&Decimal256::zero()).unwrap(), br#""0""#); + assert_eq!(to_json_vec(&Decimal256::one()).unwrap(), br#""1""#); + assert_eq!(to_json_vec(&Decimal256::percent(8)).unwrap(), br#""0.08""#); + assert_eq!(to_json_vec(&Decimal256::percent(87)).unwrap(), br#""0.87""#); + assert_eq!( + to_json_vec(&Decimal256::percent(876)).unwrap(), + br#""8.76""# + ); + assert_eq!( + to_json_vec(&Decimal256::percent(8765)).unwrap(), + br#""87.65""# + ); } #[test] fn decimal256_deserialize() { assert_eq!( - from_slice::(br#""0""#).unwrap(), + from_json::(br#""0""#).unwrap(), Decimal256::zero() ); assert_eq!( - from_slice::(br#""1""#).unwrap(), + from_json::(br#""1""#).unwrap(), Decimal256::one() ); assert_eq!( - from_slice::(br#""000""#).unwrap(), + from_json::(br#""000""#).unwrap(), Decimal256::zero() ); assert_eq!( - from_slice::(br#""001""#).unwrap(), + from_json::(br#""001""#).unwrap(), Decimal256::one() ); assert_eq!( - from_slice::(br#""0.08""#).unwrap(), + from_json::(br#""0.08""#).unwrap(), Decimal256::percent(8) ); assert_eq!( - from_slice::(br#""0.87""#).unwrap(), + from_json::(br#""0.87""#).unwrap(), Decimal256::percent(87) ); assert_eq!( - from_slice::(br#""8.76""#).unwrap(), + from_json::(br#""8.76""#).unwrap(), Decimal256::percent(876) ); assert_eq!( - from_slice::(br#""87.65""#).unwrap(), + from_json::(br#""87.65""#).unwrap(), Decimal256::percent(8765) ); } diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index cc0b7c83b..bc5cd241f 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -9,7 +9,13 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Int64, Uint128, Uint64}; +use crate::{ + forward_ref_partial_eq, CheckedMultiplyRatioError, Int256, Int512, Int64, Uint128, Uint256, + Uint512, Uint64, +}; + +use super::conversion::{forward_try_from, try_from_int_to_int}; +use super::num_consts::NumConsts; /// An implementation of i128 that is using strings for JSON encoding/decoding, /// such that the full i128 range can be used for clients that convert JSON numbers to floats, @@ -25,7 +31,7 @@ use crate::{forward_ref_partial_eq, Int64, Uint128, Uint64}; /// assert_eq!(a.i128(), 258); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Int128(#[schemars(with = "String")] i128); +pub struct Int128(#[schemars(with = "String")] pub(crate) i128); forward_ref_partial_eq!(Int128, Int128); @@ -85,11 +91,55 @@ impl Int128 { self.0 == 0 } + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.is_negative() + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { Self(self.0.pow(exp)) } + /// Returns `self * numerator / denominator`. + /// + /// Due to the nature of the integer division involved, the result is always floored. + /// E.g. 5 * 99/100 = 4. + pub fn checked_multiply_ratio, B: Into>( + &self, + numerator: A, + denominator: B, + ) -> Result { + let numerator = numerator.into(); + let denominator = denominator.into(); + if denominator.is_zero() { + return Err(CheckedMultiplyRatioError::DivideByZero); + } + match (self.full_mul(numerator) / Int256::from(denominator)).try_into() { + Ok(ratio) => Ok(ratio), + Err(_) => Err(CheckedMultiplyRatioError::Overflow), + } + } + + /// Multiplies two [`Int128`] values without overflow, producing an + /// [`Int256`]. + /// + /// # Examples + /// + /// ``` + /// use cosmwasm_std::Int128; + /// + /// let a = Int128::MAX; + /// let result = a.full_mul(2i32); + /// assert_eq!(result.to_string(), "340282366920938463463374607431768211454"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn full_mul(self, rhs: impl Into) -> Int256 { + Int256::from(self.i128()) + .checked_mul(Int256::from(rhs.into())) + .unwrap() + } + pub fn checked_add(self, other: Self) -> Result { self.0 .checked_add(other.0) @@ -209,14 +259,36 @@ impl Int128 { pub const fn abs_diff(self, other: Self) -> Uint128 { Uint128(self.0.abs_diff(other.0)) } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs(self) -> Self { + Self(self.0.abs()) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn unsigned_abs(self) -> Uint128 { + Uint128(self.0.unsigned_abs()) + } } +impl NumConsts for Int128 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + +// Uint to Int impl From for Int128 { fn from(val: Uint64) -> Self { val.u64().into() } } +forward_try_from!(Uint128, Int128); +forward_try_from!(Uint256, Int128); +forward_try_from!(Uint512, Int128); +// uint to Int impl From for Int128 { fn from(val: u64) -> Self { Int128(val.into()) @@ -241,12 +313,17 @@ impl From for Int128 { } } +// Int to Int impl From for Int128 { fn from(val: Int64) -> Self { val.i64().into() } } +try_from_int_to_int!(Int256, Int128); +try_from_int_to_int!(Int512, Int128); + +// int to Int impl From for Int128 { fn from(val: i128) -> Self { Int128(val) @@ -493,7 +570,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, math::conversion::test_try_from_uint_to_int, to_json_vec}; #[test] fn size_of_works() { @@ -608,6 +685,13 @@ mod tests { assert!(result.is_err()); } + #[test] + fn int128_try_from_unsigned_works() { + test_try_from_uint_to_int::("Uint128", "Int128"); + test_try_from_uint_to_int::("Uint256", "Int128"); + test_try_from_uint_to_int::("Uint512", "Int128"); + } + #[test] fn int128_implements_display() { let a = Int128::from(12345u32); @@ -694,6 +778,16 @@ mod tests { assert!(!Int128::from(-123i32).is_zero()); } + #[test] + fn int128_is_negative_works() { + assert!(Int128::MIN.is_negative()); + assert!(Int128::from(-123i32).is_negative()); + + assert!(!Int128::MAX.is_negative()); + assert!(!Int128::zero().is_negative()); + assert!(!Int128::from(123u32).is_negative()); + } + #[test] fn int128_wrapping_methods() { // wrapping_add @@ -728,9 +822,9 @@ mod tests { #[test] fn int128_json() { let orig = Int128::from(1234567890987654321i128); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Int128 = from_slice(&serialized).unwrap(); + let parsed: Int128 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } @@ -863,6 +957,65 @@ mod tests { _ = Int128::MAX.pow(2u32); } + #[test] + fn int128_checked_multiply_ratio_works() { + let base = Int128(500); + + // factor 1/1 + assert_eq!(base.checked_multiply_ratio(1i128, 1i128).unwrap(), base); + assert_eq!(base.checked_multiply_ratio(3i128, 3i128).unwrap(), base); + assert_eq!( + base.checked_multiply_ratio(654321i128, 654321i128).unwrap(), + base + ); + assert_eq!( + base.checked_multiply_ratio(i128::MAX, i128::MAX).unwrap(), + base + ); + + // factor 3/2 + assert_eq!( + base.checked_multiply_ratio(3i128, 2i128).unwrap(), + Int128(750) + ); + assert_eq!( + base.checked_multiply_ratio(333333i128, 222222i128).unwrap(), + Int128(750) + ); + + // factor 2/3 (integer devision always floors the result) + assert_eq!( + base.checked_multiply_ratio(2i128, 3i128).unwrap(), + Int128(333) + ); + assert_eq!( + base.checked_multiply_ratio(222222i128, 333333i128).unwrap(), + Int128(333) + ); + + // factor 5/6 (integer devision always floors the result) + assert_eq!( + base.checked_multiply_ratio(5i128, 6i128).unwrap(), + Int128(416) + ); + assert_eq!( + base.checked_multiply_ratio(100i128, 120i128).unwrap(), + Int128(416) + ); + } + + #[test] + fn int128_checked_multiply_ratio_does_not_panic() { + assert_eq!( + Int128(500i128).checked_multiply_ratio(1i128, 0i128), + Err(CheckedMultiplyRatioError::DivideByZero), + ); + assert_eq!( + Int128(500i128).checked_multiply_ratio(i128::MAX, 1i128), + Err(CheckedMultiplyRatioError::Overflow), + ); + } + #[test] fn int128_shr_works() { let original = Int128::from_be_bytes([ @@ -1088,6 +1241,37 @@ mod tests { assert_eq!(c.abs_diff(b), Uint128::from(10u32)); } + #[test] + fn int128_abs_works() { + let a = Int128::from(42i32); + assert_eq!(a.abs(), a); + + let b = Int128::from(-42i32); + assert_eq!(b.abs(), a); + + assert_eq!(Int128::zero().abs(), Int128::zero()); + assert_eq!((Int128::MIN + Int128::one()).abs(), Int128::MAX); + } + + #[test] + fn int128_unsigned_abs_works() { + assert_eq!(Int128::zero().unsigned_abs(), Uint128::zero()); + assert_eq!(Int128::one().unsigned_abs(), Uint128::one()); + assert_eq!( + Int128::MIN.unsigned_abs(), + Uint128::new(Int128::MAX.0 as u128) + Uint128::one() + ); + + let v = Int128::from(-42i32); + assert_eq!(v.unsigned_abs(), v.abs_diff(Int128::zero())); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int128_abs_min_panics() { + _ = Int128::MIN.abs(); + } + #[test] #[should_panic = "attempt to negate with overflow"] fn int128_neg_min_panics() { diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs index 5616fcc2e..70d17417d 100644 --- a/packages/std/src/math/int256.rs +++ b/packages/std/src/math/int256.rs @@ -9,12 +9,18 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Int128, Int64, Uint128, Uint256, Uint64}; +use crate::{ + forward_ref_partial_eq, CheckedMultiplyRatioError, Int128, Int512, Int64, Uint128, Uint256, + Uint512, Uint64, +}; /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. use bnum::types::{I256, U256}; +use super::conversion::{grow_be_int, try_from_int_to_int, try_from_uint_to_int}; +use super::num_consts::NumConsts; + /// An implementation of i256 that is using strings for JSON encoding/decoding, /// such that the full i256 range can be used for clients that convert JSON numbers to floats, /// like JavaScript and jq. @@ -36,7 +42,7 @@ use bnum::types::{I256, U256}; /// assert_eq!(a, b); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Int256(#[schemars(with = "String")] I256); +pub struct Int256(#[schemars(with = "String")] pub(crate) I256); forward_ref_partial_eq!(Int256, Int256); @@ -63,6 +69,12 @@ impl Int256 { Self(I256::ONE) } + /// A conversion from `i128` that, unlike the one provided by the `From` trait, + /// can be used in a `const` context. + pub const fn from_i128(v: i128) -> Self { + Self::from_be_bytes(grow_be_int(v.to_be_bytes())) + } + #[must_use] pub const fn from_be_bytes(data: [u8; 32]) -> Self { let words: [u64; 4] = [ @@ -134,11 +146,58 @@ impl Int256 { self.0.is_zero() } + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.is_negative() + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { Self(self.0.pow(exp)) } + /// Returns `self * numerator / denominator`. + /// + /// Due to the nature of the integer division involved, the result is always floored. + /// E.g. 5 * 99/100 = 4. + pub fn checked_multiply_ratio, B: Into>( + &self, + numerator: A, + denominator: B, + ) -> Result { + let numerator = numerator.into(); + let denominator = denominator.into(); + if denominator.is_zero() { + return Err(CheckedMultiplyRatioError::DivideByZero); + } + match (self.full_mul(numerator) / Int512::from(denominator)).try_into() { + Ok(ratio) => Ok(ratio), + Err(_) => Err(CheckedMultiplyRatioError::Overflow), + } + } + + /// Multiplies two [`Int256`] values without overflow, producing an + /// [`Int512`]. + /// + /// # Examples + /// + /// ``` + /// use cosmwasm_std::Int256; + /// + /// let a = Int256::MAX; + /// let result = a.full_mul(2i32); + /// assert_eq!( + /// result.to_string(), + /// "115792089237316195423570985008687907853269984665640564039457584007913129639934" + /// ); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn full_mul(self, rhs: impl Into) -> Int512 { + Int512::from(self) + .checked_mul(Int512::from(rhs.into())) + .unwrap() + } + pub fn checked_add(self, other: Self) -> Result { self.0 .checked_add(other.0) @@ -258,8 +317,29 @@ impl Int256 { pub const fn abs_diff(self, other: Self) -> Uint256 { Uint256(self.0.abs_diff(other.0)) } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs(self) -> Self { + Self(self.0.abs()) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn unsigned_abs(self) -> Uint256 { + Uint256(self.0.unsigned_abs()) + } +} + +impl NumConsts for Int256 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; } +// Uint to Int +try_from_uint_to_int!(Uint512, Int256); +try_from_uint_to_int!(Uint256, Int256); + impl From for Int256 { fn from(val: Uint128) -> Self { val.u128().into() @@ -272,6 +352,7 @@ impl From for Int256 { } } +// uint to Int impl From for Int256 { fn from(val: u128) -> Self { Int256(val.into()) @@ -302,6 +383,9 @@ impl From for Int256 { } } +// Int to Int +try_from_int_to_int!(Int512, Int256); + impl From for Int256 { fn from(val: Int128) -> Self { val.i128().into() @@ -314,6 +398,7 @@ impl From for Int256 { } } +// int to Int impl From for Int256 { fn from(val: i128) -> Self { Int256(val.into()) @@ -560,7 +645,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, math::conversion::test_try_from_uint_to_int, to_json_vec}; #[test] fn size_of_works() { @@ -671,6 +756,31 @@ mod tests { assert!(result.is_err()); } + #[test] + fn int256_try_from_unsigned_works() { + test_try_from_uint_to_int::("Uint256", "Int256"); + test_try_from_uint_to_int::("Uint512", "Int256"); + } + + #[test] + fn int256_from_i128() { + assert_eq!(Int256::from_i128(123i128), Int256::from_str("123").unwrap()); + + assert_eq!( + Int256::from_i128(9785746283745i128), + Int256::from_str("9785746283745").unwrap() + ); + + assert_eq!( + Int256::from_i128(i128::MAX).to_string(), + i128::MAX.to_string() + ); + assert_eq!( + Int256::from_i128(i128::MIN).to_string(), + i128::MIN.to_string() + ); + } + #[test] fn int256_implements_display() { let a = Int256::from(12345u32); @@ -775,6 +885,16 @@ mod tests { assert!(!Int256::from(-123i32).is_zero()); } + #[test] + fn int256_is_negative_works() { + assert!(Int256::MIN.is_negative()); + assert!(Int256::from(-123i32).is_negative()); + + assert!(!Int256::MAX.is_negative()); + assert!(!Int256::zero().is_negative()); + assert!(!Int256::from(123u32).is_negative()); + } + #[test] fn int256_wrapping_methods() { // wrapping_add @@ -809,9 +929,9 @@ mod tests { #[test] fn int256_json() { let orig = Int256::from(1234567890987654321u128); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Int256 = from_slice(&serialized).unwrap(); + let parsed: Int256 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } @@ -944,6 +1064,65 @@ mod tests { _ = Int256::MAX.pow(2u32); } + #[test] + fn int256_checked_multiply_ratio_works() { + let base = Int256::from_i128(500); + + // factor 1/1 + assert_eq!(base.checked_multiply_ratio(1i128, 1i128).unwrap(), base); + assert_eq!(base.checked_multiply_ratio(3i128, 3i128).unwrap(), base); + assert_eq!( + base.checked_multiply_ratio(654321i128, 654321i128).unwrap(), + base + ); + assert_eq!( + base.checked_multiply_ratio(i128::MAX, i128::MAX).unwrap(), + base + ); + + // factor 3/2 + assert_eq!( + base.checked_multiply_ratio(3i128, 2i128).unwrap(), + Int256::from_i128(750) + ); + assert_eq!( + base.checked_multiply_ratio(333333i128, 222222i128).unwrap(), + Int256::from_i128(750) + ); + + // factor 2/3 (integer devision always floors the result) + assert_eq!( + base.checked_multiply_ratio(2i128, 3i128).unwrap(), + Int256::from_i128(333) + ); + assert_eq!( + base.checked_multiply_ratio(222222i128, 333333i128).unwrap(), + Int256::from_i128(333) + ); + + // factor 5/6 (integer devision always floors the result) + assert_eq!( + base.checked_multiply_ratio(5i128, 6i128).unwrap(), + Int256::from_i128(416) + ); + assert_eq!( + base.checked_multiply_ratio(100i128, 120i128).unwrap(), + Int256::from_i128(416) + ); + } + + #[test] + fn int256_checked_multiply_ratio_does_not_panic() { + assert_eq!( + Int256::from_i128(500i128).checked_multiply_ratio(1i128, 0i128), + Err(CheckedMultiplyRatioError::DivideByZero), + ); + assert_eq!( + Int256::MAX.checked_multiply_ratio(Int256::MAX, 1i128), + Err(CheckedMultiplyRatioError::Overflow), + ); + } + #[test] fn int256_shr_works() { let original = Int256::new([ @@ -1171,6 +1350,37 @@ mod tests { assert_eq!(c.abs_diff(b), Uint256::from(10u32)); } + #[test] + fn int256_abs_works() { + let a = Int256::from(42i32); + assert_eq!(a.abs(), a); + + let b = Int256::from(-42i32); + assert_eq!(b.abs(), a); + + assert_eq!(Int256::zero().abs(), Int256::zero()); + assert_eq!((Int256::MIN + Int256::one()).abs(), Int256::MAX); + } + + #[test] + fn int256_unsigned_abs_works() { + assert_eq!(Int256::zero().unsigned_abs(), Uint256::zero()); + assert_eq!(Int256::one().unsigned_abs(), Uint256::one()); + assert_eq!( + Int256::MIN.unsigned_abs(), + Uint256::from_be_bytes(Int256::MAX.to_be_bytes()) + Uint256::one() + ); + + let v = Int256::from(-42i32); + assert_eq!(v.unsigned_abs(), v.abs_diff(Int256::zero())); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int256_abs_min_panics() { + _ = Int256::MIN.abs(); + } + #[test] #[should_panic = "attempt to negate with overflow"] fn int256_neg_min_panics() { diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 2b122418e..86b4a93b3 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -9,12 +9,15 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint512, Uint64}; +use crate::{forward_ref_partial_eq, Int128, Int256, Int64, Uint128, Uint256, Uint512, Uint64}; /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. use bnum::types::{I512, U512}; +use super::conversion::{grow_be_int, try_from_uint_to_int}; +use super::num_consts::NumConsts; + /// An implementation of i512 that is using strings for JSON encoding/decoding, /// such that the full i512 range can be used for clients that convert JSON numbers to floats, /// like JavaScript and jq. @@ -40,7 +43,7 @@ use bnum::types::{I512, U512}; /// assert_eq!(a, b); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Int512(#[schemars(with = "String")] I512); +pub struct Int512(#[schemars(with = "String")] pub(crate) I512); forward_ref_partial_eq!(Int512, Int512); @@ -170,6 +173,11 @@ impl Int512 { self.0.is_zero() } + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.is_negative() + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { Self(self.0.pow(exp)) @@ -294,8 +302,28 @@ impl Int512 { pub const fn abs_diff(self, other: Self) -> Uint512 { Uint512(self.0.abs_diff(other.0)) } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs(self) -> Self { + Self(self.0.abs()) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn unsigned_abs(self) -> Uint512 { + Uint512(self.0.unsigned_abs()) + } } +impl NumConsts for Int512 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + +// Uint to Int +try_from_uint_to_int!(Uint512, Int512); + impl From for Int512 { fn from(val: Uint256) -> Self { let mut bytes = [0u8; 64]; @@ -317,6 +345,7 @@ impl From for Int512 { } } +// uint to Int impl From for Int512 { fn from(val: u128) -> Self { Int512(val.into()) @@ -347,6 +376,7 @@ impl From for Int512 { } } +// int to Int impl From for Int512 { fn from(val: i128) -> Self { Int512(val.into()) @@ -377,6 +407,25 @@ impl From for Int512 { } } +// Int to Int +impl From for Int512 { + fn from(val: Int64) -> Self { + Int512(val.i64().into()) + } +} + +impl From for Int512 { + fn from(val: Int128) -> Self { + Int512(val.i128().into()) + } +} + +impl From for Int512 { + fn from(val: Int256) -> Self { + Self::from_be_bytes(grow_be_int(val.to_be_bytes())) + } +} + impl TryFrom<&str> for Int512 { type Error = StdError; @@ -593,7 +642,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, math::conversion::test_try_from_uint_to_int, to_json_vec}; #[test] fn size_of_works() { @@ -700,6 +749,40 @@ mod tests { let a = Int512::from(-5i8); assert_eq!(a.0, I512::from(-5i32)); + // other big signed integers + let values = [ + Int64::MAX, + Int64::MIN, + Int64::one(), + -Int64::one(), + Int64::zero(), + ]; + for v in values { + assert_eq!(Int512::from(v).to_string(), v.to_string()); + } + + let values = [ + Int128::MAX, + Int128::MIN, + Int128::one(), + -Int128::one(), + Int128::zero(), + ]; + for v in values { + assert_eq!(Int512::from(v).to_string(), v.to_string()); + } + + let values = [ + Int256::MAX, + Int256::MIN, + Int256::one(), + -Int256::one(), + Int256::zero(), + ]; + for v in values { + assert_eq!(Int512::from(v).to_string(), v.to_string()); + } + let result = Int512::try_from("34567"); assert_eq!( result.unwrap().0, @@ -710,6 +793,12 @@ mod tests { assert!(result.is_err()); } + #[test] + fn int512_try_from_unsigned_works() { + test_try_from_uint_to_int::("Uint256", "Int256"); + test_try_from_uint_to_int::("Uint512", "Int256"); + } + #[test] fn int512_implements_display() { let a = Int512::from(12345u32); @@ -824,6 +913,16 @@ mod tests { assert!(!Int512::from(-123i32).is_zero()); } + #[test] + fn int512_is_negative_works() { + assert!(Int512::MIN.is_negative()); + assert!(Int512::from(-123i32).is_negative()); + + assert!(!Int512::MAX.is_negative()); + assert!(!Int512::zero().is_negative()); + assert!(!Int512::from(123u32).is_negative()); + } + #[test] fn int512_wrapping_methods() { // wrapping_add @@ -858,9 +957,9 @@ mod tests { #[test] fn int512_json() { let orig = Int512::from(1234567890987654321u128); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Int512 = from_slice(&serialized).unwrap(); + let parsed: Int512 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } @@ -1224,6 +1323,37 @@ mod tests { assert_eq!(c.abs_diff(b), Uint512::from(10u32)); } + #[test] + fn int512_abs_works() { + let a = Int512::from(42i32); + assert_eq!(a.abs(), a); + + let b = Int512::from(-42i32); + assert_eq!(b.abs(), a); + + assert_eq!(Int512::zero().abs(), Int512::zero()); + assert_eq!((Int512::MIN + Int512::one()).abs(), Int512::MAX); + } + + #[test] + fn int512_unsigned_abs_works() { + assert_eq!(Int512::zero().unsigned_abs(), Uint512::zero()); + assert_eq!(Int512::one().unsigned_abs(), Uint512::one()); + assert_eq!( + Int512::MIN.unsigned_abs(), + Uint512::from_be_bytes(Int512::MAX.to_be_bytes()) + Uint512::one() + ); + + let v = Int512::from(-42i32); + assert_eq!(v.unsigned_abs(), v.abs_diff(Int512::zero())); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int512_abs_min_panics() { + _ = Int512::MIN.abs(); + } + #[test] #[should_panic = "attempt to negate with overflow"] fn int512_neg_min_panics() { diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 2e9ac7f68..86a9bc477 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -9,7 +9,13 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Uint64}; +use crate::{ + forward_ref_partial_eq, CheckedMultiplyRatioError, Int128, Int256, Int512, Uint128, Uint256, + Uint512, Uint64, +}; + +use super::conversion::{forward_try_from, try_from_int_to_int}; +use super::num_consts::NumConsts; /// An implementation of i64 that is using strings for JSON encoding/decoding, /// such that the full i64 range can be used for clients that convert JSON numbers to floats, @@ -25,7 +31,7 @@ use crate::{forward_ref_partial_eq, Uint64}; /// assert_eq!(a.i64(), 258); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Int64(#[schemars(with = "String")] i64); +pub struct Int64(#[schemars(with = "String")] pub(crate) i64); forward_ref_partial_eq!(Int64, Int64); @@ -85,11 +91,55 @@ impl Int64 { self.0 == 0 } + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.is_negative() + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { Self(self.0.pow(exp)) } + /// Returns `self * numerator / denominator`. + /// + /// Due to the nature of the integer division involved, the result is always floored. + /// E.g. 5 * 99/100 = 4. + pub fn checked_multiply_ratio, B: Into>( + &self, + numerator: A, + denominator: B, + ) -> Result { + let numerator = numerator.into(); + let denominator = denominator.into(); + if denominator.is_zero() { + return Err(CheckedMultiplyRatioError::DivideByZero); + } + match (self.full_mul(numerator) / Int128::from(denominator)).try_into() { + Ok(ratio) => Ok(ratio), + Err(_) => Err(CheckedMultiplyRatioError::Overflow), + } + } + + /// Multiplies two [`Int64`] values without overflow, producing an + /// [`Int128`]. + /// + /// # Examples + /// + /// ``` + /// use cosmwasm_std::Int64; + /// + /// let a = Int64::MAX; + /// let result = a.full_mul(2i32); + /// assert_eq!(result.to_string(), "18446744073709551614"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn full_mul(self, rhs: impl Into) -> Int128 { + Int128::from(self) + .checked_mul(Int128::from(rhs.into())) + .unwrap() + } + pub fn checked_add(self, other: Self) -> Result { self.0 .checked_add(other.0) @@ -209,8 +259,26 @@ impl Int64 { pub const fn abs_diff(self, other: Self) -> Uint64 { Uint64(self.0.abs_diff(other.0)) } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs(self) -> Self { + Self(self.0.abs()) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn unsigned_abs(self) -> Uint64 { + Uint64(self.0.unsigned_abs()) + } } +impl NumConsts for Int64 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + +// uint to Int impl From for Int64 { fn from(val: u32) -> Self { Int64(val.into()) @@ -229,6 +297,7 @@ impl From for Int64 { } } +// int to Int impl From for Int64 { fn from(val: i64) -> Self { Int64(val) @@ -253,6 +322,17 @@ impl From for Int64 { } } +// Int to Int +try_from_int_to_int!(Int128, Int64); +try_from_int_to_int!(Int256, Int64); +try_from_int_to_int!(Int512, Int64); + +// Uint to Int +forward_try_from!(Uint64, Int64); +forward_try_from!(Uint128, Int64); +forward_try_from!(Uint256, Int64); +forward_try_from!(Uint512, Int64); + impl TryFrom<&str> for Int64 { type Error = StdError; @@ -469,7 +549,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, math::conversion::test_try_from_uint_to_int, to_json_vec}; #[test] fn size_of_works() { @@ -578,6 +658,14 @@ mod tests { assert!(result.is_err()); } + #[test] + fn int64_try_from_unsigned_works() { + test_try_from_uint_to_int::("Uint64", "Int64"); + test_try_from_uint_to_int::("Uint128", "Int64"); + test_try_from_uint_to_int::("Uint256", "Int64"); + test_try_from_uint_to_int::("Uint512", "Int64"); + } + #[test] fn int64_implements_display() { let a = Int64::from(12345u32); @@ -662,6 +750,16 @@ mod tests { assert!(!Int64::from(-123i32).is_zero()); } + #[test] + fn int64_is_negative_works() { + assert!(Int64::MIN.is_negative()); + assert!(Int64::from(-123i32).is_negative()); + + assert!(!Int64::MAX.is_negative()); + assert!(!Int64::zero().is_negative()); + assert!(!Int64::from(123u32).is_negative()); + } + #[test] fn int64_wrapping_methods() { // wrapping_add @@ -696,9 +794,9 @@ mod tests { #[test] fn int64_json() { let orig = Int64::from(1234567890987654321i64); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Int64 = from_slice(&serialized).unwrap(); + let parsed: Int64 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } @@ -831,6 +929,56 @@ mod tests { _ = Int64::MAX.pow(2u32); } + #[test] + fn int64_checked_multiply_ratio_works() { + let base = Int64(500); + + // factor 1/1 + assert_eq!(base.checked_multiply_ratio(1i64, 1i64).unwrap(), base); + assert_eq!(base.checked_multiply_ratio(3i64, 3i64).unwrap(), base); + assert_eq!( + base.checked_multiply_ratio(654321i64, 654321i64).unwrap(), + base + ); + assert_eq!( + base.checked_multiply_ratio(i64::MAX, i64::MAX).unwrap(), + base + ); + + // factor 3/2 + assert_eq!(base.checked_multiply_ratio(3i64, 2i64).unwrap(), Int64(750)); + assert_eq!( + base.checked_multiply_ratio(333333i64, 222222i64).unwrap(), + Int64(750) + ); + + // factor 2/3 (integer devision always floors the result) + assert_eq!(base.checked_multiply_ratio(2i64, 3i64).unwrap(), Int64(333)); + assert_eq!( + base.checked_multiply_ratio(222222i64, 333333i64).unwrap(), + Int64(333) + ); + + // factor 5/6 (integer devision always floors the result) + assert_eq!(base.checked_multiply_ratio(5i64, 6i64).unwrap(), Int64(416)); + assert_eq!( + base.checked_multiply_ratio(100i64, 120i64).unwrap(), + Int64(416) + ); + } + + #[test] + fn int64_checked_multiply_ratio_does_not_panic() { + assert_eq!( + Int64(500i64).checked_multiply_ratio(1i64, 0i64), + Err(CheckedMultiplyRatioError::DivideByZero), + ); + assert_eq!( + Int64(500i64).checked_multiply_ratio(i64::MAX, 1i64), + Err(CheckedMultiplyRatioError::Overflow), + ); + } + #[test] fn int64_shr_works() { let original = Int64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8]); @@ -1031,6 +1179,37 @@ mod tests { assert_eq!(c.abs_diff(b), Uint64::from(10u32)); } + #[test] + fn int64_abs_works() { + let a = Int64::from(42i32); + assert_eq!(a.abs(), a); + + let b = Int64::from(-42i32); + assert_eq!(b.abs(), a); + + assert_eq!(Int64::zero().abs(), Int64::zero()); + assert_eq!((Int64::MIN + Int64::one()).abs(), Int64::MAX); + } + + #[test] + fn int64_unsigned_abs_works() { + assert_eq!(Int64::zero().unsigned_abs(), Uint64::zero()); + assert_eq!(Int64::one().unsigned_abs(), Uint64::one()); + assert_eq!( + Int64::MIN.unsigned_abs(), + Uint64::new(Int64::MAX.0 as u64) + Uint64::one() + ); + + let v = Int64::from(-42i32); + assert_eq!(v.unsigned_abs(), v.abs_diff(Int64::zero())); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int64_abs_min_panics() { + _ = Int64::MIN.abs(); + } + #[test] #[should_panic = "attempt to negate with overflow"] fn int64_neg_min_panics() { diff --git a/packages/std/src/math/isqrt.rs b/packages/std/src/math/isqrt.rs index 6fd58c397..837cada5a 100644 --- a/packages/std/src/math/isqrt.rs +++ b/packages/std/src/math/isqrt.rs @@ -59,19 +59,19 @@ mod tests { #[test] fn isqrt_primitives() { // Let's check correctness. - assert_eq!(0u8.isqrt(), 0); - assert_eq!(1u8.isqrt(), 1); - assert_eq!(24u8.isqrt(), 4); - assert_eq!(25u8.isqrt(), 5); - assert_eq!(26u8.isqrt(), 5); - assert_eq!(36u8.isqrt(), 6); + assert_eq!(super::Isqrt::isqrt(0u8), 0); + assert_eq!(super::Isqrt::isqrt(1u8), 1); + assert_eq!(super::Isqrt::isqrt(24u8), 4); + assert_eq!(super::Isqrt::isqrt(25u8), 5); + assert_eq!(super::Isqrt::isqrt(26u8), 5); + assert_eq!(super::Isqrt::isqrt(36u8), 6); // Let's also check different types. - assert_eq!(26u8.isqrt(), 5); - assert_eq!(26u16.isqrt(), 5); - assert_eq!(26u32.isqrt(), 5); - assert_eq!(26u64.isqrt(), 5); - assert_eq!(26u128.isqrt(), 5); + assert_eq!(super::Isqrt::isqrt(26u8), 5); + assert_eq!(super::Isqrt::isqrt(26u16), 5); + assert_eq!(super::Isqrt::isqrt(26u32), 5); + assert_eq!(super::Isqrt::isqrt(26u64), 5); + assert_eq!(super::Isqrt::isqrt(26u128), 5); } #[test] diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index a6cf8af3c..1e4527f21 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -1,3 +1,4 @@ +mod conversion; mod decimal; mod decimal256; mod fraction; @@ -6,6 +7,9 @@ mod int256; mod int512; mod int64; mod isqrt; +mod num_consts; +mod signed_decimal; +mod signed_decimal_256; mod uint128; mod uint256; mod uint512; @@ -19,6 +23,8 @@ pub use int256::Int256; pub use int512::Int512; pub use int64::Int64; pub use isqrt::Isqrt; +pub use signed_decimal::{SignedDecimal, SignedDecimalRangeExceeded}; +pub use signed_decimal_256::{SignedDecimal256, SignedDecimal256RangeExceeded}; pub use uint128::Uint128; pub use uint256::Uint256; pub use uint512::Uint512; @@ -70,6 +76,7 @@ mod tests { + ShrAssign + ShrAssign<&'a u32> + Not + + super::num_consts::NumConsts { } diff --git a/packages/std/src/math/num_consts.rs b/packages/std/src/math/num_consts.rs new file mode 100644 index 000000000..b53954230 --- /dev/null +++ b/packages/std/src/math/num_consts.rs @@ -0,0 +1,7 @@ +/// Crate internal trait for all our signed and unsigned number types +pub(crate) trait NumConsts { + const MAX: Self; + const MIN: Self; + const ZERO: Self; + const ONE: Self; +} diff --git a/packages/std/src/math/signed_decimal.rs b/packages/std/src/math/signed_decimal.rs new file mode 100644 index 000000000..bdb45cf04 --- /dev/null +++ b/packages/std/src/math/signed_decimal.rs @@ -0,0 +1,3100 @@ +use core::cmp::Ordering; +use core::fmt::{self, Write}; +use core::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, +}; +use core::str::FromStr; +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use thiserror::Error; + +use crate::errors::{ + CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, + OverflowOperation, RoundDownOverflowError, RoundUpOverflowError, StdError, +}; +use crate::{forward_ref_partial_eq, Decimal, Decimal256, Int256, SignedDecimal256}; + +use super::Fraction; +use super::Int128; + +/// A signed fixed-point decimal value with 18 fractional digits, i.e. SignedDecimal(1_000_000_000_000_000_000) == 1.0 +/// +/// The greatest possible value that can be represented is 170141183460469231731.687303715884105727 (which is (2^127 - 1) / 10^18) +/// and the smallest is -170141183460469231731.687303715884105728 (which is -2^127 / 10^18). +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct SignedDecimal(#[schemars(with = "String")] Int128); + +forward_ref_partial_eq!(SignedDecimal, SignedDecimal); + +#[derive(Error, Debug, PartialEq, Eq)] +#[error("SignedDecimal range exceeded")] +pub struct SignedDecimalRangeExceeded; + +impl SignedDecimal { + const DECIMAL_FRACTIONAL: Int128 = Int128::new(1_000_000_000_000_000_000i128); // 1*10**18 + const DECIMAL_FRACTIONAL_SQUARED: Int128 = + Int128::new(1_000_000_000_000_000_000_000_000_000_000_000_000i128); // (1*10**18)**2 = 1*10**36 + + /// The number of decimal places. Since decimal types are fixed-point rather than + /// floating-point, this is a constant. + pub const DECIMAL_PLACES: u32 = 18; // This needs to be an even number. + + /// The largest value that can be represented by this signed decimal type. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// assert_eq!(SignedDecimal::MAX.to_string(), "170141183460469231731.687303715884105727"); + /// ``` + pub const MAX: Self = Self(Int128::MAX); + + /// The smallest value that can be represented by this signed decimal type. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// assert_eq!(SignedDecimal::MIN.to_string(), "-170141183460469231731.687303715884105728"); + /// ``` + pub const MIN: Self = Self(Int128::MIN); + + /// Creates a SignedDecimal(value) + /// This is equivalent to `SignedDecimal::from_atomics(value, 18)` but usable in a const context. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal, Int128}; + /// assert_eq!(SignedDecimal::new(Int128::one()).to_string(), "0.000000000000000001"); + /// ``` + pub const fn new(value: Int128) -> Self { + Self(value) + } + + /// Creates a SignedDecimal(Int128(value)) + /// This is equivalent to `SignedDecimal::from_atomics(value, 18)` but usable in a const context. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// assert_eq!(SignedDecimal::raw(1234i128).to_string(), "0.000000000000001234"); + /// ``` + pub const fn raw(value: i128) -> Self { + Self(Int128::new(value)) + } + + /// Create a 1.0 SignedDecimal + #[inline] + pub const fn one() -> Self { + Self(Self::DECIMAL_FRACTIONAL) + } + + /// Create a -1.0 SignedDecimal + #[inline] + pub const fn negative_one() -> Self { + Self(Int128::new(-Self::DECIMAL_FRACTIONAL.i128())) + } + + /// Create a 0.0 SignedDecimal + #[inline] + pub const fn zero() -> Self { + Self(Int128::zero()) + } + + /// Convert x% into SignedDecimal + pub fn percent(x: i64) -> Self { + Self(((x as i128) * 10_000_000_000_000_000).into()) + } + + /// Convert permille (x/1000) into SignedDecimal + pub fn permille(x: i64) -> Self { + Self(((x as i128) * 1_000_000_000_000_000).into()) + } + + /// Convert basis points (x/10000) into SignedDecimal + pub fn bps(x: i64) -> Self { + Self(((x as i128) * 100_000_000_000_000).into()) + } + + /// Creates a signed decimal from a number of atomic units and the number + /// of decimal places. The inputs will be converted internally to form + /// a signed decimal with 18 decimal places. So the input 123 and 2 will create + /// the decimal 1.23. + /// + /// Using 18 decimal places is slightly more efficient than other values + /// as no internal conversion is necessary. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal, Int128}; + /// let a = SignedDecimal::from_atomics(Int128::new(1234), 3).unwrap(); + /// assert_eq!(a.to_string(), "1.234"); + /// + /// let a = SignedDecimal::from_atomics(1234i128, 0).unwrap(); + /// assert_eq!(a.to_string(), "1234"); + /// + /// let a = SignedDecimal::from_atomics(1i64, 18).unwrap(); + /// assert_eq!(a.to_string(), "0.000000000000000001"); + /// + /// let a = SignedDecimal::from_atomics(-1i64, 18).unwrap(); + /// assert_eq!(a.to_string(), "-0.000000000000000001"); + /// ``` + pub fn from_atomics( + atomics: impl Into, + decimal_places: u32, + ) -> Result { + let atomics = atomics.into(); + const TEN: Int128 = Int128::new(10); + Ok(match decimal_places.cmp(&(Self::DECIMAL_PLACES)) { + Ordering::Less => { + let digits = (Self::DECIMAL_PLACES) - decimal_places; // No overflow because decimal_places < DECIMAL_PLACES + let factor = TEN.checked_pow(digits).unwrap(); // Safe because digits <= 17 + Self( + atomics + .checked_mul(factor) + .map_err(|_| SignedDecimalRangeExceeded)?, + ) + } + Ordering::Equal => Self(atomics), + Ordering::Greater => { + let digits = decimal_places - (Self::DECIMAL_PLACES); // No overflow because decimal_places > DECIMAL_PLACES + if let Ok(factor) = TEN.checked_pow(digits) { + Self(atomics.checked_div(factor).unwrap()) // Safe because factor cannot be zero + } else { + // In this case `factor` exceeds the Int128 range. + // Any Int128 `x` divided by `factor` with `factor > Int128::MAX` is 0. + // Try e.g. Python3: `(2**128-1) // 2**128` + Self(Int128::zero()) + } + } + }) + } + + /// Returns the ratio (numerator / denominator) as a SignedDecimal + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// assert_eq!( + /// SignedDecimal::from_ratio(1, 3).to_string(), + /// "0.333333333333333333" + /// ); + /// ``` + pub fn from_ratio(numerator: impl Into, denominator: impl Into) -> Self { + match SignedDecimal::checked_from_ratio(numerator, denominator) { + Ok(value) => value, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => panic!("Multiplication overflow"), + } + } + + /// Returns the ratio (numerator / denominator) as a SignedDecimal + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal, CheckedFromRatioError}; + /// assert_eq!( + /// SignedDecimal::checked_from_ratio(1, 3).unwrap().to_string(), + /// "0.333333333333333333" + /// ); + /// assert_eq!( + /// SignedDecimal::checked_from_ratio(1, 0), + /// Err(CheckedFromRatioError::DivideByZero) + /// ); + /// ``` + pub fn checked_from_ratio( + numerator: impl Into, + denominator: impl Into, + ) -> Result { + let numerator: Int128 = numerator.into(); + let denominator: Int128 = denominator.into(); + match numerator.checked_multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator) { + Ok(ratio) => { + // numerator * DECIMAL_FRACTIONAL / denominator + Ok(SignedDecimal(ratio)) + } + Err(CheckedMultiplyRatioError::Overflow) => Err(CheckedFromRatioError::Overflow), + Err(CheckedMultiplyRatioError::DivideByZero) => { + Err(CheckedFromRatioError::DivideByZero) + } + } + } + + /// Returns `true` if the number is 0 + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0.is_zero() + } + + /// Returns `true` if the number is negative (< 0) + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.i128() < 0 + } + + /// A decimal is an integer of atomic units plus a number that specifies the + /// position of the decimal dot. So any decimal can be expressed as two numbers. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal, Int128}; + /// # use core::str::FromStr; + /// // Value with whole and fractional part + /// let a = SignedDecimal::from_str("1.234").unwrap(); + /// assert_eq!(a.decimal_places(), 18); + /// assert_eq!(a.atomics(), Int128::new(1234000000000000000)); + /// + /// // Smallest possible value + /// let b = SignedDecimal::from_str("0.000000000000000001").unwrap(); + /// assert_eq!(b.decimal_places(), 18); + /// assert_eq!(b.atomics(), Int128::new(1)); + /// ``` + #[must_use] + #[inline] + pub const fn atomics(&self) -> Int128 { + self.0 + } + + /// The number of decimal places. This is a constant value for now + /// but this could potentially change as the type evolves. + /// + /// See also [`SignedDecimal::atomics()`]. + #[must_use] + #[inline] + pub const fn decimal_places(&self) -> u32 { + Self::DECIMAL_PLACES + } + + /// Rounds value by truncating the decimal places. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// # use core::str::FromStr; + /// assert!(SignedDecimal::from_str("0.6").unwrap().trunc().is_zero()); + /// assert_eq!(SignedDecimal::from_str("-5.8").unwrap().trunc().to_string(), "-5"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn trunc(&self) -> Self { + Self((self.0 / Self::DECIMAL_FRACTIONAL) * Self::DECIMAL_FRACTIONAL) + } + + /// Rounds value down after decimal places. Panics on overflow. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// # use core::str::FromStr; + /// assert!(SignedDecimal::from_str("0.6").unwrap().floor().is_zero()); + /// assert_eq!(SignedDecimal::from_str("-5.2").unwrap().floor().to_string(), "-6"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn floor(&self) -> Self { + match self.checked_floor() { + Ok(value) => value, + Err(_) => panic!("attempt to floor with overflow"), + } + } + + /// Rounds value down after decimal places. + pub fn checked_floor(&self) -> Result { + if self.is_negative() { + let truncated = self.trunc(); + + if truncated != self { + truncated + .checked_sub(SignedDecimal::one()) + .map_err(|_| RoundDownOverflowError) + } else { + Ok(truncated) + } + } else { + Ok(self.trunc()) + } + } + + /// Rounds value up after decimal places. Panics on overflow. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal; + /// # use core::str::FromStr; + /// assert_eq!(SignedDecimal::from_str("0.2").unwrap().ceil(), SignedDecimal::one()); + /// assert_eq!(SignedDecimal::from_str("-5.8").unwrap().ceil().to_string(), "-5"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn ceil(&self) -> Self { + match self.checked_ceil() { + Ok(value) => value, + Err(_) => panic!("attempt to ceil with overflow"), + } + } + + /// Rounds value up after decimal places. Returns OverflowError on overflow. + pub fn checked_ceil(&self) -> Result { + let floor = self.floor(); + if floor == self { + Ok(floor) + } else { + floor + .checked_add(SignedDecimal::one()) + .map_err(|_| RoundUpOverflowError) + } + } + + /// Computes `self + other`, returning an `OverflowError` if an overflow occurred. + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Add, self, other)) + } + + /// Computes `self - other`, returning an `OverflowError` if an overflow occurred. + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + /// Multiplies one `SignedDecimal` by another, returning an `OverflowError` if an overflow occurred. + pub fn checked_mul(self, other: Self) -> Result { + let result_as_int256 = + self.numerator().full_mul(other.numerator()) / Int256::from(Self::DECIMAL_FRACTIONAL); + result_as_int256 + .try_into() + .map(Self) + .map_err(|_| OverflowError { + operation: OverflowOperation::Mul, + operand1: self.to_string(), + operand2: other.to_string(), + }) + } + + /// Raises a value to the power of `exp`, panics if an overflow occurred. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => panic!("Multiplication overflow"), + } + } + + /// Raises a value to the power of `exp`, returning an `OverflowError` if an overflow occurred. + pub fn checked_pow(self, exp: u32) -> Result { + // This uses the exponentiation by squaring algorithm: + // https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Basic_method + + fn inner(mut x: SignedDecimal, mut n: u32) -> Result { + if n == 0 { + return Ok(SignedDecimal::one()); + } + + let mut y = SignedDecimal::one(); + + while n > 1 { + if n % 2 == 0 { + x = x.checked_mul(x)?; + n /= 2; + } else { + y = x.checked_mul(y)?; + x = x.checked_mul(x)?; + n = (n - 1) / 2; + } + } + + Ok(x * y) + } + + inner(self, exp).map_err(|_| OverflowError { + operation: OverflowOperation::Pow, + operand1: self.to_string(), + operand2: exp.to_string(), + }) + } + + pub fn checked_div(self, other: Self) -> Result { + SignedDecimal::checked_from_ratio(self.numerator(), other.numerator()) + } + + /// Computes `self % other`, returning an `DivideByZeroError` if `other == 0`. + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .map_err(|_| DivideByZeroError::new(self)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs_diff(self, other: Self) -> Decimal { + Decimal::new(self.0.abs_diff(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + match self.checked_mul(other) { + Ok(value) => value, + Err(_) => { + // both negative or both positive results in positive number, otherwise negative + if self.is_negative() == other.is_negative() { + Self::MAX + } else { + Self::MIN + } + } + } + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => { + // odd exponent of negative number results in negative number + // everything else results in positive number + if self.is_negative() && exp % 2 == 1 { + Self::MIN + } else { + Self::MAX + } + } + } + } + + /// Converts this decimal to a signed integer by rounding down + /// to the next integer, e.g. 22.5 becomes 22 and -1.2 becomes -2. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal, Int128}; + /// + /// let d = SignedDecimal::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_floor(), Int128::new(12)); + /// + /// let d = SignedDecimal::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_floor(), Int128::new(-13)); + /// + /// let d = SignedDecimal::from_str("-0.05").unwrap(); + /// assert_eq!(d.to_int_floor(), Int128::new(-1)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_floor(self) -> Int128 { + if self.is_negative() { + // Using `x.to_int_floor() = -(-x).to_int_ceil()` for a negative `x`, + // but avoiding overflow by implementing the formula from `to_int_ceil` directly. + let x = self.0; + let y = Self::DECIMAL_FRACTIONAL; + // making sure not to negate `x`, as this would overflow + -Int128::one() - ((-Int128::one() - x) / y) + } else { + self.to_int_trunc() + } + } + + /// Converts this decimal to a signed integer by truncating + /// the fractional part, e.g. 22.5 becomes 22. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal, Int128}; + /// + /// let d = SignedDecimal::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int128::new(12)); + /// + /// let d = SignedDecimal::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int128::new(-12)); + /// + /// let d = SignedDecimal::from_str("75.0").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int128::new(75)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_trunc(self) -> Int128 { + self.0 / Self::DECIMAL_FRACTIONAL + } + + /// Converts this decimal to a signed integer by rounding up + /// to the next integer, e.g. 22.3 becomes 23 and -1.2 becomes -1. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal, Int128}; + /// + /// let d = SignedDecimal::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int128::new(13)); + /// + /// let d = SignedDecimal::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int128::new(-12)); + /// + /// let d = SignedDecimal::from_str("75.0").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int128::new(75)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_ceil(self) -> Int128 { + if self.is_negative() { + self.to_int_trunc() + } else { + // Using `q = 1 + ((x - 1) / y); // if x != 0` with unsigned integers x, y, q + // from https://stackoverflow.com/a/2745086/2013738. We know `x + y` CAN overflow. + let x = self.0; + let y = Self::DECIMAL_FRACTIONAL; + if x.is_zero() { + Int128::zero() + } else { + Int128::one() + ((x - Int128::one()) / y) + } + } + } +} + +impl Fraction for SignedDecimal { + #[inline] + fn numerator(&self) -> Int128 { + self.0 + } + + #[inline] + fn denominator(&self) -> Int128 { + Self::DECIMAL_FRACTIONAL + } + + /// Returns the multiplicative inverse `1/d` for decimal `d`. + /// + /// If `d` is zero, none is returned. + fn inv(&self) -> Option { + if self.is_zero() { + None + } else { + // Let self be p/q with p = self.0 and q = DECIMAL_FRACTIONAL. + // Now we calculate the inverse a/b = q/p such that b = DECIMAL_FRACTIONAL. Then + // `a = DECIMAL_FRACTIONAL*DECIMAL_FRACTIONAL / self.0`. + Some(SignedDecimal(Self::DECIMAL_FRACTIONAL_SQUARED / self.0)) + } + } +} + +impl Neg for SignedDecimal { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl TryFrom for SignedDecimal { + type Error = SignedDecimalRangeExceeded; + + fn try_from(value: SignedDecimal256) -> Result { + value + .atomics() + .try_into() + .map(SignedDecimal) + .map_err(|_| SignedDecimalRangeExceeded) + } +} + +impl TryFrom for SignedDecimal { + type Error = SignedDecimalRangeExceeded; + + fn try_from(value: Decimal) -> Result { + value + .atomics() + .try_into() + .map(SignedDecimal) + .map_err(|_| SignedDecimalRangeExceeded) + } +} + +impl TryFrom for SignedDecimal { + type Error = SignedDecimalRangeExceeded; + + fn try_from(value: Decimal256) -> Result { + value + .atomics() + .try_into() + .map(SignedDecimal) + .map_err(|_| SignedDecimalRangeExceeded) + } +} + +impl FromStr for SignedDecimal { + type Err = StdError; + + /// Converts the decimal string to a SignedDecimal + /// Possible inputs: "1.23", "1", "000012", "1.123000000", "-1.12300" + /// Disallowed: "", ".23" + /// + /// This never performs any kind of rounding. + /// More than DECIMAL_PLACES fractional digits, even zeros, result in an error. + fn from_str(input: &str) -> Result { + let mut parts_iter = input.split('.'); + + let whole_part = parts_iter.next().unwrap(); // split always returns at least one element + let is_neg = whole_part.starts_with('-'); + + let whole = whole_part + .parse::() + .map_err(|_| StdError::generic_err("Error parsing whole"))?; + let mut atomics = whole + .checked_mul(Self::DECIMAL_FRACTIONAL) + .map_err(|_| StdError::generic_err("Value too big"))?; + + if let Some(fractional_part) = parts_iter.next() { + let fractional = fractional_part + .parse::() // u64 is enough for 18 decimal places + .map_err(|_| StdError::generic_err("Error parsing fractional"))?; + let exp = (Self::DECIMAL_PLACES.checked_sub(fractional_part.len() as u32)).ok_or_else( + || { + StdError::generic_err(format!( + "Cannot parse more than {} fractional digits", + Self::DECIMAL_PLACES + )) + }, + )?; + debug_assert!(exp <= Self::DECIMAL_PLACES); + let fractional_factor = Int128::from(10i128.pow(exp)); + + // This multiplication can't overflow because + // fractional < 10^DECIMAL_PLACES && fractional_factor <= 10^DECIMAL_PLACES + let fractional_part = Int128::from(fractional) + .checked_mul(fractional_factor) + .unwrap(); + + // for negative numbers, we need to subtract the fractional part + atomics = if is_neg { + atomics.checked_sub(fractional_part) + } else { + atomics.checked_add(fractional_part) + } + .map_err(|_| StdError::generic_err("Value too big"))?; + } + + if parts_iter.next().is_some() { + return Err(StdError::generic_err("Unexpected number of dots")); + } + + Ok(SignedDecimal(atomics)) + } +} + +impl fmt::Display for SignedDecimal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let whole = (self.0) / Self::DECIMAL_FRACTIONAL; + let fractional = (self.0).checked_rem(Self::DECIMAL_FRACTIONAL).unwrap(); + + if fractional.is_zero() { + write!(f, "{whole}") + } else { + let fractional_string = format!( + "{:0>padding$}", + fractional.abs(), // fractional should always be printed as positive + padding = Self::DECIMAL_PLACES as usize + ); + if self.is_negative() { + f.write_char('-')?; + } + write!( + f, + "{whole}.{fractional}", + whole = whole.abs(), + fractional = fractional_string.trim_end_matches('0') + ) + } + } +} + +impl fmt::Debug for SignedDecimal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SignedDecimal({self})") + } +} + +impl Add for SignedDecimal { + type Output = Self; + + fn add(self, other: Self) -> Self { + SignedDecimal(self.0 + other.0) + } +} +forward_ref_binop!(impl Add, add for SignedDecimal, SignedDecimal); + +impl AddAssign for SignedDecimal { + fn add_assign(&mut self, rhs: SignedDecimal) { + *self = *self + rhs; + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for SignedDecimal, SignedDecimal); + +impl Sub for SignedDecimal { + type Output = Self; + + fn sub(self, other: Self) -> Self { + SignedDecimal(self.0 - other.0) + } +} +forward_ref_binop!(impl Sub, sub for SignedDecimal, SignedDecimal); + +impl SubAssign for SignedDecimal { + fn sub_assign(&mut self, rhs: SignedDecimal) { + *self = *self - rhs; + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for SignedDecimal, SignedDecimal); + +impl Mul for SignedDecimal { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn mul(self, other: Self) -> Self { + // SignedDecimals are fractions. We can multiply two decimals a and b + // via + // (a.numerator() * b.numerator()) / (a.denominator() * b.denominator()) + // = (a.numerator() * b.numerator()) / a.denominator() / b.denominator() + + let result_as_int256 = + self.numerator().full_mul(other.numerator()) / Int256::from(Self::DECIMAL_FRACTIONAL); + match result_as_int256.try_into() { + Ok(result) => Self(result), + Err(_) => panic!("attempt to multiply with overflow"), + } + } +} +forward_ref_binop!(impl Mul, mul for SignedDecimal, SignedDecimal); + +impl MulAssign for SignedDecimal { + fn mul_assign(&mut self, rhs: SignedDecimal) { + *self = *self * rhs; + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for SignedDecimal, SignedDecimal); + +impl Div for SignedDecimal { + type Output = Self; + + fn div(self, other: Self) -> Self { + match SignedDecimal::checked_from_ratio(self.numerator(), other.numerator()) { + Ok(ratio) => ratio, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Division failed - denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => { + panic!("Division failed - multiplication overflow") + } + } + } +} +forward_ref_binop!(impl Div, div for SignedDecimal, SignedDecimal); + +impl DivAssign for SignedDecimal { + fn div_assign(&mut self, rhs: SignedDecimal) { + *self = *self / rhs; + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for SignedDecimal, SignedDecimal); + +impl Div for SignedDecimal { + type Output = Self; + + fn div(self, rhs: Int128) -> Self::Output { + SignedDecimal(self.0 / rhs) + } +} + +impl DivAssign for SignedDecimal { + fn div_assign(&mut self, rhs: Int128) { + self.0 /= rhs; + } +} + +impl Rem for SignedDecimal { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for SignedDecimal, SignedDecimal); + +impl RemAssign for SignedDecimal { + fn rem_assign(&mut self, rhs: SignedDecimal) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for SignedDecimal, SignedDecimal); + +impl core::iter::Sum for SignedDecimal +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +/// Serializes as a decimal string +impl Serialize for SignedDecimal { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +/// Deserializes as a base64 string +impl<'de> Deserialize<'de> for SignedDecimal { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(SignedDecimalVisitor) + } +} + +struct SignedDecimalVisitor; + +impl<'de> de::Visitor<'de> for SignedDecimalVisitor { + type Value = SignedDecimal; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded decimal") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + match SignedDecimal::from_str(v) { + Ok(d) => Ok(d), + Err(e) => Err(E::custom(format!("Error parsing decimal '{v}': {e}"))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_json, to_json_vec}; + use schemars::schema_for; + + fn dec(input: &str) -> SignedDecimal { + SignedDecimal::from_str(input).unwrap() + } + + #[test] + fn signed_decimal_new() { + let expected = Int128::from(300i128); + assert_eq!(SignedDecimal::new(expected).0, expected); + + let expected = Int128::from(-300i128); + assert_eq!(SignedDecimal::new(expected).0, expected); + } + + #[test] + fn signed_decimal_raw() { + let value = 300i128; + assert_eq!(SignedDecimal::raw(value).0.i128(), value); + + let value = -300i128; + assert_eq!(SignedDecimal::raw(value).0.i128(), value); + } + + #[test] + fn signed_decimal_one() { + let value = SignedDecimal::one(); + assert_eq!(value.0, SignedDecimal::DECIMAL_FRACTIONAL); + } + + #[test] + fn signed_decimal_zero() { + let value = SignedDecimal::zero(); + assert!(value.0.is_zero()); + } + + #[test] + fn signed_decimal_percent() { + let value = SignedDecimal::percent(50); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(2u8) + ); + + let value = SignedDecimal::percent(-50); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(-2i8) + ); + } + + #[test] + fn signed_decimal_permille() { + let value = SignedDecimal::permille(125); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(8u8) + ); + + let value = SignedDecimal::permille(-125); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(-8i8) + ); + } + + #[test] + fn signed_decimal_bps() { + let value = SignedDecimal::bps(125); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(80u8) + ); + + let value = SignedDecimal::bps(-125); + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(-80i8) + ); + } + + #[test] + fn signed_decimal_from_atomics_works() { + let one = SignedDecimal::one(); + let two = one + one; + let neg_one = SignedDecimal::negative_one(); + + assert_eq!(SignedDecimal::from_atomics(1i128, 0).unwrap(), one); + assert_eq!(SignedDecimal::from_atomics(10i128, 1).unwrap(), one); + assert_eq!(SignedDecimal::from_atomics(100i128, 2).unwrap(), one); + assert_eq!(SignedDecimal::from_atomics(1000i128, 3).unwrap(), one); + assert_eq!( + SignedDecimal::from_atomics(1000000000000000000i128, 18).unwrap(), + one + ); + assert_eq!( + SignedDecimal::from_atomics(10000000000000000000i128, 19).unwrap(), + one + ); + assert_eq!( + SignedDecimal::from_atomics(100000000000000000000i128, 20).unwrap(), + one + ); + + assert_eq!(SignedDecimal::from_atomics(2i128, 0).unwrap(), two); + assert_eq!(SignedDecimal::from_atomics(20i128, 1).unwrap(), two); + assert_eq!(SignedDecimal::from_atomics(200i128, 2).unwrap(), two); + assert_eq!(SignedDecimal::from_atomics(2000i128, 3).unwrap(), two); + assert_eq!( + SignedDecimal::from_atomics(2000000000000000000i128, 18).unwrap(), + two + ); + assert_eq!( + SignedDecimal::from_atomics(20000000000000000000i128, 19).unwrap(), + two + ); + assert_eq!( + SignedDecimal::from_atomics(200000000000000000000i128, 20).unwrap(), + two + ); + + assert_eq!(SignedDecimal::from_atomics(-1i128, 0).unwrap(), neg_one); + assert_eq!(SignedDecimal::from_atomics(-10i128, 1).unwrap(), neg_one); + assert_eq!( + SignedDecimal::from_atomics(-100000000000000000000i128, 20).unwrap(), + neg_one + ); + + // Cuts decimal digits (20 provided but only 18 can be stored) + assert_eq!( + SignedDecimal::from_atomics(4321i128, 20).unwrap(), + SignedDecimal::from_str("0.000000000000000043").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(-4321i128, 20).unwrap(), + SignedDecimal::from_str("-0.000000000000000043").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(6789i128, 20).unwrap(), + SignedDecimal::from_str("0.000000000000000067").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 38).unwrap(), + SignedDecimal::from_str("1.701411834604692317").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 39).unwrap(), + SignedDecimal::from_str("0.170141183460469231").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 45).unwrap(), + SignedDecimal::from_str("0.000000170141183460").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 51).unwrap(), + SignedDecimal::from_str("0.000000000000170141").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 56).unwrap(), + SignedDecimal::from_str("0.000000000000000001").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, 57).unwrap(), + SignedDecimal::from_str("0.000000000000000000").unwrap() + ); + assert_eq!( + SignedDecimal::from_atomics(i128::MAX, u32::MAX).unwrap(), + SignedDecimal::from_str("0.000000000000000000").unwrap() + ); + + // Can be used with max value + let max = SignedDecimal::MAX; + assert_eq!( + SignedDecimal::from_atomics(max.atomics(), max.decimal_places()).unwrap(), + max + ); + + // Can be used with min value + let min = SignedDecimal::MIN; + assert_eq!( + SignedDecimal::from_atomics(min.atomics(), min.decimal_places()).unwrap(), + min + ); + + // Overflow is only possible with digits < 18 + let result = SignedDecimal::from_atomics(i128::MAX, 17); + assert_eq!(result.unwrap_err(), SignedDecimalRangeExceeded); + } + + #[test] + fn signed_decimal_from_ratio_works() { + // 1.0 + assert_eq!( + SignedDecimal::from_ratio(1i128, 1i128), + SignedDecimal::one() + ); + assert_eq!( + SignedDecimal::from_ratio(53i128, 53i128), + SignedDecimal::one() + ); + assert_eq!( + SignedDecimal::from_ratio(125i128, 125i128), + SignedDecimal::one() + ); + + // -1.0 + assert_eq!( + SignedDecimal::from_ratio(-1i128, 1i128), + SignedDecimal::negative_one() + ); + assert_eq!( + SignedDecimal::from_ratio(-53i128, 53i128), + SignedDecimal::negative_one() + ); + assert_eq!( + SignedDecimal::from_ratio(125i128, -125i128), + SignedDecimal::negative_one() + ); + + // 1.5 + assert_eq!( + SignedDecimal::from_ratio(3i128, 2i128), + SignedDecimal::percent(150) + ); + assert_eq!( + SignedDecimal::from_ratio(150i128, 100i128), + SignedDecimal::percent(150) + ); + assert_eq!( + SignedDecimal::from_ratio(333i128, 222i128), + SignedDecimal::percent(150) + ); + + // 0.125 + assert_eq!( + SignedDecimal::from_ratio(1i64, 8i64), + SignedDecimal::permille(125) + ); + assert_eq!( + SignedDecimal::from_ratio(125i64, 1000i64), + SignedDecimal::permille(125) + ); + + // -0.125 + assert_eq!( + SignedDecimal::from_ratio(-1i64, 8i64), + SignedDecimal::permille(-125) + ); + assert_eq!( + SignedDecimal::from_ratio(125i64, -1000i64), + SignedDecimal::permille(-125) + ); + + // 1/3 (result floored) + assert_eq!( + SignedDecimal::from_ratio(1i64, 3i64), + SignedDecimal(Int128::from(333_333_333_333_333_333i128)) + ); + + // 2/3 (result floored) + assert_eq!( + SignedDecimal::from_ratio(2i64, 3i64), + SignedDecimal(Int128::from(666_666_666_666_666_666i128)) + ); + + // large inputs + assert_eq!( + SignedDecimal::from_ratio(0i128, i128::MAX), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::from_ratio(i128::MAX, i128::MAX), + SignedDecimal::one() + ); + // 170141183460469231731 is the largest integer <= SignedDecimal::MAX + assert_eq!( + SignedDecimal::from_ratio(170141183460469231731i128, 1i128), + SignedDecimal::from_str("170141183460469231731").unwrap() + ); + } + + #[test] + #[should_panic(expected = "Denominator must not be zero")] + fn signed_decimal_from_ratio_panics_for_zero_denominator() { + SignedDecimal::from_ratio(1i128, 0i128); + } + + #[test] + #[should_panic(expected = "Multiplication overflow")] + fn signed_decimal_from_ratio_panics_for_mul_overflow() { + SignedDecimal::from_ratio(i128::MAX, 1i128); + } + + #[test] + fn signed_decimal_checked_from_ratio_does_not_panic() { + assert_eq!( + SignedDecimal::checked_from_ratio(1i128, 0i128), + Err(CheckedFromRatioError::DivideByZero) + ); + + assert_eq!( + SignedDecimal::checked_from_ratio(i128::MAX, 1i128), + Err(CheckedFromRatioError::Overflow) + ); + } + + #[test] + fn signed_decimal_implements_fraction() { + let fraction = SignedDecimal::from_str("1234.567").unwrap(); + assert_eq!( + fraction.numerator(), + Int128::from(1_234_567_000_000_000_000_000i128) + ); + assert_eq!( + fraction.denominator(), + Int128::from(1_000_000_000_000_000_000i128) + ); + + let fraction = SignedDecimal::from_str("-1234.567").unwrap(); + assert_eq!( + fraction.numerator(), + Int128::from(-1_234_567_000_000_000_000_000i128) + ); + assert_eq!( + fraction.denominator(), + Int128::from(1_000_000_000_000_000_000i128) + ); + } + + #[test] + fn signed_decimal_from_str_works() { + // Integers + assert_eq!( + SignedDecimal::from_str("0").unwrap(), + SignedDecimal::percent(0) + ); + assert_eq!( + SignedDecimal::from_str("1").unwrap(), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::from_str("5").unwrap(), + SignedDecimal::percent(500) + ); + assert_eq!( + SignedDecimal::from_str("42").unwrap(), + SignedDecimal::percent(4200) + ); + assert_eq!( + SignedDecimal::from_str("000").unwrap(), + SignedDecimal::percent(0) + ); + assert_eq!( + SignedDecimal::from_str("001").unwrap(), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::from_str("005").unwrap(), + SignedDecimal::percent(500) + ); + assert_eq!( + SignedDecimal::from_str("0042").unwrap(), + SignedDecimal::percent(4200) + ); + + // Positive decimals + assert_eq!( + SignedDecimal::from_str("1.0").unwrap(), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::from_str("1.5").unwrap(), + SignedDecimal::percent(150) + ); + assert_eq!( + SignedDecimal::from_str("0.5").unwrap(), + SignedDecimal::percent(50) + ); + assert_eq!( + SignedDecimal::from_str("0.123").unwrap(), + SignedDecimal::permille(123) + ); + + assert_eq!( + SignedDecimal::from_str("40.00").unwrap(), + SignedDecimal::percent(4000) + ); + assert_eq!( + SignedDecimal::from_str("04.00").unwrap(), + SignedDecimal::percent(400) + ); + assert_eq!( + SignedDecimal::from_str("00.40").unwrap(), + SignedDecimal::percent(40) + ); + assert_eq!( + SignedDecimal::from_str("00.04").unwrap(), + SignedDecimal::percent(4) + ); + // Negative decimals + assert_eq!( + SignedDecimal::from_str("-00.04").unwrap(), + SignedDecimal::percent(-4) + ); + assert_eq!( + SignedDecimal::from_str("-00.40").unwrap(), + SignedDecimal::percent(-40) + ); + assert_eq!( + SignedDecimal::from_str("-04.00").unwrap(), + SignedDecimal::percent(-400) + ); + + // Can handle DECIMAL_PLACES fractional digits + assert_eq!( + SignedDecimal::from_str("7.123456789012345678").unwrap(), + SignedDecimal(Int128::from(7123456789012345678i128)) + ); + assert_eq!( + SignedDecimal::from_str("7.999999999999999999").unwrap(), + SignedDecimal(Int128::from(7999999999999999999i128)) + ); + + // Works for documented max value + assert_eq!( + SignedDecimal::from_str("170141183460469231731.687303715884105727").unwrap(), + SignedDecimal::MAX + ); + // Works for documented min value + assert_eq!( + SignedDecimal::from_str("-170141183460469231731.687303715884105728").unwrap(), + SignedDecimal::MIN + ); + assert_eq!( + SignedDecimal::from_str("-1").unwrap(), + SignedDecimal::negative_one() + ); + } + + #[test] + fn signed_decimal_from_str_errors_for_broken_whole_part() { + let expected_err = StdError::generic_err("Error parsing whole"); + assert_eq!(SignedDecimal::from_str("").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str(" ").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str("-").unwrap_err(), expected_err); + } + + #[test] + fn signed_decimal_from_str_errors_for_broken_fractional_part() { + let expected_err = StdError::generic_err("Error parsing fractional"); + assert_eq!(SignedDecimal::from_str("1.").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str("1. ").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str("1.e").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str("1.2e3").unwrap_err(), expected_err); + assert_eq!(SignedDecimal::from_str("1.-2").unwrap_err(), expected_err); + } + + #[test] + fn signed_decimal_from_str_errors_for_more_than_18_fractional_digits() { + let expected_err = StdError::generic_err("Cannot parse more than 18 fractional digits"); + assert_eq!( + SignedDecimal::from_str("7.1234567890123456789").unwrap_err(), + expected_err + ); + // No special rules for trailing zeros. This could be changed but adds gas cost for the happy path. + assert_eq!( + SignedDecimal::from_str("7.1230000000000000000").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_from_str_errors_for_invalid_number_of_dots() { + let expected_err = StdError::generic_err("Unexpected number of dots"); + assert_eq!(SignedDecimal::from_str("1.2.3").unwrap_err(), expected_err); + assert_eq!( + SignedDecimal::from_str("1.2.3.4").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_from_str_errors_for_more_than_max_value() { + let expected_err = StdError::generic_err("Value too big"); + // Integer + assert_eq!( + SignedDecimal::from_str("170141183460469231732").unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal::from_str("-170141183460469231732").unwrap_err(), + expected_err + ); + + // SignedDecimal + assert_eq!( + SignedDecimal::from_str("170141183460469231732.0").unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal::from_str("170141183460469231731.687303715884105728").unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal::from_str("-170141183460469231731.687303715884105729").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_conversions_work() { + // signed decimal to signed decimal + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::MAX).unwrap_err(), + SignedDecimalRangeExceeded + ); + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::MIN).unwrap_err(), + SignedDecimalRangeExceeded + ); + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::zero()).unwrap(), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::one()).unwrap(), + SignedDecimal::one() + ); + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::percent(50)).unwrap(), + SignedDecimal::percent(50) + ); + assert_eq!( + SignedDecimal::try_from(SignedDecimal256::percent(-200)).unwrap(), + SignedDecimal::percent(-200) + ); + + // unsigned to signed decimal + assert_eq!( + SignedDecimal::try_from(Decimal::MAX).unwrap_err(), + SignedDecimalRangeExceeded + ); + let max = Decimal::raw(SignedDecimal::MAX.atomics().i128() as u128); + let too_big = max + Decimal::raw(1); + assert_eq!( + SignedDecimal::try_from(too_big).unwrap_err(), + SignedDecimalRangeExceeded + ); + assert_eq!( + SignedDecimal::try_from(Decimal::zero()).unwrap(), + SignedDecimal::zero() + ); + assert_eq!(SignedDecimal::try_from(max).unwrap(), SignedDecimal::MAX); + } + + #[test] + fn signed_decimal_atomics_works() { + let zero = SignedDecimal::zero(); + let one = SignedDecimal::one(); + let half = SignedDecimal::percent(50); + let two = SignedDecimal::percent(200); + let max = SignedDecimal::MAX; + let neg_half = SignedDecimal::percent(-50); + let neg_two = SignedDecimal::percent(-200); + let min = SignedDecimal::MIN; + + assert_eq!(zero.atomics(), Int128::new(0)); + assert_eq!(one.atomics(), Int128::new(1000000000000000000)); + assert_eq!(half.atomics(), Int128::new(500000000000000000)); + assert_eq!(two.atomics(), Int128::new(2000000000000000000)); + assert_eq!(max.atomics(), Int128::MAX); + assert_eq!(neg_half.atomics(), Int128::new(-500000000000000000)); + assert_eq!(neg_two.atomics(), Int128::new(-2000000000000000000)); + assert_eq!(min.atomics(), Int128::MIN); + } + + #[test] + fn signed_decimal_decimal_places_works() { + let zero = SignedDecimal::zero(); + let one = SignedDecimal::one(); + let half = SignedDecimal::percent(50); + let two = SignedDecimal::percent(200); + let max = SignedDecimal::MAX; + let neg_one = SignedDecimal::negative_one(); + + assert_eq!(zero.decimal_places(), 18); + assert_eq!(one.decimal_places(), 18); + assert_eq!(half.decimal_places(), 18); + assert_eq!(two.decimal_places(), 18); + assert_eq!(max.decimal_places(), 18); + assert_eq!(neg_one.decimal_places(), 18); + } + + #[test] + fn signed_decimal_is_zero_works() { + assert!(SignedDecimal::zero().is_zero()); + assert!(SignedDecimal::percent(0).is_zero()); + assert!(SignedDecimal::permille(0).is_zero()); + + assert!(!SignedDecimal::one().is_zero()); + assert!(!SignedDecimal::percent(123).is_zero()); + assert!(!SignedDecimal::permille(-1234).is_zero()); + } + + #[test] + fn signed_decimal_inv_works() { + // d = 0 + assert_eq!(SignedDecimal::zero().inv(), None); + + // d == 1 + assert_eq!(SignedDecimal::one().inv(), Some(SignedDecimal::one())); + + // d == -1 + assert_eq!( + SignedDecimal::negative_one().inv(), + Some(SignedDecimal::negative_one()) + ); + + // d > 1 exact + assert_eq!( + SignedDecimal::from_str("2").unwrap().inv(), + Some(SignedDecimal::from_str("0.5").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("20").unwrap().inv(), + Some(SignedDecimal::from_str("0.05").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("200").unwrap().inv(), + Some(SignedDecimal::from_str("0.005").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("2000").unwrap().inv(), + Some(SignedDecimal::from_str("0.0005").unwrap()) + ); + + // d > 1 rounded + assert_eq!( + SignedDecimal::from_str("3").unwrap().inv(), + Some(SignedDecimal::from_str("0.333333333333333333").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("6").unwrap().inv(), + Some(SignedDecimal::from_str("0.166666666666666666").unwrap()) + ); + + // d < 1 exact + assert_eq!( + SignedDecimal::from_str("0.5").unwrap().inv(), + Some(SignedDecimal::from_str("2").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("0.05").unwrap().inv(), + Some(SignedDecimal::from_str("20").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("0.005").unwrap().inv(), + Some(SignedDecimal::from_str("200").unwrap()) + ); + assert_eq!( + SignedDecimal::from_str("0.0005").unwrap().inv(), + Some(SignedDecimal::from_str("2000").unwrap()) + ); + + // d < 0 + assert_eq!( + SignedDecimal::from_str("-0.5").unwrap().inv(), + Some(SignedDecimal::from_str("-2").unwrap()) + ); + // d < 0 rounded + assert_eq!( + SignedDecimal::from_str("-3").unwrap().inv(), + Some(SignedDecimal::from_str("-0.333333333333333333").unwrap()) + ); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_add_works() { + let value = SignedDecimal::one() + SignedDecimal::percent(50); // 1.5 + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL * Int128::from(3u8) / Int128::from(2u8) + ); + + assert_eq!( + SignedDecimal::percent(5) + SignedDecimal::percent(4), + SignedDecimal::percent(9) + ); + assert_eq!( + SignedDecimal::percent(5) + SignedDecimal::zero(), + SignedDecimal::percent(5) + ); + assert_eq!( + SignedDecimal::zero() + SignedDecimal::zero(), + SignedDecimal::zero() + ); + // negative numbers + assert_eq!( + SignedDecimal::percent(-5) + SignedDecimal::percent(-4), + SignedDecimal::percent(-9) + ); + assert_eq!( + SignedDecimal::percent(-5) + SignedDecimal::percent(4), + SignedDecimal::percent(-1) + ); + assert_eq!( + SignedDecimal::percent(5) + SignedDecimal::percent(-4), + SignedDecimal::percent(1) + ); + + // works for refs + let a = SignedDecimal::percent(15); + let b = SignedDecimal::percent(25); + let expected = SignedDecimal::percent(40); + assert_eq!(a + b, expected); + assert_eq!(&a + b, expected); + assert_eq!(a + &b, expected); + assert_eq!(&a + &b, expected); + } + + #[test] + #[should_panic] + fn signed_decimal_add_overflow_panics() { + let _value = SignedDecimal::MAX + SignedDecimal::percent(50); + } + + #[test] + fn signed_decimal_add_assign_works() { + let mut a = SignedDecimal::percent(30); + a += SignedDecimal::percent(20); + assert_eq!(a, SignedDecimal::percent(50)); + + // works for refs + let mut a = SignedDecimal::percent(15); + let b = SignedDecimal::percent(3); + let expected = SignedDecimal::percent(18); + a += &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_sub_works() { + let value = SignedDecimal::one() - SignedDecimal::percent(50); // 0.5 + assert_eq!( + value.0, + SignedDecimal::DECIMAL_FRACTIONAL / Int128::from(2u8) + ); + + assert_eq!( + SignedDecimal::percent(9) - SignedDecimal::percent(4), + SignedDecimal::percent(5) + ); + assert_eq!( + SignedDecimal::percent(16) - SignedDecimal::zero(), + SignedDecimal::percent(16) + ); + assert_eq!( + SignedDecimal::percent(16) - SignedDecimal::percent(16), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::zero() - SignedDecimal::zero(), + SignedDecimal::zero() + ); + + // negative numbers + assert_eq!( + SignedDecimal::percent(-5) - SignedDecimal::percent(-4), + SignedDecimal::percent(-1) + ); + assert_eq!( + SignedDecimal::percent(-5) - SignedDecimal::percent(4), + SignedDecimal::percent(-9) + ); + assert_eq!( + SignedDecimal::percent(500) - SignedDecimal::percent(-4), + SignedDecimal::percent(504) + ); + + // works for refs + let a = SignedDecimal::percent(13); + let b = SignedDecimal::percent(6); + let expected = SignedDecimal::percent(7); + assert_eq!(a - b, expected); + assert_eq!(&a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn signed_decimal_sub_overflow_panics() { + let _value = SignedDecimal::MIN - SignedDecimal::percent(50); + } + + #[test] + fn signed_decimal_sub_assign_works() { + let mut a = SignedDecimal::percent(20); + a -= SignedDecimal::percent(2); + assert_eq!(a, SignedDecimal::percent(18)); + + // works for refs + let mut a = SignedDecimal::percent(33); + let b = SignedDecimal::percent(13); + let expected = SignedDecimal::percent(20); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_implements_mul() { + let one = SignedDecimal::one(); + let two = one + one; + let half = SignedDecimal::percent(50); + + // 1*x and x*1 + assert_eq!(one * SignedDecimal::percent(0), SignedDecimal::percent(0)); + assert_eq!(one * SignedDecimal::percent(1), SignedDecimal::percent(1)); + assert_eq!(one * SignedDecimal::percent(10), SignedDecimal::percent(10)); + assert_eq!( + one * SignedDecimal::percent(100), + SignedDecimal::percent(100) + ); + assert_eq!( + one * SignedDecimal::percent(1000), + SignedDecimal::percent(1000) + ); + assert_eq!(one * SignedDecimal::MAX, SignedDecimal::MAX); + assert_eq!(SignedDecimal::percent(0) * one, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) * one, SignedDecimal::percent(1)); + assert_eq!(SignedDecimal::percent(10) * one, SignedDecimal::percent(10)); + assert_eq!( + SignedDecimal::percent(100) * one, + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::percent(1000) * one, + SignedDecimal::percent(1000) + ); + assert_eq!(SignedDecimal::MAX * one, SignedDecimal::MAX); + assert_eq!(SignedDecimal::percent(-1) * one, SignedDecimal::percent(-1)); + assert_eq!( + one * SignedDecimal::percent(-10), + SignedDecimal::percent(-10) + ); + + // double + assert_eq!(two * SignedDecimal::percent(0), SignedDecimal::percent(0)); + assert_eq!(two * SignedDecimal::percent(1), SignedDecimal::percent(2)); + assert_eq!(two * SignedDecimal::percent(10), SignedDecimal::percent(20)); + assert_eq!( + two * SignedDecimal::percent(100), + SignedDecimal::percent(200) + ); + assert_eq!( + two * SignedDecimal::percent(1000), + SignedDecimal::percent(2000) + ); + assert_eq!(SignedDecimal::percent(0) * two, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) * two, SignedDecimal::percent(2)); + assert_eq!(SignedDecimal::percent(10) * two, SignedDecimal::percent(20)); + assert_eq!( + SignedDecimal::percent(100) * two, + SignedDecimal::percent(200) + ); + assert_eq!( + SignedDecimal::percent(1000) * two, + SignedDecimal::percent(2000) + ); + assert_eq!(SignedDecimal::percent(-1) * two, SignedDecimal::percent(-2)); + assert_eq!( + two * SignedDecimal::new(Int128::MIN / Int128::new(2)), + SignedDecimal::MIN + ); + + // half + assert_eq!(half * SignedDecimal::percent(0), SignedDecimal::percent(0)); + assert_eq!(half * SignedDecimal::percent(1), SignedDecimal::permille(5)); + assert_eq!(half * SignedDecimal::percent(10), SignedDecimal::percent(5)); + assert_eq!( + half * SignedDecimal::percent(100), + SignedDecimal::percent(50) + ); + assert_eq!( + half * SignedDecimal::percent(1000), + SignedDecimal::percent(500) + ); + assert_eq!(SignedDecimal::percent(0) * half, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) * half, SignedDecimal::permille(5)); + assert_eq!(SignedDecimal::percent(10) * half, SignedDecimal::percent(5)); + assert_eq!( + SignedDecimal::percent(100) * half, + SignedDecimal::percent(50) + ); + assert_eq!( + SignedDecimal::percent(1000) * half, + SignedDecimal::percent(500) + ); + + // Move left + let a = dec("123.127726548762582"); + assert_eq!(a * dec("1"), dec("123.127726548762582")); + assert_eq!(a * dec("10"), dec("1231.27726548762582")); + assert_eq!(a * dec("100"), dec("12312.7726548762582")); + assert_eq!(a * dec("1000"), dec("123127.726548762582")); + assert_eq!(a * dec("1000000"), dec("123127726.548762582")); + assert_eq!(a * dec("1000000000"), dec("123127726548.762582")); + assert_eq!(a * dec("1000000000000"), dec("123127726548762.582")); + assert_eq!(a * dec("1000000000000000"), dec("123127726548762582")); + assert_eq!(a * dec("1000000000000000000"), dec("123127726548762582000")); + assert_eq!(dec("1") * a, dec("123.127726548762582")); + assert_eq!(dec("10") * a, dec("1231.27726548762582")); + assert_eq!(dec("100") * a, dec("12312.7726548762582")); + assert_eq!(dec("1000") * a, dec("123127.726548762582")); + assert_eq!(dec("1000000") * a, dec("123127726.548762582")); + assert_eq!(dec("1000000000") * a, dec("123127726548.762582")); + assert_eq!(dec("1000000000000") * a, dec("123127726548762.582")); + assert_eq!(dec("1000000000000000") * a, dec("123127726548762582")); + assert_eq!(dec("1000000000000000000") * a, dec("123127726548762582000")); + assert_eq!( + dec("-1000000000000000000") * a, + dec("-123127726548762582000") + ); + + // Move right + let max = SignedDecimal::MAX; + assert_eq!( + max * dec("1.0"), + dec("170141183460469231731.687303715884105727") + ); + assert_eq!( + max * dec("0.1"), + dec("17014118346046923173.168730371588410572") + ); + assert_eq!( + max * dec("0.01"), + dec("1701411834604692317.316873037158841057") + ); + assert_eq!( + max * dec("0.001"), + dec("170141183460469231.731687303715884105") + ); + assert_eq!( + max * dec("0.000001"), + dec("170141183460469.231731687303715884") + ); + assert_eq!( + max * dec("0.000000001"), + dec("170141183460.469231731687303715") + ); + assert_eq!( + max * dec("0.000000000001"), + dec("170141183.460469231731687303") + ); + assert_eq!( + max * dec("0.000000000000001"), + dec("170141.183460469231731687") + ); + assert_eq!( + max * dec("0.000000000000000001"), + dec("170.141183460469231731") + ); + + // works for refs + let a = SignedDecimal::percent(20); + let b = SignedDecimal::percent(30); + let expected = SignedDecimal::percent(6); + assert_eq!(a * b, expected); + assert_eq!(&a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn signed_decimal_mul_assign_works() { + let mut a = SignedDecimal::percent(15); + a *= SignedDecimal::percent(60); + assert_eq!(a, SignedDecimal::percent(9)); + + // works for refs + let mut a = SignedDecimal::percent(50); + let b = SignedDecimal::percent(20); + a *= &b; + assert_eq!(a, SignedDecimal::percent(10)); + } + + #[test] + #[should_panic(expected = "attempt to multiply with overflow")] + fn signed_decimal_mul_overflow_panics() { + let _value = SignedDecimal::MAX * SignedDecimal::percent(101); + } + + #[test] + fn signed_decimal_checked_mul() { + let test_data = [ + (SignedDecimal::zero(), SignedDecimal::zero()), + (SignedDecimal::zero(), SignedDecimal::one()), + (SignedDecimal::one(), SignedDecimal::zero()), + (SignedDecimal::percent(10), SignedDecimal::zero()), + (SignedDecimal::percent(10), SignedDecimal::percent(5)), + (SignedDecimal::MAX, SignedDecimal::one()), + ( + SignedDecimal::MAX / Int128::new(2), + SignedDecimal::percent(200), + ), + (SignedDecimal::permille(6), SignedDecimal::permille(13)), + (SignedDecimal::permille(-6), SignedDecimal::permille(0)), + (SignedDecimal::MAX, SignedDecimal::negative_one()), + ]; + + // The regular core::ops::Mul is our source of truth for these tests. + for (x, y) in test_data.into_iter() { + assert_eq!(x * y, x.checked_mul(y).unwrap()); + } + } + + #[test] + fn signed_decimal_checked_mul_overflow() { + assert_eq!( + SignedDecimal::MAX.checked_mul(SignedDecimal::percent(200)), + Err(OverflowError { + operation: OverflowOperation::Mul, + operand1: SignedDecimal::MAX.to_string(), + operand2: SignedDecimal::percent(200).to_string(), + }) + ); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_implements_div() { + let one = SignedDecimal::one(); + let two = one + one; + let half = SignedDecimal::percent(50); + + // 1/x and x/1 + assert_eq!( + one / SignedDecimal::percent(1), + SignedDecimal::percent(10_000) + ); + assert_eq!( + one / SignedDecimal::percent(10), + SignedDecimal::percent(1_000) + ); + assert_eq!( + one / SignedDecimal::percent(100), + SignedDecimal::percent(100) + ); + assert_eq!( + one / SignedDecimal::percent(1000), + SignedDecimal::percent(10) + ); + assert_eq!(SignedDecimal::percent(0) / one, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) / one, SignedDecimal::percent(1)); + assert_eq!(SignedDecimal::percent(10) / one, SignedDecimal::percent(10)); + assert_eq!( + SignedDecimal::percent(100) / one, + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::percent(1000) / one, + SignedDecimal::percent(1000) + ); + assert_eq!( + one / SignedDecimal::percent(-1), + SignedDecimal::percent(-10_000) + ); + assert_eq!( + one / SignedDecimal::percent(-10), + SignedDecimal::percent(-1_000) + ); + + // double + assert_eq!( + two / SignedDecimal::percent(1), + SignedDecimal::percent(20_000) + ); + assert_eq!( + two / SignedDecimal::percent(10), + SignedDecimal::percent(2_000) + ); + assert_eq!( + two / SignedDecimal::percent(100), + SignedDecimal::percent(200) + ); + assert_eq!( + two / SignedDecimal::percent(1000), + SignedDecimal::percent(20) + ); + assert_eq!(SignedDecimal::percent(0) / two, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) / two, dec("0.005")); + assert_eq!(SignedDecimal::percent(10) / two, SignedDecimal::percent(5)); + assert_eq!( + SignedDecimal::percent(100) / two, + SignedDecimal::percent(50) + ); + assert_eq!( + SignedDecimal::percent(1000) / two, + SignedDecimal::percent(500) + ); + assert_eq!( + two / SignedDecimal::percent(-1), + SignedDecimal::percent(-20_000) + ); + assert_eq!( + SignedDecimal::percent(-10000) / two, + SignedDecimal::percent(-5000) + ); + + // half + assert_eq!( + half / SignedDecimal::percent(1), + SignedDecimal::percent(5_000) + ); + assert_eq!( + half / SignedDecimal::percent(10), + SignedDecimal::percent(500) + ); + assert_eq!( + half / SignedDecimal::percent(100), + SignedDecimal::percent(50) + ); + assert_eq!( + half / SignedDecimal::percent(1000), + SignedDecimal::percent(5) + ); + assert_eq!(SignedDecimal::percent(0) / half, SignedDecimal::percent(0)); + assert_eq!(SignedDecimal::percent(1) / half, SignedDecimal::percent(2)); + assert_eq!( + SignedDecimal::percent(10) / half, + SignedDecimal::percent(20) + ); + assert_eq!( + SignedDecimal::percent(100) / half, + SignedDecimal::percent(200) + ); + assert_eq!( + SignedDecimal::percent(1000) / half, + SignedDecimal::percent(2000) + ); + + // Move right + let a = dec("123127726548762582"); + assert_eq!(a / dec("1"), dec("123127726548762582")); + assert_eq!(a / dec("10"), dec("12312772654876258.2")); + assert_eq!(a / dec("100"), dec("1231277265487625.82")); + assert_eq!(a / dec("1000"), dec("123127726548762.582")); + assert_eq!(a / dec("1000000"), dec("123127726548.762582")); + assert_eq!(a / dec("1000000000"), dec("123127726.548762582")); + assert_eq!(a / dec("1000000000000"), dec("123127.726548762582")); + assert_eq!(a / dec("1000000000000000"), dec("123.127726548762582")); + assert_eq!(a / dec("1000000000000000000"), dec("0.123127726548762582")); + assert_eq!(dec("1") / a, dec("0.000000000000000008")); + assert_eq!(dec("10") / a, dec("0.000000000000000081")); + assert_eq!(dec("100") / a, dec("0.000000000000000812")); + assert_eq!(dec("1000") / a, dec("0.000000000000008121")); + assert_eq!(dec("1000000") / a, dec("0.000000000008121647")); + assert_eq!(dec("1000000000") / a, dec("0.000000008121647560")); + assert_eq!(dec("1000000000000") / a, dec("0.000008121647560868")); + assert_eq!(dec("1000000000000000") / a, dec("0.008121647560868164")); + assert_eq!(dec("1000000000000000000") / a, dec("8.121647560868164773")); + // negative + let a = dec("-123127726548762582"); + assert_eq!(a / dec("1"), dec("-123127726548762582")); + assert_eq!(a / dec("10"), dec("-12312772654876258.2")); + assert_eq!(a / dec("100"), dec("-1231277265487625.82")); + assert_eq!(a / dec("1000"), dec("-123127726548762.582")); + assert_eq!(a / dec("1000000"), dec("-123127726548.762582")); + assert_eq!(a / dec("1000000000"), dec("-123127726.548762582")); + assert_eq!(a / dec("1000000000000"), dec("-123127.726548762582")); + assert_eq!(a / dec("1000000000000000"), dec("-123.127726548762582")); + assert_eq!(a / dec("1000000000000000000"), dec("-0.123127726548762582")); + assert_eq!(dec("1") / a, dec("-0.000000000000000008")); + + // Move left + let a = dec("0.123127726548762582"); + assert_eq!(a / dec("1.0"), dec("0.123127726548762582")); + assert_eq!(a / dec("0.1"), dec("1.23127726548762582")); + assert_eq!(a / dec("0.01"), dec("12.3127726548762582")); + assert_eq!(a / dec("0.001"), dec("123.127726548762582")); + assert_eq!(a / dec("0.000001"), dec("123127.726548762582")); + assert_eq!(a / dec("0.000000001"), dec("123127726.548762582")); + assert_eq!(a / dec("0.000000000001"), dec("123127726548.762582")); + assert_eq!(a / dec("0.000000000000001"), dec("123127726548762.582")); + assert_eq!(a / dec("0.000000000000000001"), dec("123127726548762582")); + // negative + let a = dec("-0.123127726548762582"); + assert_eq!(a / dec("1.0"), dec("-0.123127726548762582")); + assert_eq!(a / dec("0.1"), dec("-1.23127726548762582")); + assert_eq!(a / dec("0.01"), dec("-12.3127726548762582")); + assert_eq!(a / dec("0.001"), dec("-123.127726548762582")); + assert_eq!(a / dec("0.000001"), dec("-123127.726548762582")); + assert_eq!(a / dec("0.000000001"), dec("-123127726.548762582")); + + assert_eq!( + SignedDecimal::percent(15) / SignedDecimal::percent(60), + SignedDecimal::percent(25) + ); + + // works for refs + let a = SignedDecimal::percent(100); + let b = SignedDecimal::percent(20); + let expected = SignedDecimal::percent(500); + assert_eq!(a / b, expected); + assert_eq!(&a / b, expected); + assert_eq!(a / &b, expected); + assert_eq!(&a / &b, expected); + } + + #[test] + fn signed_decimal_div_assign_works() { + let mut a = SignedDecimal::percent(15); + a /= SignedDecimal::percent(20); + assert_eq!(a, SignedDecimal::percent(75)); + + // works for refs + let mut a = SignedDecimal::percent(50); + let b = SignedDecimal::percent(20); + a /= &b; + assert_eq!(a, SignedDecimal::percent(250)); + } + + #[test] + #[should_panic(expected = "Division failed - multiplication overflow")] + fn signed_decimal_div_overflow_panics() { + let _value = SignedDecimal::MAX / SignedDecimal::percent(10); + } + + #[test] + #[should_panic(expected = "Division failed - denominator must not be zero")] + fn signed_decimal_div_by_zero_panics() { + let _value = SignedDecimal::one() / SignedDecimal::zero(); + } + + #[test] + fn signed_decimal_int128_division() { + // a/b + let left = SignedDecimal::percent(150); // 1.5 + let right = Int128::new(3); + assert_eq!(left / right, SignedDecimal::percent(50)); + + // negative + let left = SignedDecimal::percent(-150); // -1.5 + let right = Int128::new(3); + assert_eq!(left / right, SignedDecimal::percent(-50)); + + // 0/a + let left = SignedDecimal::zero(); + let right = Int128::new(300); + assert_eq!(left / right, SignedDecimal::zero()); + } + + #[test] + #[should_panic] + fn signed_decimal_int128_divide_by_zero() { + let left = SignedDecimal::percent(150); // 1.5 + let right = Int128::new(0); + let _result = left / right; + } + + #[test] + fn signed_decimal_int128_div_assign() { + // a/b + let mut dec = SignedDecimal::percent(150); // 1.5 + dec /= Int128::new(3); + assert_eq!(dec, SignedDecimal::percent(50)); + + // 0/a + let mut dec = SignedDecimal::zero(); + dec /= Int128::new(300); + assert_eq!(dec, SignedDecimal::zero()); + } + + #[test] + #[should_panic] + fn signed_decimal_int128_div_assign_by_zero() { + // a/0 + let mut dec = SignedDecimal::percent(50); + dec /= Int128::new(0); + } + + #[test] + fn signed_decimal_checked_pow() { + for exp in 0..10 { + assert_eq!( + SignedDecimal::one().checked_pow(exp).unwrap(), + SignedDecimal::one() + ); + } + + // This case is mathematically undefined but we ensure consistency with Rust standard types + // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=20df6716048e77087acd40194b233494 + assert_eq!( + SignedDecimal::zero().checked_pow(0).unwrap(), + SignedDecimal::one() + ); + + for exp in 1..10 { + assert_eq!( + SignedDecimal::zero().checked_pow(exp).unwrap(), + SignedDecimal::zero() + ); + } + + for exp in 1..10 { + assert_eq!( + SignedDecimal::negative_one().checked_pow(exp).unwrap(), + // alternates between 1 and -1 + if exp % 2 == 0 { + SignedDecimal::one() + } else { + SignedDecimal::negative_one() + } + ) + } + + for num in &[ + SignedDecimal::percent(50), + SignedDecimal::percent(99), + SignedDecimal::percent(200), + ] { + assert_eq!(num.checked_pow(0).unwrap(), SignedDecimal::one()) + } + + assert_eq!( + SignedDecimal::percent(20).checked_pow(2).unwrap(), + SignedDecimal::percent(4) + ); + + assert_eq!( + SignedDecimal::percent(20).checked_pow(3).unwrap(), + SignedDecimal::permille(8) + ); + + assert_eq!( + SignedDecimal::percent(200).checked_pow(4).unwrap(), + SignedDecimal::percent(1600) + ); + + assert_eq!( + SignedDecimal::percent(200).checked_pow(4).unwrap(), + SignedDecimal::percent(1600) + ); + + assert_eq!( + SignedDecimal::percent(700).checked_pow(5).unwrap(), + SignedDecimal::percent(1680700) + ); + + assert_eq!( + SignedDecimal::percent(700).checked_pow(8).unwrap(), + SignedDecimal::percent(576480100) + ); + + assert_eq!( + SignedDecimal::percent(700).checked_pow(10).unwrap(), + SignedDecimal::percent(28247524900) + ); + + assert_eq!( + SignedDecimal::percent(120).checked_pow(123).unwrap(), + SignedDecimal(5486473221892422150877397607i128.into()) + ); + + assert_eq!( + SignedDecimal::percent(10).checked_pow(2).unwrap(), + SignedDecimal(10000000000000000i128.into()) + ); + + assert_eq!( + SignedDecimal::percent(10).checked_pow(18).unwrap(), + SignedDecimal(1i128.into()) + ); + + let decimals = [ + SignedDecimal::percent(-50), + SignedDecimal::percent(-99), + SignedDecimal::percent(-200), + ]; + let exponents = [1, 2, 3, 4, 5, 8, 10]; + + for d in decimals { + for e in exponents { + // use multiplication as source of truth + let mut mul = Ok(d); + for _ in 1..e { + mul = mul.and_then(|mul| mul.checked_mul(d)); + } + assert_eq!(mul, d.checked_pow(e)); + } + } + } + + #[test] + fn signed_decimal_checked_pow_overflow() { + assert_eq!( + SignedDecimal::MAX.checked_pow(2), + Err(OverflowError { + operation: OverflowOperation::Pow, + operand1: SignedDecimal::MAX.to_string(), + operand2: "2".to_string(), + }) + ); + } + + #[test] + fn signed_decimal_to_string() { + // Integers + assert_eq!(SignedDecimal::zero().to_string(), "0"); + assert_eq!(SignedDecimal::one().to_string(), "1"); + assert_eq!(SignedDecimal::percent(500).to_string(), "5"); + assert_eq!(SignedDecimal::percent(-500).to_string(), "-5"); + + // SignedDecimals + assert_eq!(SignedDecimal::percent(125).to_string(), "1.25"); + assert_eq!(SignedDecimal::percent(42638).to_string(), "426.38"); + assert_eq!(SignedDecimal::percent(3).to_string(), "0.03"); + assert_eq!(SignedDecimal::permille(987).to_string(), "0.987"); + assert_eq!(SignedDecimal::percent(-125).to_string(), "-1.25"); + assert_eq!(SignedDecimal::percent(-42638).to_string(), "-426.38"); + assert_eq!(SignedDecimal::percent(-3).to_string(), "-0.03"); + assert_eq!(SignedDecimal::permille(-987).to_string(), "-0.987"); + + assert_eq!( + SignedDecimal(Int128::from(1i128)).to_string(), + "0.000000000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(10i128)).to_string(), + "0.00000000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(100i128)).to_string(), + "0.0000000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(1000i128)).to_string(), + "0.000000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(10000i128)).to_string(), + "0.00000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(100000i128)).to_string(), + "0.0000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(1000000i128)).to_string(), + "0.000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(10000000i128)).to_string(), + "0.00000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(100000000i128)).to_string(), + "0.0000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(1000000000i128)).to_string(), + "0.000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(10000000000i128)).to_string(), + "0.00000001" + ); + assert_eq!( + SignedDecimal(Int128::from(100000000000i128)).to_string(), + "0.0000001" + ); + assert_eq!( + SignedDecimal(Int128::from(10000000000000i128)).to_string(), + "0.00001" + ); + assert_eq!( + SignedDecimal(Int128::from(100000000000000i128)).to_string(), + "0.0001" + ); + assert_eq!( + SignedDecimal(Int128::from(1000000000000000i128)).to_string(), + "0.001" + ); + assert_eq!( + SignedDecimal(Int128::from(10000000000000000i128)).to_string(), + "0.01" + ); + assert_eq!( + SignedDecimal(Int128::from(100000000000000000i128)).to_string(), + "0.1" + ); + assert_eq!( + SignedDecimal(Int128::from(-1i128)).to_string(), + "-0.000000000000000001" + ); + assert_eq!( + SignedDecimal(Int128::from(-100000000000000i128)).to_string(), + "-0.0001" + ); + assert_eq!( + SignedDecimal(Int128::from(-100000000000000000i128)).to_string(), + "-0.1" + ); + } + + #[test] + fn signed_decimal_iter_sum() { + let items = vec![ + SignedDecimal::zero(), + SignedDecimal(Int128::from(2i128)), + SignedDecimal(Int128::from(2i128)), + SignedDecimal(Int128::from(-2i128)), + ]; + assert_eq!( + items.iter().sum::(), + SignedDecimal(Int128::from(2i128)) + ); + assert_eq!( + items.into_iter().sum::(), + SignedDecimal(Int128::from(2i128)) + ); + + let empty: Vec = vec![]; + assert_eq!(SignedDecimal::zero(), empty.iter().sum::()); + } + + #[test] + fn signed_decimal_serialize() { + assert_eq!(to_json_vec(&SignedDecimal::zero()).unwrap(), br#""0""#); + assert_eq!(to_json_vec(&SignedDecimal::one()).unwrap(), br#""1""#); + assert_eq!( + to_json_vec(&SignedDecimal::percent(8)).unwrap(), + br#""0.08""# + ); + assert_eq!( + to_json_vec(&SignedDecimal::percent(87)).unwrap(), + br#""0.87""# + ); + assert_eq!( + to_json_vec(&SignedDecimal::percent(876)).unwrap(), + br#""8.76""# + ); + assert_eq!( + to_json_vec(&SignedDecimal::percent(8765)).unwrap(), + br#""87.65""# + ); + assert_eq!( + to_json_vec(&SignedDecimal::percent(-87654)).unwrap(), + br#""-876.54""# + ); + assert_eq!( + to_json_vec(&SignedDecimal::negative_one()).unwrap(), + br#""-1""# + ); + assert_eq!( + to_json_vec(&-SignedDecimal::percent(8)).unwrap(), + br#""-0.08""# + ); + } + + #[test] + fn signed_decimal_deserialize() { + assert_eq!( + from_json::(br#""0""#).unwrap(), + SignedDecimal::zero() + ); + assert_eq!( + from_json::(br#""1""#).unwrap(), + SignedDecimal::one() + ); + assert_eq!( + from_json::(br#""000""#).unwrap(), + SignedDecimal::zero() + ); + assert_eq!( + from_json::(br#""001""#).unwrap(), + SignedDecimal::one() + ); + + assert_eq!( + from_json::(br#""0.08""#).unwrap(), + SignedDecimal::percent(8) + ); + assert_eq!( + from_json::(br#""0.87""#).unwrap(), + SignedDecimal::percent(87) + ); + assert_eq!( + from_json::(br#""8.76""#).unwrap(), + SignedDecimal::percent(876) + ); + assert_eq!( + from_json::(br#""87.65""#).unwrap(), + SignedDecimal::percent(8765) + ); + + // negative numbers + assert_eq!( + from_json::(br#""-0""#).unwrap(), + SignedDecimal::zero() + ); + assert_eq!( + from_json::(br#""-1""#).unwrap(), + SignedDecimal::negative_one() + ); + assert_eq!( + from_json::(br#""-001""#).unwrap(), + SignedDecimal::negative_one() + ); + assert_eq!( + from_json::(br#""-0.08""#).unwrap(), + SignedDecimal::percent(-8) + ); + } + + #[test] + fn signed_decimal_abs_diff_works() { + let a = SignedDecimal::percent(285); + let b = SignedDecimal::percent(200); + let expected = Decimal::percent(85); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let a = SignedDecimal::percent(-200); + let b = SignedDecimal::percent(200); + let expected = Decimal::percent(400); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let a = SignedDecimal::percent(-200); + let b = SignedDecimal::percent(-240); + let expected = Decimal::percent(40); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_rem_works() { + // 4.02 % 1.11 = 0.69 + assert_eq!( + SignedDecimal::percent(402) % SignedDecimal::percent(111), + SignedDecimal::percent(69) + ); + + // 15.25 % 4 = 3.25 + assert_eq!( + SignedDecimal::percent(1525) % SignedDecimal::percent(400), + SignedDecimal::percent(325) + ); + + // -20.25 % 5 = -25 + assert_eq!( + SignedDecimal::percent(-2025) % SignedDecimal::percent(500), + SignedDecimal::percent(-25) + ); + + let a = SignedDecimal::percent(318); + let b = SignedDecimal::percent(317); + let expected = SignedDecimal::percent(1); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + fn signed_decimal_rem_assign_works() { + let mut a = SignedDecimal::percent(17673); + a %= SignedDecimal::percent(2362); + assert_eq!(a, SignedDecimal::percent(1139)); // 176.73 % 23.62 = 11.39 + + let mut a = SignedDecimal::percent(4262); + let b = SignedDecimal::percent(1270); + a %= &b; + assert_eq!(a, SignedDecimal::percent(452)); // 42.62 % 12.7 = 4.52 + + let mut a = SignedDecimal::percent(-4262); + let b = SignedDecimal::percent(1270); + a %= &b; + assert_eq!(a, SignedDecimal::percent(-452)); // -42.62 % 12.7 = -4.52 + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn signed_decimal_rem_panics_for_zero() { + let _ = SignedDecimal::percent(777) % SignedDecimal::zero(); + } + + #[test] + fn signed_decimal_checked_methods() { + // checked add + assert_eq!( + SignedDecimal::percent(402) + .checked_add(SignedDecimal::percent(111)) + .unwrap(), + SignedDecimal::percent(513) + ); + assert!(matches!( + SignedDecimal::MAX.checked_add(SignedDecimal::percent(1)), + Err(OverflowError { .. }) + )); + assert!(matches!( + SignedDecimal::MIN.checked_add(SignedDecimal::percent(-1)), + Err(OverflowError { .. }) + )); + + // checked sub + assert_eq!( + SignedDecimal::percent(1111) + .checked_sub(SignedDecimal::percent(111)) + .unwrap(), + SignedDecimal::percent(1000) + ); + assert_eq!( + SignedDecimal::zero() + .checked_sub(SignedDecimal::percent(1)) + .unwrap(), + SignedDecimal::percent(-1) + ); + assert!(matches!( + SignedDecimal::MIN.checked_sub(SignedDecimal::percent(1)), + Err(OverflowError { .. }) + )); + assert!(matches!( + SignedDecimal::MAX.checked_sub(SignedDecimal::percent(-1)), + Err(OverflowError { .. }) + )); + + // checked div + assert_eq!( + SignedDecimal::percent(30) + .checked_div(SignedDecimal::percent(200)) + .unwrap(), + SignedDecimal::percent(15) + ); + assert_eq!( + SignedDecimal::percent(88) + .checked_div(SignedDecimal::percent(20)) + .unwrap(), + SignedDecimal::percent(440) + ); + assert!(matches!( + SignedDecimal::MAX.checked_div(SignedDecimal::zero()), + Err(CheckedFromRatioError::DivideByZero {}) + )); + assert!(matches!( + SignedDecimal::MAX.checked_div(SignedDecimal::percent(1)), + Err(CheckedFromRatioError::Overflow {}) + )); + assert_eq!( + SignedDecimal::percent(-88) + .checked_div(SignedDecimal::percent(20)) + .unwrap(), + SignedDecimal::percent(-440) + ); + assert_eq!( + SignedDecimal::percent(-88) + .checked_div(SignedDecimal::percent(-20)) + .unwrap(), + SignedDecimal::percent(440) + ); + + // checked rem + assert_eq!( + SignedDecimal::percent(402) + .checked_rem(SignedDecimal::percent(111)) + .unwrap(), + SignedDecimal::percent(69) + ); + assert_eq!( + SignedDecimal::percent(1525) + .checked_rem(SignedDecimal::percent(400)) + .unwrap(), + SignedDecimal::percent(325) + ); + assert_eq!( + SignedDecimal::percent(-1525) + .checked_rem(SignedDecimal::percent(400)) + .unwrap(), + SignedDecimal::percent(-325) + ); + assert_eq!( + SignedDecimal::percent(-1525) + .checked_rem(SignedDecimal::percent(-400)) + .unwrap(), + SignedDecimal::percent(-325) + ); + assert!(matches!( + SignedDecimal::MAX.checked_rem(SignedDecimal::zero()), + Err(DivideByZeroError { .. }) + )); + } + + #[test] + fn signed_decimal_pow_works() { + assert_eq!( + SignedDecimal::percent(200).pow(2), + SignedDecimal::percent(400) + ); + assert_eq!( + SignedDecimal::percent(-200).pow(2), + SignedDecimal::percent(400) + ); + assert_eq!( + SignedDecimal::percent(-200).pow(3), + SignedDecimal::percent(-800) + ); + assert_eq!( + SignedDecimal::percent(200).pow(10), + SignedDecimal::percent(102400) + ); + } + + #[test] + #[should_panic] + fn signed_decimal_pow_overflow_panics() { + _ = SignedDecimal::MAX.pow(2u32); + } + + #[test] + fn signed_decimal_saturating_works() { + assert_eq!( + SignedDecimal::percent(200).saturating_add(SignedDecimal::percent(200)), + SignedDecimal::percent(400) + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_add(SignedDecimal::percent(200)), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_add(SignedDecimal::percent(-200)), + SignedDecimal::percent(-400) + ); + assert_eq!( + SignedDecimal::MAX.saturating_add(SignedDecimal::percent(200)), + SignedDecimal::MAX + ); + assert_eq!( + SignedDecimal::MIN.saturating_add(SignedDecimal::percent(-1)), + SignedDecimal::MIN + ); + assert_eq!( + SignedDecimal::percent(200).saturating_sub(SignedDecimal::percent(100)), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_sub(SignedDecimal::percent(100)), + SignedDecimal::percent(-300) + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_sub(SignedDecimal::percent(-100)), + SignedDecimal::percent(-100) + ); + assert_eq!( + SignedDecimal::zero().saturating_sub(SignedDecimal::percent(200)), + SignedDecimal::from_str("-2").unwrap() + ); + assert_eq!( + SignedDecimal::MIN.saturating_sub(SignedDecimal::percent(200)), + SignedDecimal::MIN + ); + assert_eq!( + SignedDecimal::MAX.saturating_sub(SignedDecimal::percent(-200)), + SignedDecimal::MAX + ); + assert_eq!( + SignedDecimal::percent(200).saturating_mul(SignedDecimal::percent(50)), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_mul(SignedDecimal::percent(50)), + SignedDecimal::percent(-100) + ); + assert_eq!( + SignedDecimal::percent(-200).saturating_mul(SignedDecimal::percent(-50)), + SignedDecimal::percent(100) + ); + assert_eq!( + SignedDecimal::MAX.saturating_mul(SignedDecimal::percent(200)), + SignedDecimal::MAX + ); + assert_eq!( + SignedDecimal::MIN.saturating_mul(SignedDecimal::percent(200)), + SignedDecimal::MIN + ); + assert_eq!( + SignedDecimal::MIN.saturating_mul(SignedDecimal::percent(-200)), + SignedDecimal::MAX + ); + assert_eq!( + SignedDecimal::percent(400).saturating_pow(2u32), + SignedDecimal::percent(1600) + ); + assert_eq!(SignedDecimal::MAX.saturating_pow(2u32), SignedDecimal::MAX); + assert_eq!(SignedDecimal::MAX.saturating_pow(3u32), SignedDecimal::MAX); + assert_eq!(SignedDecimal::MIN.saturating_pow(2u32), SignedDecimal::MAX); + assert_eq!(SignedDecimal::MIN.saturating_pow(3u32), SignedDecimal::MIN); + } + + #[test] + fn signed_decimal_rounding() { + assert_eq!(SignedDecimal::one().floor(), SignedDecimal::one()); + assert_eq!(SignedDecimal::percent(150).floor(), SignedDecimal::one()); + assert_eq!(SignedDecimal::percent(199).floor(), SignedDecimal::one()); + assert_eq!( + SignedDecimal::percent(200).floor(), + SignedDecimal::percent(200) + ); + assert_eq!(SignedDecimal::percent(99).floor(), SignedDecimal::zero()); + assert_eq!( + SignedDecimal(Int128::from(1i128)).floor(), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal(Int128::from(-1i128)).floor(), + SignedDecimal::negative_one() + ); + assert_eq!( + SignedDecimal::permille(-1234).floor(), + SignedDecimal::percent(-200) + ); + + assert_eq!(SignedDecimal::one().ceil(), SignedDecimal::one()); + assert_eq!( + SignedDecimal::percent(150).ceil(), + SignedDecimal::percent(200) + ); + assert_eq!( + SignedDecimal::percent(199).ceil(), + SignedDecimal::percent(200) + ); + assert_eq!(SignedDecimal::percent(99).ceil(), SignedDecimal::one()); + assert_eq!( + SignedDecimal(Int128::from(1i128)).ceil(), + SignedDecimal::one() + ); + assert_eq!( + SignedDecimal(Int128::from(-1i128)).ceil(), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::permille(-1234).ceil(), + SignedDecimal::negative_one() + ); + + assert_eq!(SignedDecimal::one().trunc(), SignedDecimal::one()); + assert_eq!(SignedDecimal::percent(150).trunc(), SignedDecimal::one()); + assert_eq!(SignedDecimal::percent(199).trunc(), SignedDecimal::one()); + assert_eq!( + SignedDecimal::percent(200).trunc(), + SignedDecimal::percent(200) + ); + assert_eq!(SignedDecimal::percent(99).trunc(), SignedDecimal::zero()); + assert_eq!( + SignedDecimal(Int128::from(1i128)).trunc(), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal(Int128::from(-1i128)).trunc(), + SignedDecimal::zero() + ); + assert_eq!( + SignedDecimal::permille(-1234).trunc(), + SignedDecimal::negative_one() + ); + } + + #[test] + #[should_panic(expected = "attempt to ceil with overflow")] + fn signed_decimal_ceil_panics() { + let _ = SignedDecimal::MAX.ceil(); + } + + #[test] + #[should_panic(expected = "attempt to floor with overflow")] + fn signed_decimal_floor_panics() { + let _ = SignedDecimal::MIN.floor(); + } + + #[test] + fn signed_decimal_checked_ceil() { + assert_eq!( + SignedDecimal::percent(199).checked_ceil(), + Ok(SignedDecimal::percent(200)) + ); + assert_eq!(SignedDecimal::MAX.checked_ceil(), Err(RoundUpOverflowError)); + } + + #[test] + fn signed_decimal_checked_floor() { + assert_eq!( + SignedDecimal::percent(199).checked_floor(), + Ok(SignedDecimal::one()) + ); + assert_eq!( + SignedDecimal::percent(-199).checked_floor(), + Ok(SignedDecimal::percent(-200)) + ); + assert_eq!( + SignedDecimal::MIN.checked_floor(), + Err(RoundDownOverflowError) + ); + assert_eq!( + SignedDecimal::negative_one().checked_floor(), + Ok(SignedDecimal::negative_one()) + ); + } + + #[test] + fn signed_decimal_to_int_floor_works() { + let d = SignedDecimal::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(12)); + let d = SignedDecimal::from_str("12.345").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(12)); + let d = SignedDecimal::from_str("12.999").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(12)); + let d = SignedDecimal::from_str("0.98451384").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(0)); + let d = SignedDecimal::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(-13)); + let d = SignedDecimal::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(-13)); + let d = SignedDecimal::from_str("75.0").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(75)); + let d = SignedDecimal::from_str("0.0001").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(0)); + let d = SignedDecimal::from_str("0.0").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(0)); + let d = SignedDecimal::from_str("-0.0").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(0)); + let d = SignedDecimal::from_str("-0.0001").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(-1)); + let d = SignedDecimal::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_floor(), Int128::new(-75)); + let d = SignedDecimal::MAX; + assert_eq!(d.to_int_floor(), Int128::new(170141183460469231731)); + let d = SignedDecimal::MIN; + assert_eq!(d.to_int_floor(), Int128::new(-170141183460469231732)); + } + + #[test] + fn signed_decimal_to_int_ceil_works() { + let d = SignedDecimal::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(13)); + let d = SignedDecimal::from_str("12.345").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(13)); + let d = SignedDecimal::from_str("12.999").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(13)); + let d = SignedDecimal::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(-12)); + let d = SignedDecimal::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(-12)); + + let d = SignedDecimal::from_str("75.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(75)); + let d = SignedDecimal::from_str("0.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(0)); + let d = SignedDecimal::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int128::new(-75)); + + let d = SignedDecimal::MAX; + assert_eq!(d.to_int_ceil(), Int128::new(170141183460469231732)); + let d = SignedDecimal::MIN; + assert_eq!(d.to_int_ceil(), Int128::new(-170141183460469231731)); + } + + #[test] + fn signed_decimal_to_int_trunc_works() { + let d = SignedDecimal::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(12)); + let d = SignedDecimal::from_str("12.345").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(12)); + let d = SignedDecimal::from_str("12.999").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(12)); + let d = SignedDecimal::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(-12)); + let d = SignedDecimal::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(-12)); + + let d = SignedDecimal::from_str("75.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(75)); + let d = SignedDecimal::from_str("0.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(0)); + let d = SignedDecimal::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int128::new(-75)); + + let d = SignedDecimal::MAX; + assert_eq!(d.to_int_trunc(), Int128::new(170141183460469231731)); + let d = SignedDecimal::MIN; + assert_eq!(d.to_int_trunc(), Int128::new(-170141183460469231731)); + } + + #[test] + fn signed_decimal_neg_works() { + assert_eq!(-SignedDecimal::percent(50), SignedDecimal::percent(-50)); + assert_eq!(-SignedDecimal::one(), SignedDecimal::negative_one()); + } + + #[test] + fn signed_decimal_partial_eq() { + let test_cases = [ + ("1", "1", true), + ("0.5", "0.5", true), + ("0.5", "0.51", false), + ("0", "0.00000", true), + ("-1", "-1", true), + ("-0.5", "-0.5", true), + ("-0.5", "0.5", false), + ("-0.5", "-0.51", false), + ("-0", "-0.00000", true), + ] + .into_iter() + .map(|(lhs, rhs, expected)| (dec(lhs), dec(rhs), expected)); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } + + #[test] + fn signed_decimal_implements_debug() { + let decimal = SignedDecimal::from_str("123.45").unwrap(); + assert_eq!(format!("{decimal:?}"), "SignedDecimal(123.45)"); + + let test_cases = ["5", "5.01", "42", "0", "2", "-0.000001"]; + for s in test_cases { + let decimal = SignedDecimal::from_str(s).unwrap(); + let expected = format!("SignedDecimal({s})"); + assert_eq!(format!("{decimal:?}"), expected); + } + } + + #[test] + fn signed_decimal_can_be_instantiated_from_decimal256() { + let d: SignedDecimal = Decimal256::zero().try_into().unwrap(); + assert_eq!(d, SignedDecimal::zero()); + } + + #[test] + fn signed_decimal_may_fail_when_instantiated_from_decimal256() { + let err = >::try_into(Decimal256::MAX).unwrap_err(); + assert_eq!("SignedDecimalRangeExceeded", format!("{err:?}")); + assert_eq!("SignedDecimal range exceeded", format!("{err}")); + } + + #[test] + fn signed_decimal_can_be_serialized_and_deserialized() { + // properly deserialized + let value: SignedDecimal = serde_json::from_str(r#""123""#).unwrap(); + assert_eq!(SignedDecimal::from_str("123").unwrap(), value); + + // properly serialized + let value = SignedDecimal::from_str("456").unwrap(); + assert_eq!(r#""456""#, serde_json::to_string(&value).unwrap()); + + // invalid: not a string encoded decimal + assert_eq!( + "invalid type: integer `123`, expected string-encoded decimal at line 1 column 3", + serde_json::from_str::("123") + .err() + .unwrap() + .to_string() + ); + + // invalid: not properly defined signed decimal value + assert_eq!( + "Error parsing decimal '1.e': Generic error: Error parsing fractional at line 1 column 5", + serde_json::from_str::(r#""1.e""#) + .err() + .unwrap() + .to_string() + ); + } + + #[test] + fn signed_decimal_has_defined_json_schema() { + let schema = schema_for!(SignedDecimal); + assert_eq!( + "SignedDecimal", + schema.schema.metadata.unwrap().title.unwrap() + ); + } +} diff --git a/packages/std/src/math/signed_decimal_256.rs b/packages/std/src/math/signed_decimal_256.rs new file mode 100644 index 000000000..9468729c0 --- /dev/null +++ b/packages/std/src/math/signed_decimal_256.rs @@ -0,0 +1,3273 @@ +use core::cmp::Ordering; +use core::fmt::{self, Write}; +use core::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, +}; +use core::str::FromStr; +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use thiserror::Error; + +use crate::errors::{ + CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, + OverflowOperation, RoundDownOverflowError, RoundUpOverflowError, StdError, +}; +use crate::{forward_ref_partial_eq, Decimal, Decimal256, Int512, SignedDecimal}; + +use super::Fraction; +use super::Int256; + +/// A signed fixed-point decimal value with 18 fractional digits, +/// i.e. SignedDecimal256(1_000_000_000_000_000_000) == 1.0 +/// +/// The greatest possible value that can be represented is +/// 57896044618658097711785492504343953926634992332820282019728.792003956564819967 +/// (which is (2^255 - 1) / 10^18) +/// and the smallest is +/// -57896044618658097711785492504343953926634992332820282019728.792003956564819968 +/// (which is -2^255 / 10^18). +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct SignedDecimal256(#[schemars(with = "String")] Int256); + +forward_ref_partial_eq!(SignedDecimal256, SignedDecimal256); + +#[derive(Error, Debug, PartialEq, Eq)] +#[error("SignedDecimal256 range exceeded")] +pub struct SignedDecimal256RangeExceeded; + +impl SignedDecimal256 { + const DECIMAL_FRACTIONAL: Int256 = // 1*10**18 + Int256::from_i128(1_000_000_000_000_000_000); + const DECIMAL_FRACTIONAL_SQUARED: Int256 = // 1*10**36 + Int256::from_i128(1_000_000_000_000_000_000_000_000_000_000_000_000); + + /// The number of decimal places. Since decimal types are fixed-point rather than + /// floating-point, this is a constant. + pub const DECIMAL_PLACES: u32 = 18; // This needs to be an even number. + + /// The largest value that can be represented by this signed decimal type. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// assert_eq!( + /// SignedDecimal256::MAX.to_string(), + /// "57896044618658097711785492504343953926634992332820282019728.792003956564819967" + /// ); + /// ``` + pub const MAX: Self = Self(Int256::MAX); + + /// The smallest value that can be represented by this signed decimal type. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// assert_eq!( + /// SignedDecimal256::MIN.to_string(), + /// "-57896044618658097711785492504343953926634992332820282019728.792003956564819968" + /// ); + /// ``` + pub const MIN: Self = Self(Int256::MIN); + + /// Creates a SignedDecimal256(value) + /// This is equivalent to `SignedDecimal256::from_atomics(value, 18)` but usable in a const context. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal256, Int256}; + /// assert_eq!(SignedDecimal256::new(Int256::one()).to_string(), "0.000000000000000001"); + /// ``` + pub const fn new(value: Int256) -> Self { + Self(value) + } + + /// Creates a SignedDecimal256(Int256(value)) + /// This is equivalent to `SignedDecimal256::from_atomics(value, 18)` but usable in a const context. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// assert_eq!(SignedDecimal256::raw(1234i128).to_string(), "0.000000000000001234"); + /// ``` + pub const fn raw(value: i128) -> Self { + Self(Int256::from_i128(value)) + } + + /// Create a 1.0 SignedDecimal256 + #[inline] + pub const fn one() -> Self { + Self(Self::DECIMAL_FRACTIONAL) + } + + /// Create a -1.0 SignedDecimal256 + #[inline] + pub const fn negative_one() -> Self { + // -DECIMAL_FRATIONAL + Self(Int256::from_i128(-1_000_000_000_000_000_000)) + } + + /// Create a 0.0 SignedDecimal256 + #[inline] + pub const fn zero() -> Self { + Self(Int256::zero()) + } + + /// Convert x% into SignedDecimal256 + pub fn percent(x: i64) -> Self { + Self(((x as i128) * 10_000_000_000_000_000).into()) + } + + /// Convert permille (x/1000) into SignedDecimal256 + pub fn permille(x: i64) -> Self { + Self(((x as i128) * 1_000_000_000_000_000).into()) + } + + /// Convert basis points (x/10000) into SignedDecimal256 + pub fn bps(x: i64) -> Self { + Self(((x as i128) * 100_000_000_000_000).into()) + } + + /// Creates a signed decimal from a number of atomic units and the number + /// of decimal places. The inputs will be converted internally to form + /// a signed decimal with 18 decimal places. So the input 123 and 2 will create + /// the decimal 1.23. + /// + /// Using 18 decimal places is slightly more efficient than other values + /// as no internal conversion is necessary. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal256, Int256}; + /// let a = SignedDecimal256::from_atomics(Int256::from(1234), 3).unwrap(); + /// assert_eq!(a.to_string(), "1.234"); + /// + /// let a = SignedDecimal256::from_atomics(1234i128, 0).unwrap(); + /// assert_eq!(a.to_string(), "1234"); + /// + /// let a = SignedDecimal256::from_atomics(1i64, 18).unwrap(); + /// assert_eq!(a.to_string(), "0.000000000000000001"); + /// + /// let a = SignedDecimal256::from_atomics(-1i64, 18).unwrap(); + /// assert_eq!(a.to_string(), "-0.000000000000000001"); + /// ``` + pub fn from_atomics( + atomics: impl Into, + decimal_places: u32, + ) -> Result { + let atomics = atomics.into(); + let ten = Int256::from(10u64); + Ok(match decimal_places.cmp(&(Self::DECIMAL_PLACES)) { + Ordering::Less => { + let digits = (Self::DECIMAL_PLACES) - decimal_places; // No overflow because decimal_places < DECIMAL_PLACES + let factor = ten.checked_pow(digits).unwrap(); // Safe because digits <= 17 + Self( + atomics + .checked_mul(factor) + .map_err(|_| SignedDecimal256RangeExceeded)?, + ) + } + Ordering::Equal => Self(atomics), + Ordering::Greater => { + let digits = decimal_places - (Self::DECIMAL_PLACES); // No overflow because decimal_places > DECIMAL_PLACES + if let Ok(factor) = ten.checked_pow(digits) { + Self(atomics.checked_div(factor).unwrap()) // Safe because factor cannot be zero + } else { + // In this case `factor` exceeds the Int256 range. + // Any Int256 `x` divided by `factor` with `factor > Int256::MAX` is 0. + // Try e.g. Python3: `(2**128-1) // 2**128` + Self(Int256::zero()) + } + } + }) + } + + /// Returns the ratio (numerator / denominator) as a SignedDecimal256 + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// assert_eq!( + /// SignedDecimal256::from_ratio(1, 3).to_string(), + /// "0.333333333333333333" + /// ); + /// ``` + pub fn from_ratio(numerator: impl Into, denominator: impl Into) -> Self { + match SignedDecimal256::checked_from_ratio(numerator, denominator) { + Ok(value) => value, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => panic!("Multiplication overflow"), + } + } + + /// Returns the ratio (numerator / denominator) as a SignedDecimal256 + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal256, CheckedFromRatioError}; + /// assert_eq!( + /// SignedDecimal256::checked_from_ratio(1, 3).unwrap().to_string(), + /// "0.333333333333333333" + /// ); + /// assert_eq!( + /// SignedDecimal256::checked_from_ratio(1, 0), + /// Err(CheckedFromRatioError::DivideByZero) + /// ); + /// ``` + pub fn checked_from_ratio( + numerator: impl Into, + denominator: impl Into, + ) -> Result { + let numerator: Int256 = numerator.into(); + let denominator: Int256 = denominator.into(); + match numerator.checked_multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator) { + Ok(ratio) => { + // numerator * DECIMAL_FRACTIONAL / denominator + Ok(SignedDecimal256(ratio)) + } + Err(CheckedMultiplyRatioError::Overflow) => Err(CheckedFromRatioError::Overflow), + Err(CheckedMultiplyRatioError::DivideByZero) => { + Err(CheckedFromRatioError::DivideByZero) + } + } + } + + /// Returns `true` if the number is 0 + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0.is_zero() + } + + /// Returns `true` if the number is negative (< 0) + #[must_use] + pub const fn is_negative(&self) -> bool { + self.0.is_negative() + } + + /// A decimal is an integer of atomic units plus a number that specifies the + /// position of the decimal dot. So any decimal can be expressed as two numbers. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{SignedDecimal256, Int256}; + /// # use core::str::FromStr; + /// // Value with whole and fractional part + /// let a = SignedDecimal256::from_str("1.234").unwrap(); + /// assert_eq!(a.decimal_places(), 18); + /// assert_eq!(a.atomics(), Int256::from(1234000000000000000i128)); + /// + /// // Smallest possible value + /// let b = SignedDecimal256::from_str("0.000000000000000001").unwrap(); + /// assert_eq!(b.decimal_places(), 18); + /// assert_eq!(b.atomics(), Int256::from(1)); + /// ``` + #[must_use] + #[inline] + pub const fn atomics(&self) -> Int256 { + self.0 + } + + /// The number of decimal places. This is a constant value for now + /// but this could potentially change as the type evolves. + /// + /// See also [`SignedDecimal256::atomics()`]. + #[must_use] + #[inline] + pub const fn decimal_places(&self) -> u32 { + Self::DECIMAL_PLACES + } + + /// Rounds value by truncating the decimal places. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// # use core::str::FromStr; + /// assert!(SignedDecimal256::from_str("0.6").unwrap().trunc().is_zero()); + /// assert_eq!(SignedDecimal256::from_str("-5.8").unwrap().trunc().to_string(), "-5"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn trunc(&self) -> Self { + Self((self.0 / Self::DECIMAL_FRACTIONAL) * Self::DECIMAL_FRACTIONAL) + } + + /// Rounds value down after decimal places. Panics on overflow. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// # use core::str::FromStr; + /// assert!(SignedDecimal256::from_str("0.6").unwrap().floor().is_zero()); + /// assert_eq!(SignedDecimal256::from_str("-5.2").unwrap().floor().to_string(), "-6"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn floor(&self) -> Self { + match self.checked_floor() { + Ok(value) => value, + Err(_) => panic!("attempt to floor with overflow"), + } + } + + /// Rounds value down after decimal places. + pub fn checked_floor(&self) -> Result { + if self.is_negative() { + let truncated = self.trunc(); + + if truncated != self { + truncated + .checked_sub(SignedDecimal256::one()) + .map_err(|_| RoundDownOverflowError) + } else { + Ok(truncated) + } + } else { + Ok(self.trunc()) + } + } + + /// Rounds value up after decimal places. Panics on overflow. + /// + /// # Examples + /// + /// ``` + /// # use cosmwasm_std::SignedDecimal256; + /// # use core::str::FromStr; + /// assert_eq!(SignedDecimal256::from_str("0.2").unwrap().ceil(), SignedDecimal256::one()); + /// assert_eq!(SignedDecimal256::from_str("-5.8").unwrap().ceil().to_string(), "-5"); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn ceil(&self) -> Self { + match self.checked_ceil() { + Ok(value) => value, + Err(_) => panic!("attempt to ceil with overflow"), + } + } + + /// Rounds value up after decimal places. Returns OverflowError on overflow. + pub fn checked_ceil(&self) -> Result { + let floor = self.floor(); + if floor == self { + Ok(floor) + } else { + floor + .checked_add(SignedDecimal256::one()) + .map_err(|_| RoundUpOverflowError) + } + } + + /// Computes `self + other`, returning an `OverflowError` if an overflow occurred. + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Add, self, other)) + } + + /// Computes `self - other`, returning an `OverflowError` if an overflow occurred. + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + /// Multiplies one `SignedDecimal256` by another, returning an `OverflowError` if an overflow occurred. + pub fn checked_mul(self, other: Self) -> Result { + let result_as_int512 = + self.numerator().full_mul(other.numerator()) / Int512::from(Self::DECIMAL_FRACTIONAL); + result_as_int512 + .try_into() + .map(Self) + .map_err(|_| OverflowError { + operation: OverflowOperation::Mul, + operand1: self.to_string(), + operand2: other.to_string(), + }) + } + + /// Raises a value to the power of `exp`, panics if an overflow occurred. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => panic!("Multiplication overflow"), + } + } + + /// Raises a value to the power of `exp`, returning an `OverflowError` if an overflow occurred. + pub fn checked_pow(self, exp: u32) -> Result { + // This uses the exponentiation by squaring algorithm: + // https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Basic_method + + fn inner(mut x: SignedDecimal256, mut n: u32) -> Result { + if n == 0 { + return Ok(SignedDecimal256::one()); + } + + let mut y = SignedDecimal256::one(); + + while n > 1 { + if n % 2 == 0 { + x = x.checked_mul(x)?; + n /= 2; + } else { + y = x.checked_mul(y)?; + x = x.checked_mul(x)?; + n = (n - 1) / 2; + } + } + + Ok(x * y) + } + + inner(self, exp).map_err(|_| OverflowError { + operation: OverflowOperation::Pow, + operand1: self.to_string(), + operand2: exp.to_string(), + }) + } + + pub fn checked_div(self, other: Self) -> Result { + SignedDecimal256::checked_from_ratio(self.numerator(), other.numerator()) + } + + /// Computes `self % other`, returning an `DivideByZeroError` if `other == 0`. + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .map_err(|_| DivideByZeroError::new(self)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs_diff(self, other: Self) -> Decimal256 { + Decimal256::new(self.0.abs_diff(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + match self.checked_mul(other) { + Ok(value) => value, + Err(_) => { + // both negative or both positive results in positive number, otherwise negative + if self.is_negative() == other.is_negative() { + Self::MAX + } else { + Self::MIN + } + } + } + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => { + // odd exponent of negative number results in negative number + // everything else results in positive number + if self.is_negative() && exp % 2 == 1 { + Self::MIN + } else { + Self::MAX + } + } + } + } + + /// Converts this decimal to a signed integer by rounding down + /// to the next integer, e.g. 22.5 becomes 22 and -1.2 becomes -2. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal256, Int256}; + /// + /// let d = SignedDecimal256::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_floor(), Int256::from(12)); + /// + /// let d = SignedDecimal256::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_floor(), Int256::from(-13)); + /// + /// let d = SignedDecimal256::from_str("-0.05").unwrap(); + /// assert_eq!(d.to_int_floor(), Int256::from(-1)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_floor(self) -> Int256 { + if self.is_negative() { + // Using `x.to_int_floor() = -(-x).to_int_ceil()` for a negative `x`, + // but avoiding overflow by implementing the formula from `to_int_ceil` directly. + let x = self.0; + let y = Self::DECIMAL_FRACTIONAL; + // making sure not to negate `x`, as this would overflow + -Int256::one() - ((-Int256::one() - x) / y) + } else { + self.to_int_trunc() + } + } + + /// Converts this decimal to a signed integer by truncating + /// the fractional part, e.g. 22.5 becomes 22. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal256, Int256}; + /// + /// let d = SignedDecimal256::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int256::from(12)); + /// + /// let d = SignedDecimal256::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int256::from(-12)); + /// + /// let d = SignedDecimal256::from_str("75.0").unwrap(); + /// assert_eq!(d.to_int_trunc(), Int256::from(75)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_trunc(self) -> Int256 { + self.0 / Self::DECIMAL_FRACTIONAL + } + + /// Converts this decimal to a signed integer by rounding up + /// to the next integer, e.g. 22.3 becomes 23 and -1.2 becomes -1. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{SignedDecimal256, Int256}; + /// + /// let d = SignedDecimal256::from_str("12.345").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int256::from(13)); + /// + /// let d = SignedDecimal256::from_str("-12.999").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int256::from(-12)); + /// + /// let d = SignedDecimal256::from_str("75.0").unwrap(); + /// assert_eq!(d.to_int_ceil(), Int256::from(75)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_int_ceil(self) -> Int256 { + if self.is_negative() { + self.to_int_trunc() + } else { + // Using `q = 1 + ((x - 1) / y); // if x != 0` with unsigned integers x, y, q + // from https://stackoverflow.com/a/2745086/2013738. We know `x + y` CAN overflow. + let x = self.0; + let y = Self::DECIMAL_FRACTIONAL; + if x.is_zero() { + Int256::zero() + } else { + Int256::one() + ((x - Int256::one()) / y) + } + } + } +} + +impl Fraction for SignedDecimal256 { + #[inline] + fn numerator(&self) -> Int256 { + self.0 + } + + #[inline] + fn denominator(&self) -> Int256 { + Self::DECIMAL_FRACTIONAL + } + + /// Returns the multiplicative inverse `1/d` for decimal `d`. + /// + /// If `d` is zero, none is returned. + fn inv(&self) -> Option { + if self.is_zero() { + None + } else { + // Let self be p/q with p = self.0 and q = DECIMAL_FRACTIONAL. + // Now we calculate the inverse a/b = q/p such that b = DECIMAL_FRACTIONAL. Then + // `a = DECIMAL_FRACTIONAL*DECIMAL_FRACTIONAL / self.0`. + Some(SignedDecimal256(Self::DECIMAL_FRACTIONAL_SQUARED / self.0)) + } + } +} + +impl Neg for SignedDecimal256 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl From for SignedDecimal256 { + fn from(value: SignedDecimal) -> Self { + Self::new(value.atomics().into()) + } +} + +impl From for SignedDecimal256 { + fn from(value: Decimal) -> Self { + Self::new(value.atomics().into()) + } +} + +impl TryFrom for SignedDecimal256 { + type Error = SignedDecimal256RangeExceeded; + + fn try_from(value: Decimal256) -> Result { + value + .atomics() + .try_into() + .map(SignedDecimal256) + .map_err(|_| SignedDecimal256RangeExceeded) + } +} + +impl FromStr for SignedDecimal256 { + type Err = StdError; + + /// Converts the decimal string to a SignedDecimal256 + /// Possible inputs: "1.23", "1", "000012", "1.123000000", "-1.12300" + /// Disallowed: "", ".23" + /// + /// This never performs any kind of rounding. + /// More than DECIMAL_PLACES fractional digits, even zeros, result in an error. + fn from_str(input: &str) -> Result { + let mut parts_iter = input.split('.'); + + let whole_part = parts_iter.next().unwrap(); // split always returns at least one element + let is_neg = whole_part.starts_with('-'); + + let whole = whole_part + .parse::() + .map_err(|_| StdError::generic_err("Error parsing whole"))?; + let mut atomics = whole + .checked_mul(Self::DECIMAL_FRACTIONAL) + .map_err(|_| StdError::generic_err("Value too big"))?; + + if let Some(fractional_part) = parts_iter.next() { + let fractional = fractional_part + .parse::() // u64 is enough for 18 decimal places + .map_err(|_| StdError::generic_err("Error parsing fractional"))?; + let exp = (Self::DECIMAL_PLACES.checked_sub(fractional_part.len() as u32)).ok_or_else( + || { + StdError::generic_err(format!( + "Cannot parse more than {} fractional digits", + Self::DECIMAL_PLACES + )) + }, + )?; + debug_assert!(exp <= Self::DECIMAL_PLACES); + let fractional_factor = Int256::from(10i128.pow(exp)); + + // This multiplication can't overflow because + // fractional < 10^DECIMAL_PLACES && fractional_factor <= 10^DECIMAL_PLACES + let fractional_part = Int256::from(fractional) + .checked_mul(fractional_factor) + .unwrap(); + + // for negative numbers, we need to subtract the fractional part + atomics = if is_neg { + atomics.checked_sub(fractional_part) + } else { + atomics.checked_add(fractional_part) + } + .map_err(|_| StdError::generic_err("Value too big"))?; + } + + if parts_iter.next().is_some() { + return Err(StdError::generic_err("Unexpected number of dots")); + } + + Ok(SignedDecimal256(atomics)) + } +} + +impl fmt::Display for SignedDecimal256 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let whole = (self.0) / Self::DECIMAL_FRACTIONAL; + let fractional = (self.0).checked_rem(Self::DECIMAL_FRACTIONAL).unwrap(); + + if fractional.is_zero() { + write!(f, "{whole}") + } else { + let fractional_string = format!( + "{:0>padding$}", + fractional.abs(), // fractional should always be printed as positive + padding = Self::DECIMAL_PLACES as usize + ); + if self.is_negative() { + f.write_char('-')?; + } + write!( + f, + "{whole}.{fractional}", + whole = whole.abs(), + fractional = fractional_string.trim_end_matches('0') + ) + } + } +} + +impl fmt::Debug for SignedDecimal256 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SignedDecimal256({self})") + } +} + +impl Add for SignedDecimal256 { + type Output = Self; + + fn add(self, other: Self) -> Self { + SignedDecimal256(self.0 + other.0) + } +} +forward_ref_binop!(impl Add, add for SignedDecimal256, SignedDecimal256); + +impl AddAssign for SignedDecimal256 { + fn add_assign(&mut self, rhs: SignedDecimal256) { + *self = *self + rhs; + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for SignedDecimal256, SignedDecimal256); + +impl Sub for SignedDecimal256 { + type Output = Self; + + fn sub(self, other: Self) -> Self { + SignedDecimal256(self.0 - other.0) + } +} +forward_ref_binop!(impl Sub, sub for SignedDecimal256, SignedDecimal256); + +impl SubAssign for SignedDecimal256 { + fn sub_assign(&mut self, rhs: SignedDecimal256) { + *self = *self - rhs; + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for SignedDecimal256, SignedDecimal256); + +impl Mul for SignedDecimal256 { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn mul(self, other: Self) -> Self { + // SignedDecimal256s are fractions. We can multiply two decimals a and b + // via + // (a.numerator() * b.numerator()) / (a.denominator() * b.denominator()) + // = (a.numerator() * b.numerator()) / a.denominator() / b.denominator() + + let result_as_int512 = + self.numerator().full_mul(other.numerator()) / Int512::from(Self::DECIMAL_FRACTIONAL); + match result_as_int512.try_into() { + Ok(result) => Self(result), + Err(_) => panic!("attempt to multiply with overflow"), + } + } +} +forward_ref_binop!(impl Mul, mul for SignedDecimal256, SignedDecimal256); + +impl MulAssign for SignedDecimal256 { + fn mul_assign(&mut self, rhs: SignedDecimal256) { + *self = *self * rhs; + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for SignedDecimal256, SignedDecimal256); + +impl Div for SignedDecimal256 { + type Output = Self; + + fn div(self, other: Self) -> Self { + match SignedDecimal256::checked_from_ratio(self.numerator(), other.numerator()) { + Ok(ratio) => ratio, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Division failed - denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => { + panic!("Division failed - multiplication overflow") + } + } + } +} +forward_ref_binop!(impl Div, div for SignedDecimal256, SignedDecimal256); + +impl DivAssign for SignedDecimal256 { + fn div_assign(&mut self, rhs: SignedDecimal256) { + *self = *self / rhs; + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for SignedDecimal256, SignedDecimal256); + +impl Div for SignedDecimal256 { + type Output = Self; + + fn div(self, rhs: Int256) -> Self::Output { + SignedDecimal256(self.0 / rhs) + } +} + +impl DivAssign for SignedDecimal256 { + fn div_assign(&mut self, rhs: Int256) { + self.0 /= rhs; + } +} + +impl Rem for SignedDecimal256 { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for SignedDecimal256, SignedDecimal256); + +impl RemAssign for SignedDecimal256 { + fn rem_assign(&mut self, rhs: SignedDecimal256) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for SignedDecimal256, SignedDecimal256); + +impl core::iter::Sum for SignedDecimal256 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +/// Serializes as a decimal string +impl Serialize for SignedDecimal256 { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +/// Deserializes as a base64 string +impl<'de> Deserialize<'de> for SignedDecimal256 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(SignedDecimal256Visitor) + } +} + +struct SignedDecimal256Visitor; + +impl<'de> de::Visitor<'de> for SignedDecimal256Visitor { + type Value = SignedDecimal256; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded decimal") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + match SignedDecimal256::from_str(v) { + Ok(d) => Ok(d), + Err(e) => Err(E::custom(format!("Error parsing decimal '{v}': {e}"))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_json, to_json_vec}; + use schemars::schema_for; + + fn dec(input: &str) -> SignedDecimal256 { + SignedDecimal256::from_str(input).unwrap() + } + + #[test] + fn signed_decimal_256_new() { + let expected = Int256::from(300i128); + assert_eq!(SignedDecimal256::new(expected).0, expected); + + let expected = Int256::from(-300i128); + assert_eq!(SignedDecimal256::new(expected).0, expected); + } + + #[test] + fn signed_decimal_256_raw() { + let value = 300i128; + assert_eq!(SignedDecimal256::raw(value).0, Int256::from(value)); + + let value = -300i128; + assert_eq!(SignedDecimal256::raw(value).0, Int256::from(value)); + } + + #[test] + fn signed_decimal_256_one() { + let value = SignedDecimal256::one(); + assert_eq!(value.0, SignedDecimal256::DECIMAL_FRACTIONAL); + } + + #[test] + fn signed_decimal_256_zero() { + let value = SignedDecimal256::zero(); + assert!(value.0.is_zero()); + } + + #[test] + fn signed_decimal_256_percent() { + let value = SignedDecimal256::percent(50); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(2u8) + ); + + let value = SignedDecimal256::percent(-50); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(-2i8) + ); + } + + #[test] + fn signed_decimal_256_permille() { + let value = SignedDecimal256::permille(125); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(8u8) + ); + + let value = SignedDecimal256::permille(-125); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(-8i8) + ); + } + + #[test] + fn signed_decimal_256_bps() { + let value = SignedDecimal256::bps(125); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(80u8) + ); + + let value = SignedDecimal256::bps(-125); + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(-80i8) + ); + } + + #[test] + fn signed_decimal_256_from_atomics_works() { + let one = SignedDecimal256::one(); + let two = one + one; + let neg_one = SignedDecimal256::negative_one(); + + assert_eq!(SignedDecimal256::from_atomics(1i128, 0).unwrap(), one); + assert_eq!(SignedDecimal256::from_atomics(10i128, 1).unwrap(), one); + assert_eq!(SignedDecimal256::from_atomics(100i128, 2).unwrap(), one); + assert_eq!(SignedDecimal256::from_atomics(1000i128, 3).unwrap(), one); + assert_eq!( + SignedDecimal256::from_atomics(1000000000000000000i128, 18).unwrap(), + one + ); + assert_eq!( + SignedDecimal256::from_atomics(10000000000000000000i128, 19).unwrap(), + one + ); + assert_eq!( + SignedDecimal256::from_atomics(100000000000000000000i128, 20).unwrap(), + one + ); + + assert_eq!(SignedDecimal256::from_atomics(2i128, 0).unwrap(), two); + assert_eq!(SignedDecimal256::from_atomics(20i128, 1).unwrap(), two); + assert_eq!(SignedDecimal256::from_atomics(200i128, 2).unwrap(), two); + assert_eq!(SignedDecimal256::from_atomics(2000i128, 3).unwrap(), two); + assert_eq!( + SignedDecimal256::from_atomics(2000000000000000000i128, 18).unwrap(), + two + ); + assert_eq!( + SignedDecimal256::from_atomics(20000000000000000000i128, 19).unwrap(), + two + ); + assert_eq!( + SignedDecimal256::from_atomics(200000000000000000000i128, 20).unwrap(), + two + ); + + assert_eq!(SignedDecimal256::from_atomics(-1i128, 0).unwrap(), neg_one); + assert_eq!(SignedDecimal256::from_atomics(-10i128, 1).unwrap(), neg_one); + assert_eq!( + SignedDecimal256::from_atomics(-100000000000000000000i128, 20).unwrap(), + neg_one + ); + + // Cuts decimal digits (20 provided but only 18 can be stored) + assert_eq!( + SignedDecimal256::from_atomics(4321i128, 20).unwrap(), + SignedDecimal256::from_str("0.000000000000000043").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(-4321i128, 20).unwrap(), + SignedDecimal256::from_str("-0.000000000000000043").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(6789i128, 20).unwrap(), + SignedDecimal256::from_str("0.000000000000000067").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 38).unwrap(), + SignedDecimal256::from_str("1.701411834604692317").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 39).unwrap(), + SignedDecimal256::from_str("0.170141183460469231").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 45).unwrap(), + SignedDecimal256::from_str("0.000000170141183460").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 51).unwrap(), + SignedDecimal256::from_str("0.000000000000170141").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 56).unwrap(), + SignedDecimal256::from_str("0.000000000000000001").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, 57).unwrap(), + SignedDecimal256::from_str("0.000000000000000000").unwrap() + ); + assert_eq!( + SignedDecimal256::from_atomics(i128::MAX, u32::MAX).unwrap(), + SignedDecimal256::from_str("0.000000000000000000").unwrap() + ); + + // Can be used with max value + let max = SignedDecimal256::MAX; + assert_eq!( + SignedDecimal256::from_atomics(max.atomics(), max.decimal_places()).unwrap(), + max + ); + + // Can be used with min value + let min = SignedDecimal256::MIN; + assert_eq!( + SignedDecimal256::from_atomics(min.atomics(), min.decimal_places()).unwrap(), + min + ); + + // Overflow is only possible with digits < 18 + let result = SignedDecimal256::from_atomics(Int256::MAX, 17); + assert_eq!(result.unwrap_err(), SignedDecimal256RangeExceeded); + } + + #[test] + fn signed_decimal_256_from_ratio_works() { + // 1.0 + assert_eq!( + SignedDecimal256::from_ratio(1i128, 1i128), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::from_ratio(53i128, 53i128), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::from_ratio(125i128, 125i128), + SignedDecimal256::one() + ); + + // -1.0 + assert_eq!( + SignedDecimal256::from_ratio(-1i128, 1i128), + SignedDecimal256::negative_one() + ); + assert_eq!( + SignedDecimal256::from_ratio(-53i128, 53i128), + SignedDecimal256::negative_one() + ); + assert_eq!( + SignedDecimal256::from_ratio(125i128, -125i128), + SignedDecimal256::negative_one() + ); + + // 1.5 + assert_eq!( + SignedDecimal256::from_ratio(3i128, 2i128), + SignedDecimal256::percent(150) + ); + assert_eq!( + SignedDecimal256::from_ratio(150i128, 100i128), + SignedDecimal256::percent(150) + ); + assert_eq!( + SignedDecimal256::from_ratio(333i128, 222i128), + SignedDecimal256::percent(150) + ); + + // 0.125 + assert_eq!( + SignedDecimal256::from_ratio(1i64, 8i64), + SignedDecimal256::permille(125) + ); + assert_eq!( + SignedDecimal256::from_ratio(125i64, 1000i64), + SignedDecimal256::permille(125) + ); + + // -0.125 + assert_eq!( + SignedDecimal256::from_ratio(-1i64, 8i64), + SignedDecimal256::permille(-125) + ); + assert_eq!( + SignedDecimal256::from_ratio(125i64, -1000i64), + SignedDecimal256::permille(-125) + ); + + // 1/3 (result floored) + assert_eq!( + SignedDecimal256::from_ratio(1i64, 3i64), + SignedDecimal256(Int256::from(333_333_333_333_333_333i128)) + ); + + // 2/3 (result floored) + assert_eq!( + SignedDecimal256::from_ratio(2i64, 3i64), + SignedDecimal256(Int256::from(666_666_666_666_666_666i128)) + ); + + // large inputs + assert_eq!( + SignedDecimal256::from_ratio(0i128, i128::MAX), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::from_ratio(i128::MAX, i128::MAX), + SignedDecimal256::one() + ); + // 170141183460469231731 is the largest integer <= SignedDecimal256::MAX + assert_eq!( + SignedDecimal256::from_ratio(170141183460469231731i128, 1i128), + SignedDecimal256::from_str("170141183460469231731").unwrap() + ); + } + + #[test] + #[should_panic(expected = "Denominator must not be zero")] + fn signed_decimal_256_from_ratio_panics_for_zero_denominator() { + SignedDecimal256::from_ratio(1i128, 0i128); + } + + #[test] + #[should_panic(expected = "Multiplication overflow")] + fn signed_decimal_256_from_ratio_panics_for_mul_overflow() { + SignedDecimal256::from_ratio(Int256::MAX, 1i128); + } + + #[test] + fn signed_decimal_256_checked_from_ratio_does_not_panic() { + assert_eq!( + SignedDecimal256::checked_from_ratio(1i128, 0i128), + Err(CheckedFromRatioError::DivideByZero) + ); + + assert_eq!( + SignedDecimal256::checked_from_ratio(Int256::MAX, 1i128), + Err(CheckedFromRatioError::Overflow) + ); + } + + #[test] + fn signed_decimal_256_implements_fraction() { + let fraction = SignedDecimal256::from_str("1234.567").unwrap(); + assert_eq!( + fraction.numerator(), + Int256::from(1_234_567_000_000_000_000_000i128) + ); + assert_eq!( + fraction.denominator(), + Int256::from(1_000_000_000_000_000_000i128) + ); + + let fraction = SignedDecimal256::from_str("-1234.567").unwrap(); + assert_eq!( + fraction.numerator(), + Int256::from(-1_234_567_000_000_000_000_000i128) + ); + assert_eq!( + fraction.denominator(), + Int256::from(1_000_000_000_000_000_000i128) + ); + } + + #[test] + fn signed_decimal_256_from_str_works() { + // Integers + assert_eq!( + SignedDecimal256::from_str("0").unwrap(), + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::from_str("1").unwrap(), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::from_str("5").unwrap(), + SignedDecimal256::percent(500) + ); + assert_eq!( + SignedDecimal256::from_str("42").unwrap(), + SignedDecimal256::percent(4200) + ); + assert_eq!( + SignedDecimal256::from_str("000").unwrap(), + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::from_str("001").unwrap(), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::from_str("005").unwrap(), + SignedDecimal256::percent(500) + ); + assert_eq!( + SignedDecimal256::from_str("0042").unwrap(), + SignedDecimal256::percent(4200) + ); + + // Positive decimals + assert_eq!( + SignedDecimal256::from_str("1.0").unwrap(), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::from_str("1.5").unwrap(), + SignedDecimal256::percent(150) + ); + assert_eq!( + SignedDecimal256::from_str("0.5").unwrap(), + SignedDecimal256::percent(50) + ); + assert_eq!( + SignedDecimal256::from_str("0.123").unwrap(), + SignedDecimal256::permille(123) + ); + + assert_eq!( + SignedDecimal256::from_str("40.00").unwrap(), + SignedDecimal256::percent(4000) + ); + assert_eq!( + SignedDecimal256::from_str("04.00").unwrap(), + SignedDecimal256::percent(400) + ); + assert_eq!( + SignedDecimal256::from_str("00.40").unwrap(), + SignedDecimal256::percent(40) + ); + assert_eq!( + SignedDecimal256::from_str("00.04").unwrap(), + SignedDecimal256::percent(4) + ); + // Negative decimals + assert_eq!( + SignedDecimal256::from_str("-00.04").unwrap(), + SignedDecimal256::percent(-4) + ); + assert_eq!( + SignedDecimal256::from_str("-00.40").unwrap(), + SignedDecimal256::percent(-40) + ); + assert_eq!( + SignedDecimal256::from_str("-04.00").unwrap(), + SignedDecimal256::percent(-400) + ); + + // Can handle DECIMAL_PLACES fractional digits + assert_eq!( + SignedDecimal256::from_str("7.123456789012345678").unwrap(), + SignedDecimal256(Int256::from(7123456789012345678i128)) + ); + assert_eq!( + SignedDecimal256::from_str("7.999999999999999999").unwrap(), + SignedDecimal256(Int256::from(7999999999999999999i128)) + ); + + // Works for documented max value + assert_eq!( + SignedDecimal256::from_str( + "57896044618658097711785492504343953926634992332820282019728.792003956564819967" + ) + .unwrap(), + SignedDecimal256::MAX + ); + // Works for documented min value + assert_eq!( + SignedDecimal256::from_str( + "-57896044618658097711785492504343953926634992332820282019728.792003956564819968" + ) + .unwrap(), + SignedDecimal256::MIN + ); + assert_eq!( + SignedDecimal256::from_str("-1").unwrap(), + SignedDecimal256::negative_one() + ); + } + + #[test] + fn signed_decimal_256_from_str_errors_for_broken_whole_part() { + let expected_err = StdError::generic_err("Error parsing whole"); + assert_eq!(SignedDecimal256::from_str("").unwrap_err(), expected_err); + assert_eq!(SignedDecimal256::from_str(" ").unwrap_err(), expected_err); + assert_eq!(SignedDecimal256::from_str("-").unwrap_err(), expected_err); + } + + #[test] + fn signed_decimal_256_from_str_errors_for_broken_fractional_part() { + let expected_err = StdError::generic_err("Error parsing fractional"); + assert_eq!(SignedDecimal256::from_str("1.").unwrap_err(), expected_err); + assert_eq!(SignedDecimal256::from_str("1. ").unwrap_err(), expected_err); + assert_eq!(SignedDecimal256::from_str("1.e").unwrap_err(), expected_err); + assert_eq!( + SignedDecimal256::from_str("1.2e3").unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal256::from_str("1.-2").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_256_from_str_errors_for_more_than_18_fractional_digits() { + let expected_err = StdError::generic_err("Cannot parse more than 18 fractional digits"); + assert_eq!( + SignedDecimal256::from_str("7.1234567890123456789").unwrap_err(), + expected_err + ); + // No special rules for trailing zeros. This could be changed but adds gas cost for the happy path. + assert_eq!( + SignedDecimal256::from_str("7.1230000000000000000").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_256_from_str_errors_for_invalid_number_of_dots() { + let expected_err = StdError::generic_err("Unexpected number of dots"); + assert_eq!( + SignedDecimal256::from_str("1.2.3").unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal256::from_str("1.2.3.4").unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_256_from_str_errors_for_more_than_max_value() { + let expected_err = StdError::generic_err("Value too big"); + // Integer + assert_eq!( + SignedDecimal256::from_str( + "57896044618658097711785492504343953926634992332820282019729", + ) + .unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal256::from_str( + "-57896044618658097711785492504343953926634992332820282019729", + ) + .unwrap_err(), + expected_err + ); + + // SignedDecimal256 + assert_eq!( + SignedDecimal256::from_str( + "57896044618658097711785492504343953926634992332820282019729.0", + ) + .unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal256::from_str( + "57896044618658097711785492504343953926634992332820282019728.792003956564819968", + ) + .unwrap_err(), + expected_err + ); + assert_eq!( + SignedDecimal256::from_str( + "-57896044618658097711785492504343953926634992332820282019728.792003956564819969", + ) + .unwrap_err(), + expected_err + ); + } + + #[test] + fn signed_decimal_256_conversions_work() { + assert_eq!( + SignedDecimal256::from(SignedDecimal::zero()), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::from(SignedDecimal::one()), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::from(SignedDecimal::percent(50)), + SignedDecimal256::percent(50) + ); + assert_eq!( + SignedDecimal256::from(SignedDecimal::MAX), + SignedDecimal256::new(Int256::from_i128(i128::MAX)) + ); + assert_eq!( + SignedDecimal256::from(SignedDecimal::percent(-50)), + SignedDecimal256::percent(-50) + ); + assert_eq!( + SignedDecimal256::from(SignedDecimal::MIN), + SignedDecimal256::new(Int256::from_i128(i128::MIN)) + ); + } + + #[test] + fn signed_decimal_256_atomics_works() { + let zero = SignedDecimal256::zero(); + let one = SignedDecimal256::one(); + let half = SignedDecimal256::percent(50); + let two = SignedDecimal256::percent(200); + let max = SignedDecimal256::MAX; + let neg_half = SignedDecimal256::percent(-50); + let neg_two = SignedDecimal256::percent(-200); + let min = SignedDecimal256::MIN; + + assert_eq!(zero.atomics(), Int256::from(0)); + assert_eq!(one.atomics(), Int256::from(1000000000000000000i128)); + assert_eq!(half.atomics(), Int256::from(500000000000000000i128)); + assert_eq!(two.atomics(), Int256::from(2000000000000000000i128)); + assert_eq!(max.atomics(), Int256::MAX); + assert_eq!(neg_half.atomics(), Int256::from(-500000000000000000i128)); + assert_eq!(neg_two.atomics(), Int256::from(-2000000000000000000i128)); + assert_eq!(min.atomics(), Int256::MIN); + } + + #[test] + fn signed_decimal_256_decimal_places_works() { + let zero = SignedDecimal256::zero(); + let one = SignedDecimal256::one(); + let half = SignedDecimal256::percent(50); + let two = SignedDecimal256::percent(200); + let max = SignedDecimal256::MAX; + let neg_one = SignedDecimal256::negative_one(); + + assert_eq!(zero.decimal_places(), 18); + assert_eq!(one.decimal_places(), 18); + assert_eq!(half.decimal_places(), 18); + assert_eq!(two.decimal_places(), 18); + assert_eq!(max.decimal_places(), 18); + assert_eq!(neg_one.decimal_places(), 18); + } + + #[test] + fn signed_decimal_256_is_zero_works() { + assert!(SignedDecimal256::zero().is_zero()); + assert!(SignedDecimal256::percent(0).is_zero()); + assert!(SignedDecimal256::permille(0).is_zero()); + + assert!(!SignedDecimal256::one().is_zero()); + assert!(!SignedDecimal256::percent(123).is_zero()); + assert!(!SignedDecimal256::permille(-1234).is_zero()); + } + + #[test] + fn signed_decimal_256_inv_works() { + // d = 0 + assert_eq!(SignedDecimal256::zero().inv(), None); + + // d == 1 + assert_eq!(SignedDecimal256::one().inv(), Some(SignedDecimal256::one())); + + // d == -1 + assert_eq!( + SignedDecimal256::negative_one().inv(), + Some(SignedDecimal256::negative_one()) + ); + + // d > 1 exact + assert_eq!( + SignedDecimal256::from_str("2").unwrap().inv(), + Some(SignedDecimal256::from_str("0.5").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("20").unwrap().inv(), + Some(SignedDecimal256::from_str("0.05").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("200").unwrap().inv(), + Some(SignedDecimal256::from_str("0.005").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("2000").unwrap().inv(), + Some(SignedDecimal256::from_str("0.0005").unwrap()) + ); + + // d > 1 rounded + assert_eq!( + SignedDecimal256::from_str("3").unwrap().inv(), + Some(SignedDecimal256::from_str("0.333333333333333333").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("6").unwrap().inv(), + Some(SignedDecimal256::from_str("0.166666666666666666").unwrap()) + ); + + // d < 1 exact + assert_eq!( + SignedDecimal256::from_str("0.5").unwrap().inv(), + Some(SignedDecimal256::from_str("2").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("0.05").unwrap().inv(), + Some(SignedDecimal256::from_str("20").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("0.005").unwrap().inv(), + Some(SignedDecimal256::from_str("200").unwrap()) + ); + assert_eq!( + SignedDecimal256::from_str("0.0005").unwrap().inv(), + Some(SignedDecimal256::from_str("2000").unwrap()) + ); + + // d < 0 + assert_eq!( + SignedDecimal256::from_str("-0.5").unwrap().inv(), + Some(SignedDecimal256::from_str("-2").unwrap()) + ); + // d < 0 rounded + assert_eq!( + SignedDecimal256::from_str("-3").unwrap().inv(), + Some(SignedDecimal256::from_str("-0.333333333333333333").unwrap()) + ); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_256_add_works() { + let value = SignedDecimal256::one() + SignedDecimal256::percent(50); // 1.5 + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL * Int256::from(3u8) / Int256::from(2u8) + ); + + assert_eq!( + SignedDecimal256::percent(5) + SignedDecimal256::percent(4), + SignedDecimal256::percent(9) + ); + assert_eq!( + SignedDecimal256::percent(5) + SignedDecimal256::zero(), + SignedDecimal256::percent(5) + ); + assert_eq!( + SignedDecimal256::zero() + SignedDecimal256::zero(), + SignedDecimal256::zero() + ); + // negative numbers + assert_eq!( + SignedDecimal256::percent(-5) + SignedDecimal256::percent(-4), + SignedDecimal256::percent(-9) + ); + assert_eq!( + SignedDecimal256::percent(-5) + SignedDecimal256::percent(4), + SignedDecimal256::percent(-1) + ); + assert_eq!( + SignedDecimal256::percent(5) + SignedDecimal256::percent(-4), + SignedDecimal256::percent(1) + ); + + // works for refs + let a = SignedDecimal256::percent(15); + let b = SignedDecimal256::percent(25); + let expected = SignedDecimal256::percent(40); + assert_eq!(a + b, expected); + assert_eq!(&a + b, expected); + assert_eq!(a + &b, expected); + assert_eq!(&a + &b, expected); + } + + #[test] + #[should_panic] + fn signed_decimal_256_add_overflow_panics() { + let _value = SignedDecimal256::MAX + SignedDecimal256::percent(50); + } + + #[test] + fn signed_decimal_256_add_assign_works() { + let mut a = SignedDecimal256::percent(30); + a += SignedDecimal256::percent(20); + assert_eq!(a, SignedDecimal256::percent(50)); + + // works for refs + let mut a = SignedDecimal256::percent(15); + let b = SignedDecimal256::percent(3); + let expected = SignedDecimal256::percent(18); + a += &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_256_sub_works() { + let value = SignedDecimal256::one() - SignedDecimal256::percent(50); // 0.5 + assert_eq!( + value.0, + SignedDecimal256::DECIMAL_FRACTIONAL / Int256::from(2u8) + ); + + assert_eq!( + SignedDecimal256::percent(9) - SignedDecimal256::percent(4), + SignedDecimal256::percent(5) + ); + assert_eq!( + SignedDecimal256::percent(16) - SignedDecimal256::zero(), + SignedDecimal256::percent(16) + ); + assert_eq!( + SignedDecimal256::percent(16) - SignedDecimal256::percent(16), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::zero() - SignedDecimal256::zero(), + SignedDecimal256::zero() + ); + + // negative numbers + assert_eq!( + SignedDecimal256::percent(-5) - SignedDecimal256::percent(-4), + SignedDecimal256::percent(-1) + ); + assert_eq!( + SignedDecimal256::percent(-5) - SignedDecimal256::percent(4), + SignedDecimal256::percent(-9) + ); + assert_eq!( + SignedDecimal256::percent(500) - SignedDecimal256::percent(-4), + SignedDecimal256::percent(504) + ); + + // works for refs + let a = SignedDecimal256::percent(13); + let b = SignedDecimal256::percent(6); + let expected = SignedDecimal256::percent(7); + assert_eq!(a - b, expected); + assert_eq!(&a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn signed_decimal_256_sub_overflow_panics() { + let _value = SignedDecimal256::MIN - SignedDecimal256::percent(50); + } + + #[test] + fn signed_decimal_256_sub_assign_works() { + let mut a = SignedDecimal256::percent(20); + a -= SignedDecimal256::percent(2); + assert_eq!(a, SignedDecimal256::percent(18)); + + // works for refs + let mut a = SignedDecimal256::percent(33); + let b = SignedDecimal256::percent(13); + let expected = SignedDecimal256::percent(20); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_256_implements_mul() { + let one = SignedDecimal256::one(); + let two = one + one; + let half = SignedDecimal256::percent(50); + + // 1*x and x*1 + assert_eq!( + one * SignedDecimal256::percent(0), + SignedDecimal256::percent(0) + ); + assert_eq!( + one * SignedDecimal256::percent(1), + SignedDecimal256::percent(1) + ); + assert_eq!( + one * SignedDecimal256::percent(10), + SignedDecimal256::percent(10) + ); + assert_eq!( + one * SignedDecimal256::percent(100), + SignedDecimal256::percent(100) + ); + assert_eq!( + one * SignedDecimal256::percent(1000), + SignedDecimal256::percent(1000) + ); + assert_eq!(one * SignedDecimal256::MAX, SignedDecimal256::MAX); + assert_eq!( + SignedDecimal256::percent(0) * one, + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::percent(1) * one, + SignedDecimal256::percent(1) + ); + assert_eq!( + SignedDecimal256::percent(10) * one, + SignedDecimal256::percent(10) + ); + assert_eq!( + SignedDecimal256::percent(100) * one, + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::percent(1000) * one, + SignedDecimal256::percent(1000) + ); + assert_eq!(SignedDecimal256::MAX * one, SignedDecimal256::MAX); + assert_eq!( + SignedDecimal256::percent(-1) * one, + SignedDecimal256::percent(-1) + ); + assert_eq!( + one * SignedDecimal256::percent(-10), + SignedDecimal256::percent(-10) + ); + + // double + assert_eq!( + two * SignedDecimal256::percent(0), + SignedDecimal256::percent(0) + ); + assert_eq!( + two * SignedDecimal256::percent(1), + SignedDecimal256::percent(2) + ); + assert_eq!( + two * SignedDecimal256::percent(10), + SignedDecimal256::percent(20) + ); + assert_eq!( + two * SignedDecimal256::percent(100), + SignedDecimal256::percent(200) + ); + assert_eq!( + two * SignedDecimal256::percent(1000), + SignedDecimal256::percent(2000) + ); + assert_eq!( + SignedDecimal256::percent(0) * two, + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::percent(1) * two, + SignedDecimal256::percent(2) + ); + assert_eq!( + SignedDecimal256::percent(10) * two, + SignedDecimal256::percent(20) + ); + assert_eq!( + SignedDecimal256::percent(100) * two, + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(1000) * two, + SignedDecimal256::percent(2000) + ); + assert_eq!( + SignedDecimal256::percent(-1) * two, + SignedDecimal256::percent(-2) + ); + assert_eq!( + two * SignedDecimal256::new(Int256::MIN / Int256::from(2)), + SignedDecimal256::MIN + ); + + // half + assert_eq!( + half * SignedDecimal256::percent(0), + SignedDecimal256::percent(0) + ); + assert_eq!( + half * SignedDecimal256::percent(1), + SignedDecimal256::permille(5) + ); + assert_eq!( + half * SignedDecimal256::percent(10), + SignedDecimal256::percent(5) + ); + assert_eq!( + half * SignedDecimal256::percent(100), + SignedDecimal256::percent(50) + ); + assert_eq!( + half * SignedDecimal256::percent(1000), + SignedDecimal256::percent(500) + ); + assert_eq!( + SignedDecimal256::percent(0) * half, + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::percent(1) * half, + SignedDecimal256::permille(5) + ); + assert_eq!( + SignedDecimal256::percent(10) * half, + SignedDecimal256::percent(5) + ); + assert_eq!( + SignedDecimal256::percent(100) * half, + SignedDecimal256::percent(50) + ); + assert_eq!( + SignedDecimal256::percent(1000) * half, + SignedDecimal256::percent(500) + ); + + // Move left + let a = dec("123.127726548762582"); + assert_eq!(a * dec("1"), dec("123.127726548762582")); + assert_eq!(a * dec("10"), dec("1231.27726548762582")); + assert_eq!(a * dec("100"), dec("12312.7726548762582")); + assert_eq!(a * dec("1000"), dec("123127.726548762582")); + assert_eq!(a * dec("1000000"), dec("123127726.548762582")); + assert_eq!(a * dec("1000000000"), dec("123127726548.762582")); + assert_eq!(a * dec("1000000000000"), dec("123127726548762.582")); + assert_eq!(a * dec("1000000000000000"), dec("123127726548762582")); + assert_eq!(a * dec("1000000000000000000"), dec("123127726548762582000")); + assert_eq!(dec("1") * a, dec("123.127726548762582")); + assert_eq!(dec("10") * a, dec("1231.27726548762582")); + assert_eq!(dec("100") * a, dec("12312.7726548762582")); + assert_eq!(dec("1000") * a, dec("123127.726548762582")); + assert_eq!(dec("1000000") * a, dec("123127726.548762582")); + assert_eq!(dec("1000000000") * a, dec("123127726548.762582")); + assert_eq!(dec("1000000000000") * a, dec("123127726548762.582")); + assert_eq!(dec("1000000000000000") * a, dec("123127726548762582")); + assert_eq!(dec("1000000000000000000") * a, dec("123127726548762582000")); + assert_eq!( + dec("-1000000000000000000") * a, + dec("-123127726548762582000") + ); + + // Move right + let max = SignedDecimal256::MAX; + assert_eq!( + max * dec("1.0"), + dec("57896044618658097711785492504343953926634992332820282019728.792003956564819967") + ); + assert_eq!( + max * dec("0.1"), + dec("5789604461865809771178549250434395392663499233282028201972.879200395656481996") + ); + assert_eq!( + max * dec("0.01"), + dec("578960446186580977117854925043439539266349923328202820197.287920039565648199") + ); + assert_eq!( + max * dec("0.001"), + dec("57896044618658097711785492504343953926634992332820282019.728792003956564819") + ); + assert_eq!( + max * dec("0.000001"), + dec("57896044618658097711785492504343953926634992332820282.019728792003956564") + ); + assert_eq!( + max * dec("0.000000001"), + dec("57896044618658097711785492504343953926634992332820.282019728792003956") + ); + assert_eq!( + max * dec("0.000000000001"), + dec("57896044618658097711785492504343953926634992332.820282019728792003") + ); + assert_eq!( + max * dec("0.000000000000001"), + dec("57896044618658097711785492504343953926634992.332820282019728792") + ); + assert_eq!( + max * dec("0.000000000000000001"), + dec("57896044618658097711785492504343953926634.992332820282019728") + ); + + // works for refs + let a = SignedDecimal256::percent(20); + let b = SignedDecimal256::percent(30); + let expected = SignedDecimal256::percent(6); + assert_eq!(a * b, expected); + assert_eq!(&a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn signed_decimal_256_mul_assign_works() { + let mut a = SignedDecimal256::percent(15); + a *= SignedDecimal256::percent(60); + assert_eq!(a, SignedDecimal256::percent(9)); + + // works for refs + let mut a = SignedDecimal256::percent(50); + let b = SignedDecimal256::percent(20); + a *= &b; + assert_eq!(a, SignedDecimal256::percent(10)); + } + + #[test] + #[should_panic(expected = "attempt to multiply with overflow")] + fn signed_decimal_256_mul_overflow_panics() { + let _value = SignedDecimal256::MAX * SignedDecimal256::percent(101); + } + + #[test] + fn signed_decimal_256_checked_mul() { + let test_data = [ + (SignedDecimal256::zero(), SignedDecimal256::zero()), + (SignedDecimal256::zero(), SignedDecimal256::one()), + (SignedDecimal256::one(), SignedDecimal256::zero()), + (SignedDecimal256::percent(10), SignedDecimal256::zero()), + (SignedDecimal256::percent(10), SignedDecimal256::percent(5)), + (SignedDecimal256::MAX, SignedDecimal256::one()), + ( + SignedDecimal256::MAX / Int256::from(2), + SignedDecimal256::percent(200), + ), + ( + SignedDecimal256::permille(6), + SignedDecimal256::permille(13), + ), + ( + SignedDecimal256::permille(-6), + SignedDecimal256::permille(0), + ), + (SignedDecimal256::MAX, SignedDecimal256::negative_one()), + ]; + + // The regular core::ops::Mul is our source of truth for these tests. + for (x, y) in test_data.into_iter() { + assert_eq!(x * y, x.checked_mul(y).unwrap()); + } + } + + #[test] + fn signed_decimal_256_checked_mul_overflow() { + assert_eq!( + SignedDecimal256::MAX.checked_mul(SignedDecimal256::percent(200)), + Err(OverflowError { + operation: OverflowOperation::Mul, + operand1: SignedDecimal256::MAX.to_string(), + operand2: SignedDecimal256::percent(200).to_string(), + }) + ); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_256_implements_div() { + let one = SignedDecimal256::one(); + let two = one + one; + let half = SignedDecimal256::percent(50); + + // 1/x and x/1 + assert_eq!( + one / SignedDecimal256::percent(1), + SignedDecimal256::percent(10_000) + ); + assert_eq!( + one / SignedDecimal256::percent(10), + SignedDecimal256::percent(1_000) + ); + assert_eq!( + one / SignedDecimal256::percent(100), + SignedDecimal256::percent(100) + ); + assert_eq!( + one / SignedDecimal256::percent(1000), + SignedDecimal256::percent(10) + ); + assert_eq!( + SignedDecimal256::percent(0) / one, + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::percent(1) / one, + SignedDecimal256::percent(1) + ); + assert_eq!( + SignedDecimal256::percent(10) / one, + SignedDecimal256::percent(10) + ); + assert_eq!( + SignedDecimal256::percent(100) / one, + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::percent(1000) / one, + SignedDecimal256::percent(1000) + ); + assert_eq!( + one / SignedDecimal256::percent(-1), + SignedDecimal256::percent(-10_000) + ); + assert_eq!( + one / SignedDecimal256::percent(-10), + SignedDecimal256::percent(-1_000) + ); + + // double + assert_eq!( + two / SignedDecimal256::percent(1), + SignedDecimal256::percent(20_000) + ); + assert_eq!( + two / SignedDecimal256::percent(10), + SignedDecimal256::percent(2_000) + ); + assert_eq!( + two / SignedDecimal256::percent(100), + SignedDecimal256::percent(200) + ); + assert_eq!( + two / SignedDecimal256::percent(1000), + SignedDecimal256::percent(20) + ); + assert_eq!( + SignedDecimal256::percent(0) / two, + SignedDecimal256::percent(0) + ); + assert_eq!(SignedDecimal256::percent(1) / two, dec("0.005")); + assert_eq!( + SignedDecimal256::percent(10) / two, + SignedDecimal256::percent(5) + ); + assert_eq!( + SignedDecimal256::percent(100) / two, + SignedDecimal256::percent(50) + ); + assert_eq!( + SignedDecimal256::percent(1000) / two, + SignedDecimal256::percent(500) + ); + assert_eq!( + two / SignedDecimal256::percent(-1), + SignedDecimal256::percent(-20_000) + ); + assert_eq!( + SignedDecimal256::percent(-10000) / two, + SignedDecimal256::percent(-5000) + ); + + // half + assert_eq!( + half / SignedDecimal256::percent(1), + SignedDecimal256::percent(5_000) + ); + assert_eq!( + half / SignedDecimal256::percent(10), + SignedDecimal256::percent(500) + ); + assert_eq!( + half / SignedDecimal256::percent(100), + SignedDecimal256::percent(50) + ); + assert_eq!( + half / SignedDecimal256::percent(1000), + SignedDecimal256::percent(5) + ); + assert_eq!( + SignedDecimal256::percent(0) / half, + SignedDecimal256::percent(0) + ); + assert_eq!( + SignedDecimal256::percent(1) / half, + SignedDecimal256::percent(2) + ); + assert_eq!( + SignedDecimal256::percent(10) / half, + SignedDecimal256::percent(20) + ); + assert_eq!( + SignedDecimal256::percent(100) / half, + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(1000) / half, + SignedDecimal256::percent(2000) + ); + + // Move right + let a = dec("123127726548762582"); + assert_eq!(a / dec("1"), dec("123127726548762582")); + assert_eq!(a / dec("10"), dec("12312772654876258.2")); + assert_eq!(a / dec("100"), dec("1231277265487625.82")); + assert_eq!(a / dec("1000"), dec("123127726548762.582")); + assert_eq!(a / dec("1000000"), dec("123127726548.762582")); + assert_eq!(a / dec("1000000000"), dec("123127726.548762582")); + assert_eq!(a / dec("1000000000000"), dec("123127.726548762582")); + assert_eq!(a / dec("1000000000000000"), dec("123.127726548762582")); + assert_eq!(a / dec("1000000000000000000"), dec("0.123127726548762582")); + assert_eq!(dec("1") / a, dec("0.000000000000000008")); + assert_eq!(dec("10") / a, dec("0.000000000000000081")); + assert_eq!(dec("100") / a, dec("0.000000000000000812")); + assert_eq!(dec("1000") / a, dec("0.000000000000008121")); + assert_eq!(dec("1000000") / a, dec("0.000000000008121647")); + assert_eq!(dec("1000000000") / a, dec("0.000000008121647560")); + assert_eq!(dec("1000000000000") / a, dec("0.000008121647560868")); + assert_eq!(dec("1000000000000000") / a, dec("0.008121647560868164")); + assert_eq!(dec("1000000000000000000") / a, dec("8.121647560868164773")); + // negative + let a = dec("-123127726548762582"); + assert_eq!(a / dec("1"), dec("-123127726548762582")); + assert_eq!(a / dec("10"), dec("-12312772654876258.2")); + assert_eq!(a / dec("100"), dec("-1231277265487625.82")); + assert_eq!(a / dec("1000"), dec("-123127726548762.582")); + assert_eq!(a / dec("1000000"), dec("-123127726548.762582")); + assert_eq!(a / dec("1000000000"), dec("-123127726.548762582")); + assert_eq!(a / dec("1000000000000"), dec("-123127.726548762582")); + assert_eq!(a / dec("1000000000000000"), dec("-123.127726548762582")); + assert_eq!(a / dec("1000000000000000000"), dec("-0.123127726548762582")); + assert_eq!(dec("1") / a, dec("-0.000000000000000008")); + + // Move left + let a = dec("0.123127726548762582"); + assert_eq!(a / dec("1.0"), dec("0.123127726548762582")); + assert_eq!(a / dec("0.1"), dec("1.23127726548762582")); + assert_eq!(a / dec("0.01"), dec("12.3127726548762582")); + assert_eq!(a / dec("0.001"), dec("123.127726548762582")); + assert_eq!(a / dec("0.000001"), dec("123127.726548762582")); + assert_eq!(a / dec("0.000000001"), dec("123127726.548762582")); + assert_eq!(a / dec("0.000000000001"), dec("123127726548.762582")); + assert_eq!(a / dec("0.000000000000001"), dec("123127726548762.582")); + assert_eq!(a / dec("0.000000000000000001"), dec("123127726548762582")); + // negative + let a = dec("-0.123127726548762582"); + assert_eq!(a / dec("1.0"), dec("-0.123127726548762582")); + assert_eq!(a / dec("0.1"), dec("-1.23127726548762582")); + assert_eq!(a / dec("0.01"), dec("-12.3127726548762582")); + assert_eq!(a / dec("0.001"), dec("-123.127726548762582")); + assert_eq!(a / dec("0.000001"), dec("-123127.726548762582")); + assert_eq!(a / dec("0.000000001"), dec("-123127726.548762582")); + + assert_eq!( + SignedDecimal256::percent(15) / SignedDecimal256::percent(60), + SignedDecimal256::percent(25) + ); + + // works for refs + let a = SignedDecimal256::percent(100); + let b = SignedDecimal256::percent(20); + let expected = SignedDecimal256::percent(500); + assert_eq!(a / b, expected); + assert_eq!(&a / b, expected); + assert_eq!(a / &b, expected); + assert_eq!(&a / &b, expected); + } + + #[test] + fn signed_decimal_256_div_assign_works() { + let mut a = SignedDecimal256::percent(15); + a /= SignedDecimal256::percent(20); + assert_eq!(a, SignedDecimal256::percent(75)); + + // works for refs + let mut a = SignedDecimal256::percent(50); + let b = SignedDecimal256::percent(20); + a /= &b; + assert_eq!(a, SignedDecimal256::percent(250)); + } + + #[test] + #[should_panic(expected = "Division failed - multiplication overflow")] + fn signed_decimal_256_div_overflow_panics() { + let _value = SignedDecimal256::MAX / SignedDecimal256::percent(10); + } + + #[test] + #[should_panic(expected = "Division failed - denominator must not be zero")] + fn signed_decimal_256_div_by_zero_panics() { + let _value = SignedDecimal256::one() / SignedDecimal256::zero(); + } + + #[test] + fn signed_decimal_256_int128_division() { + // a/b + let left = SignedDecimal256::percent(150); // 1.5 + let right = Int256::from(3); + assert_eq!(left / right, SignedDecimal256::percent(50)); + + // negative + let left = SignedDecimal256::percent(-150); // -1.5 + let right = Int256::from(3); + assert_eq!(left / right, SignedDecimal256::percent(-50)); + + // 0/a + let left = SignedDecimal256::zero(); + let right = Int256::from(300); + assert_eq!(left / right, SignedDecimal256::zero()); + } + + #[test] + #[should_panic] + fn signed_decimal_256_int128_divide_by_zero() { + let left = SignedDecimal256::percent(150); // 1.5 + let right = Int256::from(0); + let _result = left / right; + } + + #[test] + fn signed_decimal_256_int128_div_assign() { + // a/b + let mut dec = SignedDecimal256::percent(150); // 1.5 + dec /= Int256::from(3); + assert_eq!(dec, SignedDecimal256::percent(50)); + + // 0/a + let mut dec = SignedDecimal256::zero(); + dec /= Int256::from(300); + assert_eq!(dec, SignedDecimal256::zero()); + } + + #[test] + #[should_panic] + fn signed_decimal_256_int128_div_assign_by_zero() { + // a/0 + let mut dec = SignedDecimal256::percent(50); + dec /= Int256::from(0); + } + + #[test] + fn signed_decimal_256_checked_pow() { + for exp in 0..10 { + assert_eq!( + SignedDecimal256::one().checked_pow(exp).unwrap(), + SignedDecimal256::one() + ); + } + + // This case is mathematically undefined but we ensure consistency with Rust standard types + // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=20df6716048e77087acd40194b233494 + assert_eq!( + SignedDecimal256::zero().checked_pow(0).unwrap(), + SignedDecimal256::one() + ); + + for exp in 1..10 { + assert_eq!( + SignedDecimal256::zero().checked_pow(exp).unwrap(), + SignedDecimal256::zero() + ); + } + + for exp in 1..10 { + assert_eq!( + SignedDecimal256::negative_one().checked_pow(exp).unwrap(), + // alternates between 1 and -1 + if exp % 2 == 0 { + SignedDecimal256::one() + } else { + SignedDecimal256::negative_one() + } + ) + } + + for num in &[ + SignedDecimal256::percent(50), + SignedDecimal256::percent(99), + SignedDecimal256::percent(200), + ] { + assert_eq!(num.checked_pow(0).unwrap(), SignedDecimal256::one()) + } + + assert_eq!( + SignedDecimal256::percent(20).checked_pow(2).unwrap(), + SignedDecimal256::percent(4) + ); + + assert_eq!( + SignedDecimal256::percent(20).checked_pow(3).unwrap(), + SignedDecimal256::permille(8) + ); + + assert_eq!( + SignedDecimal256::percent(200).checked_pow(4).unwrap(), + SignedDecimal256::percent(1600) + ); + + assert_eq!( + SignedDecimal256::percent(200).checked_pow(4).unwrap(), + SignedDecimal256::percent(1600) + ); + + assert_eq!( + SignedDecimal256::percent(700).checked_pow(5).unwrap(), + SignedDecimal256::percent(1680700) + ); + + assert_eq!( + SignedDecimal256::percent(700).checked_pow(8).unwrap(), + SignedDecimal256::percent(576480100) + ); + + assert_eq!( + SignedDecimal256::percent(700).checked_pow(10).unwrap(), + SignedDecimal256::percent(28247524900) + ); + + assert_eq!( + SignedDecimal256::percent(120).checked_pow(123).unwrap(), + SignedDecimal256(5486473221892422150877397607i128.into()) + ); + + assert_eq!( + SignedDecimal256::percent(10).checked_pow(2).unwrap(), + SignedDecimal256(10000000000000000i128.into()) + ); + + assert_eq!( + SignedDecimal256::percent(10).checked_pow(18).unwrap(), + SignedDecimal256(1i128.into()) + ); + + let decimals = [ + SignedDecimal256::percent(-50), + SignedDecimal256::percent(-99), + SignedDecimal256::percent(-200), + ]; + let exponents = [1, 2, 3, 4, 5, 8, 10]; + + for d in decimals { + for e in exponents { + // use multiplication as source of truth + let mut mul = Ok(d); + for _ in 1..e { + mul = mul.and_then(|mul| mul.checked_mul(d)); + } + assert_eq!(mul, d.checked_pow(e)); + } + } + } + + #[test] + fn signed_decimal_256_checked_pow_overflow() { + assert_eq!( + SignedDecimal256::MAX.checked_pow(2), + Err(OverflowError { + operation: OverflowOperation::Pow, + operand1: SignedDecimal256::MAX.to_string(), + operand2: "2".to_string(), + }) + ); + } + + #[test] + fn signed_decimal_256_to_string() { + // Integers + assert_eq!(SignedDecimal256::zero().to_string(), "0"); + assert_eq!(SignedDecimal256::one().to_string(), "1"); + assert_eq!(SignedDecimal256::percent(500).to_string(), "5"); + assert_eq!(SignedDecimal256::percent(-500).to_string(), "-5"); + + // SignedDecimal256s + assert_eq!(SignedDecimal256::percent(125).to_string(), "1.25"); + assert_eq!(SignedDecimal256::percent(42638).to_string(), "426.38"); + assert_eq!(SignedDecimal256::percent(3).to_string(), "0.03"); + assert_eq!(SignedDecimal256::permille(987).to_string(), "0.987"); + assert_eq!(SignedDecimal256::percent(-125).to_string(), "-1.25"); + assert_eq!(SignedDecimal256::percent(-42638).to_string(), "-426.38"); + assert_eq!(SignedDecimal256::percent(-3).to_string(), "-0.03"); + assert_eq!(SignedDecimal256::permille(-987).to_string(), "-0.987"); + + assert_eq!( + SignedDecimal256(Int256::from(1i128)).to_string(), + "0.000000000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10i128)).to_string(), + "0.00000000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(100i128)).to_string(), + "0.0000000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(1000i128)).to_string(), + "0.000000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10000i128)).to_string(), + "0.00000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(100000i128)).to_string(), + "0.0000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(1000000i128)).to_string(), + "0.000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10000000i128)).to_string(), + "0.00000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(100000000i128)).to_string(), + "0.0000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(1000000000i128)).to_string(), + "0.000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10000000000i128)).to_string(), + "0.00000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(100000000000i128)).to_string(), + "0.0000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10000000000000i128)).to_string(), + "0.00001" + ); + assert_eq!( + SignedDecimal256(Int256::from(100000000000000i128)).to_string(), + "0.0001" + ); + assert_eq!( + SignedDecimal256(Int256::from(1000000000000000i128)).to_string(), + "0.001" + ); + assert_eq!( + SignedDecimal256(Int256::from(10000000000000000i128)).to_string(), + "0.01" + ); + assert_eq!( + SignedDecimal256(Int256::from(100000000000000000i128)).to_string(), + "0.1" + ); + assert_eq!( + SignedDecimal256(Int256::from(-1i128)).to_string(), + "-0.000000000000000001" + ); + assert_eq!( + SignedDecimal256(Int256::from(-100000000000000i128)).to_string(), + "-0.0001" + ); + assert_eq!( + SignedDecimal256(Int256::from(-100000000000000000i128)).to_string(), + "-0.1" + ); + } + + #[test] + fn signed_decimal_256_iter_sum() { + let items = vec![ + SignedDecimal256::zero(), + SignedDecimal256(Int256::from(2i128)), + SignedDecimal256(Int256::from(2i128)), + SignedDecimal256(Int256::from(-2i128)), + ]; + assert_eq!( + items.iter().sum::(), + SignedDecimal256(Int256::from(2i128)) + ); + assert_eq!( + items.into_iter().sum::(), + SignedDecimal256(Int256::from(2i128)) + ); + + let empty: Vec = vec![]; + assert_eq!( + SignedDecimal256::zero(), + empty.iter().sum::() + ); + } + + #[test] + fn signed_decimal_256_serialize() { + assert_eq!(to_json_vec(&SignedDecimal256::zero()).unwrap(), br#""0""#); + assert_eq!(to_json_vec(&SignedDecimal256::one()).unwrap(), br#""1""#); + assert_eq!( + to_json_vec(&SignedDecimal256::percent(8)).unwrap(), + br#""0.08""# + ); + assert_eq!( + to_json_vec(&SignedDecimal256::percent(87)).unwrap(), + br#""0.87""# + ); + assert_eq!( + to_json_vec(&SignedDecimal256::percent(876)).unwrap(), + br#""8.76""# + ); + assert_eq!( + to_json_vec(&SignedDecimal256::percent(8765)).unwrap(), + br#""87.65""# + ); + assert_eq!( + to_json_vec(&SignedDecimal256::percent(-87654)).unwrap(), + br#""-876.54""# + ); + assert_eq!( + to_json_vec(&SignedDecimal256::negative_one()).unwrap(), + br#""-1""# + ); + assert_eq!( + to_json_vec(&-SignedDecimal256::percent(8)).unwrap(), + br#""-0.08""# + ); + } + + #[test] + fn signed_decimal_256_deserialize() { + assert_eq!( + from_json::(br#""0""#).unwrap(), + SignedDecimal256::zero() + ); + assert_eq!( + from_json::(br#""1""#).unwrap(), + SignedDecimal256::one() + ); + assert_eq!( + from_json::(br#""000""#).unwrap(), + SignedDecimal256::zero() + ); + assert_eq!( + from_json::(br#""001""#).unwrap(), + SignedDecimal256::one() + ); + + assert_eq!( + from_json::(br#""0.08""#).unwrap(), + SignedDecimal256::percent(8) + ); + assert_eq!( + from_json::(br#""0.87""#).unwrap(), + SignedDecimal256::percent(87) + ); + assert_eq!( + from_json::(br#""8.76""#).unwrap(), + SignedDecimal256::percent(876) + ); + assert_eq!( + from_json::(br#""87.65""#).unwrap(), + SignedDecimal256::percent(8765) + ); + + // negative numbers + assert_eq!( + from_json::(br#""-0""#).unwrap(), + SignedDecimal256::zero() + ); + assert_eq!( + from_json::(br#""-1""#).unwrap(), + SignedDecimal256::negative_one() + ); + assert_eq!( + from_json::(br#""-001""#).unwrap(), + SignedDecimal256::negative_one() + ); + assert_eq!( + from_json::(br#""-0.08""#).unwrap(), + SignedDecimal256::percent(-8) + ); + } + + #[test] + fn signed_decimal_256_abs_diff_works() { + let a = SignedDecimal256::percent(285); + let b = SignedDecimal256::percent(200); + let expected = Decimal256::percent(85); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let a = SignedDecimal256::percent(-200); + let b = SignedDecimal256::percent(200); + let expected = Decimal256::percent(400); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let a = SignedDecimal256::percent(-200); + let b = SignedDecimal256::percent(-240); + let expected = Decimal256::percent(40); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn signed_decimal_256_rem_works() { + // 4.02 % 1.11 = 0.69 + assert_eq!( + SignedDecimal256::percent(402) % SignedDecimal256::percent(111), + SignedDecimal256::percent(69) + ); + + // 15.25 % 4 = 3.25 + assert_eq!( + SignedDecimal256::percent(1525) % SignedDecimal256::percent(400), + SignedDecimal256::percent(325) + ); + + // -20.25 % 5 = -25 + assert_eq!( + SignedDecimal256::percent(-2025) % SignedDecimal256::percent(500), + SignedDecimal256::percent(-25) + ); + + let a = SignedDecimal256::percent(318); + let b = SignedDecimal256::percent(317); + let expected = SignedDecimal256::percent(1); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + fn signed_decimal_256_rem_assign_works() { + let mut a = SignedDecimal256::percent(17673); + a %= SignedDecimal256::percent(2362); + assert_eq!(a, SignedDecimal256::percent(1139)); // 176.73 % 23.62 = 11.39 + + let mut a = SignedDecimal256::percent(4262); + let b = SignedDecimal256::percent(1270); + a %= &b; + assert_eq!(a, SignedDecimal256::percent(452)); // 42.62 % 12.7 = 4.52 + + let mut a = SignedDecimal256::percent(-4262); + let b = SignedDecimal256::percent(1270); + a %= &b; + assert_eq!(a, SignedDecimal256::percent(-452)); // -42.62 % 12.7 = -4.52 + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn signed_decimal_256_rem_panics_for_zero() { + let _ = SignedDecimal256::percent(777) % SignedDecimal256::zero(); + } + + #[test] + fn signed_decimal_256_checked_methods() { + // checked add + assert_eq!( + SignedDecimal256::percent(402) + .checked_add(SignedDecimal256::percent(111)) + .unwrap(), + SignedDecimal256::percent(513) + ); + assert!(matches!( + SignedDecimal256::MAX.checked_add(SignedDecimal256::percent(1)), + Err(OverflowError { .. }) + )); + assert!(matches!( + SignedDecimal256::MIN.checked_add(SignedDecimal256::percent(-1)), + Err(OverflowError { .. }) + )); + + // checked sub + assert_eq!( + SignedDecimal256::percent(1111) + .checked_sub(SignedDecimal256::percent(111)) + .unwrap(), + SignedDecimal256::percent(1000) + ); + assert_eq!( + SignedDecimal256::zero() + .checked_sub(SignedDecimal256::percent(1)) + .unwrap(), + SignedDecimal256::percent(-1) + ); + assert!(matches!( + SignedDecimal256::MIN.checked_sub(SignedDecimal256::percent(1)), + Err(OverflowError { .. }) + )); + assert!(matches!( + SignedDecimal256::MAX.checked_sub(SignedDecimal256::percent(-1)), + Err(OverflowError { .. }) + )); + + // checked div + assert_eq!( + SignedDecimal256::percent(30) + .checked_div(SignedDecimal256::percent(200)) + .unwrap(), + SignedDecimal256::percent(15) + ); + assert_eq!( + SignedDecimal256::percent(88) + .checked_div(SignedDecimal256::percent(20)) + .unwrap(), + SignedDecimal256::percent(440) + ); + assert!(matches!( + SignedDecimal256::MAX.checked_div(SignedDecimal256::zero()), + Err(CheckedFromRatioError::DivideByZero {}) + )); + assert!(matches!( + SignedDecimal256::MAX.checked_div(SignedDecimal256::percent(1)), + Err(CheckedFromRatioError::Overflow {}) + )); + assert_eq!( + SignedDecimal256::percent(-88) + .checked_div(SignedDecimal256::percent(20)) + .unwrap(), + SignedDecimal256::percent(-440) + ); + assert_eq!( + SignedDecimal256::percent(-88) + .checked_div(SignedDecimal256::percent(-20)) + .unwrap(), + SignedDecimal256::percent(440) + ); + + // checked rem + assert_eq!( + SignedDecimal256::percent(402) + .checked_rem(SignedDecimal256::percent(111)) + .unwrap(), + SignedDecimal256::percent(69) + ); + assert_eq!( + SignedDecimal256::percent(1525) + .checked_rem(SignedDecimal256::percent(400)) + .unwrap(), + SignedDecimal256::percent(325) + ); + assert_eq!( + SignedDecimal256::percent(-1525) + .checked_rem(SignedDecimal256::percent(400)) + .unwrap(), + SignedDecimal256::percent(-325) + ); + assert_eq!( + SignedDecimal256::percent(-1525) + .checked_rem(SignedDecimal256::percent(-400)) + .unwrap(), + SignedDecimal256::percent(-325) + ); + assert!(matches!( + SignedDecimal256::MAX.checked_rem(SignedDecimal256::zero()), + Err(DivideByZeroError { .. }) + )); + } + + #[test] + fn signed_decimal_256_pow_works() { + assert_eq!( + SignedDecimal256::percent(200).pow(2), + SignedDecimal256::percent(400) + ); + assert_eq!( + SignedDecimal256::percent(-200).pow(2), + SignedDecimal256::percent(400) + ); + assert_eq!( + SignedDecimal256::percent(-200).pow(3), + SignedDecimal256::percent(-800) + ); + assert_eq!( + SignedDecimal256::percent(200).pow(10), + SignedDecimal256::percent(102400) + ); + } + + #[test] + #[should_panic] + fn signed_decimal_256_pow_overflow_panics() { + _ = SignedDecimal256::MAX.pow(2u32); + } + + #[test] + fn signed_decimal_256_saturating_works() { + assert_eq!( + SignedDecimal256::percent(200).saturating_add(SignedDecimal256::percent(200)), + SignedDecimal256::percent(400) + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_add(SignedDecimal256::percent(200)), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_add(SignedDecimal256::percent(-200)), + SignedDecimal256::percent(-400) + ); + assert_eq!( + SignedDecimal256::MAX.saturating_add(SignedDecimal256::percent(200)), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::MIN.saturating_add(SignedDecimal256::percent(-1)), + SignedDecimal256::MIN + ); + assert_eq!( + SignedDecimal256::percent(200).saturating_sub(SignedDecimal256::percent(100)), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_sub(SignedDecimal256::percent(100)), + SignedDecimal256::percent(-300) + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_sub(SignedDecimal256::percent(-100)), + SignedDecimal256::percent(-100) + ); + assert_eq!( + SignedDecimal256::zero().saturating_sub(SignedDecimal256::percent(200)), + SignedDecimal256::from_str("-2").unwrap() + ); + assert_eq!( + SignedDecimal256::MIN.saturating_sub(SignedDecimal256::percent(200)), + SignedDecimal256::MIN + ); + assert_eq!( + SignedDecimal256::MAX.saturating_sub(SignedDecimal256::percent(-200)), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::percent(200).saturating_mul(SignedDecimal256::percent(50)), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_mul(SignedDecimal256::percent(50)), + SignedDecimal256::percent(-100) + ); + assert_eq!( + SignedDecimal256::percent(-200).saturating_mul(SignedDecimal256::percent(-50)), + SignedDecimal256::percent(100) + ); + assert_eq!( + SignedDecimal256::MAX.saturating_mul(SignedDecimal256::percent(200)), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::MIN.saturating_mul(SignedDecimal256::percent(200)), + SignedDecimal256::MIN + ); + assert_eq!( + SignedDecimal256::MIN.saturating_mul(SignedDecimal256::percent(-200)), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::percent(400).saturating_pow(2u32), + SignedDecimal256::percent(1600) + ); + assert_eq!( + SignedDecimal256::MAX.saturating_pow(2u32), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::MAX.saturating_pow(3u32), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::MIN.saturating_pow(2u32), + SignedDecimal256::MAX + ); + assert_eq!( + SignedDecimal256::MIN.saturating_pow(3u32), + SignedDecimal256::MIN + ); + } + + #[test] + fn signed_decimal_256_rounding() { + assert_eq!(SignedDecimal256::one().floor(), SignedDecimal256::one()); + assert_eq!( + SignedDecimal256::percent(150).floor(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::percent(199).floor(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::percent(200).floor(), + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(99).floor(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256(Int256::from(1i128)).floor(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256(Int256::from(-1i128)).floor(), + SignedDecimal256::negative_one() + ); + assert_eq!( + SignedDecimal256::permille(-1234).floor(), + SignedDecimal256::percent(-200) + ); + + assert_eq!(SignedDecimal256::one().ceil(), SignedDecimal256::one()); + assert_eq!( + SignedDecimal256::percent(150).ceil(), + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(199).ceil(), + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(99).ceil(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256(Int256::from(1i128)).ceil(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256(Int256::from(-1i128)).ceil(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::permille(-1234).ceil(), + SignedDecimal256::negative_one() + ); + + assert_eq!(SignedDecimal256::one().trunc(), SignedDecimal256::one()); + assert_eq!( + SignedDecimal256::percent(150).trunc(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::percent(199).trunc(), + SignedDecimal256::one() + ); + assert_eq!( + SignedDecimal256::percent(200).trunc(), + SignedDecimal256::percent(200) + ); + assert_eq!( + SignedDecimal256::percent(99).trunc(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256(Int256::from(1i128)).trunc(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256(Int256::from(-1i128)).trunc(), + SignedDecimal256::zero() + ); + assert_eq!( + SignedDecimal256::permille(-1234).trunc(), + SignedDecimal256::negative_one() + ); + } + + #[test] + #[should_panic(expected = "attempt to ceil with overflow")] + fn signed_decimal_256_ceil_panics() { + let _ = SignedDecimal256::MAX.ceil(); + } + + #[test] + #[should_panic(expected = "attempt to floor with overflow")] + fn signed_decimal_256_floor_panics() { + let _ = SignedDecimal256::MIN.floor(); + } + + #[test] + fn signed_decimal_256_checked_ceil() { + assert_eq!( + SignedDecimal256::percent(199).checked_ceil(), + Ok(SignedDecimal256::percent(200)) + ); + assert_eq!( + SignedDecimal256::MAX.checked_ceil(), + Err(RoundUpOverflowError) + ); + } + + #[test] + fn signed_decimal_256_checked_floor() { + assert_eq!( + SignedDecimal256::percent(199).checked_floor(), + Ok(SignedDecimal256::one()) + ); + assert_eq!( + SignedDecimal256::percent(-199).checked_floor(), + Ok(SignedDecimal256::percent(-200)) + ); + assert_eq!( + SignedDecimal256::MIN.checked_floor(), + Err(RoundDownOverflowError) + ); + assert_eq!( + SignedDecimal256::negative_one().checked_floor(), + Ok(SignedDecimal256::negative_one()) + ); + } + + #[test] + fn signed_decimal_256_to_int_floor_works() { + let d = SignedDecimal256::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(12)); + let d = SignedDecimal256::from_str("12.345").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(12)); + let d = SignedDecimal256::from_str("12.999").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(12)); + let d = SignedDecimal256::from_str("0.98451384").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(0)); + let d = SignedDecimal256::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(-13)); + let d = SignedDecimal256::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(-13)); + let d = SignedDecimal256::from_str("0.0001").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(0)); + let d = SignedDecimal256::from_str("75.0").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(75)); + let d = SignedDecimal256::from_str("0.0").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(0)); + let d = SignedDecimal256::from_str("-0.0").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(0)); + let d = SignedDecimal256::from_str("-0.0001").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(-1)); + let d = SignedDecimal256::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_floor(), Int256::from(-75)); + + let d = SignedDecimal256::MAX; + assert_eq!( + d.to_int_floor(), + Int256::from_str("57896044618658097711785492504343953926634992332820282019728") + .unwrap() + ); + let d = SignedDecimal256::MIN; + assert_eq!( + d.to_int_floor(), + Int256::from_str("-57896044618658097711785492504343953926634992332820282019729") + .unwrap() + ); + } + + #[test] + fn signed_decimal_256_to_int_ceil_works() { + let d = SignedDecimal256::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(13)); + let d = SignedDecimal256::from_str("12.345").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(13)); + let d = SignedDecimal256::from_str("12.999").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(13)); + let d = SignedDecimal256::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(-12)); + let d = SignedDecimal256::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(-12)); + + let d = SignedDecimal256::from_str("75.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(75)); + let d = SignedDecimal256::from_str("0.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(0)); + let d = SignedDecimal256::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_ceil(), Int256::from(-75)); + + let d = SignedDecimal256::MAX; + assert_eq!( + d.to_int_ceil(), + Int256::from_str("57896044618658097711785492504343953926634992332820282019729") + .unwrap() + ); + let d = SignedDecimal256::MIN; + assert_eq!( + d.to_int_ceil(), + Int256::from_str("-57896044618658097711785492504343953926634992332820282019728") + .unwrap() + ); + } + + #[test] + fn signed_decimal_256_to_int_trunc_works() { + let d = SignedDecimal256::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(12)); + let d = SignedDecimal256::from_str("12.345").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(12)); + let d = SignedDecimal256::from_str("12.999").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(12)); + let d = SignedDecimal256::from_str("-12.000000000000000001").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(-12)); + let d = SignedDecimal256::from_str("-12.345").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(-12)); + + let d = SignedDecimal256::from_str("75.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(75)); + let d = SignedDecimal256::from_str("0.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(0)); + let d = SignedDecimal256::from_str("-75.0").unwrap(); + assert_eq!(d.to_int_trunc(), Int256::from(-75)); + + let d = SignedDecimal256::MAX; + assert_eq!( + d.to_int_trunc(), + Int256::from_str("57896044618658097711785492504343953926634992332820282019728") + .unwrap() + ); + let d = SignedDecimal256::MIN; + assert_eq!( + d.to_int_trunc(), + Int256::from_str("-57896044618658097711785492504343953926634992332820282019728") + .unwrap() + ); + } + + #[test] + fn signed_decimal_256_neg_works() { + assert_eq!( + -SignedDecimal256::percent(50), + SignedDecimal256::percent(-50) + ); + assert_eq!(-SignedDecimal256::one(), SignedDecimal256::negative_one()); + } + + #[test] + fn signed_decimal_256_partial_eq() { + let test_cases = [ + ("1", "1", true), + ("0.5", "0.5", true), + ("0.5", "0.51", false), + ("0", "0.00000", true), + ("-1", "-1", true), + ("-0.5", "-0.5", true), + ("-0.5", "0.5", false), + ("-0.5", "-0.51", false), + ("-0", "-0.00000", true), + ] + .into_iter() + .map(|(lhs, rhs, expected)| (dec(lhs), dec(rhs), expected)); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } + + #[test] + fn signed_decimal_256_implements_debug() { + let decimal = SignedDecimal256::from_str("123.45").unwrap(); + assert_eq!(format!("{decimal:?}"), "SignedDecimal256(123.45)"); + + let test_cases = ["5", "5.01", "42", "0", "2", "-0.000001"]; + for s in test_cases { + let decimal = SignedDecimal256::from_str(s).unwrap(); + let expected = format!("SignedDecimal256({s})"); + assert_eq!(format!("{decimal:?}"), expected); + } + } + + #[test] + fn signed_decimal_256_can_be_instantiated_from_decimal() { + let d: SignedDecimal256 = Decimal::one().into(); + assert_eq!(d, SignedDecimal256::one()); + } + + #[test] + fn signed_decimal_256_can_be_instantiated_from_decimal_256() { + let d: SignedDecimal256 = Decimal256::zero().try_into().unwrap(); + assert_eq!(d, SignedDecimal256::zero()); + } + + #[test] + fn signed_decimal_256_may_fail_when_instantiated_from_decimal_256() { + let err = >::try_into(Decimal256::MAX).unwrap_err(); + assert_eq!("SignedDecimal256RangeExceeded", format!("{err:?}")); + assert_eq!("SignedDecimal256 range exceeded", format!("{err}")); + } + + #[test] + fn signed_decimal_256_can_be_serialized_and_deserialized() { + // properly deserialized + let value: SignedDecimal256 = serde_json::from_str(r#""123""#).unwrap(); + assert_eq!(SignedDecimal256::from_str("123").unwrap(), value); + + // properly serialized + let value = SignedDecimal256::from_str("456").unwrap(); + assert_eq!(r#""456""#, serde_json::to_string(&value).unwrap()); + + // invalid: not a string encoded decimal + assert_eq!( + "invalid type: integer `123`, expected string-encoded decimal at line 1 column 3", + serde_json::from_str::("123") + .err() + .unwrap() + .to_string() + ); + + // invalid: not properly defined signed decimal value + assert_eq!( + "Error parsing decimal '1.e': Generic error: Error parsing fractional at line 1 column 5", + serde_json::from_str::(r#""1.e""#) + .err() + .unwrap() + .to_string() + ); + } + + #[test] + fn signed_decimal_256_has_defined_json_schema() { + let schema = schema_for!(SignedDecimal256); + assert_eq!( + "SignedDecimal256", + schema.schema.metadata.unwrap().title.unwrap() + ); + } +} diff --git a/packages/std/src/math/uint128.rs b/packages/std/src/math/uint128.rs index 0aebf6b99..28081a947 100644 --- a/packages/std/src/math/uint128.rs +++ b/packages/std/src/math/uint128.rs @@ -15,9 +15,13 @@ use crate::errors::{ OverflowOperation, StdError, }; use crate::{ - forward_ref_partial_eq, impl_mul_fraction, ConversionOverflowError, Fraction, Uint256, Uint64, + forward_ref_partial_eq, impl_mul_fraction, Fraction, Int128, Int256, Int512, Int64, Uint256, + Uint64, }; +use super::conversion::forward_try_from; +use super::num_consts::NumConsts; + /// A thin wrapper around u128 that is using strings for JSON encoding/decoding, /// such that the full u128 range can be used for clients that convert JSON numbers to floats, /// like JavaScript and jq. @@ -269,6 +273,13 @@ impl Uint128 { } } +impl NumConsts for Uint128 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + impl_mul_fraction!(Uint128); // `From` is implemented manually instead of @@ -312,15 +323,13 @@ impl From for Uint128 { } } -impl TryFrom for Uint64 { - type Error = ConversionOverflowError; +forward_try_from!(Uint128, Uint64); - fn try_from(value: Uint128) -> Result { - Ok(Uint64::new(value.0.try_into().map_err(|_| { - ConversionOverflowError::new("Uint128", "Uint64", value.to_string()) - })?)) - } -} +// Int to Uint +forward_try_from!(Int64, Uint128); +forward_try_from!(Int128, Uint128); +forward_try_from!(Int256, Uint128); +forward_try_from!(Int512, Uint128); impl TryFrom<&str> for Uint128 { type Error = StdError; @@ -607,7 +616,8 @@ where #[cfg(test)] mod tests { use crate::errors::CheckedMultiplyFractionError::{ConversionOverflow, DivideByZero}; - use crate::{from_slice, to_vec, Decimal}; + use crate::math::conversion::test_try_from_int_to_uint; + use crate::{from_json, to_json_vec, ConversionOverflowError, Decimal}; use super::*; @@ -677,6 +687,26 @@ mod tests { assert!(result.is_err()); } + #[test] + fn uint128_try_from_signed_works() { + test_try_from_int_to_uint::("Int64", "Uint128"); + test_try_from_int_to_uint::("Int128", "Uint128"); + test_try_from_int_to_uint::("Int256", "Uint128"); + test_try_from_int_to_uint::("Int512", "Uint128"); + } + + #[test] + fn uint128_try_into() { + assert!(Uint64::try_from(Uint128::MAX).is_err()); + + assert_eq!(Uint64::try_from(Uint128::zero()), Ok(Uint64::zero())); + + assert_eq!( + Uint64::try_from(Uint128::from(42u64)), + Ok(Uint64::from(42u64)) + ); + } + #[test] fn uint128_implements_display() { let a = Uint128(12345); @@ -759,9 +789,9 @@ mod tests { #[test] fn uint128_json() { let orig = Uint128(1234567890987654321); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Uint128 = from_slice(&serialized).unwrap(); + let parsed: Uint128 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } diff --git a/packages/std/src/math/uint256.rs b/packages/std/src/math/uint256.rs index 6422dee76..a4a672ad8 100644 --- a/packages/std/src/math/uint256.rs +++ b/packages/std/src/math/uint256.rs @@ -13,12 +13,18 @@ use crate::errors::{ CheckedMultiplyFractionError, CheckedMultiplyRatioError, ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, StdError, }; -use crate::{forward_ref_partial_eq, impl_mul_fraction, Fraction, Uint128, Uint512, Uint64}; +use crate::{ + forward_ref_partial_eq, impl_mul_fraction, Fraction, Int128, Int256, Int512, Int64, Uint128, + Uint512, Uint64, +}; /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. use bnum::types::U256; +use super::conversion::{forward_try_from, try_from_int_to_uint}; +use super::num_consts::NumConsts; + /// An implementation of u256 that is using strings for JSON encoding/decoding, /// such that the full u256 range can be used for clients that convert JSON numbers to floats, /// like JavaScript and jq. @@ -335,6 +341,13 @@ impl Uint256 { } } +impl NumConsts for Uint256 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + impl_mul_fraction!(Uint256); impl From for Uint256 { @@ -379,15 +392,14 @@ impl From for Uint256 { } } -impl TryFrom for Uint128 { - type Error = ConversionOverflowError; +forward_try_from!(Uint256, Uint128); +forward_try_from!(Uint256, Uint64); - fn try_from(value: Uint256) -> Result { - Ok(Uint128::new(value.0.try_into().map_err(|_| { - ConversionOverflowError::new("Uint256", "Uint128", value.to_string()) - })?)) - } -} +// Int to Uint +try_from_int_to_uint!(Int64, Uint256); +try_from_int_to_uint!(Int128, Uint256); +try_from_int_to_uint!(Int256, Uint256); +try_from_int_to_uint!(Int512, Uint256); impl TryFrom<&str> for Uint256 { type Error = StdError; @@ -667,7 +679,8 @@ where mod tests { use super::*; use crate::errors::CheckedMultiplyFractionError::{ConversionOverflow, DivideByZero}; - use crate::{from_slice, to_vec, Decimal, Decimal256}; + use crate::math::conversion::test_try_from_int_to_uint; + use crate::{from_json, to_json_vec, Decimal, Decimal256}; #[test] fn size_of_works() { @@ -1062,6 +1075,32 @@ mod tests { assert!(result.is_err()); } + #[test] + fn uint256_try_from_signed_works() { + test_try_from_int_to_uint::("Int64", "Uint256"); + test_try_from_int_to_uint::("Int128", "Uint256"); + test_try_from_int_to_uint::("Int256", "Uint256"); + test_try_from_int_to_uint::("Int512", "Uint256"); + } + + #[test] + fn uint256_try_into() { + assert!(Uint64::try_from(Uint256::MAX).is_err()); + assert!(Uint128::try_from(Uint256::MAX).is_err()); + + assert_eq!(Uint64::try_from(Uint256::zero()), Ok(Uint64::zero())); + assert_eq!(Uint128::try_from(Uint256::zero()), Ok(Uint128::zero())); + + assert_eq!( + Uint64::try_from(Uint256::from(42u64)), + Ok(Uint64::from(42u64)) + ); + assert_eq!( + Uint128::try_from(Uint256::from(42u128)), + Ok(Uint128::from(42u128)) + ); + } + #[test] fn uint256_convert_to_uint128() { let source = Uint256::from(42u128); @@ -1267,9 +1306,9 @@ mod tests { #[test] fn uint256_json() { let orig = Uint256::from(1234567890987654321u128); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Uint256 = from_slice(&serialized).unwrap(); + let parsed: Uint256 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } diff --git a/packages/std/src/math/uint512.rs b/packages/std/src/math/uint512.rs index c4dd9e3f5..30be022a6 100644 --- a/packages/std/src/math/uint512.rs +++ b/packages/std/src/math/uint512.rs @@ -11,12 +11,15 @@ use serde::{de, ser, Deserialize, Deserializer, Serialize}; use crate::errors::{ ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, StdError, }; -use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint64}; +use crate::{forward_ref_partial_eq, Int128, Int256, Int512, Int64, Uint128, Uint256, Uint64}; /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. use bnum::types::U512; +use super::conversion::{forward_try_from, try_from_int_to_uint}; +use super::num_consts::NumConsts; + /// An implementation of u512 that is using strings for JSON encoding/decoding, /// such that the full u512 range can be used for clients that convert JSON numbers to floats, /// like JavaScript and jq. @@ -300,6 +303,13 @@ impl Uint512 { } } +impl NumConsts for Uint512 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + impl From for Uint512 { fn from(val: Uint256) -> Self { let mut bytes = [0u8; 64]; @@ -370,15 +380,14 @@ impl TryFrom for Uint256 { } } -impl TryFrom for Uint128 { - type Error = ConversionOverflowError; +forward_try_from!(Uint512, Uint128); +forward_try_from!(Uint512, Uint64); - fn try_from(value: Uint512) -> Result { - Ok(Uint128::new(value.0.try_into().map_err(|_| { - ConversionOverflowError::new("Uint512", "Uint128", value.to_string()) - })?)) - } -} +// Int to Uint +try_from_int_to_uint!(Int64, Uint512); +try_from_int_to_uint!(Int128, Uint512); +try_from_int_to_uint!(Int256, Uint512); +try_from_int_to_uint!(Int512, Uint512); impl TryFrom<&str> for Uint512 { type Error = StdError; @@ -637,7 +646,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec}; + use crate::{from_json, math::conversion::test_try_from_int_to_uint, to_json_vec}; #[test] fn size_of_works() { @@ -748,6 +757,38 @@ mod tests { assert!(result.is_err()); } + #[test] + fn uint512_try_from_signed_works() { + test_try_from_int_to_uint::("Int64", "Uint512"); + test_try_from_int_to_uint::("Int128", "Uint512"); + test_try_from_int_to_uint::("Int256", "Uint512"); + test_try_from_int_to_uint::("Int512", "Uint512"); + } + + #[test] + fn uint512_try_into() { + assert!(Uint64::try_from(Uint512::MAX).is_err()); + assert!(Uint128::try_from(Uint512::MAX).is_err()); + assert!(Uint256::try_from(Uint512::MAX).is_err()); + + assert_eq!(Uint64::try_from(Uint512::zero()), Ok(Uint64::zero())); + assert_eq!(Uint128::try_from(Uint512::zero()), Ok(Uint128::zero())); + assert_eq!(Uint256::try_from(Uint512::zero()), Ok(Uint256::zero())); + + assert_eq!( + Uint64::try_from(Uint512::from(42u64)), + Ok(Uint64::from(42u64)) + ); + assert_eq!( + Uint128::try_from(Uint512::from(42u128)), + Ok(Uint128::from(42u128)) + ); + assert_eq!( + Uint256::try_from(Uint512::from(42u128)), + Ok(Uint256::from(42u128)) + ); + } + #[test] fn uint512_convert_to_uint128() { let source = Uint512::from(42u128); @@ -971,9 +1012,9 @@ mod tests { #[test] fn uint512_json() { let orig = Uint512::from(1234567890987654321u128); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Uint512 = from_slice(&serialized).unwrap(); + let parsed: Uint512 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } diff --git a/packages/std/src/math/uint64.rs b/packages/std/src/math/uint64.rs index f405b2364..f168f893c 100644 --- a/packages/std/src/math/uint64.rs +++ b/packages/std/src/math/uint64.rs @@ -12,7 +12,12 @@ use crate::errors::{ CheckedMultiplyFractionError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, OverflowOperation, StdError, }; -use crate::{forward_ref_partial_eq, impl_mul_fraction, Fraction, Uint128}; +use crate::{ + forward_ref_partial_eq, impl_mul_fraction, Fraction, Int128, Int256, Int512, Int64, Uint128, +}; + +use super::conversion::forward_try_from; +use super::num_consts::NumConsts; /// A thin wrapper around u64 that is using strings for JSON encoding/decoding, /// such that the full u64 range can be used for clients that convert JSON numbers to floats, @@ -262,6 +267,13 @@ impl Uint64 { } } +impl NumConsts for Uint64 { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + const MAX: Self = Self::MAX; + const MIN: Self = Self::MIN; +} + impl_mul_fraction!(Uint64); // `From` is implemented manually instead of @@ -269,6 +281,7 @@ impl_mul_fraction!(Uint64); // of the conflict with `TryFrom<&str>` as described here // https://stackoverflow.com/questions/63136970/how-do-i-work-around-the-upstream-crates-may-add-a-new-impl-of-trait-error +// uint to Uint impl From for Uint64 { fn from(val: u64) -> Self { Uint64(val) @@ -293,6 +306,12 @@ impl From for Uint64 { } } +// Int to Uint +forward_try_from!(Int64, Uint64); +forward_try_from!(Int128, Uint64); +forward_try_from!(Int256, Uint64); +forward_try_from!(Int512, Uint64); + impl TryFrom<&str> for Uint64 { type Error = StdError; @@ -559,7 +578,8 @@ where mod tests { use super::*; use crate::errors::CheckedMultiplyFractionError::{ConversionOverflow, DivideByZero}; - use crate::{from_slice, to_vec, ConversionOverflowError}; + use crate::math::conversion::test_try_from_int_to_uint; + use crate::{from_json, to_json_vec, ConversionOverflowError}; #[test] fn size_of_works() { @@ -618,6 +638,14 @@ mod tests { assert!(result.is_err()); } + #[test] + fn uint64_try_from_signed_works() { + test_try_from_int_to_uint::("Int64", "Uint64"); + test_try_from_int_to_uint::("Int128", "Uint64"); + test_try_from_int_to_uint::("Int256", "Uint64"); + test_try_from_int_to_uint::("Int512", "Uint64"); + } + #[test] fn uint64_implements_display() { let a = Uint64(12345); @@ -682,9 +710,9 @@ mod tests { #[test] fn uint64_json() { let orig = Uint64(1234567890987654321); - let serialized = to_vec(&orig).unwrap(); + let serialized = to_json_vec(&orig).unwrap(); assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); - let parsed: Uint64 = from_slice(&serialized).unwrap(); + let parsed: Uint64 = from_json(serialized).unwrap(); assert_eq!(parsed, orig); } diff --git a/packages/std/src/query/wasm.rs b/packages/std/src/query/wasm.rs index 9487ff592..a80008e29 100644 --- a/packages/std/src/query/wasm.rs +++ b/packages/std/src/query/wasm.rs @@ -94,14 +94,14 @@ impl QueryResponseType for CodeInfoResponse {} #[cfg(test)] mod tests { use super::*; - use crate::to_binary; + use crate::to_json_binary; #[test] fn wasm_query_contract_info_serialization() { let query = WasmQuery::ContractInfo { contract_addr: "aabbccdd456".into(), }; - let json = to_binary(&query).unwrap(); + let json = to_json_binary(&query).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"contract_info":{"contract_addr":"aabbccdd456"}}"#, @@ -112,7 +112,7 @@ mod tests { #[cfg(feature = "cosmwasm_1_2")] fn wasm_query_code_info_serialization() { let query = WasmQuery::CodeInfo { code_id: 70 }; - let json = to_binary(&query).unwrap(); + let json = to_json_binary(&query).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"code_info":{"code_id":70}}"#, @@ -128,7 +128,7 @@ mod tests { pinned: true, ibc_port: Some("wasm.123".to_string()), }; - let json = to_binary(&response).unwrap(); + let json = to_json_binary(&response).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"code_id":67,"creator":"jane","admin":"king","pinned":true,"ibc_port":"wasm.123"}"#, @@ -148,7 +148,7 @@ mod tests { ) .unwrap(), }; - let json = to_binary(&response).unwrap(); + let json = to_json_binary(&response).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"code_id":67,"creator":"jane","checksum":"f7bb7b18fb01bbf425cf4ed2cd4b7fb26a019a7fc75a4dc87e8a0b768c501f00"}"#, diff --git a/packages/std/src/results/contract_result.rs b/packages/std/src/results/contract_result.rs index 343da57c8..a55c32e3c 100644 --- a/packages/std/src/results/contract_result.rs +++ b/packages/std/src/results/contract_result.rs @@ -88,59 +88,59 @@ impl From> for Result { #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec, Response, StdError, StdResult}; + use crate::{from_json, to_json_vec, Response, StdError, StdResult}; #[test] fn contract_result_serialization_works() { let result = ContractResult::Ok(12); - assert_eq!(&to_vec(&result).unwrap(), b"{\"ok\":12}"); + assert_eq!(&to_json_vec(&result).unwrap(), b"{\"ok\":12}"); let result = ContractResult::Ok("foo"); - assert_eq!(&to_vec(&result).unwrap(), b"{\"ok\":\"foo\"}"); + assert_eq!(&to_json_vec(&result).unwrap(), b"{\"ok\":\"foo\"}"); let result: ContractResult = ContractResult::Ok(Response::default()); assert_eq!( - to_vec(&result).unwrap(), + to_json_vec(&result).unwrap(), br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"# ); let result: ContractResult = ContractResult::Err("broken".to_string()); - assert_eq!(&to_vec(&result).unwrap(), b"{\"error\":\"broken\"}"); + assert_eq!(&to_json_vec(&result).unwrap(), b"{\"error\":\"broken\"}"); } #[test] fn contract_result_deserialization_works() { - let result: ContractResult = from_slice(br#"{"ok":12}"#).unwrap(); + let result: ContractResult = from_json(br#"{"ok":12}"#).unwrap(); assert_eq!(result, ContractResult::Ok(12)); - let result: ContractResult = from_slice(br#"{"ok":"foo"}"#).unwrap(); + let result: ContractResult = from_json(br#"{"ok":"foo"}"#).unwrap(); assert_eq!(result, ContractResult::Ok("foo".to_string())); let result: ContractResult = - from_slice(br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#) + from_json(br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#) .unwrap(); assert_eq!(result, ContractResult::Ok(Response::default())); - let result: ContractResult = from_slice(br#"{"error":"broken"}"#).unwrap(); + let result: ContractResult = from_json(br#"{"error":"broken"}"#).unwrap(); assert_eq!(result, ContractResult::Err("broken".to_string())); // ignores whitespace - let result: ContractResult = from_slice(b" {\n\t \"ok\": 5898\n} ").unwrap(); + let result: ContractResult = from_json(b" {\n\t \"ok\": 5898\n} ").unwrap(); assert_eq!(result, ContractResult::Ok(5898)); // fails for additional attributes - let parse: StdResult> = from_slice(br#"{"unrelated":321,"ok":4554}"#); + let parse: StdResult> = from_json(br#"{"unrelated":321,"ok":4554}"#); match parse.unwrap_err() { StdError::ParseErr { .. } => {} err => panic!("Unexpected error: {err:?}"), } - let parse: StdResult> = from_slice(br#"{"ok":4554,"unrelated":321}"#); + let parse: StdResult> = from_json(br#"{"ok":4554,"unrelated":321}"#); match parse.unwrap_err() { StdError::ParseErr { .. } => {} err => panic!("Unexpected error: {err:?}"), } let parse: StdResult> = - from_slice(br#"{"ok":4554,"error":"What's up now?"}"#); + from_json(br#"{"ok":4554,"error":"What's up now?"}"#); match parse.unwrap_err() { StdError::ParseErr { .. } => {} err => panic!("Unexpected error: {err:?}"), diff --git a/packages/std/src/results/cosmos_msg.rs b/packages/std/src/results/cosmos_msg.rs index 1a8bd0eb6..dc886ab88 100644 --- a/packages/std/src/results/cosmos_msg.rs +++ b/packages/std/src/results/cosmos_msg.rs @@ -8,7 +8,7 @@ use crate::coin::Coin; use crate::errors::StdResult; #[cfg(feature = "stargate")] use crate::ibc::IbcMsg; -use crate::serde::to_binary; +use crate::serde::to_json_binary; #[cfg(all(feature = "stargate", feature = "cosmwasm_1_2"))] use crate::Decimal; @@ -336,7 +336,7 @@ pub fn wasm_instantiate( funds: Vec, label: String, ) -> StdResult { - let payload = to_binary(msg)?; + let payload = to_json_binary(msg)?; Ok(WasmMsg::Instantiate { admin: None, code_id, @@ -352,7 +352,7 @@ pub fn wasm_execute( msg: &impl Serialize, funds: Vec, ) -> StdResult { - let payload = to_binary(msg)?; + let payload = to_json_binary(msg)?; Ok(WasmMsg::Execute { contract_addr: contract_addr.into(), msg: payload, @@ -427,7 +427,7 @@ mod tests { funds: vec![], label: "my instance".to_string(), }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"instantiate":{"admin":"king","code_id":7897,"msg":"eyJjbGFpbSI6e319","funds":[],"label":"my instance"}}"#, @@ -441,7 +441,7 @@ mod tests { funds: vec![], label: "my instance".to_string(), }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"instantiate":{"admin":null,"code_id":7897,"msg":"eyJjbGFpbSI6e319","funds":[],"label":"my instance"}}"#, @@ -455,7 +455,7 @@ mod tests { funds: vec![coin(321, "stones")], label: "my instance".to_string(), }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"instantiate":{"admin":null,"code_id":7897,"msg":"eyJjbGFpbSI6e319","funds":[{"denom":"stones","amount":"321"}],"label":"my instance"}}"#, @@ -472,7 +472,7 @@ mod tests { funds: vec![coin(321, "stones")], salt: Binary::from_base64("UkOVazhiwoo=").unwrap(), }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"instantiate2":{"admin":null,"code_id":7897,"label":"my instance","msg":"eyJjbGFpbSI6e319","funds":[{"denom":"stones","amount":"321"}],"salt":"UkOVazhiwoo="}}"#, @@ -486,7 +486,7 @@ mod tests { // FundCommunityPool let fund_coins = vec![coin(200, "feathers"), coin(200, "stones")]; let fund_msg = DistributionMsg::FundCommunityPool { amount: fund_coins }; - let fund_json = to_binary(&fund_msg).unwrap(); + let fund_json = to_json_binary(&fund_msg).unwrap(); assert_eq!( String::from_utf8_lossy(&fund_json), r#"{"fund_community_pool":{"amount":[{"denom":"feathers","amount":"200"},{"denom":"stones","amount":"200"}]}}"#, @@ -496,7 +496,7 @@ mod tests { let set_msg = DistributionMsg::SetWithdrawAddress { address: String::from("withdrawer"), }; - let set_json = to_binary(&set_msg).unwrap(); + let set_json = to_json_binary(&set_msg).unwrap(); assert_eq!( String::from_utf8_lossy(&set_json), r#"{"set_withdraw_address":{"address":"withdrawer"}}"#, @@ -506,7 +506,7 @@ mod tests { let withdraw_msg = DistributionMsg::WithdrawDelegatorReward { validator: String::from("fancyoperator"), }; - let withdraw_json = to_binary(&withdraw_msg).unwrap(); + let withdraw_json = to_json_binary(&withdraw_msg).unwrap(); assert_eq!( String::from_utf8_lossy(&withdraw_json), r#"{"withdraw_delegator_reward":{"validator":"fancyoperator"}}"# @@ -522,7 +522,7 @@ mod tests { let msg = WasmMsg::Execute { contract_addr: "joe".to_string(), - msg: to_binary(&ExecuteMsg::Mint { + msg: to_json_binary(&ExecuteMsg::Mint { coin: coin(10, "BTC"), }) .unwrap(), @@ -557,7 +557,7 @@ mod tests { proposal_id: 4, vote: VoteOption::NoWithVeto, }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"vote":{"proposal_id":4,"vote":"no_with_veto"}}"#, @@ -584,7 +584,7 @@ mod tests { ], }; - let json = to_binary(&msg).unwrap(); + let json = to_json_binary(&msg).unwrap(); assert_eq!( String::from_utf8_lossy(&json), r#"{"vote_weighted":{"proposal_id":25,"options":[{"option":"yes","weight":"0.25"},{"option":"no","weight":"0.25"},{"option":"abstain","weight":"0.5"}]}}"#, diff --git a/packages/std/src/results/empty.rs b/packages/std/src/results/empty.rs index 56ccf3586..91e3b581b 100644 --- a/packages/std/src/results/empty.rs +++ b/packages/std/src/results/empty.rs @@ -14,7 +14,7 @@ pub struct Empty {} mod tests { use super::*; - use crate::serde::{from_slice, to_vec}; + use crate::serde::{from_json, to_json_vec}; #[test] fn empty_can_be_instantiated() { @@ -25,13 +25,13 @@ mod tests { #[test] fn empty_can_be_instantiated_serialized_and_deserialized() { let instance = Empty {}; - let serialized = to_vec(&instance).unwrap(); + let serialized = to_json_vec(&instance).unwrap(); assert_eq!(serialized, b"{}"); - let deserialized: Empty = from_slice(b"{}").unwrap(); + let deserialized: Empty = from_json(b"{}").unwrap(); assert_eq!(deserialized, instance); - let deserialized: Empty = from_slice(b"{\"stray\":\"data\"}").unwrap(); + let deserialized: Empty = from_json(b"{\"stray\":\"data\"}").unwrap(); assert_eq!(deserialized, instance); } } diff --git a/packages/std/src/results/events.rs b/packages/std/src/results/events.rs index 195cd9161..0ecf17edc 100644 --- a/packages/std/src/results/events.rs +++ b/packages/std/src/results/events.rs @@ -9,7 +9,7 @@ use crate::forward_ref_partial_eq; /// which then get magically converted to bytes for Tendermint somewhere between /// the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK. /// -/// [*Cosmos SDK* event]: https://docs.cosmos.network/main/core/events.html +/// [*Cosmos SDK* event]: https://docs.cosmos.network/main/learn/advanced/events /// [*Cosmos SDK* StringEvent]: https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[non_exhaustive] @@ -21,7 +21,7 @@ pub struct Event { /// /// You can learn more about these from [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub attributes: Vec, } diff --git a/packages/std/src/results/response.rs b/packages/std/src/results/response.rs index 88f04e5c8..3a13e1b43 100644 --- a/packages/std/src/results/response.rs +++ b/packages/std/src/results/response.rs @@ -72,14 +72,14 @@ pub struct Response { /// /// More info about events (and their attributes) can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub attributes: Vec, /// Extra, custom events separate from the main `wasm` one. These will have /// `wasm-` prepended to the type. /// /// More info about events can be found in [*Cosmos SDK* docs]. /// - /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/core/events.html + /// [*Cosmos SDK* docs]: https://docs.cosmos.network/main/learn/advanced/events pub events: Vec, /// The binary payload to include in the response. pub data: Option, @@ -315,8 +315,8 @@ mod tests { events: vec![], data: Some(Binary::from([0xAA, 0xBB])), }; - let serialized = to_vec(&original).expect("encode contract result"); - let deserialized: Response = from_slice(&serialized).expect("decode contract result"); + let serialized = to_json_vec(&original).expect("encode contract result"); + let deserialized: Response = from_json(serialized).expect("decode contract result"); assert_eq!(deserialized, original); } diff --git a/packages/std/src/results/submessages.rs b/packages/std/src/results/submessages.rs index f854e7832..7aa7d7ce7 100644 --- a/packages/std/src/results/submessages.rs +++ b/packages/std/src/results/submessages.rs @@ -206,7 +206,7 @@ pub type SubMsgExecutionResponse = SubMsgResponse; #[cfg(test)] mod tests { use super::*; - use crate::{from_slice, to_vec, StdError, StdResult}; + use crate::{from_json, to_json_vec, StdError, StdResult}; #[test] fn sub_msg_result_serialization_works() { @@ -215,7 +215,7 @@ mod tests { events: vec![], }); assert_eq!( - &to_vec(&result).unwrap(), + &to_json_vec(&result).unwrap(), br#"{"ok":{"events":[],"data":null}}"# ); @@ -224,17 +224,17 @@ mod tests { events: vec![Event::new("wasm").add_attribute("fo", "ba")], }); assert_eq!( - &to_vec(&result).unwrap(), + &to_json_vec(&result).unwrap(), br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"# ); let result: SubMsgResult = SubMsgResult::Err("broken".to_string()); - assert_eq!(&to_vec(&result).unwrap(), b"{\"error\":\"broken\"}"); + assert_eq!(&to_json_vec(&result).unwrap(), b"{\"error\":\"broken\"}"); } #[test] fn sub_msg_result_deserialization_works() { - let result: SubMsgResult = from_slice(br#"{"ok":{"events":[],"data":null}}"#).unwrap(); + let result: SubMsgResult = from_json(br#"{"ok":{"events":[],"data":null}}"#).unwrap(); assert_eq!( result, SubMsgResult::Ok(SubMsgResponse { @@ -243,7 +243,7 @@ mod tests { }) ); - let result: SubMsgResult = from_slice( + let result: SubMsgResult = from_json( br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#).unwrap(); assert_eq!( result, @@ -253,16 +253,16 @@ mod tests { }) ); - let result: SubMsgResult = from_slice(br#"{"error":"broken"}"#).unwrap(); + let result: SubMsgResult = from_json(br#"{"error":"broken"}"#).unwrap(); assert_eq!(result, SubMsgResult::Err("broken".to_string())); // fails for additional attributes - let parse: StdResult = from_slice(br#"{"unrelated":321,"error":"broken"}"#); + let parse: StdResult = from_json(br#"{"unrelated":321,"error":"broken"}"#); match parse.unwrap_err() { StdError::ParseErr { .. } => {} err => panic!("Unexpected error: {err:?}"), } - let parse: StdResult = from_slice(br#"{"error":"broken","unrelated":321}"#); + let parse: StdResult = from_json(br#"{"error":"broken","unrelated":321}"#); match parse.unwrap_err() { StdError::ParseErr { .. } => {} err => panic!("Unexpected error: {err:?}"), diff --git a/packages/std/src/serde.rs b/packages/std/src/serde.rs index 5b258fd1f..23793698b 100644 --- a/packages/std/src/serde.rs +++ b/packages/std/src/serde.rs @@ -8,26 +8,62 @@ use serde::{de::DeserializeOwned, Serialize}; use crate::binary::Binary; use crate::errors::{StdError, StdResult}; +#[deprecated = "use from_json instead"] pub fn from_slice(value: &[u8]) -> StdResult { - serde_json_wasm::from_slice(value).map_err(|e| StdError::parse_err(type_name::(), e)) + from_json(value) } +#[deprecated = "use from_json instead"] pub fn from_binary(value: &Binary) -> StdResult { - from_slice(value.as_slice()) + from_json(value) } +/// Deserializes the given JSON bytes to a data structure. +/// +/// Errors if the input is not valid JSON or cannot be deserialized to the given type. +pub fn from_json(value: impl AsRef<[u8]>) -> StdResult { + serde_json_wasm::from_slice(value.as_ref()) + .map_err(|e| StdError::parse_err(type_name::(), e)) +} + +#[deprecated = "use to_json_vec instead"] pub fn to_vec(data: &T) -> StdResult> where T: Serialize + ?Sized, { - serde_json_wasm::to_vec(data).map_err(|e| StdError::serialize_err(type_name::(), e)) + to_json_vec(data) } +#[deprecated = "use to_json_binary instead"] pub fn to_binary(data: &T) -> StdResult where T: Serialize + ?Sized, { - to_vec(data).map(Binary) + to_json_binary(data) +} + +/// Serializes the given data structure as a JSON byte vector. +pub fn to_json_vec(data: &T) -> StdResult> +where + T: Serialize + ?Sized, +{ + serde_json_wasm::to_vec(data).map_err(|e| StdError::serialize_err(type_name::(), e)) +} + +/// Serializes the given data structure as a JSON string. +pub fn to_json_string(data: &T) -> StdResult +where + T: Serialize + ?Sized, +{ + serde_json_wasm::to_string(data).map_err(|e| StdError::serialize_err(type_name::(), e)) +} + +/// Serializes the given data structure as JSON bytes. +pub fn to_json_binary(data: &T) -> StdResult +where + T: Serialize + ?Sized, +{ + to_json_vec(data).map(Binary) } #[cfg(test)] @@ -51,9 +87,9 @@ mod tests { } #[test] - fn to_vec_works() { + fn to_json_vec_works() { let msg = SomeMsg::Refund {}; - let serialized = to_vec(&msg).unwrap(); + let serialized = to_json_vec(&msg).unwrap(); assert_eq!(serialized, br#"{"refund":{}}"#); let msg = SomeMsg::ReleaseAll { @@ -62,7 +98,7 @@ mod tests { time: 9007199254740999, // Number.MAX_SAFE_INTEGER + 7 karma: -17, }; - let serialized = String::from_utf8(to_vec(&msg).unwrap()).unwrap(); + let serialized = String::from_utf8(to_json_vec(&msg).unwrap()).unwrap(); assert_eq!( serialized, r#"{"release_all":{"image":"foo","amount":42,"time":9007199254740999,"karma":-17}}"# @@ -70,50 +106,55 @@ mod tests { } #[test] - fn from_slice_works() { - let deserialized: SomeMsg = from_slice(br#"{"refund":{}}"#).unwrap(); + fn from_json_works() { + let deserialized: SomeMsg = from_json(br#"{"refund":{}}"#).unwrap(); assert_eq!(deserialized, SomeMsg::Refund {}); - let deserialized: SomeMsg = from_slice( + let expected = SomeMsg::ReleaseAll { + image: "foo".to_string(), + amount: 42, + time: 18446744073709551615, + karma: -17, + }; + // &[u8] + let deserialized: SomeMsg = from_json( br#"{"release_all":{"image":"foo","amount":42,"time":18446744073709551615,"karma":-17}}"#, ) .unwrap(); - assert_eq!( - deserialized, - SomeMsg::ReleaseAll { - image: "foo".to_string(), - amount: 42, - time: 18446744073709551615, - karma: -17 - } - ); + assert_eq!(deserialized, expected); + + // &str + let deserialized: SomeMsg = from_json( + r#"{"release_all":{"image":"foo","amount":42,"time":18446744073709551615,"karma":-17}}"#, + ) + .unwrap(); + assert_eq!(deserialized, expected); } #[test] - fn from_slice_or_binary() { + fn from_json_or_binary() { let msg = SomeMsg::Refund {}; - let serialized: Binary = to_binary(&msg).unwrap(); + let serialized: Binary = to_json_binary(&msg).unwrap(); - let parse_binary: SomeMsg = from_binary(&serialized).unwrap(); + let parse_binary: SomeMsg = from_json(&serialized).unwrap(); assert_eq!(parse_binary, msg); - let parse_slice: SomeMsg = from_slice(&serialized).unwrap(); + let parse_slice: SomeMsg = from_json(serialized.as_slice()).unwrap(); assert_eq!(parse_slice, msg); } #[test] - fn to_vec_works_for_special_chars() { + fn to_json_vec_works_for_special_chars() { let msg = SomeMsg::Cowsay { text: "foo\"bar\\\"bla".to_string(), }; - let serialized = String::from_utf8(to_vec(&msg).unwrap()).unwrap(); + let serialized = String::from_utf8(to_json_vec(&msg).unwrap()).unwrap(); assert_eq!(serialized, r#"{"cowsay":{"text":"foo\"bar\\\"bla"}}"#); } #[test] - fn from_slice_works_for_special_chars() { - let deserialized: SomeMsg = - from_slice(br#"{"cowsay":{"text":"foo\"bar\\\"bla"}}"#).unwrap(); + fn from_json_works_for_special_chars() { + let deserialized: SomeMsg = from_json(br#"{"cowsay":{"text":"foo\"bar\\\"bla"}}"#).unwrap(); assert_eq!( deserialized, SomeMsg::Cowsay { @@ -121,4 +162,23 @@ mod tests { } ); } + + #[test] + fn to_json_string_works() { + let msg = SomeMsg::Refund {}; + let serialized = to_json_string(&msg).unwrap(); + assert_eq!(serialized, r#"{"refund":{}}"#); + + let msg = SomeMsg::ReleaseAll { + image: "foo".to_string(), + amount: 42, + time: 9007199254740999, // Number.MAX_SAFE_INTEGER + 7 + karma: -17, + }; + let serialized = to_json_string(&msg).unwrap(); + assert_eq!( + serialized, + r#"{"release_all":{"image":"foo","amount":42,"time":9007199254740999,"karma":-17}}"# + ); + } } diff --git a/packages/std/src/stdack.rs b/packages/std/src/stdack.rs index e933257a0..64c711ef5 100644 --- a/packages/std/src/stdack.rs +++ b/packages/std/src/stdack.rs @@ -2,7 +2,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::binary::Binary; -use crate::to_binary; +use crate::to_json_binary; /// This is a standard IBC acknowledgement type. IBC application are free /// to use any acknowledgement format they want. However, for compatibility @@ -94,7 +94,7 @@ impl StdAck { // We need a non-failing StdAck -> Binary conversion to allow using StdAck in // `impl Into` arguments. // Pretty sure this cannot fail. If that changes we can create a non-failing implementation here. - to_binary(&self).unwrap() + to_json_binary(&self).unwrap() } pub fn unwrap(self) -> Binary { diff --git a/packages/std/src/testing/assertions.rs b/packages/std/src/testing/assertions.rs index d74736007..fa3ab6c7f 100644 --- a/packages/std/src/testing/assertions.rs +++ b/packages/std/src/testing/assertions.rs @@ -82,6 +82,7 @@ pub fn assert_hash_works_impl(left: T, right: T, panic_msg } // Check clone + #[allow(clippy::redundant_clone)] let clone = left.clone(); if left != clone { do_panic("assertion failed: `left == left.clone()`", panic_msg); diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index f7cec5430..c49f85338 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -1,10 +1,12 @@ use alloc::collections::BTreeMap; +use bech32::{encode, ToBase32, Variant}; use core::marker::PhantomData; #[cfg(feature = "cosmwasm_1_3")] use core::ops::Bound; use serde::de::DeserializeOwned; #[cfg(feature = "stargate")] use serde::Serialize; +use sha2::{Digest, Sha256}; #[cfg(feature = "cosmwasm_1_3")] use std::collections::BTreeSet; use std::collections::HashMap; @@ -36,7 +38,7 @@ use crate::query::{ #[cfg(feature = "cosmwasm_1_3")] use crate::query::{DelegatorWithdrawAddressResponse, DistributionQuery}; use crate::results::{ContractResult, Empty, SystemResult}; -use crate::serde::{from_slice, to_binary}; +use crate::serde::{from_json, to_json_binary}; use crate::storage::MemoryStorage; use crate::timestamp::Timestamp; use crate::traits::{Api, Querier, QuerierResult}; @@ -108,20 +110,26 @@ const CANONICAL_LENGTH: usize = 90; // n = 45 const SHUFFLES_ENCODE: usize = 10; const SHUFFLES_DECODE: usize = 2; -// MockPrecompiles zero pads all human addresses to make them fit the canonical_length +/// Default prefix used when creating Bech32 encoded address. +const BECH32_PREFIX: &str = "cosmwasm"; + +// MockApi zero pads all human addresses to make them fit the canonical_length // it trims off zeros for the reverse operation. -// not really smart, but allows us to see a difference (and consistent length for canonical adddresses) +// not really smart, but allows us to see a difference (and consistent length for canonical addresses) #[derive(Copy, Clone)] pub struct MockApi { /// Length of canonical addresses created with this API. Contracts should not make any assumptions /// what this value is. canonical_length: usize, + /// Prefix used for creating addresses in Bech32 encoding. + bech32_prefix: &'static str, } impl Default for MockApi { fn default() -> Self { MockApi { canonical_length: CANONICAL_LENGTH, + bech32_prefix: BECH32_PREFIX, } } } @@ -154,7 +162,7 @@ impl Api for MockApi { )); } - // mimicks formats like hex or bech32 where different casings are valid for one address + // mimics formats like hex or bech32 where different casings are valid for one address let normalized = input.to_lowercase(); let mut out = Vec::from(normalized); @@ -250,6 +258,55 @@ impl Api for MockApi { } } +impl MockApi { + /// Returns [MockApi] with Bech32 prefix set to provided value. + /// + /// Bech32 prefix must not be empty. + /// + /// # Example + /// + /// ``` + /// # use cosmwasm_std::Addr; + /// # use cosmwasm_std::testing::MockApi; + /// # + /// let mock_api = MockApi::default().with_prefix("juno"); + /// let addr = mock_api.addr_make("creator"); + /// + /// assert_eq!(addr.to_string(), "juno1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqsksmtyp"); + /// ``` + pub fn with_prefix(mut self, prefix: &'static str) -> Self { + self.bech32_prefix = prefix; + self + } + + /// Returns an address built from provided input string. + /// + /// # Example + /// + /// ``` + /// # use cosmwasm_std::Addr; + /// # use cosmwasm_std::testing::MockApi; + /// # + /// let mock_api = MockApi::default(); + /// let addr = mock_api.addr_make("creator"); + /// + /// assert_eq!(addr.to_string(), "cosmwasm1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqs8s7vcp"); + /// ``` + /// + /// # Panics + /// + /// This function panics when generating a valid address is not possible, + /// especially when Bech32 prefix set in function [with_prefix](Self::with_prefix) is empty. + /// + pub fn addr_make(&self, input: &str) -> Addr { + let digest = Sha256::digest(input).to_vec(); + match encode(self.bech32_prefix, digest.to_base32(), Variant::Bech32) { + Ok(address) => Addr::unchecked(address), + Err(reason) => panic!("Generating address failed with reason: {reason}"), + } + } +} + /// Returns a default enviroment with height, time, chain_id, and contract address /// You can submit as is to most contracts, or modify height/time if you want to /// test for expiration. @@ -366,7 +423,7 @@ pub fn mock_ibc_packet_recv( ) -> StdResult { Ok(IbcPacketReceiveMsg::new( IbcPacket { - data: to_binary(data)?, + data: to_json_binary(data)?, src: IbcEndpoint { port_id: "their-port".to_string(), channel_id: "channel-1234".to_string(), @@ -393,7 +450,7 @@ pub fn mock_ibc_packet_recv( #[cfg(feature = "stargate")] fn mock_ibc_packet(my_channel_id: &str, data: &impl Serialize) -> StdResult { Ok(IbcPacket { - data: to_binary(data)?, + data: to_json_binary(data)?, src: IbcEndpoint { port_id: "their-port".to_string(), channel_id: my_channel_id.into(), @@ -568,7 +625,7 @@ impl Default for MockQuerier { impl Querier for MockQuerier { fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { - let request: QueryRequest = match from_slice(bin_request) { + let request: QueryRequest = match from_json(bin_request) { Ok(v) => v, Err(e) => { return SystemResult::Err(SystemError::InvalidRequest { @@ -724,7 +781,7 @@ impl BankQuerier { denom: denom.to_string(), }, }; - to_binary(&bank_res).into() + to_json_binary(&bank_res).into() } BankQuery::Balance { address, denom } => { // proper error on not found, serialize result on found @@ -739,14 +796,14 @@ impl BankQuerier { denom: denom.to_string(), }, }; - to_binary(&bank_res).into() + to_json_binary(&bank_res).into() } BankQuery::AllBalances { address } => { // proper error on not found, serialize result on found let bank_res = AllBalanceResponse { amount: self.balances.get(address).cloned().unwrap_or_default(), }; - to_binary(&bank_res).into() + to_json_binary(&bank_res).into() } #[cfg(feature = "cosmwasm_1_3")] BankQuery::DenomMetadata { denom } => { @@ -756,7 +813,7 @@ impl BankQuerier { let metadata_res = DenomMetadataResponse { metadata: m.clone(), }; - to_binary(&metadata_res).into() + to_json_binary(&metadata_res).into() } None => return SystemResult::Err(SystemError::Unknown {}), } @@ -799,7 +856,7 @@ impl BankQuerier { }; let metadata_res = AllDenomMetadataResponse { metadata, next_key }; - to_binary(&metadata_res).into() + to_json_binary(&metadata_res).into() } }; // system result is always ok in the mock implementation @@ -844,7 +901,7 @@ impl IbcQuerier { }) .cloned(); let res = ChannelResponse { channel }; - to_binary(&res).into() + to_json_binary(&res).into() } IbcQuery::ListChannels { port_id } => { let channels = self @@ -857,13 +914,13 @@ impl IbcQuerier { .cloned() .collect(); let res = ListChannelsResponse { channels }; - to_binary(&res).into() + to_json_binary(&res).into() } IbcQuery::PortId {} => { let res = PortIdResponse { port_id: self.port_id.clone(), }; - to_binary(&res).into() + to_json_binary(&res).into() } }; // system result is always ok in the mock implementation @@ -895,13 +952,13 @@ impl StakingQuerier { let res = BondedDenomResponse { denom: self.denom.clone(), }; - to_binary(&res).into() + to_json_binary(&res).into() } StakingQuery::AllValidators {} => { let res = AllValidatorsResponse { validators: self.validators.clone(), }; - to_binary(&res).into() + to_json_binary(&res).into() } StakingQuery::Validator { address } => { let validator: Option = self @@ -910,7 +967,7 @@ impl StakingQuerier { .find(|validator| validator.address == *address) .cloned(); let res = ValidatorResponse { validator }; - to_binary(&res).into() + to_json_binary(&res).into() } StakingQuery::AllDelegations { delegator } => { let delegations: Vec<_> = self @@ -921,7 +978,7 @@ impl StakingQuerier { .map(|d| d.into()) .collect(); let res = AllDelegationsResponse { delegations }; - to_binary(&res).into() + to_json_binary(&res).into() } StakingQuery::Delegation { delegator, @@ -934,7 +991,7 @@ impl StakingQuerier { let res = DelegationResponse { delegation: delegation.cloned(), }; - to_binary(&res).into() + to_json_binary(&res).into() } }; // system result is always ok in the mock implementation @@ -1022,7 +1079,7 @@ impl DistributionQuerier { .unwrap_or(delegator_address), ), }; - to_binary(&res).into() + to_json_binary(&res).into() } #[cfg(feature = "cosmwasm_1_4")] DistributionQuery::DelegationRewards { @@ -1037,7 +1094,7 @@ impl DistributionQuerier { .cloned() .unwrap_or_default(), }; - to_binary(&res).into() + to_json_binary(&res).into() } #[cfg(feature = "cosmwasm_1_4")] DistributionQuery::DelegationTotalRewards { delegator_address } => { @@ -1063,7 +1120,7 @@ impl DistributionQuerier { .collect(), rewards: validator_rewards, }; - to_binary(&res).into() + to_json_binary(&res).into() } #[cfg(feature = "cosmwasm_1_4")] DistributionQuery::DelegatorValidators { delegator_address } => { @@ -1074,7 +1131,7 @@ impl DistributionQuerier { .map(|set| set.iter().cloned().collect()) .unwrap_or_default(), }; - to_binary(&res).into() + to_json_binary(&res).into() } }; // system result is always ok in the mock implementation @@ -1116,7 +1173,7 @@ mod tests { use super::*; #[cfg(feature = "cosmwasm_1_3")] use crate::DenomUnit; - use crate::{coin, coins, from_binary, to_binary, ContractInfoResponse, Response}; + use crate::{coin, coins, from_json, to_json_binary, ContractInfoResponse, Response}; #[cfg(feature = "staking")] use crate::{Decimal, Delegation}; use hex_literal::hex; @@ -1449,7 +1506,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: SupplyResponse = from_binary(&elf).unwrap(); + let res: SupplyResponse = from_json(elf).unwrap(); assert_eq!(res.amount, coin(444, "ELF")); let fly = bank @@ -1458,7 +1515,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: SupplyResponse = from_binary(&fly).unwrap(); + let res: SupplyResponse = from_json(fly).unwrap(); assert_eq!(res.amount, coin(777, "FLY")); // if a denom does not exist, should return zero amount, instead of throwing an error @@ -1468,7 +1525,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: SupplyResponse = from_binary(&atom).unwrap(); + let res: SupplyResponse = from_json(atom).unwrap(); assert_eq!(res.amount, coin(0, "ATOM")); } @@ -1482,7 +1539,7 @@ mod tests { .query(&BankQuery::AllBalances { address: addr }) .unwrap() .unwrap(); - let res: AllBalanceResponse = from_binary(&all).unwrap(); + let res: AllBalanceResponse = from_json(all).unwrap(); assert_eq!(&res.amount, &balance); } @@ -1500,7 +1557,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&fly).unwrap(); + let res: BalanceResponse = from_json(fly).unwrap(); assert_eq!(res.amount, coin(777, "FLY")); // missing denom @@ -1511,7 +1568,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&miss).unwrap(); + let res: BalanceResponse = from_json(miss).unwrap(); assert_eq!(res.amount, coin(0, "MISS")); } @@ -1528,7 +1585,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: AllBalanceResponse = from_binary(&all).unwrap(); + let res: AllBalanceResponse = from_json(all).unwrap(); assert_eq!(res.amount, vec![]); // any denom on balances on empty account is empty coin @@ -1539,7 +1596,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&miss).unwrap(); + let res: BalanceResponse = from_json(miss).unwrap(); assert_eq!(res.amount, coin(0, "ELF")); } @@ -1577,7 +1634,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: AllDenomMetadataResponse = from_binary(&res).unwrap(); + let res: AllDenomMetadataResponse = from_json(res).unwrap(); assert_eq!(res.metadata.len(), 10); assert!(res.next_key.is_some()); @@ -1592,7 +1649,7 @@ mod tests { }) .unwrap() .unwrap(); - let res2: AllDenomMetadataResponse = from_binary(&res2).unwrap(); + let res2: AllDenomMetadataResponse = from_json(res2).unwrap(); assert_eq!(res2.metadata.len(), 10); assert_ne!(res.metadata.last(), res2.metadata.first()); // should have no overlap @@ -1611,7 +1668,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: AllDenomMetadataResponse = from_binary(&res).unwrap(); + let res: AllDenomMetadataResponse = from_json(res).unwrap(); assert_eq!(res.metadata.len(), 100); assert!(res.next_key.is_none(), "no more data should be available"); assert_eq!(res.metadata[0].symbol, "FOO99", "should have been reversed"); @@ -1626,7 +1683,7 @@ mod tests { }) .unwrap() .unwrap(); - let more_res: AllDenomMetadataResponse = from_binary(&more_res).unwrap(); + let more_res: AllDenomMetadataResponse = from_json(more_res).unwrap(); assert_eq!( more_res.metadata, res.metadata, "should be same as previous query" @@ -1644,7 +1701,7 @@ mod tests { }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegatorWithdrawAddressResponse = from_binary(&res).unwrap(); + let res: DelegatorWithdrawAddressResponse = from_json(res).unwrap(); assert_eq!(res.withdraw_address, "withdraw0"); let query = DistributionQuery::DelegatorWithdrawAddress { @@ -1652,7 +1709,7 @@ mod tests { }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegatorWithdrawAddressResponse = from_binary(&res).unwrap(); + let res: DelegatorWithdrawAddressResponse = from_json(res).unwrap(); assert_eq!(res.withdraw_address, "addr1"); } @@ -1667,7 +1724,7 @@ mod tests { }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegatorValidatorsResponse = from_binary(&res).unwrap(); + let res: DelegatorValidatorsResponse = from_json(res).unwrap(); assert_eq!(res.validators, ["valoper1", "valoper2"]); let query = DistributionQuery::DelegatorValidators { @@ -1675,7 +1732,7 @@ mod tests { }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegatorValidatorsResponse = from_binary(&res).unwrap(); + let res: DelegatorValidatorsResponse = from_json(res).unwrap(); assert_eq!(res.validators, ([] as [String; 0])); } @@ -1697,7 +1754,7 @@ mod tests { validator_address: "valoper0".to_string(), }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegationRewardsResponse = from_binary(&res).unwrap(); + let res: DelegationRewardsResponse = from_json(res).unwrap(); assert_eq!(res.rewards, valoper0_rewards); // delegator does not exist @@ -1706,7 +1763,7 @@ mod tests { validator_address: "valoper0".to_string(), }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegationRewardsResponse = from_binary(&res).unwrap(); + let res: DelegationRewardsResponse = from_json(res).unwrap(); assert_eq!(res.rewards.len(), 0); // validator does not exist @@ -1715,7 +1772,7 @@ mod tests { validator_address: "valopernonexistent".to_string(), }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegationRewardsResponse = from_binary(&res).unwrap(); + let res: DelegationRewardsResponse = from_json(res).unwrap(); assert_eq!(res.rewards.len(), 0); // add one more validator @@ -1727,7 +1784,7 @@ mod tests { delegator_address: "addr0".to_string(), }; let res = distribution.query(&query).unwrap().unwrap(); - let res: DelegationTotalRewardsResponse = from_binary(&res).unwrap(); + let res: DelegationTotalRewardsResponse = from_json(res).unwrap(); assert_eq!( res.rewards, vec![ @@ -1768,7 +1825,7 @@ mod tests { port_id: Some("my_port".to_string()), }; let raw = ibc.query(query).unwrap().unwrap(); - let chan: ChannelResponse = from_binary(&raw).unwrap(); + let chan: ChannelResponse = from_json(raw).unwrap(); assert_eq!(chan.channel, Some(chan1)); } @@ -1798,7 +1855,7 @@ mod tests { port_id: Some("myport".to_string()), }; let raw = ibc.query(query).unwrap().unwrap(); - let chan: ChannelResponse = from_binary(&raw).unwrap(); + let chan: ChannelResponse = from_json(raw).unwrap(); assert_eq!(chan.channel, Some(chan1)); } @@ -1816,7 +1873,7 @@ mod tests { port_id: None, }; let raw = ibc.query(query).unwrap().unwrap(); - let chan: ChannelResponse = from_binary(&raw).unwrap(); + let chan: ChannelResponse = from_json(raw).unwrap(); assert_eq!(chan.channel, None); } @@ -1833,7 +1890,7 @@ mod tests { port_id: Some("my_port".to_string()), }; let raw = ibc.query(query).unwrap().unwrap(); - let res: ListChannelsResponse = from_binary(&raw).unwrap(); + let res: ListChannelsResponse = from_json(raw).unwrap(); assert_eq!(res.channels, vec![chan1, chan2]); } @@ -1848,7 +1905,7 @@ mod tests { // query channels matching "myport" (should be none) let query = &IbcQuery::ListChannels { port_id: None }; let raw = ibc.query(query).unwrap().unwrap(); - let res: ListChannelsResponse = from_binary(&raw).unwrap(); + let res: ListChannelsResponse = from_json(raw).unwrap(); assert_eq!(res.channels, vec![]); } @@ -1862,7 +1919,7 @@ mod tests { // query channels matching "myport" (should be none) let query = &IbcQuery::PortId {}; let raw = ibc.query(query).unwrap().unwrap(); - let res: PortIdResponse = from_binary(&raw).unwrap(); + let res: PortIdResponse = from_json(raw).unwrap(); assert_eq!(res.port_id, "myport"); } @@ -1889,7 +1946,7 @@ mod tests { .query(&StakingQuery::AllValidators {}) .unwrap() .unwrap(); - let vals: AllValidatorsResponse = from_binary(&raw).unwrap(); + let vals: AllValidatorsResponse = from_json(raw).unwrap(); assert_eq!(vals.validators, vec![val1, val2]); } @@ -1920,7 +1977,7 @@ mod tests { .query(&StakingQuery::Validator { address: address1 }) .unwrap() .unwrap(); - let res: ValidatorResponse = from_binary(&raw).unwrap(); + let res: ValidatorResponse = from_json(raw).unwrap(); assert_eq!(res.validator, Some(val1)); // query 2 @@ -1928,7 +1985,7 @@ mod tests { .query(&StakingQuery::Validator { address: address2 }) .unwrap() .unwrap(); - let res: ValidatorResponse = from_binary(&raw).unwrap(); + let res: ValidatorResponse = from_json(raw).unwrap(); assert_eq!(res.validator, Some(val2)); // query non-existent @@ -1938,7 +1995,7 @@ mod tests { }) .unwrap() .unwrap(); - let res: ValidatorResponse = from_binary(&raw).unwrap(); + let res: ValidatorResponse = from_json(raw).unwrap(); assert_eq!(res.validator, None); } @@ -1954,7 +2011,7 @@ mod tests { }) .unwrap() .unwrap(); - let dels: AllDelegationsResponse = from_binary(&raw).unwrap(); + let dels: AllDelegationsResponse = from_json(raw).unwrap(); dels.delegations } @@ -1972,7 +2029,7 @@ mod tests { }) .unwrap() .unwrap(); - let dels: DelegationResponse = from_binary(&raw).unwrap(); + let dels: DelegationResponse = from_json(raw).unwrap(); dels.delegation } @@ -2137,14 +2194,14 @@ mod tests { if *contract_addr == constract1 { #[derive(Deserialize)] struct MyMsg {} - let _msg: MyMsg = match from_binary(msg) { + let _msg: MyMsg = match from_json(msg) { Ok(msg) => msg, Err(err) => { return SystemResult::Ok(ContractResult::Err(err.to_string())) } }; let response: Response = Response::new().set_data(b"good"); - SystemResult::Ok(ContractResult::Ok(to_binary(&response).unwrap())) + SystemResult::Ok(ContractResult::Ok(to_json_binary(&response).unwrap())) } else { SystemResult::Err(SystemError::NoSuchContract { addr: contract_addr.clone(), @@ -2160,7 +2217,7 @@ mod tests { pinned: false, ibc_port: None, }; - SystemResult::Ok(ContractResult::Ok(to_binary(&response).unwrap())) + SystemResult::Ok(ContractResult::Ok(to_json_binary(&response).unwrap())) } else { SystemResult::Err(SystemError::NoSuchContract { addr: contract_addr.clone(), @@ -2180,7 +2237,7 @@ mod tests { ) .unwrap(), }; - SystemResult::Ok(ContractResult::Ok(to_binary(&response).unwrap())) + SystemResult::Ok(ContractResult::Ok(to_json_binary(&response).unwrap())) } else { SystemResult::Err(SystemError::NoSuchCode { code_id }) } @@ -2271,4 +2328,31 @@ mod tests { assert_eq!(digit_sum(&[255, 1]), 256); } + + #[test] + fn making_an_address_works() { + let mock_api = MockApi::default(); + + assert_eq!( + mock_api.addr_make("creator").to_string(), + "cosmwasm1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqs8s7vcp", + ); + + assert_eq!( + mock_api.addr_make("").to_string(), + "cosmwasm1uwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2sly4xh9", + ); + + let mock_api = MockApi::default().with_prefix("juno"); + assert_eq!( + mock_api.addr_make("creator").to_string(), + "juno1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqsksmtyp", + ); + } + + #[test] + #[should_panic(expected = "Generating address failed with reason: invalid length")] + fn making_an_address_with_empty_prefix_should_panic() { + MockApi::default().with_prefix("").addr_make("creator"); + } } diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs index 618c57f64..8286b714d 100644 --- a/packages/std/src/traits.rs +++ b/packages/std/src/traits.rs @@ -28,7 +28,7 @@ use crate::query::{ DistributionQuery, }; use crate::results::{ContractResult, Empty, SystemResult}; -use crate::serde::{from_binary, to_binary, to_vec}; +use crate::serde::{from_json, to_json_binary, to_json_vec}; use crate::ContractInfoResponse; #[cfg(feature = "cosmwasm_1_3")] use crate::{DenomMetadata, PageRequest}; @@ -244,7 +244,7 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { /// one level. Only use this if you don't need to check the SystemError /// eg. If you don't differentiate between contract missing and contract returned error pub fn query(&self, request: &QueryRequest) -> StdResult { - let raw = to_vec(request).map_err(|serialize_err| { + let raw = to_json_vec(request).map_err(|serialize_err| { StdError::generic_err(format!("Serializing QueryRequest: {serialize_err}")) })?; match self.raw_query(&raw) { @@ -254,7 +254,7 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err( format!("Querier contract error: {contract_err}"), )), - SystemResult::Ok(ContractResult::Ok(value)) => from_binary(&value), + SystemResult::Ok(ContractResult::Ok(value)) => from_json(value), } } @@ -380,7 +380,7 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { ) -> StdResult { let request = WasmQuery::Smart { contract_addr: contract_addr.into(), - msg: to_binary(msg)?, + msg: to_json_binary(msg)?, } .into(); self.query(&request) @@ -406,7 +406,7 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { .into(); // we cannot use query, as it will try to parse the binary data, when we just want to return it, // so a bit of code copy here... - let raw = to_vec(&request).map_err(|serialize_err| { + let raw = to_json_vec(&request).map_err(|serialize_err| { StdError::generic_err(format!("Serializing QueryRequest: {serialize_err}")) })?; match self.raw_query(&raw) { @@ -504,7 +504,7 @@ mod tests { use super::*; use crate::testing::MockQuerier; - use crate::{coins, from_slice, Uint128}; + use crate::{coins, from_json, Uint128}; // this is a simple demo helper to prove we can use it fn demo_helper(_querier: &dyn Querier) -> u64 { @@ -537,10 +537,10 @@ mod tests { }); let raw = wrapper - .raw_query(&to_vec(&query).unwrap()) + .raw_query(&to_json_vec(&query).unwrap()) .unwrap() .unwrap(); - let balance: BalanceResponse = from_slice(&raw).unwrap(); + let balance: BalanceResponse = from_json(raw).unwrap(); assert_eq!(balance.amount.amount, Uint128::new(5)); } @@ -583,7 +583,7 @@ mod tests { if q == &(WasmQuery::ContractInfo { contract_addr: ACCT.to_string(), }) { - SystemResult::Ok(ContractResult::Ok(to_binary(&mock_resp()).unwrap())) + SystemResult::Ok(ContractResult::Ok(to_json_binary(&mock_resp()).unwrap())) } else { SystemResult::Err(crate::SystemError::NoSuchContract { addr: ACCT.to_string(), @@ -614,7 +614,7 @@ mod tests { if q == &(WasmQuery::ContractInfo { contract_addr: ACCT.to_string(), }) { - SystemResult::Ok(ContractResult::Ok(to_binary(&mock_resp()).unwrap())) + SystemResult::Ok(ContractResult::Ok(to_json_binary(&mock_resp()).unwrap())) } else { SystemResult::Err(crate::SystemError::NoSuchContract { addr: ACCT.to_string(), diff --git a/packages/storage/README.md b/packages/storage/README.md index 9309fa209..13320a543 100644 --- a/packages/storage/README.md +++ b/packages/storage/README.md @@ -1,3 +1,9 @@ +# WARNING: Unmaintained + +This crate is no longer maintained and should not be used anymore. For an +alternative, please check out +[cw-storage-plus](https://crates.io/crates/cw-storage-plus) + # cosmwasm-storage _Forked from [CosmWasm/cosmwasm-storage](https://github.com/CosmWasm/cosmwasm/tree/main/packages/storage)_ @@ -8,10 +14,14 @@ boilerplate. ## Contents -- [PrefixedStorage](#prefixed-storage) -- [TypedStorage](#typed-storage) -- [Bucket](#bucket) -- [Singleton](#singleton) +- [WARNING: Unmaintained](#warning-unmaintained) +- [cosmwasm-storage](#cosmwasm-storage) + - [Contents](#contents) + - [Prefixed Storage](#prefixed-storage) + - [Typed Storage](#typed-storage) + - [Bucket](#bucket) + - [Singleton](#singleton) + - [License](#license) ### Prefixed Storage diff --git a/packages/storage/src/bucket.rs b/packages/storage/src/bucket.rs index a870dfac4..81c7a65c3 100644 --- a/packages/storage/src/bucket.rs +++ b/packages/storage/src/bucket.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use cosmwasm_std::{ storage_keys::{to_length_prefixed, to_length_prefixed_nested}, - to_vec, StdError, StdResult, Storage, + to_json_vec, StdError, StdResult, Storage, }; #[cfg(feature = "iterator")] use cosmwasm_std::{Order, Record}; @@ -72,7 +72,7 @@ where /// save will serialize the model and store, returns an error on serialization issues pub fn save(&mut self, key: &[u8], data: &T) -> StdResult<()> { - set_with_prefix(self.storage, &self.prefix, key, &to_vec(data)?); + set_with_prefix(self.storage, &self.prefix, key, &to_json_vec(data)?); Ok(()) } @@ -399,7 +399,7 @@ mod tests { return Err(StdError::generic_err("Current age is negative").into()); } if data.age > 10 { - to_vec(&data)?; // Uses From to convert StdError to MyError + to_json_vec(&data)?; // Uses From to convert StdError to MyError } data.age += 1; Ok(data) diff --git a/packages/storage/src/singleton.rs b/packages/storage/src/singleton.rs index 3d5e5c956..41fbda7d6 100644 --- a/packages/storage/src/singleton.rs +++ b/packages/storage/src/singleton.rs @@ -1,7 +1,7 @@ use serde::{de::DeserializeOwned, ser::Serialize}; use std::marker::PhantomData; -use cosmwasm_std::{storage_keys::to_length_prefixed, to_vec, StdError, StdResult, Storage}; +use cosmwasm_std::{storage_keys::to_length_prefixed, to_json_vec, StdError, StdResult, Storage}; use crate::type_helpers::{may_deserialize, must_deserialize}; @@ -58,7 +58,7 @@ where /// save will serialize the model and store, returns an error on serialization issues pub fn save(&mut self, data: &T) -> StdResult<()> { - self.storage.set(&self.key, &to_vec(data)?); + self.storage.set(&self.key, &to_json_vec(data)?); Ok(()) } @@ -307,7 +307,7 @@ mod tests { return Err(StdError::generic_err("broken stuff").into()); // Uses Into to convert StdError to MyError } if c.max_tokens > 10 { - to_vec(&c)?; // Uses From to convert StdError to MyError + to_json_vec(&c)?; // Uses From to convert StdError to MyError } c.max_tokens += 20; Ok(c) diff --git a/packages/storage/src/type_helpers.rs b/packages/storage/src/type_helpers.rs index 233d2b146..98855de1b 100644 --- a/packages/storage/src/type_helpers.rs +++ b/packages/storage/src/type_helpers.rs @@ -36,7 +36,7 @@ pub(crate) fn deserialize_kv(kv: Record>) -> StdRes #[cfg(test)] mod tests { use super::*; - use cosmwasm_std::{to_vec, StdError}; + use cosmwasm_std::{to_json_vec, StdError}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -51,7 +51,7 @@ mod tests { name: "Maria".to_string(), age: 42, }; - let value = to_vec(&person).unwrap(); + let value = to_json_vec(&person).unwrap(); let may_parse: Option = may_deserialize(&Some(value)).unwrap(); assert_eq!(may_parse, Some(person)); @@ -69,7 +69,7 @@ mod tests { name: "Maria".to_string(), age: 42, }; - let value = to_vec(&person).unwrap(); + let value = to_json_vec(&person).unwrap(); let loaded = Some(value); let parsed: Person = must_deserialize(&loaded).unwrap(); diff --git a/packages/vm/Cargo.toml b/packages/vm/Cargo.toml index 200c109b6..1a251fb06 100644 --- a/packages/vm/Cargo.toml +++ b/packages/vm/Cargo.toml @@ -36,7 +36,7 @@ bench = false [dependencies] bytes = "1.4.0" # need a higher version than the one required by Wasmer for the Bytes -> Vec implementation -clru = "0.4.0" +clru = "0.6.1" crc32fast = "1.3.2" # Uses the path when built locally; uses the given version from crates.io when published cosmwasm-std = { path = "../std", version = "1.1.9+0.8.1", default-features = false } @@ -48,8 +48,8 @@ serde = { version = "1.0.103", default-features = false, features = ["derive", " serde_json = "1.0.40" sha2 = "0.10.3" thiserror = "1.0.26" -wasmer = { version = "=4.1.2", default-features = false, features = ["cranelift", "singlepass"] } -wasmer-middlewares = "=4.1.2" +wasmer = { version = "=4.2.2", default-features = false, features = ["cranelift", "singlepass"] } +wasmer-middlewares = "=4.2.2" # Dependencies that we do not use ourself. We add those entries # to bump the min version of them. diff --git a/packages/vm/benches/main.rs b/packages/vm/benches/main.rs index e0911e380..afefe5d3c 100644 --- a/packages/vm/benches/main.rs +++ b/packages/vm/benches/main.rs @@ -318,7 +318,6 @@ pub fn bench_instance_threads(c: &mut Criterion) { let checksum = random_checksum(); thread::spawn(move || { - let checksum = checksum; // Perform measurement internally let t = SystemTime::now(); black_box( diff --git a/packages/vm/examples/multi_threaded_cache.rs b/packages/vm/examples/multi_threaded_cache.rs index 7be56be40..fd8c145f2 100644 --- a/packages/vm/examples/multi_threaded_cache.rs +++ b/packages/vm/examples/multi_threaded_cache.rs @@ -51,7 +51,6 @@ pub fn main() { let cache = Arc::clone(&cache); threads.push(thread::spawn(move || { - let checksum = checksum; let mut instance = cache .get_instance(&checksum, mock_backend(&[]), DEFAULT_INSTANCE_OPTIONS) .unwrap(); diff --git a/packages/vm/src/cache.rs b/packages/vm/src/cache.rs index 28fa20d3b..071b77413 100644 --- a/packages/vm/src/cache.rs +++ b/packages/vm/src/cache.rs @@ -202,6 +202,7 @@ where pub fn save_wasm_unchecked(&self, wasm: &[u8]) -> VmResult { // We need a new engine for each Wasm -> module compilation due to the metering middleware. let compiling_engine = make_compiling_engine(None); + // This module cannot be executed directly as it was not created with the runtime engine let module = compile(&compiling_engine, wasm)?; let mut cache = self.inner.lock().unwrap(); @@ -291,11 +292,22 @@ where // Re-compile from original Wasm bytecode let wasm = self.load_wasm_with_path(&cache.wasm_path, checksum)?; cache.stats.misses = cache.stats.misses.saturating_add(1); - // Module will run with a different engine, so we can set memory limit to None - let engine = make_compiling_engine(None); - let module = compile(&engine, &wasm)?; - // Store into the fs cache too - let module_size = cache.fs_cache.store(checksum, &module)?; + { + // Module will run with a different engine, so we can set memory limit to None + let compiling_engine = make_compiling_engine(None); + // This module cannot be executed directly as it was not created with the runtime engine + let module = compile(&compiling_engine, &wasm)?; + cache.fs_cache.store(checksum, &module)?; + } + + // This time we'll hit the file-system cache. + let Some((module, module_size)) = cache.fs_cache.load(checksum, &cache.runtime_engine)? + else { + return Err(VmError::generic_err( + "Can't load module from file system cache after storing it to file system cache (pin)", + )); + }; + cache .pinned_memory_cache .store(checksum, module, module_size) @@ -377,11 +389,21 @@ where // stored the old module format. let wasm = self.load_wasm_with_path(&cache.wasm_path, checksum)?; cache.stats.misses = cache.stats.misses.saturating_add(1); - // Module will run with a different engine, so we can set memory limit to None - let engine = make_compiling_engine(None); - let module = compile(&engine, &wasm)?; - let module_size = cache.fs_cache.store(checksum, &module)?; + { + // Module will run with a different engine, so we can set memory limit to None + let compiling_engine = make_compiling_engine(None); + // This module cannot be executed directly as it was not created with the runtime engine + let module = compile(&compiling_engine, &wasm)?; + cache.fs_cache.store(checksum, &module)?; + } + // This time we'll hit the file-system cache. + let Some((module, module_size)) = cache.fs_cache.load(checksum, &cache.runtime_engine)? + else { + return Err(VmError::generic_err( + "Can't load module from file system cache after storing it to file system cache (get_module)", + )); + }; cache .memory_cache .store(checksum, module.clone(), module_size)?; @@ -532,6 +554,30 @@ mod tests { } } + /// Takes an instance and executes it + fn test_hackatom_instance_execution(instance: &mut Instance) + where + A: BackendApi + 'static, + S: Storage + 'static, + Q: Querier + 'static, + { + // instantiate + let info = mock_info("creator", &coins(1000, "earth")); + let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; + let response = call_instantiate::<_, _, _, Empty>(instance, &mock_env(), &info, msg) + .unwrap() + .unwrap(); + assert_eq!(response.messages.len(), 0); + + // execute + let info = mock_info("verifies", &coins(15, "earth")); + let msg = br#"{"release":{}}"#; + let response = call_execute::<_, _, _, Empty>(instance, &mock_env(), &info, msg) + .unwrap() + .unwrap(); + assert_eq!(response.messages.len(), 1); + } + #[test] fn new_base_dir_will_be_created() { let my_base_dir = TempDir::new() @@ -854,7 +900,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 1); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let res = @@ -873,7 +919,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 1); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let res = @@ -894,7 +940,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 2); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let res = @@ -919,7 +965,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 1); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let response = @@ -947,7 +993,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 1); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let response = @@ -977,7 +1023,7 @@ mod tests { assert_eq!(cache.stats().hits_fs_cache, 2); assert_eq!(cache.stats().misses, 0); - // init + // instantiate let info = mock_info("creator", &coins(1000, "earth")); let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#; let response = @@ -996,6 +1042,27 @@ mod tests { } } + #[test] + fn call_execute_on_recompiled_contract() { + let options = make_testing_options(); + let cache = unsafe { Cache::new(options.clone()).unwrap() }; + let checksum = cache.save_wasm(CONTRACT).unwrap(); + + // Remove compiled module from disk + remove_dir_all(options.base_dir.join(CACHE_DIR).join(MODULES_DIR)).unwrap(); + + // Recompiles the Wasm (miss on all caches) + let backend = mock_backend(&[]); + let mut instance = cache + .get_instance(&checksum, backend, TESTING_OPTIONS) + .unwrap(); + assert_eq!(cache.stats().hits_pinned_memory_cache, 0); + assert_eq!(cache.stats().hits_memory_cache, 0); + assert_eq!(cache.stats().hits_fs_cache, 0); + assert_eq!(cache.stats().misses, 1); + test_hackatom_instance_execution(&mut instance); + } + #[test] fn use_multiple_cached_instances_of_same_contract() { let cache = unsafe { Cache::new(make_testing_options()).unwrap() }; @@ -1005,7 +1072,7 @@ mod tests { let backend1 = mock_backend(&[]); let backend2 = mock_backend(&[]); - // init instance 1 + // instantiate instance 1 let mut instance = cache .get_instance(&checksum, backend1, TESTING_OPTIONS) .unwrap(); @@ -1017,7 +1084,7 @@ mod tests { assert_eq!(msgs.len(), 0); let backend1 = instance.recycle().unwrap(); - // init instance 2 + // instantiate instance 2 let mut instance = cache .get_instance(&checksum, backend2, TESTING_OPTIONS) .unwrap(); @@ -1230,13 +1297,14 @@ mod tests { // check not pinned let backend = mock_backend(&[]); - let _instance = cache + let mut instance = cache .get_instance(&checksum, backend, TESTING_OPTIONS) .unwrap(); assert_eq!(cache.stats().hits_pinned_memory_cache, 0); assert_eq!(cache.stats().hits_memory_cache, 0); assert_eq!(cache.stats().hits_fs_cache, 1); assert_eq!(cache.stats().misses, 0); + test_hackatom_instance_execution(&mut instance); // first pin hits file system cache cache.pin(&checksum).unwrap(); @@ -1254,26 +1322,28 @@ mod tests { // check pinned let backend = mock_backend(&[]); - let _instance = cache + let mut instance = cache .get_instance(&checksum, backend, TESTING_OPTIONS) .unwrap(); assert_eq!(cache.stats().hits_pinned_memory_cache, 1); assert_eq!(cache.stats().hits_memory_cache, 0); assert_eq!(cache.stats().hits_fs_cache, 2); assert_eq!(cache.stats().misses, 0); + test_hackatom_instance_execution(&mut instance); // unpin cache.unpin(&checksum).unwrap(); // verify unpinned let backend = mock_backend(&[]); - let _instance = cache + let mut instance = cache .get_instance(&checksum, backend, TESTING_OPTIONS) .unwrap(); assert_eq!(cache.stats().hits_pinned_memory_cache, 1); assert_eq!(cache.stats().hits_memory_cache, 1); assert_eq!(cache.stats().hits_fs_cache, 2); assert_eq!(cache.stats().misses, 0); + test_hackatom_instance_execution(&mut instance); // unpin again has no effect cache.unpin(&checksum).unwrap(); @@ -1302,13 +1372,14 @@ mod tests { // After the compilation in pin, the module can be used from pinned memory cache let backend = mock_backend(&[]); - let _ = cache + let mut instance = cache .get_instance(&checksum, backend, TESTING_OPTIONS) .unwrap(); assert_eq!(cache.stats().hits_pinned_memory_cache, 1); assert_eq!(cache.stats().hits_memory_cache, 0); assert_eq!(cache.stats().hits_fs_cache, 0); assert_eq!(cache.stats().misses, 1); + test_hackatom_instance_execution(&mut instance); } #[test] diff --git a/packages/vm/src/calls.rs b/packages/vm/src/calls.rs index dc24a11a6..5828b0210 100644 --- a/packages/vm/src/calls.rs +++ b/packages/vm/src/calls.rs @@ -590,11 +590,15 @@ where #[cfg(test)] mod tests { use super::*; - use crate::testing::{mock_env, mock_info, mock_instance}; - use cosmwasm_std::{coins, Empty}; + use crate::testing::{ + mock_env, mock_info, mock_instance, mock_instance_with_options, MockInstanceOptions, + }; + use cosmwasm_std::{coins, from_json, to_json_string, Empty}; + use sha2::{Digest, Sha256}; static CONTRACT: &[u8] = include_bytes!("../testdata/hackatom.wasm"); static CYBERPUNK: &[u8] = include_bytes!("../testdata/cyberpunk.wasm"); + static FLOATY2: &[u8] = include_bytes!("../testdata/floaty_2.0.wasm"); #[test] fn call_instantiate_works() { @@ -734,6 +738,86 @@ mod tests { assert_eq!(query_response.as_slice(), b"{\"verifier\":\"verifies\"}"); } + #[test] + fn float_instrs_are_deterministic() { + #[derive(Debug, serde::Serialize, serde::Deserialize)] + #[serde(rename_all = "snake_case")] + pub enum Value { + U32(u32), + U64(u64), + F32(u32), + F64(u64), + } + + let mut instance = mock_instance_with_options( + FLOATY2, + MockInstanceOptions { + gas_limit: u64::MAX, + memory_limit: None, + ..Default::default() + }, + ); + + // init + let info = mock_info("creator", &[]); + call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{}"#) + .unwrap() + .unwrap(); + + // query instructions + let msg = br#"{"instructions":{}}"#; + let contract_result = call_query(&mut instance, &mock_env(), msg) + .unwrap() + .unwrap(); + let instructions: Vec = from_json(contract_result).unwrap(); + // little sanity check + assert_eq!(instructions.len(), 70); + + const RUNS_PER_INSTRUCTION: u64 = 150; + let mut hasher = Sha256::new(); + for instr in &instructions { + for seed in 0..RUNS_PER_INSTRUCTION { + // query some input values for the instruction + let args: Vec = from_json( + &call_query( + &mut instance, + &mock_env(), + format!( + r#"{{"random_args_for":{{ "instruction": "{instr}", "seed": {seed}}}}}"# + ) + .as_bytes(), + ) + .unwrap() + .unwrap(), + ) + .unwrap(); + + // build the run message + let args = to_json_string(&args).unwrap(); + let msg: String = format!( + r#"{{"run":{{ + "instruction": "{instr}", + "args": {args} + }}}}"# + ); + // run the instruction + // this might throw a runtime error (e.g. if the instruction traps) + let result = match call_query(&mut instance, &mock_env(), msg.as_bytes()) { + Ok(ContractResult::Ok(r)) => format!("{:?}", from_json::(&r).unwrap()), + Err(VmError::RuntimeErr { msg, .. }) => msg, + e => panic!("unexpected error: {e:?}"), + }; + // add the result to the hash + hasher.update(format!("{instr}{seed}{result}").as_bytes()); + } + } + let hash = Digest::finalize(hasher); + assert_eq!( + hex::encode(hash.as_slice()), + "95f70fa6451176ab04a9594417a047a1e4d8e2ff809609b8f81099496bee2393" + ); + } + #[cfg(feature = "stargate")] mod ibc { use super::*; diff --git a/packages/vm/src/environment.rs b/packages/vm/src/environment.rs index 121759ddf..f78dffbc0 100644 --- a/packages/vm/src/environment.rs +++ b/packages/vm/src/environment.rs @@ -372,7 +372,7 @@ impl Environment { /// Creates a MemoryView. /// This must be short living and not be used after the memory was grown. - pub fn memory<'a>(&self, store: &'a mut impl AsStoreMut) -> MemoryView<'a> { + pub fn memory<'a>(&self, store: &'a impl AsStoreMut) -> MemoryView<'a> { self.memory .as_ref() .expect("Memory is not set. This is a bug in the lifecycle.") @@ -458,7 +458,7 @@ mod tests { use crate::testing::{MockApi, MockQuerier, MockStorage}; use crate::wasm_backend::{compile, make_compiling_engine}; use cosmwasm_std::{ - coins, from_binary, to_vec, AllBalanceResponse, BankQuery, Empty, QueryRequest, + coins, from_json, to_json_vec, AllBalanceResponse, BankQuery, Empty, QueryRequest, }; use wasmer::{imports, Function, Instance as WasmerInstance, Store}; @@ -898,13 +898,13 @@ mod tests { address: INIT_ADDR.to_string(), }); let (result, _gas_info) = - querier.query_raw(&to_vec(&req).unwrap(), DEFAULT_QUERY_GAS_LIMIT); + querier.query_raw(&to_json_vec(&req).unwrap(), DEFAULT_QUERY_GAS_LIMIT); Ok(result.unwrap()) }) .unwrap() .unwrap() .unwrap(); - let balance: AllBalanceResponse = from_binary(&res).unwrap(); + let balance: AllBalanceResponse = from_json(res).unwrap(); assert_eq!(balance.amount, coins(INIT_AMOUNT, INIT_DENOM)); } diff --git a/packages/vm/src/imports.rs b/packages/vm/src/imports.rs index 87b722f71..f18410911 100644 --- a/packages/vm/src/imports.rs +++ b/packages/vm/src/imports.rs @@ -46,11 +46,11 @@ const MAX_LENGTH_QUERY_CHAIN_REQUEST: usize = 64 * KI; /// Length of a serialized Ed25519 signature const MAX_LENGTH_ED25519_SIGNATURE: usize = 64; /// Max length of a Ed25519 message in bytes. -/// This is an arbitrary value, for performance / memory contraints. If you need to verify larger +/// This is an arbitrary value, for performance / memory constraints. If you need to verify larger /// messages, let us know. const MAX_LENGTH_ED25519_MESSAGE: usize = 128 * 1024; /// Max number of batch Ed25519 messages / signatures / public_keys. -/// This is an arbitrary value, for performance / memory contraints. If you need to batch-verify a +/// This is an arbitrary value, for performance / memory constraints. If you need to batch-verify a /// larger number of signatures, let us know. const MAX_COUNT_ED25519_BATCH: usize = 256; /// Max count of a inputs for sha1. @@ -83,7 +83,7 @@ pub fn do_db_read VmResult { let (data, mut store) = env.data_and_store_mut(); - let key = read_region(&data.memory(&mut store), key_ptr, MAX_LENGTH_DB_KEY)?; + let key = read_region(&data.memory(&store), key_ptr, MAX_LENGTH_DB_KEY)?; let (result, gas_info) = data.with_storage_from_context::<_, _>(|store| Ok(store.get(&key)))?; process_gas_info(data, &mut store, gas_info)?; @@ -108,8 +108,25 @@ pub fn do_db_write VmError { + if let VmError::CommunicationErr { + source: CommunicationError::RegionLengthTooBig { length, max_length }, + .. + } = e + { + VmError::generic_err(format!( + "{kind} too big. Tried to write {length} bytes to storage, limit is {max_length}." + )) + } else { + e + } + } + + let key = read_region(&data.memory(&store), key_ptr, MAX_LENGTH_DB_KEY) + .map_err(|e| convert_error(e, "Key"))?; + let value = read_region(&data.memory(&store), value_ptr, MAX_LENGTH_DB_VALUE) + .map_err(|e| convert_error(e, "Value"))?; let (result, gas_info) = data.with_storage_from_context::<_, _>(|store| Ok(store.set(&key, &value)))?; @@ -129,7 +146,7 @@ pub fn do_db_remove(|store| Ok(store.remove(&key)))?; @@ -145,11 +162,7 @@ pub fn do_addr_validate VmResult { let (data, mut store) = env.data_and_store_mut(); - let source_data = read_region( - &data.memory(&mut store), - source_ptr, - MAX_LENGTH_HUMAN_ADDRESS, - )?; + let source_data = read_region(&data.memory(&store), source_ptr, MAX_LENGTH_HUMAN_ADDRESS)?; if source_data.is_empty() { return write_to_contract(data, &mut store, b"Input is empty"); } @@ -193,11 +206,7 @@ pub fn do_addr_canonicalize VmResult { let (data, mut store) = env.data_and_store_mut(); - let source_data = read_region( - &data.memory(&mut store), - source_ptr, - MAX_LENGTH_HUMAN_ADDRESS, - )?; + let source_data = read_region(&data.memory(&store), source_ptr, MAX_LENGTH_HUMAN_ADDRESS)?; if source_data.is_empty() { return write_to_contract(data, &mut store, b"Input is empty"); } @@ -211,11 +220,7 @@ pub fn do_addr_canonicalize { - write_region( - &data.memory(&mut store), - destination_ptr, - canonical.as_slice(), - )?; + write_region(&data.memory(&store), destination_ptr, canonical.as_slice())?; Ok(0) } Err(BackendError::UserErr { msg, .. }) => { @@ -233,7 +238,7 @@ pub fn do_addr_humanize { - write_region(&data.memory(&mut store), destination_ptr, human.as_bytes())?; + write_region(&data.memory(&store), destination_ptr, human.as_bytes())?; Ok(0) } Err(BackendError::UserErr { msg, .. }) => { @@ -266,9 +271,9 @@ pub fn do_secp256k1_verify VmResult { let (data, mut store) = env.data_and_store_mut(); - let hash = read_region(&data.memory(&mut store), hash_ptr, MESSAGE_HASH_MAX_LEN)?; - let signature = read_region(&data.memory(&mut store), signature_ptr, ECDSA_SIGNATURE_LEN)?; - let pubkey = read_region(&data.memory(&mut store), pubkey_ptr, ECDSA_PUBKEY_MAX_LEN)?; + let hash = read_region(&data.memory(&store), hash_ptr, MESSAGE_HASH_MAX_LEN)?; + let signature = read_region(&data.memory(&store), signature_ptr, ECDSA_SIGNATURE_LEN)?; + let pubkey = read_region(&data.memory(&store), pubkey_ptr, ECDSA_PUBKEY_MAX_LEN)?; let gas_info = GasInfo::with_cost(data.gas_config.secp256k1_verify_cost); process_gas_info(data, &mut store, gas_info)?; @@ -307,8 +312,8 @@ pub fn do_secp256k1_recover_pubkey< ) -> VmResult { let (data, mut store) = env.data_and_store_mut(); - let hash = read_region(&data.memory(&mut store), hash_ptr, MESSAGE_HASH_MAX_LEN)?; - let signature = read_region(&data.memory(&mut store), signature_ptr, ECDSA_SIGNATURE_LEN)?; + let hash = read_region(&data.memory(&store), hash_ptr, MESSAGE_HASH_MAX_LEN)?; + let signature = read_region(&data.memory(&store), signature_ptr, ECDSA_SIGNATURE_LEN)?; let recover_param: u8 = match recover_param.try_into() { Ok(rp) => rp, Err(_) => return Ok((CryptoError::invalid_recovery_param().code() as u64) << 32), @@ -350,16 +355,16 @@ pub fn do_ed25519_verify>, message_ptr: u32, ) -> VmResult<()> { - let (data, mut store) = env.data_and_store_mut(); + let (data, store) = env.data_and_store_mut(); - let message_data = read_region(&data.memory(&mut store), message_ptr, MAX_LENGTH_ABORT)?; + let message_data = read_region(&data.memory(&store), message_ptr, MAX_LENGTH_ABORT)?; let msg = String::from_utf8_lossy(&message_data); Err(VmError::aborted(msg)) } @@ -532,7 +537,7 @@ pub fn do_query_chain VmResult { let (data, mut store) = env.data_and_store_mut(); - let start = maybe_read_region(&data.memory(&mut store), start_ptr, MAX_LENGTH_DB_KEY)?; - let end = maybe_read_region(&data.memory(&mut store), end_ptr, MAX_LENGTH_DB_KEY)?; + let start = maybe_read_region(&data.memory(&store), start_ptr, MAX_LENGTH_DB_KEY)?; + let end = maybe_read_region(&data.memory(&store), end_ptr, MAX_LENGTH_DB_KEY)?; let order: Order = order .try_into() .map_err(|_| CommunicationError::invalid_order(order))?; @@ -668,8 +673,8 @@ fn to_low_half(data: u32) -> u64 { mod tests { use super::*; use cosmwasm_std::{ - coins, from_binary, AllBalanceResponse, BankQuery, Binary, Empty, QueryRequest, - SystemError, SystemResult, WasmQuery, + coins, from_json, AllBalanceResponse, BankQuery, Binary, Empty, QueryRequest, SystemError, + SystemResult, WasmQuery, }; use hex_literal::hex; use std::ptr::NonNull; @@ -770,7 +775,7 @@ mod tests { fn leave_default_data( fe_mut: &mut FunctionEnvMut>, ) { - let (env, mut _store) = fe_mut.data_and_store_mut(); + let (env, _store) = fe_mut.data_and_store_mut(); // create some mock data let mut storage = MockStorage::new(); @@ -791,12 +796,12 @@ mod tests { .call_function1(&mut store, "allocate", &[(data.len() as u32).into()]) .unwrap(); let region_ptr = ref_to_u32(&result).unwrap(); - write_region(&env.memory(&mut store), region_ptr, data).expect("error writing"); + write_region(&env.memory(&store), region_ptr, data).expect("error writing"); region_ptr } fn create_empty( - wasmer_instance: &mut WasmerInstance, + wasmer_instance: &WasmerInstance, fe_mut: &mut FunctionEnvMut>, capacity: u32, ) -> u32 { @@ -816,9 +821,9 @@ mod tests { fe_mut: &mut FunctionEnvMut>, region_ptr: u32, ) -> Vec { - let (env, mut store) = fe_mut.data_and_store_mut(); + let (env, store) = fe_mut.data_and_store_mut(); - read_region(&env.memory(&mut store), region_ptr, 5000).unwrap() + read_region(&env.memory(&store), region_ptr, 5000).unwrap() } #[test] @@ -944,25 +949,14 @@ mod tests { let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); - let key_ptr = write_data(&mut fe_mut, &vec![4u8; 300 * 1024]); + const KEY_SIZE: usize = 300 * 1024; + let key_ptr = write_data(&mut fe_mut, &vec![4u8; KEY_SIZE]); let value_ptr = write_data(&mut fe_mut, b"new value"); leave_default_data(&mut fe_mut); let result = do_db_write(fe_mut, key_ptr, value_ptr); - match result.unwrap_err() { - VmError::CommunicationErr { - source: - CommunicationError::RegionLengthTooBig { - length, max_length, .. - }, - .. - } => { - assert_eq!(length, 300 * 1024); - assert_eq!(max_length, MAX_LENGTH_DB_KEY); - } - err => panic!("unexpected error: {err:?}"), - }; + assert_eq!(result.unwrap_err().to_string(), format!("Generic error: Key too big. Tried to write {KEY_SIZE} bytes to storage, limit is {MAX_LENGTH_DB_KEY}.")); } #[test] @@ -971,25 +965,14 @@ mod tests { let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); + const VAL_SIZE: usize = 300 * 1024; let key_ptr = write_data(&mut fe_mut, b"new storage key"); - let value_ptr = write_data(&mut fe_mut, &vec![5u8; 300 * 1024]); + let value_ptr = write_data(&mut fe_mut, &vec![5u8; VAL_SIZE]); leave_default_data(&mut fe_mut); let result = do_db_write(fe_mut, key_ptr, value_ptr); - match result.unwrap_err() { - VmError::CommunicationErr { - source: - CommunicationError::RegionLengthTooBig { - length, max_length, .. - }, - .. - } => { - assert_eq!(length, 300 * 1024); - assert_eq!(max_length, MAX_LENGTH_DB_VALUE); - } - err => panic!("unexpected error: {err:?}"), - }; + assert_eq!(result.unwrap_err().to_string(), format!("Generic error: Value too big. Tried to write {VAL_SIZE} bytes to storage, limit is {MAX_LENGTH_DB_VALUE}.")); } #[test] @@ -1060,7 +1043,7 @@ mod tests { leave_default_data(&mut fe_mut); - // Note: right now we cannot differnetiate between an existent and a non-existent key + // Note: right now we cannot differentiate between an existent and a non-existent key do_db_remove(fe_mut.as_mut(), key_ptr).unwrap(); let value = fe_mut @@ -1215,12 +1198,12 @@ mod tests { #[test] fn do_addr_canonicalize_works() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let api = MockApi::default(); let source_ptr = write_data(&mut fe_mut, b"foo"); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, api.canonical_length() as u32); + let dest_ptr = create_empty(&instance, &mut fe_mut, api.canonical_length() as u32); leave_default_data(&mut fe_mut); @@ -1234,13 +1217,13 @@ mod tests { #[test] fn do_addr_canonicalize_reports_invalid_input_back_to_contract() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr1 = write_data(&mut fe_mut, b"fo\x80o"); // invalid UTF-8 (fo�o) let source_ptr2 = write_data(&mut fe_mut, b""); // empty let source_ptr3 = write_data(&mut fe_mut, b"addressexceedingaddressspacesuperlongreallylongiamensuringthatitislongerthaneverything"); // too long - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 70); + let dest_ptr = create_empty(&instance, &mut fe_mut, 70); leave_default_data(&mut fe_mut); @@ -1266,11 +1249,11 @@ mod tests { #[test] fn do_addr_canonicalize_fails_for_broken_backend() { let api = MockApi::new_failing("Temporarily unavailable"); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, b"foo"); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 7); + let dest_ptr = create_empty(&instance, &mut fe_mut, 7); leave_default_data(&mut fe_mut); @@ -1287,11 +1270,11 @@ mod tests { #[test] fn do_addr_canonicalize_fails_for_large_inputs() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, &[61; 333]); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 8); + let dest_ptr = create_empty(&instance, &mut fe_mut, 8); leave_default_data(&mut fe_mut); @@ -1314,11 +1297,11 @@ mod tests { #[test] fn do_addr_canonicalize_fails_for_small_destination_region() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, b"foo"); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 7); + let dest_ptr = create_empty(&instance, &mut fe_mut, 7); leave_default_data(&mut fe_mut); @@ -1338,13 +1321,13 @@ mod tests { #[test] fn do_addr_humanize_works() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let api = MockApi::default(); let source_data = vec![0x22; api.canonical_length()]; let source_ptr = write_data(&mut fe_mut, &source_data); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 70); + let dest_ptr = create_empty(&instance, &mut fe_mut, 70); leave_default_data(&mut fe_mut); @@ -1356,11 +1339,11 @@ mod tests { #[test] fn do_addr_humanize_reports_invalid_input_back_to_contract() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, b"foo"); // too short - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 70); + let dest_ptr = create_empty(&instance, &mut fe_mut, 70); leave_default_data(&mut fe_mut); @@ -1373,11 +1356,11 @@ mod tests { #[test] fn do_addr_humanize_fails_for_broken_backend() { let api = MockApi::new_failing("Temporarily unavailable"); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, b"foo\0\0\0\0\0"); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 70); + let dest_ptr = create_empty(&instance, &mut fe_mut, 70); leave_default_data(&mut fe_mut); @@ -1394,11 +1377,11 @@ mod tests { #[test] fn do_addr_humanize_fails_for_input_too_long() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let source_ptr = write_data(&mut fe_mut, &[61; 65]); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 70); + let dest_ptr = create_empty(&instance, &mut fe_mut, 70); leave_default_data(&mut fe_mut); @@ -1421,13 +1404,13 @@ mod tests { #[test] fn do_addr_humanize_fails_for_destination_region_too_small() { let api = MockApi::default(); - let (fe, mut store, mut instance) = make_instance(api); + let (fe, mut store, instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let api = MockApi::default(); let source_data = vec![0x22; api.canonical_length()]; let source_ptr = write_data(&mut fe_mut, &source_data); - let dest_ptr = create_empty(&mut instance, &mut fe_mut, 2); + let dest_ptr = create_empty(&instance, &mut fe_mut, 2); leave_default_data(&mut fe_mut); @@ -1447,7 +1430,7 @@ mod tests { #[test] fn do_secp256k1_verify_works() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1466,7 +1449,7 @@ mod tests { #[test] fn do_secp256k1_verify_wrong_hash_verify_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let mut hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1487,7 +1470,7 @@ mod tests { #[test] fn do_secp256k1_verify_larger_hash_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let mut hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1512,7 +1495,7 @@ mod tests { #[test] fn do_secp256k1_verify_shorter_hash_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let mut hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1533,7 +1516,7 @@ mod tests { #[test] fn do_secp256k1_verify_wrong_sig_verify_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1554,7 +1537,7 @@ mod tests { #[test] fn do_secp256k1_verify_larger_sig_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1579,7 +1562,7 @@ mod tests { #[test] fn do_secp256k1_verify_shorter_sig_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1600,7 +1583,7 @@ mod tests { #[test] fn do_secp256k1_verify_wrong_pubkey_format_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1621,7 +1604,7 @@ mod tests { #[test] fn do_secp256k1_verify_wrong_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1642,7 +1625,7 @@ mod tests { #[test] fn do_secp256k1_verify_larger_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1667,7 +1650,7 @@ mod tests { #[test] fn do_secp256k1_verify_shorter_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1688,7 +1671,7 @@ mod tests { #[test] fn do_secp256k1_verify_empty_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = hex::decode(ECDSA_HASH_HEX).unwrap(); @@ -1707,7 +1690,7 @@ mod tests { #[test] fn do_secp256k1_verify_wrong_data_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let hash = vec![0x22; MESSAGE_HASH_MAX_LEN]; @@ -1726,7 +1709,7 @@ mod tests { #[test] fn do_secp256k1_recover_pubkey_works() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); // https://gist.github.com/webmaster128/130b628d83621a33579751846699ed15 @@ -1749,7 +1732,7 @@ mod tests { #[test] fn do_ed25519_verify_works() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1768,7 +1751,7 @@ mod tests { #[test] fn do_ed25519_verify_wrong_msg_verify_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let mut msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1789,7 +1772,7 @@ mod tests { #[test] fn do_ed25519_verify_larger_msg_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let mut msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1814,7 +1797,7 @@ mod tests { #[test] fn do_ed25519_verify_wrong_sig_verify_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1835,7 +1818,7 @@ mod tests { #[test] fn do_ed25519_verify_larger_sig_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1860,7 +1843,7 @@ mod tests { #[test] fn do_ed25519_verify_shorter_sig_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1881,7 +1864,7 @@ mod tests { #[test] fn do_ed25519_verify_wrong_pubkey_verify_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1902,7 +1885,7 @@ mod tests { #[test] fn do_ed25519_verify_larger_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1927,7 +1910,7 @@ mod tests { #[test] fn do_ed25519_verify_shorter_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1948,7 +1931,7 @@ mod tests { #[test] fn do_ed25519_verify_empty_pubkey_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = hex::decode(EDDSA_MSG_HEX).unwrap(); @@ -1967,7 +1950,7 @@ mod tests { #[test] fn do_ed25519_verify_wrong_data_fails() { let api = MockApi::default(); - let (fe, mut store, mut _instance) = make_instance(api); + let (fe, mut store, _instance) = make_instance(api); let mut fe_mut = fe.into_mut(&mut store); let msg = vec![0x22; MESSAGE_HASH_MAX_LEN]; @@ -1992,7 +1975,7 @@ mod tests { let request: QueryRequest = QueryRequest::Bank(BankQuery::AllBalances { address: INIT_ADDR.to_string(), }); - let request_data = cosmwasm_std::to_vec(&request).unwrap(); + let request_data = cosmwasm_std::to_json_vec(&request).unwrap(); let request_ptr = write_data(&mut fe_mut, &request_data); leave_default_data(&mut fe_mut); @@ -2000,11 +1983,10 @@ mod tests { let response_ptr = do_query_chain(fe_mut.as_mut(), request_ptr).unwrap(); let response = force_read(&mut fe_mut, response_ptr); - let query_result: cosmwasm_std::QuerierResult = - cosmwasm_std::from_slice(&response).unwrap(); + let query_result: cosmwasm_std::QuerierResult = cosmwasm_std::from_json(response).unwrap(); let query_result_inner = query_result.unwrap(); let query_result_inner_inner = query_result_inner.unwrap(); - let parsed_again: AllBalanceResponse = from_binary(&query_result_inner_inner).unwrap(); + let parsed_again: AllBalanceResponse = from_json(query_result_inner_inner).unwrap(); assert_eq!(parsed_again.amount, coins(INIT_AMOUNT, INIT_DENOM)); } @@ -2022,8 +2004,7 @@ mod tests { let response_ptr = do_query_chain(fe_mut.as_mut(), request_ptr).unwrap(); let response = force_read(&mut fe_mut, response_ptr); - let query_result: cosmwasm_std::QuerierResult = - cosmwasm_std::from_slice(&response).unwrap(); + let query_result: cosmwasm_std::QuerierResult = cosmwasm_std::from_json(response).unwrap(); match query_result { SystemResult::Ok(_) => panic!("This must not succeed"), SystemResult::Err(SystemError::InvalidRequest { request: err, .. }) => { @@ -2043,7 +2024,7 @@ mod tests { contract_addr: String::from("non-existent"), msg: Binary::from(b"{}" as &[u8]), }); - let request_data = cosmwasm_std::to_vec(&request).unwrap(); + let request_data = cosmwasm_std::to_json_vec(&request).unwrap(); let request_ptr = write_data(&mut fe_mut, &request_data); leave_default_data(&mut fe_mut); @@ -2051,8 +2032,7 @@ mod tests { let response_ptr = do_query_chain(fe_mut.as_mut(), request_ptr).unwrap(); let response = force_read(&mut fe_mut, response_ptr); - let query_result: cosmwasm_std::QuerierResult = - cosmwasm_std::from_slice(&response).unwrap(); + let query_result: cosmwasm_std::QuerierResult = cosmwasm_std::from_json(response).unwrap(); match query_result { SystemResult::Ok(_) => panic!("This must not succeed"), SystemResult::Err(SystemError::NoSuchContract { addr }) => { diff --git a/packages/vm/src/instance.rs b/packages/vm/src/instance.rs index a616ee63a..0da0ef950 100644 --- a/packages/vm/src/instance.rs +++ b/packages/vm/src/instance.rs @@ -350,7 +350,7 @@ where /// Returns the features required by this contract. /// /// This is not needed for production because we can do static analysis - /// on the Wasm file before instatiation to obtain this information. It's + /// on the Wasm file before instantiation to obtain this information. It's /// only kept because it can be handy for integration testing. pub fn required_capabilities(&self) -> HashSet { required_capabilities_from_module(self._inner.module()) @@ -362,9 +362,9 @@ where /// (https://github.com/WebAssembly/design/issues/1300#issuecomment-573867836). pub fn memory_pages(&mut self) -> usize { let mut fe_mut = self.fe.clone().into_mut(&mut self.store); - let (env, mut store) = fe_mut.data_and_store_mut(); + let (env, store) = fe_mut.data_and_store_mut(); - env.memory(&mut store).size().0 as _ + env.memory(&store).size().0 as _ } /// Returns the currently remaining gas. @@ -449,17 +449,17 @@ where /// Copies all data described by the Region at the given pointer from Wasm to the caller. pub(crate) fn read_memory(&mut self, region_ptr: u32, max_length: usize) -> VmResult> { let mut fe_mut = self.fe.clone().into_mut(&mut self.store); - let (env, mut store) = fe_mut.data_and_store_mut(); + let (env, store) = fe_mut.data_and_store_mut(); - read_region(&env.memory(&mut store), region_ptr, max_length) + read_region(&env.memory(&store), region_ptr, max_length) } /// Copies data to the memory region that was created before using allocate. pub(crate) fn write_memory(&mut self, region_ptr: u32, data: &[u8]) -> VmResult<()> { let mut fe_mut = self.fe.clone().into_mut(&mut self.store); - let (env, mut store) = fe_mut.data_and_store_mut(); + let (env, store) = fe_mut.data_and_store_mut(); - write_region(&env.memory(&mut store), region_ptr, data)?; + write_region(&env.memory(&store), region_ptr, data)?; Ok(()) } @@ -524,8 +524,7 @@ mod tests { mock_instance_with_options, MockInstanceOptions, }; use cosmwasm_std::{ - coin, coins, from_binary, AllBalanceResponse, BalanceResponse, BankQuery, Empty, - QueryRequest, + coin, coins, from_json, AllBalanceResponse, BalanceResponse, BankQuery, Empty, QueryRequest, }; use wasmer::{FunctionEnv, FunctionEnvMut}; @@ -980,7 +979,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let BalanceResponse { amount } = from_binary(&response).unwrap(); + let BalanceResponse { amount } = from_json(response).unwrap(); assert_eq!(amount.amount.u128(), 8000); assert_eq!(amount.denom, "silver"); Ok(()) @@ -1001,7 +1000,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let AllBalanceResponse { amount } = from_binary(&response).unwrap(); + let AllBalanceResponse { amount } = from_json(response).unwrap(); assert_eq!(amount.len(), 2); assert_eq!(amount[0].amount.u128(), 10000); assert_eq!(amount[0].denom, "gold"); @@ -1013,7 +1012,7 @@ mod tests { .unwrap(); } - /// This is needed for writing intagration tests in which the balance of a contract changes over time + /// This is needed for writing integration tests in which the balance of a contract changes over time. #[test] fn with_querier_allows_updating_balances() { let rich_addr = String::from("foobar"); @@ -1036,7 +1035,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let BalanceResponse { amount } = from_binary(&response).unwrap(); + let BalanceResponse { amount } = from_json(response).unwrap(); assert_eq!(amount.amount.u128(), 500); Ok(()) }) @@ -1065,7 +1064,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let BalanceResponse { amount } = from_binary(&response).unwrap(); + let BalanceResponse { amount } = from_json(response).unwrap(); assert_eq!(amount.amount.u128(), 8000); Ok(()) }) diff --git a/packages/vm/src/modules/file_system_cache.rs b/packages/vm/src/modules/file_system_cache.rs index 12c84a0ae..9008c038b 100644 --- a/packages/vm/src/modules/file_system_cache.rs +++ b/packages/vm/src/modules/file_system_cache.rs @@ -49,7 +49,10 @@ use crate::modules::current_wasmer_module_version; /// - **v7**:
/// New version because of Wasmer 2.3.0 -> 4 upgrade. /// This internally changes how rkyv is used for module serialization, making compatibility unlikely. -const MODULE_SERIALIZATION_VERSION: &str = "v7"; +/// - **v8**:
+/// New version because of Wasmer 4.1.2 -> 4.2.2 upgrade. +/// Module compatibility between Wasmer versions is not guaranteed. +const MODULE_SERIALIZATION_VERSION: &str = "v8"; /// Representation of a directory that contains compiled Wasm artifacts. pub struct FileSystemCache { @@ -118,6 +121,14 @@ impl FileSystemCache { self.unchecked_modules = unchecked; } + /// Returns the path to the serialized module with the given checksum. + fn module_file(&self, checksum: &Checksum) -> PathBuf { + let mut path = self.modules_path.clone(); + path.push(checksum.to_hex()); + path.set_extension("module"); + path + } + /// Loads a serialized module from the file system and returns a module (i.e. artifact + store), /// along with the size of the serialized module. pub fn load( @@ -125,8 +136,7 @@ impl FileSystemCache { checksum: &Checksum, engine: &impl AsEngineRef, ) -> VmResult> { - let filename = checksum.to_hex(); - let file_path = self.modules_path.join(filename); + let file_path = self.module_file(checksum); let result = if self.unchecked_modules { unsafe { Module::deserialize_from_file_unchecked(engine, &file_path) } @@ -155,8 +165,7 @@ impl FileSystemCache { mkdir_p(&self.modules_path) .map_err(|_e| VmError::cache_err("Error creating modules directory"))?; - let filename = checksum.to_hex(); - let path = self.modules_path.join(filename); + let path = self.module_file(checksum); module .serialize_to_file(&path) .map_err(|e| VmError::cache_err(format!("Error writing module to disk: {e}")))?; @@ -168,8 +177,7 @@ impl FileSystemCache { /// /// Returns true if the file existed and false if the file did not exist. pub fn remove(&mut self, checksum: &Checksum) -> VmResult { - let filename = checksum.to_hex(); - let file_path = self.modules_path.join(filename); + let file_path = self.module_file(checksum); if file_path.exists() { fs::remove_file(file_path) @@ -287,7 +295,7 @@ mod tests { cache.store(&checksum, &module).unwrap(); let mut globber = glob::glob(&format!( - "{}/v7-wasmer4/**/{}", + "{}/v8-wasmer5/**/{}.module", tmp_dir.path().to_string_lossy(), checksum )) @@ -365,9 +373,9 @@ mod tests { assert_eq!( p.as_os_str(), if cfg!(windows) { - "modules\\v7-wasmer17\\x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" + "modules\\v8-wasmer17\\x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" } else { - "modules/v7-wasmer17/x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" + "modules/v8-wasmer17/x86_64-nintendo-fuchsia-gnu-coff-01E9F9FE" } ); } diff --git a/packages/vm/src/modules/versioning.rs b/packages/vm/src/modules/versioning.rs index 1911852b3..f48d7198b 100644 --- a/packages/vm/src/modules/versioning.rs +++ b/packages/vm/src/modules/versioning.rs @@ -51,6 +51,6 @@ mod tests { #[test] fn current_wasmer_module_version_works() { let version = current_wasmer_module_version(); - assert_eq!(version, 4); + assert_eq!(version, 5); } } diff --git a/packages/vm/src/testing/querier.rs b/packages/vm/src/testing/querier.rs index e35217862..af43432a3 100644 --- a/packages/vm/src/testing/querier.rs +++ b/packages/vm/src/testing/querier.rs @@ -2,7 +2,7 @@ use serde::de::DeserializeOwned; use cosmwasm_std::testing::{MockQuerier as StdMockQuerier, MockQuerierCustomHandlerResult}; use cosmwasm_std::{ - to_binary, to_vec, Binary, Coin, ContractResult, CustomQuery, Empty, Querier as _, + to_json_binary, to_json_vec, Binary, Coin, ContractResult, CustomQuery, Empty, Querier as _, QueryRequest, SystemError, SystemResult, }; @@ -73,7 +73,7 @@ impl Querier for MockQuerier { GAS_COST_QUERY_FLAT + (GAS_COST_QUERY_REQUEST_MULTIPLIER * (bin_request.len() as u64)) + (GAS_COST_QUERY_RESPONSE_MULTIPLIER - * (to_binary(&response).unwrap().len() as u64)), + * (to_json_binary(&response).unwrap().len() as u64)), ); // In a production implementation, this should stop the query execution in the middle of the computation. @@ -94,7 +94,7 @@ impl MockQuerier { gas_limit: u64, ) -> BackendResult>> { // encode the request, then call raw_query - let request_binary = match to_vec(request) { + let request_binary = match to_json_vec(request) { Ok(raw) => raw, Err(err) => { let gas_info = GasInfo::with_externally_used(err.to_string().len() as u64); @@ -114,7 +114,7 @@ impl MockQuerier { #[cfg(test)] mod tests { use super::*; - use cosmwasm_std::{coin, from_binary, AllBalanceResponse, BalanceResponse, BankQuery, Empty}; + use cosmwasm_std::{coin, from_json, AllBalanceResponse, BalanceResponse, BankQuery, Empty}; const DEFAULT_QUERY_GAS_LIMIT: u64 = 300_000; @@ -148,7 +148,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let res: AllBalanceResponse = from_binary(&all).unwrap(); + let res: AllBalanceResponse = from_json(all).unwrap(); assert_eq!(&res.amount, &balance); } @@ -172,7 +172,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&fly).unwrap(); + let res: BalanceResponse = from_json(fly).unwrap(); assert_eq!(res.amount, coin(777, "FLY")); // missing denom @@ -189,7 +189,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&miss).unwrap(); + let res: BalanceResponse = from_json(miss).unwrap(); assert_eq!(res.amount, coin(0, "MISS")); } @@ -212,7 +212,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let res: AllBalanceResponse = from_binary(&all).unwrap(); + let res: AllBalanceResponse = from_json(all).unwrap(); assert_eq!(res.amount, vec![]); // any denom on balances on empty account is empty coin @@ -229,7 +229,7 @@ mod tests { .unwrap() .unwrap() .unwrap(); - let res: BalanceResponse = from_binary(&miss).unwrap(); + let res: BalanceResponse = from_json(miss).unwrap(); assert_eq!(res.amount, coin(0, "ELF")); } } diff --git a/packages/vm/src/wasm_backend/compile.rs b/packages/vm/src/wasm_backend/compile.rs index 38fd4966d..d06a1ae05 100644 --- a/packages/vm/src/wasm_backend/compile.rs +++ b/packages/vm/src/wasm_backend/compile.rs @@ -16,9 +16,8 @@ mod tests { static CONTRACT: &[u8] = include_bytes!("../../testdata/floaty.wasm"); #[test] - fn contract_with_floats_fails_check() { + fn contract_with_floats_passes_check() { let engine = make_compiling_engine(None); - let err = compile(&engine, CONTRACT).unwrap_err(); - assert!(err.to_string().contains("Float operator detected:")); + assert!(compile(&engine, CONTRACT).is_ok()); } } diff --git a/packages/vm/src/wasm_backend/engine.rs b/packages/vm/src/wasm_backend/engine.rs index 0761e5968..25eceee93 100644 --- a/packages/vm/src/wasm_backend/engine.rs +++ b/packages/vm/src/wasm_backend/engine.rs @@ -54,6 +54,7 @@ pub fn make_compiling_engine(memory_limit: Option) -> Engine { #[cfg(not(feature = "cranelift"))] let mut compiler = Singlepass::default(); + compiler.canonicalize_nans(true); compiler.push_middleware(deterministic); compiler.push_middleware(metering); let mut engine = Engine::from(compiler); diff --git a/packages/vm/src/wasm_backend/gatekeeper.rs b/packages/vm/src/wasm_backend/gatekeeper.rs index 42cbc4b3f..f3646326a 100644 --- a/packages/vm/src/wasm_backend/gatekeeper.rs +++ b/packages/vm/src/wasm_backend/gatekeeper.rs @@ -57,7 +57,7 @@ impl Gatekeeper { impl Default for Gatekeeper { fn default() -> Self { Self::new(GatekeeperConfig { - allow_floats: false, + allow_floats: true, allow_feature_bulk_memory_operations: false, allow_feature_reference_types: false, allow_feature_simd: false, @@ -727,7 +727,7 @@ mod tests { } #[test] - fn parser_floats_are_not_supported() { + fn parser_floats_are_supported() { let wasm = wat::parse_str( r#" (module @@ -744,10 +744,7 @@ mod tests { compiler.push_middleware(deterministic); let store = Store::new(compiler); let result = Module::new(&store, wasm); - assert!(result - .unwrap_err() - .to_string() - .contains("Float operator detected:")); + assert!(result.is_ok()); } #[test] diff --git a/packages/vm/testdata/floaty_2.0.wasm b/packages/vm/testdata/floaty_2.0.wasm new file mode 100755 index 000000000..3dd07a913 Binary files /dev/null and b/packages/vm/testdata/floaty_2.0.wasm differ