clone_cw_multi_test/wasm_emulation/api/
mod.rs1use crate::wasm_emulation::query::gas::{GAS_COST_CANONICALIZE, GAS_COST_HUMANIZE};
2use bech32::{FromBase32, ToBase32, Variant};
3use cosmwasm_std::Addr;
4use cosmwasm_vm::{BackendApi, BackendError, GasInfo};
5use std::ops::AddAssign;
6
7const SHORT_CANON_LEN: usize = 20;
8const LONG_CANON_LEN: usize = 32;
9
10pub fn bytes_from_bech32(address: &str, prefix: &str) -> Result<Vec<u8>, BackendError> {
11 if address.is_empty() {
12 return Err(BackendError::Unknown {
13 msg: "empty address string is not allowed".to_string(),
14 });
15 }
16
17 let (hrp, data, _variant) = bech32::decode(address).map_err(|e| BackendError::Unknown {
18 msg: format!("Invalid Bech32 address : Err {}", e),
19 })?;
20 if hrp != prefix {
21 return Err(BackendError::Unknown {
22 msg: format!("invalid Bech32 prefix; expected {}, got {}", prefix, hrp),
23 });
24 }
25
26 Ok(Vec::<u8>::from_base32(&data).unwrap())
27}
28
29pub const MAX_PREFIX_CHARS: usize = 10;
30#[derive(Clone, Copy)]
33pub struct RealApi {
34 pub prefix: [char; MAX_PREFIX_CHARS],
35}
36
37impl RealApi {
38 pub fn new(prefix: &str) -> RealApi {
39 if prefix.len() > MAX_PREFIX_CHARS {
40 panic!("More chars in the prefix than {}", MAX_PREFIX_CHARS);
41 }
42
43 let mut api_prefix = ['\0'; 10];
44 for (i, c) in prefix.chars().enumerate() {
45 api_prefix[i] = c;
46 }
47 Self { prefix: api_prefix }
48 }
49
50 pub fn get_prefix(&self) -> String {
51 let mut prefix = Vec::new();
52
53 for &c in self.prefix.iter() {
54 if c != '\0' {
55 prefix.push(c);
56 }
57 }
58 prefix.iter().collect()
59 }
60
61 pub fn next_address(&self, count: usize) -> Addr {
62 let mut canon = format!("ADDRESS_{}", count).as_bytes().to_vec();
63 canon.resize(SHORT_CANON_LEN, 0);
64 Addr::unchecked(self.addr_humanize(&canon).0.unwrap())
65 }
66
67 pub fn next_contract_address(&self, count: usize) -> Addr {
68 let mut canon = format!("CONTRACT_{}", count).as_bytes().to_vec();
69 canon.resize(LONG_CANON_LEN, 0);
70 Addr::unchecked(self.addr_humanize(&canon).0.unwrap())
71 }
72}
73
74impl BackendApi for RealApi {
75 fn addr_validate(&self, input: &str) -> cosmwasm_vm::BackendResult<()> {
76 let (canon, mut gas_cost) = self.addr_canonicalize(input);
77
78 if let Err(e) = canon {
79 return (Err(e), gas_cost);
80 }
81 let canon = canon.unwrap();
82
83 let (new_human, human_gas_cost) = self.addr_humanize(&canon);
84
85 if let Err(e) = new_human {
86 gas_cost.add_assign(human_gas_cost);
87 return (Err(e), gas_cost);
88 }
89 let new_human = new_human.unwrap();
90
91 if input == new_human {
92 (Ok(()), gas_cost)
93 } else {
94 (
95 Err(BackendError::user_err(format!(
96 "Address invalid : {}",
97 input
98 ))),
99 gas_cost,
100 )
101 }
102 }
103
104 fn addr_canonicalize(&self, human: &str) -> cosmwasm_vm::BackendResult<Vec<u8>> {
105 let gas_cost = GasInfo::with_externally_used(GAS_COST_CANONICALIZE);
106 if human.trim().is_empty() {
107 return (
108 Err(BackendError::Unknown {
109 msg: "empty address string is not allowed".to_string(),
110 }),
111 gas_cost,
112 );
113 }
114
115 (bytes_from_bech32(human, &self.get_prefix()), gas_cost)
116 }
117
118 fn addr_humanize(&self, canonical: &[u8]) -> cosmwasm_vm::BackendResult<String> {
119 let gas_cost = GasInfo::with_externally_used(GAS_COST_HUMANIZE);
120
121 if canonical.len() != SHORT_CANON_LEN && canonical.len() != LONG_CANON_LEN {
122 return (
123 Err(BackendError::Unknown {
124 msg: "Canon address doesn't have the right length".to_string(),
125 }),
126 gas_cost,
127 );
128 }
129
130 if canonical.is_empty() {
131 return (Ok("".to_string()), gas_cost);
132 }
133
134 let human = bech32::encode(&self.get_prefix(), canonical.to_base32(), Variant::Bech32)
135 .map_err(|e| BackendError::Unknown { msg: e.to_string() });
136
137 (human, gas_cost)
138 }
139}
140
141#[cfg(test)]
142mod test {
143 use super::RealApi;
144
145 #[test]
146 fn prefix() {
147 let prefix = "migaloo";
148
149 let api = RealApi::new(prefix);
150
151 let final_prefix = api.get_prefix();
152 assert_eq!(prefix, final_prefix);
153 }
154
155 #[test]
156 #[should_panic]
157 fn too_long_prefix() {
158 let prefix = "migaloowithotherchars";
159 RealApi::new(prefix);
160 }
161}