Repository: CosmWasm/cosmwasm
Pull request #1561 (2023-01-03 13:35:56)

Add WasmQuery::CodeInfo to get checksum for code ID
The motivation for this addition is the following: `WasmMsg::Instantiate2` requires a code ID. But when a contract wants to pre-compute the address, it also needs the checksum. It would be very inconvenient if a contract developer had to provide both as part of the instruction. With the query you can just provide a code ID and query the checksum as part of the contract.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8292e9dfe..5012273c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,8 @@ and this project adheres to
   as the error type for `ibc_packet_receive` or `ibc_packet_ack` to gain
   confidence that the implementations never errors and the transaction does not
   get reverted. ([#1513])
+- cosmwasm-std: Add new `WasmQuery::CodeInfo` to get the checksum of a code ID
+  ([#1561]).
 
 [#1436]: https://github.com/CosmWasm/cosmwasm/issues/1436
 [#1437]: https://github.com/CosmWasm/cosmwasm/issues/1437
@@ -40,6 +42,7 @@ and this project adheres to
 [#1552]: https://github.com/CosmWasm/cosmwasm/pull/1552
 [#1554]: https://github.com/CosmWasm/cosmwasm/pull/1554
 [#1560]: https://github.com/CosmWasm/cosmwasm/pull/1560
+[#1561]: https://github.com/CosmWasm/cosmwasm/pull/1561
 
 ### Changed
 
diff --git a/devtools/check_workspace.sh b/devtools/check_workspace.sh
index a9969ae5f..7304866ee 100755
--- a/devtools/check_workspace.sh
+++ b/devtools/check_workspace.sh
@@ -7,8 +7,10 @@ cargo fmt
 (cd packages/derive && cargo check && cargo clippy --all-targets -- -D warnings)
 (
   cd packages/std
+  # default, min, all
   cargo check
-  cargo check --features iterator,staking,stargate,cosmwasm_1_1,cosmwasm_1_2
+  cargo check --no-default-features
+  cargo check --features abort,iterator,staking,stargate,cosmwasm_1_1,cosmwasm_1_2
   cargo wasm-debug
   cargo wasm-debug --features iterator,staking,stargate
   cargo clippy --all-targets --features iterator,staking,stargate -- -D warnings
diff --git a/packages/std/src/errors/system_error.rs b/packages/std/src/errors/system_error.rs
index 27cd40f12..97d7bba3c 100644
--- a/packages/std/src/errors/system_error.rs
+++ b/packages/std/src/errors/system_error.rs
@@ -28,6 +28,11 @@ pub enum SystemError {
         /// The address that was attempted to query
         addr: String,
     },
+    /// A Wasm code was not found.
+    NoSuchCode {
+        /// The code ID that is missing
+        code_id: u64,
+    },
     Unknown {},
     UnsupportedRequest {
         kind: String,
@@ -52,6 +57,7 @@ impl std::fmt::Display for SystemError {
                 String::from_utf8_lossy(response)
             ),
             SystemError::NoSuchContract { addr } => write!(f, "No such contract: {}", addr),
+            SystemError::NoSuchCode { code_id } => write!(f, "No such code: {}", code_id),
             SystemError::Unknown {} => write!(f, "Unknown system error"),
             SystemError::UnsupportedRequest { kind } => {
                 write!(f, "Unsupported query type: {}", kind)
diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs
index 809e87484..d40d25d68 100644
--- a/packages/std/src/lib.rs
+++ b/packages/std/src/lib.rs
@@ -50,6 +50,8 @@ pub use crate::math::{
     Uint256, Uint512, Uint64,
 };
 pub use crate::never::Never;
+#[cfg(feature = "cosmwasm_1_2")]
+pub use crate::query::CodeInfoResponse;
 #[cfg(feature = "cosmwasm_1_1")]
 pub use crate::query::SupplyResponse;
 pub use crate::query::{
diff --git a/packages/std/src/query/mod.rs b/packages/std/src/query/mod.rs
index 5ca17fe35..50061df09 100644
--- a/packages/std/src/query/mod.rs
+++ b/packages/std/src/query/mod.rs
@@ -21,6 +21,8 @@ pub use staking::{
     AllDelegationsResponse, AllValidatorsResponse, BondedDenomResponse, Delegation,
     DelegationResponse, FullDelegation, StakingQuery, Validator, ValidatorResponse,
 };
+#[cfg(feature = "cosmwasm_1_2")]
+pub use wasm::CodeInfoResponse;
 pub use wasm::{ContractInfoResponse, WasmQuery};
 
 #[non_exhaustive]
diff --git a/packages/std/src/query/wasm.rs b/packages/std/src/query/wasm.rs
index 2be597ae6..13070c18f 100644
--- a/packages/std/src/query/wasm.rs
+++ b/packages/std/src/query/wasm.rs
@@ -2,6 +2,8 @@ use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 
 use crate::Binary;
+#[cfg(feature = "cosmwasm_1_2")]
+use crate::HexBinary;
 
 use super::query_response::QueryResponseType;
 
@@ -26,6 +28,9 @@ pub enum WasmQuery {
     },
     /// Returns a [`ContractInfoResponse`] with metadata on the contract from the runtime
     ContractInfo { contract_addr: String },
+    /// Returns a [`CodeInfoResponse`] with metadata of the code
+    #[cfg(feature = "cosmwasm_1_2")]
+    CodeInfo { code_id: u64 },
 }
 
 #[non_exhaustive]
@@ -61,3 +66,24 @@ impl ContractInfoResponse {
         }
     }
 }
+
+/// The essential data from wasmd's [CodeInfo]/[CodeInfoResponse].
+///
+/// `code_hash`/`data_hash` was renamed to `checksum` to follow the CosmWasm
+/// convention and naming in `instantiate2_address`.
+///
+/// [CodeInfo]: https://github.com/CosmWasm/wasmd/blob/v0.30.0/proto/cosmwasm/wasm/v1/types.proto#L62-L72
+/// [CodeInfoResponse]: https://github.com/CosmWasm/wasmd/blob/v0.30.0/proto/cosmwasm/wasm/v1/query.proto#L184-L199
+#[non_exhaustive]
+#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)]
+#[cfg(feature = "cosmwasm_1_2")]
+pub struct CodeInfoResponse {
+    pub code_id: u64,
+    /// The address that initially stored the code
+    pub creator: String,
+    /// The hash of the Wasm blob
+    pub checksum: HexBinary,
+}
+
+#[cfg(feature = "cosmwasm_1_2")]
+impl QueryResponseType for CodeInfoResponse {}
diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs
index 57bd5d81b..e3ba067b6 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_2")]
+use crate::query::CodeInfoResponse;
 #[cfg(feature = "cosmwasm_1_1")]
 use crate::query::SupplyResponse;
 use crate::query::{
@@ -304,6 +306,13 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> {
         self.query(&request)
     }
 
+    /// Given a code ID, query information about that code.
+    #[cfg(feature = "cosmwasm_1_2")]
+    pub fn query_wasm_code_info(&self, code_id: u64) -> StdResult<CodeInfoResponse> {
+        let request = WasmQuery::CodeInfo { code_id }.into();
+        self.query(&request)
+    }
+
     #[cfg(feature = "staking")]
     pub fn query_all_validators(&self) -> StdResult<Vec<Validator>> {
         let request = StakingQuery::AllValidators {}.into();


Test patch:
diff --git a/devtools/test_workspace.sh b/devtools/test_workspace.sh
index 0fdcfb201..f1790536e 100755
--- a/devtools/test_workspace.sh
+++ b/devtools/test_workspace.sh
@@ -4,7 +4,7 @@ command -v shellcheck >/dev/null && shellcheck "$0"
 
 cargo fmt
 (cd packages/crypto && cargo test)
-(cd packages/std && cargo test --features iterator)
+(cd packages/std && cargo test --features iterator,cosmwasm_1_1,cosmwasm_1_2)
 (cd packages/storage && cargo test --features iterator)
 (cd packages/schema && cargo test)
 (cd packages/schema-derive && cargo test)
diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs
index 1417b93d1..023ef30ff 100644
--- a/packages/std/src/testing/mock.rs
+++ b/packages/std/src/testing/mock.rs
@@ -562,13 +562,22 @@ impl WasmQuerier {
 impl Default for WasmQuerier {
     fn default() -> Self {
         let handler = Box::from(|request: &WasmQuery| -> QuerierResult {
-            let addr = match request {
-                WasmQuery::Smart { contract_addr, .. } => contract_addr,
-                WasmQuery::Raw { contract_addr, .. } => contract_addr,
-                WasmQuery::ContractInfo { contract_addr, .. } => contract_addr,
-            }
-            .clone();
-            SystemResult::Err(SystemError::NoSuchContract { addr })
+            let err = match request {
+                WasmQuery::Smart { contract_addr, .. } => SystemError::NoSuchContract {
+                    addr: contract_addr.clone(),
+                },
+                WasmQuery::Raw { contract_addr, .. } => SystemError::NoSuchContract {
+                    addr: contract_addr.clone(),
+                },
+                WasmQuery::ContractInfo { contract_addr, .. } => SystemError::NoSuchContract {
+                    addr: contract_addr.clone(),
+                },
+                #[cfg(feature = "cosmwasm_1_2")]
+                WasmQuery::CodeInfo { code_id, .. } => {
+                    SystemError::NoSuchCode { code_id: *code_id }
+                }
+            };
+            SystemResult::Err(err)
         });
         Self::new(handler)
     }
@@ -1381,7 +1390,7 @@ mod tests {
 
         let any_addr = "foo".to_string();
 
-        // Query WasmQuery::Raw
+        // By default, querier errors for WasmQuery::Raw
         let system_err = querier
             .query(&WasmQuery::Raw {
                 contract_addr: any_addr.clone(),
@@ -1393,7 +1402,7 @@ mod tests {
             err => panic!("Unexpected error: {:?}", err),
         }
 
-        // Query WasmQuery::Smart
+        // By default, querier errors for WasmQuery::Smart
         let system_err = querier
             .query(&WasmQuery::Smart {
                 contract_addr: any_addr.clone(),
@@ -1405,7 +1414,7 @@ mod tests {
             err => panic!("Unexpected error: {:?}", err),
         }
 
-        // Query WasmQuery::ContractInfo
+        // By default, querier errors for WasmQuery::ContractInfo
         let system_err = querier
             .query(&WasmQuery::ContractInfo {
                 contract_addr: any_addr.clone(),
@@ -1416,6 +1425,18 @@ mod tests {
             err => panic!("Unexpected error: {:?}", err),
         }
 
+        #[cfg(feature = "cosmwasm_1_2")]
+        {
+            // By default, querier errors for WasmQuery::CodeInfo
+            let system_err = querier
+                .query(&WasmQuery::CodeInfo { code_id: 4 })
+                .unwrap_err();
+            match system_err {
+                SystemError::NoSuchCode { code_id } => assert_eq!(code_id, 4),
+                err => panic!("Unexpected error: {:?}", err),
+            }
+        }
+
         querier.update_handler(|request| {
             let constract1 = Addr::unchecked("contract1");
             let mut storage1 = HashMap::<Binary, Binary>::default();
@@ -1469,6 +1490,24 @@ mod tests {
                         })
                     }
                 }
+                #[cfg(feature = "cosmwasm_1_2")]
+                WasmQuery::CodeInfo { code_id } => {
+                    use crate::{CodeInfoResponse, HexBinary};
+                    let code_id = *code_id;
+                    if code_id == 4 {
+                        let response = CodeInfoResponse {
+                            code_id,
+                            creator: "lalala".into(),
+                            checksum: HexBinary::from_hex(
+                                "84cf20810fd429caf58898c3210fcb71759a27becddae08dbde8668ea2f4725d",
+                            )
+                            .unwrap(),
+                        };
+                        SystemResult::Ok(ContractResult::Ok(to_binary(&response).unwrap()))
+                    } else {
+                        SystemResult::Err(SystemError::NoSuchCode { code_id })
+                    }
+                }
             }
         });
 
@@ -1525,6 +1564,19 @@ mod tests {
             ),
             res => panic!("Unexpected result: {:?}", res),
         }
+
+        // WasmQuery::ContractInfo
+        #[cfg(feature = "cosmwasm_1_2")]
+        {
+            let result = querier.query(&WasmQuery::CodeInfo { code_id: 4 });
+            match result {
+                SystemResult::Ok(ContractResult::Ok(value)) => assert_eq!(
+                    value,
+                    br#"{"code_id":4,"creator":"lalala","checksum":"84cf20810fd429caf58898c3210fcb71759a27becddae08dbde8668ea2f4725d"}"#
+                ),
+                res => panic!("Unexpected result: {:?}", res),
+            }
+        }
     }
 
     #[test]