Repository: CosmWasm/cosmwasm (rust)

Issue:
CosmWasm 2 address systems flaws
When [upgrading a complex codebase from 1.5 to 2.0](https://github.com/noislabs/nois-contracts/pull/302) I spend roughly 1 hour to make it compile but many more hours to make all the tests pass. The biggest issue is the change in the address validation in MockApi which does not allow arbitrary strings anymore. While I still think the change is good in general and new codebases will have an easier life, there are flaws we should address.
 
- [x] `mock_info` takes a `&str` address. However, we need a `Addr` in the caller code and an `Addr` in `MessageInfo`. Due to the `&str` type it is unclear if the input is supposed to contain the full address (it does). Thies leads to a log of `mock_info(manager2.as_str(), &[])` which could just be `mock_info(&manager2, &[])` if the type was `&Addr`. (https://github.com/CosmWasm/cosmwasm/pull/2160)
- [ ] `addr_make` cannot be used in `DepsMut` due to the way the API field is typed. Use case: shared functions like `setup_channel(deps.as_mut());`
- [x] #2211
- [x] Don't validate validator address in Multitest's `.add_validator(api, storage, &block, valoper1)` and probably also message delegate implementation. Validator addresses have a different layout than the account addresses. (https://github.com/CosmWasm/cw-multi-test/issues/173)



Pull request description:
Implement downcasting for `Api` trait
closes #2157 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 637853a37..8a6525013 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,8 @@ and this project adheres to
 - cosmwasm-std: Implement `Uint128::from_{be,le}_bytes` and
   `Uint64::from_{be,le}_bytes`. ([#2269])
 - cosmwasm-std: Added new `EurekaMsg` and `CosmosMsg::Eureka` variant ([#2340])
+- cosmwasm-std: Implement downcasting for `Api` trait. This allows using
+  `MockApi::addr_make` from `DepsMut`. ([#2383])
 
 ## Changed
 
@@ -45,6 +47,7 @@ and this project adheres to
 [#2344]: https://github.com/CosmWasm/cosmwasm/pull/2344
 [#2374]: https://github.com/CosmWasm/cosmwasm/issues/2374
 [#2378]: https://github.com/CosmWasm/cosmwasm/issues/2378
+[#2383]: https://github.com/CosmWasm/cosmwasm/issues/2383
 
 ## [2.2.0] - 2024-12-17
 
diff --git a/Cargo.lock b/Cargo.lock
index 0ecb14f59..bd4de58c4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "Inflector"
@@ -769,6 +769,7 @@ dependencies = [
  "cosmwasm-schema",
  "crc32fast",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "hex-literal",
  "paste",
@@ -1170,6 +1171,12 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.18"
diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock
index dd8001bf0..8479abd1f 100644
--- a/contracts/burner/Cargo.lock
+++ b/contracts/burner/Cargo.lock
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "addr2line"
@@ -534,6 +534,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -849,6 +850,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock
index 9b11356c6..e694ca712 100644
--- a/contracts/crypto-verify/Cargo.lock
+++ b/contracts/crypto-verify/Cargo.lock
@@ -529,6 +529,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -862,6 +863,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock
index 0ab8163f2..6c92e1a68 100644
--- a/contracts/cyberpunk/Cargo.lock
+++ b/contracts/cyberpunk/Cargo.lock
@@ -552,6 +552,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -879,6 +880,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/empty/Cargo.lock b/contracts/empty/Cargo.lock
index 0ae10936f..9a546937e 100644
--- a/contracts/empty/Cargo.lock
+++ b/contracts/empty/Cargo.lock
@@ -523,6 +523,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -838,6 +839,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock
index 46a69eed2..253bf1816 100644
--- a/contracts/floaty/Cargo.lock
+++ b/contracts/floaty/Cargo.lock
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "addr2line"
@@ -523,6 +523,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -838,6 +839,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock
index ea36b853d..0ea680b42 100644
--- a/contracts/hackatom/Cargo.lock
+++ b/contracts/hackatom/Cargo.lock
@@ -523,6 +523,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -838,6 +839,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/ibc-callbacks/Cargo.lock b/contracts/ibc-callbacks/Cargo.lock
index e1b7e4ea8..586d0b26e 100644
--- a/contracts/ibc-callbacks/Cargo.lock
+++ b/contracts/ibc-callbacks/Cargo.lock
@@ -523,6 +523,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -838,6 +839,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock
index d89b977a1..b2fc13dc4 100644
--- a/contracts/ibc-reflect-send/Cargo.lock
+++ b/contracts/ibc-reflect-send/Cargo.lock
@@ -523,6 +523,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -838,6 +839,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock
index 4be955147..7579ba8ba 100644
--- a/contracts/ibc-reflect/Cargo.lock
+++ b/contracts/ibc-reflect/Cargo.lock
@@ -523,6 +523,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -838,6 +839,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock
index a1c508f6c..7a7adeff2 100644
--- a/contracts/queue/Cargo.lock
+++ b/contracts/queue/Cargo.lock
@@ -523,6 +523,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -838,6 +839,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock
index 365d43018..07367ac70 100644
--- a/contracts/reflect/Cargo.lock
+++ b/contracts/reflect/Cargo.lock
@@ -523,6 +523,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -838,6 +839,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/replier/Cargo.lock b/contracts/replier/Cargo.lock
index 1dd590748..4a3135aa3 100644
--- a/contracts/replier/Cargo.lock
+++ b/contracts/replier/Cargo.lock
@@ -264,6 +264,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -412,6 +413,12 @@ dependencies = [
  "subtle",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock
index 7a42252bc..adc8a352a 100644
--- a/contracts/staking/Cargo.lock
+++ b/contracts/staking/Cargo.lock
@@ -523,6 +523,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -838,6 +839,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock
index 0659cc26e..f45708ec4 100644
--- a/contracts/virus/Cargo.lock
+++ b/contracts/virus/Cargo.lock
@@ -523,6 +523,7 @@ dependencies = [
  "cosmwasm-crypto",
  "cosmwasm-derive",
  "derive_more 1.0.0-beta.6",
+ "downcast-rs",
  "hex",
  "rand_core",
  "rmp-serde",
@@ -838,6 +839,12 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.17"
diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml
index d357b01d0..e2a6ab426 100644
--- a/packages/std/Cargo.toml
+++ b/packages/std/Cargo.toml
@@ -74,6 +74,7 @@ serde-json-wasm = { version = "1.0.1", default-features = false, features = [
 static_assertions = "1.1.0"
 thiserror = "1.0.26"
 rmp-serde = "1.3.0"
+downcast-rs = { version = "2.0.1", default-features = false }
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
 bech32 = "0.11.0"
diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs
index 78f7fc4a2..7fcc6c15f 100644
--- a/packages/std/src/traits.rs
+++ b/packages/std/src/traits.rs
@@ -1,5 +1,6 @@
 use core::marker::PhantomData;
 use core::ops::Deref;
+use downcast_rs::{impl_downcast, Downcast};
 use serde::{de::DeserializeOwned, Serialize};
 
 use crate::coin::Coin;
@@ -130,7 +131,7 @@ pub trait Storage {
 ///
 /// We can use feature flags to opt-in to non-essential methods
 /// for backwards compatibility in systems that don't have them all.
-pub trait Api {
+pub trait Api: Downcast {
     /// Takes a human readable address and validates if it is valid.
     /// If it the validation succeeds, a `Addr` containing the same data as the input is returned.
     ///
@@ -329,6 +330,7 @@ pub trait Api {
     /// Those messages are not persisted to chain.
     fn debug(&self, message: &str);
 }
+impl_downcast!(Api);
 
 /// A short-hand alias for the two-level query result (1. accessing the contract, 2. executing query in the contract)
 pub type QuerierResult = SystemResult<ContractResult<Binary>>;


Test patch:
diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs
index 0d1c35565..7981fa3c5 100644
--- a/packages/std/src/testing/mock.rs
+++ b/packages/std/src/testing/mock.rs
@@ -2756,4 +2756,15 @@ mod tests {
         // we are not interested in the exact humanization, just that it works
         mock_api.addr_humanize(&canonical_addr).unwrap();
     }
+
+    #[test]
+    fn mock_api_downcast() {
+        let mut deps = mock_dependencies();
+        let deps_mut = deps.as_mut();
+
+        let mock_api = deps_mut.api.downcast_ref::<MockApi>().unwrap();
+
+        let addr = mock_api.addr_make("input");
+        assert_eq!(mock_api.addr_validate(addr.as_str()).unwrap(), addr);
+    }
 }