Skip to content

Commit b94f6f3

Browse files
authored
Merge pull request #1985 from CosmWasm/BackendApi-improvements
BackendApi improvements
2 parents 1ce73c6 + df69d76 commit b94f6f3

File tree

6 files changed

+67
-51
lines changed

6 files changed

+67
-51
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ and this project adheres to
7878
`set_withdraw_address`, `set_withdraw_addresses`, `clear_withdraw_addresses`,
7979
`update_ibc` and `update_staking` from `MockQuerier` and expose the underlying
8080
queriers directly. ([#1977])
81+
- cosmwasm-vm: Rename `BackendApi::canonical_address`/`::human_address` to
82+
`::addr_canonicalize`/`::addr_humanize` for consistency.
83+
- cosmwasm-vm: Add `BackendApi::addr_validate` to avoid having to do two calls
84+
from Rust into Go.
8185

8286
[#1874]: https://github.com/CosmWasm/cosmwasm/pull/1874
8387
[#1876]: https://github.com/CosmWasm/cosmwasm/pull/1876

packages/vm/src/backend.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,10 @@ pub trait Storage {
165165
/// Currently it just supports address conversion, we could add eg. crypto functions here.
166166
/// These should all be pure (stateless) functions. If you need state, you probably want
167167
/// to use the Querier.
168-
///
169-
/// We can use feature flags to opt-in to non-essential methods
170-
/// for backwards compatibility in systems that don't have them all.
171-
pub trait BackendApi: Copy + Clone + Send {
172-
fn canonical_address(&self, human: &str) -> BackendResult<Vec<u8>>;
173-
fn human_address(&self, canonical: &[u8]) -> BackendResult<String>;
168+
pub trait BackendApi: Clone + Send {
169+
fn addr_validate(&self, input: &str) -> BackendResult<()>;
170+
fn addr_canonicalize(&self, human: &str) -> BackendResult<Vec<u8>>;
171+
fn addr_humanize(&self, canonical: &[u8]) -> BackendResult<String>;
174172
}
175173

176174
pub trait Querier {

packages/vm/src/environment.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ impl<A: BackendApi, S: Storage, Q: Querier> Clone for Environment<A, S, Q> {
125125
fn clone(&self) -> Self {
126126
Environment {
127127
memory: None,
128-
api: self.api,
128+
api: self.api.clone(),
129129
gas_config: self.gas_config.clone(),
130130
data: self.data.clone(),
131131
}

packages/vm/src/imports.rs

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -162,31 +162,15 @@ pub fn do_addr_validate<A: BackendApi + 'static, S: Storage + 'static, Q: Querie
162162
Err(_) => return write_to_contract(data, &mut store, b"Input is not valid UTF-8"),
163163
};
164164

165-
let (result, gas_info) = data.api.canonical_address(&source_string);
165+
let (result, gas_info) = data.api.addr_validate(&source_string);
166166
process_gas_info(data, &mut store, gas_info)?;
167-
let canonical = match result {
168-
Ok(data) => data,
169-
Err(BackendError::UserErr { msg, .. }) => {
170-
return write_to_contract(data, &mut store, msg.as_bytes())
171-
}
172-
Err(err) => return Err(VmError::from(err)),
173-
};
174-
175-
let (result, gas_info) = data.api.human_address(&canonical);
176-
process_gas_info(data, &mut store, gas_info)?;
177-
let normalized = match result {
178-
Ok(addr) => addr,
167+
match result {
168+
Ok(()) => Ok(0),
179169
Err(BackendError::UserErr { msg, .. }) => {
180-
return write_to_contract(data, &mut store, msg.as_bytes())
170+
write_to_contract(data, &mut store, msg.as_bytes())
181171
}
182-
Err(err) => return Err(VmError::from(err)),
183-
};
184-
185-
if normalized != source_string {
186-
return write_to_contract(data, &mut store, b"Address is not normalized");
172+
Err(err) => Err(VmError::from(err)),
187173
}
188-
189-
Ok(0)
190174
}
191175

192176
pub fn do_addr_canonicalize<A: BackendApi + 'static, S: Storage + 'static, Q: Querier + 'static>(
@@ -206,7 +190,7 @@ pub fn do_addr_canonicalize<A: BackendApi + 'static, S: Storage + 'static, Q: Qu
206190
Err(_) => return write_to_contract(data, &mut store, b"Input is not valid UTF-8"),
207191
};
208192

209-
let (result, gas_info) = data.api.canonical_address(&source_string);
193+
let (result, gas_info) = data.api.addr_canonicalize(&source_string);
210194
process_gas_info(data, &mut store, gas_info)?;
211195
match result {
212196
Ok(canonical) => {
@@ -233,7 +217,7 @@ pub fn do_addr_humanize<A: BackendApi + 'static, S: Storage + 'static, Q: Querie
233217
MAX_LENGTH_CANONICAL_ADDRESS,
234218
)?;
235219

236-
let (result, gas_info) = data.api.human_address(&canonical);
220+
let (result, gas_info) = data.api.addr_humanize(&canonical);
237221
process_gas_info(data, &mut store, gas_info)?;
238222
match result {
239223
Ok(human) => {

packages/vm/src/instance.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ where
294294

295295
let env = fe.as_ref(&store);
296296
if let (Some(storage), Some(querier)) = env.move_out() {
297-
let api = env.api;
297+
let api = env.api.clone();
298298
Some(Backend {
299299
api,
300300
storage,

packages/vm/src/testing/mock.rs

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,37 @@ impl Default for MockApi {
125125
}
126126

127127
impl BackendApi for MockApi {
128-
fn canonical_address(&self, input: &str) -> BackendResult<Vec<u8>> {
128+
fn addr_validate(&self, input: &str) -> BackendResult<()> {
129+
let mut gas_total = GasInfo {
130+
cost: 0,
131+
externally_used: 0,
132+
};
133+
134+
let (result, gas_info) = self.addr_canonicalize(input);
135+
gas_total += gas_info;
136+
let canonical = match result {
137+
Ok(canonical) => canonical,
138+
Err(err) => return (Err(err), gas_total),
139+
};
140+
141+
let (result, gas_info) = self.addr_humanize(&canonical);
142+
gas_total += gas_info;
143+
let normalized = match result {
144+
Ok(norm) => norm,
145+
Err(err) => return (Err(err), gas_total),
146+
};
147+
if input != normalized.as_str() {
148+
return (
149+
Err(BackendError::user_err(
150+
"Invalid input: address not normalized",
151+
)),
152+
gas_total,
153+
);
154+
}
155+
(Ok(()), gas_total)
156+
}
157+
158+
fn addr_canonicalize(&self, input: &str) -> BackendResult<Vec<u8>> {
129159
let gas_info = GasInfo::with_cost(GAS_COST_CANONICALIZE);
130160

131161
// handle error case
@@ -156,7 +186,7 @@ impl BackendApi for MockApi {
156186
}
157187
}
158188

159-
fn human_address(&self, canonical: &[u8]) -> BackendResult<String> {
189+
fn addr_humanize(&self, canonical: &[u8]) -> BackendResult<String> {
160190
let gas_info = GasInfo::with_cost(GAS_COST_HUMANIZE);
161191

162192
// handle error case
@@ -232,20 +262,20 @@ mod tests {
232262
}
233263

234264
#[test]
235-
fn canonical_address_works() {
265+
fn addr_canonicalize_works() {
236266
let api = MockApi::default().with_prefix("osmo");
237267

238-
api.canonical_address("osmo186kh7c0k0gh4ww0wh4jqc4yhzu7n7dhswe845d")
268+
api.addr_canonicalize("osmo186kh7c0k0gh4ww0wh4jqc4yhzu7n7dhswe845d")
239269
.0
240270
.unwrap();
241271

242272
// is case insensitive
243273
let data1 = api
244-
.canonical_address("osmo186kh7c0k0gh4ww0wh4jqc4yhzu7n7dhswe845d")
274+
.addr_canonicalize("osmo186kh7c0k0gh4ww0wh4jqc4yhzu7n7dhswe845d")
245275
.0
246276
.unwrap();
247277
let data2 = api
248-
.canonical_address("OSMO186KH7C0K0GH4WW0WH4JQC4YHZU7N7DHSWE845D")
278+
.addr_canonicalize("OSMO186KH7C0K0GH4WW0WH4JQC4YHZU7N7DHSWE845D")
249279
.0
250280
.unwrap();
251281
assert_eq!(data1, data2);
@@ -257,56 +287,56 @@ mod tests {
257287

258288
// simple
259289
let original = api.addr_make("shorty");
260-
let canonical = api.canonical_address(&original).0.unwrap();
261-
let (recovered, _gas_cost) = api.human_address(&canonical);
290+
let canonical = api.addr_canonicalize(&original).0.unwrap();
291+
let (recovered, _gas_cost) = api.addr_humanize(&canonical);
262292
assert_eq!(recovered.unwrap(), original);
263293

264294
// normalizes input
265295
let original = "JUNO1MEPRU9FUQ4E65856ARD6068MFSFRWPGEMD0C3R";
266-
let canonical = api.canonical_address(original).0.unwrap();
267-
let recovered = api.human_address(&canonical).0.unwrap();
296+
let canonical = api.addr_canonicalize(original).0.unwrap();
297+
let recovered = api.addr_humanize(&canonical).0.unwrap();
268298
assert_eq!(recovered, original.to_lowercase());
269299

270300
// Long input (Juno contract address)
271301
let original =
272302
String::from("juno1v82su97skv6ucfqvuvswe0t5fph7pfsrtraxf0x33d8ylj5qnrysdvkc95");
273-
let canonical = api.canonical_address(&original).0.unwrap();
274-
let recovered = api.human_address(&canonical).0.unwrap();
303+
let canonical = api.addr_canonicalize(&original).0.unwrap();
304+
let recovered = api.addr_humanize(&canonical).0.unwrap();
275305
assert_eq!(recovered, original);
276306
}
277307

278308
#[test]
279-
fn human_address_input_length() {
309+
fn addr_humanize_input_length() {
280310
let api = MockApi::default();
281311
let input = vec![61; 256]; // too long
282-
let (result, _gas_info) = api.human_address(&input);
312+
let (result, _gas_info) = api.addr_humanize(&input);
283313
match result.unwrap_err() {
284314
BackendError::UserErr { .. } => {}
285315
err => panic!("Unexpected error: {err:?}"),
286316
}
287317
}
288318

289319
#[test]
290-
fn canonical_address_min_input_length() {
320+
fn addr_canonicalize_min_input_length() {
291321
let api = MockApi::default();
292322

293323
// empty address should fail
294324
let empty = "cosmwasm1pj90vm";
295325
assert!(matches!(api
296-
.canonical_address(empty)
326+
.addr_canonicalize(empty)
297327
.0
298328
.unwrap_err(),
299329
BackendError::UserErr { msg } if msg.contains("address length")));
300330
}
301331

302332
#[test]
303-
fn canonical_address_max_input_length() {
333+
fn addr_canonicalize_max_input_length() {
304334
let api = MockApi::default();
305335

306336
let too_long = "cosmwasm1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqehqqkz";
307337

308338
assert!(matches!(api
309-
.canonical_address(too_long)
339+
.addr_canonicalize(too_long)
310340
.0
311341
.unwrap_err(),
312342
BackendError::UserErr { msg } if msg.contains("address length")));
@@ -316,10 +346,10 @@ mod tests {
316346
fn colon_in_prefix_is_valid() {
317347
let mock_api = MockApi::default().with_prefix("did:com:");
318348
let bytes = mock_api
319-
.canonical_address("did:com:1jkf0kmeyefvyzpwf56m7sne2000ay53r6upttu")
349+
.addr_canonicalize("did:com:1jkf0kmeyefvyzpwf56m7sne2000ay53r6upttu")
320350
.0
321351
.unwrap();
322-
let humanized = mock_api.human_address(&bytes).0.unwrap();
352+
let humanized = mock_api.addr_humanize(&bytes).0.unwrap();
323353

324354
assert_eq!(
325355
humanized.as_str(),

0 commit comments

Comments
 (0)