From 4857fd2bf46b6af79e837f764905535496ce2a1b Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 19 Jun 2023 11:24:33 +0200 Subject: [PATCH 001/113] Highlight Sign-extension operations in Gatekeeper --- packages/vm/src/wasm_backend/gatekeeper.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/vm/src/wasm_backend/gatekeeper.rs b/packages/vm/src/wasm_backend/gatekeeper.rs index 897a500beb..0fad6d301c 100644 --- a/packages/vm/src/wasm_backend/gatekeeper.rs +++ b/packages/vm/src/wasm_backend/gatekeeper.rs @@ -199,13 +199,17 @@ impl FunctionMiddleware for FunctionGatekeeper { | Operator::I64Rotl | Operator::I64Rotr | Operator::I32WrapI64 + // Those are part of the MVP + // https://github.com/bytecodealliance/wasm-tools/blob/wasmparser-0.107.0/crates/wasmparser/src/lib.rs#L287-L288 + | Operator::I64ExtendI32S + | Operator::I64ExtendI32U + // Sign-extension + // https://github.com/bytecodealliance/wasm-tools/blob/wasmparser-0.107.0/crates/wasmparser/src/lib.rs#L307-L311 | Operator::I32Extend8S | Operator::I32Extend16S | Operator::I64Extend8S | Operator::I64Extend16S - | Operator::I64ExtendI32S - | Operator::I64Extend32S - | Operator::I64ExtendI32U => { + | Operator::I64Extend32S => { state.push_operator(operator); Ok(()) } From c51affcbc18a18a06b7d99f3a9edcd895374550f Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 4 May 2023 15:35:51 +0200 Subject: [PATCH 002/113] Add FromStr implementation for Coin, closes #1575 --- packages/std/src/coin.rs | 58 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/packages/std/src/coin.rs b/packages/std/src/coin.rs index af14644ae5..737c8a1c1e 100644 --- a/packages/std/src/coin.rs +++ b/packages/std/src/coin.rs @@ -1,8 +1,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::fmt; +use std::{fmt, str::FromStr}; -use crate::math::Uint128; +use crate::{math::Uint128, StdError}; #[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, JsonSchema)] pub struct Coin { @@ -25,6 +25,25 @@ impl fmt::Debug for Coin { } } +impl FromStr for Coin { + type Err = StdError; + + fn from_str(s: &str) -> Result { + let pos = s + .find(|c: char| !c.is_ascii_digit()) + .ok_or_else(|| StdError::generic_err("Parsing Coin: missing denominator"))?; + let (amount, denom) = s.split_at(pos); + + match amount.parse::() { + Ok(amount) => Ok(Coin { + amount: amount.into(), + denom: denom.to_string(), + }), + Err(e) => Err(StdError::generic_err(format!("Parsing Coin: {}", e))), + } + } +} + impl fmt::Display for Coin { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // We use the formatting without a space between amount and denom, @@ -178,4 +197,39 @@ mod tests { let coin = Coin::new(123, "ucosm"); assert_eq!(format!("{:?}", coin), r#"Coin { 123 "ucosm" }"#); } + + #[test] + fn parse_coin() { + let expected = Coin::new(123, "ucosm"); + assert_eq!(expected, "123ucosm".parse().unwrap()); + // leading zeroes should be ignored + assert_eq!(expected, "00123ucosm".parse().unwrap()); + // 0 amount parses correctly + assert_eq!(Coin::new(0, "ucosm"), "0ucosm".parse().unwrap()); + // ibc denom should work + let ibc_str = "11111ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"; + let ibc_coin = Coin::new( + 11111, + "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + ); + assert_eq!(ibc_coin, ibc_str.parse().unwrap()); + + // error cases + assert_eq!( + StdError::generic_err("Parsing Coin: missing denominator"), + Coin::from_str("123").unwrap_err() + ); + assert_eq!( + StdError::generic_err("Parsing Coin: cannot parse integer from empty string"), + Coin::from_str("ucosm").unwrap_err() + ); + assert_eq!( + StdError::generic_err("Parsing Coin: cannot parse integer from empty string"), + Coin::from_str("-123ucosm").unwrap_err() + ); + assert_eq!( + StdError::generic_err("Parsing Coin: number too large to fit in target type"), + Coin::from_str("340282366920938463463374607431768211456ucosm").unwrap_err() + ); + } } From 36353db4ca012e91cbef79683f9671b3931e7556 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 4 May 2023 16:12:11 +0200 Subject: [PATCH 003/113] Swap expected and actual value in assertions --- packages/std/src/coin.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/std/src/coin.rs b/packages/std/src/coin.rs index 737c8a1c1e..2a6fcf268c 100644 --- a/packages/std/src/coin.rs +++ b/packages/std/src/coin.rs @@ -201,35 +201,35 @@ mod tests { #[test] fn parse_coin() { let expected = Coin::new(123, "ucosm"); - assert_eq!(expected, "123ucosm".parse().unwrap()); + assert_eq!("123ucosm".parse::().unwrap(), expected); // leading zeroes should be ignored - assert_eq!(expected, "00123ucosm".parse().unwrap()); + assert_eq!("00123ucosm".parse::().unwrap(), expected); // 0 amount parses correctly - assert_eq!(Coin::new(0, "ucosm"), "0ucosm".parse().unwrap()); + assert_eq!("0ucosm".parse::().unwrap(), Coin::new(0, "ucosm")); // ibc denom should work let ibc_str = "11111ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"; let ibc_coin = Coin::new( 11111, "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", ); - assert_eq!(ibc_coin, ibc_str.parse().unwrap()); + assert_eq!(ibc_str.parse::().unwrap(), ibc_coin); // error cases assert_eq!( - StdError::generic_err("Parsing Coin: missing denominator"), - Coin::from_str("123").unwrap_err() + Coin::from_str("123").unwrap_err(), + StdError::generic_err("Parsing Coin: missing denominator") ); assert_eq!( - StdError::generic_err("Parsing Coin: cannot parse integer from empty string"), - Coin::from_str("ucosm").unwrap_err() + Coin::from_str("ucosm").unwrap_err(), + StdError::generic_err("Parsing Coin: cannot parse integer from empty string") ); assert_eq!( - StdError::generic_err("Parsing Coin: cannot parse integer from empty string"), - Coin::from_str("-123ucosm").unwrap_err() + Coin::from_str("-123ucosm").unwrap_err(), + StdError::generic_err("Parsing Coin: cannot parse integer from empty string") ); assert_eq!( - StdError::generic_err("Parsing Coin: number too large to fit in target type"), - Coin::from_str("340282366920938463463374607431768211456ucosm").unwrap_err() + Coin::from_str("340282366920938463463374607431768211456ucosm").unwrap_err(), + StdError::generic_err("Parsing Coin: number too large to fit in target type") ); } } From 168e0649c6354818c6927056b66042a327ce00f4 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 4 May 2023 17:17:09 +0200 Subject: [PATCH 004/113] Add special error type for Coin's FromStr --- packages/std/src/coin.rs | 33 ++++++++++++++-------------- packages/std/src/errors/mod.rs | 2 +- packages/std/src/errors/std_error.rs | 20 +++++++++++++++++ 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/packages/std/src/coin.rs b/packages/std/src/coin.rs index 2a6fcf268c..1422d65246 100644 --- a/packages/std/src/coin.rs +++ b/packages/std/src/coin.rs @@ -2,7 +2,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::{fmt, str::FromStr}; -use crate::{math::Uint128, StdError}; +use crate::{errors::CoinFromStrError, math::Uint128, StdError}; #[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, JsonSchema)] pub struct Coin { @@ -26,21 +26,18 @@ impl fmt::Debug for Coin { } impl FromStr for Coin { - type Err = StdError; + type Err = CoinFromStrError; fn from_str(s: &str) -> Result { let pos = s .find(|c: char| !c.is_ascii_digit()) - .ok_or_else(|| StdError::generic_err("Parsing Coin: missing denominator"))?; + .ok_or(CoinFromStrError::MissingDenom)?; let (amount, denom) = s.split_at(pos); - match amount.parse::() { - Ok(amount) => Ok(Coin { - amount: amount.into(), - denom: denom.to_string(), - }), - Err(e) => Err(StdError::generic_err(format!("Parsing Coin: {}", e))), - } + Ok(Coin { + amount: amount.parse::()?.into(), + denom: denom.to_string(), + }) } } @@ -217,19 +214,21 @@ mod tests { // error cases assert_eq!( Coin::from_str("123").unwrap_err(), - StdError::generic_err("Parsing Coin: missing denominator") + CoinFromStrError::MissingDenom ); assert_eq!( - Coin::from_str("ucosm").unwrap_err(), - StdError::generic_err("Parsing Coin: cannot parse integer from empty string") + Coin::from_str("ucosm").unwrap_err().to_string(), + "Invalid amount: cannot parse integer from empty string" ); assert_eq!( - Coin::from_str("-123ucosm").unwrap_err(), - StdError::generic_err("Parsing Coin: cannot parse integer from empty string") + Coin::from_str("-123ucosm").unwrap_err().to_string(), + "Invalid amount: cannot parse integer from empty string" ); assert_eq!( - Coin::from_str("340282366920938463463374607431768211456ucosm").unwrap_err(), - StdError::generic_err("Parsing Coin: number too large to fit in target type") + Coin::from_str("340282366920938463463374607431768211456ucosm") + .unwrap_err() + .to_string(), + "Invalid amount: number too large to fit in target type" ); } } diff --git a/packages/std/src/errors/mod.rs b/packages/std/src/errors/mod.rs index 705382b732..5535479bdf 100644 --- a/packages/std/src/errors/mod.rs +++ b/packages/std/src/errors/mod.rs @@ -6,7 +6,7 @@ mod verification_error; pub use recover_pubkey_error::RecoverPubkeyError; pub use std_error::{ CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError, - ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, + CoinFromStrError, ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, RoundUpOverflowError, StdError, StdResult, }; pub use system_error::SystemError; diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index 79b6a82f27..7093167590 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -590,6 +590,26 @@ pub enum CheckedFromRatioError { #[error("Round up operation failed because of overflow")] pub struct RoundUpOverflowError; +#[derive(Error, Debug, PartialEq, Eq)] +pub enum CoinFromStrError { + #[error("Missing denominator")] + MissingDenom, + #[error("Invalid amount: {0}")] + InvalidAmount(std::num::ParseIntError), +} + +impl From for CoinFromStrError { + fn from(value: std::num::ParseIntError) -> Self { + Self::InvalidAmount(value) + } +} + +impl From for StdError { + fn from(value: CoinFromStrError) -> Self { + Self::generic_err(format!("Parsing Coin: {}", value)) + } +} + #[cfg(test)] mod tests { use super::*; From 46cd18ddf426330a9c9169925af1b3fdb69fadd4 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 4 May 2023 17:26:32 +0200 Subject: [PATCH 005/113] Fix lint --- packages/std/src/coin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/std/src/coin.rs b/packages/std/src/coin.rs index 1422d65246..f43308d851 100644 --- a/packages/std/src/coin.rs +++ b/packages/std/src/coin.rs @@ -2,7 +2,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::{fmt, str::FromStr}; -use crate::{errors::CoinFromStrError, math::Uint128, StdError}; +use crate::{errors::CoinFromStrError, math::Uint128}; #[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, JsonSchema)] pub struct Coin { From f4724565eda257f2d7cfd19f6e916ca20f2b84a3 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 5 May 2023 09:38:41 +0200 Subject: [PATCH 006/113] Add changelog entry --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d409f023f1..7fc4233029 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to ## [Unreleased] +### Added + +- cosmwasm-std: Add `FromStr` impl for `Coin`. ([#1684]) + +[#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 + ## [1.2.7] - 2023-06-19 ### Added From b20b5729f2b3fadedb79c332d058b655447d805e Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 15 May 2023 13:04:01 +0200 Subject: [PATCH 007/113] Check for missing amount when parsing Coin --- packages/std/src/coin.rs | 24 ++++++++++++++++++++---- packages/std/src/errors/std_error.rs | 2 ++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/std/src/coin.rs b/packages/std/src/coin.rs index f43308d851..13a74e0f4b 100644 --- a/packages/std/src/coin.rs +++ b/packages/std/src/coin.rs @@ -34,6 +34,10 @@ impl FromStr for Coin { .ok_or(CoinFromStrError::MissingDenom)?; let (amount, denom) = s.split_at(pos); + if amount.is_empty() { + return Err(CoinFromStrError::MissingAmount); + } + Ok(Coin { amount: amount.parse::()?.into(), denom: denom.to_string(), @@ -217,12 +221,24 @@ mod tests { CoinFromStrError::MissingDenom ); assert_eq!( - Coin::from_str("ucosm").unwrap_err().to_string(), - "Invalid amount: cannot parse integer from empty string" + Coin::from_str("ucosm").unwrap_err(), // no amount + CoinFromStrError::MissingAmount + ); + assert_eq!( + Coin::from_str("-123ucosm").unwrap_err(), // negative amount + CoinFromStrError::MissingAmount + ); + assert_eq!( + Coin::from_str("").unwrap_err(), // empty input + CoinFromStrError::MissingDenom + ); + assert_eq!( + Coin::from_str(" 1ucosm").unwrap_err(), // unsupported whitespace + CoinFromStrError::MissingAmount ); assert_eq!( - Coin::from_str("-123ucosm").unwrap_err().to_string(), - "Invalid amount: cannot parse integer from empty string" + Coin::from_str("�1ucosm").unwrap_err(), // other broken data + CoinFromStrError::MissingAmount ); assert_eq!( Coin::from_str("340282366920938463463374607431768211456ucosm") diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index 7093167590..d90171c74c 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -594,6 +594,8 @@ pub struct RoundUpOverflowError; pub enum CoinFromStrError { #[error("Missing denominator")] MissingDenom, + #[error("Missing amount or non-digit characters in amount")] + MissingAmount, #[error("Invalid amount: {0}")] InvalidAmount(std::num::ParseIntError), } From 7c1a8e38ead9eba1fbe36ebc74fc48e0805d956b Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 27 Mar 2023 10:07:26 +0200 Subject: [PATCH 008/113] Check table section --- CHANGELOG.md | 6 +++ packages/vm/src/compatibility.rs | 81 +++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fc4233029..6531f27641 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ and this project adheres to [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 +### Changed + +- cosmwasm-vm: Add checks for table section of Wasm blob ([#1631]). + +[#1631]: https://github.com/CosmWasm/cosmwasm/pull/1631 + ## [1.2.7] - 2023-06-19 ### Added diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index c2c9826a9d..20680a450c 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -1,4 +1,4 @@ -use parity_wasm::elements::{External, ImportEntry, Module}; +use parity_wasm::elements::{External, ImportEntry, Module, TableType}; use std::collections::BTreeSet; use std::collections::HashSet; @@ -49,10 +49,25 @@ const SUPPORTED_INTERFACE_VERSIONS: &[&str] = &[ ]; const MEMORY_LIMIT: u32 = 512; // in pages +/// The upper limit for the `max` value of each table. CosmWasm contracts have +/// initial=max for 1 table. See +/// +/// ```plain +/// $ wasm-objdump --section=table -x packages/vm/testdata/hackatom.wasm +/// Section Details: +/// +/// Table[1]: +/// - table[0] type=funcref initial=161 max=161 +/// ``` +/// +/// As of March 2023, on Juno mainnet the largest value for production contracts +/// is 485. Most are between 100 and 300. +const TABLE_SIZE_LIMIT: u32 = 2500; // entries /// Checks if the data is valid wasm and compatibility with the CosmWasm API (imports and exports) pub fn check_wasm(wasm_code: &[u8], available_capabilities: &HashSet) -> VmResult<()> { let module = deserialize_wasm(wasm_code)?; + check_wasm_tables(&module)?; check_wasm_memories(&module)?; check_interface_version(&module)?; check_wasm_exports(&module)?; @@ -61,6 +76,38 @@ pub fn check_wasm(wasm_code: &[u8], available_capabilities: &HashSet) -> Ok(()) } +fn check_wasm_tables(module: &Module) -> VmResult<()> { + let sections: &[TableType] = module + .table_section() + .map_or(&[], |section| section.entries()); + match sections.len() { + 0 => Ok(()), + 1 => { + let limits = sections[0].limits(); + if let Some(maximum) = limits.maximum() { + if limits.initial() > maximum { + return Err(VmError::static_validation_err( + "Wasm contract's first table section has a initial limit > max limit", + )); + } + if maximum > TABLE_SIZE_LIMIT { + return Err(VmError::static_validation_err( + "Wasm contract's first table section has a too large max limit", + )); + } + Ok(()) + } else { + Err(VmError::static_validation_err( + "Wasm contract must not have unbound table section", + )) + } + } + _ => Err(VmError::static_validation_err( + "Wasm contract must not have more than 1 table section", + )), + } +} + fn check_wasm_memories(module: &Module) -> VmResult<()> { let section = match module.memory_section() { Some(section) => section, @@ -252,6 +299,38 @@ mod tests { }; } + #[test] + fn check_wasm_tables_works() { + // No tables is fine + let wasm = wat::parse_str("(module)").unwrap(); + check_wasm_tables(&deserialize_wasm(&wasm).unwrap()).unwrap(); + + // One table (bound) + let wasm = wat::parse_str("(module (table $name 123 123 funcref))").unwrap(); + check_wasm_tables(&deserialize_wasm(&wasm).unwrap()).unwrap(); + + // One table (bound, initial > max) + let wasm = wat::parse_str("(module (table $name 124 123 funcref))").unwrap(); + let err = check_wasm_tables(&deserialize_wasm(&wasm).unwrap()).unwrap_err(); + assert!(err + .to_string() + .contains("Wasm contract's first table section has a initial limit > max limit")); + + // One table (bound, max too large) + let wasm = wat::parse_str("(module (table $name 100 9999 funcref))").unwrap(); + let err = check_wasm_tables(&deserialize_wasm(&wasm).unwrap()).unwrap_err(); + assert!(err + .to_string() + .contains("Wasm contract's first table section has a too large max limit")); + + // One table (unbound) + let wasm = wat::parse_str("(module (table $name 100 funcref))").unwrap(); + let err = check_wasm_tables(&deserialize_wasm(&wasm).unwrap()).unwrap_err(); + assert!(err + .to_string() + .contains("Wasm contract must not have unbound table section")); + } + #[test] fn check_wasm_memories_ok() { let wasm = wat::parse_str("(module (memory 1))").unwrap(); From 9674e424b971808dd8ab05dcab253440ca2ca13d Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 13 Mar 2023 15:53:08 +0100 Subject: [PATCH 009/113] Remove unnecessary vector clone --- packages/vm/src/compatibility.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index 20680a450c..af797a3645 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -191,14 +191,14 @@ fn check_wasm_exports(module: &Module) -> VmResult<()> { /// When this is not the case, we either have an incompatibility between contract and VM /// or a error in the contract. fn check_wasm_imports(module: &Module, supported_imports: &[&str]) -> VmResult<()> { - let required_imports: Vec = module + let required_imports: &[ImportEntry] = module .import_section() - .map_or(vec![], |import_section| import_section.entries().to_vec()); + .map_or(&[], |import_section| import_section.entries()); let required_import_names: BTreeSet<_> = required_imports.iter().map(full_import_name).collect(); for required_import in required_imports { - let full_name = full_import_name(&required_import); + let full_name = full_import_name(required_import); if !supported_imports.contains(&full_name.as_str()) { return Err(VmError::static_validation_err(format!( "Wasm contract requires unsupported import: \"{}\". Required imports: {}. Available imports: {:?}.", From a2223f7dcda0e485a2602effa1ca75cce6c36a56 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 13 Mar 2023 16:24:16 +0100 Subject: [PATCH 010/113] Only create BTreeSet of names for the error message --- packages/vm/src/compatibility.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index af797a3645..59b3680833 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -194,12 +194,12 @@ fn check_wasm_imports(module: &Module, supported_imports: &[&str]) -> VmResult<( let required_imports: &[ImportEntry] = module .import_section() .map_or(&[], |import_section| import_section.entries()); - let required_import_names: BTreeSet<_> = - required_imports.iter().map(full_import_name).collect(); for required_import in required_imports { let full_name = full_import_name(required_import); if !supported_imports.contains(&full_name.as_str()) { + let required_import_names: BTreeSet<_> = + required_imports.iter().map(full_import_name).collect(); return Err(VmError::static_validation_err(format!( "Wasm contract requires unsupported import: \"{}\". Required imports: {}. Available imports: {:?}.", full_name, required_import_names.to_string_limited(200), supported_imports From cfaace4a5ee4d04fc758c97c697ec1bf8ac2fa23 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 27 Mar 2023 10:55:54 +0200 Subject: [PATCH 011/113] Limit number of imports during static validation --- CHANGELOG.md | 2 + packages/vm/src/compatibility.rs | 132 +++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6531f27641..4c0194f793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,9 @@ and this project adheres to ### Changed - cosmwasm-vm: Add checks for table section of Wasm blob ([#1631]). +- cosmwasm-vm: Limit number of imports during static validation ([#1629]). +[#1629]: https://github.com/CosmWasm/cosmwasm/pull/1629 [#1631]: https://github.com/CosmWasm/cosmwasm/pull/1631 ## [1.2.7] - 2023-06-19 diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index 59b3680833..428d51554f 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -64,6 +64,12 @@ const MEMORY_LIMIT: u32 = 512; // in pages /// is 485. Most are between 100 and 300. const TABLE_SIZE_LIMIT: u32 = 2500; // entries +/// If the contract has more than this amount of imports, it will be rejected +/// during static validation before even looking into the imports. We keep this +/// number high since failing early gives less detailed error messages. Especially +/// when a user accidentally includes wasm-bindgen, they get a bunch of unsupported imports. +const MAX_IMPORTS: usize = 100; + /// Checks if the data is valid wasm and compatibility with the CosmWasm API (imports and exports) pub fn check_wasm(wasm_code: &[u8], available_capabilities: &HashSet) -> VmResult<()> { let module = deserialize_wasm(wasm_code)?; @@ -195,6 +201,14 @@ fn check_wasm_imports(module: &Module, supported_imports: &[&str]) -> VmResult<( .import_section() .map_or(&[], |import_section| import_section.entries()); + if required_imports.len() > MAX_IMPORTS { + return Err(VmError::static_validation_err(format!( + "Import count exceeds limit. Imports: {}. Limit: {}.", + required_imports.len(), + MAX_IMPORTS + ))); + } + for required_import in required_imports { let full_name = full_import_name(required_import); if !supported_imports.contains(&full_name.as_str()) { @@ -640,6 +654,124 @@ mod tests { check_wasm_imports(&deserialize_wasm(&wasm).unwrap(), SUPPORTED_IMPORTS).unwrap(); } + #[test] + fn check_wasm_imports_exceeds_limit() { + let wasm = wat::parse_str( + r#"(module + (import "env" "db_write" (func (param i32 i32) (result i32))) + (import "env" "db_remove" (func (param i32) (result i32))) + (import "env" "addr_validate" (func (param i32) (result i32))) + (import "env" "addr_canonicalize" (func (param i32 i32) (result i32))) + (import "env" "addr_humanize" (func (param i32 i32) (result i32))) + (import "env" "secp256k1_verify" (func (param i32 i32 i32) (result i32))) + (import "env" "secp256k1_recover_pubkey" (func (param i32 i32 i32) (result i64))) + (import "env" "ed25519_verify" (func (param i32 i32 i32) (result i32))) + (import "env" "ed25519_batch_verify" (func (param i32 i32 i32) (result i32))) + (import "env" "spam01" (func (param i32 i32) (result i32))) + (import "env" "spam02" (func (param i32 i32) (result i32))) + (import "env" "spam03" (func (param i32 i32) (result i32))) + (import "env" "spam04" (func (param i32 i32) (result i32))) + (import "env" "spam05" (func (param i32 i32) (result i32))) + (import "env" "spam06" (func (param i32 i32) (result i32))) + (import "env" "spam07" (func (param i32 i32) (result i32))) + (import "env" "spam08" (func (param i32 i32) (result i32))) + (import "env" "spam09" (func (param i32 i32) (result i32))) + (import "env" "spam10" (func (param i32 i32) (result i32))) + (import "env" "spam11" (func (param i32 i32) (result i32))) + (import "env" "spam12" (func (param i32 i32) (result i32))) + (import "env" "spam13" (func (param i32 i32) (result i32))) + (import "env" "spam14" (func (param i32 i32) (result i32))) + (import "env" "spam15" (func (param i32 i32) (result i32))) + (import "env" "spam16" (func (param i32 i32) (result i32))) + (import "env" "spam17" (func (param i32 i32) (result i32))) + (import "env" "spam18" (func (param i32 i32) (result i32))) + (import "env" "spam19" (func (param i32 i32) (result i32))) + (import "env" "spam20" (func (param i32 i32) (result i32))) + (import "env" "spam21" (func (param i32 i32) (result i32))) + (import "env" "spam22" (func (param i32 i32) (result i32))) + (import "env" "spam23" (func (param i32 i32) (result i32))) + (import "env" "spam24" (func (param i32 i32) (result i32))) + (import "env" "spam25" (func (param i32 i32) (result i32))) + (import "env" "spam26" (func (param i32 i32) (result i32))) + (import "env" "spam27" (func (param i32 i32) (result i32))) + (import "env" "spam28" (func (param i32 i32) (result i32))) + (import "env" "spam29" (func (param i32 i32) (result i32))) + (import "env" "spam30" (func (param i32 i32) (result i32))) + (import "env" "spam31" (func (param i32 i32) (result i32))) + (import "env" "spam32" (func (param i32 i32) (result i32))) + (import "env" "spam33" (func (param i32 i32) (result i32))) + (import "env" "spam34" (func (param i32 i32) (result i32))) + (import "env" "spam35" (func (param i32 i32) (result i32))) + (import "env" "spam36" (func (param i32 i32) (result i32))) + (import "env" "spam37" (func (param i32 i32) (result i32))) + (import "env" "spam38" (func (param i32 i32) (result i32))) + (import "env" "spam39" (func (param i32 i32) (result i32))) + (import "env" "spam40" (func (param i32 i32) (result i32))) + (import "env" "spam41" (func (param i32 i32) (result i32))) + (import "env" "spam42" (func (param i32 i32) (result i32))) + (import "env" "spam43" (func (param i32 i32) (result i32))) + (import "env" "spam44" (func (param i32 i32) (result i32))) + (import "env" "spam45" (func (param i32 i32) (result i32))) + (import "env" "spam46" (func (param i32 i32) (result i32))) + (import "env" "spam47" (func (param i32 i32) (result i32))) + (import "env" "spam48" (func (param i32 i32) (result i32))) + (import "env" "spam49" (func (param i32 i32) (result i32))) + (import "env" "spam50" (func (param i32 i32) (result i32))) + (import "env" "spam51" (func (param i32 i32) (result i32))) + (import "env" "spam52" (func (param i32 i32) (result i32))) + (import "env" "spam53" (func (param i32 i32) (result i32))) + (import "env" "spam54" (func (param i32 i32) (result i32))) + (import "env" "spam55" (func (param i32 i32) (result i32))) + (import "env" "spam56" (func (param i32 i32) (result i32))) + (import "env" "spam57" (func (param i32 i32) (result i32))) + (import "env" "spam58" (func (param i32 i32) (result i32))) + (import "env" "spam59" (func (param i32 i32) (result i32))) + (import "env" "spam60" (func (param i32 i32) (result i32))) + (import "env" "spam61" (func (param i32 i32) (result i32))) + (import "env" "spam62" (func (param i32 i32) (result i32))) + (import "env" "spam63" (func (param i32 i32) (result i32))) + (import "env" "spam64" (func (param i32 i32) (result i32))) + (import "env" "spam65" (func (param i32 i32) (result i32))) + (import "env" "spam66" (func (param i32 i32) (result i32))) + (import "env" "spam67" (func (param i32 i32) (result i32))) + (import "env" "spam68" (func (param i32 i32) (result i32))) + (import "env" "spam69" (func (param i32 i32) (result i32))) + (import "env" "spam70" (func (param i32 i32) (result i32))) + (import "env" "spam71" (func (param i32 i32) (result i32))) + (import "env" "spam72" (func (param i32 i32) (result i32))) + (import "env" "spam73" (func (param i32 i32) (result i32))) + (import "env" "spam74" (func (param i32 i32) (result i32))) + (import "env" "spam75" (func (param i32 i32) (result i32))) + (import "env" "spam76" (func (param i32 i32) (result i32))) + (import "env" "spam77" (func (param i32 i32) (result i32))) + (import "env" "spam78" (func (param i32 i32) (result i32))) + (import "env" "spam79" (func (param i32 i32) (result i32))) + (import "env" "spam80" (func (param i32 i32) (result i32))) + (import "env" "spam81" (func (param i32 i32) (result i32))) + (import "env" "spam82" (func (param i32 i32) (result i32))) + (import "env" "spam83" (func (param i32 i32) (result i32))) + (import "env" "spam84" (func (param i32 i32) (result i32))) + (import "env" "spam85" (func (param i32 i32) (result i32))) + (import "env" "spam86" (func (param i32 i32) (result i32))) + (import "env" "spam87" (func (param i32 i32) (result i32))) + (import "env" "spam88" (func (param i32 i32) (result i32))) + (import "env" "spam89" (func (param i32 i32) (result i32))) + (import "env" "spam90" (func (param i32 i32) (result i32))) + (import "env" "spam91" (func (param i32 i32) (result i32))) + (import "env" "spam92" (func (param i32 i32) (result i32))) + )"#, + ) + .unwrap(); + let err = + check_wasm_imports(&deserialize_wasm(&wasm).unwrap(), SUPPORTED_IMPORTS).unwrap_err(); + match err { + VmError::StaticValidationErr { msg, .. } => { + assert_eq!(msg, "Import count exceeds limit. Imports: 101. Limit: 100."); + } + err => panic!("Unexpected error: {:?}", err), + } + } + #[test] fn check_wasm_imports_missing() { let wasm = wat::parse_str( From 3fcc93c7ea08b49aedb019b30c30ba2b0183c1cf Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 13 Mar 2023 17:12:25 +0100 Subject: [PATCH 012/113] Upgrade parity-wasm to 0.45 --- Cargo.lock | 4 ++-- contracts/burner/Cargo.lock | 4 ++-- contracts/crypto-verify/Cargo.lock | 4 ++-- contracts/cyberpunk/Cargo.lock | 4 ++-- contracts/floaty/Cargo.lock | 4 ++-- contracts/hackatom/Cargo.lock | 4 ++-- contracts/ibc-reflect-send/Cargo.lock | 4 ++-- contracts/ibc-reflect/Cargo.lock | 4 ++-- contracts/queue/Cargo.lock | 4 ++-- contracts/reflect/Cargo.lock | 4 ++-- contracts/staking/Cargo.lock | 4 ++-- contracts/virus/Cargo.lock | 4 ++-- packages/vm/Cargo.toml | 2 +- 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c028d0028e..bbc3a1f345 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1190,9 +1190,9 @@ checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock index 9d00c76f9a..726d32115b 100644 --- a/contracts/burner/Cargo.lock +++ b/contracts/burner/Cargo.lock @@ -949,9 +949,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock index 70de208acd..7ced541619 100644 --- a/contracts/crypto-verify/Cargo.lock +++ b/contracts/crypto-verify/Cargo.lock @@ -981,9 +981,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock index 530f7005ea..c10bb0412f 100644 --- a/contracts/cyberpunk/Cargo.lock +++ b/contracts/cyberpunk/Cargo.lock @@ -987,9 +987,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock index 32c1428dc9..2f5fe32196 100644 --- a/contracts/floaty/Cargo.lock +++ b/contracts/floaty/Cargo.lock @@ -959,9 +959,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock index dd9f21a4da..b1bee4142d 100644 --- a/contracts/hackatom/Cargo.lock +++ b/contracts/hackatom/Cargo.lock @@ -960,9 +960,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock index c428c1e7a9..a069555798 100644 --- a/contracts/ibc-reflect-send/Cargo.lock +++ b/contracts/ibc-reflect-send/Cargo.lock @@ -958,9 +958,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock index bf6645c38d..cfd912df70 100644 --- a/contracts/ibc-reflect/Cargo.lock +++ b/contracts/ibc-reflect/Cargo.lock @@ -958,9 +958,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock index 82597f4ea7..8238a459c4 100644 --- a/contracts/queue/Cargo.lock +++ b/contracts/queue/Cargo.lock @@ -938,9 +938,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock index d462e18fdd..5e89494fe9 100644 --- a/contracts/reflect/Cargo.lock +++ b/contracts/reflect/Cargo.lock @@ -946,9 +946,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock index f63bd2552a..fa28642a97 100644 --- a/contracts/staking/Cargo.lock +++ b/contracts/staking/Cargo.lock @@ -952,9 +952,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock index ff161c069d..59c4ff9e65 100644 --- a/contracts/virus/Cargo.lock +++ b/contracts/virus/Cargo.lock @@ -938,9 +938,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/packages/vm/Cargo.toml b/packages/vm/Cargo.toml index 27bef52a5e..bc603c67ef 100644 --- a/packages/vm/Cargo.toml +++ b/packages/vm/Cargo.toml @@ -44,7 +44,7 @@ clru = "0.4.0" cosmwasm-std = { path = "../std", version = "1.2.7", default-features = false } cosmwasm-crypto = { path = "../crypto", version = "1.2.7" } hex = "0.4" -parity-wasm = "0.42" +parity-wasm = "0.45" schemars = "0.8.3" serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } serde_json = "1.0.40" From e59738ae6e20e2ea583624ad0b13a0799f911ac5 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 19 Apr 2023 14:25:41 +0200 Subject: [PATCH 013/113] Add target_id --- Cargo.lock | 2 + contracts/burner/Cargo.lock | 5 ++- contracts/crypto-verify/Cargo.lock | 5 ++- contracts/cyberpunk/Cargo.lock | 5 ++- contracts/floaty/Cargo.lock | 5 ++- contracts/hackatom/Cargo.lock | 5 ++- contracts/ibc-reflect-send/Cargo.lock | 5 ++- contracts/ibc-reflect/Cargo.lock | 5 ++- contracts/queue/Cargo.lock | 5 ++- contracts/reflect/Cargo.lock | 5 ++- contracts/staking/Cargo.lock | 5 ++- contracts/virus/Cargo.lock | 5 ++- packages/vm/Cargo.toml | 2 + packages/vm/src/modules/file_system_cache.rs | 39 +++++++++++++++++++- 14 files changed, 75 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bbc3a1f345..bcd5ec7a04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -385,6 +385,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "criterion", "enumset", "hex", @@ -397,6 +398,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.3", + "target-lexicon", "tempfile", "thiserror", "wasmer", diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock index 726d32115b..ffc5bc3faf 100644 --- a/contracts/burner/Cargo.lock +++ b/contracts/burner/Cargo.lock @@ -237,6 +237,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "enumset", "hex", "loupe", @@ -329,9 +330,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock index 7ced541619..525807cef8 100644 --- a/contracts/crypto-verify/Cargo.lock +++ b/contracts/crypto-verify/Cargo.lock @@ -240,6 +240,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "enumset", "hex", "loupe", @@ -332,9 +333,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock index c10bb0412f..b254a657ac 100644 --- a/contracts/cyberpunk/Cargo.lock +++ b/contracts/cyberpunk/Cargo.lock @@ -263,6 +263,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "enumset", "hex", "loupe", @@ -355,9 +356,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock index 2f5fe32196..7903f0555d 100644 --- a/contracts/floaty/Cargo.lock +++ b/contracts/floaty/Cargo.lock @@ -234,6 +234,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "enumset", "hex", "loupe", @@ -326,9 +327,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock index b1bee4142d..77eb0d0e6d 100644 --- a/contracts/hackatom/Cargo.lock +++ b/contracts/hackatom/Cargo.lock @@ -234,6 +234,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "enumset", "hex", "loupe", @@ -326,9 +327,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock index a069555798..68f519d999 100644 --- a/contracts/ibc-reflect-send/Cargo.lock +++ b/contracts/ibc-reflect-send/Cargo.lock @@ -234,6 +234,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "enumset", "hex", "loupe", @@ -326,9 +327,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock index cfd912df70..06dcdd37bc 100644 --- a/contracts/ibc-reflect/Cargo.lock +++ b/contracts/ibc-reflect/Cargo.lock @@ -234,6 +234,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "enumset", "hex", "loupe", @@ -326,9 +327,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock index 8238a459c4..ec0086d567 100644 --- a/contracts/queue/Cargo.lock +++ b/contracts/queue/Cargo.lock @@ -226,6 +226,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "enumset", "hex", "loupe", @@ -318,9 +319,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock index 5e89494fe9..90a729bb0d 100644 --- a/contracts/reflect/Cargo.lock +++ b/contracts/reflect/Cargo.lock @@ -234,6 +234,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "enumset", "hex", "loupe", @@ -326,9 +327,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock index fa28642a97..7d8e834d27 100644 --- a/contracts/staking/Cargo.lock +++ b/contracts/staking/Cargo.lock @@ -234,6 +234,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "enumset", "hex", "loupe", @@ -326,9 +327,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock index 59c4ff9e65..a55dc46412 100644 --- a/contracts/virus/Cargo.lock +++ b/contracts/virus/Cargo.lock @@ -226,6 +226,7 @@ dependencies = [ "clru", "cosmwasm-crypto", "cosmwasm-std", + "crc32fast", "enumset", "hex", "loupe", @@ -318,9 +319,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] diff --git a/packages/vm/Cargo.toml b/packages/vm/Cargo.toml index bc603c67ef..2a478be0dd 100644 --- a/packages/vm/Cargo.toml +++ b/packages/vm/Cargo.toml @@ -40,6 +40,7 @@ required-features = ["iterator"] [dependencies] clru = "0.4.0" +crc32fast = "1.3.2" # Uses the path when built locally; uses the given version from crates.io when published cosmwasm-std = { path = "../std", version = "1.2.7", default-features = false } cosmwasm-crypto = { path = "../crypto", version = "1.2.7" } @@ -74,6 +75,7 @@ wat = "1.0" clap = "2.33.3" rand = "0.8" leb128 = "0.2" +target-lexicon = "0.12" [[bench]] name = "main" diff --git a/packages/vm/src/modules/file_system_cache.rs b/packages/vm/src/modules/file_system_cache.rs index e5041a9900..2333d0f04c 100644 --- a/packages/vm/src/modules/file_system_cache.rs +++ b/packages/vm/src/modules/file_system_cache.rs @@ -1,9 +1,10 @@ use std::fs; +use std::hash::Hash; use std::io; use std::path::PathBuf; use thiserror::Error; -use wasmer::{DeserializeError, Module, Store}; +use wasmer::{DeserializeError, Module, Store, Target}; use crate::checksum::Checksum; use crate::errors::{VmError, VmResult}; @@ -168,6 +169,18 @@ impl FileSystemCache { } } +/// Creates an identifier for the Wasmer `Target` that is used for +/// cache invalidation. The output is reasonable human friendly to be useable +/// in file path component. +#[allow(unused)] +fn target_id(target: &Target) -> String { + // Use a custom Hasher implementation to avoid randomization. + let mut deterministic_hasher = crc32fast::Hasher::new(); + target.hash(&mut deterministic_hasher); + let hash = deterministic_hasher.finalize(); + format!("{}-{:08X}", target.triple(), hash) // print 4 byte hash as 8 hex characters +} + #[cfg(test)] mod tests { use std::fs; @@ -276,4 +289,28 @@ mod tests { let existed = cache.remove(&checksum).unwrap(); assert!(!existed); } + + #[test] + fn target_id_works() { + let triple = wasmer::Triple { + architecture: wasmer::Architecture::X86_64, + vendor: target_lexicon::Vendor::Nintendo, + operating_system: target_lexicon::OperatingSystem::Fuchsia, + environment: target_lexicon::Environment::Gnu, + binary_format: target_lexicon::BinaryFormat::Coff, + }; + let target = Target::new(triple.clone(), wasmer::CpuFeature::POPCNT.into()); + let id = target_id(&target); + assert_eq!(id, "x86_64-nintendo-fuchsia-gnu-coff-4721E3F4"); + // Changing CPU features changes the hash part + let target = Target::new(triple, wasmer::CpuFeature::AVX512DQ.into()); + let id = target_id(&target); + assert_eq!(id, "x86_64-nintendo-fuchsia-gnu-coff-D5C8034F"); + + // Works for durrect target (hashing is deterministic); + let target = Target::default(); + let id1 = target_id(&target); + let id2 = target_id(&target); + assert_eq!(id1, id2); + } } From 64dbc734541d4a01d40128dad5c162eded3990aa Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 19 Apr 2023 14:53:45 +0200 Subject: [PATCH 014/113] Add target_id to modules path --- Cargo.lock | 7 ++ packages/vm/Cargo.toml | 1 + packages/vm/src/modules/file_system_cache.rs | 88 ++++++++++++-------- 3 files changed, 62 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bcd5ec7a04..2a7dafafed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,6 +388,7 @@ dependencies = [ "crc32fast", "criterion", "enumset", + "glob", "hex", "hex-literal", "leb128", @@ -892,6 +893,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "group" version = "0.12.0" diff --git a/packages/vm/Cargo.toml b/packages/vm/Cargo.toml index 2a478be0dd..59817c55dd 100644 --- a/packages/vm/Cargo.toml +++ b/packages/vm/Cargo.toml @@ -69,6 +69,7 @@ bitflags = "1.1.0" # https://github.com/CensoredUsername/dynasm-rs/pull/74 [dev-dependencies] criterion = { version = "0.4", features = [ "html_reports" ] } +glob = "0.3.1" hex-literal = "0.3.1" tempfile = "3.1.0" wat = "1.0" diff --git a/packages/vm/src/modules/file_system_cache.rs b/packages/vm/src/modules/file_system_cache.rs index 2333d0f04c..10d65dadd3 100644 --- a/packages/vm/src/modules/file_system_cache.rs +++ b/packages/vm/src/modules/file_system_cache.rs @@ -1,7 +1,7 @@ use std::fs; use std::hash::Hash; use std::io; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use thiserror::Error; use wasmer::{DeserializeError, Module, Store, Target}; @@ -48,10 +48,7 @@ const MODULE_SERIALIZATION_VERSION: &str = "v5"; /// Representation of a directory that contains compiled Wasm artifacts. pub struct FileSystemCache { - /// The base path this cache operates in. Within this path, versioned directories are created. - /// A sophisticated version of this cache might be able to read multiple input versions in the future. - base_path: PathBuf, - wasmer_module_version: u32, + modules_path: PathBuf, } /// An error type that hides system specific error information @@ -76,19 +73,20 @@ impl FileSystemCache { /// /// This method is unsafe because there's no way to ensure the artifacts /// stored in this cache haven't been corrupted or tampered with. - pub unsafe fn new(path: impl Into) -> Result { - let wasmer_module_version = current_wasmer_module_version(); - - let path: PathBuf = path.into(); - if path.exists() { - let metadata = path + pub unsafe fn new(base_path: impl Into) -> Result { + let base_path: PathBuf = base_path.into(); + if base_path.exists() { + let metadata = base_path .metadata() .map_err(|_e| NewFileSystemCacheError::CouldntGetMetadata)?; if metadata.is_dir() { if !metadata.permissions().readonly() { Ok(Self { - base_path: path, - wasmer_module_version, + modules_path: modules_path( + &base_path, + current_wasmer_module_version(), + &Target::default(), + ), }) } else { Err(NewFileSystemCacheError::ReadonlyPath) @@ -98,10 +96,13 @@ impl FileSystemCache { } } else { // Create the directory and any parent directories if they don't yet exist. - mkdir_p(&path).map_err(|_e| NewFileSystemCacheError::CouldntCreatePath)?; + mkdir_p(&base_path).map_err(|_e| NewFileSystemCacheError::CouldntCreatePath)?; Ok(Self { - base_path: path, - wasmer_module_version, + modules_path: modules_path( + &base_path, + current_wasmer_module_version(), + &Target::default(), + ), }) } } @@ -110,7 +111,7 @@ impl FileSystemCache { /// along with the size of the serialized module. pub fn load(&self, checksum: &Checksum, store: &Store) -> VmResult> { let filename = checksum.to_hex(); - let file_path = self.latest_modules_path().join(filename); + let file_path = self.modules_path.join(filename); let result = unsafe { Module::deserialize_from_file(store, file_path) }; match result { @@ -131,12 +132,11 @@ impl FileSystemCache { /// Stores a serialized module to the file system. Returns the size of the serialized module. pub fn store(&mut self, checksum: &Checksum, module: &Module) -> VmResult<()> { - let modules_dir = self.latest_modules_path(); - mkdir_p(&modules_dir) + mkdir_p(&self.modules_path) .map_err(|_e| VmError::cache_err("Error creating modules directory"))?; let filename = checksum.to_hex(); - let path = modules_dir.join(filename); + let path = self.modules_path.join(filename); module .serialize_to_file(path) .map_err(|e| VmError::cache_err(format!("Error writing module to disk: {}", e)))?; @@ -148,7 +148,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.latest_modules_path().join(filename); + let file_path = self.modules_path.join(filename); if file_path.exists() { fs::remove_file(file_path) @@ -158,21 +158,11 @@ impl FileSystemCache { Ok(false) } } - - /// The path to the latest version of the modules. - fn latest_modules_path(&self) -> PathBuf { - let version = format!( - "{}-wasmer{}", - MODULE_SERIALIZATION_VERSION, self.wasmer_module_version - ); - self.base_path.join(version) - } } /// Creates an identifier for the Wasmer `Target` that is used for /// cache invalidation. The output is reasonable human friendly to be useable /// in file path component. -#[allow(unused)] fn target_id(target: &Target) -> String { // Use a custom Hasher implementation to avoid randomization. let mut deterministic_hasher = crc32fast::Hasher::new(); @@ -181,6 +171,16 @@ fn target_id(target: &Target) -> String { format!("{}-{:08X}", target.triple(), hash) // print 4 byte hash as 8 hex characters } +/// The path to the latest version of the modules. +fn modules_path(base_path: &Path, wasmer_module_version: u32, target: &Target) -> PathBuf { + let version_dir = format!( + "{}-wasmer{}", + MODULE_SERIALIZATION_VERSION, wasmer_module_version + ); + let target_dir = target_id(target); + base_path.join(version_dir).join(target_dir) +} + #[cfg(test)] mod tests { use std::fs; @@ -252,11 +252,13 @@ mod tests { let module = compile(&wasm, None, &[]).unwrap(); cache.store(&checksum, &module).unwrap(); - let file_path = format!( - "{}/v5-wasmer1/{}", + let mut globber = glob::glob(&format!( + "{}/v5-wasmer1/**/{}", tmp_dir.path().to_string_lossy(), checksum - ); + )) + .expect("Failed to read glob pattern"); + let file_path = globber.next().unwrap().unwrap(); let _serialized_module = fs::read(file_path).unwrap(); } @@ -313,4 +315,22 @@ mod tests { let id2 = target_id(&target); assert_eq!(id1, id2); } + + #[test] + fn modules_path_works() { + let base = PathBuf::from("./modules/"); + let triple = wasmer::Triple { + architecture: wasmer::Architecture::X86_64, + vendor: target_lexicon::Vendor::Nintendo, + operating_system: target_lexicon::OperatingSystem::Fuchsia, + environment: target_lexicon::Environment::Gnu, + binary_format: target_lexicon::BinaryFormat::Coff, + }; + let target = Target::new(triple, wasmer::CpuFeature::POPCNT.into()); + let p = modules_path(&base, 17, &target); + assert_eq!( + p.as_os_str(), + "./modules/v5-wasmer17/x86_64-nintendo-fuchsia-gnu-coff-4721E3F4" + ); + } } From 7100cabb54db14bb6645b02b67c985be31299317 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 19 Apr 2023 15:19:14 +0200 Subject: [PATCH 015/113] Make modules_path_works test Windows complient --- packages/vm/src/modules/file_system_cache.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/vm/src/modules/file_system_cache.rs b/packages/vm/src/modules/file_system_cache.rs index 10d65dadd3..0525244d59 100644 --- a/packages/vm/src/modules/file_system_cache.rs +++ b/packages/vm/src/modules/file_system_cache.rs @@ -318,7 +318,7 @@ mod tests { #[test] fn modules_path_works() { - let base = PathBuf::from("./modules/"); + let base = PathBuf::from("modules"); let triple = wasmer::Triple { architecture: wasmer::Architecture::X86_64, vendor: target_lexicon::Vendor::Nintendo, @@ -330,7 +330,11 @@ mod tests { let p = modules_path(&base, 17, &target); assert_eq!( p.as_os_str(), - "./modules/v5-wasmer17/x86_64-nintendo-fuchsia-gnu-coff-4721E3F4" + if cfg!(windows) { + "modules\\v5-wasmer17\\x86_64-nintendo-fuchsia-gnu-coff-4721E3F4" + } else { + "modules/v5-wasmer17/x86_64-nintendo-fuchsia-gnu-coff-4721E3F4" + } ); } } From 1e4ff27cd06725b2534c5d6b656ec9ae8ea4d276 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 20 Apr 2023 17:10:40 +0200 Subject: [PATCH 016/113] Refactor FileSystemCache::new to only have one ending --- packages/vm/src/modules/file_system_cache.rs | 34 ++++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/packages/vm/src/modules/file_system_cache.rs b/packages/vm/src/modules/file_system_cache.rs index 0525244d59..8f592b7ff0 100644 --- a/packages/vm/src/modules/file_system_cache.rs +++ b/packages/vm/src/modules/file_system_cache.rs @@ -79,32 +79,24 @@ impl FileSystemCache { let metadata = base_path .metadata() .map_err(|_e| NewFileSystemCacheError::CouldntGetMetadata)?; - if metadata.is_dir() { - if !metadata.permissions().readonly() { - Ok(Self { - modules_path: modules_path( - &base_path, - current_wasmer_module_version(), - &Target::default(), - ), - }) - } else { - Err(NewFileSystemCacheError::ReadonlyPath) - } - } else { - Err(NewFileSystemCacheError::ExistsButNoDirectory) + if !metadata.is_dir() { + return Err(NewFileSystemCacheError::ExistsButNoDirectory); + } + if metadata.permissions().readonly() { + return Err(NewFileSystemCacheError::ReadonlyPath); } } else { // Create the directory and any parent directories if they don't yet exist. mkdir_p(&base_path).map_err(|_e| NewFileSystemCacheError::CouldntCreatePath)?; - Ok(Self { - modules_path: modules_path( - &base_path, - current_wasmer_module_version(), - &Target::default(), - ), - }) } + + Ok(Self { + modules_path: modules_path( + &base_path, + current_wasmer_module_version(), + &Target::default(), + ), + }) } /// Loads a serialized module from the file system and returns a module (i.e. artifact + store), From fdae0a2e75b3b6b4fa1151f7762f031d5026844f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 24 Apr 2023 17:19:22 +0200 Subject: [PATCH 017/113] Add CHANGELOG entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c0194f793..139f29681f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,9 +16,13 @@ and this project adheres to - cosmwasm-vm: Add checks for table section of Wasm blob ([#1631]). - cosmwasm-vm: Limit number of imports during static validation ([#1629]). +- cosmwasm-vm: Add target (triple + CPU features) into the module cache + directory to avoid using modules compiled for a different system. Bump + `MODULE_SERIALIZATION_VERSION` to "v5". ([#1664]) [#1629]: https://github.com/CosmWasm/cosmwasm/pull/1629 [#1631]: https://github.com/CosmWasm/cosmwasm/pull/1631 +[#1664]: https://github.com/CosmWasm/cosmwasm/pull/1664 ## [1.2.7] - 2023-06-19 From d3430657283463ccaf370e9728fa7e132ceef7f1 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 24 Apr 2023 17:41:30 +0200 Subject: [PATCH 018/113] Bump MODULE_SERIALIZATION_VERSION to v6 --- CHANGELOG.md | 2 +- packages/vm/src/modules/file_system_cache.rs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 139f29681f..fa06ce961b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ and this project adheres to - cosmwasm-vm: Limit number of imports during static validation ([#1629]). - cosmwasm-vm: Add target (triple + CPU features) into the module cache directory to avoid using modules compiled for a different system. Bump - `MODULE_SERIALIZATION_VERSION` to "v5". ([#1664]) + `MODULE_SERIALIZATION_VERSION` to "v6". ([#1664]) [#1629]: https://github.com/CosmWasm/cosmwasm/pull/1629 [#1631]: https://github.com/CosmWasm/cosmwasm/pull/1631 diff --git a/packages/vm/src/modules/file_system_cache.rs b/packages/vm/src/modules/file_system_cache.rs index 8f592b7ff0..a78b5661a3 100644 --- a/packages/vm/src/modules/file_system_cache.rs +++ b/packages/vm/src/modules/file_system_cache.rs @@ -44,7 +44,9 @@ use crate::modules::current_wasmer_module_version; /// A change in memory layout of some types in Rust [std] caused /// [issues with module deserialization](https://github.com/CosmWasm/wasmvm/issues/426). /// To work around this, the version was bumped to "v5" here to invalidate these corrupt caches. -const MODULE_SERIALIZATION_VERSION: &str = "v5"; +/// - **v6**:
+/// Version for cosmwasm_vm 1.3+ which adds a sub-folder with the target identier for the modules. +const MODULE_SERIALIZATION_VERSION: &str = "v6"; /// Representation of a directory that contains compiled Wasm artifacts. pub struct FileSystemCache { @@ -245,7 +247,7 @@ mod tests { cache.store(&checksum, &module).unwrap(); let mut globber = glob::glob(&format!( - "{}/v5-wasmer1/**/{}", + "{}/v6-wasmer1/**/{}", tmp_dir.path().to_string_lossy(), checksum )) @@ -323,9 +325,9 @@ mod tests { assert_eq!( p.as_os_str(), if cfg!(windows) { - "modules\\v5-wasmer17\\x86_64-nintendo-fuchsia-gnu-coff-4721E3F4" + "modules\\v6-wasmer17\\x86_64-nintendo-fuchsia-gnu-coff-4721E3F4" } else { - "modules/v5-wasmer17/x86_64-nintendo-fuchsia-gnu-coff-4721E3F4" + "modules/v6-wasmer17/x86_64-nintendo-fuchsia-gnu-coff-4721E3F4" } ); } From 3abd3c4ee25ec81c29a620df6637d436fd696c65 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 5 May 2023 10:19:31 +0200 Subject: [PATCH 019/113] Add .wasm file extension to wasm files, fixes #1665 --- packages/vm/src/cache.rs | 55 +++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/packages/vm/src/cache.rs b/packages/vm/src/cache.rs index 36c18a6ad1..43f580cdc5 100644 --- a/packages/vm/src/cache.rs +++ b/packages/vm/src/cache.rs @@ -364,7 +364,7 @@ fn save_wasm_to_disk(dir: impl Into, wasm: &[u8]) -> VmResult // calculate filename let checksum = Checksum::generate(wasm); let filename = checksum.to_hex(); - let filepath = dir.into().join(filename); + let filepath = dir.into().join(filename).with_extension("wasm"); // write data to file // Since the same filename (a collision resistent hash) cannot be generated from two different byte codes @@ -382,9 +382,12 @@ fn save_wasm_to_disk(dir: impl Into, wasm: &[u8]) -> VmResult fn load_wasm_from_disk(dir: impl Into, checksum: &Checksum) -> VmResult> { // this requires the directory and file to exist + // The files previously had no extension, so to allow for a smooth transition, + // we also try to load the file without the wasm extension. let path = dir.into().join(checksum.to_hex()); - let mut file = - File::open(path).map_err(|_e| VmError::cache_err("Error opening Wasm file for reading"))?; + let mut file = File::open(path.with_extension("wasm")) + .or_else(|_| File::open(path)) + .map_err(|_e| VmError::cache_err("Error opening Wasm file for reading"))?; let mut wasm = Vec::::new(); file.read_to_end(&mut wasm) @@ -398,13 +401,25 @@ fn load_wasm_from_disk(dir: impl Into, checksum: &Checksum) -> VmResult /// code is required. So a non-existent file leads to an error as it /// indicates a bug. fn remove_wasm_from_disk(dir: impl Into, checksum: &Checksum) -> VmResult<()> { + // the files previously had no extension, so to allow for a smooth transition, we delete both let path = dir.into().join(checksum.to_hex()); + let wasm_path = path.with_extension("wasm"); - if !path.exists() { + let path_exists = path.exists(); + let wasm_path_exists = wasm_path.exists(); + if !path_exists && !wasm_path_exists { return Err(VmError::cache_err("Wasm file does not exist")); } - fs::remove_file(path).map_err(|_e| VmError::cache_err("Error removing Wasm file from disk"))?; + if path_exists { + fs::remove_file(path) + .map_err(|_e| VmError::cache_err("Error removing Wasm file from disk"))?; + } + + if wasm_path_exists { + fs::remove_file(wasm_path) + .map_err(|_e| VmError::cache_err("Error removing Wasm file from disk"))?; + } Ok(()) } @@ -606,7 +621,8 @@ mod tests { .path() .join(STATE_DIR) .join(WASM_DIR) - .join(checksum.to_hex()); + .join(checksum.to_hex()) + .with_extension("wasm"); let mut file = OpenOptions::new().write(true).open(filepath).unwrap(); file.write_all(b"broken data").unwrap(); @@ -1169,4 +1185,31 @@ mod tests { let non_id = Checksum::generate(b"non_existent"); cache.unpin(&non_id).unwrap(); } + + #[test] + fn loading_without_extension_works() { + let tmp_dir = TempDir::new().unwrap(); + let options = CacheOptions { + base_dir: tmp_dir.path().to_path_buf(), + available_capabilities: default_capabilities(), + memory_cache_size: TESTING_MEMORY_CACHE_SIZE, + instance_memory_limit: TESTING_MEMORY_LIMIT, + }; + let cache: Cache = + unsafe { Cache::new(options).unwrap() }; + let checksum = cache.save_wasm(CONTRACT).unwrap(); + + // Move the saved wasm to the old path (without extension) + let old_path = tmp_dir + .path() + .join(STATE_DIR) + .join(WASM_DIR) + .join(checksum.to_hex()); + let new_path = old_path.with_extension("wasm"); + fs::rename(new_path, old_path).unwrap(); + + // loading wasm from before the wasm extension was added should still work + let restored = cache.load_wasm(&checksum).unwrap(); + assert_eq!(restored, CONTRACT); + } } From a7142a28fc0a534a7e3daf52680be111302c90c1 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 5 May 2023 10:28:04 +0200 Subject: [PATCH 020/113] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa06ce961b..0eb9f1ba98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,10 +19,12 @@ and this project adheres to - cosmwasm-vm: Add target (triple + CPU features) into the module cache directory to avoid using modules compiled for a different system. Bump `MODULE_SERIALIZATION_VERSION` to "v6". ([#1664]) +- cosmwasm-vm: Add `.wasm` extension to stored wasm files ([#1686]). [#1629]: https://github.com/CosmWasm/cosmwasm/pull/1629 [#1631]: https://github.com/CosmWasm/cosmwasm/pull/1631 [#1664]: https://github.com/CosmWasm/cosmwasm/pull/1664 +[#1686]: https://github.com/CosmWasm/cosmwasm/pull/1686 ## [1.2.7] - 2023-06-19 From edd06a337f03e496ec6602c02a54f33bc13f7cc9 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Mon, 14 Nov 2022 15:50:18 +0000 Subject: [PATCH 021/113] add `Coins` struct --- packages/std/src/coins.rs | 246 ++++++++++++++++++++++++++++++++++++++ packages/std/src/lib.rs | 2 + 2 files changed, 248 insertions(+) create mode 100644 packages/std/src/coins.rs diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs new file mode 100644 index 0000000000..826982d7ab --- /dev/null +++ b/packages/std/src/coins.rs @@ -0,0 +1,246 @@ +use std::any::type_name; +use std::collections::BTreeMap; +use std::fmt; +use std::str::FromStr; + +use crate::{Coin, StdError, StdResult, Uint128}; + +/// A collection of coins, similar to Cosmos SDK's `sdk.Coins` struct. +/// +/// Differently from `sdk.Coins`, which is a vector of `sdk.Coin`, here we +/// implement Coins as a BTreeMap that maps from coin denoms to amounts. +/// This has a number of advantages: +/// +/// - coins are naturally sorted alphabetically by denom +/// - duplicate denoms are automatically removed +/// - cheaper for searching/inserting/deleting: O(log(n)) compared to O(n) +#[derive(Clone, Default, Debug, PartialEq, Eq)] +pub struct Coins(BTreeMap); + +// Casting a Vec to Coins. +// The Vec can be out of order, but must not contain duplicate denoms or zero amounts. +impl TryFrom> for Coins { + type Error = StdError; + + fn try_from(vec: Vec) -> StdResult { + let vec_len = vec.len(); + + let map = vec + .into_iter() + .filter(|coin| !coin.amount.is_zero()) + .map(|coin| (coin.denom, coin.amount)) + .collect::>(); + + // the map having a different length from the vec means the vec must either + // 1) contain duplicate denoms, or 2) contain zero amounts + if map.len() != vec_len { + return Err(StdError::parse_err( + type_name::(), + "duplicate denoms or zero amount", + )); + } + + Ok(Self(map)) + } +} + +impl TryFrom<&[Coin]> for Coins { + type Error = StdError; + + fn try_from(slice: &[Coin]) -> StdResult { + slice.to_vec().try_into() + } +} + +impl FromStr for Coins { + type Err = StdError; + + fn from_str(s: &str) -> StdResult { + // Parse a string into a `Coin`. + // + // Parsing the string with regex doesn't work, because the resulting + // wasm binary would be too big from including the `regex` library. + // + // We opt for the following solution: enumerate characters in the string, + // and break before the first non-number character. Split the string at + // that index. + // + // This assumes the denom never starts with a number, which is the case: + // https://github.com/cosmos/cosmos-sdk/blob/v0.46.0/types/coin.go#L854-L856 + let parse_coin_str = |s: &str| -> StdResult { + for (i, c) in s.chars().enumerate() { + if !c.is_ascii_digit() { + let amount = Uint128::from_str(&s[..i])?; + let denom = String::from(&s[i..]); + return Ok(Coin { amount, denom }); + } + } + + Err(StdError::parse_err( + type_name::(), + format!("invalid coin string: {s}"), + )) + }; + + s.split(',') + .into_iter() + .map(parse_coin_str) + .collect::>>()? + .try_into() + } +} + +impl fmt::Display for Coins { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = self + .0 + .iter() + .map(|(denom, amount)| format!("{amount}{denom}")) + .collect::>() + .join(","); + write!(f, "{s}") + } +} + +impl Coins { + /// Cast to Vec, while NOT consuming the original object + pub fn to_vec(&self) -> Vec { + self.0 + .iter() + .map(|(denom, amount)| Coin { + denom: denom.clone(), + amount: *amount, + }) + .collect() + } + + /// Cast to Vec, consuming the original object + pub fn into_vec(self) -> Vec { + self.0 + .into_iter() + .map(|(denom, amount)| Coin { denom, amount }) + .collect() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Return the denoms as a vector of strings. + /// The vector is guaranteed to not contain duplicates and sorted alphabetically. + pub fn denoms(&self) -> Vec { + self.0.keys().cloned().collect() + } + + pub fn add(&mut self, coin: &Coin) -> StdResult<()> { + let amount = self + .0 + .entry(coin.denom.clone()) + .or_insert_with(Uint128::zero); + *amount = amount.checked_add(coin.amount)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::coin; + + /// Sort a Vec by denom alphabetically + fn sort_by_denom(vec: &mut [Coin]) { + vec.sort_by(|a, b| a.denom.cmp(&b.denom)); + } + + /// Returns a mockup Vec. In this example, the coins are not in order + fn mock_vec() -> Vec { + vec![ + coin(12345, "uatom"), + coin(69420, "ibc/1234ABCD"), + coin(88888, "factory/osmo1234abcd/subdenom"), + ] + } + + /// Return a mockup Coins that contains the same coins as in `mock_vec` + fn mock_coins() -> Coins { + let mut coins = Coins::default(); + for coin in mock_vec() { + coins.add(&coin).unwrap(); + } + coins + } + + #[test] + fn casting_vec() { + let mut vec = mock_vec(); + let coins = mock_coins(); + + // &[Coin] --> Coins + assert_eq!(Coins::try_from(vec.as_slice()).unwrap(), coins); + // Vec --> Coins + assert_eq!(Coins::try_from(vec.clone()).unwrap(), coins); + + sort_by_denom(&mut vec); + + // &Coins --> Vec + // NOTE: the returned vec should be sorted + assert_eq!(coins.to_vec(), vec); + // Coins --> Vec + // NOTE: the returned vec should be sorted + assert_eq!(coins.into_vec(), vec); + } + + #[test] + fn casting_str() { + // not in order + let s1 = "88888factory/osmo1234abcd/subdenom,12345uatom,69420ibc/1234ABCD"; + // in order + let s2 = "88888factory/osmo1234abcd/subdenom,69420ibc/1234ABCD,12345uatom"; + + let coins = mock_coins(); + + // &str --> Coins + // NOTE: should generate the same Coins, regardless of input order + assert_eq!(Coins::from_str(s1).unwrap(), coins); + assert_eq!(Coins::from_str(s2).unwrap(), coins); + + // Coins --> String + // NOTE: the generated string should be sorted + assert_eq!(coins.to_string(), s2); + } + + #[test] + fn handling_duplicates() { + // create a Vec that contains duplicate denoms + let mut vec = mock_vec(); + vec.push(coin(67890, "uatom")); + + let err = Coins::try_from(vec).unwrap_err(); + assert!(err.to_string().contains("duplicate denoms")); + } + + #[test] + fn handling_zero_amount() { + // create a Vec that contains zero amounts + let mut vec = mock_vec(); + vec[0].amount = Uint128::zero(); + + let err = Coins::try_from(vec).unwrap_err(); + assert!(err.to_string().contains("zero amount")); + } + + #[test] + fn length() { + let coins = Coins::default(); + assert_eq!(coins.len(), 0); + assert!(coins.is_empty()); + + let coins = mock_coins(); + assert_eq!(coins.len(), 3); + assert!(!coins.is_empty()); + } +} diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index 7f26868498..98e8e45f9b 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -7,6 +7,7 @@ mod addresses; mod assertions; mod binary; mod coin; +mod coins; mod conversion; mod deps; mod errors; @@ -31,6 +32,7 @@ mod types; pub use crate::addresses::{instantiate2_address, Addr, CanonicalAddr, Instantiate2AddressError}; pub use crate::binary::Binary; pub use crate::coin::{coin, coins, has_coins, Coin}; +pub use crate::coins::Coins; pub use crate::deps::{Deps, DepsMut, OwnedDeps}; pub use crate::errors::{ CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError, From c5b82d33df7684a340e16825874be60706c10296 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 5 May 2023 13:33:48 +0200 Subject: [PATCH 022/113] Ignore zero amount when adding coin to coins --- packages/std/src/coins.rs | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 826982d7ab..43489dd746 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -136,11 +136,19 @@ impl Coins { self.0.keys().cloned().collect() } - pub fn add(&mut self, coin: &Coin) -> StdResult<()> { - let amount = self - .0 - .entry(coin.denom.clone()) - .or_insert_with(Uint128::zero); + /// Returns the amount of the given denom or zero if the denom is not present. + pub fn amount_of(&self, denom: &str) -> Uint128 { + self.0.get(denom).copied().unwrap_or_else(Uint128::zero) + } + + /// Adds the given coin to the collection. + /// This errors in case of overflow. + pub fn add(&mut self, coin: Coin) -> StdResult<()> { + if coin.amount.is_zero() { + return Ok(()); + } + + let amount = self.0.entry(coin.denom).or_insert_with(Uint128::zero); *amount = amount.checked_add(coin.amount)?; Ok(()) } @@ -169,7 +177,7 @@ mod tests { fn mock_coins() -> Coins { let mut coins = Coins::default(); for coin in mock_vec() { - coins.add(&coin).unwrap(); + coins.add(coin).unwrap(); } coins } @@ -231,6 +239,11 @@ mod tests { let err = Coins::try_from(vec).unwrap_err(); assert!(err.to_string().contains("zero amount")); + + // adding a coin with zero amount should not be added + let mut coins = Coins::default(); + coins.add(coin(0, "uusd")).unwrap(); + assert!(coins.is_empty()); } #[test] @@ -243,4 +256,16 @@ mod tests { assert_eq!(coins.len(), 3); assert!(!coins.is_empty()); } + + #[test] + fn add_coin() { + let mut coins = mock_coins(); + coins.add(coin(12345, "uatom")).unwrap(); + + assert_eq!(coins.len(), 3); + assert_eq!(coins.amount_of("uatom").u128(), 24690); + + coins.add(coin(123, "uusd")).unwrap(); + assert_eq!(coins.len(), 4); + } } From 2da843212087fc734cdd654db21beea7d5f3c9b7 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 5 May 2023 15:13:37 +0200 Subject: [PATCH 023/113] Add extend method for Coins --- packages/std/src/coins.rs | 74 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 43489dd746..d2ab0f16a4 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -1,7 +1,7 @@ -use std::any::type_name; use std::collections::BTreeMap; use std::fmt; use std::str::FromStr; +use std::{any::type_name, collections::btree_map}; use crate::{Coin, StdError, StdResult, Uint128}; @@ -52,6 +52,16 @@ impl TryFrom<&[Coin]> for Coins { } } +impl TryFrom for Coins { + type Error = StdError; + + fn try_from(coin: Coin) -> StdResult { + let mut coins = Coins::default(); + coins.add(coin)?; + Ok(coins) + } +} + impl FromStr for Coins { type Err = StdError; @@ -130,7 +140,7 @@ impl Coins { self.0.is_empty() } - /// Return the denoms as a vector of strings. + /// Returns the denoms as a vector of strings. /// The vector is guaranteed to not contain duplicates and sorted alphabetically. pub fn denoms(&self) -> Vec { self.0.keys().cloned().collect() @@ -152,6 +162,51 @@ impl Coins { *amount = amount.checked_add(coin.amount)?; Ok(()) } + + /// Adds the given coins to the collection. + /// This takes anything that yields `(denom, amount)` tuples when iterated over. + /// + /// # Examples + /// + /// ```rust + /// use cosmwasm_std::{Coin, Coins, Uint128, coin}; + /// + /// let mut coins = Coins::default(); + /// let new_coins: Coins = coin(123u128, "ucosm").try_into()?; + /// coins.extend(new_coins.clone())?; + /// assert_eq!(coins, new_coins); + /// # cosmwasm_std::StdResult::Ok(()) + /// ``` + pub fn extend(&mut self, others: C) -> StdResult<()> + where + C: IntoIterator, + { + for (denom, amount) in others { + self.add(Coin { denom, amount })?; + } + Ok(()) + } +} + +impl IntoIterator for Coins { + type Item = (String, Uint128); + // TODO: do we want to wrap the iterator type with our own to avoid exposing BTreeMap? + // also: for the owned version we could return Coins instead of (String, Uint128), + // but not for the borrowed version, so it would feel inconsistent + type IntoIter = btree_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> IntoIterator for &'a Coins { + type Item = (&'a String, &'a Uint128); + type IntoIter = btree_map::Iter<'a, String, Uint128>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } } #[cfg(test)] @@ -268,4 +323,19 @@ mod tests { coins.add(coin(123, "uusd")).unwrap(); assert_eq!(coins.len(), 4); } + + #[test] + fn extend_coins() { + let mut coins: Coins = coin(12345, "uatom").try_into().unwrap(); + + coins.extend(mock_coins()).unwrap(); + assert_eq!(coins.len(), 3); + assert_eq!(coins.amount_of("uatom").u128(), 24690); + + coins + .extend([("uusd".to_string(), Uint128::new(123u128))]) + .unwrap(); + assert_eq!(coins.len(), 4); + assert_eq!(coins.amount_of("uusd").u128(), 123) + } } From ea2aab14a7d9d88e2b2abe18869290ac13ae4db7 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 5 May 2023 15:16:43 +0200 Subject: [PATCH 024/113] Fix lint --- packages/std/src/coins.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index d2ab0f16a4..1c8c263673 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -93,7 +93,6 @@ impl FromStr for Coins { }; s.split(',') - .into_iter() .map(parse_coin_str) .collect::>>()? .try_into() From b9635086f715c69292a768f0aa677b0e186dfb5b Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 5 May 2023 16:58:29 +0200 Subject: [PATCH 025/113] Add more trait impls for Coins --- packages/std/src/coins.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 1c8c263673..87cda52a65 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -52,6 +52,14 @@ impl TryFrom<&[Coin]> for Coins { } } +impl TryFrom<[Coin; N]> for Coins { + type Error = StdError; + + fn try_from(slice: [Coin; N]) -> StdResult { + slice.to_vec().try_into() + } +} + impl TryFrom for Coins { type Error = StdError; @@ -62,6 +70,12 @@ impl TryFrom for Coins { } } +impl From for Vec { + fn from(value: Coins) -> Self { + value.into_vec() + } +} + impl FromStr for Coins { type Err = StdError; @@ -111,6 +125,12 @@ impl fmt::Display for Coins { } } +impl PartialEq for Coins { + fn eq(&self, other: &Coin) -> bool { + self.0.len() == 1 && self.amount_of(&other.denom) == other.amount + } +} + impl Coins { /// Cast to Vec, while NOT consuming the original object pub fn to_vec(&self) -> Vec { From 15c60acf87e6c71219aa6cac75cb35cb735dee99 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 5 May 2023 17:05:37 +0200 Subject: [PATCH 026/113] Add helper to Coins --- packages/std/src/coins.rs | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 87cda52a65..6be7af358a 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -80,6 +80,8 @@ impl FromStr for Coins { type Err = StdError; fn from_str(s: &str) -> StdResult { + // TODO: use FromStr impl for Coin once it's merged + // Parse a string into a `Coin`. // // Parsing the string with regex doesn't work, because the resulting @@ -170,6 +172,33 @@ impl Coins { self.0.get(denom).copied().unwrap_or_else(Uint128::zero) } + /// Returns the amount of the given denom if and only if this collection contains only + /// the given denom. Otherwise `None` is returned. + /// + /// # Examples + /// + /// ```rust + /// use cosmwasm_std::{Coin, Coins, coin}; + /// + /// let coins: Coins = coin(100, "uatom").try_into().unwrap(); + /// assert_eq!(coins.contains_only("uatom").unwrap().u128(), 100); + /// assert_eq!(coins.contains_only("uluna"), None); + /// ``` + /// + /// ```rust + /// use cosmwasm_std::{Coin, Coins, coin}; + /// + /// let coins: Coins = [coin(100, "uatom"), coin(200, "uusd")].try_into().unwrap(); + /// assert_eq!(coins.contains_only("uatom"), None); + /// ``` + pub fn contains_only(&self, denom: &str) -> Option { + if self.len() == 1 { + self.0.get(denom).copied() + } else { + None + } + } + /// Adds the given coin to the collection. /// This errors in case of overflow. pub fn add(&mut self, coin: Coin) -> StdResult<()> { @@ -188,7 +217,7 @@ impl Coins { /// # Examples /// /// ```rust - /// use cosmwasm_std::{Coin, Coins, Uint128, coin}; + /// use cosmwasm_std::{Coin, Coins, coin}; /// /// let mut coins = Coins::default(); /// let new_coins: Coins = coin(123u128, "ucosm").try_into()?; @@ -357,4 +386,12 @@ mod tests { assert_eq!(coins.len(), 4); assert_eq!(coins.amount_of("uusd").u128(), 123) } + + #[test] + fn equality() { + let coin = coin(54321, "uatom"); + let coins = Coins::try_from(coin.clone()).unwrap(); + + assert_eq!(coins, coin); + } } From dab3146776119cc6cfca628e86eb862f671724a7 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 22 May 2023 10:26:28 +0200 Subject: [PATCH 027/113] Use Coin's FromStr impl in FromStr impl of Coins --- packages/std/src/coins.rs | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 6be7af358a..27b3196009 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -80,37 +80,9 @@ impl FromStr for Coins { type Err = StdError; fn from_str(s: &str) -> StdResult { - // TODO: use FromStr impl for Coin once it's merged - - // Parse a string into a `Coin`. - // - // Parsing the string with regex doesn't work, because the resulting - // wasm binary would be too big from including the `regex` library. - // - // We opt for the following solution: enumerate characters in the string, - // and break before the first non-number character. Split the string at - // that index. - // - // This assumes the denom never starts with a number, which is the case: - // https://github.com/cosmos/cosmos-sdk/blob/v0.46.0/types/coin.go#L854-L856 - let parse_coin_str = |s: &str| -> StdResult { - for (i, c) in s.chars().enumerate() { - if !c.is_ascii_digit() { - let amount = Uint128::from_str(&s[..i])?; - let denom = String::from(&s[i..]); - return Ok(Coin { amount, denom }); - } - } - - Err(StdError::parse_err( - type_name::(), - format!("invalid coin string: {s}"), - )) - }; - s.split(',') - .map(parse_coin_str) - .collect::>>()? + .map(Coin::from_str) + .collect::, _>>()? .try_into() } } From 4e38bcfe494ccc58dc299551ecd284204bb4821f Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 22 May 2023 17:12:07 +0200 Subject: [PATCH 028/113] Remove IntoIterator impls for now --- packages/std/src/coins.rs | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 27b3196009..175a024e2a 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -1,7 +1,7 @@ +use std::any::type_name; use std::collections::BTreeMap; use std::fmt; use std::str::FromStr; -use std::{any::type_name, collections::btree_map}; use crate::{Coin, StdError, StdResult, Uint128}; @@ -199,36 +199,15 @@ impl Coins { /// ``` pub fn extend(&mut self, others: C) -> StdResult<()> where - C: IntoIterator, + C: IntoIterator, { - for (denom, amount) in others { - self.add(Coin { denom, amount })?; + for c in others { + self.add(c)?; } Ok(()) } } -impl IntoIterator for Coins { - type Item = (String, Uint128); - // TODO: do we want to wrap the iterator type with our own to avoid exposing BTreeMap? - // also: for the owned version we could return Coins instead of (String, Uint128), - // but not for the borrowed version, so it would feel inconsistent - type IntoIter = btree_map::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a> IntoIterator for &'a Coins { - type Item = (&'a String, &'a Uint128); - type IntoIter = btree_map::Iter<'a, String, Uint128>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - #[cfg(test)] mod tests { use super::*; @@ -348,13 +327,11 @@ mod tests { fn extend_coins() { let mut coins: Coins = coin(12345, "uatom").try_into().unwrap(); - coins.extend(mock_coins()).unwrap(); + coins.extend(mock_coins().to_vec()).unwrap(); assert_eq!(coins.len(), 3); assert_eq!(coins.amount_of("uatom").u128(), 24690); - coins - .extend([("uusd".to_string(), Uint128::new(123u128))]) - .unwrap(); + coins.extend([coin(123, "uusd")]).unwrap(); assert_eq!(coins.len(), 4); assert_eq!(coins.amount_of("uusd").u128(), 123) } From 324289f99dbb21390de1af5bf3722e1d27a34f09 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 23 May 2023 08:51:54 +0200 Subject: [PATCH 029/113] Fix test --- packages/std/src/coins.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 175a024e2a..b6ec3380d6 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -193,7 +193,7 @@ impl Coins { /// /// let mut coins = Coins::default(); /// let new_coins: Coins = coin(123u128, "ucosm").try_into()?; - /// coins.extend(new_coins.clone())?; + /// coins.extend(new_coins.to_vec())?; /// assert_eq!(coins, new_coins); /// # cosmwasm_std::StdResult::Ok(()) /// ``` From 24e4d7a48d4cf75d9bc11e5673e2d44adf1af1b9 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 23 May 2023 17:48:31 +0200 Subject: [PATCH 030/113] Apply suggestions from code review Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> --- packages/std/src/coins.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index b6ec3380d6..abc9c11c02 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -106,7 +106,7 @@ impl PartialEq for Coins { } impl Coins { - /// Cast to Vec, while NOT consuming the original object + /// Conversion to Vec, while NOT consuming the original object pub fn to_vec(&self) -> Vec { self.0 .iter() @@ -117,7 +117,7 @@ impl Coins { .collect() } - /// Cast to Vec, consuming the original object + /// Conversion to Vec, consuming the original object pub fn into_vec(self) -> Vec { self.0 .into_iter() From 0e3bc7e5ab4582adf2424e8fba164f9a6a71fe4b Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 24 May 2023 10:08:57 +0200 Subject: [PATCH 031/113] Add custom error type for Coins conversion --- packages/std/src/coins.rs | 34 +++++++++++++++------------- packages/std/src/errors/mod.rs | 4 ++-- packages/std/src/errors/std_error.rs | 14 ++++++++++++ 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index abc9c11c02..c6fdee61c1 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -1,9 +1,8 @@ -use std::any::type_name; use std::collections::BTreeMap; use std::fmt; use std::str::FromStr; -use crate::{Coin, StdError, StdResult, Uint128}; +use crate::{errors::CoinsError, Coin, StdError, StdResult, Uint128}; /// A collection of coins, similar to Cosmos SDK's `sdk.Coins` struct. /// @@ -20,9 +19,15 @@ pub struct Coins(BTreeMap); // Casting a Vec to Coins. // The Vec can be out of order, but must not contain duplicate denoms or zero amounts. impl TryFrom> for Coins { - type Error = StdError; + type Error = CoinsError; + + fn try_from(vec: Vec) -> Result { + if let Some(coin) = vec.iter().find(|c| c.amount.is_zero()) { + return Err(CoinsError::ZeroAmount { + denom: coin.denom.clone(), + }); + } - fn try_from(vec: Vec) -> StdResult { let vec_len = vec.len(); let map = vec @@ -31,13 +36,10 @@ impl TryFrom> for Coins { .map(|coin| (coin.denom, coin.amount)) .collect::>(); - // the map having a different length from the vec means the vec must either - // 1) contain duplicate denoms, or 2) contain zero amounts + // the map having a different length from the vec means the vec must either contain + // duplicate denoms if map.len() != vec_len { - return Err(StdError::parse_err( - type_name::(), - "duplicate denoms or zero amount", - )); + return Err(CoinsError::DuplicateDenom); } Ok(Self(map)) @@ -45,17 +47,17 @@ impl TryFrom> for Coins { } impl TryFrom<&[Coin]> for Coins { - type Error = StdError; + type Error = CoinsError; - fn try_from(slice: &[Coin]) -> StdResult { + fn try_from(slice: &[Coin]) -> Result { slice.to_vec().try_into() } } impl TryFrom<[Coin; N]> for Coins { - type Error = StdError; + type Error = CoinsError; - fn try_from(slice: [Coin; N]) -> StdResult { + fn try_from(slice: [Coin; N]) -> Result { slice.to_vec().try_into() } } @@ -80,10 +82,10 @@ impl FromStr for Coins { type Err = StdError; fn from_str(s: &str) -> StdResult { - s.split(',') + Ok(s.split(',') .map(Coin::from_str) .collect::, _>>()? - .try_into() + .try_into()?) } } diff --git a/packages/std/src/errors/mod.rs b/packages/std/src/errors/mod.rs index 5535479bdf..8c41d36540 100644 --- a/packages/std/src/errors/mod.rs +++ b/packages/std/src/errors/mod.rs @@ -6,8 +6,8 @@ mod verification_error; pub use recover_pubkey_error::RecoverPubkeyError; pub use std_error::{ CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError, - CoinFromStrError, ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, - RoundUpOverflowError, StdError, StdResult, + CoinFromStrError, CoinsError, ConversionOverflowError, DivideByZeroError, OverflowError, + OverflowOperation, RoundUpOverflowError, StdError, StdResult, }; pub use system_error::SystemError; pub use verification_error::VerificationError; diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index d90171c74c..119732eff7 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -590,6 +590,20 @@ pub enum CheckedFromRatioError { #[error("Round up operation failed because of overflow")] pub struct RoundUpOverflowError; +#[derive(Error, Debug, PartialEq, Eq)] +pub enum CoinsError { + #[error("Duplicate denom")] + DuplicateDenom, + #[error("Coin with zero amount: {denom}")] + ZeroAmount { denom: String }, +} + +impl From for StdError { + fn from(value: CoinsError) -> Self { + Self::generic_err(format!("Creating Coins: {}", value)) + } +} + #[derive(Error, Debug, PartialEq, Eq)] pub enum CoinFromStrError { #[error("Missing denominator")] From b202f79a3e147861ed7e52da4c6fad8a68136382 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 24 May 2023 10:10:10 +0200 Subject: [PATCH 032/113] Remove Coin to Coins conversion --- packages/std/src/coins.rs | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index c6fdee61c1..3dc1ce8186 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -62,16 +62,6 @@ impl TryFrom<[Coin; N]> for Coins { } } -impl TryFrom for Coins { - type Error = StdError; - - fn try_from(coin: Coin) -> StdResult { - let mut coins = Coins::default(); - coins.add(coin)?; - Ok(coins) - } -} - impl From for Vec { fn from(value: Coins) -> Self { value.into_vec() @@ -154,7 +144,7 @@ impl Coins { /// ```rust /// use cosmwasm_std::{Coin, Coins, coin}; /// - /// let coins: Coins = coin(100, "uatom").try_into().unwrap(); + /// let coins: Coins = [coin(100, "uatom")].try_into().unwrap(); /// assert_eq!(coins.contains_only("uatom").unwrap().u128(), 100); /// assert_eq!(coins.contains_only("uluna"), None); /// ``` @@ -194,7 +184,7 @@ impl Coins { /// use cosmwasm_std::{Coin, Coins, coin}; /// /// let mut coins = Coins::default(); - /// let new_coins: Coins = coin(123u128, "ucosm").try_into()?; + /// let new_coins: Coins = [coin(123u128, "ucosm")].try_into()?; /// coins.extend(new_coins.to_vec())?; /// assert_eq!(coins, new_coins); /// # cosmwasm_std::StdResult::Ok(()) @@ -284,7 +274,7 @@ mod tests { vec.push(coin(67890, "uatom")); let err = Coins::try_from(vec).unwrap_err(); - assert!(err.to_string().contains("duplicate denoms")); + assert_eq!(err, CoinsError::DuplicateDenom); } #[test] @@ -327,7 +317,7 @@ mod tests { #[test] fn extend_coins() { - let mut coins: Coins = coin(12345, "uatom").try_into().unwrap(); + let mut coins: Coins = [coin(12345, "uatom")].try_into().unwrap(); coins.extend(mock_coins().to_vec()).unwrap(); assert_eq!(coins.len(), 3); @@ -341,7 +331,7 @@ mod tests { #[test] fn equality() { let coin = coin(54321, "uatom"); - let coins = Coins::try_from(coin.clone()).unwrap(); + let coins = Coins::try_from([coin.clone()]).unwrap(); assert_eq!(coins, coin); } From 94f5748cbba0b9235426d9daa5f898ca538455c3 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 24 May 2023 10:21:54 +0200 Subject: [PATCH 033/113] Remove equality impl between Coin and Coins --- packages/std/src/coins.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 3dc1ce8186..43491edd17 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -91,12 +91,6 @@ impl fmt::Display for Coins { } } -impl PartialEq for Coins { - fn eq(&self, other: &Coin) -> bool { - self.0.len() == 1 && self.amount_of(&other.denom) == other.amount - } -} - impl Coins { /// Conversion to Vec, while NOT consuming the original object pub fn to_vec(&self) -> Vec { @@ -327,12 +321,4 @@ mod tests { assert_eq!(coins.len(), 4); assert_eq!(coins.amount_of("uusd").u128(), 123) } - - #[test] - fn equality() { - let coin = coin(54321, "uatom"); - let coins = Coins::try_from([coin.clone()]).unwrap(); - - assert_eq!(coins, coin); - } } From 23d5a45cf77250769d6b8b1fba92376ee5ccc20a Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 24 May 2023 10:50:34 +0200 Subject: [PATCH 034/113] Handle empty string when parsing Coins --- packages/std/src/coins.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 43491edd17..b756bff329 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -72,6 +72,10 @@ impl FromStr for Coins { type Err = StdError; fn from_str(s: &str) -> StdResult { + if s.is_empty() { + return Ok(Self::default()); + } + Ok(s.split(',') .map(Coin::from_str) .collect::, _>>()? @@ -223,7 +227,7 @@ mod tests { } #[test] - fn casting_vec() { + fn converting_vec() { let mut vec = mock_vec(); let coins = mock_coins(); @@ -243,22 +247,30 @@ mod tests { } #[test] - fn casting_str() { + fn converting_str() { // not in order let s1 = "88888factory/osmo1234abcd/subdenom,12345uatom,69420ibc/1234ABCD"; // in order let s2 = "88888factory/osmo1234abcd/subdenom,69420ibc/1234ABCD,12345uatom"; + let invalid = "12345uatom,noamount"; + let coins = mock_coins(); // &str --> Coins // NOTE: should generate the same Coins, regardless of input order assert_eq!(Coins::from_str(s1).unwrap(), coins); assert_eq!(Coins::from_str(s2).unwrap(), coins); + assert_eq!(Coins::from_str("").unwrap(), Coins::default()); // Coins --> String // NOTE: the generated string should be sorted assert_eq!(coins.to_string(), s2); + assert_eq!(Coins::default().to_string(), ""); + assert_eq!( + Coins::from_str(invalid).unwrap_err().to_string(), + "Generic error: Parsing Coin: Missing amount or non-digit characters in amount" + ); } #[test] From e9772297bd25d28942c1e52027cd4c40ca013764 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 6 Jun 2023 10:28:41 +0200 Subject: [PATCH 035/113] Ignore zero values when creating Coins --- packages/std/src/coins.rs | 64 +++++++++++++++++----------- packages/std/src/errors/std_error.rs | 2 - 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index b756bff329..1f8356ddf6 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -16,30 +16,23 @@ use crate::{errors::CoinsError, Coin, StdError, StdResult, Uint128}; #[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct Coins(BTreeMap); -// Casting a Vec to Coins. -// The Vec can be out of order, but must not contain duplicate denoms or zero amounts. +/// Casting a Vec to Coins. +/// The Vec can be out of order, but must not contain duplicate denoms. +/// If you want to sum up duplicates, create an empty instance using [Coins::default] and use `Coins::extend`. impl TryFrom> for Coins { type Error = CoinsError; fn try_from(vec: Vec) -> Result { - if let Some(coin) = vec.iter().find(|c| c.amount.is_zero()) { - return Err(CoinsError::ZeroAmount { - denom: coin.denom.clone(), - }); - } - - let vec_len = vec.len(); - - let map = vec - .into_iter() - .filter(|coin| !coin.amount.is_zero()) - .map(|coin| (coin.denom, coin.amount)) - .collect::>(); - - // the map having a different length from the vec means the vec must either contain - // duplicate denoms - if map.len() != vec_len { - return Err(CoinsError::DuplicateDenom); + let mut map = BTreeMap::new(); + for Coin { amount, denom } in vec { + if amount.is_zero() { + continue; + } + + // if the insertion returns a previous value, we have a duplicate denom + if map.insert(denom, amount).is_some() { + return Err(CoinsError::DuplicateDenom); + } } Ok(Self(map)) @@ -54,6 +47,15 @@ impl TryFrom<&[Coin]> for Coins { } } +impl From for Coins { + fn from(value: Coin) -> Self { + let mut coins = Coins::default(); + // this can never overflow (because there are no coins in there yet), so we can unwrap + coins.add(value).unwrap(); + coins + } +} + impl TryFrom<[Coin; N]> for Coins { type Error = CoinsError; @@ -173,7 +175,8 @@ impl Coins { Ok(()) } - /// Adds the given coins to the collection. + /// Adds the given coins to the collection, ignoring any zero coins and summing up + /// duplicate denoms. /// This takes anything that yields `(denom, amount)` tuples when iterated over. /// /// # Examples @@ -226,6 +229,10 @@ mod tests { coins } + fn mock_duplicate() -> Vec { + vec![coin(12345, "uatom"), coin(6789, "uatom")] + } + #[test] fn converting_vec() { let mut vec = mock_vec(); @@ -289,8 +296,13 @@ mod tests { let mut vec = mock_vec(); vec[0].amount = Uint128::zero(); - let err = Coins::try_from(vec).unwrap_err(); - assert!(err.to_string().contains("zero amount")); + let coins = Coins::try_from(vec).unwrap(); + assert_eq!(coins.len(), 2); + assert_ne!(coins.amount_of("ibc/1234ABCD"), Uint128::zero()); + assert_ne!( + coins.amount_of("factory/osmo1234abcd/subdenom"), + Uint128::zero() + ); // adding a coin with zero amount should not be added let mut coins = Coins::default(); @@ -331,6 +343,10 @@ mod tests { coins.extend([coin(123, "uusd")]).unwrap(); assert_eq!(coins.len(), 4); - assert_eq!(coins.amount_of("uusd").u128(), 123) + assert_eq!(coins.amount_of("uusd").u128(), 123); + + // duplicate handling + coins.extend(mock_duplicate()).unwrap(); + assert_eq!(coins.amount_of("uatom").u128(), 24690 + 12345 + 6789); } } diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index 119732eff7..d30fec6a71 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -594,8 +594,6 @@ pub struct RoundUpOverflowError; pub enum CoinsError { #[error("Duplicate denom")] DuplicateDenom, - #[error("Coin with zero amount: {denom}")] - ZeroAmount { denom: String }, } impl From for StdError { From f4ead00e547a82a8c06275c7b3082bac9fd806a2 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 6 Jun 2023 12:57:00 +0200 Subject: [PATCH 036/113] Add Coin subtraction --- packages/std/src/coins.rs | 76 ++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 1f8356ddf6..8b383a70a0 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -3,6 +3,7 @@ use std::fmt; use std::str::FromStr; use crate::{errors::CoinsError, Coin, StdError, StdResult, Uint128}; +use crate::{OverflowError, OverflowOperation}; /// A collection of coins, similar to Cosmos SDK's `sdk.Coins` struct. /// @@ -18,7 +19,8 @@ pub struct Coins(BTreeMap); /// Casting a Vec to Coins. /// The Vec can be out of order, but must not contain duplicate denoms. -/// If you want to sum up duplicates, create an empty instance using [Coins::default] and use `Coins::extend`. +/// If you want to sum up duplicates, create an empty instance using `Coins::default` and +/// use `Coins::add` to add your coins. impl TryFrom> for Coins { type Error = CoinsError; @@ -175,28 +177,27 @@ impl Coins { Ok(()) } - /// Adds the given coins to the collection, ignoring any zero coins and summing up - /// duplicate denoms. - /// This takes anything that yields `(denom, amount)` tuples when iterated over. - /// - /// # Examples - /// - /// ```rust - /// use cosmwasm_std::{Coin, Coins, coin}; - /// - /// let mut coins = Coins::default(); - /// let new_coins: Coins = [coin(123u128, "ucosm")].try_into()?; - /// coins.extend(new_coins.to_vec())?; - /// assert_eq!(coins, new_coins); - /// # cosmwasm_std::StdResult::Ok(()) - /// ``` - pub fn extend(&mut self, others: C) -> StdResult<()> - where - C: IntoIterator, - { - for c in others { - self.add(c)?; + /// Subtracts the given coin or collection of coins from this `Coins` instance. + /// Errors in case of overflow or if one of the coins is not present. + pub fn sub(&mut self, coin: Coin) -> StdResult<()> { + match self.0.get_mut(&coin.denom) { + Some(v) => { + *v = v.checked_sub(coin.amount)?; + // make sure to remove zero coins + if v.is_zero() { + self.0.remove(&coin.denom); + } + } + None => { + return Err(OverflowError::new( + OverflowOperation::Sub, + Uint128::zero(), + coin.amount, + ) + .into()) + } } + Ok(()) } } @@ -229,10 +230,6 @@ mod tests { coins } - fn mock_duplicate() -> Vec { - vec![coin(12345, "uatom"), coin(6789, "uatom")] - } - #[test] fn converting_vec() { let mut vec = mock_vec(); @@ -334,19 +331,24 @@ mod tests { } #[test] - fn extend_coins() { - let mut coins: Coins = [coin(12345, "uatom")].try_into().unwrap(); + fn sub_coins() { + let mut coins: Coins = coin(12345, "uatom").into(); - coins.extend(mock_coins().to_vec()).unwrap(); - assert_eq!(coins.len(), 3); - assert_eq!(coins.amount_of("uatom").u128(), 24690); + // sub more than available + let err = coins.sub(coin(12346, "uatom")).unwrap_err(); + assert!(matches!(err, StdError::Overflow { .. })); - coins.extend([coin(123, "uusd")]).unwrap(); - assert_eq!(coins.len(), 4); - assert_eq!(coins.amount_of("uusd").u128(), 123); + // sub non-existent denom + let err = coins.sub(coin(12345, "uusd")).unwrap_err(); + assert!(matches!(err, StdError::Overflow { .. })); - // duplicate handling - coins.extend(mock_duplicate()).unwrap(); - assert_eq!(coins.amount_of("uatom").u128(), 24690 + 12345 + 6789); + // partial sub + coins.sub(coin(1, "uatom")).unwrap(); + assert_eq!(coins.len(), 1); + assert_eq!(coins.amount_of("uatom").u128(), 12344); + + // full sub + coins.sub(coin(12344, "uatom")).unwrap(); + assert!(coins.is_empty()); } } From c02a2f1cfbc71776659a07ae8a9902415b067c08 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 6 Jun 2023 14:35:12 +0200 Subject: [PATCH 037/113] Fix Coin subtraction --- packages/std/src/coins.rs | 40 ++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 8b383a70a0..0c0877ddbe 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -119,10 +119,12 @@ impl Coins { .collect() } + /// Returns the number of different denoms in this collection. pub fn len(&self) -> usize { self.0.len() } + /// Returns `true` if this collection contains no coins. pub fn is_empty(&self) -> bool { self.0.is_empty() } @@ -165,8 +167,8 @@ impl Coins { } } - /// Adds the given coin to the collection. - /// This errors in case of overflow. + /// Adds the given coin to this `Coins` instance. + /// Errors in case of overflow. pub fn add(&mut self, coin: Coin) -> StdResult<()> { if coin.amount.is_zero() { return Ok(()); @@ -177,24 +179,28 @@ impl Coins { Ok(()) } - /// Subtracts the given coin or collection of coins from this `Coins` instance. - /// Errors in case of overflow or if one of the coins is not present. + /// Subtracts the given coin from this `Coins` instance. + /// Errors in case of overflow or if the denom is not present. pub fn sub(&mut self, coin: Coin) -> StdResult<()> { match self.0.get_mut(&coin.denom) { Some(v) => { *v = v.checked_sub(coin.amount)?; - // make sure to remove zero coins + // make sure to remove zero coin if v.is_zero() { self.0.remove(&coin.denom); } } None => { + // ignore zero subtraction + if coin.amount.is_zero() { + return Ok(()); + } return Err(OverflowError::new( OverflowOperation::Sub, Uint128::zero(), coin.amount, ) - .into()) + .into()); } } @@ -321,13 +327,23 @@ mod tests { #[test] fn add_coin() { let mut coins = mock_coins(); - coins.add(coin(12345, "uatom")).unwrap(); + // existing denom + coins.add(coin(12345, "uatom")).unwrap(); assert_eq!(coins.len(), 3); assert_eq!(coins.amount_of("uatom").u128(), 24690); + // new denom coins.add(coin(123, "uusd")).unwrap(); assert_eq!(coins.len(), 4); + + // zero amount + coins.add(coin(0, "uusd")).unwrap(); + assert_eq!(coins.amount_of("uusd").u128(), 123); + + // zero amount, new denom + coins.add(coin(0, "utest")).unwrap(); + assert_eq!(coins.len(), 4); } #[test] @@ -350,5 +366,15 @@ mod tests { // full sub coins.sub(coin(12344, "uatom")).unwrap(); assert!(coins.is_empty()); + + // sub zero, existing denom + coins.sub(coin(0, "uusd")).unwrap(); + assert!(coins.is_empty()); + let mut coins: Coins = coin(12345, "uatom").into(); + + // sub zero, non-existent denom + coins.sub(coin(0, "uatom")).unwrap(); + assert_eq!(coins.len(), 1); + assert_eq!(coins.amount_of("uatom").u128(), 12345); } } From 3f5ea72a6425021f4ac02648b3694025558a7ac4 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 7 Jun 2023 12:17:05 +0200 Subject: [PATCH 038/113] Add Coin to Coins conversion test --- packages/std/src/coins.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index 0c0877ddbe..1548cfffad 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -377,4 +377,16 @@ mod tests { assert_eq!(coins.len(), 1); assert_eq!(coins.amount_of("uatom").u128(), 12345); } + + #[test] + fn coin_to_coins() { + // zero coin results in empty collection + let coins: Coins = coin(0, "uusd").into(); + assert!(coins.is_empty()); + + // happy path + let coins = Coins::from(coin(12345, "uatom")); + assert_eq!(coins.len(), 1); + assert_eq!(coins.amount_of("uatom").u128(), 12345); + } } From df8ff96ff885f857d7f561e6f65480047127d85e Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 19 Jun 2023 14:25:46 +0200 Subject: [PATCH 039/113] Add Coins to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0eb9f1ba98..7cf92978dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,10 @@ and this project adheres to ### Added - cosmwasm-std: Add `FromStr` impl for `Coin`. ([#1684]) +- cosmwasm-std: Add `Coins` helper to handle multiple coins. ([#1687]) [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 +[#1687]: https://github.com/CosmWasm/cosmwasm/pull/1687 ### Changed From 476a7b4c1826e99cf5edb5a934c3ec1e3ab294c2 Mon Sep 17 00:00:00 2001 From: Nikhil Suri Date: Fri, 24 Mar 2023 21:24:18 +0000 Subject: [PATCH 040/113] std: Add BankQuery::AllDenomMetadata gated by cosmwasm_1_3 feature flag (cherry picked from commit 4bac1e3cb3769570c91f2cd44bf1232111424ab4) # Conflicts: # CHANGELOG.md # docs/USING_COSMWASM_STD.md --- .circleci/config.yml | 10 +++++----- CHANGELOG.md | 16 ++++++++++++++++ MIGRATING.md | 4 ++-- docs/CAPABILITIES-BUILT-IN.md | 2 ++ docs/USING_COSMWASM_STD.md | 14 ++++++++++++++ packages/check/src/main.rs | 3 ++- packages/std/Cargo.toml | 3 +++ packages/std/src/exports.rs | 4 ++++ packages/std/src/lib.rs | 2 ++ packages/std/src/metadata.rs | 23 +++++++++++++++++++++++ packages/std/src/query/bank.rs | 19 +++++++++++++++++++ packages/std/src/query/mod.rs | 2 ++ packages/std/src/testing/mock.rs | 18 +++++++++++++++++- packages/std/src/traits.rs | 11 +++++++++++ packages/vm/src/testing/instance.rs | 3 ++- 15 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 packages/std/src/metadata.rs diff --git a/.circleci/config.yml b/.circleci/config.yml index 05d72ae197..ff5bb17087 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -355,15 +355,15 @@ jobs: - run: name: Build library for native target (all features) working_directory: ~/project/packages/std - command: cargo build --locked --features abort,iterator,staking,stargate,cosmwasm_1_2 + command: cargo build --locked --features abort,iterator,staking,stargate,cosmwasm_1_3 - run: name: Build library for wasm target (all features) working_directory: ~/project/packages/std - command: cargo wasm --locked --features abort,iterator,staking,stargate,cosmwasm_1_2 + command: cargo wasm --locked --features abort,iterator,staking,stargate,cosmwasm_1_3 - run: name: Run unit tests (all features) working_directory: ~/project/packages/std - command: cargo test --locked --features abort,iterator,staking,stargate,cosmwasm_1_2 + command: cargo test --locked --features abort,iterator,staking,stargate,cosmwasm_1_3 - save_cache: paths: - /usr/local/cargo/registry @@ -906,7 +906,7 @@ jobs: - run: name: Clippy linting on std (all feature flags) working_directory: ~/project/packages/std - command: cargo clippy --all-targets --features abort,iterator,staking,stargate,cosmwasm_1_2 -- -D warnings + command: cargo clippy --all-targets --features abort,iterator,staking,stargate,cosmwasm_1_3 -- -D warnings - run: name: Clippy linting on storage (no feature flags) working_directory: ~/project/packages/storage @@ -983,7 +983,7 @@ jobs: CRYPTO=" cargo tarpaulin --skip-clean --out Xml --output-dir reports/crypto --packages cosmwasm-crypto" DERIVE=" cargo tarpaulin --skip-clean --out Xml --output-dir reports/derive --packages cosmwasm-derive" SCHEMA=" cargo tarpaulin --skip-clean --out Xml --output-dir reports/schema --packages cosmwasm-schema" - STD=" cargo tarpaulin --skip-clean --out Xml --output-dir reports/std --packages cosmwasm-std --features abort,iterator,staking,stargate,cosmwasm_1_2" + STD=" cargo tarpaulin --skip-clean --out Xml --output-dir reports/std --packages cosmwasm-std --features abort,iterator,staking,stargate,cosmwasm_1_3" STORAGE="cargo tarpaulin --skip-clean --out Xml --output-dir reports/storage --packages cosmwasm-storage" docker run --security-opt seccomp=unconfined -v "${PWD}:/volume" xd009642/tarpaulin:0.21.0 \ sh -c "$CRYPTO && $DERIVE && $SCHEMA && $STD && $STORAGE" diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cf92978dc..b4158a9a3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,25 @@ and this project adheres to ### Added +<<<<<<< HEAD +======= +- cosmwasm-std: Implement `BankQuery::AllDenomMetadata` to allow querying all + the denom metadata. In order to use this query in a contract, the + `cosmwasm_1_3` feature needs to be enabled for the `cosmwasm_std` dependency. + This makes the contract incompatible with chains running anything lower than + CosmWasm `1.3.0`. ([#1647]) +- cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have + been checked before. This is useful for state-sync where we know the Wasm code + was checked when it was first uploaded. ([#1635]) +>>>>>>> 4bac1e3c (std: Add BankQuery::AllDenomMetadata gated by cosmwasm_1_3 feature flag) - cosmwasm-std: Add `FromStr` impl for `Coin`. ([#1684]) - cosmwasm-std: Add `Coins` helper to handle multiple coins. ([#1687]) +<<<<<<< HEAD +======= +[#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635 +[#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647 +>>>>>>> 4bac1e3c (std: Add BankQuery::AllDenomMetadata gated by cosmwasm_1_3 feature flag) [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 [#1687]: https://github.com/CosmWasm/cosmwasm/pull/1687 diff --git a/MIGRATING.md b/MIGRATING.md index 0000f999fc..c65a29de5c 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -28,8 +28,8 @@ major releases of `cosmwasm`. Note that you can also view the +cosmwasm-std = { version = "1.1.0", features = ["stargate", "cosmwasm_1_2"] } ``` - Please note that `cosmwasm_1_2` implies `cosmwasm_1_1`, so there is no need to - set both. + Please note that `cosmwasm_1_2` implies `cosmwasm_1_1`, and `cosmwasm_1_3` + implies `cosmwasm_1_2`, and so on, so there is no need to set multiple. - If you use mixed type multiplication between `Uint{64,128,256}` and `Decimal{,256}`, check out diff --git a/docs/CAPABILITIES-BUILT-IN.md b/docs/CAPABILITIES-BUILT-IN.md index 8fdd679fe1..190c9f825d 100644 --- a/docs/CAPABILITIES-BUILT-IN.md +++ b/docs/CAPABILITIES-BUILT-IN.md @@ -15,3 +15,5 @@ might define others. CosmWasm `1.1.0` or higher support this. - `cosmwasm_1_2` enables the `GovMsg::VoteWeighted` and `WasmMsg::Instantiate2` messages. Only chains running CosmWasm `1.2.0` or higher support this. +- `cosmwasm_1_3` enables the `BankQuery::AllDenomMetadata` query. Only chains + running CosmWasm `1.3.0` or higher support this. diff --git a/docs/USING_COSMWASM_STD.md b/docs/USING_COSMWASM_STD.md index 45841e908b..28f91a51f6 100644 --- a/docs/USING_COSMWASM_STD.md +++ b/docs/USING_COSMWASM_STD.md @@ -34,6 +34,7 @@ in the dependency tree. Otherwise conflicting C exports are created. The libarary comes with the following features: +<<<<<<< HEAD | Feature | Enabled by default | Description | | ------------ | ------------------ | -------------------------------------------------------------------------- | | iterator | x | Storage iterators | @@ -44,6 +45,19 @@ The libarary comes with the following features: | baktraces | | Add backtraces to errors (for unit testing) | | cosmwasm_1_1 | | Features that require CosmWasm 1.1+ on the chain | | cosmwasm_1_2 | | Features that require CosmWasm 1.2+ on the chain | +======= +| Feature | Enabled by default | Description | +| ------------ | ------------------ | ------------------------------------------------------------------------- | +| iterator | x | Storage iterators | +| abort | x | A panic handler that aborts the contract execution with a helpful message | +| stargate | | Cosmos SDK 0.40+ features and IBC | +| ibc3 | | New fields added in IBC v3 | +| staking | | Access to the staking module | +| backtraces | | Add backtraces to errors (for unit testing) | +| cosmwasm_1_1 | | Features that require CosmWasm 1.1+ on the chain | +| cosmwasm_1_2 | | Features that require CosmWasm 1.2+ on the chain | +| cosmwasm_1_3 | | Features that require CosmWasm 1.3+ on the chain | +>>>>>>> 4bac1e3c (std: Add BankQuery::AllDenomMetadata gated by cosmwasm_1_3 feature flag) ## The cosmwasm-std dependency for contract developers diff --git a/packages/check/src/main.rs b/packages/check/src/main.rs index c1f7da1784..f3efc4355c 100644 --- a/packages/check/src/main.rs +++ b/packages/check/src/main.rs @@ -10,7 +10,8 @@ use colored::Colorize; use cosmwasm_vm::capabilities_from_csv; use cosmwasm_vm::internals::{check_wasm, compile}; -const DEFAULT_AVAILABLE_CAPABILITIES: &str = "iterator,staking,stargate,cosmwasm_1_1,cosmwasm_1_2"; +const DEFAULT_AVAILABLE_CAPABILITIES: &str = + "iterator,staking,stargate,cosmwasm_1_1,cosmwasm_1_2,cosmwasm_1_3"; pub fn main() { let matches = App::new("Contract checking") diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index d5929f0ee6..74764ddfa8 100644 --- a/packages/std/Cargo.toml +++ b/packages/std/Cargo.toml @@ -39,6 +39,9 @@ cosmwasm_1_1 = [] # This feature makes `GovMsg::VoteWeighted` available for the contract to call, but requires # the host blockchain to run CosmWasm `1.2.0` or higher. cosmwasm_1_2 = ["cosmwasm_1_1"] +# This feature makes `BankQuery::DenomMetadata` available for the contract to call, but requires +# the host blockchain to run CosmWasm `1.3.0` or higher. +cosmwasm_1_3 = ["cosmwasm_1_2"] [dependencies] base64 = "0.13.0" diff --git a/packages/std/src/exports.rs b/packages/std/src/exports.rs index 5b4b30925c..27c46768dc 100644 --- a/packages/std/src/exports.rs +++ b/packages/std/src/exports.rs @@ -49,6 +49,10 @@ extern "C" fn requires_cosmwasm_1_1() -> () {} #[no_mangle] extern "C" fn requires_cosmwasm_1_2() -> () {} +#[cfg(feature = "cosmwasm_1_3")] +#[no_mangle] +extern "C" fn requires_cosmwasm_1_3() -> () {} + /// interface_version_* exports mark which Wasm VM interface level this contract is compiled for. /// They can be checked by cosmwasm_vm. /// Update this whenever the Wasm VM interface breaks. diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index 98e8e45f9b..b3494ea65b 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -18,6 +18,7 @@ mod import_helpers; #[cfg(feature = "iterator")] mod iterator; mod math; +mod metadata; mod never; mod panic; mod query; @@ -53,6 +54,7 @@ pub use crate::math::{ Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Isqrt, Uint128, Uint256, Uint512, Uint64, }; +pub use crate::metadata::{DenomMetadata, DenomUnit}; pub use crate::never::Never; #[cfg(feature = "cosmwasm_1_2")] pub use crate::query::CodeInfoResponse; diff --git a/packages/std/src/metadata.rs b/packages/std/src/metadata.rs new file mode 100644 index 0000000000..d3400a49a5 --- /dev/null +++ b/packages/std/src/metadata.rs @@ -0,0 +1,23 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::math::Uint128; + +#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)] +pub struct DenomMetadata { + pub description: String, + pub denom_units: Vec, + pub base: String, + pub display: String, + pub name: String, + pub symbol: String, + pub uri: String, + pub uri_hash: String, +} + +#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)] +pub struct DenomUnit { + pub denom: String, + pub exponent: Uint128, + pub aliases: Vec, +} diff --git a/packages/std/src/query/bank.rs b/packages/std/src/query/bank.rs index b3a43e11d5..222fc86e17 100644 --- a/packages/std/src/query/bank.rs +++ b/packages/std/src/query/bank.rs @@ -3,6 +3,9 @@ use serde::{Deserialize, Serialize}; use crate::Coin; +#[cfg(feature = "cosmwasm_1_3")] +use crate::DenomMetadata; + use super::query_response::QueryResponseType; #[non_exhaustive] @@ -21,6 +24,10 @@ pub enum BankQuery { /// Note that this may be much more expensive than Balance and should be avoided if possible. /// Return value is AllBalanceResponse. AllBalances { address: String }, + /// This calls into the native bank module for querying metadata for all bank tokens. + /// Return value is AllDenomMetadataResponse + #[cfg(feature = "cosmwasm_1_3")] + AllDenomMetadata {}, } #[cfg(feature = "cosmwasm_1_1")] @@ -54,3 +61,15 @@ pub struct AllBalanceResponse { } impl QueryResponseType for AllBalanceResponse {} + +#[cfg(feature = "cosmwasm_1_3")] +#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +#[non_exhaustive] +pub struct AllDenomMetadataResponse { + /// Always returns metadata for all token denoms on the base chain. + pub metadata: Vec, +} + +#[cfg(feature = "cosmwasm_1_3")] +impl QueryResponseType for AllDenomMetadataResponse {} diff --git a/packages/std/src/query/mod.rs b/packages/std/src/query/mod.rs index 50061df091..e79417bc99 100644 --- a/packages/std/src/query/mod.rs +++ b/packages/std/src/query/mod.rs @@ -11,6 +11,8 @@ mod query_response; mod staking; mod wasm; +#[cfg(feature = "cosmwasm_1_3")] +pub use bank::AllDenomMetadataResponse; #[cfg(feature = "cosmwasm_1_1")] pub use bank::SupplyResponse; pub use bank::{AllBalanceResponse, BalanceResponse, BankQuery}; diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index d219236578..c8bcf67f00 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -16,6 +16,8 @@ use crate::ibc::{ IbcTimeoutBlock, }; use crate::math::Uint128; +#[cfg(feature = "cosmwasm_1_3")] +use crate::query::AllDenomMetadataResponse; #[cfg(feature = "cosmwasm_1_1")] use crate::query::SupplyResponse; use crate::query::{ @@ -32,7 +34,7 @@ use crate::storage::MemoryStorage; use crate::timestamp::Timestamp; use crate::traits::{Api, Querier, QuerierResult}; use crate::types::{BlockInfo, ContractInfo, Env, MessageInfo, TransactionInfo}; -use crate::Attribute; +use crate::{Attribute, DenomMetadata}; #[cfg(feature = "stargate")] use crate::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdResponse}; @@ -599,6 +601,8 @@ pub struct BankQuerier { supplies: HashMap, /// HashMap balances: HashMap>, + /// Vec + denom_metadata: Vec, } impl BankQuerier { @@ -611,6 +615,7 @@ impl BankQuerier { BankQuerier { supplies: Self::calculate_supplies(&balances), balances, + denom_metadata: Vec::new(), } } @@ -625,6 +630,10 @@ impl BankQuerier { result } + pub fn set_denom_metadata(&mut self, denom_metadata: &[DenomMetadata]) { + self.denom_metadata = denom_metadata.to_vec(); + } + fn calculate_supplies(balances: &HashMap>) -> HashMap { let mut supplies = HashMap::new(); @@ -678,6 +687,13 @@ impl BankQuerier { }; to_binary(&bank_res).into() } + #[cfg(feature = "cosmwasm_1_3")] + BankQuery::AllDenomMetadata {} => { + let metadata_res = AllDenomMetadataResponse { + metadata: self.denom_metadata.clone(), + }; + to_binary(&metadata_res).into() + } }; // system result is always ok in the mock implementation SystemResult::Ok(contract_result) diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs index e3ba067b6e..79db30f6f5 100644 --- a/packages/std/src/traits.rs +++ b/packages/std/src/traits.rs @@ -8,6 +8,8 @@ use crate::coin::Coin; use crate::errors::{RecoverPubkeyError, StdError, StdResult, VerificationError}; #[cfg(feature = "iterator")] use crate::iterator::{Order, Record}; +#[cfg(feature = "cosmwasm_1_3")] +use crate::query::AllDenomMetadataResponse; #[cfg(feature = "cosmwasm_1_2")] use crate::query::CodeInfoResponse; #[cfg(feature = "cosmwasm_1_1")] @@ -23,6 +25,8 @@ use crate::query::{ use crate::results::{ContractResult, Empty, SystemResult}; use crate::serde::{from_binary, to_binary, to_vec}; use crate::ContractInfoResponse; +#[cfg(feature = "cosmwasm_1_3")] +use crate::DenomMetadata; /// Storage provides read and write access to a persistent storage. /// If you only want to provide read access, provide `&Storage` @@ -239,6 +243,13 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { Ok(res.amount) } + #[cfg(feature = "cosmwasm_1_3")] + pub fn query_all_denom_metadata(&self) -> StdResult> { + let request = BankQuery::AllDenomMetadata {}.into(); + let res: AllDenomMetadataResponse = self.query(&request)?; + Ok(res.metadata) + } + // this queries another wasm contract. You should know a priori the proper types for T and U // (response and request) based on the contract API pub fn query_wasm_smart( diff --git a/packages/vm/src/testing/instance.rs b/packages/vm/src/testing/instance.rs index 14b85cf7c9..6a4f11a37f 100644 --- a/packages/vm/src/testing/instance.rs +++ b/packages/vm/src/testing/instance.rs @@ -98,7 +98,8 @@ pub struct MockInstanceOptions<'a> { impl MockInstanceOptions<'_> { fn default_capabilities() -> HashSet { #[allow(unused_mut)] - let mut out = capabilities_from_csv("iterator,staking,cosmwasm_1_1,cosmwasm_1_2"); + let mut out = + capabilities_from_csv("iterator,staking,cosmwasm_1_1,cosmwasm_1_2,cosmwasm_1_3"); #[cfg(feature = "stargate")] out.insert("stargate".to_string()); out From 79a4327dce633814e9049d3ff50f99fa933c1ed8 Mon Sep 17 00:00:00 2001 From: Nikhil Suri Date: Wed, 29 Mar 2023 22:59:54 +0000 Subject: [PATCH 041/113] std: Update BankQuery::AllDenomMetadata query with pagination and add BankQuery::DenomMetadata also gated by cosmwasm_1_3 feature flag (cherry picked from commit 71c5a074cc5f5857a2e4c84b2cf7dd752517f757) --- MIGRATING.md | 31 +++++++++++++++++++++++++++++-- packages/std/src/lib.rs | 2 ++ packages/std/src/metadata.rs | 6 +++--- packages/std/src/pagination.rs | 14 ++++++++++++++ packages/std/src/query/bank.rs | 23 +++++++++++++++++++++-- packages/std/src/query/mod.rs | 4 ++-- packages/std/src/testing/mock.rs | 19 ++++++++++++++++--- packages/std/src/traits.rs | 23 +++++++++++++++++++---- 8 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 packages/std/src/pagination.rs diff --git a/MIGRATING.md b/MIGRATING.md index c65a29de5c..33b1af3036 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -4,6 +4,33 @@ 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.2.x -> 1.3.0 + +- Update `cosmwasm-*` dependencies in Cargo.toml (skip the ones you don't use): + + ``` + [dependencies] + cosmwasm-std = "1.3.0" + cosmwasm-storage = "1.3.0" + # ... + + [dev-dependencies] + cosmwasm-schema = "1.3.0" + cosmwasm-vm = "1.3.0" + # ... + ``` + +- If you want to use a fewture that os only available on CosmWasm 1.3+ chains, + use this feature: + + ```diff + -cosmwasm-std = { version = "1.1.0", features = ["stargate"] } + +cosmwasm-std = { version = "1.1.0", features = ["stargate", "cosmwasm_1_3"] } + ``` + + Please note that `cosmwasm_1_2` implies `cosmwasm_1_1`, and `cosmwasm_1_3` + implies `cosmwasm_1_2`, and so on, so there is no need to set multiple. + ## 1.1.x -> 1.2.0 - Update `cosmwasm-*` dependencies in Cargo.toml (skip the ones you don't use): @@ -28,8 +55,8 @@ major releases of `cosmwasm`. Note that you can also view the +cosmwasm-std = { version = "1.1.0", features = ["stargate", "cosmwasm_1_2"] } ``` - Please note that `cosmwasm_1_2` implies `cosmwasm_1_1`, and `cosmwasm_1_3` - implies `cosmwasm_1_2`, and so on, so there is no need to set multiple. + Please note that `cosmwasm_1_2` implies `cosmwasm_1_1`, so there is no need to + set both. - If you use mixed type multiplication between `Uint{64,128,256}` and `Decimal{,256}`, check out diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index b3494ea65b..28dfcc9ec1 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -20,6 +20,7 @@ mod iterator; mod math; mod metadata; mod never; +mod pagination; mod panic; mod query; mod results; @@ -56,6 +57,7 @@ pub use crate::math::{ }; pub use crate::metadata::{DenomMetadata, DenomUnit}; pub use crate::never::Never; +pub use crate::pagination::PageRequest; #[cfg(feature = "cosmwasm_1_2")] pub use crate::query::CodeInfoResponse; #[cfg(feature = "cosmwasm_1_1")] diff --git a/packages/std/src/metadata.rs b/packages/std/src/metadata.rs index d3400a49a5..c0827ce722 100644 --- a/packages/std/src/metadata.rs +++ b/packages/std/src/metadata.rs @@ -1,8 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::math::Uint128; - +/// Replicates the cosmos-sdk bank module Metadata type #[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)] pub struct DenomMetadata { pub description: String, @@ -15,9 +14,10 @@ pub struct DenomMetadata { pub uri_hash: String, } +/// Replicates the cosmos-sdk bank module DenomUnit type #[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)] pub struct DenomUnit { pub denom: String, - pub exponent: Uint128, + pub exponent: u32, pub aliases: Vec, } diff --git a/packages/std/src/pagination.rs b/packages/std/src/pagination.rs new file mode 100644 index 0000000000..3c98de73b6 --- /dev/null +++ b/packages/std/src/pagination.rs @@ -0,0 +1,14 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::{Binary, Uint64}; + +/// Replicates the PageRequest type for pagination from the cosmos-sdk +#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)] +pub struct PageRequest { + pub key: Binary, + pub offset: Uint64, + pub limit: Uint64, + pub count_total: bool, + pub reverse: bool, +} diff --git a/packages/std/src/query/bank.rs b/packages/std/src/query/bank.rs index 222fc86e17..61bfcbf2ae 100644 --- a/packages/std/src/query/bank.rs +++ b/packages/std/src/query/bank.rs @@ -6,6 +6,9 @@ use crate::Coin; #[cfg(feature = "cosmwasm_1_3")] use crate::DenomMetadata; +#[cfg(feature = "cosmwasm_1_3")] +use crate::PageRequest; + use super::query_response::QueryResponseType; #[non_exhaustive] @@ -24,10 +27,14 @@ pub enum BankQuery { /// Note that this may be much more expensive than Balance and should be avoided if possible. /// Return value is AllBalanceResponse. AllBalances { address: String }, - /// This calls into the native bank module for querying metadata for all bank tokens. + /// This calls into the native bank module for querying metadata for a specific bank token. + /// Return value is DenomMetadataResponse + #[cfg(feature = "cosmwasm_1_3")] + DenomMetadata { denom: String }, + /// This calls into the native bank module for querying metadata for all bank tokens that have a metadata entry. /// Return value is AllDenomMetadataResponse #[cfg(feature = "cosmwasm_1_3")] - AllDenomMetadata {}, + AllDenomMetadata { pagination: Option }, } #[cfg(feature = "cosmwasm_1_1")] @@ -62,6 +69,18 @@ pub struct AllBalanceResponse { impl QueryResponseType for AllBalanceResponse {} +#[cfg(feature = "cosmwasm_1_3")] +#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +#[non_exhaustive] +pub struct DenomMetadataResponse { + /// Always returns metadata for all token denoms on the base chain. + pub metadata: DenomMetadata, +} + +#[cfg(feature = "cosmwasm_1_3")] +impl QueryResponseType for DenomMetadataResponse {} + #[cfg(feature = "cosmwasm_1_3")] #[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] diff --git a/packages/std/src/query/mod.rs b/packages/std/src/query/mod.rs index e79417bc99..c88e209b8f 100644 --- a/packages/std/src/query/mod.rs +++ b/packages/std/src/query/mod.rs @@ -11,11 +11,11 @@ mod query_response; mod staking; mod wasm; -#[cfg(feature = "cosmwasm_1_3")] -pub use bank::AllDenomMetadataResponse; #[cfg(feature = "cosmwasm_1_1")] pub use bank::SupplyResponse; pub use bank::{AllBalanceResponse, BalanceResponse, BankQuery}; +#[cfg(feature = "cosmwasm_1_3")] +pub use bank::{AllDenomMetadataResponse, DenomMetadataResponse}; #[cfg(feature = "stargate")] pub use ibc::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdResponse}; #[cfg(feature = "staking")] diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index c8bcf67f00..1156268643 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -16,8 +16,6 @@ use crate::ibc::{ IbcTimeoutBlock, }; use crate::math::Uint128; -#[cfg(feature = "cosmwasm_1_3")] -use crate::query::AllDenomMetadataResponse; #[cfg(feature = "cosmwasm_1_1")] use crate::query::SupplyResponse; use crate::query::{ @@ -28,6 +26,8 @@ use crate::query::{ AllDelegationsResponse, AllValidatorsResponse, BondedDenomResponse, DelegationResponse, FullDelegation, StakingQuery, Validator, ValidatorResponse, }; +#[cfg(feature = "cosmwasm_1_3")] +use crate::query::{AllDenomMetadataResponse, DenomMetadataResponse}; use crate::results::{ContractResult, Empty, SystemResult}; use crate::serde::{from_slice, to_binary}; use crate::storage::MemoryStorage; @@ -688,7 +688,20 @@ impl BankQuerier { to_binary(&bank_res).into() } #[cfg(feature = "cosmwasm_1_3")] - BankQuery::AllDenomMetadata {} => { + BankQuery::DenomMetadata { denom } => { + let denom_metadata = self.denom_metadata.iter().find(|m| &m.base == denom); + match denom_metadata { + Some(m) => { + let metadata_res = DenomMetadataResponse { + metadata: m.clone(), + }; + to_binary(&metadata_res).into() + } + None => return SystemResult::Err(SystemError::Unknown {}), + } + } + #[cfg(feature = "cosmwasm_1_3")] + BankQuery::AllDenomMetadata { pagination: _ } => { let metadata_res = AllDenomMetadataResponse { metadata: self.denom_metadata.clone(), }; diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs index 79db30f6f5..2c153bf8d6 100644 --- a/packages/std/src/traits.rs +++ b/packages/std/src/traits.rs @@ -8,8 +8,6 @@ use crate::coin::Coin; use crate::errors::{RecoverPubkeyError, StdError, StdResult, VerificationError}; #[cfg(feature = "iterator")] use crate::iterator::{Order, Record}; -#[cfg(feature = "cosmwasm_1_3")] -use crate::query::AllDenomMetadataResponse; #[cfg(feature = "cosmwasm_1_2")] use crate::query::CodeInfoResponse; #[cfg(feature = "cosmwasm_1_1")] @@ -22,11 +20,15 @@ use crate::query::{ AllDelegationsResponse, AllValidatorsResponse, BondedDenomResponse, Delegation, DelegationResponse, FullDelegation, StakingQuery, Validator, ValidatorResponse, }; +#[cfg(feature = "cosmwasm_1_3")] +use crate::query::{AllDenomMetadataResponse, DenomMetadataResponse}; use crate::results::{ContractResult, Empty, SystemResult}; use crate::serde::{from_binary, to_binary, to_vec}; use crate::ContractInfoResponse; #[cfg(feature = "cosmwasm_1_3")] use crate::DenomMetadata; +#[cfg(feature = "cosmwasm_1_3")] +use crate::PageRequest; /// Storage provides read and write access to a persistent storage. /// If you only want to provide read access, provide `&Storage` @@ -244,8 +246,21 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { } #[cfg(feature = "cosmwasm_1_3")] - pub fn query_all_denom_metadata(&self) -> StdResult> { - let request = BankQuery::AllDenomMetadata {}.into(); + pub fn query_denom_metadata(&self, denom: impl Into) -> StdResult { + let request = BankQuery::DenomMetadata { + denom: denom.into(), + } + .into(); + let res: DenomMetadataResponse = self.query(&request)?; + Ok(res.metadata) + } + + #[cfg(feature = "cosmwasm_1_3")] + pub fn query_all_denom_metadata( + &self, + pagination: Option, + ) -> StdResult> { + let request = BankQuery::AllDenomMetadata { pagination }.into(); let res: AllDenomMetadataResponse = self.query(&request)?; Ok(res.metadata) } From dc9313e2bc4febf51ce3256568d647dd781c460f Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 31 May 2023 12:20:03 +0200 Subject: [PATCH 042/113] DenomMetadata testing (cherry picked from commit 83baec3dcbd2701bfdc6b5a89dde69b3df9f0e4b) --- packages/std/src/iterator.rs | 2 +- packages/std/src/pagination.rs | 6 ++---- packages/std/src/query/bank.rs | 6 ++---- packages/std/src/testing/mock.rs | 1 + packages/std/src/traits.rs | 16 ++++++++-------- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/std/src/iterator.rs b/packages/std/src/iterator.rs index e747581606..00190aa5ca 100644 --- a/packages/std/src/iterator.rs +++ b/packages/std/src/iterator.rs @@ -6,7 +6,7 @@ use crate::errors::StdError; /// allows contracts to reuse the type when deserializing database records. pub type Record> = (Vec, V); -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq)] // We assign these to integers to provide a stable API for passing over FFI (to wasm and Go) pub enum Order { Ascending = 1, diff --git a/packages/std/src/pagination.rs b/packages/std/src/pagination.rs index 3c98de73b6..e15d63eeaa 100644 --- a/packages/std/src/pagination.rs +++ b/packages/std/src/pagination.rs @@ -3,12 +3,10 @@ use serde::{Deserialize, Serialize}; use crate::{Binary, Uint64}; -/// Replicates the PageRequest type for pagination from the cosmos-sdk +/// Simplified version of the PageRequest type for pagination from the cosmos-sdk #[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)] pub struct PageRequest { - pub key: Binary, - pub offset: Uint64, + pub key: Option, pub limit: Uint64, - pub count_total: bool, pub reverse: bool, } diff --git a/packages/std/src/query/bank.rs b/packages/std/src/query/bank.rs index 61bfcbf2ae..150e0e4b44 100644 --- a/packages/std/src/query/bank.rs +++ b/packages/std/src/query/bank.rs @@ -4,10 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::Coin; #[cfg(feature = "cosmwasm_1_3")] -use crate::DenomMetadata; - -#[cfg(feature = "cosmwasm_1_3")] -use crate::PageRequest; +use crate::{Binary, DenomMetadata, PageRequest}; use super::query_response::QueryResponseType; @@ -88,6 +85,7 @@ impl QueryResponseType for DenomMetadataResponse {} pub struct AllDenomMetadataResponse { /// Always returns metadata for all token denoms on the base chain. pub metadata: Vec, + pub next_key: Option, } #[cfg(feature = "cosmwasm_1_3")] diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index 1156268643..58f86c7cc9 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -703,6 +703,7 @@ impl BankQuerier { #[cfg(feature = "cosmwasm_1_3")] BankQuery::AllDenomMetadata { pagination: _ } => { let metadata_res = AllDenomMetadataResponse { + next_key: None, metadata: self.denom_metadata.clone(), }; to_binary(&metadata_res).into() diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs index 2c153bf8d6..7181036cb8 100644 --- a/packages/std/src/traits.rs +++ b/packages/std/src/traits.rs @@ -26,9 +26,7 @@ use crate::results::{ContractResult, Empty, SystemResult}; use crate::serde::{from_binary, to_binary, to_vec}; use crate::ContractInfoResponse; #[cfg(feature = "cosmwasm_1_3")] -use crate::DenomMetadata; -#[cfg(feature = "cosmwasm_1_3")] -use crate::PageRequest; +use crate::{DenomMetadata, PageRequest}; /// Storage provides read and write access to a persistent storage. /// If you only want to provide read access, provide `&Storage` @@ -258,11 +256,13 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { #[cfg(feature = "cosmwasm_1_3")] pub fn query_all_denom_metadata( &self, - pagination: Option, - ) -> StdResult> { - let request = BankQuery::AllDenomMetadata { pagination }.into(); - let res: AllDenomMetadataResponse = self.query(&request)?; - Ok(res.metadata) + pagination: PageRequest, + ) -> StdResult { + let request = BankQuery::AllDenomMetadata { + pagination: Some(pagination), + } + .into(); + self.query(&request) } // this queries another wasm contract. You should know a priori the proper types for T and U From aeee0f3021116a31429e214b71b36e36093e0ef1 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 31 May 2023 15:48:06 +0200 Subject: [PATCH 043/113] Add paginated mock for AllDenomMetadata query (cherry picked from commit 74e4bd690c4bb845ce0334e62edbb2831af431c8) --- packages/std/src/testing/mock.rs | 115 +++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 6 deletions(-) diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index 58f86c7cc9..14ab622999 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -26,14 +26,17 @@ use crate::query::{ AllDelegationsResponse, AllValidatorsResponse, BondedDenomResponse, DelegationResponse, FullDelegation, StakingQuery, Validator, ValidatorResponse, }; -#[cfg(feature = "cosmwasm_1_3")] -use crate::query::{AllDenomMetadataResponse, DenomMetadataResponse}; use crate::results::{ContractResult, Empty, SystemResult}; use crate::serde::{from_slice, to_binary}; use crate::storage::MemoryStorage; use crate::timestamp::Timestamp; use crate::traits::{Api, Querier, QuerierResult}; use crate::types::{BlockInfo, ContractInfo, Env, MessageInfo, TransactionInfo}; +#[cfg(feature = "cosmwasm_1_3")] +use crate::{ + query::{AllDenomMetadataResponse, DenomMetadataResponse}, + PageRequest, Uint64, +}; use crate::{Attribute, DenomMetadata}; #[cfg(feature = "stargate")] use crate::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdResponse}; @@ -701,11 +704,38 @@ impl BankQuerier { } } #[cfg(feature = "cosmwasm_1_3")] - BankQuery::AllDenomMetadata { pagination: _ } => { - let metadata_res = AllDenomMetadataResponse { - next_key: None, - metadata: self.denom_metadata.clone(), + BankQuery::AllDenomMetadata { pagination } => { + let default_pagination = PageRequest { + key: None, + limit: Uint64::new(100), + reverse: false, + }; + let pagination = pagination.as_ref().unwrap_or(&default_pagination); + // using dynamic dispatch here to reduce code duplication and since this is only testing code + let iter: Box> = if pagination.reverse { + Box::new(self.denom_metadata.iter().rev().cloned()) + } else { + Box::new(self.denom_metadata.iter().cloned()) }; + let mut metadata: Vec<_> = iter + // skip until we find the key + .skip_while(|m| match &pagination.key { + Some(key) => m.symbol.as_bytes() < key, + None => false, + }) + // take the requested amount + 1 to get the next key + .take((pagination.limit.u64().saturating_add(1)) as usize) + .collect(); + + // if we took more than requested, remove the last element (the next key), + // otherwise this is the last batch + let next_key = if metadata.len() > pagination.limit.u64() as usize { + metadata.pop().map(|m| Binary::from(m.name.as_bytes())) + } else { + None + }; + + let metadata_res = AllDenomMetadataResponse { metadata, next_key }; to_binary(&metadata_res).into() } }; @@ -865,6 +895,8 @@ pub fn mock_wasmd_attr(key: impl Into, value: impl Into) -> Attr #[cfg(test)] mod tests { use super::*; + #[cfg(feature = "cosmwasm_1_3")] + use crate::DenomUnit; use crate::{coin, coins, from_binary, to_binary, ContractInfoResponse, Response}; #[cfg(feature = "staking")] use crate::{Decimal, Delegation}; @@ -1292,6 +1324,77 @@ mod tests { assert_eq!(res.amount, coin(0, "ELF")); } + #[cfg(feature = "cosmwasm_1_3")] + #[test] + fn bank_querier_metadata_works() { + let mut bank = BankQuerier::new(&[]); + bank.set_denom_metadata( + &(0..100) + .map(|i| DenomMetadata { + symbol: format!("FOO{i}"), + name: "Foo".to_string(), + description: "Foo coin".to_string(), + denom_units: vec![DenomUnit { + denom: "ufoo".to_string(), + exponent: 8, + aliases: vec!["microfoo".to_string(), "foobar".to_string()], + }], + display: "FOO".to_string(), + base: "ufoo".to_string(), + uri: "https://foo.bar".to_string(), + uri_hash: "foo".to_string(), + }) + .collect::>(), + ); + + // querying first 10 should work + let res = bank + .query(&BankQuery::AllDenomMetadata { + pagination: Some(PageRequest { + key: None, + limit: Uint64::new(10), + reverse: false, + }), + }) + .unwrap() + .unwrap(); + let res: AllDenomMetadataResponse = from_binary(&res).unwrap(); + assert_eq!(res.metadata.len(), 10); + assert!(res.next_key.is_some()); + + // querying all 100 should work + let res = bank + .query(&BankQuery::AllDenomMetadata { + pagination: Some(PageRequest { + key: None, + limit: Uint64::new(100), + reverse: true, + }), + }) + .unwrap() + .unwrap(); + let res: AllDenomMetadataResponse = from_binary(&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"); + + let more_res = bank + .query(&BankQuery::AllDenomMetadata { + pagination: Some(PageRequest { + key: res.next_key, + limit: Uint64::MAX, + reverse: true, + }), + }) + .unwrap() + .unwrap(); + let more_res: AllDenomMetadataResponse = from_binary(&more_res).unwrap(); + assert_eq!( + more_res.metadata, res.metadata, + "should be same as previous query" + ); + } + #[cfg(feature = "stargate")] #[test] fn ibc_querier_channel_existing() { From 175fbcd18291ae8232d1c1c7d17bee8c41f2bed1 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 31 May 2023 15:49:29 +0200 Subject: [PATCH 044/113] Set cosmwasm_1_3 feature for docs.rs (cherry picked from commit 751173db1f8e5532efd9c77207de82c78921a819) --- packages/std/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index 74764ddfa8..af772d64e4 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_2"] +features = ["abort", "stargate", "staking", "ibc3", "cosmwasm_1_3"] [features] default = ["iterator", "abort"] From 6c7f13b074f628fda52269bfdfed316331456eed Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 31 May 2023 15:53:53 +0200 Subject: [PATCH 045/113] Add cosmwasm_1_3 to rust analyzer (cherry picked from commit 5e146b0bcd350635a3b161e9aeb47d842b46fca1) --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4fc9b25774..3105948a84 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.cargo.features": ["abort", "stargate", "staking", "cosmwasm_1_2"] + "rust-analyzer.cargo.features": ["abort", "stargate", "staking", "cosmwasm_1_3"] } From ef4fa7832cf53ee1d0a0a03151d935c14074ad03 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 31 May 2023 16:33:15 +0200 Subject: [PATCH 046/113] Fix all denoms metadata mock (cherry picked from commit ea6d82c609b442b03f546a2f9d5aa36dbb47da14) --- packages/std/src/testing/mock.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index 14ab622999..66ef04c68b 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -730,7 +730,7 @@ impl BankQuerier { // if we took more than requested, remove the last element (the next key), // otherwise this is the last batch let next_key = if metadata.len() > pagination.limit.u64() as usize { - metadata.pop().map(|m| Binary::from(m.name.as_bytes())) + metadata.pop().map(|m| Binary::from(m.symbol.as_bytes())) } else { None }; @@ -1362,6 +1362,21 @@ mod tests { assert_eq!(res.metadata.len(), 10); assert!(res.next_key.is_some()); + // querying next 10 should also work + let res2 = bank + .query(&BankQuery::AllDenomMetadata { + pagination: Some(PageRequest { + key: res.next_key, + limit: Uint64::new(10), + reverse: false, + }), + }) + .unwrap() + .unwrap(); + let res2: AllDenomMetadataResponse = from_binary(&res2).unwrap(); + assert_eq!(res2.metadata.len(), 10); + assert_ne!(res.metadata.last(), res2.metadata.first()); + // querying all 100 should work let res = bank .query(&BankQuery::AllDenomMetadata { From fbbfa6c7e93d20a5924aa910f5a6bb3b6ce17c77 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 5 Jun 2023 15:46:16 +0200 Subject: [PATCH 047/113] Apply suggestions from code review Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> (cherry picked from commit ebec623576e6dca551265be3c15f0e28891b47e6) --- MIGRATING.md | 6 +++--- packages/std/src/query/bank.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MIGRATING.md b/MIGRATING.md index 33b1af3036..ac9d8dd347 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -20,12 +20,12 @@ major releases of `cosmwasm`. Note that you can also view the # ... ``` -- If you want to use a fewture that os only available on CosmWasm 1.3+ chains, +- If you want to use a feature that is only available on CosmWasm 1.3+ chains, use this feature: ```diff - -cosmwasm-std = { version = "1.1.0", features = ["stargate"] } - +cosmwasm-std = { version = "1.1.0", features = ["stargate", "cosmwasm_1_3"] } + -cosmwasm-std = { version = "1.3.0", features = ["stargate"] } + +cosmwasm-std = { version = "1.3.0", features = ["stargate", "cosmwasm_1_3"] } ``` Please note that `cosmwasm_1_2` implies `cosmwasm_1_1`, and `cosmwasm_1_3` diff --git a/packages/std/src/query/bank.rs b/packages/std/src/query/bank.rs index 150e0e4b44..74d8b2b9d7 100644 --- a/packages/std/src/query/bank.rs +++ b/packages/std/src/query/bank.rs @@ -71,7 +71,7 @@ impl QueryResponseType for AllBalanceResponse {} #[serde(rename_all = "snake_case")] #[non_exhaustive] pub struct DenomMetadataResponse { - /// Always returns metadata for all token denoms on the base chain. + /// The metadata for the queried denom. pub metadata: DenomMetadata, } From 576897e8738cb16d455a744c8503cdcdae7e7055 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 5 Jun 2023 16:00:23 +0200 Subject: [PATCH 048/113] Add DenomMetadata query to changelog (cherry picked from commit 603dd1f0e3e7438e69c5a582aaa79388f2417c5f) --- CHANGELOG.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4158a9a3c..70b1137515 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,10 +11,11 @@ and this project adheres to <<<<<<< HEAD ======= - cosmwasm-std: Implement `BankQuery::AllDenomMetadata` to allow querying all - the denom metadata. In order to use this query in a contract, the - `cosmwasm_1_3` feature needs to be enabled for the `cosmwasm_std` dependency. - This makes the contract incompatible with chains running anything lower than - CosmWasm `1.3.0`. ([#1647]) + the denom metadata and `BankQuery::DenomMetadata` to query a specific one. In + order to use this query in a contract, the `cosmwasm_1_3` feature needs to be + enabled for the `cosmwasm_std` dependency. This makes the contract + incompatible with chains running anything lower than CosmWasm `1.3.0`. + ([#1647]) - cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have been checked before. This is useful for state-sync where we know the Wasm code was checked when it was first uploaded. ([#1635]) From fa505233645d0aa78c848b35e92d1060d650119e Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 5 Jun 2023 16:03:58 +0200 Subject: [PATCH 049/113] Add DenomMetadata to capability docs (cherry picked from commit ef2541944de87f377b06f4fb6ef904f8a8ce4418) --- docs/CAPABILITIES-BUILT-IN.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CAPABILITIES-BUILT-IN.md b/docs/CAPABILITIES-BUILT-IN.md index 190c9f825d..d822971807 100644 --- a/docs/CAPABILITIES-BUILT-IN.md +++ b/docs/CAPABILITIES-BUILT-IN.md @@ -15,5 +15,6 @@ might define others. CosmWasm `1.1.0` or higher support this. - `cosmwasm_1_2` enables the `GovMsg::VoteWeighted` and `WasmMsg::Instantiate2` messages. Only chains running CosmWasm `1.2.0` or higher support this. -- `cosmwasm_1_3` enables the `BankQuery::AllDenomMetadata` query. Only chains - running CosmWasm `1.3.0` or higher support this. +- `cosmwasm_1_3` enables the `BankQuery::AllDenomMetadata` and + `BankQuery::DenomMetadata` queries. Only chains running CosmWasm `1.3.0` or + higher support this. From 3f1432fefb7f4208cb36a0ba3b92fd4afcc5dbd7 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 9 Jun 2023 10:35:02 +0200 Subject: [PATCH 050/113] Use u32 for pagination limit (cherry picked from commit 0a9b745cd4132772f1ad682c0af0319ca7d12237) --- packages/std/src/pagination.rs | 4 ++-- packages/std/src/testing/mock.rs | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/std/src/pagination.rs b/packages/std/src/pagination.rs index e15d63eeaa..d9d1d8ef96 100644 --- a/packages/std/src/pagination.rs +++ b/packages/std/src/pagination.rs @@ -1,12 +1,12 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::{Binary, Uint64}; +use crate::Binary; /// Simplified version of the PageRequest type for pagination from the cosmos-sdk #[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)] pub struct PageRequest { pub key: Option, - pub limit: Uint64, + pub limit: u32, pub reverse: bool, } diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index 66ef04c68b..3bdcb5ea95 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -35,7 +35,7 @@ use crate::types::{BlockInfo, ContractInfo, Env, MessageInfo, TransactionInfo}; #[cfg(feature = "cosmwasm_1_3")] use crate::{ query::{AllDenomMetadataResponse, DenomMetadataResponse}, - PageRequest, Uint64, + PageRequest, }; use crate::{Attribute, DenomMetadata}; #[cfg(feature = "stargate")] @@ -707,7 +707,7 @@ impl BankQuerier { BankQuery::AllDenomMetadata { pagination } => { let default_pagination = PageRequest { key: None, - limit: Uint64::new(100), + limit: 100, reverse: false, }; let pagination = pagination.as_ref().unwrap_or(&default_pagination); @@ -724,12 +724,12 @@ impl BankQuerier { None => false, }) // take the requested amount + 1 to get the next key - .take((pagination.limit.u64().saturating_add(1)) as usize) + .take((pagination.limit.saturating_add(1)) as usize) .collect(); // if we took more than requested, remove the last element (the next key), // otherwise this is the last batch - let next_key = if metadata.len() > pagination.limit.u64() as usize { + let next_key = if metadata.len() > pagination.limit as usize { metadata.pop().map(|m| Binary::from(m.symbol.as_bytes())) } else { None @@ -1352,7 +1352,7 @@ mod tests { .query(&BankQuery::AllDenomMetadata { pagination: Some(PageRequest { key: None, - limit: Uint64::new(10), + limit: 10, reverse: false, }), }) @@ -1367,7 +1367,7 @@ mod tests { .query(&BankQuery::AllDenomMetadata { pagination: Some(PageRequest { key: res.next_key, - limit: Uint64::new(10), + limit: 10, reverse: false, }), }) @@ -1382,7 +1382,7 @@ mod tests { .query(&BankQuery::AllDenomMetadata { pagination: Some(PageRequest { key: None, - limit: Uint64::new(100), + limit: 100, reverse: true, }), }) @@ -1397,7 +1397,7 @@ mod tests { .query(&BankQuery::AllDenomMetadata { pagination: Some(PageRequest { key: res.next_key, - limit: Uint64::MAX, + limit: u32::MAX, reverse: true, }), }) From a2781fca462ca3a23546f76d8a824d3b25f2f472 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 22 Jun 2023 12:52:49 +0200 Subject: [PATCH 051/113] Fix bank mock querier (cherry picked from commit c5717a790534fbfc6bd75782404d1bb595055918) --- packages/std/src/testing/mock.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index 3bdcb5ea95..ff562c3375 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -478,6 +478,10 @@ impl MockQuerier { self.bank.update_balance(addr, balance) } + pub fn set_denom_metadata(&mut self, denom_metadata: &[DenomMetadata]) { + self.bank.set_denom_metadata(denom_metadata); + } + #[cfg(feature = "staking")] pub fn update_staking( &mut self, @@ -635,6 +639,8 @@ impl BankQuerier { pub fn set_denom_metadata(&mut self, denom_metadata: &[DenomMetadata]) { self.denom_metadata = denom_metadata.to_vec(); + self.denom_metadata + .sort_unstable_by(|a, b| a.symbol.cmp(&b.symbol)); } fn calculate_supplies(balances: &HashMap>) -> HashMap { @@ -1376,6 +1382,10 @@ mod tests { let res2: AllDenomMetadataResponse = from_binary(&res2).unwrap(); assert_eq!(res2.metadata.len(), 10); assert_ne!(res.metadata.last(), res2.metadata.first()); + // should have no overlap + for m in res.metadata { + assert!(!res2.metadata.contains(&m)); + } // querying all 100 should work let res = bank From 7deddfbeeb070f483750968c47c39b6a11a9da56 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 22 Jun 2023 12:55:12 +0200 Subject: [PATCH 052/113] Add Denoms query to cyberpunk contract (cherry picked from commit 7e96644e08d08f0e9f2fc16eb3b6b75670c1f97a) # Conflicts: # contracts/cyberpunk/src/contract.rs --- contracts/cyberpunk/Cargo.toml | 2 +- contracts/cyberpunk/src/contract.rs | 111 +++++++++++++++++++++++++++- contracts/cyberpunk/src/msg.rs | 4 + 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/contracts/cyberpunk/Cargo.toml b/contracts/cyberpunk/Cargo.toml index 32f43bf413..7bb0c3a514 100644 --- a/contracts/cyberpunk/Cargo.toml +++ b/contracts/cyberpunk/Cargo.toml @@ -30,7 +30,7 @@ backtraces = ["cosmwasm-std/backtraces", "cosmwasm-vm/backtraces"] [dependencies] cosmwasm-schema = { path = "../../packages/schema" } -cosmwasm-std = { path = "../../packages/std", default-features = false, features = ["abort"] } +cosmwasm-std = { path = "../../packages/std", default-features = false, features = ["abort", "cosmwasm_1_3"] } rust-argon2 = "0.8" thiserror = "1.0.26" diff --git a/contracts/cyberpunk/src/contract.rs b/contracts/cyberpunk/src/contract.rs index f5f1b98655..74d9d78f36 100644 --- a/contracts/cyberpunk/src/contract.rs +++ b/contracts/cyberpunk/src/contract.rs @@ -1,6 +1,11 @@ use cosmwasm_std::{ +<<<<<<< HEAD entry_point, to_binary, Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response, StdError, StdResult, WasmMsg, +======= + entry_point, to_binary, Api, Deps, DepsMut, Empty, Env, MessageInfo, PageRequest, + QueryResponse, Response, StdError, StdResult, WasmMsg, +>>>>>>> 7e96644e (Add Denoms query to cyberpunk contract) }; use crate::errors::ContractError; @@ -151,14 +156,118 @@ fn execute_mirror_env(env: Env) -> Result { } #[entry_point] -pub fn query(_deps: Deps, env: Env, msg: QueryMsg) -> StdResult { +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)?), } } fn query_mirror_env(env: Env) -> Env { env } +<<<<<<< HEAD +======= + +fn query_denoms(deps: Deps) -> StdResult<(String, Vec)> { + let metadata = deps.querier.query_denom_metadata("uatom")?; + + const PAGE_SIZE: u32 = 10; + let mut next_key = None; + let mut all_metadata = Vec::new(); + loop { + let page = deps.querier.query_all_denom_metadata(PageRequest { + key: next_key, + limit: PAGE_SIZE, + reverse: false, + })?; + + let len = page.metadata.len() as u32; + all_metadata.extend(page.metadata.into_iter().map(|m| m.symbol)); + next_key = page.next_key; + + if next_key.is_none() || len < PAGE_SIZE { + break; + } + } + + Ok((metadata.symbol, all_metadata)) +} + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::testing::{ + mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, + }; + use cosmwasm_std::{from_binary, DenomMetadata, DenomUnit, OwnedDeps}; + + fn setup() -> OwnedDeps { + let mut deps = mock_dependencies(); + let msg = Empty {}; + let info = mock_info("creator", &[]); + let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); + assert_eq!(0, res.messages.len()); + deps + } + + #[test] + fn instantiate_works() { + setup(); + } + + #[test] + fn debug_works() { + let mut deps = setup(); + + let msg = ExecuteMsg::Debug {}; + execute(deps.as_mut(), mock_env(), mock_info("caller", &[]), msg).unwrap(); + } + + #[test] + fn query_denoms_works() { + let mut deps = setup(); + + deps.querier.set_denom_metadata( + &(0..98) + .map(|i| DenomMetadata { + symbol: format!("FOO{i}"), + name: "Foo".to_string(), + description: "Foo coin".to_string(), + denom_units: vec![DenomUnit { + denom: "ufoo".to_string(), + exponent: 8, + aliases: vec!["microfoo".to_string(), "foobar".to_string()], + }], + display: "FOO".to_string(), + base: "ufoo".to_string(), + uri: "https://foo.bar".to_string(), + uri_hash: "foo".to_string(), + }) + .chain(std::iter::once(DenomMetadata { + description: "Atom".to_string(), + denom_units: vec![DenomUnit { + denom: "uatom".to_string(), + exponent: 8, + aliases: vec!["microatom".to_string()], + }], + base: "uatom".to_string(), + display: "ATOM".to_string(), + name: "Cosmos Atom".to_string(), + symbol: "ATOM".to_string(), + uri: "https://cosmos.network".to_string(), + uri_hash: "hash".to_string(), + })) + .collect::>(), + ); + + let (atom, symbols): (String, Vec) = + from_binary(&query(deps.as_ref(), mock_env(), QueryMsg::Denoms {}).unwrap()).unwrap(); + + assert_eq!(atom, "ATOM"); + assert_eq!(symbols.len(), 99); + } +} +>>>>>>> 7e96644e (Add Denoms query to cyberpunk contract) diff --git a/contracts/cyberpunk/src/msg.rs b/contracts/cyberpunk/src/msg.rs index 33d73a51b1..97c8cabb9c 100644 --- a/contracts/cyberpunk/src/msg.rs +++ b/contracts/cyberpunk/src/msg.rs @@ -36,4 +36,8 @@ pub enum QueryMsg { /// Returns the env for testing #[returns(cosmwasm_std::Env)] MirrorEnv {}, + + /// Queries `DenomMetadata` and `AllDenomMetadata` from the bank module and returns the symbols + #[returns((String, Vec))] + Denoms {}, } From c44c4952415f7a405fd2508dece09fe837b3b11b Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 22 Jun 2023 16:50:53 +0200 Subject: [PATCH 053/113] Use BTreeMap for denom metadata mock (cherry picked from commit acdd4e7b2d7d4c7d37f6ccb1873d090cd878c6cf) --- contracts/cyberpunk/src/contract.rs | 2 +- packages/std/src/testing/mock.rs | 42 +++++++++++++++++------------ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/contracts/cyberpunk/src/contract.rs b/contracts/cyberpunk/src/contract.rs index 74d9d78f36..bf866abb73 100644 --- a/contracts/cyberpunk/src/contract.rs +++ b/contracts/cyberpunk/src/contract.rs @@ -242,7 +242,7 @@ mod tests { aliases: vec!["microfoo".to_string(), "foobar".to_string()], }], display: "FOO".to_string(), - base: "ufoo".to_string(), + base: format!("ufoo{i}"), uri: "https://foo.bar".to_string(), uri_hash: "foo".to_string(), }) diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index ff562c3375..3b5ce6f253 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -1,8 +1,10 @@ use serde::de::DeserializeOwned; #[cfg(feature = "stargate")] use serde::Serialize; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::marker::PhantomData; +#[cfg(feature = "cosmwasm_1_3")] +use std::ops::Bound; use crate::addresses::{Addr, CanonicalAddr}; use crate::binary::Binary; @@ -609,7 +611,7 @@ pub struct BankQuerier { /// HashMap balances: HashMap>, /// Vec - denom_metadata: Vec, + denom_metadata: BTreeMap, DenomMetadata>, } impl BankQuerier { @@ -622,7 +624,7 @@ impl BankQuerier { BankQuerier { supplies: Self::calculate_supplies(&balances), balances, - denom_metadata: Vec::new(), + denom_metadata: BTreeMap::new(), } } @@ -638,9 +640,10 @@ impl BankQuerier { } pub fn set_denom_metadata(&mut self, denom_metadata: &[DenomMetadata]) { - self.denom_metadata = denom_metadata.to_vec(); - self.denom_metadata - .sort_unstable_by(|a, b| a.symbol.cmp(&b.symbol)); + self.denom_metadata = denom_metadata + .iter() + .map(|d| (d.base.as_bytes().to_vec(), d.clone())) + .collect(); } fn calculate_supplies(balances: &HashMap>) -> HashMap { @@ -698,7 +701,7 @@ impl BankQuerier { } #[cfg(feature = "cosmwasm_1_3")] BankQuery::DenomMetadata { denom } => { - let denom_metadata = self.denom_metadata.iter().find(|m| &m.base == denom); + let denom_metadata = self.denom_metadata.get(denom.as_bytes()); match denom_metadata { Some(m) => { let metadata_res = DenomMetadataResponse { @@ -717,26 +720,31 @@ impl BankQuerier { reverse: false, }; let pagination = pagination.as_ref().unwrap_or(&default_pagination); + + // range of all denoms after the given key (or until the key for reverse) + let range = match (pagination.reverse, &pagination.key) { + (_, None) => (Bound::Unbounded, Bound::Unbounded), + (true, Some(key)) => (Bound::Unbounded, Bound::Included(key.as_slice())), + (false, Some(key)) => (Bound::Included(key.as_slice()), Bound::Unbounded), + }; + let iter = self.denom_metadata.range::<[u8], _>(range); // using dynamic dispatch here to reduce code duplication and since this is only testing code - let iter: Box> = if pagination.reverse { - Box::new(self.denom_metadata.iter().rev().cloned()) + let iter: Box> = if pagination.reverse { + Box::new(iter.rev()) } else { - Box::new(self.denom_metadata.iter().cloned()) + Box::new(iter) }; + let mut metadata: Vec<_> = iter - // skip until we find the key - .skip_while(|m| match &pagination.key { - Some(key) => m.symbol.as_bytes() < key, - None => false, - }) // take the requested amount + 1 to get the next key .take((pagination.limit.saturating_add(1)) as usize) + .map(|(_, m)| m.clone()) .collect(); // if we took more than requested, remove the last element (the next key), // otherwise this is the last batch let next_key = if metadata.len() > pagination.limit as usize { - metadata.pop().map(|m| Binary::from(m.symbol.as_bytes())) + metadata.pop().map(|m| Binary::from(m.base.as_bytes())) } else { None }; @@ -1346,7 +1354,7 @@ mod tests { aliases: vec!["microfoo".to_string(), "foobar".to_string()], }], display: "FOO".to_string(), - base: "ufoo".to_string(), + base: format!("ufoo{i}"), uri: "https://foo.bar".to_string(), uri_hash: "foo".to_string(), }) From e5b6657b13db3c5ec3427806ddb660e17e90f682 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 22 Jun 2023 17:17:12 +0200 Subject: [PATCH 054/113] Split Denoms query up (cherry picked from commit feab42e450f21f823efd1444015f0ba84e0d7d6a) # Conflicts: # contracts/cyberpunk/src/contract.rs --- contracts/cyberpunk/src/contract.rs | 51 ++++++++++++++++------------- contracts/cyberpunk/src/msg.rs | 9 +++-- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/contracts/cyberpunk/src/contract.rs b/contracts/cyberpunk/src/contract.rs index bf866abb73..f8802f6c09 100644 --- a/contracts/cyberpunk/src/contract.rs +++ b/contracts/cyberpunk/src/contract.rs @@ -1,4 +1,5 @@ use cosmwasm_std::{ +<<<<<<< HEAD <<<<<<< HEAD entry_point, to_binary, Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response, StdError, StdResult, WasmMsg, @@ -6,6 +7,10 @@ use cosmwasm_std::{ entry_point, to_binary, Api, Deps, DepsMut, Empty, Env, MessageInfo, PageRequest, QueryResponse, Response, StdError, StdResult, WasmMsg, >>>>>>> 7e96644e (Add Denoms query to cyberpunk contract) +======= + entry_point, to_binary, Api, DenomMetadata, Deps, DepsMut, Empty, Env, MessageInfo, + PageRequest, QueryResponse, Response, StdError, StdResult, WasmMsg, +>>>>>>> feab42e4 (Split Denoms query up) }; use crate::errors::ContractError; @@ -162,6 +167,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { MirrorEnv {} => to_binary(&query_mirror_env(env)), Denoms {} => to_binary(&query_denoms(deps)?), + Denom { denom } => to_binary(&query_denom(deps, denom)?), } } @@ -171,9 +177,7 @@ fn query_mirror_env(env: Env) -> Env { <<<<<<< HEAD ======= -fn query_denoms(deps: Deps) -> StdResult<(String, Vec)> { - let metadata = deps.querier.query_denom_metadata("uatom")?; - +fn query_denoms(deps: Deps) -> StdResult> { const PAGE_SIZE: u32 = 10; let mut next_key = None; let mut all_metadata = Vec::new(); @@ -185,7 +189,7 @@ fn query_denoms(deps: Deps) -> StdResult<(String, Vec)> { })?; let len = page.metadata.len() as u32; - all_metadata.extend(page.metadata.into_iter().map(|m| m.symbol)); + all_metadata.extend(page.metadata.into_iter()); next_key = page.next_key; if next_key.is_none() || len < PAGE_SIZE { @@ -193,7 +197,11 @@ fn query_denoms(deps: Deps) -> StdResult<(String, Vec)> { } } - Ok((metadata.symbol, all_metadata)) + Ok(all_metadata) +} + +fn query_denom(deps: Deps, denom: String) -> StdResult { + deps.querier.query_denom_metadata(denom) } #[cfg(test)] @@ -246,28 +254,27 @@ mod tests { uri: "https://foo.bar".to_string(), uri_hash: "foo".to_string(), }) - .chain(std::iter::once(DenomMetadata { - description: "Atom".to_string(), - denom_units: vec![DenomUnit { - denom: "uatom".to_string(), - exponent: 8, - aliases: vec!["microatom".to_string()], - }], - base: "uatom".to_string(), - display: "ATOM".to_string(), - name: "Cosmos Atom".to_string(), - symbol: "ATOM".to_string(), - uri: "https://cosmos.network".to_string(), - uri_hash: "hash".to_string(), - })) .collect::>(), ); - let (atom, symbols): (String, Vec) = + let symbols: Vec = from_binary(&query(deps.as_ref(), mock_env(), QueryMsg::Denoms {}).unwrap()).unwrap(); - assert_eq!(atom, "ATOM"); - assert_eq!(symbols.len(), 99); + assert_eq!(symbols.len(), 98); + + let denom: DenomMetadata = from_binary( + &query( + deps.as_ref(), + mock_env(), + QueryMsg::Denom { + denom: "ufoo0".to_string(), + }, + ) + .unwrap(), + ) + .unwrap(); + + assert_eq!(denom.symbol, "FOO0"); } } >>>>>>> 7e96644e (Add Denoms query to cyberpunk contract) diff --git a/contracts/cyberpunk/src/msg.rs b/contracts/cyberpunk/src/msg.rs index 97c8cabb9c..e3a9366d73 100644 --- a/contracts/cyberpunk/src/msg.rs +++ b/contracts/cyberpunk/src/msg.rs @@ -1,4 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::DenomMetadata; #[cw_serde] pub enum ExecuteMsg { @@ -37,7 +38,11 @@ pub enum QueryMsg { #[returns(cosmwasm_std::Env)] MirrorEnv {}, - /// Queries `DenomMetadata` and `AllDenomMetadata` from the bank module and returns the symbols - #[returns((String, Vec))] + /// Queries `AllDenomMetadata` from the bank module repeatedly and returns all entries + #[returns(Vec)] Denoms {}, + + /// Queries `DenomMetadata` from the bank module and returns the result + #[returns(DenomMetadata)] + Denom { denom: String }, } From daf6de8056709b64e3facc9db95e5ca33198aa72 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 22 Jun 2023 17:17:32 +0200 Subject: [PATCH 055/113] Update cyberpunk schemas (cherry picked from commit 0d0f8199fa50391a4ccd149f7116e63b2b0bfd4f) --- contracts/cyberpunk/schema/cyberpunk.json | 186 ++++++++++++++++++ contracts/cyberpunk/schema/raw/query.json | 36 ++++ .../schema/raw/response_to_denom.json | 72 +++++++ .../schema/raw/response_to_denoms.json | 78 ++++++++ 4 files changed, 372 insertions(+) create mode 100644 contracts/cyberpunk/schema/raw/response_to_denom.json create mode 100644 contracts/cyberpunk/schema/raw/response_to_denoms.json diff --git a/contracts/cyberpunk/schema/cyberpunk.json b/contracts/cyberpunk/schema/cyberpunk.json index 1584ac900e..b639402f26 100644 --- a/contracts/cyberpunk/schema/cyberpunk.json +++ b/contracts/cyberpunk/schema/cyberpunk.json @@ -185,12 +185,198 @@ } }, "additionalProperties": false + }, + { + "description": "Queries `AllDenomMetadata` from the bank module repeatedly and returns all entries", + "type": "object", + "required": [ + "denoms" + ], + "properties": { + "denoms": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Queries `DenomMetadata` from the bank module and returns the result", + "type": "object", + "required": [ + "denom" + ], + "properties": { + "denom": { + "type": "object", + "required": [ + "denom" + ], + "properties": { + "denom": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } ] }, "migrate": null, "sudo": null, "responses": { + "denom": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DenomMetadata", + "description": "Replicates the cosmos-sdk bank module Metadata type", + "type": "object", + "required": [ + "base", + "denom_units", + "description", + "display", + "name", + "symbol", + "uri", + "uri_hash" + ], + "properties": { + "base": { + "type": "string" + }, + "denom_units": { + "type": "array", + "items": { + "$ref": "#/definitions/DenomUnit" + } + }, + "description": { + "type": "string" + }, + "display": { + "type": "string" + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "uri_hash": { + "type": "string" + } + }, + "definitions": { + "DenomUnit": { + "description": "Replicates the cosmos-sdk bank module DenomUnit type", + "type": "object", + "required": [ + "aliases", + "denom", + "exponent" + ], + "properties": { + "aliases": { + "type": "array", + "items": { + "type": "string" + } + }, + "denom": { + "type": "string" + }, + "exponent": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } + } + }, + "denoms": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_DenomMetadata", + "type": "array", + "items": { + "$ref": "#/definitions/DenomMetadata" + }, + "definitions": { + "DenomMetadata": { + "description": "Replicates the cosmos-sdk bank module Metadata type", + "type": "object", + "required": [ + "base", + "denom_units", + "description", + "display", + "name", + "symbol", + "uri", + "uri_hash" + ], + "properties": { + "base": { + "type": "string" + }, + "denom_units": { + "type": "array", + "items": { + "$ref": "#/definitions/DenomUnit" + } + }, + "description": { + "type": "string" + }, + "display": { + "type": "string" + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "uri_hash": { + "type": "string" + } + } + }, + "DenomUnit": { + "description": "Replicates the cosmos-sdk bank module DenomUnit type", + "type": "object", + "required": [ + "aliases", + "denom", + "exponent" + ], + "properties": { + "aliases": { + "type": "array", + "items": { + "type": "string" + } + }, + "denom": { + "type": "string" + }, + "exponent": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } + } + }, "mirror_env": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Env", diff --git a/contracts/cyberpunk/schema/raw/query.json b/contracts/cyberpunk/schema/raw/query.json index 18c07a5622..47b493442f 100644 --- a/contracts/cyberpunk/schema/raw/query.json +++ b/contracts/cyberpunk/schema/raw/query.json @@ -15,6 +15,42 @@ } }, "additionalProperties": false + }, + { + "description": "Queries `AllDenomMetadata` from the bank module repeatedly and returns all entries", + "type": "object", + "required": [ + "denoms" + ], + "properties": { + "denoms": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Queries `DenomMetadata` from the bank module and returns the result", + "type": "object", + "required": [ + "denom" + ], + "properties": { + "denom": { + "type": "object", + "required": [ + "denom" + ], + "properties": { + "denom": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } ] } diff --git a/contracts/cyberpunk/schema/raw/response_to_denom.json b/contracts/cyberpunk/schema/raw/response_to_denom.json new file mode 100644 index 0000000000..b541da804b --- /dev/null +++ b/contracts/cyberpunk/schema/raw/response_to_denom.json @@ -0,0 +1,72 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DenomMetadata", + "description": "Replicates the cosmos-sdk bank module Metadata type", + "type": "object", + "required": [ + "base", + "denom_units", + "description", + "display", + "name", + "symbol", + "uri", + "uri_hash" + ], + "properties": { + "base": { + "type": "string" + }, + "denom_units": { + "type": "array", + "items": { + "$ref": "#/definitions/DenomUnit" + } + }, + "description": { + "type": "string" + }, + "display": { + "type": "string" + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "uri_hash": { + "type": "string" + } + }, + "definitions": { + "DenomUnit": { + "description": "Replicates the cosmos-sdk bank module DenomUnit type", + "type": "object", + "required": [ + "aliases", + "denom", + "exponent" + ], + "properties": { + "aliases": { + "type": "array", + "items": { + "type": "string" + } + }, + "denom": { + "type": "string" + }, + "exponent": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } + } +} diff --git a/contracts/cyberpunk/schema/raw/response_to_denoms.json b/contracts/cyberpunk/schema/raw/response_to_denoms.json new file mode 100644 index 0000000000..e8fcd9a16e --- /dev/null +++ b/contracts/cyberpunk/schema/raw/response_to_denoms.json @@ -0,0 +1,78 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_DenomMetadata", + "type": "array", + "items": { + "$ref": "#/definitions/DenomMetadata" + }, + "definitions": { + "DenomMetadata": { + "description": "Replicates the cosmos-sdk bank module Metadata type", + "type": "object", + "required": [ + "base", + "denom_units", + "description", + "display", + "name", + "symbol", + "uri", + "uri_hash" + ], + "properties": { + "base": { + "type": "string" + }, + "denom_units": { + "type": "array", + "items": { + "$ref": "#/definitions/DenomUnit" + } + }, + "description": { + "type": "string" + }, + "display": { + "type": "string" + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "uri_hash": { + "type": "string" + } + } + }, + "DenomUnit": { + "description": "Replicates the cosmos-sdk bank module DenomUnit type", + "type": "object", + "required": [ + "aliases", + "denom", + "exponent" + ], + "properties": { + "aliases": { + "type": "array", + "items": { + "type": "string" + } + }, + "denom": { + "type": "string" + }, + "exponent": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } + } +} From 86f75e431bd506a68f9604466da5c8f74bac113b Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 22 Jun 2023 17:59:33 +0200 Subject: [PATCH 056/113] Resolve conflicts --- CHANGELOG.md | 10 ---------- contracts/cyberpunk/src/contract.rs | 23 +---------------------- docs/USING_COSMWASM_STD.md | 15 +-------------- 3 files changed, 2 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70b1137515..19c04c7611 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,26 +8,16 @@ and this project adheres to ### Added -<<<<<<< HEAD -======= - cosmwasm-std: Implement `BankQuery::AllDenomMetadata` to allow querying all the denom metadata and `BankQuery::DenomMetadata` to query a specific one. In order to use this query in a contract, the `cosmwasm_1_3` feature needs to be enabled for the `cosmwasm_std` dependency. This makes the contract incompatible with chains running anything lower than CosmWasm `1.3.0`. ([#1647]) -- cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have - been checked before. This is useful for state-sync where we know the Wasm code - was checked when it was first uploaded. ([#1635]) ->>>>>>> 4bac1e3c (std: Add BankQuery::AllDenomMetadata gated by cosmwasm_1_3 feature flag) - cosmwasm-std: Add `FromStr` impl for `Coin`. ([#1684]) - cosmwasm-std: Add `Coins` helper to handle multiple coins. ([#1687]) -<<<<<<< HEAD -======= -[#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635 [#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647 ->>>>>>> 4bac1e3c (std: Add BankQuery::AllDenomMetadata gated by cosmwasm_1_3 feature flag) [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 [#1687]: https://github.com/CosmWasm/cosmwasm/pull/1687 diff --git a/contracts/cyberpunk/src/contract.rs b/contracts/cyberpunk/src/contract.rs index f8802f6c09..d5b0c9c2c5 100644 --- a/contracts/cyberpunk/src/contract.rs +++ b/contracts/cyberpunk/src/contract.rs @@ -1,16 +1,6 @@ use cosmwasm_std::{ -<<<<<<< HEAD -<<<<<<< HEAD - entry_point, to_binary, Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response, - StdError, StdResult, WasmMsg, -======= - entry_point, to_binary, Api, Deps, DepsMut, Empty, Env, MessageInfo, PageRequest, + entry_point, to_binary, DenomMetadata, Deps, DepsMut, Empty, Env, MessageInfo, PageRequest, QueryResponse, Response, StdError, StdResult, WasmMsg, ->>>>>>> 7e96644e (Add Denoms query to cyberpunk contract) -======= - entry_point, to_binary, Api, DenomMetadata, Deps, DepsMut, Empty, Env, MessageInfo, - PageRequest, QueryResponse, Response, StdError, StdResult, WasmMsg, ->>>>>>> feab42e4 (Split Denoms query up) }; use crate::errors::ContractError; @@ -174,8 +164,6 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { fn query_mirror_env(env: Env) -> Env { env } -<<<<<<< HEAD -======= fn query_denoms(deps: Deps) -> StdResult> { const PAGE_SIZE: u32 = 10; @@ -226,14 +214,6 @@ mod tests { setup(); } - #[test] - fn debug_works() { - let mut deps = setup(); - - let msg = ExecuteMsg::Debug {}; - execute(deps.as_mut(), mock_env(), mock_info("caller", &[]), msg).unwrap(); - } - #[test] fn query_denoms_works() { let mut deps = setup(); @@ -277,4 +257,3 @@ mod tests { assert_eq!(denom.symbol, "FOO0"); } } ->>>>>>> 7e96644e (Add Denoms query to cyberpunk contract) diff --git a/docs/USING_COSMWASM_STD.md b/docs/USING_COSMWASM_STD.md index 28f91a51f6..2519b9b8a5 100644 --- a/docs/USING_COSMWASM_STD.md +++ b/docs/USING_COSMWASM_STD.md @@ -34,7 +34,6 @@ in the dependency tree. Otherwise conflicting C exports are created. The libarary comes with the following features: -<<<<<<< HEAD | Feature | Enabled by default | Description | | ------------ | ------------------ | -------------------------------------------------------------------------- | | iterator | x | Storage iterators | @@ -45,19 +44,7 @@ The libarary comes with the following features: | baktraces | | Add backtraces to errors (for unit testing) | | cosmwasm_1_1 | | Features that require CosmWasm 1.1+ on the chain | | cosmwasm_1_2 | | Features that require CosmWasm 1.2+ on the chain | -======= -| Feature | Enabled by default | Description | -| ------------ | ------------------ | ------------------------------------------------------------------------- | -| iterator | x | Storage iterators | -| abort | x | A panic handler that aborts the contract execution with a helpful message | -| stargate | | Cosmos SDK 0.40+ features and IBC | -| ibc3 | | New fields added in IBC v3 | -| staking | | Access to the staking module | -| backtraces | | Add backtraces to errors (for unit testing) | -| cosmwasm_1_1 | | Features that require CosmWasm 1.1+ on the chain | -| cosmwasm_1_2 | | Features that require CosmWasm 1.2+ on the chain | -| cosmwasm_1_3 | | Features that require CosmWasm 1.3+ on the chain | ->>>>>>> 4bac1e3c (std: Add BankQuery::AllDenomMetadata gated by cosmwasm_1_3 feature flag) +| cosmwasm_1_3 | | Features that require CosmWasm 1.3+ on the chain | ## The cosmwasm-std dependency for contract developers From 40a8ba414d52718966c5cd3a65c5d0c23958bc08 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 22 Jun 2023 19:04:43 +0200 Subject: [PATCH 057/113] Add Cache::save_wasm_unchecked --- CHANGELOG.md | 4 +++ packages/vm/src/cache.rs | 55 ++++++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19c04c7611..cc73ece393 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,11 @@ and this project adheres to ([#1647]) - cosmwasm-std: Add `FromStr` impl for `Coin`. ([#1684]) - cosmwasm-std: Add `Coins` helper to handle multiple coins. ([#1687]) +- cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have + been checked before. This is useful for state-sync where we know the Wasm code + was checked when it was first uploaded. ([#1635]) +[#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635 [#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647 [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 [#1687]: https://github.com/CosmWasm/cosmwasm/pull/1687 diff --git a/packages/vm/src/cache.rs b/packages/vm/src/cache.rs index 43f580cdc5..85c145e15d 100644 --- a/packages/vm/src/cache.rs +++ b/packages/vm/src/cache.rs @@ -159,8 +159,27 @@ where } } + /// Takes a Wasm bytecode and stores it to the cache. + /// + /// This performs static checks, compiles the bytescode to a module and + /// stores the Wasm file on disk. + /// + /// This does the same as [`save_wasm_unchecked`] plus the static checks. + /// When a Wasm blob is stored the first time, use this function. pub fn save_wasm(&self, wasm: &[u8]) -> VmResult { check_wasm(wasm, &self.available_capabilities)?; + self.save_wasm_unchecked(wasm) + } + + /// Takes a Wasm bytecode and stores it to the cache. + /// + /// This compiles the bytescode to a module and + /// stores the Wasm file on disk. + /// + /// This does the same as [`save_wasm`] but without the static checks. + /// When a Wasm blob is stored which was previously checked (e.g. as part of state sync), + /// use this function. + pub fn save_wasm_unchecked(&self, wasm: &[u8]) -> VmResult { let module = compile(wasm, None, &[])?; let mut cache = self.inner.lock().unwrap(); @@ -446,6 +465,14 @@ mod tests { static CONTRACT: &[u8] = include_bytes!("../testdata/hackatom.wasm"); static IBC_CONTRACT: &[u8] = include_bytes!("../testdata/ibc_reflect.wasm"); + // Invalid because it doesn't contain required memory and exports + static INVALID_CONTRACT_WAT: &str = r#"(module + (type $t0 (func (param i32) (result i32))) + (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) + get_local $p0 + i32.const 1 + i32.add)) + "#; fn default_capabilities() -> HashSet { capabilities_from_csv("iterator,staking") @@ -504,17 +531,7 @@ mod tests { #[test] fn save_wasm_rejects_invalid_contract() { - // Invalid because it doesn't contain required memory and exports - let wasm = wat::parse_str( - r#"(module - (type $t0 (func (param i32) (result i32))) - (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) - get_local $p0 - i32.const 1 - i32.add)) - "#, - ) - .unwrap(); + let wasm = wat::parse_str(INVALID_CONTRACT_WAT).unwrap(); let cache: Cache = unsafe { Cache::new(make_testing_options()).unwrap() }; @@ -545,6 +562,22 @@ mod tests { assert_eq!(cache.stats().misses, 0); } + #[test] + fn save_wasm_unchecked_works() { + let cache: Cache = + unsafe { Cache::new(make_testing_options()).unwrap() }; + cache.save_wasm_unchecked(CONTRACT).unwrap(); + } + + #[test] + fn save_wasm_unchecked_accepts_invalid_contract() { + let wasm = wat::parse_str(INVALID_CONTRACT_WAT).unwrap(); + + let cache: Cache = + unsafe { Cache::new(make_testing_options()).unwrap() }; + cache.save_wasm_unchecked(&wasm).unwrap(); + } + #[test] fn load_wasm_works() { let cache: Cache = From a2fdd07bf96c8571c102b3d697a72f7dd4cf5ac6 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 22 Jun 2023 17:12:28 +0200 Subject: [PATCH 058/113] Test contract compiled with Rust 1.70.0 --- packages/vm/README.md | 11 +++++++++++ packages/vm/src/compatibility.rs | 7 +++++++ packages/vm/testdata/cyberpunk_rust170.wasm | Bin 0 -> 198039 bytes 3 files changed, 18 insertions(+) create mode 100755 packages/vm/testdata/cyberpunk_rust170.wasm diff --git a/packages/vm/README.md b/packages/vm/README.md index 7fc48e561f..a50ae52130 100644 --- a/packages/vm/README.md +++ b/packages/vm/README.md @@ -75,6 +75,17 @@ docker run --rm -v "$(pwd)":/code \ && cp artifacts/floaty.wasm packages/vm/testdata/floaty_1.2.wasm ``` +The `cyberpunk_rust170.wasm` for +https://github.com/CosmWasm/cosmwasm/issues/1727 is built as follows +(non-reproducible): + +```sh +cd contracts/cyberpunk +rm -r target +RUSTFLAGS='-C link-arg=-s' cargo build --release --lib --target wasm32-unknown-unknown --locked +cp target/wasm32-unknown-unknown/release/cyberpunk.wasm ../../packages/vm/testdata/cyberpunk_rust170.wasm +``` + ## Testing By default, this repository is built and tested with the singlepass backend. You diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index 428d51554f..2c500bdb30 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -263,6 +263,7 @@ mod tests { static CONTRACT_0_14: &[u8] = include_bytes!("../testdata/hackatom_0.14.wasm"); static CONTRACT_0_15: &[u8] = include_bytes!("../testdata/hackatom_0.15.wasm"); static CONTRACT: &[u8] = include_bytes!("../testdata/hackatom.wasm"); + static CONTRACT_RUST_170: &[u8] = include_bytes!("../testdata/cyberpunk_rust170.wasm"); fn default_capabilities() -> HashSet { ["staking".to_string()].into_iter().collect() @@ -274,6 +275,12 @@ mod tests { check_wasm(CONTRACT, &default_capabilities()).unwrap(); } + #[test] + fn check_wasm_allows_sign_ext() { + // See https://github.com/CosmWasm/cosmwasm/issues/1727 + check_wasm(CONTRACT_RUST_170, &default_capabilities()).unwrap(); + } + #[test] fn check_wasm_old_contract() { match check_wasm(CONTRACT_0_15, &default_capabilities()) { diff --git a/packages/vm/testdata/cyberpunk_rust170.wasm b/packages/vm/testdata/cyberpunk_rust170.wasm new file mode 100755 index 0000000000000000000000000000000000000000..3bd0b471c0d0692e2cf7fa421e65ec21e2b4bc36 GIT binary patch literal 198039 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 mG)yuAh_bUHJ(h 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%0Dsjv=+hngs2`QAQ#6c^us@cYoxCHsZjT3P6$0f3j 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 nmmA7?n)rng)~h)d literal 0 HcmV?d00001 From 54f32f476dbefd734da10944a1b21f7dfa706998 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 22 Jun 2023 17:13:57 +0200 Subject: [PATCH 059/113] Enable sign_ext in parity-wasm --- CHANGELOG.md | 3 +++ packages/vm/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc73ece393..53e075edb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,11 +19,14 @@ and this project adheres to - cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have been checked before. This is useful for state-sync where we know the Wasm code was checked when it was first uploaded. ([#1635]) +- cosmwasm-vm: Allow sign extension Wasm opcodes in static validation. This + allows contracts to be compiled with Rust 1.70.0 and above. ([#1727]) [#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635 [#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647 [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 [#1687]: https://github.com/CosmWasm/cosmwasm/pull/1687 +[#1727]: https://github.com/CosmWasm/cosmwasm/issues/1727 ### Changed diff --git a/packages/vm/Cargo.toml b/packages/vm/Cargo.toml index 59817c55dd..206716f8a7 100644 --- a/packages/vm/Cargo.toml +++ b/packages/vm/Cargo.toml @@ -45,7 +45,7 @@ crc32fast = "1.3.2" cosmwasm-std = { path = "../std", version = "1.2.7", default-features = false } cosmwasm-crypto = { path = "../crypto", version = "1.2.7" } hex = "0.4" -parity-wasm = "0.45" +parity-wasm = { version = "0.45", features = ["sign_ext"] } schemars = "0.8.3" serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } serde_json = "1.0.40" From 0e0b18da6343768c6e9eef1e932b4a7ece24cc8b Mon Sep 17 00:00:00 2001 From: ekez Date: Sun, 22 Jan 2023 18:05:03 -0800 Subject: [PATCH 060/113] Add distribution module query type. --- packages/std/src/query/distribution.rs | 17 +++++++++++++++++ packages/std/src/query/mod.rs | 3 +++ 2 files changed, 20 insertions(+) create mode 100644 packages/std/src/query/distribution.rs diff --git a/packages/std/src/query/distribution.rs b/packages/std/src/query/distribution.rs new file mode 100644 index 0000000000..28e81d4e2a --- /dev/null +++ b/packages/std/src/query/distribution.rs @@ -0,0 +1,17 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[non_exhaustive] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum DistributionQuery { + // https://github.com/cosmos/cosmos-sdk/blob/4f6f6c00021f4b5ee486bbb71ae2071a8ceb47c9/x/distribution/types/query.pb.go#L792-L795 + DelegatorWithdrawAddress { delegator_address: String }, +} + +// https://github.com/cosmos/cosmos-sdk/blob/4f6f6c00021f4b5ee486bbb71ae2071a8ceb47c9/x/distribution/types/query.pb.go#L832-L835 +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct DelegatorWithdrawAddressResponse { + withdraw_address: String, +} diff --git a/packages/std/src/query/mod.rs b/packages/std/src/query/mod.rs index c88e209b8f..11be28f0de 100644 --- a/packages/std/src/query/mod.rs +++ b/packages/std/src/query/mod.rs @@ -6,6 +6,7 @@ use crate::Binary; use crate::Empty; mod bank; +mod distribution; mod ibc; mod query_response; mod staking; @@ -16,6 +17,8 @@ pub use bank::SupplyResponse; pub use bank::{AllBalanceResponse, BalanceResponse, BankQuery}; #[cfg(feature = "cosmwasm_1_3")] pub use bank::{AllDenomMetadataResponse, DenomMetadataResponse}; +#[cfg(feature = "staking")] +pub use distribution::DistributionQuery; #[cfg(feature = "stargate")] pub use ibc::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdResponse}; #[cfg(feature = "staking")] From e3fad3a6557d4bd0ad475c8f0b88386106bffc58 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 23 Jun 2023 13:57:21 +0200 Subject: [PATCH 061/113] Add withdraw address query to QuerierWrapper --- packages/std/src/query/distribution.rs | 2 +- packages/std/src/query/mod.rs | 13 ++++++++-- packages/std/src/testing/mock.rs | 34 ++++++++++++++++++++++++++ packages/std/src/traits.rs | 15 ++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/packages/std/src/query/distribution.rs b/packages/std/src/query/distribution.rs index 28e81d4e2a..065bd7c8c7 100644 --- a/packages/std/src/query/distribution.rs +++ b/packages/std/src/query/distribution.rs @@ -13,5 +13,5 @@ pub enum DistributionQuery { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub struct DelegatorWithdrawAddressResponse { - withdraw_address: String, + pub withdraw_address: String, } diff --git a/packages/std/src/query/mod.rs b/packages/std/src/query/mod.rs index 11be28f0de..f768faadad 100644 --- a/packages/std/src/query/mod.rs +++ b/packages/std/src/query/mod.rs @@ -17,8 +17,8 @@ pub use bank::SupplyResponse; pub use bank::{AllBalanceResponse, BalanceResponse, BankQuery}; #[cfg(feature = "cosmwasm_1_3")] pub use bank::{AllDenomMetadataResponse, DenomMetadataResponse}; -#[cfg(feature = "staking")] -pub use distribution::DistributionQuery; +#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +pub use distribution::{DelegatorWithdrawAddressResponse, DistributionQuery}; #[cfg(feature = "stargate")] pub use ibc::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdResponse}; #[cfg(feature = "staking")] @@ -38,6 +38,8 @@ pub enum QueryRequest { Custom(C), #[cfg(feature = "staking")] Staking(StakingQuery), + #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + Distribution(DistributionQuery), /// A Stargate query is encoded the same way as abci_query, with path and protobuf encoded request data. /// The format is defined in [ADR-21](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-021-protobuf-query-encoding.md). /// The response is protobuf encoded data directly without a JSON response wrapper. @@ -111,3 +113,10 @@ impl From for QueryRequest { QueryRequest::Ibc(msg) } } + +#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +impl From for QueryRequest { + fn from(msg: DistributionQuery) -> Self { + QueryRequest::Distribution(msg) + } +} diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index 3b5ce6f253..dfb75e9b0c 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -28,6 +28,8 @@ use crate::query::{ AllDelegationsResponse, AllValidatorsResponse, BondedDenomResponse, DelegationResponse, FullDelegation, StakingQuery, Validator, ValidatorResponse, }; +#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +use crate::query::{DelegatorWithdrawAddressResponse, DistributionQuery}; use crate::results::{ContractResult, Empty, SystemResult}; use crate::serde::{from_slice, to_binary}; use crate::storage::MemoryStorage; @@ -443,6 +445,8 @@ pub struct MockQuerier { bank: BankQuerier, #[cfg(feature = "staking")] staking: StakingQuerier, + #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + distribution: DistributionQuerier, wasm: WasmQuerier, #[cfg(feature = "stargate")] ibc: IbcQuerier, @@ -457,6 +461,8 @@ impl MockQuerier { pub fn new(balances: &[(&str, &[Coin])]) -> Self { MockQuerier { bank: BankQuerier::new(balances), + #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + distribution: DistributionQuerier::default(), #[cfg(feature = "staking")] staking: StakingQuerier::default(), wasm: WasmQuerier::default(), @@ -543,6 +549,10 @@ impl MockQuerier { QueryRequest::Custom(custom_query) => (*self.custom_handler)(custom_query), #[cfg(feature = "staking")] QueryRequest::Staking(staking_query) => self.staking.query(staking_query), + #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + QueryRequest::Distribution(distribution_query) => { + self.distribution.query(distribution_query) + } QueryRequest::Wasm(msg) => self.wasm.query(msg), #[cfg(feature = "stargate")] QueryRequest::Stargate { .. } => SystemResult::Err(SystemError::UnsupportedRequest { @@ -893,6 +903,30 @@ impl StakingQuerier { } } +#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +#[derive(Clone, Default)] +pub struct DistributionQuerier {} + +#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +impl DistributionQuerier { + pub fn new() -> Self { + DistributionQuerier {} + } + + pub fn query(&self, request: &DistributionQuery) -> QuerierResult { + let contract_result: ContractResult = match request { + DistributionQuery::DelegatorWithdrawAddress { delegator_address } => { + let res = DelegatorWithdrawAddressResponse { + withdraw_address: delegator_address.clone(), + }; + to_binary(&res).into() + } + }; + // system result is always ok in the mock implementation + SystemResult::Ok(contract_result) + } +} + pub fn digit_sum(input: &[u8]) -> usize { input.iter().fold(0, |sum, val| sum + (*val as usize)) } diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs index 7181036cb8..c6a12ef504 100644 --- a/packages/std/src/traits.rs +++ b/packages/std/src/traits.rs @@ -22,6 +22,8 @@ use crate::query::{ }; #[cfg(feature = "cosmwasm_1_3")] use crate::query::{AllDenomMetadataResponse, DenomMetadataResponse}; +#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +use crate::query::{DelegatorWithdrawAddressResponse, DistributionQuery}; use crate::results::{ContractResult, Empty, SystemResult}; use crate::serde::{from_binary, to_binary, to_vec}; use crate::ContractInfoResponse; @@ -243,6 +245,19 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { Ok(res.amount) } + #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + pub fn query_delegator_withdraw_address( + &self, + delegator: impl Into, + ) -> StdResult { + let request = DistributionQuery::DelegatorWithdrawAddress { + delegator_address: delegator.into(), + } + .into(); + let res: DelegatorWithdrawAddressResponse = self.query(&request)?; + Ok(res.withdraw_address) + } + #[cfg(feature = "cosmwasm_1_3")] pub fn query_denom_metadata(&self, denom: impl Into) -> StdResult { let request = BankQuery::DenomMetadata { From 8d91dd2235b8cdd0c4a966680d3e206ce927768f Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 23 Jun 2023 14:48:57 +0200 Subject: [PATCH 062/113] Implement QueryResponseType for DelegatorWithdrawAddressResponse --- packages/std/src/query/distribution.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/std/src/query/distribution.rs b/packages/std/src/query/distribution.rs index 065bd7c8c7..ffe5772322 100644 --- a/packages/std/src/query/distribution.rs +++ b/packages/std/src/query/distribution.rs @@ -1,6 +1,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use super::query_response::QueryResponseType; + #[non_exhaustive] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] @@ -10,8 +12,11 @@ pub enum DistributionQuery { } // https://github.com/cosmos/cosmos-sdk/blob/4f6f6c00021f4b5ee486bbb71ae2071a8ceb47c9/x/distribution/types/query.pb.go#L832-L835 -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] +#[non_exhaustive] pub struct DelegatorWithdrawAddressResponse { pub withdraw_address: String, } + +impl QueryResponseType for DelegatorWithdrawAddressResponse {} From 6873497eb41fa21f2f40e8896d5a376856c472cf Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 26 Jun 2023 10:48:43 +0200 Subject: [PATCH 063/113] Allow mocking withdraw address --- packages/std/src/testing/mock.rs | 45 +++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index dfb75e9b0c..16099d1b29 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -905,19 +905,29 @@ impl StakingQuerier { #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] #[derive(Clone, Default)] -pub struct DistributionQuerier {} +pub struct DistributionQuerier { + withdraw_addresses: HashMap, +} #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] impl DistributionQuerier { - pub fn new() -> Self { - DistributionQuerier {} + pub fn new(withdraw_addresses: HashMap) -> Self { + DistributionQuerier { withdraw_addresses } + } + + pub fn set_withdraw_addresses(&mut self, withdraw_addresses: HashMap) { + self.withdraw_addresses = withdraw_addresses; } pub fn query(&self, request: &DistributionQuery) -> QuerierResult { let contract_result: ContractResult = match request { DistributionQuery::DelegatorWithdrawAddress { delegator_address } => { let res = DelegatorWithdrawAddressResponse { - withdraw_address: delegator_address.clone(), + withdraw_address: self + .withdraw_addresses + .get(delegator_address) + .unwrap_or(delegator_address) + .clone(), }; to_binary(&res).into() } @@ -1462,6 +1472,33 @@ mod tests { ); } + #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + #[test] + fn distribution_querier_delegator_withdraw_address() { + let mut distribution = DistributionQuerier::new(HashMap::new()); + distribution.set_withdraw_addresses( + [("addr0".to_string(), "withdraw0".to_string())] + .into_iter() + .collect(), + ); + + let query = DistributionQuery::DelegatorWithdrawAddress { + delegator_address: "addr0".to_string(), + }; + + let res = distribution.query(&query).unwrap().unwrap(); + let res: DelegatorWithdrawAddressResponse = from_binary(&res).unwrap(); + assert_eq!(res.withdraw_address, "withdraw0"); + + let query = DistributionQuery::DelegatorWithdrawAddress { + delegator_address: "addr1".to_string(), + }; + + let res = distribution.query(&query).unwrap().unwrap(); + let res: DelegatorWithdrawAddressResponse = from_binary(&res).unwrap(); + assert_eq!(res.withdraw_address, "addr1"); + } + #[cfg(feature = "stargate")] #[test] fn ibc_querier_channel_existing() { From cbb66304feb39f123db719782199385a0c7706a0 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 26 Jun 2023 13:46:09 +0200 Subject: [PATCH 064/113] Expose DistributionQuerier mock --- packages/std/src/testing/mock.rs | 5 +++++ packages/std/src/testing/mod.rs | 2 ++ 2 files changed, 7 insertions(+) diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index 16099d1b29..2542f87249 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -490,6 +490,11 @@ impl MockQuerier { self.bank.set_denom_metadata(denom_metadata); } + #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + pub fn set_withdraw_addresses(&mut self, withdraw_addresses: HashMap) { + self.distribution.set_withdraw_addresses(withdraw_addresses); + } + #[cfg(feature = "staking")] pub fn update_staking( &mut self, diff --git a/packages/std/src/testing/mod.rs b/packages/std/src/testing/mod.rs index 657aada5de..02487f8496 100644 --- a/packages/std/src/testing/mod.rs +++ b/packages/std/src/testing/mod.rs @@ -9,6 +9,8 @@ mod shuffle; pub use assertions::assert_approx_eq_impl; +#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +pub use mock::DistributionQuerier; #[cfg(feature = "staking")] pub use mock::StakingQuerier; pub use mock::{ From a9d2b0a9a8a5b3872140bd874116e12a76a1b4bb Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 27 Jun 2023 15:22:34 +0200 Subject: [PATCH 065/113] Add Storage::range_keys and Storage::range_values --- CHANGELOG.md | 4 ++++ packages/std/src/traits.rs | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53e075edb1..1d882e7ab3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,12 +21,16 @@ and this project adheres to was checked when it was first uploaded. ([#1635]) - cosmwasm-vm: Allow sign extension Wasm opcodes in static validation. This allows contracts to be compiled with Rust 1.70.0 and above. ([#1727]) +- cosmwasm-std: Add trait functions `Storage::range_keys` and + `Storage::range_values`. The default implementations just use + `Storage::range`. Later this can be implemented more efficiently. ([#1748]) [#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635 [#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647 [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 [#1687]: https://github.com/CosmWasm/cosmwasm/pull/1687 [#1727]: https://github.com/CosmWasm/cosmwasm/issues/1727 +[#1748]: https://github.com/CosmWasm/cosmwasm/pull/1748 ### Changed diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs index 7181036cb8..0b28803b89 100644 --- a/packages/std/src/traits.rs +++ b/packages/std/src/traits.rs @@ -38,12 +38,11 @@ pub trait Storage { /// is not great yet and might not be possible in all backends. But we're trying to get there. fn get(&self, key: &[u8]) -> Option>; - #[cfg(feature = "iterator")] /// Allows iteration over a set of key/value pairs, either forwards or backwards. /// /// The bound `start` is inclusive and `end` is exclusive. - /// /// If `start` is lexicographically greater than or equal to `end`, an empty range is described, mo matter of the order. + #[cfg(feature = "iterator")] fn range<'a>( &'a self, start: Option<&[u8]>, @@ -51,6 +50,40 @@ pub trait Storage { order: Order, ) -> Box + 'a>; + /// Allows iteration over a set of keys, either forwards or backwards. + /// + /// The bound `start` is inclusive and `end` is exclusive. + /// If `start` is lexicographically greater than or equal to `end`, an empty range is described, mo matter of the order. + /// + /// The default implementation uses [`Storage::range`] and discards the values. More efficient + /// implementations might be possible depending on the storage. + #[cfg(feature = "iterator")] + fn range_keys<'a>( + &'a self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box> + 'a> { + Box::new(self.range(start, end, order).map(|(k, _v)| k)) + } + + /// Allows iteration over a set of values, either forwards or backwards. + /// + /// The bound `start` is inclusive and `end` is exclusive. + /// If `start` is lexicographically greater than or equal to `end`, an empty range is described, mo matter of the order. + /// + /// The default implementation uses [`Storage::range`] and discards the keys. More efficient implementations + /// might be possible depending on the storage. + #[cfg(feature = "iterator")] + fn range_values<'a>( + &'a self, + start: Option<&[u8]>, + end: Option<&[u8]>, + order: Order, + ) -> Box> + 'a> { + Box::new(self.range(start, end, order).map(|(_k, v)| v)) + } + fn set(&mut self, key: &[u8], value: &[u8]); /// Removes a database entry at `key`. From 3c1e58b6fb957ccd59dbb576e9cd932b075d3d32 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 27 Jun 2023 16:56:28 +0200 Subject: [PATCH 066/113] Remove staking feature gate for distribution query Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> --- packages/std/src/query/mod.rs | 6 +++--- packages/std/src/testing/mock.rs | 16 ++++++++-------- packages/std/src/testing/mod.rs | 2 +- packages/std/src/traits.rs | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/std/src/query/mod.rs b/packages/std/src/query/mod.rs index f768faadad..5401f9145d 100644 --- a/packages/std/src/query/mod.rs +++ b/packages/std/src/query/mod.rs @@ -17,7 +17,7 @@ pub use bank::SupplyResponse; pub use bank::{AllBalanceResponse, BalanceResponse, BankQuery}; #[cfg(feature = "cosmwasm_1_3")] pub use bank::{AllDenomMetadataResponse, DenomMetadataResponse}; -#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +#[cfg(feature = "cosmwasm_1_3")] pub use distribution::{DelegatorWithdrawAddressResponse, DistributionQuery}; #[cfg(feature = "stargate")] pub use ibc::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdResponse}; @@ -38,7 +38,7 @@ pub enum QueryRequest { Custom(C), #[cfg(feature = "staking")] Staking(StakingQuery), - #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + #[cfg(feature = "cosmwasm_1_3")] Distribution(DistributionQuery), /// A Stargate query is encoded the same way as abci_query, with path and protobuf encoded request data. /// The format is defined in [ADR-21](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-021-protobuf-query-encoding.md). @@ -114,7 +114,7 @@ impl From for QueryRequest { } } -#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +#[cfg(feature = "cosmwasm_1_3")] impl From for QueryRequest { fn from(msg: DistributionQuery) -> Self { QueryRequest::Distribution(msg) diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index 2542f87249..f2ed550cda 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -28,7 +28,7 @@ use crate::query::{ AllDelegationsResponse, AllValidatorsResponse, BondedDenomResponse, DelegationResponse, FullDelegation, StakingQuery, Validator, ValidatorResponse, }; -#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +#[cfg(feature = "cosmwasm_1_3")] use crate::query::{DelegatorWithdrawAddressResponse, DistributionQuery}; use crate::results::{ContractResult, Empty, SystemResult}; use crate::serde::{from_slice, to_binary}; @@ -445,7 +445,7 @@ pub struct MockQuerier { bank: BankQuerier, #[cfg(feature = "staking")] staking: StakingQuerier, - #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + #[cfg(feature = "cosmwasm_1_3")] distribution: DistributionQuerier, wasm: WasmQuerier, #[cfg(feature = "stargate")] @@ -461,7 +461,7 @@ impl MockQuerier { pub fn new(balances: &[(&str, &[Coin])]) -> Self { MockQuerier { bank: BankQuerier::new(balances), - #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + #[cfg(feature = "cosmwasm_1_3")] distribution: DistributionQuerier::default(), #[cfg(feature = "staking")] staking: StakingQuerier::default(), @@ -490,7 +490,7 @@ impl MockQuerier { self.bank.set_denom_metadata(denom_metadata); } - #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + #[cfg(feature = "cosmwasm_1_3")] pub fn set_withdraw_addresses(&mut self, withdraw_addresses: HashMap) { self.distribution.set_withdraw_addresses(withdraw_addresses); } @@ -554,7 +554,7 @@ impl MockQuerier { QueryRequest::Custom(custom_query) => (*self.custom_handler)(custom_query), #[cfg(feature = "staking")] QueryRequest::Staking(staking_query) => self.staking.query(staking_query), - #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + #[cfg(feature = "cosmwasm_1_3")] QueryRequest::Distribution(distribution_query) => { self.distribution.query(distribution_query) } @@ -908,13 +908,13 @@ impl StakingQuerier { } } -#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +#[cfg(feature = "cosmwasm_1_3")] #[derive(Clone, Default)] pub struct DistributionQuerier { withdraw_addresses: HashMap, } -#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +#[cfg(feature = "cosmwasm_1_3")] impl DistributionQuerier { pub fn new(withdraw_addresses: HashMap) -> Self { DistributionQuerier { withdraw_addresses } @@ -1477,7 +1477,7 @@ mod tests { ); } - #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + #[cfg(feature = "cosmwasm_1_3")] #[test] fn distribution_querier_delegator_withdraw_address() { let mut distribution = DistributionQuerier::new(HashMap::new()); diff --git a/packages/std/src/testing/mod.rs b/packages/std/src/testing/mod.rs index 02487f8496..550486a0b6 100644 --- a/packages/std/src/testing/mod.rs +++ b/packages/std/src/testing/mod.rs @@ -9,7 +9,7 @@ mod shuffle; pub use assertions::assert_approx_eq_impl; -#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +#[cfg(feature = "cosmwasm_1_3")] pub use mock::DistributionQuerier; #[cfg(feature = "staking")] pub use mock::StakingQuerier; diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs index c6a12ef504..c9bb3e3972 100644 --- a/packages/std/src/traits.rs +++ b/packages/std/src/traits.rs @@ -22,7 +22,7 @@ use crate::query::{ }; #[cfg(feature = "cosmwasm_1_3")] use crate::query::{AllDenomMetadataResponse, DenomMetadataResponse}; -#[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] +#[cfg(feature = "cosmwasm_1_3")] use crate::query::{DelegatorWithdrawAddressResponse, DistributionQuery}; use crate::results::{ContractResult, Empty, SystemResult}; use crate::serde::{from_binary, to_binary, to_vec}; @@ -245,7 +245,7 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> { Ok(res.amount) } - #[cfg(all(feature = "staking", feature = "cosmwasm_1_3"))] + #[cfg(feature = "cosmwasm_1_3")] pub fn query_delegator_withdraw_address( &self, delegator: impl Into, From 3f6cc077486ca94ba6e6af6568b259cd2fbf9816 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 27 Jun 2023 17:06:27 +0200 Subject: [PATCH 067/113] Update changelog and capabilities docs --- CHANGELOG.md | 3 +++ docs/CAPABILITIES-BUILT-IN.md | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc73ece393..55dff32b36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,12 +14,15 @@ and this project adheres to enabled for the `cosmwasm_std` dependency. This makes the contract incompatible with chains running anything lower than CosmWasm `1.3.0`. ([#1647]) +- cosmwasm-std: Add `DistributionQuery::DelegatorWithdrawAddress`. Also needs + the `cosmwasm_1_3` feature (see above). ([#1593]) - cosmwasm-std: Add `FromStr` impl for `Coin`. ([#1684]) - cosmwasm-std: Add `Coins` helper to handle multiple coins. ([#1687]) - cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have been checked before. This is useful for state-sync where we know the Wasm code was checked when it was first uploaded. ([#1635]) +[#1593]: https://github.com/CosmWasm/cosmwasm/pull/1593 [#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635 [#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647 [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 diff --git a/docs/CAPABILITIES-BUILT-IN.md b/docs/CAPABILITIES-BUILT-IN.md index d822971807..11990c839a 100644 --- a/docs/CAPABILITIES-BUILT-IN.md +++ b/docs/CAPABILITIES-BUILT-IN.md @@ -16,5 +16,6 @@ might define others. - `cosmwasm_1_2` enables the `GovMsg::VoteWeighted` and `WasmMsg::Instantiate2` messages. Only chains running CosmWasm `1.2.0` or higher support this. - `cosmwasm_1_3` enables the `BankQuery::AllDenomMetadata` and - `BankQuery::DenomMetadata` queries. Only chains running CosmWasm `1.3.0` or - higher support this. + `BankQuery::DenomMetadata` queries, as well as + `DistributionQuery::DelegatorWithdrawAddress`. Only chains running CosmWasm + `1.3.0` or higher support this. From 851ea63ef3d8204e75ae8bb916b134cea65f486c Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 11:39:41 +0200 Subject: [PATCH 068/113] Improve DistributionQuerier api --- packages/std/src/testing/mock.rs | 58 +++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs index f2ed550cda..109342884e 100644 --- a/packages/std/src/testing/mock.rs +++ b/packages/std/src/testing/mock.rs @@ -491,10 +491,32 @@ impl MockQuerier { } #[cfg(feature = "cosmwasm_1_3")] - pub fn set_withdraw_addresses(&mut self, withdraw_addresses: HashMap) { + pub fn set_withdraw_address( + &mut self, + delegator_address: impl Into, + withdraw_address: impl Into, + ) { + self.distribution + .set_withdraw_address(delegator_address, withdraw_address); + } + + /// Sets multiple withdraw addresses. + /// + /// This allows passing multiple tuples of `(delegator_address, withdraw_address)`. + /// It does not overwrite existing entries. + #[cfg(feature = "cosmwasm_1_3")] + pub fn set_withdraw_addresses( + &mut self, + withdraw_addresses: impl IntoIterator, impl Into)>, + ) { self.distribution.set_withdraw_addresses(withdraw_addresses); } + #[cfg(feature = "cosmwasm_1_3")] + pub fn clear_withdraw_addresses(&mut self) { + self.distribution.clear_withdraw_addresses(); + } + #[cfg(feature = "staking")] pub fn update_staking( &mut self, @@ -920,8 +942,30 @@ impl DistributionQuerier { DistributionQuerier { withdraw_addresses } } - pub fn set_withdraw_addresses(&mut self, withdraw_addresses: HashMap) { - self.withdraw_addresses = withdraw_addresses; + pub fn set_withdraw_address( + &mut self, + delegator_address: impl Into, + withdraw_address: impl Into, + ) { + self.withdraw_addresses + .insert(delegator_address.into(), withdraw_address.into()); + } + + /// Sets multiple withdraw addresses. + /// + /// This allows passing multiple tuples of `(delegator_address, withdraw_address)`. + /// It does not overwrite existing entries. + pub fn set_withdraw_addresses( + &mut self, + withdraw_addresses: impl IntoIterator, impl Into)>, + ) { + for (d, w) in withdraw_addresses { + self.set_withdraw_address(d, w); + } + } + + pub fn clear_withdraw_addresses(&mut self) { + self.withdraw_addresses.clear(); } pub fn query(&self, request: &DistributionQuery) -> QuerierResult { @@ -1480,12 +1524,8 @@ mod tests { #[cfg(feature = "cosmwasm_1_3")] #[test] fn distribution_querier_delegator_withdraw_address() { - let mut distribution = DistributionQuerier::new(HashMap::new()); - distribution.set_withdraw_addresses( - [("addr0".to_string(), "withdraw0".to_string())] - .into_iter() - .collect(), - ); + let mut distribution = DistributionQuerier::default(); + distribution.set_withdraw_address("addr0", "withdraw0"); let query = DistributionQuery::DelegatorWithdrawAddress { delegator_address: "addr0".to_string(), From 3e3c9eece27e2e224e9401cd5ce91e7109056599 Mon Sep 17 00:00:00 2001 From: Till Ziegler Date: Fri, 23 Jun 2023 17:33:40 +0200 Subject: [PATCH 069/113] add FundCommunityPool distribution message --- packages/std/src/results/cosmos_msg.rs | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/packages/std/src/results/cosmos_msg.rs b/packages/std/src/results/cosmos_msg.rs index 3e93ba1868..ccd5c97084 100644 --- a/packages/std/src/results/cosmos_msg.rs +++ b/packages/std/src/results/cosmos_msg.rs @@ -111,6 +111,12 @@ pub enum DistributionMsg { /// The `validator_address` validator: String, }, + /// This is translated to a [[MsgFundCommunityPool](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#LL69C1-L76C2). + /// `depositor` is automatically filled with the current contract's address. + FundCommunityPool { + /// The amount to spend + amount: Vec, + }, } fn binary_to_string(data: &Binary, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { @@ -462,6 +468,44 @@ mod tests { ); } } + + #[test] + fn msg_distribution_serializes_to_correct_json() { + + // FundCommunityPool + let fund_coins = vec![ + coin(200, "feathers"), + coin(200, "stones"), + ]; + let fund_msg = DistributionMsg::FundCommunityPool{ + amount: fund_coins, + }; + let fund_json = to_binary(&fund_msg).unwrap(); + assert_eq!( + String::from_utf8_lossy(&fund_json), + r#"{"fund_community_pool":{"amount":[{"denom":"feathers","amount":"200"},{"denom":"stones","amount":"200"}]}}"#, + ); + + // SetWithdrawAddress + let set_msg = DistributionMsg::SetWithdrawAddress{ + address: String::from("withdrawer"), + }; + let set_json = to_binary(&set_msg).unwrap(); + assert_eq!( + String::from_utf8_lossy(&set_json), + r#"{"set_withdraw_address":{"address":"withdrawer"}}"#, + ); + + // WithdrawDelegatorRewards + let withdraw_msg = DistributionMsg::WithdrawDelegatorReward{ + validator: String::from("fancyoperator"), + }; + let withdraw_json = to_binary(&withdraw_msg).unwrap(); + assert_eq!( + String::from_utf8_lossy(&withdraw_json), + r#"{"withdraw_delegator_reward":{"validator":"fancyoperator"}}"# + ); + } #[test] fn wasm_msg_debug_decodes_binary_string_when_possible() { From faf9ee7fbc443489093d9a4d0b028019cb823dce Mon Sep 17 00:00:00 2001 From: Till Ziegler Date: Wed, 28 Jun 2023 01:56:13 +0200 Subject: [PATCH 070/113] add unreleased changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40aec8c334..6cbe5face8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to ## [Unreleased] +- cosmwasm-std: Add `DistributionMsg::FundCommunityPool` ([#1747]) + ### Added - cosmwasm-std: Implement `BankQuery::AllDenomMetadata` to allow querying all From d4af1b0645e2962d7d6785f5c76d11fc4f8d2d87 Mon Sep 17 00:00:00 2001 From: Till Ziegler Date: Wed, 28 Jun 2023 01:59:13 +0200 Subject: [PATCH 071/113] Commit Suggested Changes Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> --- packages/std/src/results/cosmos_msg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/std/src/results/cosmos_msg.rs b/packages/std/src/results/cosmos_msg.rs index ccd5c97084..2da661292b 100644 --- a/packages/std/src/results/cosmos_msg.rs +++ b/packages/std/src/results/cosmos_msg.rs @@ -113,6 +113,7 @@ pub enum DistributionMsg { }, /// This is translated to a [[MsgFundCommunityPool](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#LL69C1-L76C2). /// `depositor` is automatically filled with the current contract's address. + #[cfg(feature = "cosmwasm_1_3")] FundCommunityPool { /// The amount to spend amount: Vec, From 021617f0c5e4907a19390b82c86037c70f02ed06 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 28 Jun 2023 14:47:13 +0200 Subject: [PATCH 072/113] Format code --- packages/std/src/results/cosmos_msg.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/std/src/results/cosmos_msg.rs b/packages/std/src/results/cosmos_msg.rs index 2da661292b..cf5c61e91e 100644 --- a/packages/std/src/results/cosmos_msg.rs +++ b/packages/std/src/results/cosmos_msg.rs @@ -115,9 +115,9 @@ pub enum DistributionMsg { /// `depositor` is automatically filled with the current contract's address. #[cfg(feature = "cosmwasm_1_3")] FundCommunityPool { - /// The amount to spend - amount: Vec, - }, + /// The amount to spend + amount: Vec, + }, } fn binary_to_string(data: &Binary, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { @@ -469,26 +469,20 @@ mod tests { ); } } - + #[test] fn msg_distribution_serializes_to_correct_json() { - // FundCommunityPool - let fund_coins = vec![ - coin(200, "feathers"), - coin(200, "stones"), - ]; - let fund_msg = DistributionMsg::FundCommunityPool{ - amount: fund_coins, - }; + let fund_coins = vec![coin(200, "feathers"), coin(200, "stones")]; + let fund_msg = DistributionMsg::FundCommunityPool { amount: fund_coins }; let fund_json = to_binary(&fund_msg).unwrap(); assert_eq!( String::from_utf8_lossy(&fund_json), r#"{"fund_community_pool":{"amount":[{"denom":"feathers","amount":"200"},{"denom":"stones","amount":"200"}]}}"#, ); - + // SetWithdrawAddress - let set_msg = DistributionMsg::SetWithdrawAddress{ + let set_msg = DistributionMsg::SetWithdrawAddress { address: String::from("withdrawer"), }; let set_json = to_binary(&set_msg).unwrap(); @@ -496,9 +490,9 @@ mod tests { String::from_utf8_lossy(&set_json), r#"{"set_withdraw_address":{"address":"withdrawer"}}"#, ); - + // WithdrawDelegatorRewards - let withdraw_msg = DistributionMsg::WithdrawDelegatorReward{ + let withdraw_msg = DistributionMsg::WithdrawDelegatorReward { validator: String::from("fancyoperator"), }; let withdraw_json = to_binary(&withdraw_msg).unwrap(); From ca986c5cd5205e4ec3e0e7a4f965a8c61835c701 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 28 Jun 2023 14:47:26 +0200 Subject: [PATCH 073/113] Limit msg_distribution_serializes_to_correct_json to cosmwasm_1_3 --- packages/std/src/results/cosmos_msg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/std/src/results/cosmos_msg.rs b/packages/std/src/results/cosmos_msg.rs index cf5c61e91e..9502f3efc6 100644 --- a/packages/std/src/results/cosmos_msg.rs +++ b/packages/std/src/results/cosmos_msg.rs @@ -471,6 +471,7 @@ mod tests { } #[test] + #[cfg(feature = "cosmwasm_1_3")] fn msg_distribution_serializes_to_correct_json() { // FundCommunityPool let fund_coins = vec![coin(200, "feathers"), coin(200, "stones")]; From f5886940613a8106b7e8edf55344174254c5d3e5 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 28 Jun 2023 14:52:11 +0200 Subject: [PATCH 074/113] Polish CHANGELOG entry and capabilities docs --- CHANGELOG.md | 5 +++-- docs/CAPABILITIES-BUILT-IN.md | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cbe5face8..4aafbc2baf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,6 @@ and this project adheres to ## [Unreleased] -- cosmwasm-std: Add `DistributionMsg::FundCommunityPool` ([#1747]) - ### Added - cosmwasm-std: Implement `BankQuery::AllDenomMetadata` to allow querying all @@ -18,6 +16,8 @@ and this project adheres to ([#1647]) - cosmwasm-std: Add `DistributionQuery::DelegatorWithdrawAddress`. Also needs the `cosmwasm_1_3` feature (see above). ([#1593]) +- cosmwasm-std: Add `DistributionMsg::FundCommunityPool`. Also needs the + `cosmwasm_1_3` feature (see above). ([#1747]) - cosmwasm-std: Add `FromStr` impl for `Coin`. ([#1684]) - cosmwasm-std: Add `Coins` helper to handle multiple coins. ([#1687]) - cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have @@ -35,6 +35,7 @@ and this project adheres to [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 [#1687]: https://github.com/CosmWasm/cosmwasm/pull/1687 [#1727]: https://github.com/CosmWasm/cosmwasm/issues/1727 +[#1747]: https://github.com/CosmWasm/cosmwasm/pull/1747 [#1748]: https://github.com/CosmWasm/cosmwasm/pull/1748 ### Changed diff --git a/docs/CAPABILITIES-BUILT-IN.md b/docs/CAPABILITIES-BUILT-IN.md index 11990c839a..2f2268e4e7 100644 --- a/docs/CAPABILITIES-BUILT-IN.md +++ b/docs/CAPABILITIES-BUILT-IN.md @@ -15,7 +15,7 @@ might define others. CosmWasm `1.1.0` or higher support this. - `cosmwasm_1_2` enables the `GovMsg::VoteWeighted` and `WasmMsg::Instantiate2` messages. Only chains running CosmWasm `1.2.0` or higher support this. -- `cosmwasm_1_3` enables the `BankQuery::AllDenomMetadata` and - `BankQuery::DenomMetadata` queries, as well as - `DistributionQuery::DelegatorWithdrawAddress`. Only chains running CosmWasm - `1.3.0` or higher support this. +- `cosmwasm_1_3` enables the `BankQuery::AllDenomMetadata`, + `BankQuery::DenomMetadata` and `DistributionQuery::DelegatorWithdrawAddress` + queries, as well as `DistributionMsg::FundCommunityPool`. Only chains running + CosmWasm `1.3.0` or higher support this. From 78c9adfeeb29d64e890fa45827ce5de4c9d43eb4 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 7 Jun 2023 11:34:54 +0200 Subject: [PATCH 075/113] Port big uint types to bnum --- Cargo.lock | 7 ++ contracts/burner/Cargo.lock | 7 ++ contracts/crypto-verify/Cargo.lock | 7 ++ contracts/cyberpunk/Cargo.lock | 7 ++ contracts/floaty/Cargo.lock | 7 ++ contracts/hackatom/Cargo.lock | 7 ++ contracts/ibc-reflect-send/Cargo.lock | 7 ++ contracts/ibc-reflect/Cargo.lock | 7 ++ contracts/queue/Cargo.lock | 7 ++ contracts/reflect/Cargo.lock | 7 ++ contracts/staking/Cargo.lock | 7 ++ contracts/virus/Cargo.lock | 7 ++ packages/std/Cargo.toml | 1 + packages/std/src/math/decimal256.rs | 2 +- packages/std/src/math/uint256.rs | 67 ++++++++--------- packages/std/src/math/uint512.rs | 104 +++++++++++++------------- 16 files changed, 167 insertions(+), 91 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a7dafafed..dc3a1c5bf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,6 +123,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -350,6 +356,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "chrono", "cosmwasm-crypto", "cosmwasm-derive", diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock index ffc5bc3faf..129021b06e 100644 --- a/contracts/burner/Cargo.lock +++ b/contracts/burner/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -215,6 +221,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock index 525807cef8..a4e25b5b03 100644 --- a/contracts/crypto-verify/Cargo.lock +++ b/contracts/crypto-verify/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -210,6 +216,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock index b254a657ac..e6ab3c05f3 100644 --- a/contracts/cyberpunk/Cargo.lock +++ b/contracts/cyberpunk/Cargo.lock @@ -114,6 +114,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -233,6 +239,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock index 7903f0555d..e9932e6158 100644 --- a/contracts/floaty/Cargo.lock +++ b/contracts/floaty/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock index 77eb0d0e6d..f7ac3df002 100644 --- a/contracts/hackatom/Cargo.lock +++ b/contracts/hackatom/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock index 68f519d999..b5688564bc 100644 --- a/contracts/ibc-reflect-send/Cargo.lock +++ b/contracts/ibc-reflect-send/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock index 06dcdd37bc..1491d26548 100644 --- a/contracts/ibc-reflect/Cargo.lock +++ b/contracts/ibc-reflect/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock index ec0086d567..f4d4dd6354 100644 --- a/contracts/queue/Cargo.lock +++ b/contracts/queue/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock index 90a729bb0d..8099b681ec 100644 --- a/contracts/reflect/Cargo.lock +++ b/contracts/reflect/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock index 7d8e834d27..0886b36c00 100644 --- a/contracts/staking/Cargo.lock +++ b/contracts/staking/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock index a55dc46412..d01769b280 100644 --- a/contracts/virus/Cargo.lock +++ b/contracts/virus/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index af772d64e4..fbec9eb810 100644 --- a/packages/std/Cargo.toml +++ b/packages/std/Cargo.toml @@ -55,6 +55,7 @@ serde = { version = "1.0.103", default-features = false, features = ["derive", " serde-json-wasm = { version = "0.5.0" } thiserror = "1.0.26" uint = "0.9.3" +bnum = "=0.7.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] cosmwasm-crypto = { path = "../crypto", version = "1.2.7" } diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index 157e7397b7..927c10b616 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -2070,7 +2070,7 @@ mod tests { } #[test] - #[should_panic(expected = "division by zero")] + #[should_panic(expected = "divisor of zero")] fn decimal256_rem_panics_for_zero() { let _ = Decimal256::percent(777) % Decimal256::zero(); } diff --git a/packages/std/src/math/uint256.rs b/packages/std/src/math/uint256.rs index e074be4a45..b770f6614b 100644 --- a/packages/std/src/math/uint256.rs +++ b/packages/std/src/math/uint256.rs @@ -14,18 +14,9 @@ use crate::errors::{ }; use crate::{forward_ref_partial_eq, impl_mul_fraction, Fraction, Uint128, Uint512, Uint64}; -/// This module is purely a workaround that lets us ignore lints for all the code -/// the `construct_uint!` macro generates. -#[allow(clippy::all)] -mod uints { - uint::construct_uint! { - pub struct U256(4); - } -} - /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. -use uints::U256; +use bnum::types::U256; /// An implementation of u256 that is using strings for JSON encoding/decoding, /// such that the full u256 range can be used for clients that convert JSON numbers to floats, @@ -54,7 +45,7 @@ forward_ref_partial_eq!(Uint256, Uint256); impl Uint256 { pub const MAX: Uint256 = Uint256(U256::MAX); - pub const MIN: Uint256 = Uint256(U256::zero()); + pub const MIN: Uint256 = Uint256(U256::ZERO); /// Creates a Uint256(value) from a big endian representation. It's just an alias for /// [`Uint256::from_be_bytes`]. @@ -67,7 +58,7 @@ impl Uint256 { /// Creates a Uint256(0) #[inline] pub const fn zero() -> Self { - Uint256(U256::zero()) + Uint256(U256::ZERO) } /// Creates a Uint256(1) @@ -95,7 +86,7 @@ impl Uint256 { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], ]), ]; - Self(U256(words)) + Self(U256::from_digits(words)) } #[must_use] @@ -114,7 +105,7 @@ impl Uint256 { data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], ]), ]; - Uint256(U256(words)) + Self(U256::from_digits(words)) } /// A conversion from `u128` that, unlike the one provided by the `From` trait, @@ -140,11 +131,12 @@ impl Uint256 { /// Returns a copy of the number as big endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_be_bytes(self) -> [u8; 32] { + let words = self.0.digits(); let words = [ - (self.0).0[3].to_be_bytes(), - (self.0).0[2].to_be_bytes(), - (self.0).0[1].to_be_bytes(), - (self.0).0[0].to_be_bytes(), + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } } @@ -152,24 +144,24 @@ impl Uint256 { /// Returns a copy of the number as little endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_le_bytes(self) -> [u8; 32] { + let words = self.0.digits(); let words = [ - (self.0).0[0].to_le_bytes(), - (self.0).0[1].to_le_bytes(), - (self.0).0[2].to_le_bytes(), - (self.0).0[3].to_le_bytes(), + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } } #[must_use] pub const fn is_zero(&self) -> bool { - let words = (self.0).0; - words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 + self.0.is_zero() } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { - let res = self.0.pow(exp.into()); + let res = self.0.pow(exp); Self(res) } @@ -257,7 +249,7 @@ impl Uint256 { pub fn checked_pow(self, exp: u32) -> Result { self.0 - .checked_pow(exp.into()) + .checked_pow(exp) .map(Self) .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) } @@ -320,7 +312,7 @@ impl Uint256 { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_pow(self, other: u32) -> Self { - let (value, _did_overflow) = self.0.overflowing_pow(other.into()); + let (value, _did_overflow) = self.0.overflowing_pow(other); Self(value) } @@ -427,7 +419,7 @@ impl FromStr for Uint256 { return Err(StdError::generic_err("Parsing u256: received empty string")); } - match U256::from_dec_str(s) { + match U256::from_str_radix(s, 10) { Ok(u) => Ok(Uint256(u)), Err(e) => Err(StdError::generic_err(format!("Parsing u256: {}", e))), } @@ -1047,22 +1039,25 @@ mod tests { #[test] fn uint256_convert_from() { let a = Uint256::from(5u128); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u64); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u32); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u16); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u8); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let result = Uint256::try_from("34567"); - assert_eq!(result.unwrap().0, U256::from_dec_str("34567").unwrap()); + assert_eq!( + result.unwrap().0, + U256::from_str_radix("34567", 10).unwrap() + ); let result = Uint256::try_from("1.23"); assert!(result.is_err()); @@ -1222,7 +1217,7 @@ mod tests { #[test] fn uint256_is_zero_works() { assert!(Uint256::zero().is_zero()); - assert!(Uint256(U256::from(0)).is_zero()); + assert!(Uint256(U256::from(0u32)).is_zero()); assert!(!Uint256::from(1u32).is_zero()); assert!(!Uint256::from(123u32).is_zero()); @@ -1643,7 +1638,7 @@ mod tests { } #[test] - #[should_panic(expected = "division by zero")] + #[should_panic(expected = "divisor of zero")] fn uint256_rem_panics_for_zero() { let _ = Uint256::from(10u32) % Uint256::zero(); } diff --git a/packages/std/src/math/uint512.rs b/packages/std/src/math/uint512.rs index 9b5809d3d0..c44e214b50 100644 --- a/packages/std/src/math/uint512.rs +++ b/packages/std/src/math/uint512.rs @@ -3,8 +3,8 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use std::fmt; use std::ops::{ - Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, - Sub, SubAssign, + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, }; use std::str::FromStr; @@ -13,18 +13,9 @@ use crate::errors::{ }; use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint64}; -/// This module is purely a workaround that lets us ignore lints for all the code -/// the `construct_uint!` macro generates. -#[allow(clippy::all)] -mod uints { - uint::construct_uint! { - pub struct U512(8); - } -} - /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. -use uints::U512; +use bnum::types::U512; /// An implementation of u512 that is using strings for JSON encoding/decoding, /// such that the full u512 range can be used for clients that convert JSON numbers to floats, @@ -57,10 +48,10 @@ forward_ref_partial_eq!(Uint512, Uint512); impl Uint512 { pub const MAX: Uint512 = Uint512(U512::MAX); - pub const MIN: Uint512 = Uint512(U512::zero()); + pub const MIN: Uint512 = Uint512(U512::ZERO); /// Creates a Uint512(value) from a big endian representation. It's just an alias for - /// `from_big_endian`. + /// `from_be_bytes`. pub const fn new(value: [u8; 64]) -> Self { Self::from_be_bytes(value) } @@ -68,7 +59,7 @@ impl Uint512 { /// Creates a Uint512(0) #[inline] pub const fn zero() -> Self { - Uint512(U512::zero()) + Uint512(U512::ZERO) } /// Creates a Uint512(1) @@ -109,7 +100,7 @@ impl Uint512 { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], ]), ]; - Self(U512(words)) + Self(U512::from_digits(words)) } #[must_use] @@ -140,7 +131,7 @@ impl Uint512 { data[56], data[57], data[58], data[59], data[60], data[61], data[62], data[63], ]), ]; - Self(U512(words)) + Self(U512::from_digits(words)) } /// A conversion from `Uint256` that, unlike the one provided by the `From` trait, @@ -161,15 +152,16 @@ impl Uint512 { /// Returns a copy of the number as big endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_be_bytes(self) -> [u8; 64] { + let words = self.0.digits(); let words = [ - (self.0).0[7].to_be_bytes(), - (self.0).0[6].to_be_bytes(), - (self.0).0[5].to_be_bytes(), - (self.0).0[4].to_be_bytes(), - (self.0).0[3].to_be_bytes(), - (self.0).0[2].to_be_bytes(), - (self.0).0[1].to_be_bytes(), - (self.0).0[0].to_be_bytes(), + words[7].to_be_bytes(), + words[6].to_be_bytes(), + words[5].to_be_bytes(), + words[4].to_be_bytes(), + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } } @@ -177,35 +169,28 @@ impl Uint512 { /// Returns a copy of the number as little endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_le_bytes(self) -> [u8; 64] { + let words = self.0.digits(); let words = [ - (self.0).0[0].to_le_bytes(), - (self.0).0[1].to_le_bytes(), - (self.0).0[2].to_le_bytes(), - (self.0).0[3].to_le_bytes(), - (self.0).0[4].to_le_bytes(), - (self.0).0[5].to_le_bytes(), - (self.0).0[6].to_le_bytes(), - (self.0).0[7].to_le_bytes(), + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), + words[4].to_le_bytes(), + words[5].to_le_bytes(), + words[6].to_le_bytes(), + words[7].to_le_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } } #[must_use] pub const fn is_zero(&self) -> bool { - let words = (self.0).0; - words[0] == 0 - && words[1] == 0 - && words[2] == 0 - && words[3] == 0 - && words[4] == 0 - && words[5] == 0 - && words[6] == 0 - && words[7] == 0 + self.0.is_zero() } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { - let res = self.0.pow(exp.into()); + let res = self.0.pow(exp); Self(res) } @@ -232,7 +217,7 @@ impl Uint512 { pub fn checked_pow(self, exp: u32) -> Result { self.0 - .checked_pow(exp.into()) + .checked_pow(exp) .map(Self) .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) } @@ -295,7 +280,7 @@ impl Uint512 { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_pow(self, other: u32) -> Self { - let (value, _did_overflow) = self.0.overflowing_pow(other.into()); + let (value, _did_overflow) = self.0.overflowing_pow(other); Self(value) } @@ -424,7 +409,7 @@ impl FromStr for Uint512 { type Err = StdError; fn from_str(s: &str) -> Result { - match U512::from_dec_str(s) { + match U512::from_str_radix(s, 10) { Ok(u) => Ok(Self(u)), Err(e) => Err(StdError::generic_err(format!("Parsing u512: {}", e))), } @@ -508,6 +493,14 @@ impl Rem for Uint512 { } forward_ref_binop!(impl Rem, rem for Uint512, Uint512); +impl Not for Uint512 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + impl RemAssign for Uint512 { fn rem_assign(&mut self, rhs: Uint512) { *self = *self % rhs; @@ -743,22 +736,25 @@ mod tests { #[test] fn uint512_convert_from() { let a = Uint512::from(5u128); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u64); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u32); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u16); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u8); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let result = Uint512::try_from("34567"); - assert_eq!(result.unwrap().0, U512::from_dec_str("34567").unwrap()); + assert_eq!( + result.unwrap().0, + U512::from_str_radix("34567", 10).unwrap() + ); let result = Uint512::try_from("1.23"); assert!(result.is_err()); @@ -936,7 +932,7 @@ mod tests { #[test] fn uint512_is_zero_works() { assert!(Uint512::zero().is_zero()); - assert!(Uint512(U512::from(0)).is_zero()); + assert!(Uint512(U512::from(0u32)).is_zero()); assert!(!Uint512::from(1u32).is_zero()); assert!(!Uint512::from(123u32).is_zero()); @@ -1301,7 +1297,7 @@ mod tests { } #[test] - #[should_panic(expected = "division by zero")] + #[should_panic(expected = "divisor of zero")] fn uint512_rem_panics_for_zero() { let _ = Uint512::from(10u32) % Uint512::zero(); } From 6b9b234cc0f283abd12daf6f451d0a153ea5c796 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 7 Jun 2023 12:07:02 +0200 Subject: [PATCH 076/113] Implement Int512 --- packages/std/src/lib.rs | 4 +- packages/std/src/math/int512.rs | 1293 +++++++++++++++++++++++++++++++ packages/std/src/math/mod.rs | 3 + 3 files changed, 1298 insertions(+), 2 deletions(-) create mode 100644 packages/std/src/math/int512.rs diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index 28dfcc9ec1..e05f1b4eb1 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -52,8 +52,8 @@ pub use crate::ibc::{ #[cfg(feature = "iterator")] pub use crate::iterator::{Order, Record}; pub use crate::math::{ - Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Isqrt, Uint128, - Uint256, Uint512, Uint64, + Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int512, Isqrt, + Uint128, Uint256, Uint512, Uint64, }; pub use crate::metadata::{DenomMetadata, DenomUnit}; pub use crate::never::Never; diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs new file mode 100644 index 0000000000..ddf1a89675 --- /dev/null +++ b/packages/std/src/math/int512.rs @@ -0,0 +1,1293 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Not, Rem, RemAssign, Shr, ShrAssign, Sub, + SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{ + ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, StdError, +}; +use crate::{forward_ref_partial_eq, 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::{I512, U512}; + +/// An implementation of u512 that is using strings for JSON encoding/decoding, +/// such that the full u512 range can be used for clients that convert JSON numbers to floats, +/// like JavaScript and jq. +/// +/// # Examples +/// +/// Use `from` to create instances out of primitive uint types or `new` to provide big +/// endian bytes: +/// +/// ``` +/// # use cosmwasm_std::Int512; +/// let a = Int512::from(258u128); +/// let b = Int512::new([ +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, +/// ]); +/// assert_eq!(a, b); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int512(#[schemars(with = "String")] I512); + +forward_ref_partial_eq!(Int512, Int512); + +impl Int512 { + pub const MAX: Int512 = Int512(I512::MAX); + pub const MIN: Int512 = Int512(I512::MIN); + + /// Creates a Int512(value) from a big endian representation. It's just an alias for + /// `from_be_bytes`. + pub const fn new(value: [u8; 64]) -> Self { + Self::from_be_bytes(value) + } + + /// Creates a Int512(0) + #[inline] + pub const fn zero() -> Self { + Int512(I512::ZERO) + } + + /// Creates a Int512(1) + #[inline] + pub const fn one() -> Self { + Self::from_be_bytes([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + ]) + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 64]) -> Self { + let words: [u64; 8] = [ + u64::from_le_bytes([ + data[63], data[62], data[61], data[60], data[59], data[58], data[57], data[56], + ]), + u64::from_le_bytes([ + data[55], data[54], data[53], data[52], data[51], data[50], data[49], data[48], + ]), + u64::from_le_bytes([ + data[47], data[46], data[45], data[44], data[43], data[42], data[41], data[40], + ]), + u64::from_le_bytes([ + data[39], data[38], data[37], data[36], data[35], data[34], data[33], data[32], + ]), + u64::from_le_bytes([ + data[31], data[30], data[29], data[28], data[27], data[26], data[25], data[24], + ]), + u64::from_le_bytes([ + data[23], data[22], data[21], data[20], data[19], data[18], data[17], data[16], + ]), + u64::from_le_bytes([ + data[15], data[14], data[13], data[12], data[11], data[10], data[9], data[8], + ]), + u64::from_le_bytes([ + data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], + ]), + ]; + Self(I512::from_bits(U512::from_digits(words))) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 64]) -> Self { + let words: [u64; 8] = [ + u64::from_le_bytes([ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]), + u64::from_le_bytes([ + data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], + ]), + u64::from_le_bytes([ + data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23], + ]), + u64::from_le_bytes([ + data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], + ]), + u64::from_le_bytes([ + data[32], data[33], data[34], data[35], data[36], data[37], data[38], data[39], + ]), + u64::from_le_bytes([ + data[40], data[41], data[42], data[43], data[44], data[45], data[46], data[47], + ]), + u64::from_le_bytes([ + data[48], data[49], data[50], data[51], data[52], data[53], data[54], data[55], + ]), + u64::from_le_bytes([ + data[56], data[57], data[58], data[59], data[60], data[61], data[62], data[63], + ]), + ]; + Self(I512::from_bits(U512::from_digits(words))) + } + + /// A conversion from `Uint256` that, unlike the one provided by the `From` trait, + /// can be used in a `const` context. + #[must_use] + pub const fn from_uint256(num: Uint256) -> Self { + let bytes = num.to_le_bytes(); + Self::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], + bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23], + bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31], + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 64] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[7].to_be_bytes(), + words[6].to_be_bytes(), + words[5].to_be_bytes(), + words[4].to_be_bytes(), + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 64] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), + words[4].to_le_bytes(), + words[5].to_le_bytes(), + words[6].to_le_bytes(), + words[7].to_le_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + let res = self.0.pow(exp); + Self(res) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + self.0 + .checked_div(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + self.checked_div(other) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 512 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + let (value, _did_overflow) = self.0.overflowing_add(other.0); + Self(value) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + let (value, _did_overflow) = self.0.overflowing_sub(other.0); + Self(value) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + let (value, _did_overflow) = self.0.overflowing_mul(other.0); + Self(value) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + let (value, _did_overflow) = self.0.overflowing_pow(other); + Self(value) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => Self::MAX, + } + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Self { + if self < other { + other - self + } else { + self - other + } + } +} + +impl From for Int512 { + fn from(val: Uint256) -> Self { + let bytes = [[0u8; 32], val.to_be_bytes()].concat(); + + Self::from_be_bytes(bytes.try_into().unwrap()) + } +} + +impl From for Int512 { + fn from(val: Uint128) -> Self { + val.u128().into() + } +} + +impl From for Int512 { + fn from(val: Uint64) -> Self { + val.u64().into() + } +} + +impl From for Int512 { + fn from(val: u128) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u64) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u32) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u16) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u8) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i128) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i64) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i32) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i16) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i8) -> Self { + Int512(val.into()) + } +} + +// impl TryFrom for Int256 { +// type Error = ConversionOverflowError; + +// fn try_from(value: Int512) -> Result { +// let bytes = value.to_be_bytes(); +// let (first_bytes, last_bytes) = bytes.split_at(32); + +// if first_bytes != [0u8; 32] { +// return Err(ConversionOverflowError::new( +// "Int512", +// "Uint256", +// value.to_string(), +// )); +// } + +// Ok(Self::from_be_bytes(last_bytes.try_into().unwrap())) +// } +// } + +// impl TryFrom for Int128 { +// type Error = ConversionOverflowError; + +// fn try_from(value: Int512) -> Result { +// Ok(Uint128::new(value.0.try_into().map_err(|_| { +// ConversionOverflowError::new("Int512", "Uint128", value.to_string()) +// })?)) +// } +// } + +impl TryFrom<&str> for Int512 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int512 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match I512::from_str_radix(s, 10) { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing u512: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int512) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int512 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // The inner type doesn't work as expected with padding, so we + // work around that. + let unpadded = self.0.to_string(); + + f.pad_integral(true, "", &unpadded) + } +} + +impl Add for Int512 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int512(self.0.checked_add(rhs.0).unwrap()) + } +} + +impl<'a> Add<&'a Int512> for Int512 { + type Output = Self; + + fn add(self, rhs: &'a Int512) -> Self { + Int512(self.0.checked_add(rhs.0).unwrap()) + } +} + +impl Sub for Int512 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int512(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int512, Int512); + +impl SubAssign for Int512 { + fn sub_assign(&mut self, rhs: Int512) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int512, Int512); + +impl Div for Int512 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} + +impl<'a> Div<&'a Int512> for Int512 { + type Output = Self; + + fn div(self, rhs: &'a Int512) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} + +impl Rem for Int512 { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero. + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for Int512, Int512); + +impl Not for Int512 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl RemAssign for Int512 { + fn rem_assign(&mut self, rhs: Int512) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int512, Int512); + +impl Mul for Int512 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int512, Int512); + +impl MulAssign for Int512 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int512, Int512); + +impl Shr for Int512 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int512", + rhs, + ) + }) + } +} + +impl<'a> Shr<&'a u32> for Int512 { + type Output = Self; + + fn shr(self, rhs: &'a u32) -> Self::Output { + Shr::::shr(self, *rhs) + } +} + +impl AddAssign for Int512 { + fn add_assign(&mut self, rhs: Int512) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} + +impl<'a> AddAssign<&'a Int512> for Int512 { + fn add_assign(&mut self, rhs: &'a Int512) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} + +impl DivAssign for Int512 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} + +impl<'a> DivAssign<&'a Int512> for Int512 { + fn div_assign(&mut self, rhs: &'a Int512) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} + +impl ShrAssign for Int512 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} + +impl<'a> ShrAssign<&'a u32> for Int512 { + fn shr_assign(&mut self, rhs: &'a u32) { + *self = Shr::::shr(*self, *rhs); + } +} + +impl Serialize for Int512 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int512 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int512Visitor) + } +} + +struct Int512Visitor; + +impl<'de> de::Visitor<'de> for Int512Visitor { + type Value = Int512; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int512::try_from(v).map_err(|e| E::custom(format!("invalid Int512 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int512 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 64); + } + + #[test] + fn int512_new_works() { + let num = Int512::new([1; 64]); + let a: [u8; 64] = num.to_be_bytes(); + assert_eq!(a, [1; 64]); + + let be_bytes = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Int512::new(be_bytes); + let resulting_bytes: [u8; 64] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int512_zero_works() { + let zero = Int512::zero(); + assert_eq!( + zero.to_be_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + } + + #[test] + fn uin512_one_works() { + let one = Int512::one(); + assert_eq!( + one.to_be_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1 + ] + ); + } + + #[test] + fn int512_endianness() { + let be_bytes = [ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let le_bytes = [ + 3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]; + + // These should all be the same. + let num1 = Int512::new(be_bytes); + let num2 = Int512::from_be_bytes(be_bytes); + let num3 = Int512::from_le_bytes(le_bytes); + assert_eq!(num1, Int512::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + assert_eq!(num1, num3); + } + + #[test] + fn int512_convert_from() { + let a = Int512::from(5u128); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u64); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u32); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u16); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u8); + assert_eq!(a.0, I512::from(5u32)); + + let result = Int512::try_from("34567"); + assert_eq!( + result.unwrap().0, + I512::from_str_radix("34567", 10).unwrap() + ); + + let result = Int512::try_from("1.23"); + assert!(result.is_err()); + } + + // #[test] + // fn int512_convert_to_uint128() { + // let source = Int512::from(42u128); + // let target = Uint128::try_from(source); + // assert_eq!(target, Ok(Uint128::new(42u128))); + + // let source = Int512::MAX; + // let target = Uint128::try_from(source); + // assert_eq!( + // target, + // Err(ConversionOverflowError::new( + // "Int512", + // "Uint128", + // Int512::MAX.to_string() + // )) + // ); + // } + + #[test] + fn int512_from_uint256() { + assert_eq!( + Int512::from_uint256(Uint256::from_str("123").unwrap()), + Int512::from_str("123").unwrap() + ); + + assert_eq!( + Int512::from_uint256(Uint256::from_str("9785746283745").unwrap()), + Int512::from_str("9785746283745").unwrap() + ); + + assert_eq!( + Int512::from_uint256( + Uint256::from_str( + "97857462837575757832978493758398593853985452378423874623874628736482736487236" + ) + .unwrap() + ), + Int512::from_str( + "97857462837575757832978493758398593853985452378423874623874628736482736487236" + ) + .unwrap() + ); + } + + #[test] + fn int512_implements_display() { + let a = Int512::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int512::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int512_display_padding_works() { + let a = Int512::from(123u64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + } + + #[test] + fn int512_to_be_bytes_works() { + assert_eq!( + Int512::zero().to_be_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + ] + ); + assert_eq!( + Int512::MAX.to_be_bytes(), + [ + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ] + ); + assert_eq!( + Int512::from(1u128).to_be_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1 + ] + ); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(64, "big")]` + assert_eq!( + Int512::from(240282366920938463463374607431768124608u128).to_be_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 196, 179, 87, 165, + 121, 59, 133, 246, 117, 221, 191, 255, 254, 172, 192 + ] + ); + assert_eq!( + Int512::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240, 218, 88, 106, 45, 208, 134, + 238, 119, 85, 22, 14, 88, 166, 195, 154, 73, 64, 10, 44, 59, 13, 22, 47, 12, 99, 8, + 252, 96, 230, 187, 38, 29 + ]) + .to_be_bytes(), + [ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240, 218, 88, 106, 45, 208, 134, + 238, 119, 85, 22, 14, 88, 166, 195, 154, 73, 64, 10, 44, 59, 13, 22, 47, 12, 99, 8, + 252, 96, 230, 187, 38, 29 + ] + ); + } + + #[test] + fn int512_to_le_bytes_works() { + assert_eq!( + Int512::zero().to_le_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + assert_eq!( + Int512::MAX.to_le_bytes(), + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f + ] + ); + assert_eq!( + Int512::from(1u128).to_le_bytes(), + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(64, "little")]` + assert_eq!( + Int512::from(240282366920938463463374607431768124608u128).to_le_bytes(), + [ + 192, 172, 254, 255, 191, 221, 117, 246, 133, 59, 121, 165, 87, 179, 196, 180, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + assert_eq!( + Int512::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240, 218, 88, 106, 45, 208, 134, + 238, 119, 85, 22, 14, 88, 166, 195, 154, 73, 64, 10, 44, 59, 13, 22, 47, 12, 99, 8, + 252, 96, 230, 187, 38, 29 + ]) + .to_le_bytes(), + [ + 29, 38, 187, 230, 96, 252, 8, 99, 12, 47, 22, 13, 59, 44, 10, 64, 73, 154, 195, + 166, 88, 14, 22, 85, 119, 238, 134, 208, 45, 106, 88, 218, 240, 150, 115, 200, 240, + 2, 233, 42, 7, 192, 201, 211, 54, 65, 76, 87, 78, 67, 21, 33, 38, 0, 91, 58, 200, + 123, 67, 87, 32, 23, 4, 17 + ] + ); + } + + #[test] + fn int512_is_zero_works() { + assert!(Int512::zero().is_zero()); + assert!(Int512(I512::from(0u32)).is_zero()); + + assert!(!Int512::from(1u32).is_zero()); + assert!(!Int512::from(123u32).is_zero()); + } + + #[test] + fn int512_wrapping_methods() { + // wrapping_add + assert_eq!( + Int512::from(2u32).wrapping_add(Int512::from(2u32)), + Int512::from(4u32) + ); // non-wrapping + assert_eq!(Int512::MAX.wrapping_add(Int512::from(1u32)), Int512::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int512::from(7u32).wrapping_sub(Int512::from(5u32)), + Int512::from(2u32) + ); // non-wrapping + assert_eq!(Int512::MIN.wrapping_sub(Int512::from(1u32)), Int512::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int512::from(3u32).wrapping_mul(Int512::from(2u32)), + Int512::from(6u32) + ); // non-wrapping + assert_eq!( + Int512::MAX.wrapping_mul(Int512::from(2u32)), + Int512::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int512::from(2u32).wrapping_pow(3), Int512::from(8u32)); // non-wrapping + assert_eq!(Int512::MAX.wrapping_pow(2), Int512::from(1u32)); // wrapping + } + + #[test] + fn int512_json() { + let orig = Int512::from(1234567890987654321u128); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int512 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int512_compare() { + let a = Int512::from(12345u32); + let b = Int512::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int512::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_math() { + let a = Int512::from(12345u32); + let b = Int512::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int512::from(35801u32)); + assert_eq!(a + &b, Int512::from(35801u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int512::from(11111u32)); + assert_eq!(b - &a, Int512::from(11111u32)); + + // test += with owned and reference right hand side + let mut c = Int512::from(300000u32); + c += b; + assert_eq!(c, Int512::from(323456u32)); + let mut d = Int512::from(300000u32); + d += &b; + assert_eq!(d, Int512::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int512::from(300000u32); + c -= b; + assert_eq!(c, Int512::from(276544u32)); + let mut d = Int512::from(300000u32); + d -= &b; + assert_eq!(d, Int512::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int512::from(-11111i32)); + } + + #[test] + #[should_panic] + fn int512_add_overflow_panics() { + let _ = Int512::MAX + Int512::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_sub_works() { + assert_eq!(Int512::from(2u32) - Int512::from(1u32), Int512::from(1u32)); + assert_eq!(Int512::from(2u32) - Int512::from(0u32), Int512::from(2u32)); + assert_eq!(Int512::from(2u32) - Int512::from(2u32), Int512::from(0u32)); + + // works for refs + let a = Int512::from(10u32); + let b = Int512::from(3u32); + let expected = Int512::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int512_sub_overflow_panics() { + let _ = Int512::MIN + Int512::one() - Int512::from(2u32); + } + + #[test] + fn int512_sub_assign_works() { + let mut a = Int512::from(14u32); + a -= Int512::from(2u32); + assert_eq!(a, Int512::from(12u32)); + + // works for refs + let mut a = Int512::from(10u32); + let b = Int512::from(3u32); + let expected = Int512::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_mul_works() { + assert_eq!(Int512::from(2u32) * Int512::from(3u32), Int512::from(6u32)); + assert_eq!(Int512::from(2u32) * Int512::zero(), Int512::zero()); + + // works for refs + let a = Int512::from(11u32); + let b = Int512::from(3u32); + let expected = Int512::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int512_mul_assign_works() { + let mut a = Int512::from(14u32); + a *= Int512::from(2u32); + assert_eq!(a, Int512::from(28u32)); + + // works for refs + let mut a = Int512::from(10u32); + let b = Int512::from(3u32); + a *= &b; + assert_eq!(a, Int512::from(30u32)); + } + + #[test] + fn int512_pow_works() { + assert_eq!(Int512::from(2u32).pow(2), Int512::from(4u32)); + assert_eq!(Int512::from(2u32).pow(10), Int512::from(1024u32)); + } + + #[test] + #[should_panic] + fn int512_pow_overflow_panics() { + _ = Int512::MAX.pow(2u32); + } + + #[test] + fn int512_shr_works() { + let original = Int512::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, + ]); + + let shifted = Int512::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, + ]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int512_shr_overflow_panics() { + let _ = Int512::from(1u32) >> 512u32; + } + + #[test] + fn sum_works() { + let nums = vec![ + Int512::from(17u32), + Int512::from(123u32), + Int512::from(540u32), + Int512::from(82u32), + ]; + let expected = Int512::from(762u32); + + let sum_as_ref: Int512 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int512 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int512_methods() { + // checked_* + assert!(matches!( + Int512::MAX.checked_add(Int512::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int512::from(1u32).checked_add(Int512::from(1u32)), + Ok(Int512::from(2u32)), + ); + assert!(matches!( + Int512::MIN.checked_sub(Int512::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int512::from(2u32).checked_sub(Int512::from(1u32)), + Ok(Int512::from(1u32)), + ); + assert!(matches!( + Int512::MAX.checked_mul(Int512::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int512::from(2u32).checked_mul(Int512::from(2u32)), + Ok(Int512::from(4u32)), + ); + assert!(matches!( + Int512::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int512::from(2u32).checked_pow(3u32), Ok(Int512::from(8u32)),); + assert!(matches!( + Int512::MAX.checked_div(Int512::from(0u32)), + Err(DivideByZeroError { .. }) + )); + assert_eq!( + Int512::from(6u32).checked_div(Int512::from(2u32)), + Ok(Int512::from(3u32)), + ); + assert!(matches!( + Int512::MAX.checked_div_euclid(Int512::from(0u32)), + Err(DivideByZeroError { .. }) + )); + assert_eq!( + Int512::from(6u32).checked_div_euclid(Int512::from(2u32)), + Ok(Int512::from(3u32)), + ); + assert_eq!( + Int512::from(7u32).checked_div_euclid(Int512::from(2u32)), + Ok(Int512::from(3u32)), + ); + assert!(matches!( + Int512::MAX.checked_rem(Int512::from(0u32)), + Err(DivideByZeroError { .. }) + )); + + // saturating_* + assert_eq!(Int512::MAX.saturating_add(Int512::from(1u32)), Int512::MAX); + assert_eq!(Int512::MIN.saturating_sub(Int512::from(1u32)), Int512::MIN); + assert_eq!(Int512::MAX.saturating_mul(Int512::from(2u32)), Int512::MAX); + assert_eq!(Int512::from(4u32).saturating_pow(2u32), Int512::from(16u32)); + assert_eq!(Int512::MAX.saturating_pow(2u32), Int512::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_implements_rem() { + let a = Int512::from(10u32); + assert_eq!(a % Int512::from(10u32), Int512::zero()); + assert_eq!(a % Int512::from(2u32), Int512::zero()); + assert_eq!(a % Int512::from(1u32), Int512::zero()); + assert_eq!(a % Int512::from(3u32), Int512::from(1u32)); + assert_eq!(a % Int512::from(4u32), Int512::from(2u32)); + + // works for refs + let a = Int512::from(10u32); + let b = Int512::from(3u32); + let expected = Int512::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int512_rem_panics_for_zero() { + let _ = Int512::from(10u32) % Int512::zero(); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_rem_works() { + assert_eq!( + Int512::from(12u32) % Int512::from(10u32), + Int512::from(2u32) + ); + assert_eq!(Int512::from(50u32) % Int512::from(5u32), Int512::zero()); + + // works for refs + let a = Int512::from(42u32); + let b = Int512::from(5u32); + let expected = Int512::from(2u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + fn int512_rem_assign_works() { + let mut a = Int512::from(30u32); + a %= Int512::from(4u32); + assert_eq!(a, Int512::from(2u32)); + + // works for refs + let mut a = Int512::from(25u32); + let b = Int512::from(6u32); + a %= &b; + assert_eq!(a, Int512::from(1u32)); + } + + #[test] + fn int512_abs_diff_works() { + let a = Int512::from(42u32); + let b = Int512::from(5u32); + let expected = Int512::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + } + + #[test] + fn int512_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (u64, u64, bool)| { + (Int512::from(lhs), Int512::from(rhs), expected) + }); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } +} diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index 72e55d9f1d..ee514f36b8 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -1,6 +1,7 @@ mod decimal; mod decimal256; mod fraction; +mod int512; mod isqrt; mod uint128; mod uint256; @@ -10,6 +11,7 @@ mod uint64; pub use decimal::{Decimal, DecimalRangeExceeded}; pub use decimal256::{Decimal256, Decimal256RangeExceeded}; pub use fraction::Fraction; +pub use int512::Int512; pub use isqrt::Isqrt; pub use uint128::Uint128; pub use uint256::Uint256; @@ -68,6 +70,7 @@ mod tests { impl AllImpl<'_> for Uint128 {} impl AllImpl<'_> for Uint256 {} impl AllImpl<'_> for Uint512 {} + impl AllImpl<'_> for Int512 {} impl IntImpl<'_> for Uint64 {} impl IntImpl<'_> for Uint128 {} impl IntImpl<'_> for Uint256 {} From 1f157e7e7fcd0593980d9db511cd69593eee5ed9 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 7 Jun 2023 15:12:11 +0200 Subject: [PATCH 077/113] Update ci rust version --- .circleci/config.yml | 144 +++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ff5bb17087..ab1f977075 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -72,7 +72,7 @@ workflows: matrix: parameters: # Run with MSRV and some modern stable Rust - rust-version: ["1.60.0", "1.68.2"] + rust-version: ["1.65.0", "1.68.2"] - benchmarking: requires: - package_vm @@ -117,7 +117,7 @@ jobs: command: | wget https://static.rust-lang.org/rustup/dist/aarch64-unknown-linux-gnu/rustup-init chmod +x rustup-init - ./rustup-init -y --default-toolchain 1.60.0 --profile minimal + ./rustup-init -y --default-toolchain 1.65.0 --profile minimal - run: name: Version information command: rustc --version; cargo --version; rustup --version; rustup target list --installed @@ -126,12 +126,12 @@ jobs: command: rustup target add wasm32-unknown-unknown && rustup target list --installed - restore_cache: keys: - - v4-arm64-workspace-rust:1.60.0-{{ checksum "Cargo.lock" }} - - v4-arm64-workspace-rust:1.60.0- + - v4-arm64-workspace-rust:1.65.0-{{ checksum "Cargo.lock" }} + - v4-arm64-workspace-rust:1.65.0- - restore_cache: keys: - - v4-arm64-contracts-rust:1.60.0-{{ checksum "contracts/crypto-verify/Cargo.lock" }}-{{ checksum "contracts/hackatom/Cargo.lock" }}-{{ checksum "contracts/queue/Cargo.lock" }}-{{ checksum "contracts/reflect/Cargo.lock" }}-{{ checksum "contracts/staking/Cargo.lock" }} - - v4-arm64-contracts-rust:1.60.0- + - v4-arm64-contracts-rust:1.65.0-{{ checksum "contracts/crypto-verify/Cargo.lock" }}-{{ checksum "contracts/hackatom/Cargo.lock" }}-{{ checksum "contracts/queue/Cargo.lock" }}-{{ checksum "contracts/reflect/Cargo.lock" }}-{{ checksum "contracts/staking/Cargo.lock" }} + - v4-arm64-contracts-rust:1.65.0- # Test a few contracts that do something potentially interesting in the VM # to test contract execution on ARM64. # No need to add all contracts here. @@ -169,14 +169,14 @@ jobs: # use all features command: cargo test --locked --features iterator,staking,stargate - save_cache: - key: v4-arm64-workspace-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: v4-arm64-workspace-rust:1.65.0-{{ checksum "Cargo.lock" }} paths: - ~/.cargo/registry - target/debug/.fingerprint - target/debug/build - target/debug/deps - save_cache: - key: v4-arm64-contracts-rust:1.60.0-{{ checksum "contracts/crypto-verify/Cargo.lock" }}-{{ checksum "contracts/hackatom/Cargo.lock" }}-{{ checksum "contracts/queue/Cargo.lock" }}-{{ checksum "contracts/reflect/Cargo.lock" }}-{{ checksum "contracts/staking/Cargo.lock" }} + key: v4-arm64-contracts-rust:1.65.0-{{ checksum "contracts/crypto-verify/Cargo.lock" }}-{{ checksum "contracts/hackatom/Cargo.lock" }}-{{ checksum "contracts/queue/Cargo.lock" }}-{{ checksum "contracts/reflect/Cargo.lock" }}-{{ checksum "contracts/staking/Cargo.lock" }} paths: - ~/.cargo/registry # crypto-verify @@ -217,7 +217,7 @@ jobs: package_crypto: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -225,7 +225,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_crypto-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_crypto-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build working_directory: ~/project/packages/crypto @@ -240,11 +240,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_crypto-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_crypto-rust:1.65.0-{{ checksum "Cargo.lock" }} package_check: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -252,7 +252,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_check-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_check-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build working_directory: ~/project/packages/check @@ -267,11 +267,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_check-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_check-rust:1.65.0-{{ checksum "Cargo.lock" }} package_schema: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -279,7 +279,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_schema-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_schema-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build working_directory: ~/project/packages/schema @@ -294,11 +294,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_schema-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_schema-rust:1.65.0-{{ checksum "Cargo.lock" }} package_schema_derive: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -306,7 +306,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_schema_derive-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_schema_derive-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build working_directory: ~/project/packages/schema-derive @@ -321,11 +321,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_schema_derive-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_schema_derive-rust:1.65.0-{{ checksum "Cargo.lock" }} package_std: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: # Limit the number of parallel jobs to avoid OOM crashes during doc testing RUST_TEST_THREADS: 8 @@ -336,7 +336,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_std-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_std-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Add wasm32 target command: rustup target add wasm32-unknown-unknown && rustup target list --installed @@ -370,11 +370,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_std-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_std-rust:1.65.0-{{ checksum "Cargo.lock" }} package_storage: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -382,7 +382,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_storage-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_storage-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build library for native target working_directory: ~/project/packages/storage @@ -401,11 +401,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_storage-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_storage-rust:1.65.0-{{ checksum "Cargo.lock" }} package_vm: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -413,7 +413,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_vm-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_vm-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build working_directory: ~/project/packages/vm @@ -442,7 +442,7 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_vm-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_vm-rust:1.65.0-{{ checksum "Cargo.lock" }} package_vm_windows: executor: @@ -490,7 +490,7 @@ jobs: contract_burner: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/burner @@ -502,7 +502,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_burner-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_burner-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -513,11 +513,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_burner-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_burner-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_crypto_verify: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/crypto-verify @@ -529,7 +529,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_crypto_verify-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_crypto_verify-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -540,11 +540,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_crypto_verify-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_crypto_verify-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_cyberpunk: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/cyberpunk @@ -556,7 +556,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_cyberpunk-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_cyberpunk-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -567,11 +567,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_cyberpunk-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_cyberpunk-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_hackatom: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/hackatom @@ -583,7 +583,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_hackatom-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_hackatom-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -594,11 +594,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_hackatom-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_hackatom-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_ibc_reflect: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/ibc-reflect @@ -610,7 +610,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_ibc_reflect-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_ibc_reflect-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -621,11 +621,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_ibc_reflect-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_ibc_reflect-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_ibc_reflect_send: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/ibc-reflect-send @@ -637,7 +637,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_ibc_reflect_send-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_ibc_reflect_send-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -648,11 +648,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_ibc_reflect_send-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_ibc_reflect_send-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_floaty: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/floaty @@ -664,7 +664,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_floaty-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_floaty-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -675,11 +675,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_floaty-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_floaty-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_queue: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/queue @@ -691,7 +691,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_queue-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_queue-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -702,11 +702,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_queue-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_queue-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_reflect: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/reflect @@ -718,7 +718,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_reflect-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_reflect-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -729,11 +729,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_reflect-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_reflect-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_staking: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/staking @@ -745,7 +745,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_staking-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_staking-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -756,11 +756,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_staking-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_staking-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_virus: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/virus @@ -772,7 +772,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_virus-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_virus-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -783,11 +783,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_virus-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_virus-rust:1.65.0-{{ checksum "Cargo.lock" }} fmt: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -795,7 +795,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-fmt-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-fmt-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Add rustfmt component command: rustup component add rustfmt @@ -808,7 +808,7 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-fmt-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-fmt-rust:1.65.0-{{ checksum "Cargo.lock" }} fmt_extra: docker: @@ -830,7 +830,7 @@ jobs: deadlinks: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -838,7 +838,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-deadlinks-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-deadlinks-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Generate docs command: cargo doc @@ -858,7 +858,7 @@ jobs: - target/debug/build - target/debug/deps - /root/.cache/pip - key: cargocache-v2-deadlinks-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-deadlinks-rust:1.65.0-{{ checksum "Cargo.lock" }} clippy: parameters: @@ -934,7 +934,7 @@ jobs: benchmarking: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 steps: @@ -944,7 +944,7 @@ jobs: command: rustc --version && cargo --version - restore_cache: keys: - - cargocache-v2-benchmarking-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-benchmarking-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Run vm benchmarks (Singlepass) working_directory: ~/project/packages/vm @@ -962,7 +962,7 @@ jobs: - target/release/.fingerprint - target/release/build - target/release/deps - key: cargocache-v2-benchmarking-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-benchmarking-rust:1.65.0-{{ checksum "Cargo.lock" }} coverage: # https://circleci.com/developer/images?imageType=machine @@ -1034,7 +1034,7 @@ jobs: name: Check development contracts command: | echo "Checking all contracts under ./artifacts" - docker run --volumes-from with_code rust:1.60.0 \ + docker run --volumes-from with_code rust:1.65.0 \ /bin/bash -e -c 'export GLOBIGNORE="artifacts/floaty.wasm"; cd ./code; cargo run --bin cosmwasm-check artifacts/*.wasm' docker cp with_code:/code/artifacts . - run: From 560d1d9769d1babaf8b689cf9533133b5e62bbef Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 7 Jun 2023 16:21:32 +0200 Subject: [PATCH 078/113] Disable coverage check temporarily --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ab1f977075..ddd1d20bf1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -85,7 +85,7 @@ workflows: - /^[0-9]+\.[0-9]+$/ # Add your branch here if benchmarking matters to your work - fix-benchmarking - - coverage + # - coverage # disabled temporarily because Rust version is too low deploy: jobs: - build_and_upload_devcontracts: From ee059cd76fb41b3509d9341ee222c08392a9a5b7 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 7 Jun 2023 16:43:35 +0200 Subject: [PATCH 079/113] Simplify uint impls --- packages/std/src/math/uint256.rs | 35 ++++++++------------------ packages/std/src/math/uint512.rs | 43 ++++++++++---------------------- 2 files changed, 23 insertions(+), 55 deletions(-) diff --git a/packages/std/src/math/uint256.rs b/packages/std/src/math/uint256.rs index b770f6614b..8e329e20c7 100644 --- a/packages/std/src/math/uint256.rs +++ b/packages/std/src/math/uint256.rs @@ -39,7 +39,7 @@ use bnum::types::U256; /// assert_eq!(a, b); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint256(#[schemars(with = "String")] U256); +pub struct Uint256(#[schemars(with = "String")] pub(crate) U256); forward_ref_partial_eq!(Uint256, Uint256); @@ -58,16 +58,13 @@ impl Uint256 { /// Creates a Uint256(0) #[inline] pub const fn zero() -> Self { - Uint256(U256::ZERO) + Self(U256::ZERO) } /// Creates a Uint256(1) #[inline] pub const fn one() -> Self { - Self::from_be_bytes([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, - ]) + Self(U256::ONE) } #[must_use] @@ -161,8 +158,7 @@ impl Uint256 { #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { - let res = self.0.pow(exp); - Self(res) + Self(self.0.pow(exp)) } /// Returns `self * numerator / denominator`. @@ -291,29 +287,25 @@ impl Uint256 { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_add(other.0); - Self(value) + Self(self.0.wrapping_add(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_sub(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_sub(other.0); - Self(value) + Self(self.0.wrapping_sub(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_mul(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_mul(other.0); - Self(value) + Self(self.0.wrapping_mul(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_pow(self, other: u32) -> Self { - let (value, _did_overflow) = self.0.overflowing_pow(other); - Self(value) + Self(self.0.wrapping_pow(other)) } #[must_use = "this returns the result of the operation, without modifying the original"] @@ -333,19 +325,12 @@ impl Uint256 { #[must_use = "this returns the result of the operation, without modifying the original"] pub fn saturating_pow(self, exp: u32) -> Self { - match self.checked_pow(exp) { - Ok(value) => value, - Err(_) => Self::MAX, - } + Self(self.0.saturating_pow(exp)) } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn abs_diff(self, other: Self) -> Self { - if self < other { - other - self - } else { - self - other - } + Self(self.0.abs_diff(other.0)) } } diff --git a/packages/std/src/math/uint512.rs b/packages/std/src/math/uint512.rs index c44e214b50..b3f1f278cf 100644 --- a/packages/std/src/math/uint512.rs +++ b/packages/std/src/math/uint512.rs @@ -42,7 +42,7 @@ use bnum::types::U512; /// assert_eq!(a, b); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint512(#[schemars(with = "String")] U512); +pub struct Uint512(#[schemars(with = "String")] pub(crate) U512); forward_ref_partial_eq!(Uint512, Uint512); @@ -65,11 +65,7 @@ impl Uint512 { /// Creates a Uint512(1) #[inline] pub const fn one() -> Self { - Self::from_be_bytes([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - ]) + Self(U512::ONE) } #[must_use] @@ -190,8 +186,7 @@ impl Uint512 { #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { - let res = self.0.pow(exp); - Self(res) + Self(self.0.pow(exp)) } pub fn checked_add(self, other: Self) -> Result { @@ -241,11 +236,10 @@ impl Uint512 { } pub fn checked_shr(self, other: u32) -> Result { - if other >= 512 { - return Err(OverflowError::new(OverflowOperation::Shr, self, other)); - } - - Ok(Self(self.0.shr(other))) + self.0 + .checked_shr(other) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Shr, self, other)) } pub fn checked_shl(self, other: u32) -> Result { @@ -259,29 +253,25 @@ impl Uint512 { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_add(other.0); - Self(value) + Self(self.0.wrapping_add(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_sub(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_sub(other.0); - Self(value) + Self(self.0.wrapping_sub(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_mul(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_mul(other.0); - Self(value) + Self(self.0.wrapping_mul(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_pow(self, other: u32) -> Self { - let (value, _did_overflow) = self.0.overflowing_pow(other); - Self(value) + Self(self.0.wrapping_pow(other)) } #[must_use = "this returns the result of the operation, without modifying the original"] @@ -301,19 +291,12 @@ impl Uint512 { #[must_use = "this returns the result of the operation, without modifying the original"] pub fn saturating_pow(self, exp: u32) -> Self { - match self.checked_pow(exp) { - Ok(value) => value, - Err(_) => Self::MAX, - } + Self(self.0.saturating_pow(exp)) } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn abs_diff(self, other: Self) -> Self { - if self < other { - other - self - } else { - self - other - } + Self(self.0.abs_diff(other.0)) } } From 77be691886a96dcd186f248805a25572da4bf5a2 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 9 Jun 2023 09:56:28 +0200 Subject: [PATCH 080/113] Cleanup int512 --- packages/std/src/math/int512.rs | 133 ++++---------------------------- 1 file changed, 14 insertions(+), 119 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index ddf1a89675..7533c8dcd8 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -8,17 +8,15 @@ use std::ops::{ }; use std::str::FromStr; -use crate::errors::{ - ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, StdError, -}; -use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint64}; +use crate::errors::{DivideByZeroError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint512, Uint64}; /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. use bnum::types::{I512, U512}; -/// An implementation of 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, +/// An implementation of i512 that is using strings for JSON encoding/decoding, +/// such that the full i512 range can be used for clients that convert JSON numbers to floats, /// like JavaScript and jq. /// /// # Examples @@ -65,11 +63,7 @@ impl Int512 { /// Creates a Int512(1) #[inline] pub const fn one() -> Self { - Self::from_be_bytes([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - ]) + Self(I512::ONE) } #[must_use] @@ -134,21 +128,6 @@ impl Int512 { Self(I512::from_bits(U512::from_digits(words))) } - /// A conversion from `Uint256` that, unlike the one provided by the `From` trait, - /// can be used in a `const` context. - #[must_use] - pub const fn from_uint256(num: Uint256) -> Self { - let bytes = num.to_le_bytes(); - Self::from_le_bytes([ - bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], - bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], - bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23], - bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31], - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, - ]) - } - /// Returns a copy of the number as big endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_be_bytes(self) -> [u8; 64] { @@ -192,8 +171,7 @@ impl Int512 { #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { - let res = self.0.pow(exp); - Self(res) + Self(self.0.pow(exp)) } pub fn checked_add(self, other: Self) -> Result { @@ -253,22 +231,19 @@ impl Int512 { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_add(other.0); - Self(value) + Self(self.0.wrapping_add(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_sub(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_sub(other.0); - Self(value) + Self(self.0.wrapping_sub(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_mul(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_mul(other.0); - Self(value) + Self(self.0.wrapping_mul(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] @@ -295,19 +270,12 @@ impl Int512 { #[must_use = "this returns the result of the operation, without modifying the original"] pub fn saturating_pow(self, exp: u32) -> Self { - match self.checked_pow(exp) { - Ok(value) => value, - Err(_) => Self::MAX, - } + Self(self.0.saturating_pow(exp)) } #[must_use = "this returns the result of the operation, without modifying the original"] - pub fn abs_diff(self, other: Self) -> Self { - if self < other { - other - self - } else { - self - other - } + pub fn abs_diff(self, other: Self) -> Uint512 { + Uint512(self.0.abs_diff(other.0)) } } @@ -391,35 +359,6 @@ impl From for Int512 { } } -// impl TryFrom for Int256 { -// type Error = ConversionOverflowError; - -// fn try_from(value: Int512) -> Result { -// let bytes = value.to_be_bytes(); -// let (first_bytes, last_bytes) = bytes.split_at(32); - -// if first_bytes != [0u8; 32] { -// return Err(ConversionOverflowError::new( -// "Int512", -// "Uint256", -// value.to_string(), -// )); -// } - -// Ok(Self::from_be_bytes(last_bytes.try_into().unwrap())) -// } -// } - -// impl TryFrom for Int128 { -// type Error = ConversionOverflowError; - -// fn try_from(value: Int512) -> Result { -// Ok(Uint128::new(value.0.try_into().map_err(|_| { -// ConversionOverflowError::new("Int512", "Uint128", value.to_string()) -// })?)) -// } -// } - impl TryFrom<&str> for Int512 { type Error = StdError; @@ -434,7 +373,7 @@ impl FromStr for Int512 { fn from_str(s: &str) -> Result { match I512::from_str_radix(s, 10) { Ok(u) => Ok(Self(u)), - Err(e) => Err(StdError::generic_err(format!("Parsing u512: {}", e))), + Err(e) => Err(StdError::generic_err(format!("Parsing i512: {}", e))), } } } @@ -754,50 +693,6 @@ mod tests { assert!(result.is_err()); } - // #[test] - // fn int512_convert_to_uint128() { - // let source = Int512::from(42u128); - // let target = Uint128::try_from(source); - // assert_eq!(target, Ok(Uint128::new(42u128))); - - // let source = Int512::MAX; - // let target = Uint128::try_from(source); - // assert_eq!( - // target, - // Err(ConversionOverflowError::new( - // "Int512", - // "Uint128", - // Int512::MAX.to_string() - // )) - // ); - // } - - #[test] - fn int512_from_uint256() { - assert_eq!( - Int512::from_uint256(Uint256::from_str("123").unwrap()), - Int512::from_str("123").unwrap() - ); - - assert_eq!( - Int512::from_uint256(Uint256::from_str("9785746283745").unwrap()), - Int512::from_str("9785746283745").unwrap() - ); - - assert_eq!( - Int512::from_uint256( - Uint256::from_str( - "97857462837575757832978493758398593853985452378423874623874628736482736487236" - ) - .unwrap() - ), - Int512::from_str( - "97857462837575757832978493758398593853985452378423874623874628736482736487236" - ) - .unwrap() - ); - } - #[test] fn int512_implements_display() { let a = Int512::from(12345u32); @@ -1269,7 +1164,7 @@ mod tests { fn int512_abs_diff_works() { let a = Int512::from(42u32); let b = Int512::from(5u32); - let expected = Int512::from(37u32); + let expected = Uint512::from(37u32); assert_eq!(a.abs_diff(b), expected); assert_eq!(b.abs_diff(a), expected); } From 776d6295ce93c4f97b1319b461c955878725ce51 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 9 Jun 2023 11:00:51 +0200 Subject: [PATCH 081/113] Update rust in github workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d50ccbda5..3a83bedddb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: 1.60.0 + toolchain: 1.65.0 target: wasm32-unknown-unknown profile: minimal override: true From 92ef1c64cf13b3e947fae2c8f6016f207efc34d3 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 12 Jun 2023 12:59:34 +0200 Subject: [PATCH 082/113] Improve int512 tests --- packages/std/src/math/int512.rs | 89 ++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 7533c8dcd8..a7877f9f69 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -683,6 +683,21 @@ mod tests { let a = Int512::from(5u8); assert_eq!(a.0, I512::from(5u32)); + let a = Int512::from(-5i128); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i64); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i32); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i16); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i8); + assert_eq!(a.0, I512::from(-5i32)); + let result = Int512::try_from("34567"); assert_eq!( result.unwrap().0, @@ -699,6 +714,10 @@ mod tests { assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); assert_eq!(a.to_string(), "12345"); + let a = Int512::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + let a = Int512::zero(); assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); assert_eq!(a.to_string(), "0"); @@ -708,6 +727,9 @@ mod tests { fn int512_display_padding_works() { let a = Int512::from(123u64); assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int512::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); } #[test] @@ -825,6 +847,7 @@ mod tests { assert!(!Int512::from(1u32).is_zero()); assert!(!Int512::from(123u32).is_zero()); + assert!(!Int512::from(-123i32).is_zero()); } #[test] @@ -880,16 +903,16 @@ mod tests { #[test] #[allow(clippy::op_ref)] fn int512_math() { - let a = Int512::from(12345u32); + let a = Int512::from(-12345i32); let b = Int512::from(23456u32); // test + with owned and reference right hand side - assert_eq!(a + b, Int512::from(35801u32)); - assert_eq!(a + &b, Int512::from(35801u32)); + assert_eq!(a + b, Int512::from(11111u32)); + assert_eq!(a + &b, Int512::from(11111u32)); // test - with owned and reference right hand side - assert_eq!(b - a, Int512::from(11111u32)); - assert_eq!(b - &a, Int512::from(11111u32)); + assert_eq!(b - a, Int512::from(35801u32)); + assert_eq!(b - &a, Int512::from(35801u32)); // test += with owned and reference right hand side let mut c = Int512::from(300000u32); @@ -908,7 +931,7 @@ mod tests { assert_eq!(d, Int512::from(276544u32)); // test - with negative result - assert_eq!(a - b, Int512::from(-11111i32)); + assert_eq!(a - b, Int512::from(-35801i32)); } #[test] @@ -923,6 +946,7 @@ mod tests { assert_eq!(Int512::from(2u32) - Int512::from(1u32), Int512::from(1u32)); assert_eq!(Int512::from(2u32) - Int512::from(0u32), Int512::from(2u32)); assert_eq!(Int512::from(2u32) - Int512::from(2u32), Int512::from(0u32)); + assert_eq!(Int512::from(2u32) - Int512::from(3u32), Int512::from(-1i32)); // works for refs let a = Int512::from(10u32); @@ -1093,6 +1117,23 @@ mod tests { Int512::MAX.checked_rem(Int512::from(0u32)), Err(DivideByZeroError { .. }) )); + // checked_* with negative numbers + assert_eq!( + Int512::from(-12i32).checked_div(Int512::from(10i32)), + Ok(Int512::from(-1i32)), + ); + assert_eq!( + Int512::from(-2i32).checked_pow(3u32), + Ok(Int512::from(-8i32)), + ); + assert_eq!( + Int512::from(-6i32).checked_mul(Int512::from(-7i32)), + Ok(Int512::from(42i32)), + ); + assert_eq!( + Int512::from(-2i32).checked_add(Int512::from(3i32)), + Ok(Int512::from(1i32)), + ); // saturating_* assert_eq!(Int512::MAX.saturating_add(Int512::from(1u32)), Int512::MAX); @@ -1112,6 +1153,19 @@ mod tests { assert_eq!(a % Int512::from(3u32), Int512::from(1u32)); assert_eq!(a % Int512::from(4u32), Int512::from(2u32)); + assert_eq!( + Int512::from(-12i32) % Int512::from(10i32), + Int512::from(-2i32) + ); + assert_eq!( + Int512::from(12i32) % Int512::from(-10i32), + Int512::from(2i32) + ); + assert_eq!( + Int512::from(-12i32) % Int512::from(-10i32), + Int512::from(-2i32) + ); + // works for refs let a = Int512::from(10u32); let b = Int512::from(3u32); @@ -1128,25 +1182,6 @@ mod tests { let _ = Int512::from(10u32) % Int512::zero(); } - #[test] - #[allow(clippy::op_ref)] - fn int512_rem_works() { - assert_eq!( - Int512::from(12u32) % Int512::from(10u32), - Int512::from(2u32) - ); - assert_eq!(Int512::from(50u32) % Int512::from(5u32), Int512::zero()); - - // works for refs - let a = Int512::from(42u32); - let b = Int512::from(5u32); - let expected = Int512::from(2u32); - assert_eq!(a % b, expected); - assert_eq!(a % &b, expected); - assert_eq!(&a % b, expected); - assert_eq!(&a % &b, expected); - } - #[test] fn int512_rem_assign_works() { let mut a = Int512::from(30u32); @@ -1167,6 +1202,10 @@ mod tests { let expected = Uint512::from(37u32); assert_eq!(a.abs_diff(b), expected); assert_eq!(b.abs_diff(a), expected); + + let c = Int512::from(-5i32); + assert_eq!(b.abs_diff(c), Uint512::from(10u32)); + assert_eq!(c.abs_diff(b), Uint512::from(10u32)); } #[test] From 59f7f83e06def8154a6cd85f8a6c93dcc097cc58 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 12 Jun 2023 13:04:04 +0200 Subject: [PATCH 083/113] Add Shl and Neg impl for Int512 --- packages/std/src/math/int512.rs | 99 +++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index a7877f9f69..8726e3e4b2 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -3,8 +3,8 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use std::fmt; use std::ops::{ - Add, AddAssign, Div, DivAssign, Mul, MulAssign, Not, Rem, RemAssign, Shr, ShrAssign, Sub, - SubAssign, + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, }; use std::str::FromStr; @@ -228,6 +228,14 @@ impl Int512 { Ok(Self(self.0.shr(other))) } + pub fn checked_shl(self, other: u32) -> Result { + if other >= 512 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -249,8 +257,7 @@ impl Int512 { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_pow(self, other: u32) -> Self { - let (value, _did_overflow) = self.0.overflowing_pow(other); - Self(value) + Self(self.0.wrapping_pow(other)) } #[must_use = "this returns the result of the operation, without modifying the original"] @@ -387,10 +394,11 @@ impl From for String { impl fmt::Display for Int512 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // The inner type doesn't work as expected with padding, so we - // work around that. + // work around that. Remove this code when the upstream padding is fixed. let unpadded = self.0.to_string(); + let numeric = unpadded.strip_prefix('-').unwrap_or(&unpadded); - f.pad_integral(true, "", &unpadded) + f.pad_integral(self >= &Self::zero(), "", numeric) } } @@ -463,6 +471,14 @@ impl Not for Int512 { } } +impl Neg for Int512 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + impl RemAssign for Int512 { fn rem_assign(&mut self, rhs: Int512) { *self = *self % rhs; @@ -507,6 +523,27 @@ impl<'a> Shr<&'a u32> for Int512 { } } +impl Shl for Int512 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int512", + rhs, + ) + }) + } +} + +impl<'a> Shl<&'a u32> for Int512 { + type Output = Self; + + fn shl(self, rhs: &'a u32) -> Self::Output { + Shl::::shl(self, *rhs) + } +} + impl AddAssign for Int512 { fn add_assign(&mut self, rhs: Int512) { self.0 = self.0.checked_add(rhs.0).unwrap(); @@ -543,6 +580,18 @@ impl<'a> ShrAssign<&'a u32> for Int512 { } } +impl ShlAssign for Int512 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} + +impl<'a> ShlAssign<&'a u32> for Int512 { + fn shl_assign(&mut self, rhs: &'a u32) { + *self = Shl::::shl(*self, *rhs); + } +} + impl Serialize for Int512 { /// Serializes as an integer string using base 10 fn serialize(&self, serializer: S) -> Result @@ -1195,6 +1244,44 @@ mod tests { assert_eq!(a, Int512::from(1u32)); } + #[test] + fn int512_shr() { + let x: Int512 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!( + x >> 1, + Int512::from(0x4000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x >> 4, + Int512::from(0x0800_0000_0000_0000_0000_0000_0000_0000u128) + ); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int512::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int512::one() + ); + } + + #[test] + fn int512_shl() { + let x: Int512 = 0x0800_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!( + x << 1, + Int512::from(0x1000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x << 4, + Int512::from(0x8000_0000_0000_0000_0000_0000_0000_0000u128) + ); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int512::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int512::MIN + ); + } + #[test] fn int512_abs_diff_works() { let a = Int512::from(42u32); From bcbb3e169ca8827c0b35977b93c454169ac0e035 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 13 Jun 2023 11:07:39 +0200 Subject: [PATCH 084/113] Int512::checked_div_euclid --- packages/std/src/errors/mod.rs | 4 ++-- packages/std/src/errors/std_error.rs | 11 ++++++++++ packages/std/src/math/int512.rs | 33 ++++++++++++++++++++++------ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/packages/std/src/errors/mod.rs b/packages/std/src/errors/mod.rs index 8c41d36540..54422007d3 100644 --- a/packages/std/src/errors/mod.rs +++ b/packages/std/src/errors/mod.rs @@ -6,8 +6,8 @@ mod verification_error; pub use recover_pubkey_error::RecoverPubkeyError; pub use std_error::{ CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError, - CoinFromStrError, CoinsError, ConversionOverflowError, DivideByZeroError, OverflowError, - OverflowOperation, RoundUpOverflowError, StdError, StdResult, + CoinFromStrError, CoinsError, ConversionOverflowError, DivideByZeroError, DivisionError, + OverflowError, OverflowOperation, RoundUpOverflowError, StdError, StdResult, }; pub use system_error::SystemError; pub use verification_error::VerificationError; diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index d30fec6a71..f06e91fa59 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -481,6 +481,8 @@ pub enum OverflowOperation { Add, Sub, Mul, + // TODO: Adding these is technically breaking + Div, Pow, Shr, Shl, @@ -556,6 +558,15 @@ impl DivideByZeroError { } } +#[derive(Error, Debug, PartialEq, Eq)] +pub enum DivisionError { + #[error("Divide by zero error: {0}")] + DivideByZero(#[from] DivideByZeroError), + + #[error("Overflow error: {0}")] + Overflow(#[from] OverflowError), +} + #[derive(Error, Debug, PartialEq, Eq)] pub enum CheckedMultiplyFractionError { #[error("{0}")] diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 8726e3e4b2..a9da55ff4f 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -8,7 +8,7 @@ use std::ops::{ }; use std::str::FromStr; -use crate::errors::{DivideByZeroError, OverflowError, OverflowOperation, StdError}; +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint512, Uint64}; /// Used internally - we don't want to leak this type since we might change @@ -202,15 +202,24 @@ impl Int512 { .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) } - pub fn checked_div(self, other: Self) -> Result { + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivideByZeroError::new(self).into()); + } self.0 .checked_div(other.0) .map(Self) - .ok_or_else(|| DivideByZeroError::new(self)) + .ok_or_else(|| OverflowError::new(OverflowOperation::Div, self, other).into()) } - pub fn checked_div_euclid(self, other: Self) -> Result { - self.checked_div(other) + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivideByZeroError::new(self).into()); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Div, self, other).into()) } pub fn checked_rem(self, other: Self) -> Result { @@ -1144,7 +1153,7 @@ mod tests { assert_eq!(Int512::from(2u32).checked_pow(3u32), Ok(Int512::from(8u32)),); assert!(matches!( Int512::MAX.checked_div(Int512::from(0u32)), - Err(DivideByZeroError { .. }) + Err(DivisionError::DivideByZero(_)) )); assert_eq!( Int512::from(6u32).checked_div(Int512::from(2u32)), @@ -1152,7 +1161,7 @@ mod tests { ); assert!(matches!( Int512::MAX.checked_div_euclid(Int512::from(0u32)), - Err(DivideByZeroError { .. }) + Err(DivisionError::DivideByZero(_)) )); assert_eq!( Int512::from(6u32).checked_div_euclid(Int512::from(2u32)), @@ -1183,6 +1192,10 @@ mod tests { Int512::from(-2i32).checked_add(Int512::from(3i32)), Ok(Int512::from(1i32)), ); + assert_eq!( + Int512::from(-1i32).checked_div_euclid(Int512::from(-2i32)), + Ok(Int512::from(1u32)), + ); // saturating_* assert_eq!(Int512::MAX.saturating_add(Int512::from(1u32)), Int512::MAX); @@ -1295,6 +1308,12 @@ mod tests { assert_eq!(c.abs_diff(b), Uint512::from(10u32)); } + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int512_neg_min_panics() { + _ = -Int512::MIN; + } + #[test] fn int512_partial_eq() { let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] From 98e877f95514ecfd8cfa547821d25791eb016d8c Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 13 Jun 2023 12:19:25 +0200 Subject: [PATCH 085/113] Add checked_neg to Int512 --- packages/std/src/errors/std_error.rs | 1 + packages/std/src/math/int512.rs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index f06e91fa59..f4121e6b9e 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -483,6 +483,7 @@ pub enum OverflowOperation { Mul, // TODO: Adding these is technically breaking Div, + Neg, Pow, Shr, Shl, diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index a9da55ff4f..cebf7b409b 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -245,6 +245,13 @@ impl Int512 { Ok(Self(self.0.shl(other))) } + pub fn checked_neg(self) -> Result { + self.0 + .checked_neg() + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) + } + #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -1102,6 +1109,16 @@ mod tests { let _ = Int512::from(1u32) >> 512u32; } + #[test] + fn int512_checked_neg() { + assert_eq!(Int512::one().checked_neg(), Ok(Int512::from(-1i32))); + assert!(matches!( + Int512::MIN.checked_neg(), + Err(OverflowError { .. }) + )); + assert_eq!(Int512::MAX.checked_neg(), Ok(Int512::MIN + Int512::one())); + } + #[test] fn sum_works() { let nums = vec![ From 630e299aada1951a3069957a9f611788982fefae Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 10:12:01 +0200 Subject: [PATCH 086/113] Remove uint dependency --- Cargo.lock | 25 ------------------------- contracts/burner/Cargo.lock | 25 ------------------------- contracts/crypto-verify/Cargo.lock | 25 ------------------------- contracts/cyberpunk/Cargo.lock | 25 ------------------------- contracts/floaty/Cargo.lock | 25 ------------------------- contracts/hackatom/Cargo.lock | 25 ------------------------- contracts/ibc-reflect-send/Cargo.lock | 25 ------------------------- contracts/ibc-reflect/Cargo.lock | 25 ------------------------- contracts/queue/Cargo.lock | 25 ------------------------- contracts/reflect/Cargo.lock | 25 ------------------------- contracts/staking/Cargo.lock | 25 ------------------------- contracts/virus/Cargo.lock | 25 ------------------------- packages/std/Cargo.toml | 1 - 13 files changed, 301 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc3a1c5bf9..c76a0f514a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -371,7 +371,6 @@ dependencies = [ "serde_json", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -572,12 +571,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1691,12 +1684,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.8.0" @@ -1824,18 +1811,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-width" version = "0.1.9" diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock index 129021b06e..8ebe8fa1a9 100644 --- a/contracts/burner/Cargo.lock +++ b/contracts/burner/Cargo.lock @@ -232,7 +232,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -388,12 +387,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1395,12 +1388,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1503,18 +1490,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock index a4e25b5b03..8193676a13 100644 --- a/contracts/crypto-verify/Cargo.lock +++ b/contracts/crypto-verify/Cargo.lock @@ -227,7 +227,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -391,12 +390,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1453,12 +1446,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1561,18 +1548,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock index e6ab3c05f3..60fb94530e 100644 --- a/contracts/cyberpunk/Cargo.lock +++ b/contracts/cyberpunk/Cargo.lock @@ -250,7 +250,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -414,12 +413,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1445,12 +1438,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1553,18 +1540,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock index e9932e6158..b7eefeda60 100644 --- a/contracts/floaty/Cargo.lock +++ b/contracts/floaty/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1405,12 +1398,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1513,18 +1500,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock index f7ac3df002..4a22c86347 100644 --- a/contracts/hackatom/Cargo.lock +++ b/contracts/hackatom/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1406,12 +1399,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1514,18 +1501,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock index b5688564bc..dfff7aa4e7 100644 --- a/contracts/ibc-reflect-send/Cargo.lock +++ b/contracts/ibc-reflect-send/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1404,12 +1397,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1512,18 +1499,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock index 1491d26548..f14335aa9a 100644 --- a/contracts/ibc-reflect/Cargo.lock +++ b/contracts/ibc-reflect/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1404,12 +1397,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1512,18 +1499,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock index f4d4dd6354..baecd4b897 100644 --- a/contracts/queue/Cargo.lock +++ b/contracts/queue/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -377,12 +376,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1395,12 +1388,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1503,18 +1490,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock index 8099b681ec..6539fc1a75 100644 --- a/contracts/reflect/Cargo.lock +++ b/contracts/reflect/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1405,12 +1398,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1513,18 +1500,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock index 0886b36c00..ab6d49866e 100644 --- a/contracts/staking/Cargo.lock +++ b/contracts/staking/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1432,12 +1425,6 @@ dependencies = [ "snafu", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1540,18 +1527,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock index d01769b280..ba54a8d9e3 100644 --- a/contracts/virus/Cargo.lock +++ b/contracts/virus/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -377,12 +376,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1384,12 +1377,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1492,18 +1479,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index fbec9eb810..0175ec7b3d 100644 --- a/packages/std/Cargo.toml +++ b/packages/std/Cargo.toml @@ -54,7 +54,6 @@ sha2 = "0.10.3" serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } serde-json-wasm = { version = "0.5.0" } thiserror = "1.0.26" -uint = "0.9.3" bnum = "=0.7.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] From d530dbcaedcc4001c45382fc4794719de0eb25ba Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 12:40:32 +0200 Subject: [PATCH 087/113] Use forward_ref macros for Int512 impls --- packages/std/src/math/int512.rs | 64 +++++---------------------------- 1 file changed, 8 insertions(+), 56 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index cebf7b409b..9027dd8cef 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -425,14 +425,7 @@ impl Add for Int512 { Int512(self.0.checked_add(rhs.0).unwrap()) } } - -impl<'a> Add<&'a Int512> for Int512 { - type Output = Self; - - fn add(self, rhs: &'a Int512) -> Self { - Int512(self.0.checked_add(rhs.0).unwrap()) - } -} +forward_ref_binop!(impl Add, add for Int512, Int512); impl Sub for Int512 { type Output = Self; @@ -457,14 +450,7 @@ impl Div for Int512 { Self(self.0.checked_div(rhs.0).unwrap()) } } - -impl<'a> Div<&'a Int512> for Int512 { - type Output = Self; - - fn div(self, rhs: &'a Int512) -> Self::Output { - Self(self.0.checked_div(rhs.0).unwrap()) - } -} +forward_ref_binop!(impl Div, div for Int512, Int512); impl Rem for Int512 { type Output = Self; @@ -530,14 +516,7 @@ impl Shr for Int512 { }) } } - -impl<'a> Shr<&'a u32> for Int512 { - type Output = Self; - - fn shr(self, rhs: &'a u32) -> Self::Output { - Shr::::shr(self, *rhs) - } -} +forward_ref_binop!(impl Shr, shr for Int512, u32); impl Shl for Int512 { type Output = Self; @@ -551,62 +530,35 @@ impl Shl for Int512 { }) } } - -impl<'a> Shl<&'a u32> for Int512 { - type Output = Self; - - fn shl(self, rhs: &'a u32) -> Self::Output { - Shl::::shl(self, *rhs) - } -} +forward_ref_binop!(impl Shl, shl for Int512, u32); impl AddAssign for Int512 { fn add_assign(&mut self, rhs: Int512) { self.0 = self.0.checked_add(rhs.0).unwrap(); } } - -impl<'a> AddAssign<&'a Int512> for Int512 { - fn add_assign(&mut self, rhs: &'a Int512) { - self.0 = self.0.checked_add(rhs.0).unwrap(); - } -} +forward_ref_op_assign!(impl AddAssign, add_assign for Int512, Int512); impl DivAssign for Int512 { fn div_assign(&mut self, rhs: Self) { self.0 = self.0.checked_div(rhs.0).unwrap(); } } - -impl<'a> DivAssign<&'a Int512> for Int512 { - fn div_assign(&mut self, rhs: &'a Int512) { - self.0 = self.0.checked_div(rhs.0).unwrap(); - } -} +forward_ref_op_assign!(impl DivAssign, div_assign for Int512, Int512); impl ShrAssign for Int512 { fn shr_assign(&mut self, rhs: u32) { *self = Shr::::shr(*self, rhs); } } - -impl<'a> ShrAssign<&'a u32> for Int512 { - fn shr_assign(&mut self, rhs: &'a u32) { - *self = Shr::::shr(*self, *rhs); - } -} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int512, u32); impl ShlAssign for Int512 { fn shl_assign(&mut self, rhs: u32) { *self = Shl::::shl(*self, rhs); } } - -impl<'a> ShlAssign<&'a u32> for Int512 { - fn shl_assign(&mut self, rhs: &'a u32) { - *self = Shl::::shl(*self, *rhs); - } -} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int512, u32); impl Serialize for Int512 { /// Serializes as an integer string using base 10 From 5bcb9d2332f010a0d309e5003eac7122f3ebfc58 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 13:19:00 +0200 Subject: [PATCH 088/113] Improve DivisionError --- packages/std/src/errors/std_error.rs | 10 ++++------ packages/std/src/math/int512.rs | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index f4121e6b9e..a6837a98b6 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -481,8 +481,6 @@ pub enum OverflowOperation { Add, Sub, Mul, - // TODO: Adding these is technically breaking - Div, Neg, Pow, Shr, @@ -561,11 +559,11 @@ impl DivideByZeroError { #[derive(Error, Debug, PartialEq, Eq)] pub enum DivisionError { - #[error("Divide by zero error: {0}")] - DivideByZero(#[from] DivideByZeroError), + #[error("Divide by zero")] + DivideByZero, - #[error("Overflow error: {0}")] - Overflow(#[from] OverflowError), + #[error("Overflow in division")] + Overflow, } #[derive(Error, Debug, PartialEq, Eq)] diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 9027dd8cef..d6d427afab 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -204,22 +204,22 @@ impl Int512 { pub fn checked_div(self, other: Self) -> Result { if other.is_zero() { - return Err(DivideByZeroError::new(self).into()); + return Err(DivisionError::DivideByZero); } self.0 .checked_div(other.0) .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Div, self, other).into()) + .ok_or(DivisionError::Overflow) } pub fn checked_div_euclid(self, other: Self) -> Result { if other.is_zero() { - return Err(DivideByZeroError::new(self).into()); + return Err(DivisionError::DivideByZero); } self.0 .checked_div_euclid(other.0) .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Div, self, other).into()) + .ok_or(DivisionError::Overflow) } pub fn checked_rem(self, other: Self) -> Result { @@ -1120,18 +1120,18 @@ mod tests { Err(OverflowError { .. }) )); assert_eq!(Int512::from(2u32).checked_pow(3u32), Ok(Int512::from(8u32)),); - assert!(matches!( + assert_eq!( Int512::MAX.checked_div(Int512::from(0u32)), - Err(DivisionError::DivideByZero(_)) - )); + Err(DivisionError::DivideByZero) + ); assert_eq!( Int512::from(6u32).checked_div(Int512::from(2u32)), Ok(Int512::from(3u32)), ); - assert!(matches!( + assert_eq!( Int512::MAX.checked_div_euclid(Int512::from(0u32)), - Err(DivisionError::DivideByZero(_)) - )); + Err(DivisionError::DivideByZero) + ); assert_eq!( Int512::from(6u32).checked_div_euclid(Int512::from(2u32)), Ok(Int512::from(3u32)), From 6b38adf08d083517342da992923eb32713fb7f6e Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 13:19:16 +0200 Subject: [PATCH 089/113] Improve error string --- packages/std/src/math/int512.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index d6d427afab..74ba0218c2 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -396,7 +396,7 @@ impl FromStr for Int512 { fn from_str(s: &str) -> Result { match I512::from_str_radix(s, 10) { Ok(u) => Ok(Self(u)), - Err(e) => Err(StdError::generic_err(format!("Parsing i512: {}", e))), + Err(e) => Err(StdError::generic_err(format!("Parsing Int512: {}", e))), } } } From 67fe1b438cc346483e6bf4b7448d8520ca4dde15 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 13:28:33 +0200 Subject: [PATCH 090/113] Inline Int512 constructor Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> --- packages/std/src/math/int512.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 74ba0218c2..954f355a51 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -50,6 +50,7 @@ impl Int512 { /// Creates a Int512(value) from a big endian representation. It's just an alias for /// `from_be_bytes`. + #[inline] pub const fn new(value: [u8; 64]) -> Self { Self::from_be_bytes(value) } From c65232f327f9fef810d90f236b9a7dcdffae6b01 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 13:26:02 +0200 Subject: [PATCH 091/113] Add non_exhaustive to OverflowOperation --- packages/std/src/errors/std_error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index a6837a98b6..5accfcc6be 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -477,6 +477,7 @@ impl From for StdError { pub type StdResult = core::result::Result; #[derive(Error, Debug, PartialEq, Eq)] +#[non_exhaustive] pub enum OverflowOperation { Add, Sub, From 7a379c5491250001729ac7f9f553d4592b88d7e7 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 16:15:51 +0200 Subject: [PATCH 092/113] Simplify tests --- packages/std/src/math/int512.rs | 91 +++++++++------------------------ 1 file changed, 23 insertions(+), 68 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 954f355a51..b4f5655e89 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -637,27 +637,16 @@ mod tests { #[test] fn int512_zero_works() { let zero = Int512::zero(); - assert_eq!( - zero.to_be_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - ] - ); + assert_eq!(zero.to_be_bytes(), [0; 64]); } #[test] fn uin512_one_works() { let one = Int512::one(); - assert_eq!( - one.to_be_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1 - ] - ); + let mut one_be = [0; 64]; + one_be[63] = 1; + + assert_eq!(one.to_be_bytes(), one_be); } #[test] @@ -752,32 +741,15 @@ mod tests { #[test] fn int512_to_be_bytes_works() { - assert_eq!( - Int512::zero().to_be_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - ] - ); - assert_eq!( - Int512::MAX.to_be_bytes(), - [ - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - ] - ); - assert_eq!( - Int512::from(1u128).to_be_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1 - ] - ); + assert_eq!(Int512::zero().to_be_bytes(), [0; 64]); + + let mut max = [0xff; 64]; + max[0] = 0x7f; + assert_eq!(Int512::MAX.to_be_bytes(), max); + + let mut one = [0; 64]; + one[63] = 1; + assert_eq!(Int512::from(1u128).to_be_bytes(), one); // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(64, "big")]` assert_eq!( Int512::from(240282366920938463463374607431768124608u128).to_be_bytes(), @@ -806,32 +778,15 @@ mod tests { #[test] fn int512_to_le_bytes_works() { - assert_eq!( - Int512::zero().to_le_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - ] - ); - assert_eq!( - Int512::MAX.to_le_bytes(), - [ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f - ] - ); - assert_eq!( - Int512::from(1u128).to_le_bytes(), - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - ] - ); + assert_eq!(Int512::zero().to_le_bytes(), [0; 64]); + + let mut max = [0xff; 64]; + max[63] = 0x7f; + assert_eq!(Int512::MAX.to_le_bytes(), max); + + let mut one = [0; 64]; + one[0] = 1; + assert_eq!(Int512::from(1u128).to_le_bytes(), one); // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(64, "little")]` assert_eq!( Int512::from(240282366920938463463374607431768124608u128).to_le_bytes(), From ac2b0be321b34515fa901569514257f5761f6c67 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 16:33:41 +0200 Subject: [PATCH 093/113] Add Int256 --- packages/std/src/lib.rs | 4 +- packages/std/src/math/int256.rs | 1202 +++++++++++++++++++++++++++++++ packages/std/src/math/mod.rs | 2 + 3 files changed, 1206 insertions(+), 2 deletions(-) create mode 100644 packages/std/src/math/int256.rs diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index e05f1b4eb1..e6abac0efb 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -52,8 +52,8 @@ pub use crate::ibc::{ #[cfg(feature = "iterator")] pub use crate::iterator::{Order, Record}; pub use crate::math::{ - Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int512, Isqrt, - Uint128, Uint256, Uint512, Uint64, + Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int256, Int512, + Isqrt, Uint128, Uint256, Uint512, Uint64, }; pub use crate::metadata::{DenomMetadata, DenomUnit}; pub use crate::never::Never; diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs new file mode 100644 index 0000000000..211719d1e6 --- /dev/null +++ b/packages/std/src/math/int256.rs @@ -0,0 +1,1202 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint64}; + +/// Used internally - we don't want to leak this type since we might change +/// the implementation in the future. +use bnum::types::{I256, U256}; + +/// An implementation of i256 that is using strings for JSON encoding/decoding, +/// such that the full i256 range can be used for clients that convert JSON numbers to floats, +/// like JavaScript and jq. +/// +/// # Examples +/// +/// Use `from` to create instances out of primitive uint types or `new` to provide big +/// endian bytes: +/// +/// ``` +/// # use cosmwasm_std::Int256; +/// let a = Int256::from(258u128); +/// let b = Int256::new([ +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, +/// ]); +/// assert_eq!(a, b); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int256(#[schemars(with = "String")] I256); + +forward_ref_partial_eq!(Int256, Int256); + +impl Int256 { + pub const MAX: Int256 = Int256(I256::MAX); + pub const MIN: Int256 = Int256(I256::MIN); + + /// Creates a Int256(value) from a big endian representation. It's just an alias for + /// `from_be_bytes`. + #[inline] + pub const fn new(value: [u8; 32]) -> Self { + Self::from_be_bytes(value) + } + + /// Creates a Int256(0) + #[inline] + pub const fn zero() -> Self { + Int256(I256::ZERO) + } + + /// Creates a Int256(1) + #[inline] + pub const fn one() -> Self { + Self(I256::ONE) + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 32]) -> Self { + let words: [u64; 4] = [ + u64::from_le_bytes([ + data[31], data[30], data[29], data[28], data[27], data[26], data[25], data[24], + ]), + u64::from_le_bytes([ + data[23], data[22], data[21], data[20], data[19], data[18], data[17], data[16], + ]), + u64::from_le_bytes([ + data[15], data[14], data[13], data[12], data[11], data[10], data[9], data[8], + ]), + u64::from_le_bytes([ + data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], + ]), + ]; + Self(I256::from_bits(U256::from_digits(words))) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 32]) -> Self { + let words: [u64; 4] = [ + u64::from_le_bytes([ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]), + u64::from_le_bytes([ + data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], + ]), + u64::from_le_bytes([ + data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23], + ]), + u64::from_le_bytes([ + data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], + ]), + ]; + Self(I256::from_bits(U256::from_digits(words))) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 32] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 32] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + Self(self.0.pow(exp)) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 256 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 256 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + pub fn checked_neg(self) -> Result { + self.0 + .checked_neg() + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + Self(self.0.wrapping_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + Self(self.0.wrapping_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + Self(self.0.wrapping_pow(other)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + Self(self.0.saturating_pow(exp)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Uint256 { + Uint256(self.0.abs_diff(other.0)) + } +} + +impl From for Int256 { + fn from(val: Uint256) -> Self { + let bytes = [[0u8; 32], val.to_be_bytes()].concat(); + + Self::from_be_bytes(bytes.try_into().unwrap()) + } +} + +impl From for Int256 { + fn from(val: Uint128) -> Self { + val.u128().into() + } +} + +impl From for Int256 { + fn from(val: Uint64) -> Self { + val.u64().into() + } +} + +impl From for Int256 { + fn from(val: u128) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u64) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u32) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u16) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u8) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i128) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i64) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i32) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i16) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i8) -> Self { + Int256(val.into()) + } +} + +impl TryFrom<&str> for Int256 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int256 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match I256::from_str_radix(s, 10) { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing Int256: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int256) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int256 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // The inner type doesn't work as expected with padding, so we + // work around that. Remove this code when the upstream padding is fixed. + let unpadded = self.0.to_string(); + let numeric = unpadded.strip_prefix('-').unwrap_or(&unpadded); + + f.pad_integral(self >= &Self::zero(), "", numeric) + } +} + +impl Add for Int256 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int256(self.0.checked_add(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Add, add for Int256, Int256); + +impl Sub for Int256 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int256(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int256, Int256); + +impl SubAssign for Int256 { + fn sub_assign(&mut self, rhs: Int256) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int256, Int256); + +impl Div for Int256 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Div, div for Int256, Int256); + +impl Rem for Int256 { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero. + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for Int256, Int256); + +impl Not for Int256 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Neg for Int256 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl RemAssign for Int256 { + fn rem_assign(&mut self, rhs: Int256) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int256, Int256); + +impl Mul for Int256 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int256, Int256); + +impl MulAssign for Int256 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int256, Int256); + +impl Shr for Int256 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int256", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shr, shr for Int256, u32); + +impl Shl for Int256 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int256", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shl, shl for Int256, u32); + +impl AddAssign for Int256 { + fn add_assign(&mut self, rhs: Int256) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for Int256, Int256); + +impl DivAssign for Int256 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Int256, Int256); + +impl ShrAssign for Int256 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int256, u32); + +impl ShlAssign for Int256 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int256, u32); + +impl Serialize for Int256 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int256 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int256Visitor) + } +} + +struct Int256Visitor; + +impl<'de> de::Visitor<'de> for Int256Visitor { + type Value = Int256; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int256::try_from(v).map_err(|e| E::custom(format!("invalid Int256 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int256 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 32); + } + + #[test] + fn int256_new_works() { + let num = Int256::new([1; 32]); + let a: [u8; 32] = num.to_be_bytes(); + assert_eq!(a, [1; 32]); + + let be_bytes = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Int256::new(be_bytes); + let resulting_bytes: [u8; 32] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int256_zero_works() { + let zero = Int256::zero(); + assert_eq!(zero.to_be_bytes(), [0; 32]); + } + + #[test] + fn uin256_one_works() { + let one = Int256::one(); + let mut one_be = [0; 32]; + one_be[31] = 1; + + assert_eq!(one.to_be_bytes(), one_be); + } + + #[test] + fn int256_endianness() { + let be_bytes = [ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let le_bytes = [ + 3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]; + + // These should all be the same. + let num1 = Int256::new(be_bytes); + let num2 = Int256::from_be_bytes(be_bytes); + let num3 = Int256::from_le_bytes(le_bytes); + assert_eq!(num1, Int256::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + assert_eq!(num1, num3); + } + + #[test] + fn int256_convert_from() { + let a = Int256::from(5u128); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u64); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u32); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u16); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u8); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(-5i128); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i64); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i32); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i16); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i8); + assert_eq!(a.0, I256::from(-5i32)); + + let result = Int256::try_from("34567"); + assert_eq!( + result.unwrap().0, + I256::from_str_radix("34567", 10).unwrap() + ); + + let result = Int256::try_from("1.23"); + assert!(result.is_err()); + } + + #[test] + fn int256_implements_display() { + let a = Int256::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int256::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + + let a = Int256::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int256_display_padding_works() { + let a = Int256::from(123u64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int256::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + } + + #[test] + fn int256_to_be_bytes_works() { + assert_eq!(Int256::zero().to_be_bytes(), [0; 32]); + + let mut max = [0xff; 32]; + max[0] = 0x7f; + assert_eq!(Int256::MAX.to_be_bytes(), max); + + let mut one = [0; 32]; + one[31] = 1; + assert_eq!(Int256::from(1u128).to_be_bytes(), one); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(32, "big")]` + assert_eq!( + Int256::from(240282366920938463463374607431768124608u128).to_be_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 196, 179, 87, 165, 121, 59, + 133, 246, 117, 221, 191, 255, 254, 172, 192 + ] + ); + assert_eq!( + Int256::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240 + ]) + .to_be_bytes(), + [ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240 + ] + ); + } + + #[test] + fn int256_to_le_bytes_works() { + assert_eq!(Int256::zero().to_le_bytes(), [0; 32]); + + let mut max = [0xff; 32]; + max[31] = 0x7f; + assert_eq!(Int256::MAX.to_le_bytes(), max); + + let mut one = [0; 32]; + one[0] = 1; + assert_eq!(Int256::from(1u128).to_le_bytes(), one); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(64, "little")]` + assert_eq!( + Int256::from(240282366920938463463374607431768124608u128).to_le_bytes(), + [ + 192, 172, 254, 255, 191, 221, 117, 246, 133, 59, 121, 165, 87, 179, 196, 180, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ] + ); + assert_eq!( + Int256::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240 + ]) + .to_le_bytes(), + [ + 240, 150, 115, 200, 240, 2, 233, 42, 7, 192, 201, 211, 54, 65, 76, 87, 78, 67, 21, + 33, 38, 0, 91, 58, 200, 123, 67, 87, 32, 23, 4, 17 + ] + ); + } + + #[test] + fn int256_is_zero_works() { + assert!(Int256::zero().is_zero()); + assert!(Int256(I256::from(0u32)).is_zero()); + + assert!(!Int256::from(1u32).is_zero()); + assert!(!Int256::from(123u32).is_zero()); + assert!(!Int256::from(-123i32).is_zero()); + } + + #[test] + fn int256_wrapping_methods() { + // wrapping_add + assert_eq!( + Int256::from(2u32).wrapping_add(Int256::from(2u32)), + Int256::from(4u32) + ); // non-wrapping + assert_eq!(Int256::MAX.wrapping_add(Int256::from(1u32)), Int256::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int256::from(7u32).wrapping_sub(Int256::from(5u32)), + Int256::from(2u32) + ); // non-wrapping + assert_eq!(Int256::MIN.wrapping_sub(Int256::from(1u32)), Int256::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int256::from(3u32).wrapping_mul(Int256::from(2u32)), + Int256::from(6u32) + ); // non-wrapping + assert_eq!( + Int256::MAX.wrapping_mul(Int256::from(2u32)), + Int256::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int256::from(2u32).wrapping_pow(3), Int256::from(8u32)); // non-wrapping + assert_eq!(Int256::MAX.wrapping_pow(2), Int256::from(1u32)); // wrapping + } + + #[test] + fn int256_json() { + let orig = Int256::from(1234567890987654321u128); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int256 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int256_compare() { + let a = Int256::from(12345u32); + let b = Int256::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int256::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_math() { + let a = Int256::from(-12345i32); + let b = Int256::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int256::from(11111u32)); + assert_eq!(a + &b, Int256::from(11111u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int256::from(35801u32)); + assert_eq!(b - &a, Int256::from(35801u32)); + + // test += with owned and reference right hand side + let mut c = Int256::from(300000u32); + c += b; + assert_eq!(c, Int256::from(323456u32)); + let mut d = Int256::from(300000u32); + d += &b; + assert_eq!(d, Int256::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int256::from(300000u32); + c -= b; + assert_eq!(c, Int256::from(276544u32)); + let mut d = Int256::from(300000u32); + d -= &b; + assert_eq!(d, Int256::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int256::from(-35801i32)); + } + + #[test] + #[should_panic] + fn int256_add_overflow_panics() { + let _ = Int256::MAX + Int256::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_sub_works() { + assert_eq!(Int256::from(2u32) - Int256::from(1u32), Int256::from(1u32)); + assert_eq!(Int256::from(2u32) - Int256::from(0u32), Int256::from(2u32)); + assert_eq!(Int256::from(2u32) - Int256::from(2u32), Int256::from(0u32)); + assert_eq!(Int256::from(2u32) - Int256::from(3u32), Int256::from(-1i32)); + + // works for refs + let a = Int256::from(10u32); + let b = Int256::from(3u32); + let expected = Int256::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int256_sub_overflow_panics() { + let _ = Int256::MIN + Int256::one() - Int256::from(2u32); + } + + #[test] + fn int256_sub_assign_works() { + let mut a = Int256::from(14u32); + a -= Int256::from(2u32); + assert_eq!(a, Int256::from(12u32)); + + // works for refs + let mut a = Int256::from(10u32); + let b = Int256::from(3u32); + let expected = Int256::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_mul_works() { + assert_eq!(Int256::from(2u32) * Int256::from(3u32), Int256::from(6u32)); + assert_eq!(Int256::from(2u32) * Int256::zero(), Int256::zero()); + + // works for refs + let a = Int256::from(11u32); + let b = Int256::from(3u32); + let expected = Int256::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int256_mul_assign_works() { + let mut a = Int256::from(14u32); + a *= Int256::from(2u32); + assert_eq!(a, Int256::from(28u32)); + + // works for refs + let mut a = Int256::from(10u32); + let b = Int256::from(3u32); + a *= &b; + assert_eq!(a, Int256::from(30u32)); + } + + #[test] + fn int256_pow_works() { + assert_eq!(Int256::from(2u32).pow(2), Int256::from(4u32)); + assert_eq!(Int256::from(2u32).pow(10), Int256::from(1024u32)); + } + + #[test] + #[should_panic] + fn int256_pow_overflow_panics() { + _ = Int256::MAX.pow(2u32); + } + + #[test] + fn int256_shr_works() { + let original = Int256::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, + ]); + + let shifted = Int256::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, + ]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int256_shr_overflow_panics() { + let _ = Int256::from(1u32) >> 256u32; + } + + #[test] + fn int256_checked_neg() { + assert_eq!(Int256::one().checked_neg(), Ok(Int256::from(-1i32))); + assert!(matches!( + Int256::MIN.checked_neg(), + Err(OverflowError { .. }) + )); + assert_eq!(Int256::MAX.checked_neg(), Ok(Int256::MIN + Int256::one())); + } + + #[test] + fn sum_works() { + let nums = vec![ + Int256::from(17u32), + Int256::from(123u32), + Int256::from(540u32), + Int256::from(82u32), + ]; + let expected = Int256::from(762u32); + + let sum_as_ref: Int256 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int256 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int256_methods() { + // checked_* + assert!(matches!( + Int256::MAX.checked_add(Int256::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int256::from(1u32).checked_add(Int256::from(1u32)), + Ok(Int256::from(2u32)), + ); + assert!(matches!( + Int256::MIN.checked_sub(Int256::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int256::from(2u32).checked_sub(Int256::from(1u32)), + Ok(Int256::from(1u32)), + ); + assert!(matches!( + Int256::MAX.checked_mul(Int256::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int256::from(2u32).checked_mul(Int256::from(2u32)), + Ok(Int256::from(4u32)), + ); + assert!(matches!( + Int256::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int256::from(2u32).checked_pow(3u32), Ok(Int256::from(8u32)),); + assert_eq!( + Int256::MAX.checked_div(Int256::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int256::from(6u32).checked_div(Int256::from(2u32)), + Ok(Int256::from(3u32)), + ); + assert_eq!( + Int256::MAX.checked_div_euclid(Int256::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int256::from(6u32).checked_div_euclid(Int256::from(2u32)), + Ok(Int256::from(3u32)), + ); + assert_eq!( + Int256::from(7u32).checked_div_euclid(Int256::from(2u32)), + Ok(Int256::from(3u32)), + ); + assert!(matches!( + Int256::MAX.checked_rem(Int256::from(0u32)), + Err(DivideByZeroError { .. }) + )); + // checked_* with negative numbers + assert_eq!( + Int256::from(-12i32).checked_div(Int256::from(10i32)), + Ok(Int256::from(-1i32)), + ); + assert_eq!( + Int256::from(-2i32).checked_pow(3u32), + Ok(Int256::from(-8i32)), + ); + assert_eq!( + Int256::from(-6i32).checked_mul(Int256::from(-7i32)), + Ok(Int256::from(42i32)), + ); + assert_eq!( + Int256::from(-2i32).checked_add(Int256::from(3i32)), + Ok(Int256::from(1i32)), + ); + assert_eq!( + Int256::from(-1i32).checked_div_euclid(Int256::from(-2i32)), + Ok(Int256::from(1u32)), + ); + + // saturating_* + assert_eq!(Int256::MAX.saturating_add(Int256::from(1u32)), Int256::MAX); + assert_eq!(Int256::MIN.saturating_sub(Int256::from(1u32)), Int256::MIN); + assert_eq!(Int256::MAX.saturating_mul(Int256::from(2u32)), Int256::MAX); + assert_eq!(Int256::from(4u32).saturating_pow(2u32), Int256::from(16u32)); + assert_eq!(Int256::MAX.saturating_pow(2u32), Int256::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_implements_rem() { + let a = Int256::from(10u32); + assert_eq!(a % Int256::from(10u32), Int256::zero()); + assert_eq!(a % Int256::from(2u32), Int256::zero()); + assert_eq!(a % Int256::from(1u32), Int256::zero()); + assert_eq!(a % Int256::from(3u32), Int256::from(1u32)); + assert_eq!(a % Int256::from(4u32), Int256::from(2u32)); + + assert_eq!( + Int256::from(-12i32) % Int256::from(10i32), + Int256::from(-2i32) + ); + assert_eq!( + Int256::from(12i32) % Int256::from(-10i32), + Int256::from(2i32) + ); + assert_eq!( + Int256::from(-12i32) % Int256::from(-10i32), + Int256::from(-2i32) + ); + + // works for refs + let a = Int256::from(10u32); + let b = Int256::from(3u32); + let expected = Int256::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int256_rem_panics_for_zero() { + let _ = Int256::from(10u32) % Int256::zero(); + } + + #[test] + fn int256_rem_assign_works() { + let mut a = Int256::from(30u32); + a %= Int256::from(4u32); + assert_eq!(a, Int256::from(2u32)); + + // works for refs + let mut a = Int256::from(25u32); + let b = Int256::from(6u32); + a %= &b; + assert_eq!(a, Int256::from(1u32)); + } + + #[test] + fn int256_shr() { + let x: Int256 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!( + x >> 1, + Int256::from(0x4000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x >> 4, + Int256::from(0x0800_0000_0000_0000_0000_0000_0000_0000u128) + ); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int256::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int256::one() + ); + } + + #[test] + fn int256_shl() { + let x: Int256 = 0x0800_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!( + x << 1, + Int256::from(0x1000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x << 4, + Int256::from(0x8000_0000_0000_0000_0000_0000_0000_0000u128) + ); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int256::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int256::MIN + ); + } + + #[test] + fn int256_abs_diff_works() { + let a = Int256::from(42u32); + let b = Int256::from(5u32); + let expected = Uint256::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let c = Int256::from(-5i32); + assert_eq!(b.abs_diff(c), Uint256::from(10u32)); + assert_eq!(c.abs_diff(b), Uint256::from(10u32)); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int256_neg_min_panics() { + _ = -Int256::MIN; + } + + #[test] + fn int256_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (u64, u64, bool)| { + (Int256::from(lhs), Int256::from(rhs), expected) + }); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } +} diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index ee514f36b8..d1cce93bf5 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -1,6 +1,7 @@ mod decimal; mod decimal256; mod fraction; +mod int256; mod int512; mod isqrt; mod uint128; @@ -11,6 +12,7 @@ mod uint64; pub use decimal::{Decimal, DecimalRangeExceeded}; pub use decimal256::{Decimal256, Decimal256RangeExceeded}; pub use fraction::Fraction; +pub use int256::Int256; pub use int512::Int512; pub use isqrt::Isqrt; pub use uint128::Uint128; From 49867d900ff083f46ded912816cee06636a0b53e Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 17:18:45 +0200 Subject: [PATCH 094/113] Fix typo --- packages/std/src/math/int256.rs | 2 +- packages/std/src/math/int512.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs index 211719d1e6..d8d666e1e8 100644 --- a/packages/std/src/math/int256.rs +++ b/packages/std/src/math/int256.rs @@ -603,7 +603,7 @@ mod tests { } #[test] - fn uin256_one_works() { + fn uint256_one_works() { let one = Int256::one(); let mut one_be = [0; 32]; one_be[31] = 1; diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index b4f5655e89..7fbb89411b 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -641,7 +641,7 @@ mod tests { } #[test] - fn uin512_one_works() { + fn uint512_one_works() { let one = Int512::one(); let mut one_be = [0; 64]; one_be[63] = 1; From b395d8e7cb960446677388921c9c99eed4b80607 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 17:18:54 +0200 Subject: [PATCH 095/113] Add Int128 --- packages/std/src/lib.rs | 2 +- packages/std/src/math/int128.rs | 1106 ++++++++++++++++++++++++++++++ packages/std/src/math/mod.rs | 2 + packages/std/src/math/uint128.rs | 2 +- 4 files changed, 1110 insertions(+), 2 deletions(-) create mode 100644 packages/std/src/math/int128.rs diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index e6abac0efb..a0a6a546d8 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -52,7 +52,7 @@ pub use crate::ibc::{ #[cfg(feature = "iterator")] pub use crate::iterator::{Order, Record}; pub use crate::math::{ - Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int256, Int512, + Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int256, Int512, Int128, Isqrt, Uint128, Uint256, Uint512, Uint64, }; pub use crate::metadata::{DenomMetadata, DenomUnit}; diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs new file mode 100644 index 0000000000..c88c4b3a79 --- /dev/null +++ b/packages/std/src/math/int128.rs @@ -0,0 +1,1106 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, Uint128, Uint64}; + +/// An implementation of i128 that is using strings for JSON encoding/decoding, +/// such that the full i128 range can be used for clients that convert JSON numbers to floats, +/// like JavaScript and jq. +/// +/// # Examples +/// +/// Use `from` to create instances out of primitive uint types or `new` to provide big +/// endian bytes: +/// +/// ``` +/// # use cosmwasm_std::Int128; +/// let a = Int128::from(258i128); +/// let b = Int128::new([ +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, +/// ]); +/// assert_eq!(a, b); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int128(#[schemars(with = "String")] i128); + +forward_ref_partial_eq!(Int128, Int128); + +impl Int128 { + pub const MAX: Int128 = Int128(i128::MAX); + pub const MIN: Int128 = Int128(i128::MIN); + + /// Creates a Int128(value) from a big endian representation. It's just an alias for + /// `from_be_bytes`. + #[inline] + pub const fn new(value: [u8; 16]) -> Self { + Self::from_be_bytes(value) + } + + /// Creates a Int128(0) + #[inline] + pub const fn zero() -> Self { + Int128(0) + } + + /// Creates a Int128(1) + #[inline] + pub const fn one() -> Self { + Self(1) + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 16]) -> Self { + Self(i128::from_be_bytes(data)) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 16]) -> Self { + Self(i128::from_le_bytes(data)) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 16] { + self.0.to_be_bytes() + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 16] { + self.0.to_le_bytes() + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0 == 0 + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + Self(self.0.pow(exp)) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 128 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 128 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + pub fn checked_neg(self) -> Result { + self.0 + .checked_neg() + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + Self(self.0.wrapping_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + Self(self.0.wrapping_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + Self(self.0.wrapping_pow(other)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + Self(self.0.saturating_pow(exp)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Uint128 { + Uint128(self.0.abs_diff(other.0)) + } +} + +impl From for Int128 { + fn from(val: Uint64) -> Self { + val.u64().into() + } +} + +impl From for Int128 { + fn from(val: u64) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: u32) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: u16) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: u8) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: i128) -> Self { + Int128(val) + } +} + +impl From for Int128 { + fn from(val: i64) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: i32) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: i16) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: i8) -> Self { + Int128(val.into()) + } +} + +impl TryFrom<&str> for Int128 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int128 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match s.parse::() { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing Int128: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int128) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int128 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // The inner type doesn't work as expected with padding, so we + // work around that. Remove this code when the upstream padding is fixed. + let unpadded = self.0.to_string(); + let numeric = unpadded.strip_prefix('-').unwrap_or(&unpadded); + + f.pad_integral(self >= &Self::zero(), "", numeric) + } +} + +impl Add for Int128 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int128(self.0.checked_add(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Add, add for Int128, Int128); + +impl Sub for Int128 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int128(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int128, Int128); + +impl SubAssign for Int128 { + fn sub_assign(&mut self, rhs: Int128) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int128, Int128); + +impl Div for Int128 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Div, div for Int128, Int128); + +impl Rem for Int128 { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero. + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for Int128, Int128); + +impl Not for Int128 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Neg for Int128 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl RemAssign for Int128 { + fn rem_assign(&mut self, rhs: Int128) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int128, Int128); + +impl Mul for Int128 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int128, Int128); + +impl MulAssign for Int128 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int128, Int128); + +impl Shr for Int128 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int128", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shr, shr for Int128, u32); + +impl Shl for Int128 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int128", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shl, shl for Int128, u32); + +impl AddAssign for Int128 { + fn add_assign(&mut self, rhs: Int128) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for Int128, Int128); + +impl DivAssign for Int128 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Int128, Int128); + +impl ShrAssign for Int128 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int128, u32); + +impl ShlAssign for Int128 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int128, u32); + +impl Serialize for Int128 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int128 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int128Visitor) + } +} + +struct Int128Visitor; + +impl<'de> de::Visitor<'de> for Int128Visitor { + type Value = Int128; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int128::try_from(v).map_err(|e| E::custom(format!("invalid Int128 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int128 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 16); + } + + #[test] + fn int128_new_works() { + let num = Int128::new([1; 16]); + let a: [u8; 16] = num.to_be_bytes(); + assert_eq!(a, [1; 16]); + + let be_bytes = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Int128::new(be_bytes); + let resulting_bytes: [u8; 16] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int128_zero_works() { + let zero = Int128::zero(); + assert_eq!(zero.to_be_bytes(), [0; 16]); + } + + #[test] + fn uint128_one_works() { + let one = Int128::one(); + let mut one_be = [0; 16]; + one_be[15] = 1; + + assert_eq!(one.to_be_bytes(), one_be); + } + + #[test] + fn int128_endianness() { + let be_bytes = [ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let le_bytes = [ + 3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]; + + // These should all be the same. + let num1 = Int128::new(be_bytes); + let num2 = Int128::from_be_bytes(be_bytes); + let num3 = Int128::from_le_bytes(le_bytes); + assert_eq!(num1, Int128::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + assert_eq!(num1, num3); + } + + #[test] + fn int128_convert_from() { + let a = Int128::from(5i128); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u64); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u32); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u16); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u8); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(-5i128); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i64); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i32); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i16); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i8); + assert_eq!(a.0, i128::from(-5i32)); + + let result = Int128::try_from("34567"); + assert_eq!(result.unwrap().0, "34567".parse::().unwrap()); + + let result = Int128::try_from("1.23"); + assert!(result.is_err()); + } + + #[test] + fn int128_implements_display() { + let a = Int128::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int128::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + + let a = Int128::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int128_display_padding_works() { + let a = Int128::from(123u64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int128::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + } + + #[test] + fn int128_to_be_bytes_works() { + assert_eq!(Int128::zero().to_be_bytes(), [0; 16]); + + let mut max = [0xff; 16]; + max[0] = 0x7f; + assert_eq!(Int128::MAX.to_be_bytes(), max); + + let mut one = [0; 16]; + one[15] = 1; + assert_eq!(Int128::from(1i128).to_be_bytes(), one); + // Python: `[b for b in (70141183460469231731687303715884018880).to_bytes(16, "big")]` + assert_eq!( + Int128::from(70141183460469231731687303715884018880i128).to_be_bytes(), + [52, 196, 179, 87, 165, 121, 59, 133, 246, 117, 221, 191, 255, 254, 172, 192] + ); + assert_eq!( + Int128::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78]) + .to_be_bytes(), + [17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78] + ); + } + + #[test] + fn int128_to_le_bytes_works() { + assert_eq!(Int128::zero().to_le_bytes(), [0; 16]); + + let mut max = [0xff; 16]; + max[15] = 0x7f; + assert_eq!(Int128::MAX.to_le_bytes(), max); + + let mut one = [0; 16]; + one[0] = 1; + assert_eq!(Int128::from(1i128).to_le_bytes(), one); + // Python: `[b for b in (70141183460469231731687303715884018880).to_bytes(16, "little")]` + assert_eq!( + Int128::from(70141183460469231731687303715884018880i128).to_le_bytes(), + [192, 172, 254, 255, 191, 221, 117, 246, 133, 59, 121, 165, 87, 179, 196, 52] + ); + assert_eq!( + Int128::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78]) + .to_le_bytes(), + [78, 67, 21, 33, 38, 0, 91, 58, 200, 123, 67, 87, 32, 23, 4, 17] + ); + } + + #[test] + fn int128_is_zero_works() { + assert!(Int128::zero().is_zero()); + assert!(Int128(i128::from(0u32)).is_zero()); + + assert!(!Int128::from(1u32).is_zero()); + assert!(!Int128::from(123u32).is_zero()); + assert!(!Int128::from(-123i32).is_zero()); + } + + #[test] + fn int128_wrapping_methods() { + // wrapping_add + assert_eq!( + Int128::from(2u32).wrapping_add(Int128::from(2u32)), + Int128::from(4u32) + ); // non-wrapping + assert_eq!(Int128::MAX.wrapping_add(Int128::from(1u32)), Int128::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int128::from(7u32).wrapping_sub(Int128::from(5u32)), + Int128::from(2u32) + ); // non-wrapping + assert_eq!(Int128::MIN.wrapping_sub(Int128::from(1u32)), Int128::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int128::from(3u32).wrapping_mul(Int128::from(2u32)), + Int128::from(6u32) + ); // non-wrapping + assert_eq!( + Int128::MAX.wrapping_mul(Int128::from(2u32)), + Int128::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int128::from(2u32).wrapping_pow(3), Int128::from(8u32)); // non-wrapping + assert_eq!(Int128::MAX.wrapping_pow(2), Int128::from(1u32)); // wrapping + } + + #[test] + fn int128_json() { + let orig = Int128::from(1234567890987654321i128); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int128 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int128_compare() { + let a = Int128::from(12345u32); + let b = Int128::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int128::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_math() { + let a = Int128::from(-12345i32); + let b = Int128::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int128::from(11111u32)); + assert_eq!(a + &b, Int128::from(11111u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int128::from(35801u32)); + assert_eq!(b - &a, Int128::from(35801u32)); + + // test += with owned and reference right hand side + let mut c = Int128::from(300000u32); + c += b; + assert_eq!(c, Int128::from(323456u32)); + let mut d = Int128::from(300000u32); + d += &b; + assert_eq!(d, Int128::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int128::from(300000u32); + c -= b; + assert_eq!(c, Int128::from(276544u32)); + let mut d = Int128::from(300000u32); + d -= &b; + assert_eq!(d, Int128::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int128::from(-35801i32)); + } + + #[test] + #[should_panic] + fn int128_add_overflow_panics() { + let _ = Int128::MAX + Int128::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_sub_works() { + assert_eq!(Int128::from(2u32) - Int128::from(1u32), Int128::from(1u32)); + assert_eq!(Int128::from(2u32) - Int128::from(0u32), Int128::from(2u32)); + assert_eq!(Int128::from(2u32) - Int128::from(2u32), Int128::from(0u32)); + assert_eq!(Int128::from(2u32) - Int128::from(3u32), Int128::from(-1i32)); + + // works for refs + let a = Int128::from(10u32); + let b = Int128::from(3u32); + let expected = Int128::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int128_sub_overflow_panics() { + let _ = Int128::MIN + Int128::one() - Int128::from(2u32); + } + + #[test] + fn int128_sub_assign_works() { + let mut a = Int128::from(14u32); + a -= Int128::from(2u32); + assert_eq!(a, Int128::from(12u32)); + + // works for refs + let mut a = Int128::from(10u32); + let b = Int128::from(3u32); + let expected = Int128::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_mul_works() { + assert_eq!(Int128::from(2u32) * Int128::from(3u32), Int128::from(6u32)); + assert_eq!(Int128::from(2u32) * Int128::zero(), Int128::zero()); + + // works for refs + let a = Int128::from(11u32); + let b = Int128::from(3u32); + let expected = Int128::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int128_mul_assign_works() { + let mut a = Int128::from(14u32); + a *= Int128::from(2u32); + assert_eq!(a, Int128::from(28u32)); + + // works for refs + let mut a = Int128::from(10u32); + let b = Int128::from(3u32); + a *= &b; + assert_eq!(a, Int128::from(30u32)); + } + + #[test] + fn int128_pow_works() { + assert_eq!(Int128::from(2u32).pow(2), Int128::from(4u32)); + assert_eq!(Int128::from(2u32).pow(10), Int128::from(1024u32)); + } + + #[test] + #[should_panic] + fn int128_pow_overflow_panics() { + _ = Int128::MAX.pow(2u32); + } + + #[test] + fn int128_shr_works() { + let original = Int128::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, + ]); + + let shifted = Int128::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, + ]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int128_shr_overflow_panics() { + let _ = Int128::from(1u32) >> 128u32; + } + + #[test] + fn int128_checked_neg() { + assert_eq!(Int128::one().checked_neg(), Ok(Int128::from(-1i32))); + assert!(matches!( + Int128::MIN.checked_neg(), + Err(OverflowError { .. }) + )); + assert_eq!(Int128::MAX.checked_neg(), Ok(Int128::MIN + Int128::one())); + } + + #[test] + fn sum_works() { + let nums = vec![ + Int128::from(17u32), + Int128::from(123u32), + Int128::from(540u32), + Int128::from(82u32), + ]; + let expected = Int128::from(762u32); + + let sum_as_ref: Int128 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int128 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int128_methods() { + // checked_* + assert!(matches!( + Int128::MAX.checked_add(Int128::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int128::from(1u32).checked_add(Int128::from(1u32)), + Ok(Int128::from(2u32)), + ); + assert!(matches!( + Int128::MIN.checked_sub(Int128::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int128::from(2u32).checked_sub(Int128::from(1u32)), + Ok(Int128::from(1u32)), + ); + assert!(matches!( + Int128::MAX.checked_mul(Int128::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int128::from(2u32).checked_mul(Int128::from(2u32)), + Ok(Int128::from(4u32)), + ); + assert!(matches!( + Int128::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int128::from(2u32).checked_pow(3u32), Ok(Int128::from(8u32)),); + assert_eq!( + Int128::MAX.checked_div(Int128::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int128::from(6u32).checked_div(Int128::from(2u32)), + Ok(Int128::from(3u32)), + ); + assert_eq!( + Int128::MAX.checked_div_euclid(Int128::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int128::from(6u32).checked_div_euclid(Int128::from(2u32)), + Ok(Int128::from(3u32)), + ); + assert_eq!( + Int128::from(7u32).checked_div_euclid(Int128::from(2u32)), + Ok(Int128::from(3u32)), + ); + assert!(matches!( + Int128::MAX.checked_rem(Int128::from(0u32)), + Err(DivideByZeroError { .. }) + )); + // checked_* with negative numbers + assert_eq!( + Int128::from(-12i32).checked_div(Int128::from(10i32)), + Ok(Int128::from(-1i32)), + ); + assert_eq!( + Int128::from(-2i32).checked_pow(3u32), + Ok(Int128::from(-8i32)), + ); + assert_eq!( + Int128::from(-6i32).checked_mul(Int128::from(-7i32)), + Ok(Int128::from(42i32)), + ); + assert_eq!( + Int128::from(-2i32).checked_add(Int128::from(3i32)), + Ok(Int128::from(1i32)), + ); + assert_eq!( + Int128::from(-1i32).checked_div_euclid(Int128::from(-2i32)), + Ok(Int128::from(1u32)), + ); + + // saturating_* + assert_eq!(Int128::MAX.saturating_add(Int128::from(1u32)), Int128::MAX); + assert_eq!(Int128::MIN.saturating_sub(Int128::from(1u32)), Int128::MIN); + assert_eq!(Int128::MAX.saturating_mul(Int128::from(2u32)), Int128::MAX); + assert_eq!(Int128::from(4u32).saturating_pow(2u32), Int128::from(16u32)); + assert_eq!(Int128::MAX.saturating_pow(2u32), Int128::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_implements_rem() { + let a = Int128::from(10u32); + assert_eq!(a % Int128::from(10u32), Int128::zero()); + assert_eq!(a % Int128::from(2u32), Int128::zero()); + assert_eq!(a % Int128::from(1u32), Int128::zero()); + assert_eq!(a % Int128::from(3u32), Int128::from(1u32)); + assert_eq!(a % Int128::from(4u32), Int128::from(2u32)); + + assert_eq!( + Int128::from(-12i32) % Int128::from(10i32), + Int128::from(-2i32) + ); + assert_eq!( + Int128::from(12i32) % Int128::from(-10i32), + Int128::from(2i32) + ); + assert_eq!( + Int128::from(-12i32) % Int128::from(-10i32), + Int128::from(-2i32) + ); + + // works for refs + let a = Int128::from(10u32); + let b = Int128::from(3u32); + let expected = Int128::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int128_rem_panics_for_zero() { + let _ = Int128::from(10u32) % Int128::zero(); + } + + #[test] + fn int128_rem_assign_works() { + let mut a = Int128::from(30u32); + a %= Int128::from(4u32); + assert_eq!(a, Int128::from(2u32)); + + // works for refs + let mut a = Int128::from(25u32); + let b = Int128::from(6u32); + a %= &b; + assert_eq!(a, Int128::from(1u32)); + } + + #[test] + fn int128_shr() { + let x: Int128 = 0x4000_0000_0000_0000_0000_0000_0000_0000i128.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!( + x >> 1, + Int128::from(0x2000_0000_0000_0000_0000_0000_0000_0000i128) + ); + assert_eq!( + x >> 4, + Int128::from(0x0400_0000_0000_0000_0000_0000_0000_0000i128) + ); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int128::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int128::one() + ); + } + + #[test] + fn int128_shl() { + let x: Int128 = 0x0800_0000_0000_0000_0000_0000_0000_0000i128.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!( + x << 1, + Int128::from(0x1000_0000_0000_0000_0000_0000_0000_0000i128) + ); + assert_eq!( + x << 4, + Int128::from(0x0800_0000_0000_0000_0000_0000_0000_0000i128 << 4) + ); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int128::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int128::MIN + ); + } + + #[test] + fn int128_abs_diff_works() { + let a = Int128::from(42u32); + let b = Int128::from(5u32); + let expected = Uint128::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let c = Int128::from(-5i32); + assert_eq!(b.abs_diff(c), Uint128::from(10u32)); + assert_eq!(c.abs_diff(b), Uint128::from(10u32)); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int128_neg_min_panics() { + _ = -Int128::MIN; + } + + #[test] + fn int128_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (u64, u64, bool)| { + (Int128::from(lhs), Int128::from(rhs), expected) + }); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } +} diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index d1cce93bf5..6122814f36 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -1,6 +1,7 @@ mod decimal; mod decimal256; mod fraction; +mod int128; mod int256; mod int512; mod isqrt; @@ -12,6 +13,7 @@ mod uint64; pub use decimal::{Decimal, DecimalRangeExceeded}; pub use decimal256::{Decimal256, Decimal256RangeExceeded}; pub use fraction::Fraction; +pub use int128::Int128; pub use int256::Int256; pub use int512::Int512; pub use isqrt::Isqrt; diff --git a/packages/std/src/math/uint128.rs b/packages/std/src/math/uint128.rs index 9c58c2cc3b..8dfd89e976 100644 --- a/packages/std/src/math/uint128.rs +++ b/packages/std/src/math/uint128.rs @@ -37,7 +37,7 @@ use crate::{ /// assert_eq!(c.u128(), 70); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint128(#[schemars(with = "String")] u128); +pub struct Uint128(#[schemars(with = "String")] pub(crate) u128); forward_ref_partial_eq!(Uint128, Uint128); From a5ae44ae04c1a2907cd737b0f11fecfc9be60139 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 17:38:10 +0200 Subject: [PATCH 096/113] Add Int64 --- packages/std/src/lib.rs | 4 +- packages/std/src/math/int64.rs | 1054 +++++++++++++++++++++++++++++++ packages/std/src/math/mod.rs | 2 + packages/std/src/math/uint64.rs | 2 +- 4 files changed, 1059 insertions(+), 3 deletions(-) create mode 100644 packages/std/src/math/int64.rs diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index a0a6a546d8..13eeee2874 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -52,8 +52,8 @@ pub use crate::ibc::{ #[cfg(feature = "iterator")] pub use crate::iterator::{Order, Record}; pub use crate::math::{ - Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int256, Int512, Int128, - Isqrt, Uint128, Uint256, Uint512, Uint64, + Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int128, Int256, + Int512, Int64, Isqrt, Uint128, Uint256, Uint512, Uint64, }; pub use crate::metadata::{DenomMetadata, DenomUnit}; pub use crate::never::Never; diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs new file mode 100644 index 0000000000..088c7d4565 --- /dev/null +++ b/packages/std/src/math/int64.rs @@ -0,0 +1,1054 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, Uint64}; + +/// An implementation of i64 that is using strings for JSON encoding/decoding, +/// such that the full i64 range can be used for clients that convert JSON numbers to floats, +/// like JavaScript and jq. +/// +/// # Examples +/// +/// Use `from` to create instances out of primitive uint types or `new` to provide big +/// endian bytes: +/// +/// ``` +/// # use cosmwasm_std::Int64; +/// let a = Int64::from(258i64); +/// let b = Int64::new([ +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, +/// ]); +/// assert_eq!(a, b); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int64(#[schemars(with = "String")] i64); + +forward_ref_partial_eq!(Int64, Int64); + +impl Int64 { + pub const MAX: Int64 = Int64(i64::MAX); + pub const MIN: Int64 = Int64(i64::MIN); + + /// Creates a Int64(value) from a big endian representation. It's just an alias for + /// `from_be_bytes`. + #[inline] + pub const fn new(value: [u8; 8]) -> Self { + Self::from_be_bytes(value) + } + + /// Creates a Int64(0) + #[inline] + pub const fn zero() -> Self { + Int64(0) + } + + /// Creates a Int64(1) + #[inline] + pub const fn one() -> Self { + Self(1) + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 8]) -> Self { + Self(i64::from_be_bytes(data)) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 8]) -> Self { + Self(i64::from_le_bytes(data)) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 8] { + self.0.to_be_bytes() + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 8] { + self.0.to_le_bytes() + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0 == 0 + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + Self(self.0.pow(exp)) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 64 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 64 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + pub fn checked_neg(self) -> Result { + self.0 + .checked_neg() + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + Self(self.0.wrapping_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + Self(self.0.wrapping_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + Self(self.0.wrapping_pow(other)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + Self(self.0.saturating_pow(exp)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Uint64 { + Uint64(self.0.abs_diff(other.0)) + } +} + +impl From for Int64 { + fn from(val: u32) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: u16) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: u8) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: i64) -> Self { + Int64(val) + } +} + +impl From for Int64 { + fn from(val: i32) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: i16) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: i8) -> Self { + Int64(val.into()) + } +} + +impl TryFrom<&str> for Int64 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int64 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match s.parse::() { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing Int64: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int64) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int64 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // The inner type doesn't work as expected with padding, so we + // work around that. Remove this code when the upstream padding is fixed. + let unpadded = self.0.to_string(); + let numeric = unpadded.strip_prefix('-').unwrap_or(&unpadded); + + f.pad_integral(self >= &Self::zero(), "", numeric) + } +} + +impl Add for Int64 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int64(self.0.checked_add(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Add, add for Int64, Int64); + +impl Sub for Int64 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int64(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int64, Int64); + +impl SubAssign for Int64 { + fn sub_assign(&mut self, rhs: Int64) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int64, Int64); + +impl Div for Int64 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Div, div for Int64, Int64); + +impl Rem for Int64 { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero. + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for Int64, Int64); + +impl Not for Int64 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Neg for Int64 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl RemAssign for Int64 { + fn rem_assign(&mut self, rhs: Int64) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int64, Int64); + +impl Mul for Int64 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int64, Int64); + +impl MulAssign for Int64 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int64, Int64); + +impl Shr for Int64 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int64", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shr, shr for Int64, u32); + +impl Shl for Int64 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int64", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shl, shl for Int64, u32); + +impl AddAssign for Int64 { + fn add_assign(&mut self, rhs: Int64) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for Int64, Int64); + +impl DivAssign for Int64 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Int64, Int64); + +impl ShrAssign for Int64 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int64, u32); + +impl ShlAssign for Int64 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int64, u32); + +impl Serialize for Int64 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int64 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int64Visitor) + } +} + +struct Int64Visitor; + +impl<'de> de::Visitor<'de> for Int64Visitor { + type Value = Int64; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int64::try_from(v).map_err(|e| E::custom(format!("invalid Int64 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int64 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 8); + } + + #[test] + fn int64_new_works() { + let num = Int64::new([1; 8]); + let a: [u8; 8] = num.to_be_bytes(); + assert_eq!(a, [1; 8]); + + let be_bytes = [0u8, 222u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; + let num = Int64::new(be_bytes); + let resulting_bytes: [u8; 8] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int64_zero_works() { + let zero = Int64::zero(); + assert_eq!(zero.to_be_bytes(), [0; 8]); + } + + #[test] + fn uint64_one_works() { + let one = Int64::one(); + let mut one_be = [0; 8]; + one_be[7] = 1; + + assert_eq!(one.to_be_bytes(), one_be); + } + + #[test] + fn int64_endianness() { + let be_bytes = [0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; + let le_bytes = [3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8]; + + // These should all be the same. + let num1 = Int64::new(be_bytes); + let num2 = Int64::from_be_bytes(be_bytes); + let num3 = Int64::from_le_bytes(le_bytes); + assert_eq!(num1, Int64::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + assert_eq!(num1, num3); + } + + #[test] + fn int64_convert_from() { + let a = Int64::from(5i64); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5i64); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5u32); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5u16); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5u8); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(-5i64); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i64); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i32); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i16); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i8); + assert_eq!(a.0, i64::from(-5i32)); + + let result = Int64::try_from("34567"); + assert_eq!(result.unwrap().0, "34567".parse::().unwrap()); + + let result = Int64::try_from("1.23"); + assert!(result.is_err()); + } + + #[test] + fn int64_implements_display() { + let a = Int64::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int64::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + + let a = Int64::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int64_display_padding_works() { + let a = Int64::from(123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int64::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + } + + #[test] + fn int64_to_be_bytes_works() { + assert_eq!(Int64::zero().to_be_bytes(), [0; 8]); + + let mut max = [0xff; 8]; + max[0] = 0x7f; + assert_eq!(Int64::MAX.to_be_bytes(), max); + + let mut one = [0; 8]; + one[7] = 1; + assert_eq!(Int64::from(1i64).to_be_bytes(), one); + // Python: `[b for b in (8535972485454015680).to_bytes(8, "big")]` + assert_eq!( + Int64::from(8535972485454015680i64).to_be_bytes(), + [118, 117, 221, 191, 255, 254, 172, 192] + ); + assert_eq!( + Int64::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200]).to_be_bytes(), + [17, 4, 23, 32, 87, 67, 123, 200] + ); + } + + #[test] + fn int64_to_le_bytes_works() { + assert_eq!(Int64::zero().to_le_bytes(), [0; 8]); + + let mut max = [0xff; 8]; + max[7] = 0x7f; + assert_eq!(Int64::MAX.to_le_bytes(), max); + + let mut one = [0; 8]; + one[0] = 1; + assert_eq!(Int64::from(1i64).to_le_bytes(), one); + // Python: `[b for b in (8535972485454015680).to_bytes(8, "little")]` + assert_eq!( + Int64::from(8535972485454015680i64).to_le_bytes(), + [192, 172, 254, 255, 191, 221, 117, 118] + ); + assert_eq!( + Int64::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200]).to_le_bytes(), + [200, 123, 67, 87, 32, 23, 4, 17] + ); + } + + #[test] + fn int64_is_zero_works() { + assert!(Int64::zero().is_zero()); + assert!(Int64(i64::from(0u32)).is_zero()); + + assert!(!Int64::from(1u32).is_zero()); + assert!(!Int64::from(123u32).is_zero()); + assert!(!Int64::from(-123i32).is_zero()); + } + + #[test] + fn int64_wrapping_methods() { + // wrapping_add + assert_eq!( + Int64::from(2u32).wrapping_add(Int64::from(2u32)), + Int64::from(4u32) + ); // non-wrapping + assert_eq!(Int64::MAX.wrapping_add(Int64::from(1u32)), Int64::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int64::from(7u32).wrapping_sub(Int64::from(5u32)), + Int64::from(2u32) + ); // non-wrapping + assert_eq!(Int64::MIN.wrapping_sub(Int64::from(1u32)), Int64::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int64::from(3u32).wrapping_mul(Int64::from(2u32)), + Int64::from(6u32) + ); // non-wrapping + assert_eq!( + Int64::MAX.wrapping_mul(Int64::from(2u32)), + Int64::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int64::from(2u32).wrapping_pow(3), Int64::from(8u32)); // non-wrapping + assert_eq!(Int64::MAX.wrapping_pow(2), Int64::from(1u32)); // wrapping + } + + #[test] + fn int64_json() { + let orig = Int64::from(1234567890987654321i64); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int64 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int64_compare() { + let a = Int64::from(12345u32); + let b = Int64::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int64::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_math() { + let a = Int64::from(-12345i32); + let b = Int64::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int64::from(11111u32)); + assert_eq!(a + &b, Int64::from(11111u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int64::from(35801u32)); + assert_eq!(b - &a, Int64::from(35801u32)); + + // test += with owned and reference right hand side + let mut c = Int64::from(300000u32); + c += b; + assert_eq!(c, Int64::from(323456u32)); + let mut d = Int64::from(300000u32); + d += &b; + assert_eq!(d, Int64::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int64::from(300000u32); + c -= b; + assert_eq!(c, Int64::from(276544u32)); + let mut d = Int64::from(300000u32); + d -= &b; + assert_eq!(d, Int64::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int64::from(-35801i32)); + } + + #[test] + #[should_panic] + fn int64_add_overflow_panics() { + let _ = Int64::MAX + Int64::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_sub_works() { + assert_eq!(Int64::from(2u32) - Int64::from(1u32), Int64::from(1u32)); + assert_eq!(Int64::from(2u32) - Int64::from(0u32), Int64::from(2u32)); + assert_eq!(Int64::from(2u32) - Int64::from(2u32), Int64::from(0u32)); + assert_eq!(Int64::from(2u32) - Int64::from(3u32), Int64::from(-1i32)); + + // works for refs + let a = Int64::from(10u32); + let b = Int64::from(3u32); + let expected = Int64::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int64_sub_overflow_panics() { + let _ = Int64::MIN + Int64::one() - Int64::from(2u32); + } + + #[test] + fn int64_sub_assign_works() { + let mut a = Int64::from(14u32); + a -= Int64::from(2u32); + assert_eq!(a, Int64::from(12u32)); + + // works for refs + let mut a = Int64::from(10u32); + let b = Int64::from(3u32); + let expected = Int64::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_mul_works() { + assert_eq!(Int64::from(2u32) * Int64::from(3u32), Int64::from(6u32)); + assert_eq!(Int64::from(2u32) * Int64::zero(), Int64::zero()); + + // works for refs + let a = Int64::from(11u32); + let b = Int64::from(3u32); + let expected = Int64::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int64_mul_assign_works() { + let mut a = Int64::from(14u32); + a *= Int64::from(2u32); + assert_eq!(a, Int64::from(28u32)); + + // works for refs + let mut a = Int64::from(10u32); + let b = Int64::from(3u32); + a *= &b; + assert_eq!(a, Int64::from(30u32)); + } + + #[test] + fn int64_pow_works() { + assert_eq!(Int64::from(2u32).pow(2), Int64::from(4u32)); + assert_eq!(Int64::from(2u32).pow(10), Int64::from(1024u32)); + } + + #[test] + #[should_panic] + fn int64_pow_overflow_panics() { + _ = Int64::MAX.pow(2u32); + } + + #[test] + fn int64_shr_works() { + let original = Int64::new([0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8]); + + let shifted = Int64::new([0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int64_shr_overflow_panics() { + let _ = Int64::from(1u32) >> 64u32; + } + + #[test] + fn int64_checked_neg() { + assert_eq!(Int64::one().checked_neg(), Ok(Int64::from(-1i32))); + assert!(matches!( + Int64::MIN.checked_neg(), + Err(OverflowError { .. }) + )); + assert_eq!(Int64::MAX.checked_neg(), Ok(Int64::MIN + Int64::one())); + } + + #[test] + fn sum_works() { + let nums = vec![ + Int64::from(17u32), + Int64::from(123u32), + Int64::from(540u32), + Int64::from(82u32), + ]; + let expected = Int64::from(762u32); + + let sum_as_ref: Int64 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int64 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int64_methods() { + // checked_* + assert!(matches!( + Int64::MAX.checked_add(Int64::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int64::from(1u32).checked_add(Int64::from(1u32)), + Ok(Int64::from(2u32)), + ); + assert!(matches!( + Int64::MIN.checked_sub(Int64::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int64::from(2u32).checked_sub(Int64::from(1u32)), + Ok(Int64::from(1u32)), + ); + assert!(matches!( + Int64::MAX.checked_mul(Int64::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int64::from(2u32).checked_mul(Int64::from(2u32)), + Ok(Int64::from(4u32)), + ); + assert!(matches!( + Int64::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int64::from(2u32).checked_pow(3u32), Ok(Int64::from(8u32)),); + assert_eq!( + Int64::MAX.checked_div(Int64::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int64::from(6u32).checked_div(Int64::from(2u32)), + Ok(Int64::from(3u32)), + ); + assert_eq!( + Int64::MAX.checked_div_euclid(Int64::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int64::from(6u32).checked_div_euclid(Int64::from(2u32)), + Ok(Int64::from(3u32)), + ); + assert_eq!( + Int64::from(7u32).checked_div_euclid(Int64::from(2u32)), + Ok(Int64::from(3u32)), + ); + assert!(matches!( + Int64::MAX.checked_rem(Int64::from(0u32)), + Err(DivideByZeroError { .. }) + )); + // checked_* with negative numbers + assert_eq!( + Int64::from(-12i32).checked_div(Int64::from(10i32)), + Ok(Int64::from(-1i32)), + ); + assert_eq!(Int64::from(-2i32).checked_pow(3u32), Ok(Int64::from(-8i32)),); + assert_eq!( + Int64::from(-6i32).checked_mul(Int64::from(-7i32)), + Ok(Int64::from(42i32)), + ); + assert_eq!( + Int64::from(-2i32).checked_add(Int64::from(3i32)), + Ok(Int64::from(1i32)), + ); + assert_eq!( + Int64::from(-1i32).checked_div_euclid(Int64::from(-2i32)), + Ok(Int64::from(1u32)), + ); + + // saturating_* + assert_eq!(Int64::MAX.saturating_add(Int64::from(1u32)), Int64::MAX); + assert_eq!(Int64::MIN.saturating_sub(Int64::from(1u32)), Int64::MIN); + assert_eq!(Int64::MAX.saturating_mul(Int64::from(2u32)), Int64::MAX); + assert_eq!(Int64::from(4u32).saturating_pow(2u32), Int64::from(16u32)); + assert_eq!(Int64::MAX.saturating_pow(2u32), Int64::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_implements_rem() { + let a = Int64::from(10u32); + assert_eq!(a % Int64::from(10u32), Int64::zero()); + assert_eq!(a % Int64::from(2u32), Int64::zero()); + assert_eq!(a % Int64::from(1u32), Int64::zero()); + assert_eq!(a % Int64::from(3u32), Int64::from(1u32)); + assert_eq!(a % Int64::from(4u32), Int64::from(2u32)); + + assert_eq!(Int64::from(-12i32) % Int64::from(10i32), Int64::from(-2i32)); + assert_eq!(Int64::from(12i32) % Int64::from(-10i32), Int64::from(2i32)); + assert_eq!( + Int64::from(-12i32) % Int64::from(-10i32), + Int64::from(-2i32) + ); + + // works for refs + let a = Int64::from(10u32); + let b = Int64::from(3u32); + let expected = Int64::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int64_rem_panics_for_zero() { + let _ = Int64::from(10u32) % Int64::zero(); + } + + #[test] + fn int64_rem_assign_works() { + let mut a = Int64::from(30u32); + a %= Int64::from(4u32); + assert_eq!(a, Int64::from(2u32)); + + // works for refs + let mut a = Int64::from(25u32); + let b = Int64::from(6u32); + a %= &b; + assert_eq!(a, Int64::from(1u32)); + } + + #[test] + fn int64_shr() { + let x: Int64 = 0x4000_0000_0000_0000i64.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!(x >> 1, Int64::from(0x2000_0000_0000_0000i64)); + assert_eq!(x >> 4, Int64::from(0x0400_0000_0000_0000i64)); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int64::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int64::one() + ); + } + + #[test] + fn int64_shl() { + let x: Int64 = 0x0800_0000_0000_0000i64.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!(x << 1, Int64::from(0x1000_0000_0000_0000i64)); + assert_eq!(x << 4, Int64::from(0x0800_0000_0000_0000i64 << 4)); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int64::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int64::MIN + ); + } + + #[test] + fn int64_abs_diff_works() { + let a = Int64::from(42u32); + let b = Int64::from(5u32); + let expected = Uint64::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let c = Int64::from(-5i32); + assert_eq!(b.abs_diff(c), Uint64::from(10u32)); + assert_eq!(c.abs_diff(b), Uint64::from(10u32)); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int64_neg_min_panics() { + _ = -Int64::MIN; + } + + #[test] + fn int64_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (i64, i64, bool)| { + (Int64::from(lhs), Int64::from(rhs), expected) + }); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } +} diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index 6122814f36..fde3d4789b 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -4,6 +4,7 @@ mod fraction; mod int128; mod int256; mod int512; +mod int64; mod isqrt; mod uint128; mod uint256; @@ -16,6 +17,7 @@ pub use fraction::Fraction; pub use int128::Int128; pub use int256::Int256; pub use int512::Int512; +pub use int64::Int64; pub use isqrt::Isqrt; pub use uint128::Uint128; pub use uint256::Uint256; diff --git a/packages/std/src/math/uint64.rs b/packages/std/src/math/uint64.rs index 9afd119647..c0a6603b5a 100644 --- a/packages/std/src/math/uint64.rs +++ b/packages/std/src/math/uint64.rs @@ -30,7 +30,7 @@ use crate::{forward_ref_partial_eq, impl_mul_fraction, Fraction, Uint128}; /// assert_eq!(b.u64(), 70); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint64(#[schemars(with = "String")] u64); +pub struct Uint64(#[schemars(with = "String")] pub(crate) u64); forward_ref_partial_eq!(Uint64, Uint64); From 8d8968479faf9653a911ff7138ddcc69afc2747f Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 17:41:37 +0200 Subject: [PATCH 097/113] Add trait impl check for signed ints --- packages/std/src/math/mod.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index fde3d4789b..782276af37 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -72,15 +72,31 @@ mod tests { { } + trait SignedImpl<'a>: IntImpl<'a> + Neg {} + impl AllImpl<'_> for Uint64 {} impl AllImpl<'_> for Uint128 {} impl AllImpl<'_> for Uint256 {} impl AllImpl<'_> for Uint512 {} + impl AllImpl<'_> for Int64 {} + impl AllImpl<'_> for Int128 {} + impl AllImpl<'_> for Int256 {} impl AllImpl<'_> for Int512 {} + + impl IntImpl<'_> for Int64 {} + impl IntImpl<'_> for Int128 {} + impl IntImpl<'_> for Int256 {} + impl IntImpl<'_> for Int512 {} impl IntImpl<'_> for Uint64 {} impl IntImpl<'_> for Uint128 {} impl IntImpl<'_> for Uint256 {} impl IntImpl<'_> for Uint512 {} + impl AllImpl<'_> for Decimal {} impl AllImpl<'_> for Decimal256 {} + + impl SignedImpl<'_> for Int64 {} + impl SignedImpl<'_> for Int128 {} + impl SignedImpl<'_> for Int256 {} + impl SignedImpl<'_> for Int512 {} } From 4d560c99b101adaebd49244da5725e26e0cf40dc Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 10:14:44 +0200 Subject: [PATCH 098/113] Fix left-shift error message --- packages/std/src/math/int128.rs | 2 +- packages/std/src/math/int256.rs | 2 +- packages/std/src/math/int512.rs | 2 +- packages/std/src/math/int64.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index c88c4b3a79..75073ce30a 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -154,7 +154,7 @@ impl Int128 { pub fn checked_shl(self, other: u32) -> Result { if other >= 128 { - return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); } Ok(Self(self.0.shl(other))) diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs index d8d666e1e8..0e0717bae6 100644 --- a/packages/std/src/math/int256.rs +++ b/packages/std/src/math/int256.rs @@ -204,7 +204,7 @@ impl Int256 { pub fn checked_shl(self, other: u32) -> Result { if other >= 256 { - return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); } Ok(Self(self.0.shl(other))) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 7fbb89411b..a29b9746d5 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -240,7 +240,7 @@ impl Int512 { pub fn checked_shl(self, other: u32) -> Result { if other >= 512 { - return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); } Ok(Self(self.0.shl(other))) diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 088c7d4565..0f9bb0982a 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -153,7 +153,7 @@ impl Int64 { pub fn checked_shl(self, other: u32) -> Result { if other >= 64 { - return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); } Ok(Self(self.0.shl(other))) From 87d685cb503790b0e1ae5ff301e103385d64952e Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 10:31:00 +0200 Subject: [PATCH 099/113] Avoid vec allocation --- packages/std/src/math/int512.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index a29b9746d5..cc63c39626 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -305,9 +305,10 @@ impl Int512 { impl From for Int512 { fn from(val: Uint256) -> Self { - let bytes = [[0u8; 32], val.to_be_bytes()].concat(); + let mut bytes = [0u8; 64]; + bytes[32..].copy_from_slice(&val.to_be_bytes()); - Self::from_be_bytes(bytes.try_into().unwrap()) + Self::from_be_bytes(bytes) } } From bdb28e29299255111d60066d45c0e676ccd8b453 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 10:45:59 +0200 Subject: [PATCH 100/113] Fix integer conversions --- packages/std/src/math/int128.rs | 13 ++++++++++++- packages/std/src/math/int256.rs | 22 +++++++++++++--------- packages/std/src/math/int64.rs | 5 +++++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index 75073ce30a..ab1da7cd35 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -9,7 +9,7 @@ use std::ops::{ use std::str::FromStr; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Uint128, Uint64}; +use crate::{forward_ref_partial_eq, Int64, Uint128, Uint64}; /// An implementation of i128 that is using strings for JSON encoding/decoding, /// such that the full i128 range can be used for clients that convert JSON numbers to floats, @@ -57,6 +57,11 @@ impl Int128 { Self(1) } + /// Returns a copy of the internal data + pub const fn i128(&self) -> i128 { + self.0 + } + #[must_use] pub const fn from_be_bytes(data: [u8; 16]) -> Self { Self(i128::from_be_bytes(data)) @@ -247,6 +252,12 @@ impl From for Int128 { } } +impl From for Int128 { + fn from(val: Int64) -> Self { + val.i64().into() + } +} + impl From for Int128 { fn from(val: i128) -> Self { Int128(val) diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs index 0e0717bae6..6bc061df39 100644 --- a/packages/std/src/math/int256.rs +++ b/packages/std/src/math/int256.rs @@ -9,7 +9,7 @@ use std::ops::{ use std::str::FromStr; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint64}; +use crate::{forward_ref_partial_eq, Int128, Int64, Uint128, Uint256, Uint64}; /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. @@ -267,14 +267,6 @@ impl Int256 { } } -impl From for Int256 { - fn from(val: Uint256) -> Self { - let bytes = [[0u8; 32], val.to_be_bytes()].concat(); - - Self::from_be_bytes(bytes.try_into().unwrap()) - } -} - impl From for Int256 { fn from(val: Uint128) -> Self { val.u128().into() @@ -317,6 +309,18 @@ impl From for Int256 { } } +impl From for Int256 { + fn from(val: Int128) -> Self { + val.i128().into() + } +} + +impl From for Int256 { + fn from(val: Int64) -> Self { + val.i64().into() + } +} + impl From for Int256 { fn from(val: i128) -> Self { Int256(val.into()) diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 0f9bb0982a..82c56ca853 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -56,6 +56,11 @@ impl Int64 { Self(1) } + /// Returns a copy of the internal data + pub const fn i64(&self) -> i64 { + self.0 + } + #[must_use] pub const fn from_be_bytes(data: [u8; 8]) -> Self { Self(i64::from_be_bytes(data)) From 8d5a243980265c539ae16659c31794c41c96fbc4 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 10:56:21 +0200 Subject: [PATCH 101/113] Fix Int64 and Int128 Display impls --- packages/std/src/math/int128.rs | 7 +------ packages/std/src/math/int64.rs | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index ab1da7cd35..18283de135 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -315,12 +315,7 @@ impl From for String { impl fmt::Display for Int128 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // The inner type doesn't work as expected with padding, so we - // work around that. Remove this code when the upstream padding is fixed. - let unpadded = self.0.to_string(); - let numeric = unpadded.strip_prefix('-').unwrap_or(&unpadded); - - f.pad_integral(self >= &Self::zero(), "", numeric) + self.0.fmt(f) } } diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 82c56ca853..0ea734998d 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -290,12 +290,7 @@ impl From for String { impl fmt::Display for Int64 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // The inner type doesn't work as expected with padding, so we - // work around that. Remove this code when the upstream padding is fixed. - let unpadded = self.0.to_string(); - let numeric = unpadded.strip_prefix('-').unwrap_or(&unpadded); - - f.pad_integral(self >= &Self::zero(), "", numeric) + self.0.fmt(f) } } From 4ecaea72d78f5ad1467d3379bd8923cd97767293 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 11:06:00 +0200 Subject: [PATCH 102/113] Improve Int64 and Int128 constructor --- packages/std/src/math/int128.rs | 32 +++++++++++++------------------- packages/std/src/math/int64.rs | 31 +++++++++++++------------------ 2 files changed, 26 insertions(+), 37 deletions(-) diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index 18283de135..37f14a9764 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -17,17 +17,12 @@ use crate::{forward_ref_partial_eq, Int64, Uint128, Uint64}; /// /// # Examples /// -/// Use `from` to create instances out of primitive uint types or `new` to provide big -/// endian bytes: +/// Use `from` to create instances of this and `i128` to get the value out: /// /// ``` /// # use cosmwasm_std::Int128; /// let a = Int128::from(258i128); -/// let b = Int128::new([ -/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, -/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, -/// ]); -/// assert_eq!(a, b); +/// assert_eq!(a.i128(), 258); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] pub struct Int128(#[schemars(with = "String")] i128); @@ -38,11 +33,12 @@ impl Int128 { pub const MAX: Int128 = Int128(i128::MAX); pub const MIN: Int128 = Int128(i128::MIN); - /// Creates a Int128(value) from a big endian representation. It's just an alias for - /// `from_be_bytes`. + /// Creates a Int128(value). + /// + /// This method is less flexible than `from` but can be called in a const context. #[inline] - pub const fn new(value: [u8; 16]) -> Self { - Self::from_be_bytes(value) + pub const fn new(value: i128) -> Self { + Self(value) } /// Creates a Int128(0) @@ -519,14 +515,14 @@ mod tests { #[test] fn int128_new_works() { - let num = Int128::new([1; 16]); + let num = Int128::from_be_bytes([1; 16]); let a: [u8; 16] = num.to_be_bytes(); assert_eq!(a, [1; 16]); let be_bytes = [ 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, ]; - let num = Int128::new(be_bytes); + let num = Int128::from_be_bytes(be_bytes); let resulting_bytes: [u8; 16] = num.to_be_bytes(); assert_eq!(be_bytes, resulting_bytes); } @@ -556,12 +552,10 @@ mod tests { ]; // These should all be the same. - let num1 = Int128::new(be_bytes); - let num2 = Int128::from_be_bytes(be_bytes); - let num3 = Int128::from_le_bytes(le_bytes); + let num1 = Int128::from_be_bytes(be_bytes); + let num2 = Int128::from_le_bytes(le_bytes); assert_eq!(num1, Int128::from(65536u32 + 512 + 3)); assert_eq!(num1, num2); - assert_eq!(num1, num3); } #[test] @@ -854,11 +848,11 @@ mod tests { #[test] fn int128_shr_works() { - let original = Int128::new([ + let original = Int128::from_be_bytes([ 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, ]); - let shifted = Int128::new([ + let shifted = Int128::from_be_bytes([ 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, ]); diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 0ea734998d..6f75d7c54e 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -17,16 +17,12 @@ use crate::{forward_ref_partial_eq, Uint64}; /// /// # Examples /// -/// Use `from` to create instances out of primitive uint types or `new` to provide big -/// endian bytes: +/// Use `from` to create instances of this and `i64` to get the value out: /// /// ``` /// # use cosmwasm_std::Int64; /// let a = Int64::from(258i64); -/// let b = Int64::new([ -/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, -/// ]); -/// assert_eq!(a, b); +/// assert_eq!(a.i64(), 258); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] pub struct Int64(#[schemars(with = "String")] i64); @@ -37,11 +33,12 @@ impl Int64 { pub const MAX: Int64 = Int64(i64::MAX); pub const MIN: Int64 = Int64(i64::MIN); - /// Creates a Int64(value) from a big endian representation. It's just an alias for - /// `from_be_bytes`. + /// Creates a Int64(value). + /// + /// This method is less flexible than `from` but can be called in a const context. #[inline] - pub const fn new(value: [u8; 8]) -> Self { - Self::from_be_bytes(value) + pub const fn new(value: i64) -> Self { + Self(value) } /// Creates a Int64(0) @@ -494,12 +491,12 @@ mod tests { #[test] fn int64_new_works() { - let num = Int64::new([1; 8]); + let num = Int64::from_be_bytes([1; 8]); let a: [u8; 8] = num.to_be_bytes(); assert_eq!(a, [1; 8]); let be_bytes = [0u8, 222u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; - let num = Int64::new(be_bytes); + let num = Int64::from_be_bytes(be_bytes); let resulting_bytes: [u8; 8] = num.to_be_bytes(); assert_eq!(be_bytes, resulting_bytes); } @@ -525,12 +522,10 @@ mod tests { let le_bytes = [3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8]; // These should all be the same. - let num1 = Int64::new(be_bytes); - let num2 = Int64::from_be_bytes(be_bytes); - let num3 = Int64::from_le_bytes(le_bytes); + let num1 = Int64::from_be_bytes(be_bytes); + let num2 = Int64::from_le_bytes(le_bytes); assert_eq!(num1, Int64::from(65536u32 + 512 + 3)); assert_eq!(num1, num2); - assert_eq!(num1, num3); } #[test] @@ -821,9 +816,9 @@ mod tests { #[test] fn int64_shr_works() { - let original = Int64::new([0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8]); + let original = Int64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8]); - let shifted = Int64::new([0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8]); + let shifted = Int64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8]); assert_eq!(original >> 2u32, shifted); } From 062be1ab4768a2f481ce08fb95d0e417cb8b7251 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 29 Jun 2023 15:05:18 +0200 Subject: [PATCH 103/113] Add missing stats call --- CHANGELOG.md | 4 ++++ packages/vm/src/cache.rs | 1 + 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aafbc2baf..66ff512359 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to ## [Unreleased] +### Fixed + +- cosmwasm-vm: Add missing cache stats increment when calling `pin`. + ### Added - cosmwasm-std: Implement `BankQuery::AllDenomMetadata` to allow querying all diff --git a/packages/vm/src/cache.rs b/packages/vm/src/cache.rs index 85c145e15d..4f00e7430a 100644 --- a/packages/vm/src/cache.rs +++ b/packages/vm/src/cache.rs @@ -272,6 +272,7 @@ where // Re-compile from original Wasm bytecode let code = self.load_wasm_with_path(&cache.wasm_path, checksum)?; + cache.stats.misses = cache.stats.misses.saturating_add(1); let module = compile(&code, Some(cache.instance_memory_limit), &[])?; // Store into the fs cache too cache.fs_cache.store(checksum, &module)?; From 053b5c9a91bdcfcbac7a2bab257e7d0b2cdb000e Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 29 Jun 2023 15:41:04 +0200 Subject: [PATCH 104/113] Test recompiling modules from Wasm --- packages/vm/src/cache.rs | 60 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/packages/vm/src/cache.rs b/packages/vm/src/cache.rs index 4f00e7430a..dbc809ef09 100644 --- a/packages/vm/src/cache.rs +++ b/packages/vm/src/cache.rs @@ -452,7 +452,7 @@ mod tests { use crate::errors::VmError; use crate::testing::{mock_backend, mock_env, mock_info, MockApi, MockQuerier, MockStorage}; use cosmwasm_std::{coins, Empty}; - use std::fs::{create_dir_all, OpenOptions}; + use std::fs::{create_dir_all, remove_dir_all, OpenOptions}; use std::io::Write; use tempfile::TempDir; @@ -776,6 +776,36 @@ mod tests { assert_eq!(cache.stats().misses, 0); } + #[test] + fn get_instance_recompiles_module() { + let options = make_testing_options(); + let cache = unsafe { Cache::new(options.clone()).unwrap() }; + let checksum = cache.save_wasm(CONTRACT).unwrap(); + + // Remove compiled module from disk + remove_dir_all(options.base_dir.join(CACHE_DIR).join(MODULES_DIR)).unwrap(); + + // The first get_instance recompiles the Wasm (miss) + let backend = mock_backend(&[]); + let _instance = cache + .get_instance(&checksum, backend, TESTING_OPTIONS) + .unwrap(); + assert_eq!(cache.stats().hits_pinned_memory_cache, 0); + assert_eq!(cache.stats().hits_memory_cache, 0); + assert_eq!(cache.stats().hits_fs_cache, 0); + assert_eq!(cache.stats().misses, 1); + + // The second get_instance finds the module in cache (hit) + let backend = mock_backend(&[]); + let _instance = cache + .get_instance(&checksum, backend, TESTING_OPTIONS) + .unwrap(); + assert_eq!(cache.stats().hits_pinned_memory_cache, 0); + assert_eq!(cache.stats().hits_memory_cache, 1); + assert_eq!(cache.stats().hits_fs_cache, 0); + assert_eq!(cache.stats().misses, 1); + } + #[test] fn call_instantiate_on_cached_contract() { let cache = unsafe { Cache::new(make_testing_options()).unwrap() }; @@ -1220,6 +1250,34 @@ mod tests { cache.unpin(&non_id).unwrap(); } + #[test] + fn pin_recompiles_module() { + let options = make_testing_options(); + let cache: Cache = + unsafe { Cache::new(options.clone()).unwrap() }; + let checksum = cache.save_wasm(CONTRACT).unwrap(); + + // Remove compiled module from disk + remove_dir_all(options.base_dir.join(CACHE_DIR).join(MODULES_DIR)).unwrap(); + + // Pin misses, forcing a re-compile of the module + cache.pin(&checksum).unwrap(); + assert_eq!(cache.stats().hits_pinned_memory_cache, 0); + assert_eq!(cache.stats().hits_memory_cache, 0); + assert_eq!(cache.stats().hits_fs_cache, 0); + assert_eq!(cache.stats().misses, 1); + + // After the compilation in pin, the module can be used from pinned memory cache + let backend = mock_backend(&[]); + let _ = cache + .get_instance(&checksum, backend, TESTING_OPTIONS) + .unwrap(); + assert_eq!(cache.stats().hits_pinned_memory_cache, 1); + assert_eq!(cache.stats().hits_memory_cache, 0); + assert_eq!(cache.stats().hits_fs_cache, 0); + assert_eq!(cache.stats().misses, 1); + } + #[test] fn loading_without_extension_works() { let tmp_dir = TempDir::new().unwrap(); From 3c1abf3c385c5d5905b851aa139971905ef4d615 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 16:26:42 +0200 Subject: [PATCH 105/113] Remove breaking checked_neg implementations --- packages/std/src/errors/std_error.rs | 2 -- packages/std/src/math/int128.rs | 17 ----------------- packages/std/src/math/int256.rs | 17 ----------------- packages/std/src/math/int512.rs | 17 ----------------- packages/std/src/math/int64.rs | 17 ----------------- packages/std/src/math/mod.rs | 7 ------- 6 files changed, 77 deletions(-) diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index 5accfcc6be..aecdb82cc6 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -477,12 +477,10 @@ impl From for StdError { pub type StdResult = core::result::Result; #[derive(Error, Debug, PartialEq, Eq)] -#[non_exhaustive] pub enum OverflowOperation { Add, Sub, Mul, - Neg, Pow, Shr, Shl, diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index 37f14a9764..f95c2e0b02 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -161,13 +161,6 @@ impl Int128 { Ok(Self(self.0.shl(other))) } - pub fn checked_neg(self) -> Result { - self.0 - .checked_neg() - .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) - } - #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -865,16 +858,6 @@ mod tests { let _ = Int128::from(1u32) >> 128u32; } - #[test] - fn int128_checked_neg() { - assert_eq!(Int128::one().checked_neg(), Ok(Int128::from(-1i32))); - assert!(matches!( - Int128::MIN.checked_neg(), - Err(OverflowError { .. }) - )); - assert_eq!(Int128::MAX.checked_neg(), Ok(Int128::MIN + Int128::one())); - } - #[test] fn sum_works() { let nums = vec![ diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs index 6bc061df39..937cf8df80 100644 --- a/packages/std/src/math/int256.rs +++ b/packages/std/src/math/int256.rs @@ -210,13 +210,6 @@ impl Int256 { Ok(Self(self.0.shl(other))) } - pub fn checked_neg(self) -> Result { - self.0 - .checked_neg() - .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) - } - #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -965,16 +958,6 @@ mod tests { let _ = Int256::from(1u32) >> 256u32; } - #[test] - fn int256_checked_neg() { - assert_eq!(Int256::one().checked_neg(), Ok(Int256::from(-1i32))); - assert!(matches!( - Int256::MIN.checked_neg(), - Err(OverflowError { .. }) - )); - assert_eq!(Int256::MAX.checked_neg(), Ok(Int256::MIN + Int256::one())); - } - #[test] fn sum_works() { let nums = vec![ diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index cc63c39626..980b81ee5c 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -246,13 +246,6 @@ impl Int512 { Ok(Self(self.0.shl(other))) } - pub fn checked_neg(self) -> Result { - self.0 - .checked_neg() - .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) - } - #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -1018,16 +1011,6 @@ mod tests { let _ = Int512::from(1u32) >> 512u32; } - #[test] - fn int512_checked_neg() { - assert_eq!(Int512::one().checked_neg(), Ok(Int512::from(-1i32))); - assert!(matches!( - Int512::MIN.checked_neg(), - Err(OverflowError { .. }) - )); - assert_eq!(Int512::MAX.checked_neg(), Ok(Int512::MIN + Int512::one())); - } - #[test] fn sum_works() { let nums = vec![ diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 6f75d7c54e..abd3f162a6 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -161,13 +161,6 @@ impl Int64 { Ok(Self(self.0.shl(other))) } - pub fn checked_neg(self) -> Result { - self.0 - .checked_neg() - .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) - } - #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -829,16 +822,6 @@ mod tests { let _ = Int64::from(1u32) >> 64u32; } - #[test] - fn int64_checked_neg() { - assert_eq!(Int64::one().checked_neg(), Ok(Int64::from(-1i32))); - assert!(matches!( - Int64::MIN.checked_neg(), - Err(OverflowError { .. }) - )); - assert_eq!(Int64::MAX.checked_neg(), Ok(Int64::MIN + Int64::one())); - } - #[test] fn sum_works() { let nums = vec![ diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index 782276af37..900b46f6ae 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -72,8 +72,6 @@ mod tests { { } - trait SignedImpl<'a>: IntImpl<'a> + Neg {} - impl AllImpl<'_> for Uint64 {} impl AllImpl<'_> for Uint128 {} impl AllImpl<'_> for Uint256 {} @@ -94,9 +92,4 @@ mod tests { impl AllImpl<'_> for Decimal {} impl AllImpl<'_> for Decimal256 {} - - impl SignedImpl<'_> for Int64 {} - impl SignedImpl<'_> for Int128 {} - impl SignedImpl<'_> for Int256 {} - impl SignedImpl<'_> for Int512 {} } From e2b7437eb2c51f39502e21072e65e494e311f527 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 16:45:38 +0200 Subject: [PATCH 106/113] Actually test constructors --- packages/std/src/math/int128.rs | 17 ++++++++++++++++- packages/std/src/math/int64.rs | 17 ++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index f95c2e0b02..e30aea7914 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -507,7 +507,7 @@ mod tests { } #[test] - fn int128_new_works() { + fn int128_from_be_bytes_works() { let num = Int128::from_be_bytes([1; 16]); let a: [u8; 16] = num.to_be_bytes(); assert_eq!(a, [1; 16]); @@ -520,6 +520,21 @@ mod tests { assert_eq!(be_bytes, resulting_bytes); } + #[test] + fn int128_new_works() { + let num = Int128::new(222); + assert_eq!(num.i128(), 222); + + let num = Int128::new(-222); + assert_eq!(num.i128(), -222); + + let num = Int128::new(i128::MAX); + assert_eq!(num.i128(), i128::MAX); + + let num = Int128::new(i128::MIN); + assert_eq!(num.i128(), i128::MIN); + } + #[test] fn int128_zero_works() { let zero = Int128::zero(); diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index abd3f162a6..8cfbc756c6 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -483,7 +483,7 @@ mod tests { } #[test] - fn int64_new_works() { + fn int64_from_be_bytes_works() { let num = Int64::from_be_bytes([1; 8]); let a: [u8; 8] = num.to_be_bytes(); assert_eq!(a, [1; 8]); @@ -494,6 +494,21 @@ mod tests { assert_eq!(be_bytes, resulting_bytes); } + #[test] + fn int64_new_works() { + let num = Int64::new(222); + assert_eq!(num.i64(), 222); + + let num = Int64::new(-222); + assert_eq!(num.i64(), -222); + + let num = Int64::new(i64::MAX); + assert_eq!(num.i64(), i64::MAX); + + let num = Int64::new(i64::MIN); + assert_eq!(num.i64(), i64::MIN); + } + #[test] fn int64_zero_works() { let zero = Int64::zero(); From e16f13d1c1627d3fd8fae673d8d89d220bca7b35 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 16:47:49 +0200 Subject: [PATCH 107/113] Don't fix bnum version Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> --- packages/std/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index 0175ec7b3d..d9c9c37ac3 100644 --- a/packages/std/Cargo.toml +++ b/packages/std/Cargo.toml @@ -54,7 +54,7 @@ sha2 = "0.10.3" serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } serde-json-wasm = { version = "0.5.0" } thiserror = "1.0.26" -bnum = "=0.7.0" +bnum = "0.7.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] cosmwasm-crypto = { path = "../crypto", version = "1.2.7" } From a173bb82f8210ac4ec0e169910a63bfe07fd33dd Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 16:51:45 +0200 Subject: [PATCH 108/113] Add signed int trait check back in --- packages/std/src/math/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index 900b46f6ae..782276af37 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -72,6 +72,8 @@ mod tests { { } + trait SignedImpl<'a>: IntImpl<'a> + Neg {} + impl AllImpl<'_> for Uint64 {} impl AllImpl<'_> for Uint128 {} impl AllImpl<'_> for Uint256 {} @@ -92,4 +94,9 @@ mod tests { impl AllImpl<'_> for Decimal {} impl AllImpl<'_> for Decimal256 {} + + impl SignedImpl<'_> for Int64 {} + impl SignedImpl<'_> for Int128 {} + impl SignedImpl<'_> for Int256 {} + impl SignedImpl<'_> for Int512 {} } From 6f01a8193e8d81915de97cd321245f31485d805a Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 30 Jun 2023 10:28:19 +0200 Subject: [PATCH 109/113] Add changelog entry --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aafbc2baf..41108579a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,12 +28,15 @@ and this project adheres to - cosmwasm-std: Add trait functions `Storage::range_keys` and `Storage::range_values`. The default implementations just use `Storage::range`. Later this can be implemented more efficiently. ([#1748]) +- cosmwasm-std: Add `Int64`, `Int128`, `Int256` and `Int512` signed integer + types. ([#1718]) [#1593]: https://github.com/CosmWasm/cosmwasm/pull/1593 [#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635 [#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647 [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 [#1687]: https://github.com/CosmWasm/cosmwasm/pull/1687 +[#1718]: https://github.com/CosmWasm/cosmwasm/pull/1718 [#1727]: https://github.com/CosmWasm/cosmwasm/issues/1727 [#1747]: https://github.com/CosmWasm/cosmwasm/pull/1747 [#1748]: https://github.com/CosmWasm/cosmwasm/pull/1748 From 7d5edd5aa02525e78570c6dccecac5e90f6e2091 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 3 Jul 2023 09:54:13 +0200 Subject: [PATCH 110/113] Set version: 1.3.0-rc.0 --- Cargo.lock | 16 ++++++++-------- contracts/burner/Cargo.lock | 12 ++++++------ contracts/crypto-verify/Cargo.lock | 14 +++++++------- contracts/cyberpunk/Cargo.lock | 14 +++++++------- contracts/floaty/Cargo.lock | 14 +++++++------- contracts/hackatom/Cargo.lock | 14 +++++++------- contracts/ibc-reflect-send/Cargo.lock | 14 +++++++------- contracts/ibc-reflect/Cargo.lock | 14 +++++++------- contracts/queue/Cargo.lock | 12 ++++++------ contracts/reflect/Cargo.lock | 14 +++++++------- contracts/staking/Cargo.lock | 14 +++++++------- contracts/virus/Cargo.lock | 12 ++++++------ packages/check/Cargo.toml | 6 +++--- packages/crypto/Cargo.toml | 2 +- packages/derive/Cargo.toml | 2 +- packages/schema-derive/Cargo.toml | 2 +- packages/schema/Cargo.toml | 6 +++--- packages/std/Cargo.toml | 6 +++--- packages/storage/Cargo.toml | 4 ++-- packages/vm/Cargo.toml | 6 +++--- 20 files changed, 99 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c76a0f514a..69cbf273a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,7 +291,7 @@ dependencies = [ [[package]] name = "cosmwasm-check" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "anyhow", "clap 2.34.0", @@ -302,7 +302,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "criterion", @@ -321,7 +321,7 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "syn", @@ -329,7 +329,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "anyhow", "cosmwasm-schema-derive", @@ -344,7 +344,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -353,7 +353,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -375,7 +375,7 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "serde", @@ -383,7 +383,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock index 8ebe8fa1a9..be7757298c 100644 --- a/contracts/burner/Cargo.lock +++ b/contracts/burner/Cargo.lock @@ -180,7 +180,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -191,14 +191,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -209,7 +209,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -218,7 +218,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -236,7 +236,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock index 8193676a13..e8a7642c26 100644 --- a/contracts/crypto-verify/Cargo.lock +++ b/contracts/crypto-verify/Cargo.lock @@ -175,7 +175,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -186,14 +186,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -204,7 +204,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -213,7 +213,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -231,7 +231,7 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "serde", @@ -239,7 +239,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock index 60fb94530e..7892644170 100644 --- a/contracts/cyberpunk/Cargo.lock +++ b/contracts/cyberpunk/Cargo.lock @@ -198,7 +198,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -209,14 +209,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -227,7 +227,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -236,7 +236,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -254,7 +254,7 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "serde", @@ -262,7 +262,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock index b7eefeda60..b58fbb095f 100644 --- a/contracts/floaty/Cargo.lock +++ b/contracts/floaty/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -180,14 +180,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -198,7 +198,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -207,7 +207,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "serde", @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock index 4a22c86347..af14c9838d 100644 --- a/contracts/hackatom/Cargo.lock +++ b/contracts/hackatom/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -180,14 +180,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -198,7 +198,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -207,7 +207,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "serde", @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock index dfff7aa4e7..74a3b933e2 100644 --- a/contracts/ibc-reflect-send/Cargo.lock +++ b/contracts/ibc-reflect-send/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -180,14 +180,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -198,7 +198,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -207,7 +207,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "serde", @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock index f14335aa9a..2d988662b7 100644 --- a/contracts/ibc-reflect/Cargo.lock +++ b/contracts/ibc-reflect/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -180,14 +180,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -198,7 +198,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -207,7 +207,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "serde", @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock index baecd4b897..ae7fe21b4f 100644 --- a/contracts/queue/Cargo.lock +++ b/contracts/queue/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -180,14 +180,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -198,7 +198,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -207,7 +207,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock index 6539fc1a75..194816e2eb 100644 --- a/contracts/reflect/Cargo.lock +++ b/contracts/reflect/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -180,14 +180,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -198,7 +198,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -207,7 +207,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "serde", @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock index ab6d49866e..14a3ce5011 100644 --- a/contracts/staking/Cargo.lock +++ b/contracts/staking/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -180,14 +180,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -198,7 +198,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -207,7 +207,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-std", "serde", @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock index ba54a8d9e3..c422907033 100644 --- a/contracts/virus/Cargo.lock +++ b/contracts/virus/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "digest 0.10.3", "ed25519-zebra", @@ -180,14 +180,14 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -198,7 +198,7 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "proc-macro2", "quote", @@ -207,7 +207,7 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "base64", "bnum", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" dependencies = [ "bitflags", "bytecheck", diff --git a/packages/check/Cargo.toml b/packages/check/Cargo.toml index 9cd05860a7..40f0a0c777 100644 --- a/packages/check/Cargo.toml +++ b/packages/check/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-check" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Mauro Lacy "] edition = "2021" description = "A CLI tool for verifying CosmWasm smart contracts" @@ -11,5 +11,5 @@ license = "Apache-2.0" anyhow = "1.0.57" clap = "2" colored = "2" -cosmwasm-vm = { path = "../vm", version = "1.2.7" } -cosmwasm-std = { path = "../std", version = "1.2.7" } +cosmwasm-vm = { path = "../vm", version = "1.3.0-rc.0" } +cosmwasm-std = { path = "../std", version = "1.3.0-rc.0" } diff --git a/packages/crypto/Cargo.toml b/packages/crypto/Cargo.toml index e5e16090a3..c1a3d9b102 100644 --- a/packages/crypto/Cargo.toml +++ b/packages/crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-crypto" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Mauro Lacy "] edition = "2021" description = "Crypto bindings for cosmwasm contracts" diff --git a/packages/derive/Cargo.toml b/packages/derive/Cargo.toml index c6ab3d563e..68212e10a3 100644 --- a/packages/derive/Cargo.toml +++ b/packages/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-derive" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Simon Warta "] edition = "2021" description = "A package for auto-generated code used for CosmWasm contract development. This is shipped as part of cosmwasm-std. Do not use directly." diff --git a/packages/schema-derive/Cargo.toml b/packages/schema-derive/Cargo.toml index 07b76b970d..ccf9e6ce09 100644 --- a/packages/schema-derive/Cargo.toml +++ b/packages/schema-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-schema-derive" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Tomasz Kurcz "] edition = "2021" description = "Derive macros for cosmwasm-schema" diff --git a/packages/schema/Cargo.toml b/packages/schema/Cargo.toml index 153beae691..7394ecb894 100644 --- a/packages/schema/Cargo.toml +++ b/packages/schema/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-schema" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Simon Warta ", "Ethan Frey "] edition = "2021" description = "A dev-dependency for CosmWasm contracts to generate JSON Schema files." @@ -8,7 +8,7 @@ repository = "https://github.com/CosmWasm/cosmwasm/tree/main/packages/schema" license = "Apache-2.0" [dependencies] -cosmwasm-schema-derive = { version = "=1.2.7", path = "../schema-derive" } +cosmwasm-schema-derive = { version = "=1.3.0-rc.0", path = "../schema-derive" } schemars = "0.8.3" serde = "1.0" serde_json = "1.0.40" @@ -16,6 +16,6 @@ thiserror = "1.0.26" [dev-dependencies] anyhow = "1.0.57" -cosmwasm-std = { version = "1.2.7", path = "../std" } +cosmwasm-std = { version = "1.3.0-rc.0", path = "../std" } semver = "1" tempfile = "3" diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index d9c9c37ac3..0c34b84a5c 100644 --- a/packages/std/Cargo.toml +++ b/packages/std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-std" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Ethan Frey "] edition = "2021" description = "Standard library for Wasm based smart contracts on Cosmos blockchains" @@ -45,7 +45,7 @@ cosmwasm_1_3 = ["cosmwasm_1_2"] [dependencies] base64 = "0.13.0" -cosmwasm-derive = { path = "../derive", version = "1.2.7" } +cosmwasm-derive = { path = "../derive", version = "1.3.0-rc.0" } derivative = "2" forward_ref = "1" hex = "0.4" @@ -57,7 +57,7 @@ thiserror = "1.0.26" bnum = "0.7.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -cosmwasm-crypto = { path = "../crypto", version = "1.2.7" } +cosmwasm-crypto = { path = "../crypto", version = "1.3.0-rc.0" } [dev-dependencies] cosmwasm-schema = { path = "../schema" } diff --git a/packages/storage/Cargo.toml b/packages/storage/Cargo.toml index b5dbbab45a..270906d7e9 100644 --- a/packages/storage/Cargo.toml +++ b/packages/storage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-storage" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Ethan Frey "] edition = "2021" description = "CosmWasm library with useful helpers for Storage patterns" @@ -16,5 +16,5 @@ iterator = ["cosmwasm-std/iterator"] [dependencies] # Uses the path when built locally; uses the given version from crates.io when published -cosmwasm-std = { path = "../std", version = "1.2.7", default-features = false } +cosmwasm-std = { path = "../std", version = "1.3.0-rc.0", default-features = false } serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } diff --git a/packages/vm/Cargo.toml b/packages/vm/Cargo.toml index 206716f8a7..4073bdddb1 100644 --- a/packages/vm/Cargo.toml +++ b/packages/vm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmwasm-vm" -version = "1.2.7" +version = "1.3.0-rc.0" authors = ["Ethan Frey "] edition = "2021" description = "VM bindings to run cosmwams contracts" @@ -42,8 +42,8 @@ required-features = ["iterator"] clru = "0.4.0" crc32fast = "1.3.2" # Uses the path when built locally; uses the given version from crates.io when published -cosmwasm-std = { path = "../std", version = "1.2.7", default-features = false } -cosmwasm-crypto = { path = "../crypto", version = "1.2.7" } +cosmwasm-std = { path = "../std", version = "1.3.0-rc.0", default-features = false } +cosmwasm-crypto = { path = "../crypto", version = "1.3.0-rc.0" } hex = "0.4" parity-wasm = { version = "0.45", features = ["sign_ext"] } schemars = "0.8.3" From f6301da70f38cb57b47f5a525f255f0127a4d32d Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 3 Jul 2023 11:54:03 +0200 Subject: [PATCH 111/113] Update changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ae197ae3b..90769d0a04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [1.3.0-rc.0] - 2023-07-03 ### Fixed @@ -1769,7 +1769,7 @@ Some main points: All future Changelog entries will reference this base -[unreleased]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.7...HEAD +[1.3.0-rc.0]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.7...v1.3.0-rc.0 [1.2.7]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.6...v1.2.7 [1.2.6]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.5...v1.2.6 [1.2.5]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.4...v1.2.5 From 1d58dc9547aa134833ba1d016f0c02ce2e90c663 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 3 Jul 2023 15:39:15 +0200 Subject: [PATCH 112/113] Fix clippy lints --- packages/std/src/errors/std_error.rs | 4 ++-- packages/std/src/math/int128.rs | 24 +++++++++--------------- packages/std/src/math/int256.rs | 24 +++++++++--------------- packages/std/src/math/int512.rs | 24 +++++++++--------------- packages/std/src/math/int64.rs | 24 +++++++++--------------- 5 files changed, 38 insertions(+), 62 deletions(-) diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index af18d282ea..8e2a486cdc 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -607,7 +607,7 @@ pub enum CoinsError { impl From for StdError { fn from(value: CoinsError) -> Self { - Self::generic_err(format!("Creating Coins: {}", value)) + Self::generic_err(format!("Creating Coins: {value}")) } } @@ -629,7 +629,7 @@ impl From for CoinFromStrError { impl From for StdError { fn from(value: CoinFromStrError) -> Self { - Self::generic_err(format!("Parsing Coin: {}", value)) + Self::generic_err(format!("Parsing Coin: {value}")) } } diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index e30aea7914..0a44bd2744 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -291,7 +291,7 @@ impl FromStr for Int128 { fn from_str(s: &str) -> Result { match s.parse::() { Ok(u) => Ok(Self(u)), - Err(e) => Err(StdError::generic_err(format!("Parsing Int128: {}", e))), + Err(e) => Err(StdError::generic_err(format!("Parsing Int128: {e}"))), } } } @@ -399,10 +399,7 @@ impl Shr for Int128 { fn shr(self, rhs: u32) -> Self::Output { self.checked_shr(rhs).unwrap_or_else(|_| { - panic!( - "right shift error: {} is larger or equal than the number of bits in Int128", - rhs, - ) + panic!("right shift error: {rhs} is larger or equal than the number of bits in Int128",) }) } } @@ -413,10 +410,7 @@ impl Shl for Int128 { fn shl(self, rhs: u32) -> Self::Output { self.checked_shl(rhs).unwrap_or_else(|_| { - panic!( - "left shift error: {} is larger or equal than the number of bits in Int128", - rhs, - ) + panic!("left shift error: {rhs} is larger or equal than the number of bits in Int128",) }) } } @@ -483,7 +477,7 @@ impl<'de> de::Visitor<'de> for Int128Visitor { where E: de::Error, { - Int128::try_from(v).map_err(|e| E::custom(format!("invalid Int128 '{}' - {}", v, e))) + Int128::try_from(v).map_err(|e| E::custom(format!("invalid Int128 '{v}' - {e}"))) } } @@ -608,25 +602,25 @@ mod tests { #[test] fn int128_implements_display() { let a = Int128::from(12345u32); - assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(format!("Embedded: {a}"), "Embedded: 12345"); assert_eq!(a.to_string(), "12345"); let a = Int128::from(-12345i32); - assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(format!("Embedded: {a}"), "Embedded: -12345"); assert_eq!(a.to_string(), "-12345"); let a = Int128::zero(); - assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(format!("Embedded: {a}"), "Embedded: 0"); assert_eq!(a.to_string(), "0"); } #[test] fn int128_display_padding_works() { let a = Int128::from(123u64); - assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + assert_eq!(format!("Embedded: {a:05}"), "Embedded: 00123"); let a = Int128::from(-123i64); - assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + assert_eq!(format!("Embedded: {a:05}"), "Embedded: -0123"); } #[test] diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs index 937cf8df80..74ace49ac0 100644 --- a/packages/std/src/math/int256.rs +++ b/packages/std/src/math/int256.rs @@ -358,7 +358,7 @@ impl FromStr for Int256 { fn from_str(s: &str) -> Result { match I256::from_str_radix(s, 10) { Ok(u) => Ok(Self(u)), - Err(e) => Err(StdError::generic_err(format!("Parsing Int256: {}", e))), + Err(e) => Err(StdError::generic_err(format!("Parsing Int256: {e}"))), } } } @@ -471,10 +471,7 @@ impl Shr for Int256 { fn shr(self, rhs: u32) -> Self::Output { self.checked_shr(rhs).unwrap_or_else(|_| { - panic!( - "right shift error: {} is larger or equal than the number of bits in Int256", - rhs, - ) + panic!("right shift error: {rhs} is larger or equal than the number of bits in Int256",) }) } } @@ -485,10 +482,7 @@ impl Shl for Int256 { fn shl(self, rhs: u32) -> Self::Output { self.checked_shl(rhs).unwrap_or_else(|_| { - panic!( - "left shift error: {} is larger or equal than the number of bits in Int256", - rhs, - ) + panic!("left shift error: {rhs} is larger or equal than the number of bits in Int256",) }) } } @@ -555,7 +549,7 @@ impl<'de> de::Visitor<'de> for Int256Visitor { where E: de::Error, { - Int256::try_from(v).map_err(|e| E::custom(format!("invalid Int256 '{}' - {}", v, e))) + Int256::try_from(v).map_err(|e| E::custom(format!("invalid Int256 '{v}' - {e}"))) } } @@ -673,25 +667,25 @@ mod tests { #[test] fn int256_implements_display() { let a = Int256::from(12345u32); - assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(format!("Embedded: {a}"), "Embedded: 12345"); assert_eq!(a.to_string(), "12345"); let a = Int256::from(-12345i32); - assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(format!("Embedded: {a}"), "Embedded: -12345"); assert_eq!(a.to_string(), "-12345"); let a = Int256::zero(); - assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(format!("Embedded: {a}"), "Embedded: 0"); assert_eq!(a.to_string(), "0"); } #[test] fn int256_display_padding_works() { let a = Int256::from(123u64); - assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + assert_eq!(format!("Embedded: {a:05}"), "Embedded: 00123"); let a = Int256::from(-123i64); - assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + assert_eq!(format!("Embedded: {a:05}"), "Embedded: -0123"); } #[test] diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 980b81ee5c..f3e5cba0f9 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -391,7 +391,7 @@ impl FromStr for Int512 { fn from_str(s: &str) -> Result { match I512::from_str_radix(s, 10) { Ok(u) => Ok(Self(u)), - Err(e) => Err(StdError::generic_err(format!("Parsing Int512: {}", e))), + Err(e) => Err(StdError::generic_err(format!("Parsing Int512: {e}"))), } } } @@ -504,10 +504,7 @@ impl Shr for Int512 { fn shr(self, rhs: u32) -> Self::Output { self.checked_shr(rhs).unwrap_or_else(|_| { - panic!( - "right shift error: {} is larger or equal than the number of bits in Int512", - rhs, - ) + panic!("right shift error: {rhs} is larger or equal than the number of bits in Int512",) }) } } @@ -518,10 +515,7 @@ impl Shl for Int512 { fn shl(self, rhs: u32) -> Self::Output { self.checked_shl(rhs).unwrap_or_else(|_| { - panic!( - "left shift error: {} is larger or equal than the number of bits in Int512", - rhs, - ) + panic!("left shift error: {rhs} is larger or equal than the number of bits in Int512",) }) } } @@ -588,7 +582,7 @@ impl<'de> de::Visitor<'de> for Int512Visitor { where E: de::Error, { - Int512::try_from(v).map_err(|e| E::custom(format!("invalid Int512 '{}' - {}", v, e))) + Int512::try_from(v).map_err(|e| E::custom(format!("invalid Int512 '{v}' - {e}"))) } } @@ -712,25 +706,25 @@ mod tests { #[test] fn int512_implements_display() { let a = Int512::from(12345u32); - assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(format!("Embedded: {a}"), "Embedded: 12345"); assert_eq!(a.to_string(), "12345"); let a = Int512::from(-12345i32); - assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(format!("Embedded: {a}"), "Embedded: -12345"); assert_eq!(a.to_string(), "-12345"); let a = Int512::zero(); - assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(format!("Embedded: {a}"), "Embedded: 0"); assert_eq!(a.to_string(), "0"); } #[test] fn int512_display_padding_works() { let a = Int512::from(123u64); - assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + assert_eq!(format!("Embedded: {a:05}"), "Embedded: 00123"); let a = Int512::from(-123i64); - assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + assert_eq!(format!("Embedded: {a:05}"), "Embedded: -0123"); } #[test] diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 8cfbc756c6..f26e79fe75 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -267,7 +267,7 @@ impl FromStr for Int64 { fn from_str(s: &str) -> Result { match s.parse::() { Ok(u) => Ok(Self(u)), - Err(e) => Err(StdError::generic_err(format!("Parsing Int64: {}", e))), + Err(e) => Err(StdError::generic_err(format!("Parsing Int64: {e}"))), } } } @@ -375,10 +375,7 @@ impl Shr for Int64 { fn shr(self, rhs: u32) -> Self::Output { self.checked_shr(rhs).unwrap_or_else(|_| { - panic!( - "right shift error: {} is larger or equal than the number of bits in Int64", - rhs, - ) + panic!("right shift error: {rhs} is larger or equal than the number of bits in Int64",) }) } } @@ -389,10 +386,7 @@ impl Shl for Int64 { fn shl(self, rhs: u32) -> Self::Output { self.checked_shl(rhs).unwrap_or_else(|_| { - panic!( - "left shift error: {} is larger or equal than the number of bits in Int64", - rhs, - ) + panic!("left shift error: {rhs} is larger or equal than the number of bits in Int64",) }) } } @@ -459,7 +453,7 @@ impl<'de> de::Visitor<'de> for Int64Visitor { where E: de::Error, { - Int64::try_from(v).map_err(|e| E::custom(format!("invalid Int64 '{}' - {}", v, e))) + Int64::try_from(v).map_err(|e| E::custom(format!("invalid Int64 '{v}' - {e}"))) } } @@ -578,25 +572,25 @@ mod tests { #[test] fn int64_implements_display() { let a = Int64::from(12345u32); - assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(format!("Embedded: {a}"), "Embedded: 12345"); assert_eq!(a.to_string(), "12345"); let a = Int64::from(-12345i32); - assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(format!("Embedded: {a}"), "Embedded: -12345"); assert_eq!(a.to_string(), "-12345"); let a = Int64::zero(); - assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(format!("Embedded: {a}"), "Embedded: 0"); assert_eq!(a.to_string(), "0"); } #[test] fn int64_display_padding_works() { let a = Int64::from(123i64); - assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + assert_eq!(format!("Embedded: {a:05}"), "Embedded: 00123"); let a = Int64::from(-123i64); - assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + assert_eq!(format!("Embedded: {a:05}"), "Embedded: -0123"); } #[test] From 5a4533242cff2414e98c1457c98f8016606b5297 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 3 Jul 2023 17:29:55 +0200 Subject: [PATCH 113/113] Fix changelog --- CHANGELOG.md | 67 +++++++++++++--------------------------------------- 1 file changed, 17 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f818a6feb..9cea75c7e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- cosmwasm-vm: Avoid using loupe for getting the `Module` size in the file + system cache to prepare for the Wasmer 3 upgrade. +- cosmwasm-vm: When enabling `print_debug` the debug logs are now printed to + STDERR instead of STDOUT by default ([#1667]). +- cosmwasm-vm: Add `Instance::set_debug_handler`/`unset_debug_handler` to allow + customizing the handling of debug messages emitted by the contract ([#1667]). +- cosmwasm-vm: Upgrade Wasmer to version 4.0.0. +- cosmwasm-check: Update clap dependency to version 4 ([#1677]) + +[#1667]: https://github.com/CosmWasm/cosmwasm/pull/1667 +[#1677]: https://github.com/CosmWasm/cosmwasm/pull/1677 + ## [1.3.0-rc.0] - 2023-07-03 ### Fixed @@ -59,56 +75,6 @@ and this project adheres to [#1664]: https://github.com/CosmWasm/cosmwasm/pull/1664 [#1686]: https://github.com/CosmWasm/cosmwasm/pull/1686 -### Added - -- cosmwasm-std: Implement `BankQuery::AllDenomMetadata` to allow querying all - the denom metadata and `BankQuery::DenomMetadata` to query a specific one. In - order to use this query in a contract, the `cosmwasm_1_3` feature needs to be - enabled for the `cosmwasm_std` dependency. This makes the contract - incompatible with chains running anything lower than CosmWasm `1.3.0`. - ([#1647]) -- cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have - been checked before. This is useful for state-sync where we know the Wasm code - was checked when it was first uploaded. ([#1635]) -- cosmwasm-std: Add `FromStr` impl for `Coin`. ([#1684]) -- cosmwasm-std: Add `Decimal::bps` and `Decimal256::bps` to create a decimal - from a basis point value ([#1715]). - -[#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635 -[#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647 -[#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 -[#1715]: https://github.com/CosmWasm/cosmwasm/pull/1715 - -### Changed - -- cosmwasm-vm: Add checks for table section of Wasm blob ([#1631]). -- cosmwasm-vm: Limit number of imports during static validation ([#1629]). -- cosmwasm-vm: The `check_contract` example was removed. Please use the crate - [cosmwasm-check](https://crates.io/crates/cosmwasm-check) instead ([#1511]). -- cosmwasm-vm: Avoid using loupe for getting the `Module` size in the file - system cache to prepare for the Wasmer 3 upgrade. -- cosmwasm-vm: Add target (triple + CPU features) into the module cache - directory to avoid using modules compiled for a different system. Bump - `MODULE_SERIALIZATION_VERSION` to "v5". ([#1664]) -- cosmwasm-vm: When enabling `print_debug` the debug logs are now printed to - STDERR instead of STDOUT by default ([#1667]). -- cosmwasm-vm: Add `Instance::set_debug_handler`/`unset_debug_handler` to allow - customizing the handling of debug messages emitted by the contract ([#1667]). -- cosmwasm-vm: Add `.wasm` extension to stored wasm files ([#1686]). -- cosmwasm-vm: Upgrade Wasmer to version 4.0.0. -- cosmwasm-check: Update clap dependency to version 4 ([#1677]) -- cosmwasm-std: Coin uses shorter `Coin { 123 "ucosm" }` format for Debug - ([#1704]) - -[#1511]: https://github.com/CosmWasm/cosmwasm/issues/1511 -[#1629]: https://github.com/CosmWasm/cosmwasm/pull/1629 -[#1631]: https://github.com/CosmWasm/cosmwasm/pull/1631 -[#1664]: https://github.com/CosmWasm/cosmwasm/pull/1664 -[#1667]: https://github.com/CosmWasm/cosmwasm/pull/1667 -[#1677]: https://github.com/CosmWasm/cosmwasm/pull/1677 -[#1686]: https://github.com/CosmWasm/cosmwasm/pull/1686 -[#1704]: https://github.com/CosmWasm/cosmwasm/pull/1704 - ### Deprecated - cosmwasm-storage: All exports are deprecated because this crate will be @@ -1826,6 +1792,7 @@ Some main points: All future Changelog entries will reference this base +[unreleased]: https://github.com/CosmWasm/cosmwasm/compare/v1.3.0-rc.0...HEAD [1.3.0-rc.0]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.7...v1.3.0-rc.0 [1.2.7]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.6...v1.2.7 [1.2.6]: https://github.com/CosmWasm/cosmwasm/compare/v1.2.5...v1.2.6