Repository: CosmWasm/cosmwasm (rust)

Issue:
Add MockApi::addr_make
Create a function that allows creating test addresses from arbitrary strings. As discussed in #1873.

E.g. in test code

```rust
let manager: Addr = api.addr_make("manager");
let user1: Addr = api.addr_make("Alice");
let user2: Addr = api.addr_make("Bob");
```

Pull request:
Add `make_addr` function to `MockApi`
closes #1887

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 31018a206..21e8b20c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@ and this project adheres to
 
 ### Added
 
+- cosmwasm-std: Add `addr_make` and `with_prefix` for
+  `cosmwasm_std::testing::MockApi` ([#1905]).
 - cosmwasm-std: Add `abs` and `unsigned_abs` for `Int{64,128,256,512}`
   ([#1854]).
 - cosmwasm-std: Add `From<Int{64,128,256}>` for `Int512`,
@@ -25,6 +27,7 @@ and this project adheres to
 - cosmwasm-std: Add `SignedDecimal` and `SignedDecimal256` ([#1807]).
 - cosmwasm-vm: Allow float operations with NaN canonicalization ([#1864]).
 
+[#1905]: https://github.com/CosmWasm/cosmwasm/pull/1905
 [#1854]: https://github.com/CosmWasm/cosmwasm/pull/1854
 [#1861]: https://github.com/CosmWasm/cosmwasm/pull/1861
 [#1866]: https://github.com/CosmWasm/cosmwasm/pull/1866
diff --git a/Cargo.lock b/Cargo.lock
index bb734ad74..315dfca72 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -179,6 +179,12 @@ version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.3.2"
@@ -488,6 +494,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64",
+ "bech32",
  "bnum",
  "chrono",
  "cosmwasm-crypto",
diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock
index 566e96cb4..02737f48b 100644
--- a/contracts/burner/Cargo.lock
+++ b/contracts/burner/Cargo.lock
@@ -73,6 +73,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -235,6 +241,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64",
+ "bech32",
  "bnum",
  "cosmwasm-crypto",
  "cosmwasm-derive",
diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock
index a8008a6df..304029844 100644
--- a/contracts/crypto-verify/Cargo.lock
+++ b/contracts/crypto-verify/Cargo.lock
@@ -73,6 +73,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -224,6 +230,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64",
+ "bech32",
  "bnum",
  "cosmwasm-crypto",
  "cosmwasm-derive",
diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock
index 21bffcd7f..672322b13 100644
--- a/contracts/cyberpunk/Cargo.lock
+++ b/contracts/cyberpunk/Cargo.lock
@@ -91,6 +91,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -259,6 +265,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64 0.21.2",
+ "bech32",
  "bnum",
  "cosmwasm-crypto",
  "cosmwasm-derive",
diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock
index fa69b7a4b..4dbedc6f7 100644
--- a/contracts/floaty/Cargo.lock
+++ b/contracts/floaty/Cargo.lock
@@ -73,6 +73,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -224,6 +230,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64",
+ "bech32",
  "bnum",
  "cosmwasm-crypto",
  "cosmwasm-derive",
diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock
index bf7fdc272..ad372a0f9 100644
--- a/contracts/hackatom/Cargo.lock
+++ b/contracts/hackatom/Cargo.lock
@@ -73,6 +73,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -224,6 +230,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64",
+ "bech32",
  "bnum",
  "cosmwasm-crypto",
  "cosmwasm-derive",
diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock
index 60524cc90..68a66fdf7 100644
--- a/contracts/ibc-reflect-send/Cargo.lock
+++ b/contracts/ibc-reflect-send/Cargo.lock
@@ -73,6 +73,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -224,6 +230,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64",
+ "bech32",
  "bnum",
  "cosmwasm-crypto",
  "cosmwasm-derive",
diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock
index 8aa6139a1..342121c6e 100644
--- a/contracts/ibc-reflect/Cargo.lock
+++ b/contracts/ibc-reflect/Cargo.lock
@@ -73,6 +73,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -224,6 +230,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64",
+ "bech32",
  "bnum",
  "cosmwasm-crypto",
  "cosmwasm-derive",
diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock
index e2eaef6aa..165eb6a32 100644
--- a/contracts/queue/Cargo.lock
+++ b/contracts/queue/Cargo.lock
@@ -73,6 +73,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -224,6 +230,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64",
+ "bech32",
  "bnum",
  "cosmwasm-crypto",
  "cosmwasm-derive",
diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock
index 52c5a727e..25303ef1b 100644
--- a/contracts/reflect/Cargo.lock
+++ b/contracts/reflect/Cargo.lock
@@ -73,6 +73,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -224,6 +230,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64",
+ "bech32",
  "bnum",
  "cosmwasm-crypto",
  "cosmwasm-derive",
diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock
index 1a1373ef1..e7e58ec94 100644
--- a/contracts/staking/Cargo.lock
+++ b/contracts/staking/Cargo.lock
@@ -73,6 +73,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -224,6 +230,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64",
+ "bech32",
  "bnum",
  "cosmwasm-crypto",
  "cosmwasm-derive",
diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock
index f214963f0..efc2cf15d 100644
--- a/contracts/virus/Cargo.lock
+++ b/contracts/virus/Cargo.lock
@@ -73,6 +73,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
 
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -224,6 +230,7 @@ name = "cosmwasm-std"
 version = "1.4.0"
 dependencies = [
  "base64",
+ "bech32",
  "bnum",
  "cosmwasm-crypto",
  "cosmwasm-derive",
diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml
index 323b73b70..6ab8a5ab4 100644
--- a/packages/std/Cargo.toml
+++ b/packages/std/Cargo.toml
@@ -64,6 +64,7 @@ bnum = "0.8.0"
 static_assertions = "1.1.0"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+bech32 = "0.9.1"
 cosmwasm-crypto = { path = "../crypto", version = "1.4.0" }
 
 [dev-dependencies]


Test patch:
diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs
index d2ca6d65f..1c337e1ac 100644
--- a/packages/std/src/testing/mock.rs
+++ b/packages/std/src/testing/mock.rs
@@ -1,10 +1,12 @@
 use alloc::collections::BTreeMap;
+use bech32::{encode, ToBase32, Variant};
 use core::marker::PhantomData;
 #[cfg(feature = "cosmwasm_1_3")]
 use core::ops::Bound;
 use serde::de::DeserializeOwned;
 #[cfg(feature = "stargate")]
 use serde::Serialize;
+use sha2::{Digest, Sha256};
 #[cfg(feature = "cosmwasm_1_3")]
 use std::collections::BTreeSet;
 use std::collections::HashMap;
@@ -106,6 +108,9 @@ const CANONICAL_LENGTH: usize = 90; // n = 45
 const SHUFFLES_ENCODE: usize = 10;
 const SHUFFLES_DECODE: usize = 2;
 
+/// Default prefix used when creating Bech32 encoded address.
+const BECH32_PREFIX: &str = "cosmwasm";
+
 // MockApi zero pads all human addresses to make them fit the canonical_length
 // it trims off zeros for the reverse operation.
 // not really smart, but allows us to see a difference (and consistent length for canonical addresses)
@@ -114,12 +119,15 @@ pub struct MockApi {
     /// Length of canonical addresses created with this API. Contracts should not make any assumptions
     /// what this value is.
     canonical_length: usize,
+    /// Prefix used for creating addresses in Bech32 encoding.
+    bech32_prefix: &'static str,
 }
 
 impl Default for MockApi {
     fn default() -> Self {
         MockApi {
             canonical_length: CANONICAL_LENGTH,
+            bech32_prefix: BECH32_PREFIX,
         }
     }
 }
@@ -244,6 +252,55 @@ impl Api for MockApi {
     }
 }
 
+impl MockApi {
+    /// Returns [MockApi] with Bech32 prefix set to provided value.
+    ///
+    /// Bech32 prefix must not be empty.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # use cosmwasm_std::Addr;
+    /// # use cosmwasm_std::testing::MockApi;
+    /// #
+    /// let mock_api = MockApi::default().with_prefix("juno");
+    /// let addr = mock_api.addr_make("creator");
+    ///
+    /// assert_eq!(addr.to_string(), "juno1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqsksmtyp");
+    /// ```
+    pub fn with_prefix(mut self, prefix: &'static str) -> Self {
+        self.bech32_prefix = prefix;
+        self
+    }
+
+    /// Returns an address built from provided input string.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # use cosmwasm_std::Addr;
+    /// # use cosmwasm_std::testing::MockApi;
+    /// #
+    /// let mock_api = MockApi::default();
+    /// let addr = mock_api.addr_make("creator");
+    ///
+    /// assert_eq!(addr.to_string(), "cosmwasm1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqs8s7vcp");
+    /// ```
+    ///
+    /// # Panics
+    ///
+    /// This function panics when generating a valid address is not possible,
+    /// especially when Bech32 prefix set in function [with_prefix](Self::with_prefix) is empty.
+    ///
+    pub fn addr_make(&self, input: &str) -> Addr {
+        let digest = Sha256::digest(input).to_vec();
+        match encode(self.bech32_prefix, digest.to_base32(), Variant::Bech32) {
+            Ok(address) => Addr::unchecked(address),
+            Err(reason) => panic!("Generating address failed with reason: {reason}"),
+        }
+    }
+}
+
 /// Returns a default enviroment with height, time, chain_id, and contract address
 /// You can submit as is to most contracts, or modify height/time if you want to
 /// test for expiration.
@@ -2264,4 +2321,31 @@ mod tests {
 
         assert_eq!(digit_sum(&[255, 1]), 256);
     }
+
+    #[test]
+    fn making_an_address_works() {
+        let mock_api = MockApi::default();
+
+        assert_eq!(
+            mock_api.addr_make("creator").to_string(),
+            "cosmwasm1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqs8s7vcp",
+        );
+
+        assert_eq!(
+            mock_api.addr_make("").to_string(),
+            "cosmwasm1uwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2sly4xh9",
+        );
+
+        let mock_api = MockApi::default().with_prefix("juno");
+        assert_eq!(
+            mock_api.addr_make("creator").to_string(),
+            "juno1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqsksmtyp",
+        );
+    }
+
+    #[test]
+    #[should_panic(expected = "Generating address failed with reason: invalid length")]
+    fn making_an_address_with_empty_prefix_should_panic() {
+        MockApi::default().with_prefix("").addr_make("creator");
+    }
 }