1
1
use alloc:: collections:: BTreeMap ;
2
- use bech32:: { encode, ToBase32 , Variant } ;
2
+ use bech32:: { decode , encode, FromBase32 , ToBase32 , Variant } ;
3
3
use core:: iter:: IntoIterator ;
4
4
use core:: marker:: PhantomData ;
5
5
#[ cfg( feature = "cosmwasm_1_3" ) ]
@@ -53,8 +53,6 @@ use crate::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdResponse};
53
53
#[ cfg( feature = "cosmwasm_1_4" ) ]
54
54
use crate :: { Decimal256 , DelegationRewardsResponse , DelegatorValidatorsResponse } ;
55
55
56
- use super :: riffle_shuffle;
57
-
58
56
pub const MOCK_CONTRACT_ADDR : & str = "cosmos2contract" ;
59
57
60
58
/// Creates all external requirements that can be injected for unit tests.
@@ -96,19 +94,6 @@ pub fn mock_dependencies_with_balances(
96
94
// We can later make simplifications here if needed
97
95
pub type MockStorage = MemoryStorage ;
98
96
99
- /// Length of canonical addresses created with this API. Contracts should not make any assumptions
100
- /// what this value is.
101
- ///
102
- /// The mock API can only canonicalize and humanize addresses up to this length. So it must be
103
- /// long enough to store common bech32 addresses.
104
- ///
105
- /// The value here must be restorable with `SHUFFLES_ENCODE` + `SHUFFLES_DECODE` in-shuffles.
106
- /// See <https://oeis.org/A002326/list> for a table of those values.
107
- const CANONICAL_LENGTH : usize = 90 ; // n = 45
108
-
109
- const SHUFFLES_ENCODE : usize = 10 ;
110
- const SHUFFLES_DECODE : usize = 2 ;
111
-
112
97
/// Default prefix used when creating Bech32 encoded address.
113
98
const BECH32_PREFIX : & str = "cosmwasm" ;
114
99
@@ -117,17 +102,13 @@ const BECH32_PREFIX: &str = "cosmwasm";
117
102
// not really smart, but allows us to see a difference (and consistent length for canonical addresses)
118
103
#[ derive( Copy , Clone ) ]
119
104
pub struct MockApi {
120
- /// Length of canonical addresses created with this API. Contracts should not make any assumptions
121
- /// what this value is.
122
- canonical_length : usize ,
123
105
/// Prefix used for creating addresses in Bech32 encoding.
124
106
bech32_prefix : & ' static str ,
125
107
}
126
108
127
109
impl Default for MockApi {
128
110
fn default ( ) -> Self {
129
111
MockApi {
130
- canonical_length : CANONICAL_LENGTH ,
131
112
bech32_prefix : BECH32_PREFIX ,
132
113
}
133
114
}
@@ -142,62 +123,29 @@ impl Api for MockApi {
142
123
"Invalid input: address not normalized" ,
143
124
) ) ;
144
125
}
145
-
146
126
Ok ( Addr :: unchecked ( input) )
147
127
}
148
128
149
129
fn addr_canonicalize ( & self , input : & str ) -> StdResult < CanonicalAddr > {
150
- // Dummy input validation. This is more sophisticated for formats like bech32, where format and checksum are validated.
151
- let min_length = 3 ;
152
- let max_length = self . canonical_length ;
153
- if input. len ( ) < min_length {
154
- return Err ( StdError :: generic_err (
155
- format ! ( "Invalid input: human address too short for this mock implementation (must be >= {min_length})." ) ,
156
- ) ) ;
157
- }
158
- if input. len ( ) > max_length {
159
- return Err ( StdError :: generic_err (
160
- format ! ( "Invalid input: human address too long for this mock implementation (must be <= {max_length})." ) ,
161
- ) ) ;
162
- }
163
-
164
- // mimics formats like hex or bech32 where different casings are valid for one address
165
- let normalized = input. to_lowercase ( ) ;
166
-
167
- let mut out = Vec :: from ( normalized) ;
168
-
169
- // pad to canonical length with NULL bytes
170
- out. resize ( self . canonical_length , 0x00 ) ;
171
- // content-dependent rotate followed by shuffle to destroy
172
- // the most obvious structure (https://github.com/CosmWasm/cosmwasm/issues/552)
173
- let rotate_by = digit_sum ( & out) % self . canonical_length ;
174
- out. rotate_left ( rotate_by) ;
175
- for _ in 0 ..SHUFFLES_ENCODE {
176
- out = riffle_shuffle ( & out) ;
130
+ if let Ok ( ( prefix, decoded, Variant :: Bech32 ) ) = decode ( input) {
131
+ if prefix == self . bech32_prefix {
132
+ if let Ok ( bytes) = Vec :: < u8 > :: from_base32 ( & decoded) {
133
+ return Ok ( bytes. into ( ) ) ;
134
+ }
135
+ }
177
136
}
178
- Ok ( out . into ( ) )
137
+ Err ( StdError :: generic_err ( "Invalid input" ) )
179
138
}
180
139
181
140
fn addr_humanize ( & self , canonical : & CanonicalAddr ) -> StdResult < Addr > {
182
- if canonical. len ( ) != self . canonical_length {
183
- return Err ( StdError :: generic_err (
184
- "Invalid input: canonical address length not correct" ,
185
- ) ) ;
186
- }
187
-
188
- let mut tmp: Vec < u8 > = canonical. clone ( ) . into ( ) ;
189
- // Shuffle two more times which restored the original value (24 elements are back to original after 20 rounds)
190
- for _ in 0 ..SHUFFLES_DECODE {
191
- tmp = riffle_shuffle ( & tmp) ;
192
- }
193
- // Rotate back
194
- let rotate_by = digit_sum ( & tmp) % self . canonical_length ;
195
- tmp. rotate_right ( rotate_by) ;
196
- // Remove NULL bytes (i.e. the padding)
197
- let trimmed = tmp. into_iter ( ) . filter ( |& x| x != 0x00 ) . collect ( ) ;
198
- // decode UTF-8 bytes into string
199
- let human = String :: from_utf8 ( trimmed) ?;
200
- Ok ( Addr :: unchecked ( human) )
141
+ let Ok ( encoded) = encode (
142
+ self . bech32_prefix ,
143
+ canonical. as_slice ( ) . to_base32 ( ) ,
144
+ Variant :: Bech32 ,
145
+ ) else {
146
+ return Err ( StdError :: generic_err ( "Invalid canonical address" ) ) ;
147
+ } ;
148
+ Ok ( Addr :: unchecked ( encoded) )
201
149
}
202
150
203
151
fn secp256k1_verify (
@@ -1197,11 +1145,13 @@ mod tests {
1197
1145
1198
1146
#[ test]
1199
1147
fn addr_validate_works ( ) {
1148
+ // default prefix is 'cosmwasm'
1200
1149
let api = MockApi :: default ( ) ;
1201
1150
1202
1151
// valid
1203
- let addr = api. addr_validate ( "foobar123" ) . unwrap ( ) ;
1204
- assert_eq ! ( addr. as_str( ) , "foobar123" ) ;
1152
+ let humanized = "cosmwasm1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqs8s7vcp" ;
1153
+ let addr = api. addr_validate ( humanized) . unwrap ( ) ;
1154
+ assert_eq ! ( addr, humanized) ;
1205
1155
1206
1156
// invalid: too short
1207
1157
api. addr_validate ( "" ) . unwrap_err ( ) ;
@@ -1214,31 +1164,44 @@ mod tests {
1214
1164
fn addr_canonicalize_works ( ) {
1215
1165
let api = MockApi :: default ( ) ;
1216
1166
1217
- api. addr_canonicalize ( "foobar123" ) . unwrap ( ) ;
1167
+ api. addr_canonicalize (
1168
+ "cosmwasm1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqs8s7vcp" ,
1169
+ )
1170
+ . unwrap ( ) ;
1218
1171
1219
1172
// is case insensitive
1220
- let data1 = api. addr_canonicalize ( "foo123" ) . unwrap ( ) ;
1221
- let data2 = api. addr_canonicalize ( "FOO123" ) . unwrap ( ) ;
1173
+ let data1 = api
1174
+ . addr_canonicalize (
1175
+ "cosmwasm1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqs8s7vcp" ,
1176
+ )
1177
+ . unwrap ( ) ;
1178
+ let data2 = api
1179
+ . addr_canonicalize (
1180
+ "COSMWASM1H34LMPYWH4UPNJDG90CJF4J70AEE6Z8QQFSPUGAMJP42E4Q28KQS8S7VCP" ,
1181
+ )
1182
+ . unwrap ( ) ;
1222
1183
assert_eq ! ( data1, data2) ;
1223
1184
}
1224
1185
1225
1186
#[ test]
1226
1187
fn canonicalize_and_humanize_restores_original ( ) {
1188
+ // create api with 'cosmwasm' prefix
1227
1189
let api = MockApi :: default ( ) ;
1228
1190
1229
- // simple
1230
- let original = String :: from ( "shorty" ) ;
1231
- let canonical = api. addr_canonicalize ( & original) . unwrap ( ) ;
1232
- let recovered = api. addr_humanize ( & canonical) . unwrap ( ) ;
1233
- assert_eq ! ( recovered. as_str( ) , original) ;
1234
-
1235
1191
// normalizes input
1236
- let original = String :: from ( "CosmWasmChef" ) ;
1192
+ let original =
1193
+ String :: from ( "COSMWASM1H34LMPYWH4UPNJDG90CJF4J70AEE6Z8QQFSPUGAMJP42E4Q28KQS8S7VCP" ) ;
1237
1194
let canonical = api. addr_canonicalize ( & original) . unwrap ( ) ;
1238
1195
let recovered = api. addr_humanize ( & canonical) . unwrap ( ) ;
1239
- assert_eq ! ( recovered. as_str( ) , "cosmwasmchef" ) ;
1196
+ assert_eq ! (
1197
+ recovered,
1198
+ "cosmwasm1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqs8s7vcp"
1199
+ ) ;
1240
1200
1241
- // Long input (Juno contract address)
1201
+ // create api with 'juno' prefix
1202
+ let api = MockApi :: default ( ) . with_prefix ( "juno" ) ;
1203
+
1204
+ // long input (Juno contract address)
1242
1205
let original =
1243
1206
String :: from ( "juno1v82su97skv6ucfqvuvswe0t5fph7pfsrtraxf0x33d8ylj5qnrysdvkc95" ) ;
1244
1207
let canonical = api. addr_canonicalize ( & original) . unwrap ( ) ;
@@ -1247,32 +1210,29 @@ mod tests {
1247
1210
}
1248
1211
1249
1212
#[ test]
1250
- fn addr_canonicalize_min_input_length ( ) {
1213
+ fn addr_canonicalize_short_input ( ) {
1251
1214
let api = MockApi :: default ( ) ;
1252
- let human = String :: from ( "1" ) ;
1253
- let err = api. addr_canonicalize ( & human) . unwrap_err ( ) ;
1254
- assert ! ( err
1255
- . to_string( )
1256
- . contains( "human address too short for this mock implementation (must be >= 3)" ) ) ;
1215
+ let human = String :: from ( "cosmwasm1pj90vm" ) ;
1216
+ assert_eq ! ( api. addr_canonicalize( & human) . unwrap( ) . to_string( ) , "" ) ;
1257
1217
}
1258
1218
1259
1219
#[ test]
1260
- fn addr_canonicalize_max_input_length ( ) {
1220
+ fn addr_canonicalize_long_input ( ) {
1261
1221
let api = MockApi :: default ( ) ;
1262
1222
let human =
1263
- String :: from ( "some-extremely-long-address-not-supported-by-this-api-longer-than-supported------------------------" ) ;
1264
- let err = api. addr_canonicalize ( & human) . unwrap_err ( ) ;
1265
- assert ! ( err
1266
- . to_string( )
1267
- . contains( "human address too long for this mock implementation (must be <= 90)" ) ) ;
1223
+ "some-extremely-long-address-some-extremely-long-address-some-extremely-long-address-some-extremely-long-address" ;
1224
+ let err = api. addr_canonicalize ( human) . unwrap_err ( ) ;
1225
+ assert ! ( err. to_string( ) . contains( "Invalid input" ) ) ;
1268
1226
}
1269
1227
1270
1228
#[ test]
1271
- #[ should_panic( expected = "length not correct" ) ]
1272
1229
fn addr_humanize_input_length ( ) {
1273
1230
let api = MockApi :: default ( ) ;
1274
- let input = CanonicalAddr :: from ( vec ! [ 61 ; 11 ] ) ;
1275
- api. addr_humanize ( & input) . unwrap ( ) ;
1231
+ let input = CanonicalAddr :: from ( vec ! [ ] ) ;
1232
+ assert_eq ! (
1233
+ api. addr_humanize( & input) . unwrap( ) ,
1234
+ Addr :: unchecked( "cosmwasm1pj90vm" )
1235
+ ) ;
1276
1236
}
1277
1237
1278
1238
// Basic "works" test. Exhaustive tests on VM's side (packages/vm/src/imports.rs)
@@ -2363,27 +2323,4 @@ mod tests {
2363
2323
fn making_an_address_with_empty_prefix_should_panic ( ) {
2364
2324
MockApi :: default ( ) . with_prefix ( "" ) . addr_make ( "creator" ) ;
2365
2325
}
2366
-
2367
- #[ test]
2368
- #[ cfg( feature = "cosmwasm_1_3" ) ]
2369
- fn distribution_querier_new_works ( ) {
2370
- let addresses = [
2371
- ( "addr0000" . to_string ( ) , "addr0001" . to_string ( ) ) ,
2372
- ( "addr0002" . to_string ( ) , "addr0001" . to_string ( ) ) ,
2373
- ] ;
2374
- let btree_map = BTreeMap :: from ( addresses. clone ( ) ) ;
2375
-
2376
- // should still work with HashMap
2377
- let hashmap = HashMap :: from ( addresses. clone ( ) ) ;
2378
- let querier = DistributionQuerier :: new ( hashmap) ;
2379
- assert_eq ! ( querier. withdraw_addresses, btree_map) ;
2380
-
2381
- // should work with BTreeMap
2382
- let querier = DistributionQuerier :: new ( btree_map. clone ( ) ) ;
2383
- assert_eq ! ( querier. withdraw_addresses, btree_map) ;
2384
-
2385
- // should work with array
2386
- let querier = DistributionQuerier :: new ( addresses) ;
2387
- assert_eq ! ( querier. withdraw_addresses, btree_map) ;
2388
- }
2389
2326
}
0 commit comments