diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b8d125698..bf6aeedd5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,11 @@ and this project adheres to returns "submessages" (above). ([#796]) - cosmwasm-std: Implement `From for String`, `From for u128` as well as `From for Uint128`. +- cosmwasm-std: Create new address type `Addr`. This is human readable (like + `HumanAddr`) but is immutable and always contains a valid address ([#802]). +- cosmwasm-std: Replace `HumanAddr` with `String` in `BankQuery`, `StakingQuery` + and `WasmQuery` query requests. +- cosmwasm-vm: Add import `addr_validate` ([#802]). [#692]: https://github.com/CosmWasm/cosmwasm/issues/692 [#706]: https://github.com/CosmWasm/cosmwasm/pull/706 @@ -62,6 +67,7 @@ and this project adheres to [#768]: https://github.com/CosmWasm/cosmwasm/pull/768 [#793]: https://github.com/CosmWasm/cosmwasm/pull/793 [#796]: https://github.com/CosmWasm/cosmwasm/pull/796 +[#802]: https://github.com/CosmWasm/cosmwasm/pull/802 ### Changed @@ -106,10 +112,18 @@ and this project adheres to - cosmwasm-std: Rename type `KV` to `Pair` in order to comply to naming convention as enforced by clippy rule `upper_case_acronyms` from Rust 1.51.0 on. +- cosmwasm-std: `ContractInfo::address` and `MessageInfo::sender` are now of + type `Addr`. The value of those fields is created by the host and thus valid. - cosmwasm-vm: Bump required marker export `cosmwasm_vm_version_4` to `interface_version_5`. - cosmwasm-vm: Rename trait `Api` to `BackendApi` to better express this is the API provided by the VM's backend (i.e. the blockchain). +- cosmwasm-vm: Rename imports to `addr_canonicalize` and `addr_humanize` + ([#802]). +- cosmwasm-vm: Replace types `HumanAddr`/`CanonicalAddr` with + `&str`/`String`/`&[u8]`/`Vec` in the methods of `BackendApi`. The address + types belong in the contract development and the backend operates on raw + strings and binary anyways. - contracts: `reflect` contract requires `stargate` feature and supports redispatching `Stargate` and `IbcMsg::Transfer` messages ([#692]) - cosmwasm-std: The arithmetic methods of `Uint128` got a huge overhaul, making @@ -131,6 +145,10 @@ and this project adheres to library. Please use the explicit `*_sub` methods introduced above. In a couple of releases from now, we want to introduce the operator again with panicking overflow behaviour ([#858]). +- cosmwasm-std: Change address types in `BankMsg`, `IbcMsg` and `WasmMsg` from + `HumanAddr` to `String` ([#802]). +- cosmwasm-std: `Api::addr_humanize` now returns `Addr` instead of `HumanAddr` + ([#802]). [#696]: https://github.com/CosmWasm/cosmwasm/issues/696 [#697]: https://github.com/CosmWasm/cosmwasm/issues/697 @@ -140,6 +158,7 @@ and this project adheres to [#853]: https://github.com/CosmWasm/cosmwasm/pull/853 [#858]: https://github.com/CosmWasm/cosmwasm/issues/858 [u128]: https://doc.rust-lang.org/std/primitive.u128.html +[#802]: https://github.com/CosmWasm/cosmwasm/pull/802 ### Deprecated @@ -147,6 +166,12 @@ and this project adheres to deprecated in favour of the new `Response`. - cosmwasm-std: `Context` is deprecated in favour of the new mutable helpers in `Response`. +- cosmwasm-std: `HumanAddr` is not much more than an alias to `String` and it + does not provide significant safety advantages. With CosmWasm 0.14, we now use + `String` when there was `HumanAddr` before. There is also the new `Addr`, + which holds a validated immutable human readable address. ([#802]) + +[#802]: https://github.com/CosmWasm/cosmwasm/pull/802 ## [0.13.2] - 2021-01-14 diff --git a/MIGRATING.md b/MIGRATING.md index d97b6c90a1..5ab7c92579 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -225,6 +225,80 @@ major releases of `cosmwasm`. Note that you can also view the - If necessary, add a wildcard arm to the `match` of now non-exhaustive message types `BankMsg`, `BankQuery`, `WasmMsg` and `WasmQuery`. +- `HumanAddr` has been deprecated in favour of simply `String`. It never added + any significant safety bonus over `String` and was just a marker type. The new + type `Addr` was created to hold validated addresses. Those can be created via + `Addr::unchecked`, `Api::addr_validate`, `Api::addr_humanize` and JSON + deserialization. In order to maintain type safety, deserialization into `Addr` + must only be done from trusted sources like a contract's state or a query + response. User inputs must be deserialized into `String`. This new `Addr` type + makes it easy to use human readable addresses in state: + + With pre-validated `Addr` from `MessageInfo`: + + ```rust + // before + pub struct State { + pub owner: CanonicalAddr, + } + + let state = State { + owner: deps.api.canonical_address(&info.sender /* of type HumanAddr */)?, + }; + + + // after + pub struct State { + pub owner: Addr, + } + let state = State { + owner: info.sender.clone() /* of type Addr */, + }; + ``` + + With user input in `msg`: + + ```rust + // before + pub struct State { + pub verifier: CanonicalAddr, + pub beneficiary: CanonicalAddr, + pub funder: CanonicalAddr, + } + + deps.storage.set( + CONFIG_KEY, + &to_vec(&State { + verifier: deps.api.canonical_address(&msg.verifier /* of type HumanAddr */)?, + beneficiary: deps.api.canonical_address(&msg.beneficiary /* of type HumanAddr */)?, + funder: deps.api.canonical_address(&info.sender /* of type HumanAddr */)?, + })?, + ); + + // after + pub struct State { + pub verifier: Addr, + pub beneficiary: Addr, + pub funder: Addr, + } + + deps.storage.set( + CONFIG_KEY, + &to_vec(&State { + verifier: deps.api.addr_validate(&msg.verifier /* of type String */)?, + beneficiary: deps.api.addr_validate(&msg.beneficiary /* of type String */)?, + funder: info.sender /* of type Addr */, + })?, + ); + ``` + + The existing `CanonicalAddr` remains unchanged and can be used in cases in + which a compact binary representation is desired. For JSON state this does not + save much data (e.g. the bech32 address + cosmos1pfq05em6sfkls66ut4m2257p7qwlk448h8mysz takes 45 bytes as direct ASCII + and 28 bytes when its canonical representation is base64 encoded). For fixed + length database keys `CanonicalAddr` remains handy though. + ## 0.12 -> 0.13 - The minimum Rust supported version for 0.13 is 1.47.0. Verify your Rust diff --git a/README.md b/README.md index 76f8106ba1..58d606f98c 100644 --- a/README.md +++ b/README.md @@ -129,8 +129,9 @@ extern "C" { #[cfg(feature = "iterator")] fn db_next(iterator_id: u32) -> u32; - fn canonicalize_address(source: u32, destination: u32) -> u32; - fn humanize_address(source: u32, destination: u32) -> u32; + fn addr_validate(source_ptr: u32) -> u32; + fn addr_canonicalize(source: u32, destination: u32) -> u32; + fn addr_humanize(source: u32, destination: u32) -> u32; /// Verifies message hashes against a signature with a public key, using the /// secp256k1 ECDSA parametrization. diff --git a/contracts/burner/schema/migrate_msg.json b/contracts/burner/schema/migrate_msg.json index b0ef43c536..b2fd46b4fa 100644 --- a/contracts/burner/schema/migrate_msg.json +++ b/contracts/burner/schema/migrate_msg.json @@ -7,11 +7,6 @@ ], "properties": { "payout": { - "$ref": "#/definitions/HumanAddr" - } - }, - "definitions": { - "HumanAddr": { "type": "string" } } diff --git a/contracts/burner/src/contract.rs b/contracts/burner/src/contract.rs index 24334cf8ee..93a15620b6 100644 --- a/contracts/burner/src/contract.rs +++ b/contracts/burner/src/contract.rs @@ -30,7 +30,7 @@ pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> StdResult } // get balance and send all to recipient - let balance = deps.querier.query_all_balances(&env.contract.address)?; + let balance = deps.querier.query_all_balances(env.contract.address)?; let send = BankMsg::Send { to_address: msg.payout.clone(), amount: balance, @@ -50,7 +50,7 @@ pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> StdResult mod tests { use super::*; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{coins, HumanAddr, StdError, Storage}; + use cosmwasm_std::{coins, StdError, Storage}; #[test] fn instantiate_fails() { @@ -80,7 +80,7 @@ mod tests { assert_eq!(3, cnt); // change the verifier via migrate - let payout = HumanAddr::from("someone else"); + let payout = String::from("someone else"); let msg = MigrateMsg { payout: payout.clone(), }; diff --git a/contracts/burner/src/msg.rs b/contracts/burner/src/msg.rs index 952fb8f1c3..3a705c430c 100644 --- a/contracts/burner/src/msg.rs +++ b/contracts/burner/src/msg.rs @@ -1,11 +1,9 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::HumanAddr; - #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct MigrateMsg { - pub payout: HumanAddr, + pub payout: String, } /// A placeholder where we don't take any input diff --git a/contracts/burner/tests/integration.rs b/contracts/burner/tests/integration.rs index 1e24e1961e..fdd437e07b 100644 --- a/contracts/burner/tests/integration.rs +++ b/contracts/burner/tests/integration.rs @@ -17,7 +17,7 @@ //! }); //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) -use cosmwasm_std::{coins, BankMsg, ContractResult, HumanAddr, Order, Response}; +use cosmwasm_std::{coins, BankMsg, ContractResult, Order, Response}; use cosmwasm_vm::testing::{instantiate, migrate, mock_env, mock_info, mock_instance}; use burner::msg::{InstantiateMsg, MigrateMsg}; @@ -60,7 +60,7 @@ fn migrate_cleans_up_data() { .unwrap(); // change the verifier via migrate - let payout = HumanAddr::from("someone else"); + let payout = String::from("someone else"); let msg = MigrateMsg { payout: payout.clone(), }; diff --git a/contracts/hackatom/examples/schema.rs b/contracts/hackatom/examples/schema.rs index 3db2694adf..8adeee986f 100644 --- a/contracts/hackatom/examples/schema.rs +++ b/contracts/hackatom/examples/schema.rs @@ -4,9 +4,8 @@ use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; use cosmwasm_std::BalanceResponse; -use hackatom::contract::{ - ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, State, SudoMsg, VerifierResponse, -}; +use hackatom::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, SudoMsg, VerifierResponse}; +use hackatom::state::State; fn main() { let mut out_dir = current_dir().unwrap(); @@ -14,12 +13,15 @@ fn main() { create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); + // messages export_schema(&schema_for!(InstantiateMsg), &out_dir); export_schema(&schema_for!(ExecuteMsg), &out_dir); export_schema(&schema_for!(MigrateMsg), &out_dir); export_schema(&schema_for!(SudoMsg), &out_dir); export_schema(&schema_for!(QueryMsg), &out_dir); - export_schema(&schema_for!(State), &out_dir); export_schema(&schema_for!(VerifierResponse), &out_dir); export_schema(&schema_for!(BalanceResponse), &out_dir); + + // state + export_schema(&schema_for!(State), &out_dir); } diff --git a/contracts/hackatom/schema/instantiate_msg.json b/contracts/hackatom/schema/instantiate_msg.json index f40476d5d3..7f844a5062 100644 --- a/contracts/hackatom/schema/instantiate_msg.json +++ b/contracts/hackatom/schema/instantiate_msg.json @@ -8,14 +8,9 @@ ], "properties": { "beneficiary": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "verifier": { - "$ref": "#/definitions/HumanAddr" - } - }, - "definitions": { - "HumanAddr": { "type": "string" } } diff --git a/contracts/hackatom/schema/migrate_msg.json b/contracts/hackatom/schema/migrate_msg.json index a9878c23e3..dbdad8fec9 100644 --- a/contracts/hackatom/schema/migrate_msg.json +++ b/contracts/hackatom/schema/migrate_msg.json @@ -8,11 +8,6 @@ ], "properties": { "verifier": { - "$ref": "#/definitions/HumanAddr" - } - }, - "definitions": { - "HumanAddr": { "type": "string" } } diff --git a/contracts/hackatom/schema/query_msg.json b/contracts/hackatom/schema/query_msg.json index 6c3df170c2..a0b5791acf 100644 --- a/contracts/hackatom/schema/query_msg.json +++ b/contracts/hackatom/schema/query_msg.json @@ -29,7 +29,7 @@ ], "properties": { "address": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -65,10 +65,5 @@ }, "additionalProperties": false } - ], - "definitions": { - "HumanAddr": { - "type": "string" - } - } + ] } diff --git a/contracts/hackatom/schema/state.json b/contracts/hackatom/schema/state.json index 0b578b8e40..8462e9fb34 100644 --- a/contracts/hackatom/schema/state.json +++ b/contracts/hackatom/schema/state.json @@ -9,22 +9,19 @@ ], "properties": { "beneficiary": { - "$ref": "#/definitions/CanonicalAddr" + "$ref": "#/definitions/Addr" }, "funder": { - "$ref": "#/definitions/CanonicalAddr" + "$ref": "#/definitions/Addr" }, "verifier": { - "$ref": "#/definitions/CanonicalAddr" + "$ref": "#/definitions/Addr" } }, "definitions": { - "Binary": { - "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", "type": "string" - }, - "CanonicalAddr": { - "$ref": "#/definitions/Binary" } } } diff --git a/contracts/hackatom/schema/sudo_msg.json b/contracts/hackatom/schema/sudo_msg.json index 64fe612845..0b78b63422 100644 --- a/contracts/hackatom/schema/sudo_msg.json +++ b/contracts/hackatom/schema/sudo_msg.json @@ -23,7 +23,7 @@ } }, "recipient": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -47,9 +47,6 @@ } } }, - "HumanAddr": { - "type": "string" - }, "Uint128": { "type": "string" } diff --git a/contracts/hackatom/schema/verifier_response.json b/contracts/hackatom/schema/verifier_response.json index 530c0b02f0..8af8ef3684 100644 --- a/contracts/hackatom/schema/verifier_response.json +++ b/contracts/hackatom/schema/verifier_response.json @@ -7,11 +7,6 @@ ], "properties": { "verifier": { - "$ref": "#/definitions/HumanAddr" - } - }, - "definitions": { - "HumanAddr": { "type": "string" } } diff --git a/contracts/hackatom/src/contract.rs b/contracts/hackatom/src/contract.rs index ba7f9dbdaf..49b5a2bc8d 100644 --- a/contracts/hackatom/src/contract.rs +++ b/contracts/hackatom/src/contract.rs @@ -1,101 +1,16 @@ -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use cosmwasm_std::{ - entry_point, from_slice, to_binary, to_vec, AllBalanceResponse, Api, BankMsg, Binary, - CanonicalAddr, Coin, Deps, DepsMut, Env, HumanAddr, MessageInfo, QueryRequest, QueryResponse, - Response, StdError, StdResult, WasmQuery, + entry_point, from_slice, to_binary, to_vec, Addr, AllBalanceResponse, Api, BankMsg, + CanonicalAddr, Deps, DepsMut, Env, MessageInfo, QueryRequest, QueryResponse, Response, + StdError, StdResult, WasmQuery, }; use crate::errors::HackError; - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct InstantiateMsg { - pub verifier: HumanAddr, - pub beneficiary: HumanAddr, -} - -/// MigrateMsg allows a privileged contract administrator to run -/// a migration on the contract. In this (demo) case it is just migrating -/// from one hackatom code to the same code, but taking advantage of the -/// migration step to set a new validator. -/// -/// Note that the contract doesn't enforce permissions here, this is done -/// by blockchain logic (in the future by blockchain governance) -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct MigrateMsg { - pub verifier: HumanAddr, -} - -/// SudoMsg is only exposed for internal Cosmos SDK modules to call. -/// This is showing how we can expose "admin" functionality than can not be called by -/// external users or contracts, but only trusted (native/Go) code in the blockchain -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum SudoMsg { - StealFunds { - recipient: HumanAddr, - amount: Vec, - }, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct State { - pub verifier: CanonicalAddr, - pub beneficiary: CanonicalAddr, - pub funder: CanonicalAddr, -} - -// failure modes to help test wasmd, based on this comment -// https://github.com/cosmwasm/wasmd/issues/8#issuecomment-576146751 -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum ExecuteMsg { - /// Releasing all funds in the contract to the beneficiary. This is the only "proper" action of this demo contract. - Release {}, - /// Infinite loop to burn cpu cycles (only run when metering is enabled) - CpuLoop {}, - /// Infinite loop making storage calls (to test when their limit hits) - StorageLoop {}, - /// Infinite loop reading and writing memory - MemoryLoop {}, - /// Allocate large amounts of memory without consuming much gas - AllocateLargeMemory { pages: u32 }, - /// Trigger a panic to ensure framework handles gracefully - Panic {}, - /// Starting with CosmWasm 0.10, some API calls return user errors back to the contract. - /// This triggers such user errors, ensuring the transaction does not fail in the backend. - UserErrorsInApiCalls {}, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum QueryMsg { - /// returns a human-readable representation of the verifier - /// use to ensure query path works in integration tests - Verifier {}, - /// This returns cosmwasm_std::AllBalanceResponse to demo use of the querier - OtherBalance { address: HumanAddr }, - /// Recurse will execute a query into itself up to depth-times and return - /// Each step of the recursion may perform some extra work to test gas metering - /// (`work` rounds of sha256 on contract). - /// Now that we have Env, we can auto-calculate the address to recurse into - Recurse { depth: u32, work: u32 }, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct VerifierResponse { - pub verifier: HumanAddr, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct RecurseResponse { - /// hashed is the result of running sha256 "work+1" times on the contract's human address - pub hashed: Binary, -} - -pub const CONFIG_KEY: &[u8] = b"config"; +use crate::msg::{ + ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, RecurseResponse, SudoMsg, VerifierResponse, +}; +use crate::state::{State, CONFIG_KEY}; pub fn instantiate( deps: DepsMut, @@ -108,9 +23,9 @@ pub fn instantiate( deps.storage.set( CONFIG_KEY, &to_vec(&State { - verifier: deps.api.canonical_address(&msg.verifier)?, - beneficiary: deps.api.canonical_address(&msg.beneficiary)?, - funder: deps.api.canonical_address(&info.sender)?, + verifier: deps.api.addr_validate(&msg.verifier)?, + beneficiary: deps.api.addr_validate(&msg.beneficiary)?, + funder: info.sender, })?, ); @@ -126,7 +41,7 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result Result Result { fn do_user_errors_in_api_calls(api: &dyn Api) -> Result { // Canonicalize - let empty = HumanAddr::from(""); - match api.canonical_address(&empty).unwrap_err() { + let empty = ""; + match api.addr_canonicalize(empty).unwrap_err() { StdError::GenericErr { .. } => {} err => { return Err(StdError::generic_err(format!( @@ -258,8 +173,8 @@ fn do_user_errors_in_api_calls(api: &dyn Api) -> Result { } } - let invalid_bech32 = HumanAddr::from("bn93hg934hg08q340g8u4jcau3"); - match api.canonical_address(&invalid_bech32).unwrap_err() { + let invalid_bech32 = "bn93hg934hg08q340g8u4jcau3"; + match api.addr_canonicalize(invalid_bech32).unwrap_err() { StdError::GenericErr { .. } => {} err => { return Err(StdError::generic_err(format!( @@ -273,7 +188,7 @@ fn do_user_errors_in_api_calls(api: &dyn Api) -> Result { // Humanize let empty: CanonicalAddr = vec![].into(); - match api.human_address(&empty).unwrap_err() { + match api.addr_humanize(&empty).unwrap_err() { StdError::GenericErr { .. } => {} err => { return Err(StdError::generic_err(format!( @@ -285,7 +200,7 @@ fn do_user_errors_in_api_calls(api: &dyn Api) -> Result { } let too_short: CanonicalAddr = vec![0xAA, 0xBB, 0xCC].into(); - match api.human_address(&too_short).unwrap_err() { + match api.addr_humanize(&too_short).unwrap_err() { StdError::GenericErr { .. } => {} err => { return Err(StdError::generic_err(format!( @@ -297,7 +212,7 @@ fn do_user_errors_in_api_calls(api: &dyn Api) -> Result { } let wrong_length: CanonicalAddr = vec![0xA6; 17].into(); - match api.human_address(&wrong_length).unwrap_err() { + match api.addr_humanize(&wrong_length).unwrap_err() { StdError::GenericErr { .. } => {} err => { return Err(StdError::generic_err(format!( @@ -327,23 +242,19 @@ fn query_verifier(deps: Deps) -> StdResult { .get(CONFIG_KEY) .ok_or_else(|| StdError::not_found("State"))?; let state: State = from_slice(&data)?; - let addr = deps.api.human_address(&state.verifier)?; - Ok(VerifierResponse { verifier: addr }) + Ok(VerifierResponse { + verifier: state.verifier.into(), + }) } -fn query_other_balance(deps: Deps, address: HumanAddr) -> StdResult { +fn query_other_balance(deps: Deps, address: String) -> StdResult { let amount = deps.querier.query_all_balances(address)?; Ok(AllBalanceResponse { amount }) } -fn query_recurse( - deps: Deps, - depth: u32, - work: u32, - contract: HumanAddr, -) -> StdResult { +fn query_recurse(deps: Deps, depth: u32, work: u32, contract: Addr) -> StdResult { // perform all hashes as requested - let mut hashed: Vec = contract.as_str().as_bytes().to_vec(); + let mut hashed: Vec = contract.as_ref().into(); for _ in 0..work { hashed = Sha256::digest(&hashed).to_vec() } @@ -360,7 +271,7 @@ fn query_recurse( work, }; let query = QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: contract, + contract_addr: contract.into(), msg: to_binary(&req)?, }); deps.querier.query(&query) @@ -374,19 +285,19 @@ mod tests { mock_dependencies, mock_dependencies_with_balances, mock_env, mock_info, MOCK_CONTRACT_ADDR, }; // import trait Storage to get access to read - use cosmwasm_std::{attr, coins, Storage}; + use cosmwasm_std::{attr, coins, Binary, Storage}; #[test] fn proper_initialization() { let mut deps = mock_dependencies(&[]); - let verifier = HumanAddr(String::from("verifies")); - let beneficiary = HumanAddr(String::from("benefits")); - let creator = HumanAddr(String::from("creator")); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); + let creator = String::from("creator"); let expected_state = State { - verifier: deps.api.canonical_address(&verifier).unwrap(), - beneficiary: deps.api.canonical_address(&beneficiary).unwrap(), - funder: deps.api.canonical_address(&creator).unwrap(), + verifier: deps.api.addr_validate(&verifier).unwrap(), + beneficiary: deps.api.addr_validate(&beneficiary).unwrap(), + funder: deps.api.addr_validate(&creator).unwrap(), }; let msg = InstantiateMsg { @@ -410,14 +321,14 @@ mod tests { fn instantiate_and_query() { let mut deps = mock_dependencies(&[]); - let verifier = HumanAddr(String::from("verifies")); - let beneficiary = HumanAddr(String::from("benefits")); - let creator = HumanAddr(String::from("creator")); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); + let creator = String::from("creator"); let msg = InstantiateMsg { verifier: verifier.clone(), beneficiary, }; - let info = mock_info(creator.as_str(), &[]); + let info = mock_info(&creator, &[]); let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); assert_eq!(0, res.messages.len()); @@ -430,14 +341,14 @@ mod tests { fn migrate_verifier() { let mut deps = mock_dependencies(&[]); - let verifier = HumanAddr::from("verifies"); - let beneficiary = HumanAddr::from("benefits"); - let creator = HumanAddr::from("creator"); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); + let creator = String::from("creator"); let msg = InstantiateMsg { verifier, beneficiary, }; - let info = mock_info(creator.as_str(), &[]); + let info = mock_info(&creator, &[]); let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); assert_eq!(0, res.messages.len()); @@ -446,7 +357,7 @@ mod tests { assert_eq!(query_response.as_slice(), b"{\"verifier\":\"verifies\"}"); // change the verifier via migrate - let new_verifier = HumanAddr::from("someone else"); + let new_verifier = String::from("someone else"); let msg = MigrateMsg { verifier: new_verifier.clone(), }; @@ -462,19 +373,19 @@ mod tests { fn sudo_can_steal_tokens() { let mut deps = mock_dependencies(&[]); - let verifier = HumanAddr::from("verifies"); - let beneficiary = HumanAddr::from("benefits"); - let creator = HumanAddr::from("creator"); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); + let creator = String::from("creator"); let msg = InstantiateMsg { verifier, beneficiary, }; - let info = mock_info(creator.as_str(), &[]); + let info = mock_info(&creator, &[]); let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); assert_eq!(0, res.messages.len()); // sudo takes any tax it wants - let to_address = HumanAddr::from("community-pool"); + let to_address = String::from("community-pool"); let amount = coins(700, "gold"); let sys_msg = SudoMsg::StealFunds { recipient: to_address.clone(), @@ -488,7 +399,7 @@ mod tests { #[test] fn querier_callbacks_work() { - let rich_addr = HumanAddr::from("foobar"); + let rich_addr = String::from("foobar"); let rich_balance = coins(10000, "gold"); let deps = mock_dependencies_with_balances(&[(&rich_addr, &rich_balance)]); @@ -497,7 +408,7 @@ mod tests { assert_eq!(bal.amount, rich_balance); // querying other accounts gets none - let bal = query_other_balance(deps.as_ref(), HumanAddr::from("someone else")).unwrap(); + let bal = query_other_balance(deps.as_ref(), String::from("someone else")).unwrap(); assert_eq!(bal.amount, vec![]); } @@ -506,16 +417,16 @@ mod tests { let mut deps = mock_dependencies(&[]); // initialize the store - let creator = HumanAddr::from("creator"); - let verifier = HumanAddr::from("verifies"); - let beneficiary = HumanAddr::from("benefits"); + let creator = String::from("creator"); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); let instantiate_msg = InstantiateMsg { verifier: verifier.clone(), beneficiary: beneficiary.clone(), }; let init_amount = coins(1000, "earth"); - let init_info = mock_info(creator.as_str(), &init_amount); + let init_info = mock_info(&creator, &init_amount); let init_res = instantiate(deps.as_mut(), mock_env(), init_info, instantiate_msg).unwrap(); assert_eq!(init_res.messages.len(), 0); @@ -553,16 +464,16 @@ mod tests { let mut deps = mock_dependencies(&[]); // initialize the store - let creator = HumanAddr::from("creator"); - let verifier = HumanAddr::from("verifies"); - let beneficiary = HumanAddr::from("benefits"); + let creator = String::from("creator"); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); let instantiate_msg = InstantiateMsg { verifier: verifier.clone(), beneficiary: beneficiary.clone(), }; let init_amount = coins(1000, "earth"); - let init_info = mock_info(creator.as_str(), &init_amount); + let init_info = mock_info(&creator, &init_amount); let init_res = instantiate(deps.as_mut(), mock_env(), init_info, instantiate_msg).unwrap(); assert_eq!(init_res.messages.len(), 0); @@ -585,9 +496,9 @@ mod tests { assert_eq!( state, State { - verifier: deps.api.canonical_address(&verifier).unwrap(), - beneficiary: deps.api.canonical_address(&beneficiary).unwrap(), - funder: deps.api.canonical_address(&creator).unwrap(), + verifier: Addr::unchecked(verifier), + beneficiary: Addr::unchecked(beneficiary), + funder: Addr::unchecked(creator), } ); } @@ -598,19 +509,19 @@ mod tests { let mut deps = mock_dependencies(&[]); // initialize the store - let verifier = HumanAddr(String::from("verifies")); - let beneficiary = HumanAddr(String::from("benefits")); - let creator = HumanAddr(String::from("creator")); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); + let creator = String::from("creator"); let instantiate_msg = InstantiateMsg { verifier, beneficiary: beneficiary.clone(), }; - let init_info = mock_info(creator.as_str(), &coins(1000, "earth")); + let init_info = mock_info(&creator, &coins(1000, "earth")); let init_res = instantiate(deps.as_mut(), mock_env(), init_info, instantiate_msg).unwrap(); assert_eq!(0, init_res.messages.len()); - let execute_info = mock_info(beneficiary.as_str(), &[]); + let execute_info = mock_info(&beneficiary, &[]); // this should panic let _ = execute( deps.as_mut(), @@ -625,8 +536,8 @@ mod tests { let mut deps = mock_dependencies(&[]); let instantiate_msg = InstantiateMsg { - verifier: HumanAddr::from("verifies"), - beneficiary: HumanAddr::from("benefits"), + verifier: String::from("verifies"), + beneficiary: String::from("benefits"), }; let init_info = mock_info("creator", &coins(1000, "earth")); let init_res = instantiate(deps.as_mut(), mock_env(), init_info, instantiate_msg).unwrap(); @@ -648,7 +559,7 @@ mod tests { // let's just make sure the last step looks right let deps = mock_dependencies(&[]); - let contract = HumanAddr::from("my-contract"); + let contract = Addr::unchecked("my-contract"); let bin_contract: &[u8] = b"my-contract"; // return the unhashed value here diff --git a/contracts/hackatom/src/lib.rs b/contracts/hackatom/src/lib.rs index 7acafa4e11..1e46dd6144 100644 --- a/contracts/hackatom/src/lib.rs +++ b/contracts/hackatom/src/lib.rs @@ -1,5 +1,7 @@ pub mod contract; mod errors; +pub mod msg; +pub mod state; #[cfg(target_arch = "wasm32")] cosmwasm_std::create_entry_points_with_migration!(contract); diff --git a/contracts/hackatom/src/msg.rs b/contracts/hackatom/src/msg.rs new file mode 100644 index 0000000000..6b51d1c853 --- /dev/null +++ b/contracts/hackatom/src/msg.rs @@ -0,0 +1,82 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use cosmwasm_std::{Binary, Coin}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct InstantiateMsg { + pub verifier: String, + pub beneficiary: String, +} + +/// MigrateMsg allows a privileged contract administrator to run +/// a migration on the contract. In this (demo) case it is just migrating +/// from one hackatom code to the same code, but taking advantage of the +/// migration step to set a new validator. +/// +/// Note that the contract doesn't enforce permissions here, this is done +/// by blockchain logic (in the future by blockchain governance) +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct MigrateMsg { + pub verifier: String, +} + +/// SudoMsg is only exposed for internal Cosmos SDK modules to call. +/// This is showing how we can expose "admin" functionality than can not be called by +/// external users or contracts, but only trusted (native/Go) code in the blockchain +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum SudoMsg { + StealFunds { + recipient: String, + amount: Vec, + }, +} + +// failure modes to help test wasmd, based on this comment +// https://github.com/cosmwasm/wasmd/issues/8#issuecomment-576146751 +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ExecuteMsg { + /// Releasing all funds in the contract to the beneficiary. This is the only "proper" action of this demo contract. + Release {}, + /// Infinite loop to burn cpu cycles (only run when metering is enabled) + CpuLoop {}, + /// Infinite loop making storage calls (to test when their limit hits) + StorageLoop {}, + /// Infinite loop reading and writing memory + MemoryLoop {}, + /// Allocate large amounts of memory without consuming much gas + AllocateLargeMemory { pages: u32 }, + /// Trigger a panic to ensure framework handles gracefully + Panic {}, + /// Starting with CosmWasm 0.10, some API calls return user errors back to the contract. + /// This triggers such user errors, ensuring the transaction does not fail in the backend. + UserErrorsInApiCalls {}, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum QueryMsg { + /// returns a human-readable representation of the verifier + /// use to ensure query path works in integration tests + Verifier {}, + /// This returns cosmwasm_std::AllBalanceResponse to demo use of the querier + OtherBalance { address: String }, + /// Recurse will execute a query into itself up to depth-times and return + /// Each step of the recursion may perform some extra work to test gas metering + /// (`work` rounds of sha256 on contract). + /// Now that we have Env, we can auto-calculate the address to recurse into + Recurse { depth: u32, work: u32 }, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct VerifierResponse { + pub verifier: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct RecurseResponse { + /// hashed is the result of running sha256 "work+1" times on the contract's human address + pub hashed: Binary, +} diff --git a/contracts/hackatom/src/state.rs b/contracts/hackatom/src/state.rs new file mode 100644 index 0000000000..b1e8196bee --- /dev/null +++ b/contracts/hackatom/src/state.rs @@ -0,0 +1,13 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use cosmwasm_std::Addr; + +pub const CONFIG_KEY: &[u8] = b"config"; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct State { + pub verifier: Addr, + pub beneficiary: Addr, + pub funder: Addr, +} diff --git a/contracts/hackatom/tests/integration.rs b/contracts/hackatom/tests/integration.rs index 62d42408b2..64a353a421 100644 --- a/contracts/hackatom/tests/integration.rs +++ b/contracts/hackatom/tests/integration.rs @@ -18,8 +18,8 @@ //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) use cosmwasm_std::{ - attr, coins, from_binary, to_vec, AllBalanceResponse, BankMsg, Binary, ContractResult, Empty, - HumanAddr, Response, + attr, coins, from_binary, to_vec, Addr, AllBalanceResponse, BankMsg, Binary, ContractResult, + Empty, Response, }; use cosmwasm_vm::{ call_execute, from_slice, @@ -27,19 +27,18 @@ use cosmwasm_vm::{ execute, instantiate, migrate, mock_env, mock_info, mock_instance, mock_instance_with_balances, query, sudo, test_io, MOCK_CONTRACT_ADDR, }, - BackendApi, Storage, VmError, + Storage, VmError, }; -use hackatom::contract::{ - ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, State, SudoMsg, CONFIG_KEY, -}; +use hackatom::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, SudoMsg}; +use hackatom::state::{State, CONFIG_KEY}; static WASM: &[u8] = include_bytes!("../target/wasm32-unknown-unknown/release/hackatom.wasm"); -fn make_init_msg() -> (InstantiateMsg, HumanAddr) { - let verifier = HumanAddr::from("verifies"); - let beneficiary = HumanAddr::from("benefits"); - let creator = HumanAddr::from("creator"); +fn make_init_msg() -> (InstantiateMsg, String) { + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); + let creator = String::from("creator"); ( InstantiateMsg { verifier, @@ -54,20 +53,20 @@ fn proper_initialization() { let mut deps = mock_instance(WASM, &[]); assert_eq!(deps.required_features.len(), 0); - let verifier = HumanAddr(String::from("verifies")); - let beneficiary = HumanAddr(String::from("benefits")); - let creator = HumanAddr(String::from("creator")); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); + let creator = String::from("creator"); let expected_state = State { - verifier: deps.api().canonical_address(&verifier).0.unwrap(), - beneficiary: deps.api().canonical_address(&beneficiary).0.unwrap(), - funder: deps.api().canonical_address(&creator).0.unwrap(), + verifier: Addr::unchecked(&verifier), + beneficiary: Addr::unchecked(&beneficiary), + funder: Addr::unchecked(&creator), }; let msg = InstantiateMsg { verifier, beneficiary, }; - let info = mock_info("creator", &coins(1000, "earth")); + let info = mock_info(&creator, &coins(1000, "earth")); let res: Response = instantiate(&mut deps, mock_env(), info, msg).unwrap(); assert_eq!(res.messages.len(), 0); assert_eq!(res.attributes.len(), 1); @@ -92,14 +91,14 @@ fn proper_initialization() { fn instantiate_and_query() { let mut deps = mock_instance(WASM, &[]); - let verifier = HumanAddr(String::from("verifies")); - let beneficiary = HumanAddr(String::from("benefits")); - let creator = HumanAddr(String::from("creator")); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); + let creator = String::from("creator"); let msg = InstantiateMsg { verifier, beneficiary, }; - let info = mock_info(creator.as_str(), &coins(1000, "earth")); + let info = mock_info(&creator, &coins(1000, "earth")); let res: Response = instantiate(&mut deps, mock_env(), info, msg).unwrap(); assert_eq!(0, res.messages.len()); @@ -117,14 +116,14 @@ fn instantiate_and_query() { fn migrate_verifier() { let mut deps = mock_instance(WASM, &[]); - let verifier = HumanAddr::from("verifies"); - let beneficiary = HumanAddr::from("benefits"); - let creator = HumanAddr::from("creator"); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); + let creator = String::from("creator"); let msg = InstantiateMsg { verifier, beneficiary, }; - let info = mock_info(creator.as_str(), &[]); + let info = mock_info(&creator, &[]); let res: Response = instantiate(&mut deps, mock_env(), info, msg).unwrap(); assert_eq!(0, res.messages.len()); @@ -134,7 +133,7 @@ fn migrate_verifier() { // change the verifier via migrate let msg = MigrateMsg { - verifier: HumanAddr::from("someone else"), + verifier: String::from("someone else"), }; let res: Response = migrate(&mut deps, mock_env(), msg).unwrap(); assert_eq!(0, res.messages.len()); @@ -151,19 +150,19 @@ fn migrate_verifier() { fn sudo_can_steal_tokens() { let mut deps = mock_instance(WASM, &[]); - let verifier = HumanAddr::from("verifies"); - let beneficiary = HumanAddr::from("benefits"); - let creator = HumanAddr::from("creator"); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); + let creator = String::from("creator"); let msg = InstantiateMsg { verifier, beneficiary, }; - let info = mock_info(creator.as_str(), &[]); + let info = mock_info(&creator, &[]); let res: Response = instantiate(&mut deps, mock_env(), info, msg).unwrap(); assert_eq!(0, res.messages.len()); // sudo takes any tax it wants - let to_address = HumanAddr::from("community-pool"); + let to_address = String::from("community-pool"); let amount = coins(700, "gold"); let sys_msg = SudoMsg::StealFunds { recipient: to_address.clone(), @@ -177,7 +176,7 @@ fn sudo_can_steal_tokens() { #[test] fn querier_callbacks_work() { - let rich_addr = HumanAddr::from("foobar"); + let rich_addr = String::from("foobar"); let rich_balance = coins(10000, "gold"); let mut deps = mock_instance_with_balances(WASM, &[(&rich_addr, &rich_balance)]); @@ -189,7 +188,7 @@ fn querier_callbacks_work() { // querying other accounts gets none let query_msg = QueryMsg::OtherBalance { - address: HumanAddr::from("someone else"), + address: String::from("someone else"), }; let query_response = query(&mut deps, mock_env(), query_msg).unwrap(); let bal: AllBalanceResponse = from_binary(&query_response).unwrap(); @@ -212,16 +211,16 @@ fn execute_release_works() { let mut deps = mock_instance(WASM, &[]); // initialize the store - let creator = HumanAddr::from("creator"); - let verifier = HumanAddr::from("verifies"); - let beneficiary = HumanAddr::from("benefits"); + let creator = String::from("creator"); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); let instantiate_msg = InstantiateMsg { verifier: verifier.clone(), beneficiary: beneficiary.clone(), }; let init_amount = coins(1000, "earth"); - let init_info = mock_info(creator.as_str(), &init_amount); + let init_info = mock_info(&creator, &init_amount); let init_res: Response = instantiate(&mut deps, mock_env(), init_info, instantiate_msg).unwrap(); assert_eq!(init_res.messages.len(), 0); @@ -234,7 +233,7 @@ fn execute_release_works() { .unwrap(); // beneficiary can release it - let execute_info = mock_info(verifier.as_str(), &[]); + let execute_info = mock_info(&verifier, &[]); let execute_res: Response = execute(&mut deps, mock_env(), execute_info, ExecuteMsg::Release {}).unwrap(); assert_eq!(execute_res.messages.len(), 1); @@ -259,16 +258,16 @@ fn execute_release_fails_for_wrong_sender() { let mut deps = mock_instance(WASM, &[]); // initialize the store - let creator = HumanAddr::from("creator"); - let verifier = HumanAddr::from("verifies"); - let beneficiary = HumanAddr::from("benefits"); + let creator = String::from("creator"); + let verifier = String::from("verifies"); + let beneficiary = String::from("benefits"); let instantiate_msg = InstantiateMsg { verifier: verifier.clone(), beneficiary: beneficiary.clone(), }; let init_amount = coins(1000, "earth"); - let init_info = mock_info(creator.as_str(), &init_amount); + let init_info = mock_info(&creator, &init_amount); let init_res: Response = instantiate(&mut deps, mock_env(), init_info, instantiate_msg).unwrap(); assert_eq!(init_res.messages.len(), 0); @@ -281,7 +280,7 @@ fn execute_release_fails_for_wrong_sender() { .unwrap(); // beneficiary cannot release it - let execute_info = mock_info(beneficiary.as_str(), &[]); + let execute_info = mock_info(&beneficiary, &[]); let execute_res: ContractResult = execute(&mut deps, mock_env(), execute_info, ExecuteMsg::Release {}); let msg = execute_res.unwrap_err(); @@ -301,9 +300,9 @@ fn execute_release_fails_for_wrong_sender() { assert_eq!( state, State { - verifier: deps.api().canonical_address(&verifier).0.unwrap(), - beneficiary: deps.api().canonical_address(&beneficiary).0.unwrap(), - funder: deps.api().canonical_address(&creator).0.unwrap(), + verifier: Addr::unchecked(&verifier), + beneficiary: Addr::unchecked(&beneficiary), + funder: Addr::unchecked(&creator), } ); } diff --git a/contracts/ibc-reflect-send/schema/account_response.json b/contracts/ibc-reflect-send/schema/account_response.json index a30a7b2787..90faddfa1a 100644 --- a/contracts/ibc-reflect-send/schema/account_response.json +++ b/contracts/ibc-reflect-send/schema/account_response.json @@ -15,13 +15,9 @@ }, "remote_addr": { "description": "in normal cases, it should be set, but there is a delay between binding the channel and making a query and in that time it is empty", - "anyOf": [ - { - "$ref": "#/definitions/HumanAddr" - }, - { - "type": "null" - } + "type": [ + "string", + "null" ] }, "remote_balance": { @@ -47,9 +43,6 @@ } } }, - "HumanAddr": { - "type": "string" - }, "Uint128": { "type": "string" } diff --git a/contracts/ibc-reflect-send/schema/admin_response.json b/contracts/ibc-reflect-send/schema/admin_response.json index af7a896be9..b0d3272d06 100644 --- a/contracts/ibc-reflect-send/schema/admin_response.json +++ b/contracts/ibc-reflect-send/schema/admin_response.json @@ -7,11 +7,6 @@ ], "properties": { "admin": { - "$ref": "#/definitions/HumanAddr" - } - }, - "definitions": { - "HumanAddr": { "type": "string" } } diff --git a/contracts/ibc-reflect-send/schema/execute_msg.json b/contracts/ibc-reflect-send/schema/execute_msg.json index bb22dd5b23..dfc8c5988d 100644 --- a/contracts/ibc-reflect-send/schema/execute_msg.json +++ b/contracts/ibc-reflect-send/schema/execute_msg.json @@ -16,7 +16,7 @@ ], "properties": { "admin": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -123,7 +123,7 @@ } }, "to_address": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -244,9 +244,6 @@ "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", "type": "object" }, - "HumanAddr": { - "type": "string" - }, "IbcMsg": { "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", "anyOf": [ @@ -299,11 +296,7 @@ }, "to_address": { "description": "address on the remote chain to receive these tokens", - "allOf": [ - { - "$ref": "#/definitions/HumanAddr" - } - ] + "type": "string" } } } @@ -421,7 +414,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -446,7 +439,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -468,17 +461,13 @@ "properties": { "recipient": { "description": "this is the \"withdraw address\", the one that should receive the rewards if None, then use delegator address", - "anyOf": [ - { - "$ref": "#/definitions/HumanAddr" - }, - { - "type": "null" - } + "type": [ + "string", + "null" ] }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -504,10 +493,10 @@ "$ref": "#/definitions/Coin" }, "dst_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "src_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -538,7 +527,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", @@ -619,7 +608,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", diff --git a/contracts/ibc-reflect-send/schema/list_accounts_response.json b/contracts/ibc-reflect-send/schema/list_accounts_response.json index 3d99c0f11d..a229e91f38 100644 --- a/contracts/ibc-reflect-send/schema/list_accounts_response.json +++ b/contracts/ibc-reflect-send/schema/list_accounts_response.json @@ -33,13 +33,9 @@ }, "remote_addr": { "description": "in normal cases, it should be set, but there is a delay between binding the channel and making a query and in that time it is empty", - "anyOf": [ - { - "$ref": "#/definitions/HumanAddr" - }, - { - "type": "null" - } + "type": [ + "string", + "null" ] }, "remote_balance": { @@ -65,9 +61,6 @@ } } }, - "HumanAddr": { - "type": "string" - }, "Uint128": { "type": "string" } diff --git a/contracts/ibc-reflect-send/schema/packet_msg.json b/contracts/ibc-reflect-send/schema/packet_msg.json index f83b977942..167f9f556d 100644 --- a/contracts/ibc-reflect-send/schema/packet_msg.json +++ b/contracts/ibc-reflect-send/schema/packet_msg.json @@ -76,7 +76,7 @@ } }, "to_address": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -197,9 +197,6 @@ "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", "type": "object" }, - "HumanAddr": { - "type": "string" - }, "IbcMsg": { "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", "anyOf": [ @@ -252,11 +249,7 @@ }, "to_address": { "description": "address on the remote chain to receive these tokens", - "allOf": [ - { - "$ref": "#/definitions/HumanAddr" - } - ] + "type": "string" } } } @@ -374,7 +367,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -399,7 +392,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -421,17 +414,13 @@ "properties": { "recipient": { "description": "this is the \"withdraw address\", the one that should receive the rewards if None, then use delegator address", - "anyOf": [ - { - "$ref": "#/definitions/HumanAddr" - }, - { - "type": "null" - } + "type": [ + "string", + "null" ] }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -457,10 +446,10 @@ "$ref": "#/definitions/Coin" }, "dst_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "src_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -491,7 +480,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", @@ -572,7 +561,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", diff --git a/contracts/ibc-reflect-send/src/contract.rs b/contracts/ibc-reflect-send/src/contract.rs index 4951c915b6..b85e08fb27 100644 --- a/contracts/ibc-reflect-send/src/contract.rs +++ b/contracts/ibc-reflect-send/src/contract.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ - attr, entry_point, to_binary, CosmosMsg, Deps, DepsMut, Env, HumanAddr, IbcMsg, MessageInfo, - Order, QueryResponse, Response, StdError, StdResult, + attr, entry_point, to_binary, CosmosMsg, Deps, DepsMut, Env, IbcMsg, MessageInfo, Order, + QueryResponse, Response, StdError, StdResult, }; use crate::ibc::build_timeout_timestamp; @@ -50,14 +50,14 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S pub fn handle_update_admin( deps: DepsMut, info: MessageInfo, - new_admin: HumanAddr, + new_admin: String, ) -> StdResult { // auth check let mut cfg = config(deps.storage).load()?; if info.sender != cfg.admin { return Err(StdError::generic_err("Only admin may set new admin")); } - cfg.admin = new_admin; + cfg.admin = deps.api.addr_validate(&new_admin)?; config(deps.storage).save(&cfg)?; Ok(Response { @@ -215,7 +215,9 @@ fn query_list_accounts(deps: Deps) -> StdResult { fn query_admin(deps: Deps) -> StdResult { let Config { admin } = config_read(deps.storage).load()?; - Ok(AdminResponse { admin }) + Ok(AdminResponse { + admin: admin.into(), + }) } #[cfg(test)] diff --git a/contracts/ibc-reflect-send/src/ibc.rs b/contracts/ibc-reflect-send/src/ibc.rs index c9acecf27f..b15b96387d 100644 --- a/contracts/ibc-reflect-send/src/ibc.rs +++ b/contracts/ibc-reflect-send/src/ibc.rs @@ -160,7 +160,7 @@ fn acknowledge_who_am_i( ack: AcknowledgementMsg, ) -> StdResult { // ignore errors (but mention in log) - let res: WhoAmIResponse = match ack { + let WhoAmIResponse { account } = match ack { AcknowledgementMsg::Ok(res) => res, AcknowledgementMsg::Err(e) => { return Ok(IbcBasicResponse { @@ -176,7 +176,7 @@ fn acknowledge_who_am_i( Some(mut acct) => { // set the account the first time if acct.remote_addr.is_none() { - acct.remote_addr = Some(res.account); + acct.remote_addr = Some(account); } Ok(acct) } @@ -199,7 +199,7 @@ fn acknowledge_balances( ack: AcknowledgementMsg, ) -> StdResult { // ignore errors (but mention in log) - let res: BalancesResponse = match ack { + let BalancesResponse { account, balances } = match ack { AcknowledgementMsg::Ok(res) => res, AcknowledgementMsg::Err(e) => { return Ok(IbcBasicResponse { @@ -213,18 +213,18 @@ fn acknowledge_balances( accounts(deps.storage).update(caller.as_bytes(), |acct| -> StdResult<_> { match acct { Some(acct) => { - if let Some(old_addr) = &acct.remote_addr { - if old_addr != &res.account { + if let Some(old_addr) = acct.remote_addr { + if old_addr != account { return Err(StdError::generic_err(format!( "remote account changed from {} to {}", - old_addr, &res.account + old_addr, account ))); } } Ok(AccountData { last_update_time: env.block.time, - remote_addr: Some(res.account), - remote_balance: res.balances, + remote_addr: Some(account), + remote_balance: balances, }) } None => Err(StdError::generic_err("no account to update")), @@ -262,7 +262,7 @@ mod tests { mock_dependencies, mock_env, mock_ibc_channel, mock_ibc_packet_ack, mock_info, MockApi, MockQuerier, MockStorage, }; - use cosmwasm_std::{coin, coins, BankMsg, CosmosMsg, HumanAddr, OwnedDeps}; + use cosmwasm_std::{coin, coins, BankMsg, CosmosMsg, OwnedDeps}; const CREATOR: &str = "creator"; @@ -299,7 +299,7 @@ mod tests { }; } - fn who_am_i_response>(deps: DepsMut, channel_id: &str, account: T) { + fn who_am_i_response>(deps: DepsMut, channel_id: &str, account: T) { let packet = PacketMsg::WhoAmI {}; let response = AcknowledgementMsg::Ok(WhoAmIResponse { account: account.into(), @@ -353,7 +353,7 @@ mod tests { }; let r = query(deps.as_ref(), mock_env(), q).unwrap(); let acct: AccountResponse = from_slice(&r).unwrap(); - assert_eq!(acct.remote_addr.unwrap(), HumanAddr::from(remote_addr)); + assert_eq!(acct.remote_addr.unwrap(), remote_addr); assert!(acct.remote_balance.is_empty()); assert_eq!(0, acct.last_update_time); } diff --git a/contracts/ibc-reflect-send/src/ibc_msg.rs b/contracts/ibc-reflect-send/src/ibc_msg.rs index 6a436be752..94c55fdacc 100644 --- a/contracts/ibc-reflect-send/src/ibc_msg.rs +++ b/contracts/ibc-reflect-send/src/ibc_msg.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Coin, ContractResult, CosmosMsg, HumanAddr}; +use cosmwasm_std::{Coin, ContractResult, CosmosMsg}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -23,13 +23,13 @@ pub type DispatchResponse = (); /// Return the caller's account address on the remote chain #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct WhoAmIResponse { - pub account: HumanAddr, + pub account: String, } /// This is the success response we send on ack for PacketMsg::Balance. /// Just acknowledge success or error #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct BalancesResponse { - pub account: HumanAddr, + pub account: String, pub balances: Vec, } diff --git a/contracts/ibc-reflect-send/src/msg.rs b/contracts/ibc-reflect-send/src/msg.rs index e61491bb65..2bc47c4309 100644 --- a/contracts/ibc-reflect-send/src/msg.rs +++ b/contracts/ibc-reflect-send/src/msg.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Coin, CosmosMsg, Empty, HumanAddr}; +use cosmwasm_std::{Coin, CosmosMsg, Empty}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -14,7 +14,7 @@ pub struct InstantiateMsg {} pub enum ExecuteMsg { /// Changes the admin UpdateAdmin { - admin: HumanAddr, + admin: String, }, SendMsgs { channel_id: String, @@ -50,7 +50,7 @@ pub enum QueryMsg { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct AdminResponse { - pub admin: HumanAddr, + pub admin: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -65,7 +65,7 @@ pub struct AccountInfo { pub last_update_time: u64, /// in normal cases, it should be set, but there is a delay between binding /// the channel and making a query and in that time it is empty - pub remote_addr: Option, + pub remote_addr: Option, pub remote_balance: Vec, } @@ -86,7 +86,7 @@ pub struct AccountResponse { pub last_update_time: u64, /// in normal cases, it should be set, but there is a delay between binding /// the channel and making a query and in that time it is empty - pub remote_addr: Option, + pub remote_addr: Option, pub remote_balance: Vec, } diff --git a/contracts/ibc-reflect-send/src/state.rs b/contracts/ibc-reflect-send/src/state.rs index da687edef4..7e8e5e165e 100644 --- a/contracts/ibc-reflect-send/src/state.rs +++ b/contracts/ibc-reflect-send/src/state.rs @@ -1,7 +1,6 @@ -use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{Coin, HumanAddr, Storage}; +use cosmwasm_std::{Addr, Coin, Storage}; use cosmwasm_storage::{ bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton, Singleton, @@ -10,18 +9,21 @@ use cosmwasm_storage::{ pub const KEY_CONFIG: &[u8] = b"config"; pub const PREFIX_ACCOUNTS: &[u8] = b"accounts"; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Config { - pub admin: HumanAddr, + pub admin: Addr, } -#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq)] pub struct AccountData { /// last block balance was updated (0 is never) pub last_update_time: u64, - /// in normal cases, it should be set, but there is a delay between binding - /// the channel and making a query and in that time it is empty - pub remote_addr: Option, + /// In normal cases, it should be set, but there is a delay between binding + /// the channel and making a query and in that time it is empty. + /// + /// Since we do not have a way to validate the remote address format, this + /// must not be of type `Addr`. + pub remote_addr: Option, pub remote_balance: Vec, } diff --git a/contracts/ibc-reflect-send/tests/integration.rs b/contracts/ibc-reflect-send/tests/integration.rs index 0193a82d2e..a0a375c008 100644 --- a/contracts/ibc-reflect-send/tests/integration.rs +++ b/contracts/ibc-reflect-send/tests/integration.rs @@ -19,8 +19,8 @@ use cosmwasm_std::testing::{mock_ibc_channel, mock_ibc_packet_ack}; use cosmwasm_std::{ - attr, coin, coins, to_binary, BankMsg, CosmosMsg, Empty, HumanAddr, IbcAcknowledgement, - IbcBasicResponse, IbcMsg, IbcOrder, Response, + attr, coin, coins, to_binary, BankMsg, CosmosMsg, Empty, IbcAcknowledgement, IbcBasicResponse, + IbcMsg, IbcOrder, Response, }; use cosmwasm_vm::testing::{ execute, ibc_channel_connect, ibc_channel_open, ibc_packet_ack, instantiate, mock_env, @@ -71,7 +71,7 @@ fn connect(deps: &mut Instance, channel_id: & }; } -fn who_am_i_response>( +fn who_am_i_response>( deps: &mut Instance, channel_id: &str, account: T, @@ -140,7 +140,7 @@ fn proper_handshake_flow() { // account should be set up let acct = get_account(&mut deps, channel_id); - assert_eq!(acct.remote_addr.unwrap(), HumanAddr::from(remote_addr)); + assert_eq!(acct.remote_addr.unwrap(), remote_addr); assert!(acct.remote_balance.is_empty()); assert_eq!(0, acct.last_update_time); } diff --git a/contracts/ibc-reflect/schema/acknowledgement_msg_balances.json b/contracts/ibc-reflect/schema/acknowledgement_msg_balances.json index d7e06ce0f9..198e96e575 100644 --- a/contracts/ibc-reflect/schema/acknowledgement_msg_balances.json +++ b/contracts/ibc-reflect/schema/acknowledgement_msg_balances.json @@ -39,7 +39,7 @@ ], "properties": { "account": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "balances": { "type": "array", @@ -64,9 +64,6 @@ } } }, - "HumanAddr": { - "type": "string" - }, "Uint128": { "type": "string" } diff --git a/contracts/ibc-reflect/schema/acknowledgement_msg_who_am_i.json b/contracts/ibc-reflect/schema/acknowledgement_msg_who_am_i.json index 5718ad1197..ded83faf45 100644 --- a/contracts/ibc-reflect/schema/acknowledgement_msg_who_am_i.json +++ b/contracts/ibc-reflect/schema/acknowledgement_msg_who_am_i.json @@ -30,9 +30,6 @@ } ], "definitions": { - "HumanAddr": { - "type": "string" - }, "WhoAmIResponse": { "description": "This is the success response we send on ack for PacketMsg::WhoAmI. Return the caller's account address on the remote chain", "type": "object", @@ -41,7 +38,7 @@ ], "properties": { "account": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } diff --git a/contracts/ibc-reflect/schema/execute_msg.json b/contracts/ibc-reflect/schema/execute_msg.json index f97134ed82..8fb4672447 100644 --- a/contracts/ibc-reflect/schema/execute_msg.json +++ b/contracts/ibc-reflect/schema/execute_msg.json @@ -18,11 +18,7 @@ "properties": { "contract_addr": { "description": "contract_addr is the address of this contract", - "allOf": [ - { - "$ref": "#/definitions/HumanAddr" - } - ] + "type": "string" }, "id": { "description": "id was provided in the InitMsg", @@ -33,10 +29,5 @@ }, "additionalProperties": false } - ], - "definitions": { - "HumanAddr": { - "type": "string" - } - } + ] } diff --git a/contracts/ibc-reflect/schema/packet_msg.json b/contracts/ibc-reflect/schema/packet_msg.json index d7e478df9c..9fc4c1890c 100644 --- a/contracts/ibc-reflect/schema/packet_msg.json +++ b/contracts/ibc-reflect/schema/packet_msg.json @@ -75,7 +75,7 @@ } }, "to_address": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -196,9 +196,6 @@ "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", "type": "object" }, - "HumanAddr": { - "type": "string" - }, "IbcMsg": { "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", "anyOf": [ @@ -251,11 +248,7 @@ }, "to_address": { "description": "address on the remote chain to receive these tokens", - "allOf": [ - { - "$ref": "#/definitions/HumanAddr" - } - ] + "type": "string" } } } @@ -373,7 +366,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -398,7 +391,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -420,17 +413,13 @@ "properties": { "recipient": { "description": "this is the \"withdraw address\", the one that should receive the rewards if None, then use delegator address", - "anyOf": [ - { - "$ref": "#/definitions/HumanAddr" - }, - { - "type": "null" - } + "type": [ + "string", + "null" ] }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -456,10 +445,10 @@ "$ref": "#/definitions/Coin" }, "dst_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "src_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -490,7 +479,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", @@ -571,7 +560,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", diff --git a/contracts/ibc-reflect/src/contract.rs b/contracts/ibc-reflect/src/contract.rs index 70b770b555..33c9727e03 100644 --- a/contracts/ibc-reflect/src/contract.rs +++ b/contracts/ibc-reflect/src/contract.rs @@ -1,7 +1,7 @@ use cosmwasm_std::{ attr, entry_point, from_slice, to_binary, wasm_execute, wasm_instantiate, BankMsg, CosmosMsg, - Deps, DepsMut, Empty, Env, HumanAddr, IbcAcknowledgement, IbcBasicResponse, IbcChannel, - IbcOrder, IbcPacket, IbcReceiveResponse, MessageInfo, Order, QueryResponse, Response, StdError, + Deps, DepsMut, Empty, Env, IbcAcknowledgement, IbcBasicResponse, IbcChannel, IbcOrder, + IbcPacket, IbcReceiveResponse, MessageInfo, Order, QueryResponse, Response, StdError, StdResult, }; @@ -53,8 +53,10 @@ pub fn execute_init_callback( deps: DepsMut, info: MessageInfo, id: String, - contract_addr: HumanAddr, + contract_addr: String, ) -> StdResult { + let contract_addr = deps.api.addr_validate(&contract_addr)?; + // sanity check - the caller is registering itself if info.sender != contract_addr { return Err(StdError::generic_err("Must register self on callback")); @@ -90,18 +92,18 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { pub fn query_account(deps: Deps, channel_id: String) -> StdResult { let account = accounts_read(deps.storage).load(channel_id.as_bytes())?; Ok(AccountResponse { - account: Some(account), + account: Some(account.into()), }) } pub fn query_list_accounts(deps: Deps) -> StdResult { let accounts: StdResult> = accounts_read(deps.storage) .range(None, None, Order::Ascending) - .map(|r| { - let (k, account) = r?; + .map(|item| { + let (key, account) = item?; Ok(AccountInfo { - account, - channel_id: String::from_utf8(k)?, + account: account.into(), + channel_id: String::from_utf8(key)?, }) }) .collect(); @@ -175,7 +177,7 @@ pub fn ibc_channel_close( let amount = deps.querier.query_all_balances(&reflect_addr)?; let messages: Vec> = if !amount.is_empty() { let bank_msg = BankMsg::Send { - to_address: env.contract.address, + to_address: env.contract.address.into(), amount, }; let reflect_msg = ReflectExecuteMsg::ReflectMsg { @@ -266,7 +268,9 @@ fn receive_dispatch( // processes PacketMsg::WhoAmI variant fn receive_who_am_i(deps: DepsMut, caller: String) -> StdResult { let account = accounts(deps.storage).load(caller.as_bytes())?; - let response = WhoAmIResponse { account }; + let response = WhoAmIResponse { + account: account.into(), + }; let acknowledgement = to_binary(&AcknowledgementMsg::Ok(response))?; // and we are golden Ok(IbcReceiveResponse { @@ -281,7 +285,10 @@ fn receive_who_am_i(deps: DepsMut, caller: String) -> StdResult StdResult { let account = accounts(deps.storage).load(caller.as_bytes())?; let balances = deps.querier.query_all_balances(&account)?; - let response = BalancesResponse { account, balances }; + let response = BalancesResponse { + account: account.into(), + balances, + }; let acknowledgement = to_binary(&AcknowledgementMsg::Ok(response))?; // and we are golden Ok(IbcReceiveResponse { @@ -348,8 +355,8 @@ mod tests { // connect will run through the entire handshake to set up a proper connect and // save the account (tested in detail in `proper_handshake_flow`) - fn connect>(mut deps: DepsMut, channel_id: &str, account: T) { - let account = account.into(); + fn connect>(mut deps: DepsMut, channel_id: &str, account: T) { + let account: String = account.into(); // open packet has no counterparty versin, connect does // TODO: validate this with alpe @@ -367,7 +374,7 @@ mod tests { id: channel_id.into(), contract_addr: account.clone(), }; - let info = mock_info(account, &[]); + let info = mock_info(&account, &[]); execute(deps.branch(), mock_env(), info, execute_msg).unwrap(); } @@ -465,7 +472,7 @@ mod tests { ) .unwrap(); let res: AccountResponse = from_slice(&raw).unwrap(); - assert_eq!(res.account.unwrap(), HumanAddr::from(REFLECT_ADDR)); + assert_eq!(res.account.unwrap(), REFLECT_ADDR); } #[test] @@ -492,7 +499,7 @@ mod tests { let ack: AcknowledgementMsg = from_slice(&res.acknowledgement).unwrap(); assert_eq!( ack.unwrap_err(), - "invalid packet: cosmwasm_std::addresses::HumanAddr not found" + "invalid packet: cosmwasm_std::addresses::Addr not found" ); // register the channel diff --git a/contracts/ibc-reflect/src/msg.rs b/contracts/ibc-reflect/src/msg.rs index f11a807dd5..41ef3b7460 100644 --- a/contracts/ibc-reflect/src/msg.rs +++ b/contracts/ibc-reflect/src/msg.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Coin, ContractResult, CosmosMsg, HumanAddr}; +use cosmwasm_std::{Coin, ContractResult, CosmosMsg}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -18,7 +18,7 @@ pub enum ExecuteMsg { /// id was provided in the InitMsg id: String, /// contract_addr is the address of this contract - contract_addr: HumanAddr, + contract_addr: String, }, } @@ -35,7 +35,7 @@ pub enum QueryMsg { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct AccountResponse { - pub account: Option, + pub account: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -45,7 +45,7 @@ pub struct ListAccountsResponse { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct AccountInfo { - pub account: HumanAddr, + pub account: String, pub channel_id: String, } @@ -81,13 +81,13 @@ pub type DispatchResponse = (); /// Return the caller's account address on the remote chain #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct WhoAmIResponse { - pub account: HumanAddr, + pub account: String, } /// This is the success response we send on ack for PacketMsg::Balance. /// Just acknowledge success or error #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct BalancesResponse { - pub account: HumanAddr, + pub account: String, pub balances: Vec, } diff --git a/contracts/ibc-reflect/src/state.rs b/contracts/ibc-reflect/src/state.rs index 355123a6cc..21bc37835e 100644 --- a/contracts/ibc-reflect/src/state.rs +++ b/contracts/ibc-reflect/src/state.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{HumanAddr, Storage}; +use cosmwasm_std::{Addr, Storage}; use cosmwasm_storage::{ bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton, Singleton, @@ -16,11 +16,11 @@ pub struct Config { } /// accounts is lookup of channel_id to reflect contract -pub fn accounts(storage: &mut dyn Storage) -> Bucket { +pub fn accounts(storage: &mut dyn Storage) -> Bucket { bucket(storage, PREFIX_ACCOUNTS) } -pub fn accounts_read(storage: &dyn Storage) -> ReadonlyBucket { +pub fn accounts_read(storage: &dyn Storage) -> ReadonlyBucket { bucket_read(storage, PREFIX_ACCOUNTS) } diff --git a/contracts/ibc-reflect/tests/integration.rs b/contracts/ibc-reflect/tests/integration.rs index 0d59d86205..b6028b2766 100644 --- a/contracts/ibc-reflect/tests/integration.rs +++ b/contracts/ibc-reflect/tests/integration.rs @@ -19,8 +19,8 @@ use cosmwasm_std::testing::{mock_ibc_channel, mock_ibc_packet_recv}; use cosmwasm_std::{ - coins, BankMsg, ContractResult, CosmosMsg, HumanAddr, IbcBasicResponse, IbcOrder, - IbcReceiveResponse, Response, WasmMsg, + coins, BankMsg, ContractResult, CosmosMsg, IbcBasicResponse, IbcOrder, IbcReceiveResponse, + Response, WasmMsg, }; use cosmwasm_vm::testing::{ execute, ibc_channel_connect, ibc_channel_open, ibc_packet_receive, instantiate, mock_env, @@ -56,12 +56,12 @@ fn setup() -> Instance { // connect will run through the entire handshake to set up a proper connect and // save the account (tested in detail in `proper_handshake_flow`) -fn connect>( +fn connect>( deps: &mut Instance, channel_id: &str, account: T, ) { - let account = account.into(); + let account: String = account.into(); // first we try to open with a valid handshake let mut handshake_open = mock_ibc_channel(channel_id, IbcOrder::Ordered, IBC_VERSION); handshake_open.counterparty_version = None; @@ -76,7 +76,7 @@ fn connect>( id: channel_id.into(), contract_addr: account.clone(), }; - let info = mock_info(account, &[]); + let info = mock_info(&account, &[]); let _: Response = execute(deps, mock_env(), info, execute_msg).unwrap(); } @@ -176,7 +176,7 @@ fn proper_handshake_flow() { ) .unwrap(); let res: AccountResponse = from_slice(&raw).unwrap(); - assert_eq!(res.account.unwrap(), HumanAddr::from(REFLECT_ADDR)); + assert_eq!(res.account.unwrap(), REFLECT_ADDR); } #[test] @@ -203,7 +203,7 @@ fn handle_dispatch_packet() { let ack: AcknowledgementMsg = from_slice(&res.acknowledgement).unwrap(); assert_eq!( ack.unwrap_err(), - "invalid packet: cosmwasm_std::addresses::HumanAddr not found" + "invalid packet: cosmwasm_std::addresses::Addr not found" ); // register the channel diff --git a/contracts/queue/tests/integration.rs b/contracts/queue/tests/integration.rs index d33650c13e..d096ad9380 100644 --- a/contracts/queue/tests/integration.rs +++ b/contracts/queue/tests/integration.rs @@ -17,7 +17,7 @@ //! }); //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) -use cosmwasm_std::{from_binary, from_slice, HumanAddr, MessageInfo, Response}; +use cosmwasm_std::{from_binary, from_slice, MessageInfo, Response}; use cosmwasm_vm::{ testing::{ execute, instantiate, migrate, mock_env, mock_info, mock_instance_with_gas_limit, query, @@ -36,8 +36,8 @@ static WASM: &[u8] = include_bytes!("../target/wasm32-unknown-unknown/release/qu fn create_contract() -> (Instance, MessageInfo) { let gas_limit = 500_000_000; // enough for many executions within one instance let mut deps = mock_instance_with_gas_limit(WASM, gas_limit); - let creator = HumanAddr(String::from("creator")); - let info = mock_info(creator.as_str(), &[]); + let creator = String::from("creator"); + let info = mock_info(&creator, &[]); let res: Response = instantiate(&mut deps, mock_env(), info.clone(), InstantiateMsg {}).unwrap(); assert_eq!(0, res.messages.len()); diff --git a/contracts/reflect/examples/schema.rs b/contracts/reflect/examples/schema.rs index edbb6e083b..cc86ed17d9 100644 --- a/contracts/reflect/examples/schema.rs +++ b/contracts/reflect/examples/schema.rs @@ -16,16 +16,19 @@ fn main() { create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); + // messages export_schema(&schema_for!(CustomMsg), &out_dir); export_schema(&schema_for!(InstantiateMsg), &out_dir); export_schema(&schema_for!(ExecuteMsg), &out_dir); export_schema(&schema_for!(Response), &out_dir); export_schema(&schema_for!(QueryMsg), &out_dir); - export_schema(&schema_for!(State), &out_dir); // The possible return types for QueryMsg cases export_schema(&schema_for!(OwnerResponse), &out_dir); export_schema(&schema_for!(CapitalizedResponse), &out_dir); export_schema(&schema_for!(ChainResponse), &out_dir); export_schema(&schema_for!(RawResponse), &out_dir); + + // state + export_schema(&schema_for!(State), &out_dir); } diff --git a/contracts/reflect/schema/execute_msg.json b/contracts/reflect/schema/execute_msg.json index 5fb601b79d..1be850d27e 100644 --- a/contracts/reflect/schema/execute_msg.json +++ b/contracts/reflect/schema/execute_msg.json @@ -61,7 +61,7 @@ ], "properties": { "owner": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -94,7 +94,7 @@ } }, "to_address": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -240,9 +240,6 @@ } ] }, - "HumanAddr": { - "type": "string" - }, "IbcMsg": { "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", "anyOf": [ @@ -295,11 +292,7 @@ }, "to_address": { "description": "address on the remote chain to receive these tokens", - "allOf": [ - { - "$ref": "#/definitions/HumanAddr" - } - ] + "type": "string" } } } @@ -426,7 +419,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -451,7 +444,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -473,17 +466,13 @@ "properties": { "recipient": { "description": "this is the \"withdraw address\", the one that should receive the rewards if None, then use delegator address", - "anyOf": [ - { - "$ref": "#/definitions/HumanAddr" - }, - { - "type": "null" - } + "type": [ + "string", + "null" ] }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -509,10 +498,10 @@ "$ref": "#/definitions/Coin" }, "dst_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "src_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -573,7 +562,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", @@ -654,7 +643,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", diff --git a/contracts/reflect/schema/owner_response.json b/contracts/reflect/schema/owner_response.json index e2a3fc2b8e..0ec6409812 100644 --- a/contracts/reflect/schema/owner_response.json +++ b/contracts/reflect/schema/owner_response.json @@ -7,11 +7,6 @@ ], "properties": { "owner": { - "$ref": "#/definitions/HumanAddr" - } - }, - "definitions": { - "HumanAddr": { "type": "string" } } diff --git a/contracts/reflect/schema/query_msg.json b/contracts/reflect/schema/query_msg.json index c746c5d090..8b24046d67 100644 --- a/contracts/reflect/schema/query_msg.json +++ b/contracts/reflect/schema/query_msg.json @@ -71,7 +71,7 @@ ], "properties": { "contract": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "key": { "$ref": "#/definitions/Binary" @@ -123,7 +123,7 @@ ], "properties": { "address": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "denom": { "type": "string" @@ -147,7 +147,7 @@ ], "properties": { "address": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -160,9 +160,6 @@ "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", "type": "string" }, - "HumanAddr": { - "type": "string" - }, "IbcQuery": { "description": "These are queries to the various IBC modules to see the state of the contract's IBC connection. These will return errors if the contract is not \"ibc enabled\"", "anyOf": [ @@ -390,7 +387,7 @@ ], "properties": { "delegator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -412,10 +409,10 @@ ], "properties": { "delegator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -454,7 +451,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded QueryMsg struct", @@ -484,7 +481,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "key": { "description": "Key is the raw key used in the contracts Storage", diff --git a/contracts/reflect/schema/response_for__custom_msg.json b/contracts/reflect/schema/response_for__custom_msg.json index 50c25700d7..24bb6b74fc 100644 --- a/contracts/reflect/schema/response_for__custom_msg.json +++ b/contracts/reflect/schema/response_for__custom_msg.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Response_for_CustomMsg", - "description": "A response of a contract entry point, such as `instantiate`, `execute` or `migrate`.\n\nThis type can be constructed directly at the end of the call. Alternatively a mutable response instance can be created early in the contract's logic and incrementally be updated.\n\n## Examples\n\nDirect:\n\n``` # use cosmwasm_std::{Binary, DepsMut, Env, MessageInfo}; # type InstantiateMsg = (); # use cosmwasm_std::{attr, Response, StdResult};\n\npub fn instantiate( deps: DepsMut, _env: Env, _info: MessageInfo, msg: InstantiateMsg, ) -> StdResult { // ...\n\nOk(Response { submessages: vec![], messages: vec![], attributes: vec![attr(\"action\", \"instantiate\")], data: None, }) } ```\n\nMutating:\n\n``` # use cosmwasm_std::{coins, BankMsg, Binary, DepsMut, Env, HumanAddr, MessageInfo}; # type InstantiateMsg = (); # type MyError = (); # use cosmwasm_std::Response;\n\npub fn instantiate( deps: DepsMut, _env: Env, info: MessageInfo, msg: InstantiateMsg, ) -> Result { let mut response = Response::new(); // ... response.add_attribute(\"Let the\", \"hacking begin\"); // ... response.add_message(BankMsg::Send { to_address: HumanAddr::from(\"recipient\"), amount: coins(128, \"uint\"), }); response.add_attribute(\"foo\", \"bar\"); // ... response.set_data(Binary::from(b\"the result data\")); Ok(response) } ```", + "description": "A response of a contract entry point, such as `instantiate`, `execute` or `migrate`.\n\nThis type can be constructed directly at the end of the call. Alternatively a mutable response instance can be created early in the contract's logic and incrementally be updated.\n\n## Examples\n\nDirect:\n\n``` # use cosmwasm_std::{Binary, DepsMut, Env, MessageInfo}; # type InstantiateMsg = (); # use cosmwasm_std::{attr, Response, StdResult};\n\npub fn instantiate( deps: DepsMut, _env: Env, _info: MessageInfo, msg: InstantiateMsg, ) -> StdResult { // ...\n\nOk(Response { submessages: vec![], messages: vec![], attributes: vec![attr(\"action\", \"instantiate\")], data: None, }) } ```\n\nMutating:\n\n``` # use cosmwasm_std::{coins, BankMsg, Binary, DepsMut, Env, MessageInfo}; # type InstantiateMsg = (); # type MyError = (); # use cosmwasm_std::Response;\n\npub fn instantiate( deps: DepsMut, _env: Env, info: MessageInfo, msg: InstantiateMsg, ) -> Result { let mut response = Response::new(); // ... response.add_attribute(\"Let the\", \"hacking begin\"); // ... response.add_message(BankMsg::Send { to_address: String::from(\"recipient\"), amount: coins(128, \"uint\"), }); response.add_attribute(\"foo\", \"bar\"); // ... response.set_data(Binary::from(b\"the result data\")); Ok(response) } ```", "type": "object", "required": [ "attributes", @@ -82,7 +82,7 @@ } }, "to_address": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -228,9 +228,6 @@ } ] }, - "HumanAddr": { - "type": "string" - }, "IbcMsg": { "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", "anyOf": [ @@ -283,11 +280,7 @@ }, "to_address": { "description": "address on the remote chain to receive these tokens", - "allOf": [ - { - "$ref": "#/definitions/HumanAddr" - } - ] + "type": "string" } } } @@ -414,7 +407,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -439,7 +432,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -461,17 +454,13 @@ "properties": { "recipient": { "description": "this is the \"withdraw address\", the one that should receive the rewards if None, then use delegator address", - "anyOf": [ - { - "$ref": "#/definitions/HumanAddr" - }, - { - "type": "null" - } + "type": [ + "string", + "null" ] }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -497,10 +486,10 @@ "$ref": "#/definitions/Coin" }, "dst_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "src_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -561,7 +550,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", @@ -642,7 +631,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", diff --git a/contracts/reflect/schema/state.json b/contracts/reflect/schema/state.json index d4b4a3340f..1c10ba441e 100644 --- a/contracts/reflect/schema/state.json +++ b/contracts/reflect/schema/state.json @@ -7,16 +7,13 @@ ], "properties": { "owner": { - "$ref": "#/definitions/CanonicalAddr" + "$ref": "#/definitions/Addr" } }, "definitions": { - "Binary": { - "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", "type": "string" - }, - "CanonicalAddr": { - "$ref": "#/definitions/Binary" } } } diff --git a/contracts/reflect/src/contract.rs b/contracts/reflect/src/contract.rs index bff304510f..dcd31f538a 100644 --- a/contracts/reflect/src/contract.rs +++ b/contracts/reflect/src/contract.rs @@ -1,7 +1,7 @@ use cosmwasm_std::{ attr, entry_point, to_binary, to_vec, Binary, ContractResult, CosmosMsg, Deps, DepsMut, Env, - HumanAddr, MessageInfo, QueryRequest, QueryResponse, Reply, Response, StdError, StdResult, - SubMsg, SystemResult, WasmMsg, + MessageInfo, QueryRequest, QueryResponse, Reply, Response, StdError, StdResult, SubMsg, + SystemResult, WasmMsg, }; use crate::errors::ReflectError; @@ -18,7 +18,7 @@ pub fn instantiate( msg: InstantiateMsg, ) -> StdResult> { let state = State { - owner: deps.api.canonical_address(&info.sender)?, + owner: info.sender.clone(), }; config(deps.storage).save(&state)?; @@ -26,10 +26,10 @@ pub fn instantiate( if let Some(id) = msg.callback_id { let data = CallbackMsg::InitCallback { id, - contract_addr: env.contract.address, + contract_addr: env.contract.address.into(), }; let msg = WasmMsg::Execute { - contract_addr: info.sender, + contract_addr: info.sender.into(), msg: to_binary(&data)?, send: vec![], }; @@ -59,11 +59,10 @@ pub fn try_reflect( ) -> Result, ReflectError> { let state = config(deps.storage).load()?; - let sender = deps.api.canonical_address(&info.sender)?; - if sender != state.owner { + if info.sender != state.owner { return Err(ReflectError::NotCurrentOwner { - expected: state.owner, - actual: sender, + expected: state.owner.into(), + actual: info.sender.into(), }); } @@ -86,11 +85,10 @@ pub fn try_reflect_subcall( msgs: Vec>, ) -> Result, ReflectError> { let state = config(deps.storage).load()?; - let sender = deps.api.canonical_address(&info.sender)?; - if sender != state.owner { + if info.sender != state.owner { return Err(ReflectError::NotCurrentOwner { - expected: state.owner, - actual: sender, + expected: state.owner.into(), + actual: info.sender.into(), }); } @@ -110,22 +108,21 @@ pub fn try_change_owner( deps: DepsMut, _env: Env, info: MessageInfo, - owner: HumanAddr, + new_owner: String, ) -> Result, ReflectError> { let api = deps.api; config(deps.storage).update(|mut state| { - let sender = api.canonical_address(&info.sender)?; - if sender != state.owner { + if info.sender != state.owner { return Err(ReflectError::NotCurrentOwner { - expected: state.owner, - actual: sender, + expected: state.owner.into(), + actual: info.sender.into(), }); } - state.owner = api.canonical_address(&owner)?; + state.owner = api.addr_validate(&new_owner)?; Ok(state) })?; Ok(Response { - attributes: vec![attr("action", "change_owner"), attr("owner", owner)], + attributes: vec![attr("action", "change_owner"), attr("owner", new_owner)], ..Response::default() }) } @@ -151,7 +148,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { fn query_owner(deps: Deps) -> StdResult { let state = config_read(deps.storage).load()?; let resp = OwnerResponse { - owner: deps.api.human_address(&state.owner)?, + owner: state.owner.into(), }; Ok(resp) } @@ -184,7 +181,7 @@ fn query_chain(deps: Deps, request: &QueryRequest) -> StdResult StdResult { +fn query_raw(deps: Deps, contract: String, key: Binary) -> StdResult { let response: Option> = deps.querier.query_wasm_raw(contract, key)?; Ok(RawResponse { data: response.unwrap_or_default().into(), @@ -197,8 +194,8 @@ mod tests { use crate::testing::mock_dependencies_with_custom_querier; use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ - coin, coins, from_binary, AllBalanceResponse, Api, BankMsg, BankQuery, Binary, - ContractResult, Event, ReplyOn, StakingMsg, StdError, SubcallResponse, + coin, coins, from_binary, AllBalanceResponse, BankMsg, BankQuery, Binary, ContractResult, + Event, ReplyOn, StakingMsg, StdError, SubcallResponse, }; #[test] @@ -220,7 +217,7 @@ mod tests { #[test] fn instantiate_with_callback() { let mut deps = mock_dependencies_with_custom_querier(&[]); - let caller = HumanAddr::from("calling-contract"); + let caller = String::from("calling-contract"); let msg = InstantiateMsg { callback_id: Some("foobar".to_string()), @@ -237,7 +234,7 @@ mod tests { msg, send, }) => { - assert_eq!(contract_addr, &caller); + assert_eq!(contract_addr.as_str(), &caller); let parsed: CallbackMsg = from_binary(&msg).unwrap(); assert_eq!( parsed, @@ -253,7 +250,7 @@ mod tests { // it worked, let's query the state let value = query_owner(deps.as_ref()).unwrap(); - assert_eq!(caller, value.owner); + assert_eq!(value.owner, caller); } #[test] @@ -265,7 +262,7 @@ mod tests { let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); let payload = vec![BankMsg::Send { - to_address: HumanAddr::from("friend"), + to_address: String::from("friend"), amount: coins(1, "token"), } .into()]; @@ -288,7 +285,7 @@ mod tests { // signer is not owner let payload = vec![BankMsg::Send { - to_address: HumanAddr::from("friend"), + to_address: String::from("friend"), amount: coins(1, "token"), } .into()]; @@ -328,7 +325,7 @@ mod tests { let payload = vec![ BankMsg::Send { - to_address: HumanAddr::from("friend"), + to_address: String::from("friend"), amount: coins(1, "token"), } .into(), @@ -336,7 +333,7 @@ mod tests { CustomMsg::Raw(Binary(b"{\"foo\":123}".to_vec())).into(), CustomMsg::Debug("Hi, Dad!".to_string()).into(), StakingMsg::Delegate { - validator: HumanAddr::from("validator"), + validator: String::from("validator"), amount: coin(100, "ustake"), } .into(), @@ -359,7 +356,7 @@ mod tests { let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); let info = mock_info("creator", &[]); - let new_owner = HumanAddr::from("friend"); + let new_owner = String::from("friend"); let msg = ExecuteMsg::ChangeOwner { owner: new_owner }; let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); @@ -374,25 +371,29 @@ mod tests { let mut deps = mock_dependencies_with_custom_querier(&[]); let msg = InstantiateMsg { callback_id: None }; - let creator = HumanAddr::from("creator"); + let creator = String::from("creator"); let info = mock_info(&creator, &coins(2, "token")); let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); - let random = HumanAddr::from("random"); + let random = String::from("random"); let info = mock_info(&random, &[]); - let new_owner = HumanAddr::from("friend"); + let new_owner = String::from("friend"); let msg = ExecuteMsg::ChangeOwner { owner: new_owner }; let err = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err(); - let expected = deps.api.canonical_address(&creator).unwrap(); - let actual = deps.api.canonical_address(&random).unwrap(); - assert_eq!(err, ReflectError::NotCurrentOwner { expected, actual }); + assert_eq!( + err, + ReflectError::NotCurrentOwner { + expected: creator, + actual: random + } + ); } #[test] fn change_owner_errors_for_invalid_new_address() { let mut deps = mock_dependencies_with_custom_querier(&[]); - let creator = HumanAddr::from("creator"); + let creator = String::from("creator"); let msg = InstantiateMsg { callback_id: None }; let info = mock_info(&creator, &coins(2, "token")); @@ -400,7 +401,7 @@ mod tests { let info = mock_info(&creator, &[]); let msg = ExecuteMsg::ChangeOwner { - owner: HumanAddr::from("x"), + owner: String::from("x"), }; let err = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err(); match err { @@ -430,7 +431,7 @@ mod tests { // with bank query let msg = QueryMsg::Chain { request: BankQuery::AllBalances { - address: HumanAddr::from(MOCK_CONTRACT_ADDR), + address: MOCK_CONTRACT_ADDR.to_string(), } .into(), }; @@ -462,7 +463,7 @@ mod tests { id, gas_limit: None, msg: BankMsg::Send { - to_address: HumanAddr::from("friend"), + to_address: String::from("friend"), amount: coins(1, "token"), } .into(), diff --git a/contracts/reflect/src/errors.rs b/contracts/reflect/src/errors.rs index 58e360d9ed..98db940523 100644 --- a/contracts/reflect/src/errors.rs +++ b/contracts/reflect/src/errors.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{CanonicalAddr, StdError}; +use cosmwasm_std::StdError; use thiserror::Error; #[derive(Error, Debug, PartialEq)] @@ -8,10 +8,7 @@ pub enum ReflectError { Std(#[from] StdError), // this is whatever we want #[error("Permission denied: the sender is not the current owner")] - NotCurrentOwner { - expected: CanonicalAddr, - actual: CanonicalAddr, - }, + NotCurrentOwner { expected: String, actual: String }, #[error("Messages empty. Must reflect at least one message")] MessagesEmpty, } diff --git a/contracts/reflect/src/msg.rs b/contracts/reflect/src/msg.rs index b4cd5e0aae..14f4b364c8 100644 --- a/contracts/reflect/src/msg.rs +++ b/contracts/reflect/src/msg.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{Binary, CosmosMsg, CustomQuery, HumanAddr, QueryRequest, SubMsg}; +use cosmwasm_std::{Binary, CosmosMsg, CustomQuery, QueryRequest, SubMsg}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct InstantiateMsg { @@ -19,7 +19,7 @@ pub enum CallbackMsg { /// Callback ID provided in the InstantiateMsg id: String, /// contract_addr is the address of this contract - contract_addr: HumanAddr, + contract_addr: String, }, } @@ -28,7 +28,7 @@ pub enum CallbackMsg { pub enum ExecuteMsg { ReflectMsg { msgs: Vec> }, ReflectSubCall { msgs: Vec> }, - ChangeOwner { owner: HumanAddr }, + ChangeOwner { owner: String }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -45,7 +45,7 @@ pub enum QueryMsg { }, /// Queries another contract and returns the data Raw { - contract: HumanAddr, + contract: String, key: Binary, }, /// If there was a previous ReflectSubCall with this ID, returns cosmwasm_std::Reply @@ -58,7 +58,7 @@ pub enum QueryMsg { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct OwnerResponse { - pub owner: HumanAddr, + pub owner: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] diff --git a/contracts/reflect/src/state.rs b/contracts/reflect/src/state.rs index 00b9031401..57b475dac7 100644 --- a/contracts/reflect/src/state.rs +++ b/contracts/reflect/src/state.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{CanonicalAddr, Reply, Storage}; +use cosmwasm_std::{Addr, Reply, Storage}; use cosmwasm_storage::{ bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton, Singleton, @@ -12,7 +12,7 @@ const RESULT_PREFIX: &[u8] = b"result"; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct State { - pub owner: CanonicalAddr, + pub owner: Addr, } pub fn config(storage: &mut dyn Storage) -> Singleton { diff --git a/contracts/reflect/src/testing.rs b/contracts/reflect/src/testing.rs index e20c9c58b3..32ccedaafc 100644 --- a/contracts/reflect/src/testing.rs +++ b/contracts/reflect/src/testing.rs @@ -1,16 +1,15 @@ use crate::msg::{SpecialQuery, SpecialResponse}; use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR}; -use cosmwasm_std::{to_binary, Binary, Coin, ContractResult, HumanAddr, OwnedDeps, SystemResult}; +use cosmwasm_std::{to_binary, Binary, Coin, ContractResult, OwnedDeps, SystemResult}; /// A drop-in replacement for cosmwasm_std::testing::mock_dependencies /// this uses our CustomQuerier. pub fn mock_dependencies_with_custom_querier( contract_balance: &[Coin], ) -> OwnedDeps> { - let contract_addr = HumanAddr::from(MOCK_CONTRACT_ADDR); let custom_querier: MockQuerier = - MockQuerier::new(&[(&contract_addr, contract_balance)]) + MockQuerier::new(&[(MOCK_CONTRACT_ADDR, contract_balance)]) .with_custom_handler(|query| SystemResult::Ok(custom_query_execute(&query))); OwnedDeps { storage: MockStorage::default(), diff --git a/contracts/reflect/tests/integration.rs b/contracts/reflect/tests/integration.rs index ee744cadd5..775ae7f03f 100644 --- a/contracts/reflect/tests/integration.rs +++ b/contracts/reflect/tests/integration.rs @@ -18,8 +18,8 @@ //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) use cosmwasm_std::{ - attr, coin, coins, from_binary, BankMsg, Binary, Coin, ContractResult, Event, HumanAddr, Reply, - Response, StakingMsg, SubMsg, SubcallResponse, SystemResult, + attr, coin, coins, from_binary, BankMsg, Binary, Coin, ContractResult, Event, Reply, Response, + StakingMsg, SubMsg, SubcallResponse, SystemResult, }; use cosmwasm_vm::{ testing::{ @@ -45,9 +45,8 @@ static WASM: &[u8] = include_bytes!("../target/wasm32-unknown-unknown/release/re pub fn mock_dependencies_with_custom_querier( contract_balance: &[Coin], ) -> Backend> { - let contract_addr = HumanAddr::from(MOCK_CONTRACT_ADDR); let custom_querier: MockQuerier = - MockQuerier::new(&[(&contract_addr, contract_balance)]) + MockQuerier::new(&[(MOCK_CONTRACT_ADDR, contract_balance)]) .with_custom_handler(|query| SystemResult::Ok(custom_query_execute(query))); Backend { @@ -84,7 +83,7 @@ fn reflect() { let payload = vec![ BankMsg::Send { - to_address: HumanAddr::from("friend"), + to_address: String::from("friend"), amount: coins(1, "token"), } .into(), @@ -92,7 +91,7 @@ fn reflect() { CustomMsg::Raw(Binary(b"{\"foo\":123}".to_vec())).into(), CustomMsg::Debug("Hi, Dad!".to_string()).into(), StakingMsg::Delegate { - validator: HumanAddr::from("validator"), + validator: String::from("validator"), amount: coin(100, "ustake"), } .into(), @@ -117,7 +116,7 @@ fn reflect_requires_owner() { // signer is not owner let payload = vec![BankMsg::Send { - to_address: HumanAddr::from("friend"), + to_address: String::from("friend"), amount: coins(1, "token"), } .into()]; @@ -138,7 +137,7 @@ fn transfer() { let _res: Response = instantiate(&mut deps, mock_env(), info, msg).unwrap(); let info = mock_info("creator", &[]); - let new_owner = HumanAddr::from("friend"); + let new_owner = String::from("friend"); let msg = ExecuteMsg::ChangeOwner { owner: new_owner }; let res: Response = execute(&mut deps, mock_env(), info, msg).unwrap(); @@ -158,7 +157,7 @@ fn transfer_requires_owner() { let _res: Response = instantiate(&mut deps, mock_env(), info, msg).unwrap(); let info = mock_info("random", &[]); - let new_owner = HumanAddr::from("friend"); + let new_owner = String::from("friend"); let msg = ExecuteMsg::ChangeOwner { owner: new_owner }; let res: ContractResult = execute(&mut deps, mock_env(), info, msg); @@ -200,7 +199,7 @@ fn reflect_subcall() { id, gas_limit: None, msg: BankMsg::Send { - to_address: HumanAddr::from("friend"), + to_address: String::from("friend"), amount: coins(1, "token"), } .into(), diff --git a/contracts/staking/examples/schema.rs b/contracts/staking/examples/schema.rs index cb706f1c21..780fc0c0ed 100644 --- a/contracts/staking/examples/schema.rs +++ b/contracts/staking/examples/schema.rs @@ -7,7 +7,7 @@ use staking::msg::{ BalanceResponse, ClaimsResponse, ExecuteMsg, InstantiateMsg, InvestmentResponse, QueryMsg, TokenInfoResponse, }; -use staking::state::{InvestmentInfo, Supply}; +use staking::state::{InvestmentInfo, Supply, TokenInfo}; fn main() { let mut out_dir = current_dir().unwrap(); @@ -15,6 +15,7 @@ fn main() { create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); + // messages export_schema(&schema_for!(InstantiateMsg), &out_dir); export_schema(&schema_for!(ExecuteMsg), &out_dir); export_schema(&schema_for!(QueryMsg), &out_dir); @@ -22,6 +23,9 @@ fn main() { export_schema(&schema_for!(ClaimsResponse), &out_dir); export_schema(&schema_for!(InvestmentResponse), &out_dir); export_schema(&schema_for!(TokenInfoResponse), &out_dir); + + // state export_schema(&schema_for!(InvestmentInfo), &out_dir); + export_schema(&schema_for!(TokenInfo), &out_dir); export_schema(&schema_for!(Supply), &out_dir); } diff --git a/contracts/staking/schema/execute_msg.json b/contracts/staking/schema/execute_msg.json index c324cab0e2..2aad3c31ec 100644 --- a/contracts/staking/schema/execute_msg.json +++ b/contracts/staking/schema/execute_msg.json @@ -20,7 +20,7 @@ "$ref": "#/definitions/Uint128" }, "recipient": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -102,9 +102,6 @@ } ], "definitions": { - "HumanAddr": { - "type": "string" - }, "Uint128": { "type": "string" } diff --git a/contracts/staking/schema/instantiate_msg.json b/contracts/staking/schema/instantiate_msg.json index e4f865f970..361879b512 100644 --- a/contracts/staking/schema/instantiate_msg.json +++ b/contracts/staking/schema/instantiate_msg.json @@ -43,11 +43,7 @@ }, "validator": { "description": "This is the validator that all tokens will be bonded to", - "allOf": [ - { - "$ref": "#/definitions/HumanAddr" - } - ] + "type": "string" } }, "definitions": { @@ -55,9 +51,6 @@ "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", "type": "string" }, - "HumanAddr": { - "type": "string" - }, "Uint128": { "type": "string" } diff --git a/contracts/staking/schema/investment_info.json b/contracts/staking/schema/investment_info.json index be49695bb3..9ea300a35d 100644 --- a/contracts/staking/schema/investment_info.json +++ b/contracts/staking/schema/investment_info.json @@ -35,34 +35,24 @@ "description": "owner created the contract and takes a cut", "allOf": [ { - "$ref": "#/definitions/CanonicalAddr" + "$ref": "#/definitions/Addr" } ] }, "validator": { "description": "All tokens are bonded to this validator FIXME: humanize/canonicalize address doesn't work for validator addrresses", - "allOf": [ - { - "$ref": "#/definitions/HumanAddr" - } - ] + "type": "string" } }, "definitions": { - "Binary": { - "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", "type": "string" }, - "CanonicalAddr": { - "$ref": "#/definitions/Binary" - }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", "type": "string" }, - "HumanAddr": { - "type": "string" - }, "Uint128": { "type": "string" } diff --git a/contracts/staking/schema/investment_response.json b/contracts/staking/schema/investment_response.json index cbaaafef5c..ce1a140b6a 100644 --- a/contracts/staking/schema/investment_response.json +++ b/contracts/staking/schema/investment_response.json @@ -33,11 +33,7 @@ }, "owner": { "description": "owner created the contract and takes a cut", - "allOf": [ - { - "$ref": "#/definitions/HumanAddr" - } - ] + "type": "string" }, "staked_tokens": { "$ref": "#/definitions/Coin" @@ -47,11 +43,7 @@ }, "validator": { "description": "All tokens are bonded to this validator", - "allOf": [ - { - "$ref": "#/definitions/HumanAddr" - } - ] + "type": "string" } }, "definitions": { @@ -74,9 +66,6 @@ "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", "type": "string" }, - "HumanAddr": { - "type": "string" - }, "Uint128": { "type": "string" } diff --git a/contracts/staking/schema/query_msg.json b/contracts/staking/schema/query_msg.json index d7cde031f2..4eb5798f66 100644 --- a/contracts/staking/schema/query_msg.json +++ b/contracts/staking/schema/query_msg.json @@ -16,7 +16,7 @@ ], "properties": { "address": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -37,7 +37,7 @@ ], "properties": { "address": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -70,10 +70,5 @@ }, "additionalProperties": false } - ], - "definitions": { - "HumanAddr": { - "type": "string" - } - } + ] } diff --git a/contracts/staking/schema/token_info.json b/contracts/staking/schema/token_info.json new file mode 100644 index 0000000000..5329082429 --- /dev/null +++ b/contracts/staking/schema/token_info.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TokenInfo", + "description": "Info to display the derivative token in a UI", + "type": "object", + "required": [ + "decimals", + "name", + "symbol" + ], + "properties": { + "decimals": { + "description": "decimal places of the derivative token (for UI)", + "type": "integer", + "format": "uint8", + "minimum": 0.0 + }, + "name": { + "description": "name of the derivative token", + "type": "string" + }, + "symbol": { + "description": "symbol / ticker of the derivative token", + "type": "string" + } + } +} diff --git a/contracts/staking/src/contract.rs b/contracts/staking/src/contract.rs index e389cef94a..d087a80ccc 100644 --- a/contracts/staking/src/contract.rs +++ b/contracts/staking/src/contract.rs @@ -1,7 +1,6 @@ use cosmwasm_std::{ - attr, coin, entry_point, to_binary, BankMsg, Decimal, Deps, DepsMut, Env, HumanAddr, - MessageInfo, QuerierWrapper, QueryResponse, Response, StakingMsg, StdError, StdResult, Uint128, - WasmMsg, + attr, coin, entry_point, to_binary, BankMsg, Decimal, Deps, DepsMut, Env, MessageInfo, + QuerierWrapper, QueryResponse, Response, StakingMsg, StdError, StdResult, Uint128, WasmMsg, }; use crate::errors::{StakingError, Unauthorized}; @@ -11,7 +10,7 @@ use crate::msg::{ }; use crate::state::{ balances, balances_read, claims, claims_read, invest_info, invest_info_read, token_info, - token_info_read, total_supply, total_supply_read, InvestmentInfo, Supply, + token_info_read, total_supply, total_supply_read, InvestmentInfo, Supply, TokenInfo, }; const FALLBACK_RATIO: Decimal = Decimal::one(); @@ -32,7 +31,7 @@ pub fn instantiate( ))); } - let token = TokenInfoResponse { + let token = TokenInfo { name: msg.name, symbol: msg.symbol, decimals: msg.decimals, @@ -41,10 +40,10 @@ pub fn instantiate( let denom = deps.querier.query_bonded_denom()?; let invest = InvestmentInfo { - owner: deps.api.canonical_address(&info.sender)?, + owner: info.sender, exit_tax: msg.exit_tax, bond_denom: denom, - validator: msg.validator, + validator: deps.api.addr_validate(&msg.validator)?.into(), min_withdrawal: msg.min_withdrawal, }; invest_info(deps.storage).save(&invest)?; @@ -79,11 +78,11 @@ pub fn transfer( deps: DepsMut, _env: Env, info: MessageInfo, - recipient: HumanAddr, + recipient: String, send: Uint128, ) -> StdResult { - let rcpt_raw = deps.api.canonical_address(&recipient)?; - let sender_raw = deps.api.canonical_address(&info.sender)?; + let rcpt_raw = deps.api.addr_canonicalize(&recipient)?; + let sender_raw = deps.api.addr_canonicalize(info.sender.as_ref())?; let mut accounts = balances(deps.storage); accounts.update(&sender_raw, |balance: Option| -> StdResult<_> { @@ -109,8 +108,8 @@ pub fn transfer( // get_bonded returns the total amount of delegations from contract // it ensures they are all the same denom -fn get_bonded(querier: &QuerierWrapper, contract: &HumanAddr) -> StdResult { - let bonds = querier.query_all_delegations(contract)?; +fn get_bonded>(querier: &QuerierWrapper, contract_addr: U) -> StdResult { + let bonds = querier.query_all_delegations(contract_addr)?; if bonds.is_empty() { return Ok(Uint128(0)); } @@ -140,7 +139,7 @@ fn assert_bonds(supply: &Supply, bonded: Uint128) -> StdResult<()> { } pub fn bond(deps: DepsMut, env: Env, info: MessageInfo) -> StdResult { - let sender_raw = deps.api.canonical_address(&info.sender)?; + let sender_raw = deps.api.addr_canonicalize(info.sender.as_ref())?; // ensure we have the proper denom let invest = invest_info_read(deps.storage).load()?; @@ -152,7 +151,7 @@ pub fn bond(deps: DepsMut, env: Env, info: MessageInfo) -> StdResult { .ok_or_else(|| StdError::generic_err(format!("No {} tokens sent", &invest.bond_denom)))?; // bonded is the total number of tokens we have delegated from this address - let bonded = get_bonded(&deps.querier, &env.contract.address)?; + let bonded = get_bonded(&deps.querier, env.contract.address)?; // calculate to_mint and update total supply let mut totals = total_supply(deps.storage); @@ -193,8 +192,6 @@ pub fn bond(deps: DepsMut, env: Env, info: MessageInfo) -> StdResult { } pub fn unbond(deps: DepsMut, env: Env, info: MessageInfo, amount: Uint128) -> StdResult { - let sender_raw = deps.api.canonical_address(&info.sender)?; - let invest = invest_info_read(deps.storage).load()?; // ensure it is big enough to care if amount < invest.min_withdrawal { @@ -203,6 +200,10 @@ pub fn unbond(deps: DepsMut, env: Env, info: MessageInfo, amount: Uint128) -> St invest.min_withdrawal, invest.bond_denom ))); } + + let sender_raw = deps.api.addr_canonicalize(info.sender.as_ref())?; + let owner_raw = deps.api.addr_canonicalize(invest.owner.as_ref())?; + // calculate tax and remainer to unbond let tax = amount * invest.exit_tax; @@ -213,14 +214,14 @@ pub fn unbond(deps: DepsMut, env: Env, info: MessageInfo, amount: Uint128) -> St })?; if tax > Uint128(0) { // add tax to the owner - accounts.update(&invest.owner, |balance: Option| -> StdResult<_> { + accounts.update(&owner_raw, |balance: Option| -> StdResult<_> { Ok(balance.unwrap_or_default() + tax) })?; } // re-calculate bonded to ensure we have real values // bonded is the total number of tokens we have delegated from this address - let bonded = get_bonded(&deps.querier, &env.contract.address)?; + let bonded = get_bonded(&deps.querier, env.contract.address)?; // calculate how many native tokens this is worth and update supply let remainder = amount.checked_sub(tax)?; @@ -263,7 +264,7 @@ pub fn claim(deps: DepsMut, env: Env, info: MessageInfo) -> StdResult let invest = invest_info_read(deps.storage).load()?; let mut balance = deps .querier - .query_balance(&env.contract.address, &invest.bond_denom)?; + .query_balance(env.contract.address, invest.bond_denom)?; if balance.amount < invest.min_withdrawal { return Err(StdError::generic_err( "Insufficient balance in contract to process claim", @@ -271,7 +272,7 @@ pub fn claim(deps: DepsMut, env: Env, info: MessageInfo) -> StdResult } // check how much to send - min(balance, claims[sender]), and reduce the claim - let sender_raw = deps.api.canonical_address(&info.sender)?; + let sender_raw = deps.api.addr_canonicalize(info.sender.as_ref())?; let mut to_send = balance.amount; claims(deps.storage).update(sender_raw.as_slice(), |claim| -> StdResult<_> { let claim = claim.ok_or_else(|| StdError::generic_err("no claim for this address"))?; @@ -290,7 +291,7 @@ pub fn claim(deps: DepsMut, env: Env, info: MessageInfo) -> StdResult let res = Response { submessages: vec![], messages: vec![BankMsg::Send { - to_address: info.sender.clone(), + to_address: info.sender.clone().into(), amount: vec![balance], } .into()], @@ -318,11 +319,11 @@ pub fn reinvest(deps: DepsMut, env: Env, _info: MessageInfo) -> StdResult StdResult { match msg { QueryMsg::TokenInfo {} => to_binary(&query_token_info(deps)?), QueryMsg::Investment {} => to_binary(&query_investment(deps)?), - QueryMsg::Balance { address } => to_binary(&query_balance(deps, address)?), - QueryMsg::Claims { address } => to_binary(&query_claims(deps, address)?), + QueryMsg::Balance { address } => to_binary(&query_balance(deps, &address)?), + QueryMsg::Claims { address } => to_binary(&query_claims(deps, &address)?), } } pub fn query_token_info(deps: Deps) -> StdResult { - token_info_read(deps.storage).load() + let TokenInfo { + name, + symbol, + decimals, + } = token_info_read(deps.storage).load()?; + + Ok(TokenInfoResponse { + name, + symbol, + decimals, + }) } -pub fn query_balance(deps: Deps, address: HumanAddr) -> StdResult { - let address_raw = deps.api.canonical_address(&address)?; +pub fn query_balance(deps: Deps, address: &str) -> StdResult { + let address_raw = deps.api.addr_canonicalize(address)?; let balance = balances_read(deps.storage) .may_load(address_raw.as_slice())? .unwrap_or_default(); Ok(BalanceResponse { balance }) } -pub fn query_claims(deps: Deps, address: HumanAddr) -> StdResult { - let address_raw = deps.api.canonical_address(&address)?; +pub fn query_claims(deps: Deps, address: &str) -> StdResult { + let address_raw = deps.api.addr_canonicalize(address)?; let claims = claims_read(deps.storage) .may_load(address_raw.as_slice())? .unwrap_or_default(); @@ -414,7 +425,7 @@ pub fn query_investment(deps: Deps) -> StdResult { let supply = total_supply_read(deps.storage).load()?; let res = InvestmentResponse { - owner: deps.api.human_address(&invest.owner)?, + owner: invest.owner.into(), exit_tax: invest.exit_tax, validator: invest.validator, min_withdrawal: invest.min_withdrawal, @@ -435,23 +446,23 @@ mod tests { use cosmwasm_std::testing::{ mock_dependencies, mock_env, mock_info, MockQuerier, MOCK_CONTRACT_ADDR, }; - use cosmwasm_std::{coins, Coin, CosmosMsg, Decimal, FullDelegation, Validator}; + use cosmwasm_std::{coins, Addr, Coin, CosmosMsg, Decimal, FullDelegation, Validator}; use std::str::FromStr; - fn sample_validator>(addr: U) -> Validator { + fn sample_validator(addr: &str) -> Validator { Validator { - address: addr.into(), + address: Addr::unchecked(addr), commission: Decimal::percent(3), max_commission: Decimal::percent(10), max_change_rate: Decimal::percent(1), } } - fn sample_delegation>(addr: U, amount: Coin) -> FullDelegation { + fn sample_delegation(validator_addr: &str, amount: Coin) -> FullDelegation { let can_redelegate = amount.clone(); FullDelegation { - validator: addr.into(), - delegator: HumanAddr::from(MOCK_CONTRACT_ADDR), + validator: Addr::unchecked(validator_addr), + delegator: Addr::unchecked(MOCK_CONTRACT_ADDR), amount, can_redelegate, accumulated_rewards: Vec::new(), @@ -477,18 +488,18 @@ mod tests { name: "Cool Derivative".to_string(), symbol: "DRV".to_string(), decimals: 9, - validator: HumanAddr::from(DEFAULT_VALIDATOR), + validator: String::from(DEFAULT_VALIDATOR), exit_tax: Decimal::percent(tax_percent), min_withdrawal: Uint128(min_withdrawal), } } - fn get_balance>(deps: Deps, addr: U) -> Uint128 { - query_balance(deps, addr.into()).unwrap().balance + fn get_balance(deps: Deps, addr: &str) -> Uint128 { + query_balance(deps, addr).unwrap().balance } - fn get_claims>(deps: Deps, addr: U) -> Uint128 { - query_claims(deps, addr.into()).unwrap().claims + fn get_claims(deps: Deps, addr: &str) -> Uint128 { + query_claims(deps, addr).unwrap().claims } #[test] @@ -497,12 +508,12 @@ mod tests { deps.querier .update_staking("ustake", &[sample_validator("john")], &[]); - let creator = HumanAddr::from("creator"); + let creator = String::from("creator"); let msg = InstantiateMsg { name: "Cool Derivative".to_string(), symbol: "DRV".to_string(), decimals: 9, - validator: HumanAddr::from("my-validator"), + validator: String::from("my-validator"), exit_tax: Decimal::percent(2), min_withdrawal: Uint128(50), }; @@ -531,12 +542,12 @@ mod tests { &[], ); - let creator = HumanAddr::from("creator"); + let creator = String::from("creator"); let msg = InstantiateMsg { name: "Cool Derivative".to_string(), symbol: "DRV".to_string(), decimals: 0, - validator: HumanAddr::from("my-validator"), + validator: String::from("my-validator"), exit_tax: Decimal::percent(2), min_withdrawal: Uint128(50), }; @@ -574,7 +585,7 @@ mod tests { let mut deps = mock_dependencies(&[]); set_validator(&mut deps.querier); - let creator = HumanAddr::from("creator"); + let creator = String::from("creator"); let instantiate_msg = default_init(2, 50); let info = mock_info(&creator, &[]); @@ -583,7 +594,7 @@ mod tests { assert_eq!(0, res.messages.len()); // let's bond some tokens now - let bob = HumanAddr::from("bob"); + let bob = String::from("bob"); let bond_msg = ExecuteMsg::Bond {}; let info = mock_info(&bob, &[coin(10, "random"), coin(1000, "ustake")]); @@ -614,7 +625,7 @@ mod tests { let mut deps = mock_dependencies(&[]); set_validator(&mut deps.querier); - let creator = HumanAddr::from("creator"); + let creator = String::from("creator"); let instantiate_msg = default_init(2, 50); let info = mock_info(&creator, &[]); @@ -623,7 +634,7 @@ mod tests { assert_eq!(0, res.messages.len()); // let's bond some tokens now - let bob = HumanAddr::from("bob"); + let bob = String::from("bob"); let bond_msg = ExecuteMsg::Bond {}; let info = mock_info(&bob, &[coin(10, "random"), coin(1000, "ustake")]); let res = execute(deps.as_mut(), mock_env(), info, bond_msg).unwrap(); @@ -650,7 +661,7 @@ mod tests { assert_eq!(invest.nominal_value, ratio); // we bond some other tokens and get a different issuance price (maintaining the ratio) - let alice = HumanAddr::from("alice"); + let alice = String::from("alice"); let bond_msg = ExecuteMsg::Bond {}; let info = mock_info(&alice, &[coin(3000, "ustake")]); let res = execute(deps.as_mut(), mock_env(), info, bond_msg).unwrap(); @@ -673,7 +684,7 @@ mod tests { let mut deps = mock_dependencies(&[]); set_validator(&mut deps.querier); - let creator = HumanAddr::from("creator"); + let creator = String::from("creator"); let instantiate_msg = default_init(2, 50); let info = mock_info(&creator, &[]); @@ -682,7 +693,7 @@ mod tests { assert_eq!(0, res.messages.len()); // let's bond some tokens now - let bob = HumanAddr::from("bob"); + let bob = String::from("bob"); let bond_msg = ExecuteMsg::Bond {}; let info = mock_info(&bob, &[coin(500, "photon")]); @@ -701,7 +712,7 @@ mod tests { let mut deps = mock_dependencies(&[]); set_validator(&mut deps.querier); - let creator = HumanAddr::from("creator"); + let creator = String::from("creator"); let instantiate_msg = default_init(10, 50); let info = mock_info(&creator, &[]); @@ -710,7 +721,7 @@ mod tests { assert_eq!(0, res.messages.len()); // let's bond some tokens now - let bob = HumanAddr::from("bob"); + let bob = String::from("bob"); let bond_msg = ExecuteMsg::Bond {}; let info = mock_info(&bob, &[coin(10, "random"), coin(1000, "ustake")]); let res = execute(deps.as_mut(), mock_env(), info, bond_msg).unwrap(); diff --git a/contracts/staking/src/msg.rs b/contracts/staking/src/msg.rs index 5742fc0caa..0603597186 100644 --- a/contracts/staking/src/msg.rs +++ b/contracts/staking/src/msg.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{Coin, Decimal, HumanAddr, Uint128}; +use cosmwasm_std::{Coin, Decimal, Uint128}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct InstantiateMsg { @@ -15,7 +15,7 @@ pub struct InstantiateMsg { pub decimals: u8, /// This is the validator that all tokens will be bonded to - pub validator: HumanAddr, + pub validator: String, /// this is how much the owner takes as a cut when someone unbonds /// TODO @@ -29,10 +29,7 @@ pub struct InstantiateMsg { #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { /// Transfer moves the derivative token - Transfer { - recipient: HumanAddr, - amount: Uint128, - }, + Transfer { recipient: String, amount: Uint128 }, /// Bond will bond all staking tokens sent with the message and release derivative tokens Bond {}, /// Unbond will "burn" the given amount of derivative tokens and send the unbonded @@ -55,9 +52,9 @@ pub enum ExecuteMsg { #[serde(rename_all = "snake_case")] pub enum QueryMsg { /// Balance shows the number of staking derivatives - Balance { address: HumanAddr }, + Balance { address: String }, /// Claims shows the number of tokens this address can access when they are done unbonding - Claims { address: HumanAddr }, + Claims { address: String }, /// TokenInfo shows the metadata of the token for UIs TokenInfo {}, /// Investment shows info on total staking tokens under custody, @@ -95,11 +92,11 @@ pub struct InvestmentResponse { pub nominal_value: Decimal, /// owner created the contract and takes a cut - pub owner: HumanAddr, + pub owner: String, /// this is how much the owner takes as a cut when someone unbonds pub exit_tax: Decimal, /// All tokens are bonded to this validator - pub validator: HumanAddr, + pub validator: String, /// This is the minimum amount we will pull out to reinvest, as well as a minumum /// that can be unbonded (to avoid needless staking tx) pub min_withdrawal: Uint128, diff --git a/contracts/staking/src/state.rs b/contracts/staking/src/state.rs index d868af45ac..f59c2ccea1 100644 --- a/contracts/staking/src/state.rs +++ b/contracts/staking/src/state.rs @@ -1,14 +1,12 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{CanonicalAddr, Decimal, HumanAddr, Storage, Uint128}; +use cosmwasm_std::{Addr, Decimal, Storage, Uint128}; use cosmwasm_storage::{ bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton, Singleton, }; -use crate::msg::TokenInfoResponse; - pub const KEY_INVESTMENT: &[u8] = b"invest"; pub const KEY_TOKEN_INFO: &[u8] = b"token"; pub const KEY_TOTAL_SUPPLY: &[u8] = b"total_supply"; @@ -38,21 +36,32 @@ pub fn claims_read(storage: &dyn Storage) -> ReadonlyBucket { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct InvestmentInfo { /// owner created the contract and takes a cut - pub owner: CanonicalAddr, + pub owner: Addr, /// this is the denomination we can stake (and only one we accept for payments) pub bond_denom: String, /// this is how much the owner takes as a cut when someone unbonds pub exit_tax: Decimal, /// All tokens are bonded to this validator /// FIXME: humanize/canonicalize address doesn't work for validator addrresses - pub validator: HumanAddr, + pub validator: String, /// This is the minimum amount we will pull out to reinvest, as well as a minumum /// that can be unbonded (to avoid needless staking tx) pub min_withdrawal: Uint128, } +/// Info to display the derivative token in a UI +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct TokenInfo { + /// name of the derivative token + pub name: String, + /// symbol / ticker of the derivative token + pub symbol: String, + /// decimal places of the derivative token (for UI) + pub decimals: u8, +} + /// Supply is dynamic and tracks the current supply of staked and ERC20 tokens. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default, JsonSchema)] pub struct Supply { /// issued is how many derivative tokens this contract has issued pub issued: Uint128, @@ -70,11 +79,11 @@ pub fn invest_info_read(storage: &dyn Storage) -> ReadonlySingleton Singleton { +pub fn token_info(storage: &mut dyn Storage) -> Singleton { singleton(storage, KEY_TOKEN_INFO) } -pub fn token_info_read(storage: &dyn Storage) -> ReadonlySingleton { +pub fn token_info_read(storage: &dyn Storage) -> ReadonlySingleton { singleton_read(storage, KEY_TOKEN_INFO) } diff --git a/contracts/staking/tests/integration.rs b/contracts/staking/tests/integration.rs index 7a0203ca39..0d5fc5c33e 100644 --- a/contracts/staking/tests/integration.rs +++ b/contracts/staking/tests/integration.rs @@ -18,7 +18,7 @@ //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) use cosmwasm_std::{ - coin, from_binary, ContractResult, Decimal, HumanAddr, Response, Uint128, Validator, + coin, from_binary, Addr, ContractResult, Decimal, Response, Uint128, Validator, }; use cosmwasm_vm::testing::{ instantiate, mock_backend, mock_env, mock_info, mock_instance_options, query, @@ -35,9 +35,9 @@ static WASM: &[u8] = include_bytes!("../target/wasm32-unknown-unknown/release/st // You can uncomment this line instead to test productionified build from cosmwasm-opt // static WASM: &[u8] = include_bytes!("../contract.wasm"); -fn sample_validator>(addr: U) -> Validator { +fn sample_validator(addr: &str) -> Validator { Validator { - address: addr.into(), + address: Addr::unchecked(addr), commission: Decimal::percent(3), max_commission: Decimal::percent(10), max_change_rate: Decimal::percent(1), @@ -53,12 +53,12 @@ fn initialization_with_missing_validator() { let (instance_options, memory_limit) = mock_instance_options(); let mut deps = Instance::from_code(WASM, backend, instance_options, memory_limit).unwrap(); - let creator = HumanAddr::from("creator"); + let creator = String::from("creator"); let msg = InstantiateMsg { name: "Cool Derivative".to_string(), symbol: "DRV".to_string(), decimals: 9, - validator: HumanAddr::from("my-validator"), + validator: String::from("my-validator"), exit_tax: Decimal::percent(2), min_withdrawal: Uint128(50), }; @@ -91,12 +91,12 @@ fn proper_initialization() { assert_eq!(deps.required_features.len(), 1); assert!(deps.required_features.contains("staking")); - let creator = HumanAddr::from("creator"); + let creator = String::from("creator"); let msg = InstantiateMsg { name: "Cool Derivative".to_string(), symbol: "DRV".to_string(), decimals: 9, - validator: HumanAddr::from("my-validator"), + validator: String::from("my-validator"), exit_tax: Decimal::percent(2), min_withdrawal: Uint128(50), }; diff --git a/devtools/check_workspace.sh b/devtools/check_workspace.sh index 4a1666d55b..47cc56a23f 100755 --- a/devtools/check_workspace.sh +++ b/devtools/check_workspace.sh @@ -3,8 +3,8 @@ set -o errexit -o nounset -o pipefail command -v shellcheck >/dev/null && shellcheck "$0" cargo fmt -(cd packages/crypto && cargo build && cargo test && cargo clippy --tests -- -D warnings) -(cd packages/std && cargo wasm-debug --features iterator && cargo test --features iterator && cargo clippy --tests --features iterator -- -D warnings && cargo schema) -(cd packages/storage && cargo build && cargo test --features iterator && cargo clippy --tests --features iterator -- -D warnings) -(cd packages/schema && cargo build && cargo test && cargo clippy --tests -- -D warnings) -(cd packages/vm && cargo build --features iterator,stargate && cargo test --features iterator,stargate && cargo clippy --tests --features iterator,stargate -- -D warnings) +(cd packages/crypto && cargo build && cargo clippy --tests -- -D warnings) +(cd packages/std && cargo wasm-debug --features iterator && cargo clippy --tests --features iterator -- -D warnings && cargo schema) +(cd packages/storage && cargo build && cargo clippy --tests --features iterator -- -D warnings) +(cd packages/schema && cargo build && cargo clippy --tests -- -D warnings) +(cd packages/vm && cargo build --features iterator,stargate && cargo clippy --tests --features iterator,stargate -- -D warnings) diff --git a/devtools/test_workspace.sh b/devtools/test_workspace.sh new file mode 100755 index 0000000000..b2d3a1771f --- /dev/null +++ b/devtools/test_workspace.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -o errexit -o nounset -o pipefail +command -v shellcheck >/dev/null && shellcheck "$0" + +cargo fmt +(cd packages/crypto && cargo test) +(cd packages/std && cargo test --features iterator) +(cd packages/storage && cargo test --features iterator) +(cd packages/schema && cargo test) +(cd packages/vm && cargo test --features iterator,stargate) diff --git a/packages/std/examples/schema.rs b/packages/std/examples/schema.rs index 0c1eb1ca96..ff23a86fe7 100644 --- a/packages/std/examples/schema.rs +++ b/packages/std/examples/schema.rs @@ -1,8 +1,8 @@ use std::env::current_dir; use std::fs::create_dir_all; -use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for}; -use cosmwasm_std::{CosmosMsg, Env, MessageInfo}; +use cosmwasm_schema::{export_schema_with_title, remove_schemas, schema_for}; +use cosmwasm_std::CosmosMsg; fn main() { let mut out_dir = current_dir().unwrap(); @@ -10,7 +10,5 @@ fn main() { create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); - export_schema(&schema_for!(Env), &out_dir); - export_schema(&schema_for!(MessageInfo), &out_dir); export_schema_with_title(&mut schema_for!(CosmosMsg), &out_dir, "CosmosMsg"); } diff --git a/packages/std/schema/cosmos_msg.json b/packages/std/schema/cosmos_msg.json index 9d7ce5b6b0..f087876f08 100644 --- a/packages/std/schema/cosmos_msg.json +++ b/packages/std/schema/cosmos_msg.json @@ -76,7 +76,7 @@ } }, "to_address": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -108,9 +108,6 @@ "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", "type": "object" }, - "HumanAddr": { - "type": "string" - }, "StakingMsg": { "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", "anyOf": [ @@ -132,7 +129,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -157,7 +154,7 @@ "$ref": "#/definitions/Coin" }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -179,17 +176,13 @@ "properties": { "recipient": { "description": "this is the \"withdraw address\", the one that should receive the rewards if None, then use delegator address", - "anyOf": [ - { - "$ref": "#/definitions/HumanAddr" - }, - { - "type": "null" - } + "type": [ + "string", + "null" ] }, "validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -215,10 +208,10 @@ "$ref": "#/definitions/Coin" }, "dst_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "src_validator": { - "$ref": "#/definitions/HumanAddr" + "type": "string" } } } @@ -249,7 +242,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", @@ -330,7 +323,7 @@ ], "properties": { "contract_addr": { - "$ref": "#/definitions/HumanAddr" + "type": "string" }, "msg": { "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", diff --git a/packages/std/schema/env.json b/packages/std/schema/env.json deleted file mode 100644 index 5a44676efe..0000000000 --- a/packages/std/schema/env.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Env", - "type": "object", - "required": [ - "block", - "contract" - ], - "properties": { - "block": { - "$ref": "#/definitions/BlockInfo" - }, - "contract": { - "$ref": "#/definitions/ContractInfo" - } - }, - "definitions": { - "BlockInfo": { - "type": "object", - "required": [ - "chain_id", - "height", - "time", - "time_nanos" - ], - "properties": { - "chain_id": { - "type": "string" - }, - "height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "time": { - "description": "Absolute time of the block creation in seconds since the UNIX epoch (00:00:00 on 1970-01-01 UTC).\n\nThe source of this is the [BFT Time in Tendermint](https://docs.tendermint.com/master/spec/consensus/bft-time.html), converted from nanoseconds to second precision by truncating the fractioal part.", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "time_nanos": { - "description": "The fractional part of the block time in nanoseconds since `time` (0 to 999999999). Add this to `time` if you need a high precision block time.\n\n# Examples\n\nUsing chrono:\n\n``` # use cosmwasm_std::{BlockInfo, ContractInfo, Env, HumanAddr, MessageInfo}; # let env = Env { # block: BlockInfo { # height: 12_345, # time: 1_571_797_419, # time_nanos: 879305533, # chain_id: \"cosmos-testnet-14002\".to_string(), # }, # contract: ContractInfo { # address: HumanAddr::from(\"contract\"), # }, # }; # extern crate chrono; use chrono::NaiveDateTime; let dt = NaiveDateTime::from_timestamp(env.block.time as i64, env.block.time_nanos as u32); ```\n\nCreating a simple millisecond-precision timestamp (as used in JavaScript):\n\n``` # use cosmwasm_std::{BlockInfo, ContractInfo, Env, HumanAddr, MessageInfo}; # let env = Env { # block: BlockInfo { # height: 12_345, # time: 1_571_797_419, # time_nanos: 879305533, # chain_id: \"cosmos-testnet-14002\".to_string(), # }, # contract: ContractInfo { # address: HumanAddr::from(\"contract\"), # }, # }; let millis = (env.block.time * 1_000) + (env.block.time_nanos / 1_000_000); ```", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - } - }, - "ContractInfo": { - "type": "object", - "required": [ - "address" - ], - "properties": { - "address": { - "$ref": "#/definitions/HumanAddr" - } - } - }, - "HumanAddr": { - "type": "string" - } - } -} diff --git a/packages/std/schema/message_info.json b/packages/std/schema/message_info.json deleted file mode 100644 index ee1732e1b6..0000000000 --- a/packages/std/schema/message_info.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "MessageInfo", - "description": "Additional information from [MsgInstantiateContract] and [MsgExecuteContract], which is passed along with the contract execution message into the `instantiate` and `execute` entry points.\n\nIt contains the essential info for authorization - identity of the call, and payment.\n\n[MsgInstantiateContract]: https://github.com/CosmWasm/wasmd/blob/v0.15.0/x/wasm/internal/types/tx.proto#L47-L61 [MsgExecuteContract]: https://github.com/CosmWasm/wasmd/blob/v0.15.0/x/wasm/internal/types/tx.proto#L68-L78", - "type": "object", - "required": [ - "funds", - "sender" - ], - "properties": { - "funds": { - "description": "The funds that are sent to the contract as part of `MsgInstantiateContract` or `MsgExecuteContract`. The transfer is processed in bank before the contract is executed such that the new balance is visible during contract execution.", - "type": "array", - "items": { - "$ref": "#/definitions/Coin" - } - }, - "sender": { - "description": "The `sender` field from `MsgInstantiateContract` and `MsgExecuteContract`. You can think of this as the address that initiated the action (i.e. the message). What that means exactly heavily depends on the application.\n\nThe x/wasm module ensures that the sender address signed the transaction or is otherwise authorized to send the message.\n\nAdditional signers of the transaction that are either needed for other messages or contain unnecessary signatures are not propagated into the contract.", - "allOf": [ - { - "$ref": "#/definitions/HumanAddr" - } - ] - } - }, - "definitions": { - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "denom": { - "type": "string" - } - } - }, - "HumanAddr": { - "type": "string" - }, - "Uint128": { - "type": "string" - } - } -} diff --git a/packages/std/src/addresses.rs b/packages/std/src/addresses.rs index 05a107d7b0..953866db31 100644 --- a/packages/std/src/addresses.rs +++ b/packages/std/src/addresses.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::fmt; @@ -5,6 +7,118 @@ use std::ops::Deref; use crate::binary::Binary; +/// A human readable address. +/// +/// In Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no +/// assumptions should be made other than being UTF-8 encoded and of reasonable length. +/// +/// This type represents a validated address. It can be created in the following ways +/// 1. Use `Addr::unchecked(input)` +/// 2. Use `let checked: Addr = deps.api.addr_validate(input)?` +/// 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` +/// 4. Deserialize from JSON. This must only be done from JSON that was validated before +/// such as a contract's state. `Addr` must not be used in messages sent by the user +/// because this would result in unvalidated instances. +/// +/// This type is immutable. If you really need to mutate it (Really? Are you sure?), create +/// a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` +/// instance. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, JsonSchema)] +pub struct Addr(String); + +impl Addr { + /// Creates a new `Addr` instance from the given input without checking the validity + /// of the input. Since `Addr` must always contain valid addresses, the caller is + /// responsible for ensuring the input is valid. + /// + /// Use this in cases where the address was validated before or in test code. + /// If you see this in contract code, it should most likely be replaced with + /// `let checked: Addr = deps.api.addr_humanize(canonical_addr)?`. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{Addr}; + /// let address = Addr::unchecked("foobar"); + /// assert_eq!(address, "foobar"); + /// ``` + pub fn unchecked>(input: T) -> Addr { + Addr(input.into()) + } +} + +impl fmt::Display for Addr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", &self.0) + } +} + +impl AsRef for Addr { + #[inline] + fn as_ref(&self) -> &str { + &self.0 + } +} + +/// Implement `Addr == &str` +impl PartialEq<&str> for Addr { + fn eq(&self, rhs: &&str) -> bool { + self.0 == *rhs + } +} + +/// Implement `&str == Addr` +impl PartialEq for &str { + fn eq(&self, rhs: &Addr) -> bool { + *self == rhs.0 + } +} + +/// Implement `Addr == String` +impl PartialEq for Addr { + fn eq(&self, rhs: &String) -> bool { + &self.0 == rhs + } +} + +/// Implement `String == Addr` +impl PartialEq for String { + fn eq(&self, rhs: &Addr) -> bool { + self == &rhs.0 + } +} + +// Addr->String and Addr->HumanAddr are safe conversions. +// However, the opposite direction is unsafe and must not be implemented. + +impl From for String { + fn from(addr: Addr) -> Self { + addr.0 + } +} + +impl From<&Addr> for String { + fn from(addr: &Addr) -> Self { + addr.0.clone() + } +} + +impl From for HumanAddr { + fn from(addr: Addr) -> Self { + HumanAddr(addr.0) + } +} + +impl From<&Addr> for HumanAddr { + fn from(addr: &Addr) -> Self { + HumanAddr(addr.0.clone()) + } +} + +#[deprecated( + since = "0.14.0", + note = "HumanAddr is not much more than an alias to String and it does not provide significant safety advantages. With CosmWasm 0.14, we now use String when there was HumanAddr before. There is also the new Addr, which holds a validated immutable human readable address." +)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, JsonSchema)] pub struct HumanAddr(pub String); @@ -151,6 +265,77 @@ mod tests { use std::hash::{Hash, Hasher}; use std::iter::FromIterator; + #[test] + fn addr_unchecked_works() { + let a = Addr::unchecked("123"); + let aa = Addr::unchecked(String::from("123")); + let b = Addr::unchecked("be"); + assert_eq!(a, aa); + assert_ne!(a, b); + } + + #[test] + fn addr_implements_display() { + let addr = Addr::unchecked("cos934gh9034hg04g0h134"); + let embedded = format!("Address: {}", addr); + assert_eq!(embedded, "Address: cos934gh9034hg04g0h134"); + assert_eq!(addr.to_string(), "cos934gh9034hg04g0h134"); + } + + #[test] + fn addr_implements_as_ref_for_str() { + let addr = Addr::unchecked("literal-string"); + assert_eq!(addr.as_ref(), "literal-string"); + } + + #[test] + fn addr_implements_partial_eq_with_str() { + let addr = Addr::unchecked("cos934gh9034hg04g0h134"); + + // `Addr == &str` + assert_eq!(addr, "cos934gh9034hg04g0h134"); + // `&str == Addr` + assert_eq!("cos934gh9034hg04g0h134", addr); + } + + #[test] + fn addr_implements_partial_eq_with_string() { + let addr = Addr::unchecked("cos934gh9034hg04g0h134"); + + // `Addr == String` + assert_eq!(addr, String::from("cos934gh9034hg04g0h134")); + // `String == Addr` + assert_eq!(String::from("cos934gh9034hg04g0h134"), addr); + } + + #[test] + fn addr_implements_into_string() { + // owned Addr + let addr = Addr::unchecked("cos934gh9034hg04g0h134"); + let string: String = addr.into(); + assert_eq!(string, "cos934gh9034hg04g0h134"); + + // &Addr + let addr = Addr::unchecked("cos934gh9034hg04g0h134"); + let addr_ref = &addr; + let string: String = addr_ref.into(); + assert_eq!(string, "cos934gh9034hg04g0h134"); + } + + #[test] + fn addr_implements_into_human_address() { + // owned Addr + let addr = Addr::unchecked("cos934gh9034hg04g0h134"); + let human: HumanAddr = addr.into(); + assert_eq!(human, "cos934gh9034hg04g0h134"); + + // &Addr + let addr = Addr::unchecked("cos934gh9034hg04g0h134"); + let addr_ref = &addr; + let human: HumanAddr = addr_ref.into(); + assert_eq!(human, "cos934gh9034hg04g0h134"); + } + // Test HumanAddr as_str() for each HumanAddr::from input type #[test] fn human_addr_as_str() { diff --git a/packages/std/src/coins.rs b/packages/std/src/coins.rs index f2e77ad592..cc03665501 100644 --- a/packages/std/src/coins.rs +++ b/packages/std/src/coins.rs @@ -31,7 +31,7 @@ impl Coin { /// /// let mut response: Response = Default::default(); /// response.messages = vec![CosmosMsg::Bank(BankMsg::Send { -/// to_address: info.sender, +/// to_address: info.sender.into(), /// amount: tip, /// })]; /// ``` @@ -55,7 +55,7 @@ pub fn coins>(amount: u128, denom: S) -> Vec { /// /// let mut response: Response = Default::default(); /// response.messages = vec![CosmosMsg::Bank(BankMsg::Send { -/// to_address: info.sender, +/// to_address: info.sender.into(), /// amount: tip, /// })]; /// ``` diff --git a/packages/std/src/errors/system_error.rs b/packages/std/src/errors/system_error.rs index 7d4a095f2c..e04a6bbcd0 100644 --- a/packages/std/src/errors/system_error.rs +++ b/packages/std/src/errors/system_error.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::{Binary, HumanAddr}; +use crate::Binary; /// SystemError is used for errors inside the VM and is API friendly (i.e. serializable). /// @@ -16,11 +16,22 @@ use crate::{Binary, HumanAddr}; #[serde(rename_all = "snake_case")] #[non_exhaustive] pub enum SystemError { - InvalidRequest { error: String, request: Binary }, - InvalidResponse { error: String, response: Binary }, - NoSuchContract { addr: HumanAddr }, + InvalidRequest { + error: String, + request: Binary, + }, + InvalidResponse { + error: String, + response: Binary, + }, + NoSuchContract { + /// The address that was attempted to query + addr: String, + }, Unknown {}, - UnsupportedRequest { kind: String }, + UnsupportedRequest { + kind: String, + }, } impl std::error::Error for SystemError {} diff --git a/packages/std/src/ibc.rs b/packages/std/src/ibc.rs index 4f5bd5310d..725aaf84fe 100644 --- a/packages/std/src/ibc.rs +++ b/packages/std/src/ibc.rs @@ -7,7 +7,6 @@ use serde::{Deserialize, Serialize}; use std::cmp::{Ord, Ordering, PartialOrd}; use std::fmt; -use crate::addresses::HumanAddr; use crate::binary::Binary; use crate::coins::Coin; use crate::results::{Attribute, CosmosMsg, Empty, SubMsg}; @@ -27,7 +26,7 @@ pub enum IbcMsg { /// exisiting channel to send the tokens over channel_id: String, /// address on the remote chain to receive these tokens - to_address: HumanAddr, + to_address: String, /// packet data only supports one coin /// https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20 amount: Coin, diff --git a/packages/std/src/imports.rs b/packages/std/src/imports.rs index 8baf2e7d8d..33aab44a6c 100644 --- a/packages/std/src/imports.rs +++ b/packages/std/src/imports.rs @@ -1,6 +1,6 @@ use std::vec::Vec; -use crate::addresses::{CanonicalAddr, HumanAddr}; +use crate::addresses::{Addr, CanonicalAddr}; use crate::binary::Binary; use crate::errors::{RecoverPubkeyError, StdError, StdResult, SystemError, VerificationError}; use crate::import_helpers::{from_high_half, from_low_half}; @@ -36,8 +36,9 @@ extern "C" { #[cfg(feature = "iterator")] fn db_next(iterator_id: u32) -> u32; - fn canonicalize_address(source_ptr: u32, destination_ptr: u32) -> u32; - fn humanize_address(source_ptr: u32, destination_ptr: u32) -> u32; + fn addr_validate(source_ptr: u32) -> u32; + fn addr_canonicalize(source_ptr: u32, destination_ptr: u32) -> u32; + fn addr_humanize(source_ptr: u32, destination_ptr: u32) -> u32; fn secp256k1_verify(message_hash_ptr: u32, signature_ptr: u32, public_key_ptr: u32) -> u32; fn secp256k1_recover_pubkey( @@ -155,16 +156,32 @@ impl ExternalApi { } impl Api for ExternalApi { - fn canonical_address(&self, human: &HumanAddr) -> StdResult { - let send = build_region(human.as_str().as_bytes()); + fn addr_validate(&self, human: &str) -> StdResult { + let source = build_region(human.as_bytes()); + let source_ptr = &*source as *const Region as u32; + + let result = unsafe { addr_validate(source_ptr) }; + if result != 0 { + let error = unsafe { consume_string_region_written_by_vm(result as *mut Region) }; + return Err(StdError::generic_err(format!( + "addr_validate errored: {}", + error + ))); + } + + Ok(Addr::unchecked(human)) + } + + fn addr_canonicalize(&self, human: &str) -> StdResult { + let send = build_region(human.as_bytes()); let send_ptr = &*send as *const Region as u32; let canon = alloc(CANONICAL_ADDRESS_BUFFER_LENGTH); - let result = unsafe { canonicalize_address(send_ptr, canon as u32) }; + let result = unsafe { addr_canonicalize(send_ptr, canon as u32) }; if result != 0 { let error = unsafe { consume_string_region_written_by_vm(result as *mut Region) }; return Err(StdError::generic_err(format!( - "canonicalize_address errored: {}", + "addr_canonicalize errored: {}", error ))); } @@ -173,22 +190,22 @@ impl Api for ExternalApi { Ok(CanonicalAddr(Binary(out))) } - fn human_address(&self, canonical: &CanonicalAddr) -> StdResult { + fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult { let send = build_region(&canonical); let send_ptr = &*send as *const Region as u32; let human = alloc(HUMAN_ADDRESS_BUFFER_LENGTH); - let result = unsafe { humanize_address(send_ptr, human as u32) }; + let result = unsafe { addr_humanize(send_ptr, human as u32) }; if result != 0 { let error = unsafe { consume_string_region_written_by_vm(result as *mut Region) }; return Err(StdError::generic_err(format!( - "humanize_address errored: {}", + "addr_humanize errored: {}", error ))); } let address = unsafe { consume_string_region_written_by_vm(human) }; - Ok(address.into()) + Ok(Addr::unchecked(address)) } fn secp256k1_verify( diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index 069e348733..1274357451 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -22,7 +22,8 @@ mod storage; mod traits; mod types; -pub use crate::addresses::{CanonicalAddr, HumanAddr}; +#[allow(deprecated)] +pub use crate::addresses::{Addr, CanonicalAddr, HumanAddr}; pub use crate::binary::Binary; pub use crate::coins::{coin, coins, has_coins, Coin}; pub use crate::deps::{Deps, DepsMut, OwnedDeps}; diff --git a/packages/std/src/mock.rs b/packages/std/src/mock.rs index 23038073f1..836083f3cb 100644 --- a/packages/std/src/mock.rs +++ b/packages/std/src/mock.rs @@ -3,7 +3,7 @@ use serde::de::DeserializeOwned; use serde::Serialize; use std::collections::HashMap; -use crate::addresses::{CanonicalAddr, HumanAddr}; +use crate::addresses::{Addr, CanonicalAddr}; use crate::binary::Binary; use crate::coins::Coin; use crate::deps::OwnedDeps; @@ -28,18 +28,17 @@ pub const MOCK_CONTRACT_ADDR: &str = "cosmos2contract"; pub fn mock_dependencies( contract_balance: &[Coin], ) -> OwnedDeps { - let contract_addr = HumanAddr::from(MOCK_CONTRACT_ADDR); OwnedDeps { storage: MockStorage::default(), api: MockApi::default(), - querier: MockQuerier::new(&[(&contract_addr, contract_balance)]), + querier: MockQuerier::new(&[(MOCK_CONTRACT_ADDR, contract_balance)]), } } /// Initializes the querier along with the mock_dependencies. /// Sets all balances provided (yoy must explicitly set contract balance if desired) pub fn mock_dependencies_with_balances( - balances: &[(&HumanAddr, &[Coin])], + balances: &[(&str, &[Coin])], ) -> OwnedDeps { OwnedDeps { storage: MockStorage::default(), @@ -71,7 +70,12 @@ impl Default for MockApi { } impl Api for MockApi { - fn canonical_address(&self, human: &HumanAddr) -> StdResult { + fn addr_validate(&self, human: &str) -> StdResult { + self.addr_canonicalize(human).map(|_canonical| ())?; + Ok(Addr::unchecked(human)) + } + + fn addr_canonicalize(&self, human: &str) -> StdResult { // Dummy input validation. This is more sophisticated for formats like bech32, where format and checksum are validated. if human.len() < 3 { return Err(StdError::generic_err( @@ -84,7 +88,7 @@ impl Api for MockApi { )); } - let mut out = Vec::from(human.as_str()); + let mut out = Vec::from(human); // pad to canonical length with NULL bytes out.resize(self.canonical_length, 0x00); @@ -98,7 +102,7 @@ impl Api for MockApi { Ok(out.into()) } - fn human_address(&self, canonical: &CanonicalAddr) -> StdResult { + fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult { if canonical.len() != self.canonical_length { return Err(StdError::generic_err( "Invalid input: canonical address length not correct", @@ -117,7 +121,7 @@ impl Api for MockApi { let trimmed = tmp.into_iter().filter(|&x| x != 0x00).collect(); // decode UTF-8 bytes into string let human = String::from_utf8(trimmed)?; - Ok(human.into()) + Ok(Addr::unchecked(human)) } fn secp256k1_verify( @@ -187,16 +191,16 @@ pub fn mock_env() -> Env { chain_id: "cosmos-testnet-14002".to_string(), }, contract: ContractInfo { - address: HumanAddr::from(MOCK_CONTRACT_ADDR), + address: Addr::unchecked(MOCK_CONTRACT_ADDR), }, } } /// Just set sender and funds for the message. /// This is intended for use in test code only. -pub fn mock_info>(sender: U, funds: &[Coin]) -> MessageInfo { +pub fn mock_info(sender: &str, funds: &[Coin]) -> MessageInfo { MessageInfo { - sender: sender.into(), + sender: Addr::unchecked(sender), funds: funds.to_vec(), } } @@ -287,7 +291,7 @@ pub struct MockQuerier { } impl MockQuerier { - pub fn new(balances: &[(&HumanAddr, &[Coin])]) -> Self { + pub fn new(balances: &[(&str, &[Coin])]) -> Self { MockQuerier { bank: BankQuerier::new(balances), staking: StakingQuerier::default(), @@ -302,7 +306,7 @@ impl MockQuerier { } // set a new balance for the given address and return the old balance - pub fn update_balance>( + pub fn update_balance>( &mut self, addr: U, balance: Vec, @@ -381,14 +385,14 @@ impl NoWasmQuerier { #[derive(Clone, Default)] pub struct BankQuerier { - balances: HashMap>, + balances: HashMap>, } impl BankQuerier { - pub fn new(balances: &[(&HumanAddr, &[Coin])]) -> Self { + pub fn new(balances: &[(&str, &[Coin])]) -> Self { let mut map = HashMap::new(); for (addr, coins) in balances.iter() { - map.insert(HumanAddr::from(addr), coins.to_vec()); + map.insert(addr.to_string(), coins.to_vec()); } BankQuerier { balances: map } } @@ -457,7 +461,7 @@ impl StakingQuerier { let delegations: Vec<_> = self .delegations .iter() - .filter(|d| &d.delegator == delegator) + .filter(|d| d.delegator.as_ref() == delegator) .cloned() .map(|d| d.into()) .collect(); @@ -468,10 +472,9 @@ impl StakingQuerier { delegator, validator, } => { - let delegation = self - .delegations - .iter() - .find(|d| &d.delegator == delegator && &d.validator == validator); + let delegation = self.delegations.iter().find(|d| { + d.delegator.as_ref() == delegator && d.validator.as_ref() == validator + }); let res = DelegationResponse { delegation: delegation.cloned(), }; @@ -510,7 +513,7 @@ pub fn digit_sum(input: &[u8]) -> usize { mod tests { use super::*; use crate::query::Delegation; - use crate::{coin, coins, from_binary, Decimal, HumanAddr}; + use crate::{coin, coins, from_binary, Decimal}; use hex_literal::hex; const SECP256K1_MSG_HASH_HEX: &str = @@ -524,51 +527,63 @@ mod tests { "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c"; #[test] - fn mock_info_arguments() { - let name = HumanAddr("my name".to_string()); - - // make sure we can generate with &str, &HumanAddr, and HumanAddr - let a = mock_info("my name", &coins(100, "atom")); - let b = mock_info(&name, &coins(100, "atom")); - let c = mock_info(name, &coins(100, "atom")); - - // and the results are the same - assert_eq!(a, b); - assert_eq!(a, c); + fn mock_info_works() { + let info = mock_info("my name", &coins(100, "atom")); + assert_eq!( + info, + MessageInfo { + sender: Addr::unchecked("my name"), + funds: vec![Coin { + amount: 100u128.into(), + denom: "atom".into(), + }] + } + ); } #[test] fn canonicalize_and_humanize_restores_original() { let api = MockApi::default(); - let original = HumanAddr::from("shorty"); - let canonical = api.canonical_address(&original).unwrap(); - let recovered = api.human_address(&canonical).unwrap(); + let original = String::from("shorty"); + let canonical = api.addr_canonicalize(&original).unwrap(); + let recovered = api.addr_humanize(&canonical).unwrap(); assert_eq!(recovered, original); } #[test] - #[should_panic(expected = "length not correct")] - fn human_address_input_length() { + #[should_panic(expected = "address too short")] + fn addr_canonicalize_min_input_length() { let api = MockApi::default(); - let input = CanonicalAddr(Binary(vec![61; 11])); - api.human_address(&input).unwrap(); + let human = String::from("1"); + let _ = api.addr_canonicalize(&human).unwrap(); } #[test] - #[should_panic(expected = "address too short")] - fn canonical_address_min_input_length() { + #[should_panic(expected = "address too long")] + fn addr_canonicalize_max_input_length() { let api = MockApi::default(); - let human = HumanAddr("1".to_string()); - let _ = api.canonical_address(&human).unwrap(); + let human = String::from("some-extremely-long-address-not-supported-by-this-api"); + let _ = api.addr_canonicalize(&human).unwrap(); } #[test] - #[should_panic(expected = "address too long")] - fn canonical_address_max_input_length() { + fn addr_canonicalize_works_with_string_inputs() { let api = MockApi::default(); - let human = HumanAddr::from("some-extremely-long-address-not-supported-by-this-api"); - let _ = api.canonical_address(&human).unwrap(); + + let input = String::from("foobar123"); + api.addr_canonicalize(&input).unwrap(); + + let input = "foobar456"; + api.addr_canonicalize(&input).unwrap(); + } + + #[test] + #[should_panic(expected = "length not correct")] + fn addr_humanize_input_length() { + let api = MockApi::default(); + let input = CanonicalAddr(Binary(vec![61; 11])); + api.addr_humanize(&input).unwrap(); } // Basic "works" test. Exhaustive tests on VM's side (packages/vm/src/imports.rs) @@ -778,11 +793,10 @@ mod tests { #[test] fn bank_querier_all_balances() { - let addr = HumanAddr::from("foobar"); + let addr = String::from("foobar"); let balance = vec![coin(123, "ELF"), coin(777, "FLY")]; let bank = BankQuerier::new(&[(&addr, &balance)]); - // all let all = bank .query(&BankQuery::AllBalances { address: addr }) .unwrap() @@ -793,7 +807,7 @@ mod tests { #[test] fn bank_querier_one_balance() { - let addr = HumanAddr::from("foobar"); + let addr = String::from("foobar"); let balance = vec![coin(123, "ELF"), coin(777, "FLY")]; let bank = BankQuerier::new(&[(&addr, &balance)]); @@ -822,14 +836,14 @@ mod tests { #[test] fn bank_querier_missing_account() { - let addr = HumanAddr::from("foobar"); + let addr = String::from("foobar"); let balance = vec![coin(123, "ELF"), coin(777, "FLY")]; let bank = BankQuerier::new(&[(&addr, &balance)]); // all balances on empty account is empty vec let all = bank .query(&BankQuery::AllBalances { - address: HumanAddr::from("elsewhere"), + address: String::from("elsewhere"), }) .unwrap() .unwrap(); @@ -839,7 +853,7 @@ mod tests { // any denom on balances on empty account is empty coin let miss = bank .query(&BankQuery::Balance { - address: HumanAddr::from("elsewhere"), + address: String::from("elsewhere"), denom: "ELF".to_string(), }) .unwrap() @@ -851,13 +865,13 @@ mod tests { #[test] fn staking_querier_validators() { let val1 = Validator { - address: HumanAddr::from("validator-one"), + address: Addr::unchecked("validator-one"), commission: Decimal::percent(1), max_commission: Decimal::percent(3), max_change_rate: Decimal::percent(1), }; let val2 = Validator { - address: HumanAddr::from("validator-two"), + address: Addr::unchecked("validator-two"), commission: Decimal::permille(15), max_commission: Decimal::permille(40), max_change_rate: Decimal::permille(5), @@ -875,9 +889,14 @@ mod tests { } // gets delegators from query or panic - fn get_all_delegators(staking: &StakingQuerier, delegator: HumanAddr) -> Vec { + fn get_all_delegators>( + staking: &StakingQuerier, + delegator: U, + ) -> Vec { let raw = staking - .query(&StakingQuery::AllDelegations { delegator }) + .query(&StakingQuery::AllDelegations { + delegator: delegator.into(), + }) .unwrap() .unwrap(); let dels: AllDelegationsResponse = from_binary(&raw).unwrap(); @@ -885,15 +904,15 @@ mod tests { } // gets full delegators from query or panic - fn get_delegator( + fn get_delegator, V: Into>( staking: &StakingQuerier, - delegator: HumanAddr, - validator: HumanAddr, + delegator: U, + validator: V, ) -> Option { let raw = staking .query(&StakingQuery::Delegation { - delegator, - validator, + delegator: delegator.into(), + validator: validator.into(), }) .unwrap() .unwrap(); @@ -903,12 +922,12 @@ mod tests { #[test] fn staking_querier_delegations() { - let val1 = HumanAddr::from("validator-one"); - let val2 = HumanAddr::from("validator-two"); + let val1 = Addr::unchecked("validator-one"); + let val2 = Addr::unchecked("validator-two"); - let user_a = HumanAddr::from("investor"); - let user_b = HumanAddr::from("speculator"); - let user_c = HumanAddr::from("hodler"); + let user_a = Addr::unchecked("investor"); + let user_b = Addr::unchecked("speculator"); + let user_c = Addr::unchecked("hodler"); // we need multiple validators per delegator, so the queries provide different results let del1a = FullDelegation { @@ -963,7 +982,7 @@ mod tests { assert_eq!(dels, vec![del2c.clone().into()]); // for user with no delegations... - let dels = get_all_delegators(&staking, HumanAddr::from("no one")); + let dels = get_all_delegators(&staking, String::from("no one")); assert_eq!(dels, vec![]); // filter a by validator (1 and 1) diff --git a/packages/std/src/query.rs b/packages/std/src/query.rs index d120a67cd2..7491f82880 100644 --- a/packages/std/src/query.rs +++ b/packages/std/src/query.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::addresses::HumanAddr; +use crate::addresses::Addr; use crate::binary::Binary; use crate::coins::Coin; #[cfg(feature = "stargate")] @@ -38,11 +38,11 @@ pub enum QueryRequest { pub enum BankQuery { /// This calls into the native bank module for one denomination /// Return value is BalanceResponse - Balance { address: HumanAddr, denom: String }, + Balance { address: String, denom: String }, /// This calls into the native bank module for all denominations. /// Note that this may be much more expensive than Balance and should be avoided if possible. /// Return value is AllBalanceResponse. - AllBalances { address: HumanAddr }, + AllBalances { address: String }, } /// A trait that is required to avoid conflicts with other query types like BankQuery and WasmQuery @@ -75,14 +75,14 @@ pub enum WasmQuery { /// this queries the public API of another contract at a known address (with known ABI) /// return value is whatever the contract returns (caller should know) Smart { - contract_addr: HumanAddr, + contract_addr: String, /// msg is the json-encoded QueryMsg struct msg: Binary, }, /// this queries the raw kv-store of the contract. /// returns the raw, unparsed data stored at that key, which may be an empty vector if not present Raw { - contract_addr: HumanAddr, + contract_addr: String, /// Key is the raw key used in the contracts Storage key: Binary, }, @@ -150,12 +150,12 @@ pub enum StakingQuery { /// Returns the denomination that can be bonded (if there are multiple native tokens on the chain) BondedDenom {}, /// AllDelegations will return all delegations by the delegator - AllDelegations { delegator: HumanAddr }, + AllDelegations { delegator: String }, /// Delegation will return more detailed info on a particular /// delegation, defined by delegator/validator pair Delegation { - delegator: HumanAddr, - validator: HumanAddr, + delegator: String, + validator: String, }, /// Returns all registered Validators on the system Validators {}, @@ -175,11 +175,13 @@ pub struct AllDelegationsResponse { pub delegations: Vec, } -/// Delegation is basic (cheap to query) data about a delegation +/// Delegation is basic (cheap to query) data about a delegation. +/// +/// Instances are created in the querier. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct Delegation { - pub delegator: HumanAddr, - pub validator: HumanAddr, + pub delegator: Addr, + pub validator: Addr, /// How much we have locked in the delegation pub amount: Coin, } @@ -202,11 +204,13 @@ pub struct DelegationResponse { } /// FullDelegation is all the info on the delegation, some (like accumulated_reward and can_redelegate) -/// is expensive to query +/// is expensive to query. +/// +/// Instances are created in the querier. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct FullDelegation { - pub delegator: HumanAddr, - pub validator: HumanAddr, + pub delegator: Addr, + pub validator: Addr, /// How much we have locked in the delegation pub amount: Coin, /// can_redelegate captures how much can be immediately redelegated. @@ -223,9 +227,10 @@ pub struct ValidatorsResponse { pub validators: Vec, } +/// Instances are created in the querier. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct Validator { - pub address: HumanAddr, + pub address: Addr, pub commission: Decimal, pub max_commission: Decimal, /// TODO: what units are these (in terms of time)? diff --git a/packages/std/src/results/attribute.rs b/packages/std/src/results/attribute.rs index e068dfc98f..619e12bbad 100644 --- a/packages/std/src/results/attribute.rs +++ b/packages/std/src/results/attribute.rs @@ -19,7 +19,6 @@ pub fn attr(key: K, value: V) -> Attribute { #[cfg(test)] mod tests { use super::*; - use crate::addresses::HumanAddr; use crate::Uint128; #[test] @@ -32,7 +31,6 @@ mod tests { assert_eq!(attr("foo", "42"), expeceted); assert_eq!(attr("foo".to_string(), "42"), expeceted); assert_eq!(attr("foo", "42".to_string()), expeceted); - assert_eq!(attr("foo", HumanAddr::from("42")), expeceted); assert_eq!(attr("foo", Uint128(42)), expeceted); assert_eq!(attr("foo", 42), expeceted); } diff --git a/packages/std/src/results/context.rs b/packages/std/src/results/context.rs index 306efe328c..e75c9b19c0 100644 --- a/packages/std/src/results/context.rs +++ b/packages/std/src/results/context.rs @@ -74,7 +74,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{coins, BankMsg, HumanAddr, Response}; + use crate::{coins, BankMsg, Response}; #[test] fn empty_context() { @@ -89,17 +89,17 @@ mod tests { let mut ctx = Context::new(); // build it up with the builder commands - ctx.add_attribute("sender", &HumanAddr::from("john")); + ctx.add_attribute("sender", &String::from("john")); ctx.add_attribute("action", "test"); ctx.add_message(BankMsg::Send { - to_address: HumanAddr::from("foo"), + to_address: String::from("foo"), amount: coins(128, "uint"), }); ctx.set_data(b"banana"); // and this is what is should return let expected_msgs = vec![CosmosMsg::Bank(BankMsg::Send { - to_address: HumanAddr::from("foo"), + to_address: String::from("foo"), amount: coins(128, "uint"), })]; let expected_attributes = vec![attr("sender", "john"), attr("action", "test")]; diff --git a/packages/std/src/results/cosmos_msg.rs b/packages/std/src/results/cosmos_msg.rs index a2a0db0568..9c61c36c1c 100644 --- a/packages/std/src/results/cosmos_msg.rs +++ b/packages/std/src/results/cosmos_msg.rs @@ -2,7 +2,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::fmt; -use crate::addresses::HumanAddr; use crate::binary::Binary; use crate::coins::Coin; use crate::errors::StdResult; @@ -49,7 +48,7 @@ pub enum BankMsg { /// This is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). /// `from_address` is automatically filled with the current contract's address. Send { - to_address: HumanAddr, + to_address: String, amount: Vec, }, } @@ -63,24 +62,24 @@ pub enum BankMsg { pub enum StakingMsg { /// This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). /// `delegator_address` is automatically filled with the current contract's address. - Delegate { validator: HumanAddr, amount: Coin }, + Delegate { validator: String, amount: Coin }, /// This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). /// `delegator_address` is automatically filled with the current contract's address. - Undelegate { validator: HumanAddr, amount: Coin }, + Undelegate { validator: String, amount: Coin }, /// This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37) /// followed by a [MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). /// `delegator_address` is automatically filled with the current contract's address. Withdraw { - validator: HumanAddr, + validator: String, /// this is the "withdraw address", the one that should receive the rewards /// if None, then use delegator address - recipient: Option, + recipient: Option, }, /// This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). /// `delegator_address` is automatically filled with the current contract's address. Redelegate { - src_validator: HumanAddr, - dst_validator: HumanAddr, + src_validator: String, + dst_validator: String, amount: Coin, }, } @@ -97,7 +96,7 @@ pub enum WasmMsg { /// This is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). /// `sender` is automatically filled with the current contract's address. Execute { - contract_addr: HumanAddr, + contract_addr: String, /// msg is the json-encoded ExecuteMsg struct (as raw Binary) msg: Binary, send: Vec, @@ -122,7 +121,7 @@ pub enum WasmMsg { /// This is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). /// `sender` is automatically filled with the current contract's address. Migrate { - contract_addr: HumanAddr, + contract_addr: String, /// the code_id of the new logic to place in the given contract new_code_id: u64, /// msg is the json-encoded MigrateMsg struct that will be passed to the new code @@ -152,7 +151,7 @@ where /// Shortcut helper as the construction of WasmMsg::Instantiate can be quite verbose in contract code pub fn wasm_execute(contract_addr: T, msg: &U, send: Vec) -> StdResult where - T: Into, + T: Into, U: Serialize, { let payload = to_binary(msg)?; @@ -196,7 +195,7 @@ mod tests { #[test] fn from_bank_msg_works() { - let to_address = HumanAddr::from("you"); + let to_address = String::from("you"); let amount = coins(1015, "earth"); let bank = BankMsg::Send { to_address, amount }; let msg: CosmosMsg = bank.clone().into(); diff --git a/packages/std/src/results/response.rs b/packages/std/src/results/response.rs index f50d5a1334..85adfa4187 100644 --- a/packages/std/src/results/response.rs +++ b/packages/std/src/results/response.rs @@ -43,7 +43,7 @@ use crate::results::SubMsg; /// Mutating: /// /// ``` -/// # use cosmwasm_std::{coins, BankMsg, Binary, DepsMut, Env, HumanAddr, MessageInfo}; +/// # use cosmwasm_std::{coins, BankMsg, Binary, DepsMut, Env, MessageInfo}; /// # type InstantiateMsg = (); /// # type MyError = (); /// # @@ -60,7 +60,7 @@ use crate::results::SubMsg; /// response.add_attribute("Let the", "hacking begin"); /// // ... /// response.add_message(BankMsg::Send { -/// to_address: HumanAddr::from("recipient"), +/// to_address: String::from("recipient"), /// amount: coins(128, "uint"), /// }); /// response.add_attribute("foo", "bar"); @@ -145,7 +145,6 @@ where mod tests { use super::super::BankMsg; use super::*; - use crate::addresses::HumanAddr; use crate::{coins, from_slice, to_vec}; #[test] @@ -154,7 +153,7 @@ mod tests { submessages: vec![SubMsg { id: 12, msg: BankMsg::Send { - to_address: HumanAddr::from("checker"), + to_address: String::from("checker"), amount: coins(888, "moon"), } .into(), @@ -162,7 +161,7 @@ mod tests { reply_on: ReplyOn::Always, }], messages: vec![BankMsg::Send { - to_address: HumanAddr::from("you"), + to_address: String::from("you"), amount: coins(1015, "earth"), } .into()], diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs index 5d0c1f5496..52718b4a27 100644 --- a/packages/std/src/traits.rs +++ b/packages/std/src/traits.rs @@ -1,7 +1,7 @@ use serde::{de::DeserializeOwned, Serialize}; use std::ops::Deref; -use crate::addresses::{CanonicalAddr, HumanAddr}; +use crate::addresses::{Addr, CanonicalAddr}; use crate::binary::Binary; use crate::coins::Coin; use crate::errors::{RecoverPubkeyError, StdError, StdResult, VerificationError}; @@ -64,8 +64,28 @@ 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 { - fn canonical_address(&self, human: &HumanAddr) -> StdResult; - fn human_address(&self, canonical: &CanonicalAddr) -> StdResult; + /// Takes a human readable address and validates if it's correctly formatted. + /// If it succeeds, a Addr is returned. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{Api, Addr}; + /// # use cosmwasm_std::testing::MockApi; + /// # let api = MockApi::default(); + /// let input = "what-users-provide"; + /// let validated: Addr = api.addr_validate(input).unwrap(); + /// assert_eq!(validated, input); + /// ``` + fn addr_validate(&self, human: &str) -> StdResult; + + /// Takes a human readable address and returns a canonical binary representation of it. + /// This can be used when a compact fixed length representation is needed. + fn addr_canonicalize(&self, human: &str) -> StdResult; + + /// Takes a canonical address and returns a human readble address. + /// This is the inverse of [addr_canonicalize]. + fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult; fn secp256k1_verify( &self, @@ -162,17 +182,21 @@ impl<'a> QuerierWrapper<'a> { } } - pub fn query_balance>(&self, address: U, denom: &str) -> StdResult { + pub fn query_balance, V: Into>( + &self, + address: U, + denom: V, + ) -> StdResult { let request = BankQuery::Balance { address: address.into(), - denom: denom.to_string(), + denom: denom.into(), } .into(); let res: BalanceResponse = self.query(&request)?; Ok(res.amount) } - pub fn query_all_balances>(&self, address: U) -> StdResult> { + pub fn query_all_balances>(&self, address: U) -> StdResult> { let request = BankQuery::AllBalances { address: address.into(), } @@ -183,13 +207,13 @@ impl<'a> QuerierWrapper<'a> { // 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>( + pub fn query_wasm_smart>( &self, - contract: V, + contract_addr: V, msg: &U, ) -> StdResult { let request = WasmQuery::Smart { - contract_addr: contract.into(), + contract_addr: contract_addr.into(), msg: to_binary(msg)?, } .into(); @@ -203,13 +227,13 @@ impl<'a> QuerierWrapper<'a> { // // Similar return value to Storage.get(). Returns Some(val) or None if the data is there. // It only returns error on some runtime issue, not on any data cases. - pub fn query_wasm_raw, U: Into>( + pub fn query_wasm_raw, U: Into>( &self, - contract: T, + contract_addr: T, key: U, ) -> StdResult>> { let request: QueryRequest = WasmQuery::Raw { - contract_addr: contract.into(), + contract_addr: contract_addr.into(), key: key.into(), } .into(); @@ -251,7 +275,7 @@ impl<'a> QuerierWrapper<'a> { } #[cfg(feature = "staking")] - pub fn query_all_delegations>( + pub fn query_all_delegations>( &self, delegator: U, ) -> StdResult> { @@ -264,10 +288,10 @@ impl<'a> QuerierWrapper<'a> { } #[cfg(feature = "staking")] - pub fn query_delegation>( + pub fn query_delegation, V: Into>( &self, delegator: U, - validator: U, + validator: V, ) -> StdResult> { let request = StakingQuery::Delegation { delegator: delegator.into(), @@ -307,7 +331,7 @@ mod tests { #[test] fn auto_deref_raw_query() { - let acct = HumanAddr::from("foobar"); + let acct = String::from("foobar"); let querier: MockQuerier = MockQuerier::new(&[(&acct, &coins(5, "BTC"))]); let wrapper = QuerierWrapper::new(&querier); let query = QueryRequest::::Bank(BankQuery::Balance { diff --git a/packages/std/src/types.rs b/packages/std/src/types.rs index dd6d0911ad..b2e163c04b 100644 --- a/packages/std/src/types.rs +++ b/packages/std/src/types.rs @@ -1,16 +1,15 @@ -use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::addresses::HumanAddr; +use crate::addresses::Addr; use crate::coins::Coin; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Env { pub block: BlockInfo, pub contract: ContractInfo, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct BlockInfo { pub height: u64, /// Absolute time of the block creation in seconds since the UNIX epoch (00:00:00 on 1970-01-01 UTC). @@ -26,7 +25,7 @@ pub struct BlockInfo { /// Using chrono: /// /// ``` - /// # use cosmwasm_std::{BlockInfo, ContractInfo, Env, HumanAddr, MessageInfo}; + /// # use cosmwasm_std::{Addr, BlockInfo, ContractInfo, Env, MessageInfo}; /// # let env = Env { /// # block: BlockInfo { /// # height: 12_345, @@ -35,7 +34,7 @@ pub struct BlockInfo { /// # chain_id: "cosmos-testnet-14002".to_string(), /// # }, /// # contract: ContractInfo { - /// # address: HumanAddr::from("contract"), + /// # address: Addr::unchecked("contract"), /// # }, /// # }; /// # extern crate chrono; @@ -46,7 +45,7 @@ pub struct BlockInfo { /// Creating a simple millisecond-precision timestamp (as used in JavaScript): /// /// ``` - /// # use cosmwasm_std::{BlockInfo, ContractInfo, Env, HumanAddr, MessageInfo}; + /// # use cosmwasm_std::{Addr, BlockInfo, ContractInfo, Env, MessageInfo}; /// # let env = Env { /// # block: BlockInfo { /// # height: 12_345, @@ -55,7 +54,7 @@ pub struct BlockInfo { /// # chain_id: "cosmos-testnet-14002".to_string(), /// # }, /// # contract: ContractInfo { - /// # address: HumanAddr::from("contract"), + /// # address: Addr::unchecked("contract"), /// # }, /// # }; /// let millis = (env.block.time * 1_000) + (env.block.time_nanos / 1_000_000); @@ -71,7 +70,7 @@ pub struct BlockInfo { /// /// [MsgInstantiateContract]: https://github.com/CosmWasm/wasmd/blob/v0.15.0/x/wasm/internal/types/tx.proto#L47-L61 /// [MsgExecuteContract]: https://github.com/CosmWasm/wasmd/blob/v0.15.0/x/wasm/internal/types/tx.proto#L68-L78 -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct MessageInfo { /// The `sender` field from `MsgInstantiateContract` and `MsgExecuteContract`. /// You can think of this as the address that initiated the action (i.e. the message). What that @@ -82,14 +81,14 @@ pub struct MessageInfo { /// /// Additional signers of the transaction that are either needed for other messages or contain unnecessary /// signatures are not propagated into the contract. - pub sender: HumanAddr, + pub sender: Addr, /// The funds that are sent to the contract as part of `MsgInstantiateContract` /// or `MsgExecuteContract`. The transfer is processed in bank before the contract /// is executed such that the new balance is visible during contract execution. pub funds: Vec, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct ContractInfo { - pub address: HumanAddr, + pub address: Addr, } diff --git a/packages/vm/src/backend.rs b/packages/vm/src/backend.rs index 3fb238310a..11771bc361 100644 --- a/packages/vm/src/backend.rs +++ b/packages/vm/src/backend.rs @@ -3,7 +3,7 @@ use std::ops::AddAssign; use std::string::FromUtf8Error; use thiserror::Error; -use cosmwasm_std::{Binary, CanonicalAddr, ContractResult, HumanAddr, SystemResult}; +use cosmwasm_std::{Binary, ContractResult, SystemResult}; #[cfg(feature = "iterator")] use cosmwasm_std::{Order, Pair}; @@ -125,8 +125,8 @@ 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 BackendApi: Copy + Clone + Send { - fn canonical_address(&self, human: &HumanAddr) -> BackendResult; - fn human_address(&self, canonical: &CanonicalAddr) -> BackendResult; + fn canonical_address(&self, human: &str) -> BackendResult>; + fn human_address(&self, canonical: &[u8]) -> BackendResult; } pub trait Querier { diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index b593af0033..7c744ba393 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -13,8 +13,9 @@ const SUPPORTED_IMPORTS: &[&str] = &[ "env.db_read", "env.db_write", "env.db_remove", - "env.canonicalize_address", - "env.humanize_address", + "env.addr_validate", + "env.addr_canonicalize", + "env.addr_humanize", "env.secp256k1_verify", "env.secp256k1_recover_pubkey", "env.ed25519_verify", @@ -319,8 +320,9 @@ mod tests { (import "env" "db_read" (func (param i32 i32) (result i32))) (import "env" "db_write" (func (param i32 i32) (result i32))) (import "env" "db_remove" (func (param i32) (result i32))) - (import "env" "canonicalize_address" (func (param i32 i32) (result i32))) - (import "env" "humanize_address" (func (param i32 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))) @@ -354,8 +356,8 @@ mod tests { "env.db_read", "env.db_write", "env.db_remove", - "env.canonicalize_address", - "env.humanize_address", + "env.addr_canonicalize", + "env.addr_humanize", "env.debug", "env.query_chain", ]; @@ -365,7 +367,7 @@ mod tests { println!("{}", msg); assert_eq!( msg, - r#"Wasm contract requires unsupported import: "env.foo". Required imports: {"env.bar", "env.foo", "env.spammyspam01", "env.spammyspam02", "env.spammyspam03", "env.spammyspam04", "env.spammyspam05", "env.spammyspam06", "env.spammyspam07", "env.spammyspam08", ... 2 more}. Available imports: ["env.db_read", "env.db_write", "env.db_remove", "env.canonicalize_address", "env.humanize_address", "env.debug", "env.query_chain"]."# + r#"Wasm contract requires unsupported import: "env.foo". Required imports: {"env.bar", "env.foo", "env.spammyspam01", "env.spammyspam02", "env.spammyspam03", "env.spammyspam04", "env.spammyspam05", "env.spammyspam06", "env.spammyspam07", "env.spammyspam08", ... 2 more}. Available imports: ["env.db_read", "env.db_write", "env.db_remove", "env.addr_canonicalize", "env.addr_humanize", "env.debug", "env.query_chain"]."# ); } err => panic!("Unexpected error: {:?}", err), diff --git a/packages/vm/src/environment.rs b/packages/vm/src/environment.rs index 25ffd5afa4..d0e30ad5ad 100644 --- a/packages/vm/src/environment.rs +++ b/packages/vm/src/environment.rs @@ -392,7 +392,7 @@ mod tests { use crate::testing::{MockApi, MockQuerier, MockStorage}; use crate::wasm_backend::compile; use cosmwasm_std::{ - coins, from_binary, to_vec, AllBalanceResponse, BankQuery, Empty, HumanAddr, QueryRequest, + coins, from_binary, to_vec, AllBalanceResponse, BankQuery, Empty, QueryRequest, }; use wasmer::{imports, Function, Instance as WasmerInstance}; @@ -429,8 +429,9 @@ mod tests { "db_scan" => Function::new_native(&store, |_a: u32, _b: u32, _c: i32| -> u32 { 0 }), "db_next" => Function::new_native(&store, |_a: u32| -> u32 { 0 }), "query_chain" => Function::new_native(&store, |_a: u32| -> u32 { 0 }), - "canonicalize_address" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }), - "humanize_address" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }), + "addr_validate" => Function::new_native(&store, |_a: u32| -> u32 { 0 }), + "addr_canonicalize" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }), + "addr_humanize" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }), "secp256k1_verify" => Function::new_native(&store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }), "secp256k1_recover_pubkey" => Function::new_native(&store, |_a: u32, _b: u32, _c: u32| -> u64 { 0 }), "ed25519_verify" => Function::new_native(&store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }), @@ -455,7 +456,7 @@ mod tests { .0 .expect("error setting value"); let querier: MockQuerier = - MockQuerier::new(&[(&HumanAddr::from(INIT_ADDR), &coins(INIT_AMOUNT, INIT_DENOM))]); + MockQuerier::new(&[(INIT_ADDR, &coins(INIT_AMOUNT, INIT_DENOM))]); env.move_in(storage, querier); } @@ -812,7 +813,7 @@ mod tests { let res = env .with_querier_from_context::<_, _>(|querier| { let req: QueryRequest = QueryRequest::Bank(BankQuery::AllBalances { - address: HumanAddr::from(INIT_ADDR), + address: INIT_ADDR.to_string(), }); let (result, _gas_info) = querier.query_raw(&to_vec(&req).unwrap(), DEFAULT_QUERY_GAS_LIMIT); diff --git a/packages/vm/src/imports.rs b/packages/vm/src/imports.rs index d40b8fc1ab..31212133a3 100644 --- a/packages/vm/src/imports.rs +++ b/packages/vm/src/imports.rs @@ -13,7 +13,6 @@ use cosmwasm_crypto::{ #[cfg(feature = "iterator")] use cosmwasm_std::Order; -use cosmwasm_std::{CanonicalAddr, HumanAddr}; use crate::backend::{BackendApi, BackendError, Querier, Storage}; use crate::conversion::{ref_to_u32, to_u32}; @@ -74,20 +73,27 @@ pub fn native_db_remove( do_remove(env, key_ptr) } -pub fn native_canonicalize_address( +pub fn native_addr_validate( + env: &Environment, + source_ptr: u32, +) -> VmResult { + do_addr_validate(&env, source_ptr) +} + +pub fn native_addr_canonicalize( env: &Environment, source_ptr: u32, destination_ptr: u32, ) -> VmResult { - do_canonicalize_address(&env, source_ptr, destination_ptr) + do_addr_canonicalize(&env, source_ptr, destination_ptr) } -pub fn native_humanize_address( +pub fn native_addr_humanize( env: &Environment, source_ptr: u32, destination_ptr: u32, ) -> VmResult { - do_humanize_address(&env, source_ptr, destination_ptr) + do_addr_humanize(&env, source_ptr, destination_ptr) } pub fn native_secp256k1_verify( @@ -226,7 +232,32 @@ fn do_remove( Ok(()) } -fn do_canonicalize_address( +fn do_addr_validate( + env: &Environment, + source_ptr: u32, +) -> VmResult { + let source_data = read_region(&env.memory(), source_ptr, MAX_LENGTH_HUMAN_ADDRESS)?; + if source_data.is_empty() { + return write_to_contract::(env, b"Input is empty"); + } + + let source_string = match String::from_utf8(source_data) { + Ok(s) => s, + Err(_) => return write_to_contract::(env, b"Input is not valid UTF-8"), + }; + + let (result, gas_info) = env.api.canonical_address(&source_string); + process_gas_info::(env, gas_info)?; + match result { + Ok(_canonical) => Ok(0), + Err(BackendError::UserErr { msg, .. }) => { + Ok(write_to_contract::(env, msg.as_bytes())?) + } + Err(err) => Err(VmError::from(err)), + } +} + +fn do_addr_canonicalize( env: &Environment, source_ptr: u32, destination_ptr: u32, @@ -240,9 +271,8 @@ fn do_canonicalize_address( Ok(s) => s, Err(_) => return write_to_contract::(env, b"Input is not valid UTF-8"), }; - let human: HumanAddr = source_string.into(); - let (result, gas_info) = env.api.canonical_address(&human); + let (result, gas_info) = env.api.canonical_address(&source_string); process_gas_info::(env, gas_info)?; match result { Ok(canonical) => { @@ -256,19 +286,18 @@ fn do_canonicalize_address( } } -fn do_humanize_address( +fn do_addr_humanize( env: &Environment, source_ptr: u32, destination_ptr: u32, ) -> VmResult { - let canonical: CanonicalAddr = - read_region(&env.memory(), source_ptr, MAX_LENGTH_CANONICAL_ADDRESS)?.into(); + let canonical = read_region(&env.memory(), source_ptr, MAX_LENGTH_CANONICAL_ADDRESS)?; let (result, gas_info) = env.api.human_address(&canonical); process_gas_info::(env, gas_info)?; match result { Ok(human) => { - write_region(&env.memory(), destination_ptr, human.as_str().as_bytes())?; + write_region(&env.memory(), destination_ptr, human.as_bytes())?; Ok(0) } Err(BackendError::UserErr { msg, .. }) => { @@ -507,7 +536,7 @@ fn to_low_half(data: u32) -> u64 { mod tests { use super::*; use cosmwasm_std::{ - coins, from_binary, AllBalanceResponse, BankQuery, Binary, Empty, HumanAddr, QueryRequest, + coins, from_binary, AllBalanceResponse, BankQuery, Binary, Empty, QueryRequest, SystemError, SystemResult, WasmQuery, }; use hex_literal::hex; @@ -564,8 +593,9 @@ mod tests { "db_scan" => Function::new_native(&store, |_a: u32, _b: u32, _c: i32| -> u32 { 0 }), "db_next" => Function::new_native(&store, |_a: u32| -> u32 { 0 }), "query_chain" => Function::new_native(&store, |_a: u32| -> u32 { 0 }), - "canonicalize_address" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }), - "humanize_address" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }), + "addr_validate" => Function::new_native(&store, |_a: u32| -> u32 { 0 }), + "addr_canonicalize" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }), + "addr_humanize" => Function::new_native(&store, |_a: u32, _b: u32| -> u32 { 0 }), "secp256k1_verify" => Function::new_native(&store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }), "secp256k1_recover_pubkey" => Function::new_native(&store, |_a: u32, _b: u32, _c: u32| -> u64 { 0 }), "ed25519_verify" => Function::new_native(&store, |_a: u32, _b: u32, _c: u32| -> u32 { 0 }), @@ -589,7 +619,7 @@ mod tests { storage.set(KEY1, VALUE1).0.expect("error setting"); storage.set(KEY2, VALUE2).0.expect("error setting"); let querier: MockQuerier = - MockQuerier::new(&[(&HumanAddr::from(INIT_ADDR), &coins(INIT_AMOUNT, INIT_DENOM))]); + MockQuerier::new(&[(INIT_ADDR, &coins(INIT_AMOUNT, INIT_DENOM))]); env.move_in(storage, querier); } @@ -894,7 +924,93 @@ mod tests { } #[test] - fn do_canonicalize_address_works() { + fn do_addr_validate_works() { + let api = MockApi::default(); + let (env, _instance) = make_instance(api); + + let source_ptr = write_data(&env, b"foo"); + + leave_default_data(&env); + + let res = do_addr_validate(&env, source_ptr).unwrap(); + assert_eq!(res, 0); + } + + #[test] + fn do_addr_validate_reports_invalid_input_back_to_contract() { + let api = MockApi::default(); + let (env, _instance) = make_instance(api); + + let source_ptr1 = write_data(&env, b"fo\x80o"); // invalid UTF-8 (fo�o) + let source_ptr2 = write_data(&env, b""); // empty + let source_ptr3 = write_data(&env, b"addressexceedingaddressspace"); // too long + + leave_default_data(&env); + + let res = do_addr_validate(&env, source_ptr1).unwrap(); + assert_ne!(res, 0); + let err = String::from_utf8(force_read(&env, res)).unwrap(); + assert_eq!(err, "Input is not valid UTF-8"); + + let res = do_addr_validate(&env, source_ptr2).unwrap(); + assert_ne!(res, 0); + let err = String::from_utf8(force_read(&env, res)).unwrap(); + assert_eq!(err, "Input is empty"); + + let res = do_addr_validate(&env, source_ptr3).unwrap(); + assert_ne!(res, 0); + let err = String::from_utf8(force_read(&env, res)).unwrap(); + assert_eq!(err, "Invalid input: human address too long"); + } + + #[test] + fn do_addr_validate_fails_for_broken_backend() { + let api = MockApi::new_failing("Temporarily unavailable"); + let (env, _instance) = make_instance(api); + + let source_ptr = write_data(&env, b"foo"); + + leave_default_data(&env); + + let result = do_addr_validate(&env, source_ptr); + match result.unwrap_err() { + VmError::BackendErr { + source: BackendError::Unknown { msg, .. }, + .. + } => { + assert_eq!(msg.unwrap(), "Temporarily unavailable"); + } + err => panic!("Incorrect error returned: {:?}", err), + } + } + + #[test] + fn do_addr_validate_fails_for_large_inputs() { + let api = MockApi::default(); + let (env, _instance) = make_instance(api); + + let source_ptr = write_data(&env, &[61; 100]); + + leave_default_data(&env); + + let result = do_addr_validate(&env, source_ptr); + match result.unwrap_err() { + VmError::CommunicationErr { + source: + CommunicationError::RegionLengthTooBig { + length, max_length, .. + }, + .. + } => { + assert_eq!(length, 100); + assert_eq!(max_length, 90); + } + err => panic!("Incorrect error returned: {:?}", err), + } + } + + #[test] + fn do_addr_canonicalize_works() { let api = MockApi::default(); let (env, mut instance) = make_instance(api); let api = MockApi::default(); @@ -905,14 +1021,14 @@ mod tests { leave_default_data(&env); let api = MockApi::default(); - let res = do_canonicalize_address(&env, source_ptr, dest_ptr).unwrap(); + let res = do_addr_canonicalize(&env, source_ptr, dest_ptr).unwrap(); assert_eq!(res, 0); let data = force_read(&env, dest_ptr); assert_eq!(data.len(), api.canonical_length); } #[test] - fn do_canonicalize_address_reports_invalid_input_back_to_contract() { + fn do_addr_canonicalize_reports_invalid_input_back_to_contract() { let api = MockApi::default(); let (env, mut instance) = make_instance(api); @@ -923,24 +1039,24 @@ mod tests { leave_default_data(&env); - let res = do_canonicalize_address(&env, source_ptr1, dest_ptr).unwrap(); + let res = do_addr_canonicalize(&env, source_ptr1, dest_ptr).unwrap(); assert_ne!(res, 0); let err = String::from_utf8(force_read(&env, res)).unwrap(); assert_eq!(err, "Input is not valid UTF-8"); - let res = do_canonicalize_address(&env, source_ptr2, dest_ptr).unwrap(); + let res = do_addr_canonicalize(&env, source_ptr2, dest_ptr).unwrap(); assert_ne!(res, 0); let err = String::from_utf8(force_read(&env, res)).unwrap(); assert_eq!(err, "Input is empty"); - let res = do_canonicalize_address(&env, source_ptr3, dest_ptr).unwrap(); + let res = do_addr_canonicalize(&env, source_ptr3, dest_ptr).unwrap(); assert_ne!(res, 0); let err = String::from_utf8(force_read(&env, res)).unwrap(); assert_eq!(err, "Invalid input: human address too long"); } #[test] - fn do_canonicalize_address_fails_for_broken_backend() { + fn do_addr_canonicalize_fails_for_broken_backend() { let api = MockApi::new_failing("Temporarily unavailable"); let (env, mut instance) = make_instance(api); @@ -949,7 +1065,7 @@ mod tests { leave_default_data(&env); - let result = do_canonicalize_address(&env, source_ptr, dest_ptr); + let result = do_addr_canonicalize(&env, source_ptr, dest_ptr); match result.unwrap_err() { VmError::BackendErr { source: BackendError::Unknown { msg, .. }, @@ -962,7 +1078,7 @@ mod tests { } #[test] - fn do_canonicalize_address_fails_for_large_inputs() { + fn do_addr_canonicalize_fails_for_large_inputs() { let api = MockApi::default(); let (env, mut instance) = make_instance(api); @@ -971,7 +1087,7 @@ mod tests { leave_default_data(&env); - let result = do_canonicalize_address(&env, source_ptr, dest_ptr); + let result = do_addr_canonicalize(&env, source_ptr, dest_ptr); match result.unwrap_err() { VmError::CommunicationErr { source: @@ -988,7 +1104,7 @@ mod tests { } #[test] - fn do_canonicalize_address_fails_for_small_destination_region() { + fn do_addr_canonicalize_fails_for_small_destination_region() { let api = MockApi::default(); let (env, mut instance) = make_instance(api); @@ -997,7 +1113,7 @@ mod tests { leave_default_data(&env); - let result = do_canonicalize_address(&env, source_ptr, dest_ptr); + let result = do_addr_canonicalize(&env, source_ptr, dest_ptr); match result.unwrap_err() { VmError::CommunicationErr { source: CommunicationError::RegionTooSmall { size, required, .. }, @@ -1011,7 +1127,7 @@ mod tests { } #[test] - fn do_humanize_address_works() { + fn do_addr_humanize_works() { let api = MockApi::default(); let (env, mut instance) = make_instance(api); let api = MockApi::default(); @@ -1022,13 +1138,13 @@ mod tests { leave_default_data(&env); - let error_ptr = do_humanize_address(&env, source_ptr, dest_ptr).unwrap(); + let error_ptr = do_addr_humanize(&env, source_ptr, dest_ptr).unwrap(); assert_eq!(error_ptr, 0); assert_eq!(force_read(&env, dest_ptr), source_data); } #[test] - fn do_humanize_address_reports_invalid_input_back_to_contract() { + fn do_addr_humanize_reports_invalid_input_back_to_contract() { let api = MockApi::default(); let (env, mut instance) = make_instance(api); @@ -1037,14 +1153,14 @@ mod tests { leave_default_data(&env); - let res = do_humanize_address(&env, source_ptr, dest_ptr).unwrap(); + let res = do_addr_humanize(&env, source_ptr, dest_ptr).unwrap(); assert_ne!(res, 0); let err = String::from_utf8(force_read(&env, res)).unwrap(); assert_eq!(err, "Invalid input: canonical address length not correct"); } #[test] - fn do_humanize_address_fails_for_broken_backend() { + fn do_addr_humanize_fails_for_broken_backend() { let api = MockApi::new_failing("Temporarily unavailable"); let (env, mut instance) = make_instance(api); @@ -1053,7 +1169,7 @@ mod tests { leave_default_data(&env); - let result = do_humanize_address(&env, source_ptr, dest_ptr); + let result = do_addr_humanize(&env, source_ptr, dest_ptr); match result.unwrap_err() { VmError::BackendErr { source: BackendError::Unknown { msg, .. }, @@ -1064,7 +1180,7 @@ mod tests { } #[test] - fn do_humanize_address_fails_for_input_too_long() { + fn do_addr_humanize_fails_for_input_too_long() { let api = MockApi::default(); let (env, mut instance) = make_instance(api); @@ -1073,7 +1189,7 @@ mod tests { leave_default_data(&env); - let result = do_humanize_address(&env, source_ptr, dest_ptr); + let result = do_addr_humanize(&env, source_ptr, dest_ptr); match result.unwrap_err() { VmError::CommunicationErr { source: @@ -1090,7 +1206,7 @@ mod tests { } #[test] - fn do_humanize_address_fails_for_destination_region_too_small() { + fn do_addr_humanize_fails_for_destination_region_too_small() { let api = MockApi::default(); let (env, mut instance) = make_instance(api); let api = MockApi::default(); @@ -1101,7 +1217,7 @@ mod tests { leave_default_data(&env); - let result = do_humanize_address(&env, source_ptr, dest_ptr); + let result = do_addr_humanize(&env, source_ptr, dest_ptr); match result.unwrap_err() { VmError::CommunicationErr { source: CommunicationError::RegionTooSmall { size, required, .. }, @@ -1632,7 +1748,7 @@ mod tests { let (env, _instance) = make_instance(api); let request: QueryRequest = QueryRequest::Bank(BankQuery::AllBalances { - address: HumanAddr::from(INIT_ADDR), + address: INIT_ADDR.to_string(), }); let request_data = cosmwasm_std::to_vec(&request).unwrap(); let request_ptr = write_data(&env, &request_data); @@ -1680,7 +1796,7 @@ mod tests { let (env, _instance) = make_instance(api); let request: QueryRequest = QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: HumanAddr::from("non-existent"), + contract_addr: String::from("non-existent"), msg: Binary::from(b"{}" as &[u8]), }); let request_data = cosmwasm_std::to_vec(&request).unwrap(); @@ -1696,7 +1812,7 @@ mod tests { match query_result { SystemResult::Ok(_) => panic!("This must not succeed"), SystemResult::Err(SystemError::NoSuchContract { addr }) => { - assert_eq!(addr, HumanAddr::from("non-existent")) + assert_eq!(addr, "non-existent") } SystemResult::Err(err) => panic!("Unexpected error: {:?}", err), } diff --git a/packages/vm/src/instance.rs b/packages/vm/src/instance.rs index 1e9076842c..99eacc9246 100644 --- a/packages/vm/src/instance.rs +++ b/packages/vm/src/instance.rs @@ -9,9 +9,10 @@ use crate::environment::Environment; use crate::errors::{CommunicationError, VmError, VmResult}; use crate::features::required_features_from_wasmer_instance; use crate::imports::{ - native_canonicalize_address, native_db_read, native_db_remove, native_db_write, native_debug, - native_ed25519_batch_verify, native_ed25519_verify, native_humanize_address, - native_query_chain, native_secp256k1_recover_pubkey, native_secp256k1_verify, + native_addr_canonicalize, native_addr_humanize, native_addr_validate, native_db_read, + native_db_remove, native_db_write, native_debug, native_ed25519_batch_verify, + native_ed25519_verify, native_query_chain, native_secp256k1_recover_pubkey, + native_secp256k1_verify, }; #[cfg(feature = "iterator")] use crate::imports::{native_db_next, native_db_scan}; @@ -105,13 +106,21 @@ where Function::new_native_with_env(store, env.clone(), native_db_remove), ); + // Reads human address from source_ptr and checks if it is valid. + // Returns 0 on if the input is valid. Returns a non-zero memory location to a Region containing an UTF-8 encoded error string for invalid inputs. + // Ownership of the input pointer is not transferred to the host. + env_imports.insert( + "addr_validate", + Function::new_native_with_env(store, env.clone(), native_addr_validate), + ); + // Reads human address from source_ptr and writes canonicalized representation to destination_ptr. // A prepared and sufficiently large memory Region is expected at destination_ptr that points to pre-allocated memory. // Returns 0 on success. Returns a non-zero memory location to a Region containing an UTF-8 encoded error string for invalid inputs. // Ownership of both input and output pointer is not transferred to the host. env_imports.insert( - "canonicalize_address", - Function::new_native_with_env(store, env.clone(), native_canonicalize_address), + "addr_canonicalize", + Function::new_native_with_env(store, env.clone(), native_addr_canonicalize), ); // Reads canonical address from source_ptr and writes humanized representation to destination_ptr. @@ -119,8 +128,8 @@ where // Returns 0 on success. Returns a non-zero memory location to a Region containing an UTF-8 encoded error string for invalid inputs. // Ownership of both input and output pointer is not transferred to the host. env_imports.insert( - "humanize_address", - Function::new_native_with_env(store, env.clone(), native_humanize_address), + "addr_humanize", + Function::new_native_with_env(store, env.clone(), native_addr_humanize), ); // Verifies message hashes against a signature with a public key, using the secp256k1 ECDSA parametrization. @@ -334,7 +343,7 @@ mod tests { mock_instance_with_options, MockInstanceOptions, }; use cosmwasm_std::{ - coin, coins, from_binary, AllBalanceResponse, BalanceResponse, BankQuery, Empty, HumanAddr, + coin, coins, from_binary, AllBalanceResponse, BalanceResponse, BankQuery, Empty, QueryRequest, }; @@ -597,7 +606,7 @@ mod tests { let report2 = instance.create_gas_report(); assert_eq!(report2.used_externally, 146); - assert_eq!(report2.used_internally, 52531); + assert_eq!(report2.used_internally, 52702); assert_eq!(report2.limit, LIMIT); assert_eq!( report2.remaining, @@ -662,7 +671,7 @@ mod tests { #[test] fn with_querier_works_readonly() { - let rich_addr = HumanAddr::from("foobar"); + let rich_addr = String::from("foobar"); let rich_balance = vec![coin(10000, "gold"), coin(8000, "silver")]; let mut instance = mock_instance_with_balances(&CONTRACT, &[(&rich_addr, &rich_balance)]); @@ -717,7 +726,7 @@ mod tests { /// This is needed for writing intagration tests in which the balance of a contract changes over time #[test] fn with_querier_allows_updating_balances() { - let rich_addr = HumanAddr::from("foobar"); + let rich_addr = String::from("foobar"); let rich_balance1 = vec![coin(10000, "gold"), coin(500, "silver")]; let rich_balance2 = vec![coin(10000, "gold"), coin(8000, "silver")]; let mut instance = mock_instance_with_balances(&CONTRACT, &[(&rich_addr, &rich_balance1)]); @@ -796,7 +805,7 @@ mod singlepass_tests { .unwrap(); let init_used = orig_gas - instance.get_gas_left(); - assert_eq!(init_used, 52677); + assert_eq!(init_used, 52848); } #[test] @@ -819,7 +828,7 @@ mod singlepass_tests { .unwrap(); let execute_used = gas_before_execute - instance.get_gas_left(); - assert_eq!(execute_used, 168564); + assert_eq!(execute_used, 168158); } #[test] @@ -853,6 +862,6 @@ mod singlepass_tests { assert_eq!(answer.as_slice(), b"{\"verifier\":\"verifies\"}"); let query_used = gas_before_query - instance.get_gas_left(); - assert_eq!(query_used, 38502); + assert_eq!(query_used, 38349); } } diff --git a/packages/vm/src/testing/instance.rs b/packages/vm/src/testing/instance.rs index eb0b045df0..fb522a06ac 100644 --- a/packages/vm/src/testing/instance.rs +++ b/packages/vm/src/testing/instance.rs @@ -1,7 +1,7 @@ //! This file has some helpers for integration tests. //! They should be imported via full path to ensure there is no confusion //! use cosmwasm_vm::testing::X -use cosmwasm_std::{Coin, HumanAddr}; +use cosmwasm_std::Coin; use std::collections::HashSet; use crate::compatibility::check_wasm; @@ -51,7 +51,7 @@ pub fn mock_instance_with_failing_api( pub fn mock_instance_with_balances( wasm: &[u8], - balances: &[(&HumanAddr, &[Coin])], + balances: &[(&str, &[Coin])], ) -> Instance { mock_instance_with_options( wasm, @@ -78,7 +78,7 @@ pub fn mock_instance_with_gas_limit( #[derive(Debug)] pub struct MockInstanceOptions<'a> { // dependencies - pub balances: &'a [(&'a HumanAddr, &'a [Coin])], + pub balances: &'a [(&'a str, &'a [Coin])], /// This option is merged into balances and might override an existing value pub contract_balance: Option<&'a [Coin]>, /// When set, all calls to the API fail with BackendError::Unknown containing this message @@ -126,16 +126,16 @@ pub fn mock_instance_with_options( options: MockInstanceOptions, ) -> Instance { check_wasm(wasm, &options.supported_features).unwrap(); - let contract_address = HumanAddr::from(MOCK_CONTRACT_ADDR); + let contract_address = MOCK_CONTRACT_ADDR; // merge balances let mut balances = options.balances.to_vec(); if let Some(contract_balance) = options.contract_balance { // Remove old entry if exists - if let Some(pos) = balances.iter().position(|item| *item.0 == contract_address) { + if let Some(pos) = balances.iter().position(|item| item.0 == contract_address) { balances.remove(pos); } - balances.push((&contract_address, contract_balance)); + balances.push((contract_address, contract_balance)); } let api = if let Some(backend_error) = options.backend_error { diff --git a/packages/vm/src/testing/mock.rs b/packages/vm/src/testing/mock.rs index b51fc5c971..6bdbfbd7df 100644 --- a/packages/vm/src/testing/mock.rs +++ b/packages/vm/src/testing/mock.rs @@ -1,5 +1,5 @@ use cosmwasm_std::testing::{digit_sum, riffle_shuffle}; -use cosmwasm_std::{BlockInfo, CanonicalAddr, Coin, ContractInfo, Env, HumanAddr, MessageInfo}; +use cosmwasm_std::{Addr, BlockInfo, Coin, ContractInfo, Env, MessageInfo}; use super::querier::MockQuerier; use super::storage::MockStorage; @@ -12,18 +12,17 @@ const GAS_COST_CANONICALIZE: u64 = 55; /// All external requirements that can be injected for unit tests. /// It sets the given balance for the contract itself, nothing else pub fn mock_backend(contract_balance: &[Coin]) -> Backend { - let contract_addr = HumanAddr::from(MOCK_CONTRACT_ADDR); Backend { api: MockApi::default(), storage: MockStorage::default(), - querier: MockQuerier::new(&[(&contract_addr, contract_balance)]), + querier: MockQuerier::new(&[(MOCK_CONTRACT_ADDR, contract_balance)]), } } /// Initializes the querier along with the mock_dependencies. /// Sets all balances provided (yoy must explicitly set contract balance if desired) pub fn mock_backend_with_balances( - balances: &[(&HumanAddr, &[Coin])], + balances: &[(&str, &[Coin])], ) -> Backend { Backend { api: MockApi::default(), @@ -63,7 +62,7 @@ impl Default for MockApi { } impl BackendApi for MockApi { - fn canonical_address(&self, human: &HumanAddr) -> BackendResult { + fn canonical_address(&self, human: &str) -> BackendResult> { let gas_info = GasInfo::with_cost(GAS_COST_CANONICALIZE); if let Some(backend_error) = self.backend_error { @@ -88,7 +87,7 @@ impl BackendApi for MockApi { ); } - let mut out = Vec::from(human.as_str()); + let mut out = Vec::from(human); // pad to canonical length with NULL bytes out.resize(self.canonical_length, 0x00); // content-dependent rotate followed by shuffle to destroy @@ -98,10 +97,10 @@ impl BackendApi for MockApi { for _ in 0..18 { out = riffle_shuffle(&out); } - (Ok(out.into()), gas_info) + (Ok(out), gas_info) } - fn human_address(&self, canonical: &CanonicalAddr) -> BackendResult { + fn human_address(&self, canonical: &[u8]) -> BackendResult { let gas_info = GasInfo::with_cost(GAS_COST_HUMANIZE); if let Some(backend_error) = self.backend_error { @@ -117,7 +116,7 @@ impl BackendApi for MockApi { ); } - let mut tmp: Vec = canonical.clone().into(); + let mut tmp: Vec = canonical.into(); // Shuffle two more times which restored the original value (24 elements are back to original after 20 rounds) for _ in 0..2 { tmp = riffle_shuffle(&tmp); @@ -129,7 +128,7 @@ impl BackendApi for MockApi { let trimmed = tmp.into_iter().filter(|&x| x != 0x00).collect(); let result = match String::from_utf8(trimmed) { - Ok(human) => Ok(HumanAddr(human)), + Ok(human) => Ok(human), Err(err) => Err(err.into()), }; (result, gas_info) @@ -150,16 +149,16 @@ pub fn mock_env() -> Env { chain_id: "cosmos-testnet-14002".to_string(), }, contract: ContractInfo { - address: HumanAddr::from(MOCK_CONTRACT_ADDR), + address: Addr::unchecked(MOCK_CONTRACT_ADDR), }, } } /// Just set sender and funds for the message. /// This is intended for use in test code only. -pub fn mock_info>(sender: U, funds: &[Coin]) -> MessageInfo { +pub fn mock_info(sender: &str, funds: &[Coin]) -> MessageInfo { MessageInfo { - sender: sender.into(), + sender: Addr::unchecked(sender), funds: funds.to_vec(), } } @@ -168,28 +167,29 @@ pub fn mock_info>(sender: U, funds: &[Coin]) -> MessageInfo { mod tests { use super::*; use crate::BackendError; - use cosmwasm_std::{coins, Binary}; + use cosmwasm_std::coins; #[test] - fn mock_info_arguments() { - let name = HumanAddr("my name".to_string()); - - // make sure we can generate with &str, &HumanAddr, and HumanAddr - let a = mock_info("my name", &coins(100, "atom")); - let b = mock_info(&name, &coins(100, "atom")); - let c = mock_info(name, &coins(100, "atom")); - - // and the results are the same - assert_eq!(a, b); - assert_eq!(a, c); + fn mock_info_works() { + let info = mock_info("my name", &coins(100, "atom")); + assert_eq!( + info, + MessageInfo { + sender: Addr::unchecked("my name"), + funds: vec![Coin { + amount: 100u128.into(), + denom: "atom".into(), + }] + } + ); } #[test] fn canonicalize_and_humanize_restores_original() { let api = MockApi::default(); - let original = HumanAddr::from("shorty"); - let canonical = api.canonical_address(&original).0.unwrap(); + let original = "shorty"; + let canonical = api.canonical_address(original).0.unwrap(); let (recovered, _gas_cost) = api.human_address(&canonical); assert_eq!(recovered.unwrap(), original); } @@ -197,7 +197,7 @@ mod tests { #[test] fn human_address_input_length() { let api = MockApi::default(); - let input = CanonicalAddr(Binary(vec![61; 11])); + let input = vec![61; 11]; let (result, _gas_info) = api.human_address(&input); match result.unwrap_err() { BackendError::UserErr { .. } => {} @@ -208,8 +208,8 @@ mod tests { #[test] fn canonical_address_min_input_length() { let api = MockApi::default(); - let human = HumanAddr::from("1"); - match api.canonical_address(&human).0.unwrap_err() { + let human = "1"; + match api.canonical_address(human).0.unwrap_err() { BackendError::UserErr { .. } => {} err => panic!("Unexpected error: {:?}", err), } @@ -218,8 +218,8 @@ mod tests { #[test] fn canonical_address_max_input_length() { let api = MockApi::default(); - let human = HumanAddr::from("longer-than-the-address-length-supported-by-this-api"); - match api.canonical_address(&human).0.unwrap_err() { + let human = "longer-than-the-address-length-supported-by-this-api"; + match api.canonical_address(human).0.unwrap_err() { BackendError::UserErr { .. } => {} err => panic!("Unexpected error: {:?}", err), } diff --git a/packages/vm/src/testing/querier.rs b/packages/vm/src/testing/querier.rs index 0e6b3853ef..a6d6c36bb2 100644 --- a/packages/vm/src/testing/querier.rs +++ b/packages/vm/src/testing/querier.rs @@ -2,7 +2,7 @@ use serde::de::DeserializeOwned; use cosmwasm_std::testing::{MockQuerier as StdMockQuerier, MockQuerierCustomHandlerResult}; use cosmwasm_std::{ - to_binary, to_vec, Binary, Coin, ContractResult, CustomQuery, Empty, HumanAddr, Querier as _, + to_binary, to_vec, Binary, Coin, ContractResult, CustomQuery, Empty, Querier as _, QueryRequest, SystemError, SystemResult, }; @@ -21,14 +21,14 @@ pub struct MockQuerier { } impl MockQuerier { - pub fn new(balances: &[(&HumanAddr, &[Coin])]) -> Self { + pub fn new(balances: &[(&str, &[Coin])]) -> Self { MockQuerier { querier: StdMockQuerier::new(balances), } } // set a new balance for the given address and return the old balance - pub fn update_balance>( + pub fn update_balance>( &mut self, addr: U, balance: Vec, @@ -113,7 +113,7 @@ mod tests { #[test] fn query_raw_fails_when_out_of_gas() { - let addr = HumanAddr::from("foobar"); + let addr = String::from("foobar"); let balance = vec![coin(123, "ELF"), coin(777, "FLY")]; let querier: MockQuerier = MockQuerier::new(&[(&addr, &balance)]); @@ -127,7 +127,7 @@ mod tests { #[test] fn bank_querier_all_balances() { - let addr = HumanAddr::from("foobar"); + let addr = String::from("foobar"); let balance = vec![coin(123, "ELF"), coin(777, "FLY")]; let querier = MockQuerier::new(&[(&addr, &balance)]); @@ -147,7 +147,7 @@ mod tests { #[test] fn bank_querier_one_balance() { - let addr = HumanAddr::from("foobar"); + let addr = String::from("foobar"); let balance = vec![coin(123, "ELF"), coin(777, "FLY")]; let querier = MockQuerier::new(&[(&addr, &balance)]); @@ -188,7 +188,7 @@ mod tests { #[test] fn bank_querier_missing_account() { - let addr = HumanAddr::from("foobar"); + let addr = String::from("foobar"); let balance = vec![coin(123, "ELF"), coin(777, "FLY")]; let querier = MockQuerier::new(&[(&addr, &balance)]); @@ -196,7 +196,7 @@ mod tests { let all = querier .query::( &BankQuery::AllBalances { - address: HumanAddr::from("elsewhere"), + address: String::from("elsewhere"), } .into(), DEFAULT_QUERY_GAS_LIMIT, @@ -212,7 +212,7 @@ mod tests { let miss = querier .query::( &BankQuery::Balance { - address: HumanAddr::from("elsewhere"), + address: String::from("elsewhere"), denom: "ELF".to_string(), } .into(), diff --git a/packages/vm/testdata/hackatom_0.14.wasm b/packages/vm/testdata/hackatom_0.14.wasm index 875d77f12f..6fdf0187d1 100644 Binary files a/packages/vm/testdata/hackatom_0.14.wasm and b/packages/vm/testdata/hackatom_0.14.wasm differ diff --git a/packages/vm/testdata/ibc_reflect_0.14.wasm b/packages/vm/testdata/ibc_reflect_0.14.wasm index 5f5b185370..39e9ba08e4 100644 Binary files a/packages/vm/testdata/ibc_reflect_0.14.wasm and b/packages/vm/testdata/ibc_reflect_0.14.wasm differ