clone_cw_multi_test/addons/api/bech32.rs
1use bech32::{decode, encode, FromBase32, ToBase32, Variant};
2use cosmwasm_std::testing::MockApi;
3use cosmwasm_std::{
4 Addr, Api, CanonicalAddr, RecoverPubkeyError, StdError, StdResult, VerificationError,
5};
6use sha2::{Digest, Sha256};
7
8/// Implementation of the `Api` trait that uses [`Bech32`] format
9/// for humanizing canonical addresses.
10///
11/// [`Bech32`]:https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
12pub struct MockApiBech32 {
13 api: MockApi,
14 prefix: String,
15 variant: Variant,
16}
17
18impl MockApiBech32 {
19 /// Returns `Api` implementation that uses specified prefix
20 /// to generate addresses in **Bech32** format.
21 ///
22 /// # Example
23 ///
24 /// ```
25 /// use cw_multi_test::addons::MockApiBech32;
26 ///
27 /// let api = MockApiBech32::new("juno");
28 /// let addr = api.addr_make("creator");
29 /// assert_eq!(addr.as_str(),
30 /// "juno1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqsksmtyp");
31 /// ```
32 pub fn new(prefix: &str) -> Self {
33 Self::new_with_variant(prefix, Variant::Bech32)
34 }
35
36 /// Creates `Api` implementation that uses specified prefix
37 /// to generate addresses in format defined by provided Bech32 variant.
38 pub(crate) fn new_with_variant(prefix: &str, variant: Variant) -> Self {
39 Self {
40 api: MockApi::default(),
41 prefix: prefix.to_string(),
42 variant,
43 }
44 }
45}
46
47impl Api for MockApiBech32 {
48 /// Takes a human readable address in **Bech32** format and checks if it is valid.
49 ///
50 /// If the validation succeeds, an `Addr` containing the same string as the input is returned.
51 ///
52 /// # Example
53 ///
54 /// ```
55 /// use cosmwasm_std::Api;
56 /// use cw_multi_test::addons::MockApiBech32;
57 ///
58 /// let api = MockApiBech32::new("juno");
59 /// let addr = api.addr_make("creator");
60 /// assert_eq!(api.addr_validate(addr.as_str()).unwrap().as_str(),
61 /// addr.as_str());
62 /// ```
63 fn addr_validate(&self, input: &str) -> StdResult<Addr> {
64 self.addr_humanize(&self.addr_canonicalize(input)?)
65 }
66
67 /// Takes a human readable address in **Bech32** format and returns
68 /// a canonical binary representation of it.
69 ///
70 /// # Example
71 ///
72 /// ```
73 /// use cosmwasm_std::Api;
74 /// use cw_multi_test::addons::MockApiBech32;
75 ///
76 /// let api = MockApiBech32::new("juno");
77 /// let addr = api.addr_make("creator");
78 /// assert_eq!(api.addr_canonicalize(addr.as_str()).unwrap().to_string(),
79 /// "BC6BFD848EBD7819C9A82BF124D65E7F739D08E002601E23BB906AACD40A3D81");
80 /// ```
81 fn addr_canonicalize(&self, input: &str) -> StdResult<CanonicalAddr> {
82 if let Ok((prefix, decoded, variant)) = decode(input) {
83 if prefix == self.prefix && variant == self.variant {
84 if let Ok(bytes) = Vec::<u8>::from_base32(&decoded) {
85 return Ok(bytes.into());
86 }
87 }
88 }
89 Err(StdError::generic_err("Invalid input"))
90 }
91
92 /// Takes a canonical address and returns a human readable address in **Bech32** format.
93 ///
94 /// This is the inverse operation of [`addr_canonicalize`].
95 ///
96 /// [`addr_canonicalize`]: MockApiBech32::addr_canonicalize
97 ///
98 /// # Example
99 ///
100 /// ```
101 /// use cosmwasm_std::Api;
102 /// use cw_multi_test::addons::MockApiBech32;
103 ///
104 /// let api = MockApiBech32::new("juno");
105 /// let addr = api.addr_make("creator");
106 /// let canonical_addr = api.addr_canonicalize(addr.as_str()).unwrap();
107 /// assert_eq!(api.addr_humanize(&canonical_addr).unwrap().as_str(),
108 /// addr.as_str());
109 /// ```
110 fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult<Addr> {
111 if let Ok(encoded) = encode(&self.prefix, canonical.as_slice().to_base32(), self.variant) {
112 Ok(Addr::unchecked(encoded))
113 } else {
114 Err(StdError::generic_err("Invalid canonical address"))
115 }
116 }
117
118 fn secp256k1_verify(
119 &self,
120 message_hash: &[u8],
121 signature: &[u8],
122 public_key: &[u8],
123 ) -> Result<bool, VerificationError> {
124 self.api
125 .secp256k1_verify(message_hash, signature, public_key)
126 }
127
128 fn secp256k1_recover_pubkey(
129 &self,
130 message_hash: &[u8],
131 signature: &[u8],
132 recovery_param: u8,
133 ) -> Result<Vec<u8>, RecoverPubkeyError> {
134 self.api
135 .secp256k1_recover_pubkey(message_hash, signature, recovery_param)
136 }
137
138 fn ed25519_verify(
139 &self,
140 message: &[u8],
141 signature: &[u8],
142 public_key: &[u8],
143 ) -> Result<bool, VerificationError> {
144 self.api.ed25519_verify(message, signature, public_key)
145 }
146
147 fn ed25519_batch_verify(
148 &self,
149 messages: &[&[u8]],
150 signatures: &[&[u8]],
151 public_keys: &[&[u8]],
152 ) -> Result<bool, VerificationError> {
153 self.api
154 .ed25519_batch_verify(messages, signatures, public_keys)
155 }
156
157 fn debug(&self, message: &str) {
158 self.api.debug(message)
159 }
160}
161
162impl MockApiBech32 {
163 /// Returns an address in **Bech32** format, built from provided input string.
164 ///
165 /// # Example
166 ///
167 /// ```
168 /// use cw_multi_test::addons::MockApiBech32;
169 ///
170 /// let api = MockApiBech32::new("juno");
171 /// let addr = api.addr_make("creator");
172 /// assert_eq!(addr.as_str(),
173 /// "juno1h34lmpywh4upnjdg90cjf4j70aee6z8qqfspugamjp42e4q28kqsksmtyp");
174 /// ```
175 ///
176 /// # Panics
177 ///
178 /// This function panics when generating a valid address in **Bech32**
179 /// format is not possible, especially when prefix is too long or empty.
180 pub fn addr_make(&self, input: &str) -> Addr {
181 let digest = Sha256::digest(input).to_vec();
182 match encode(&self.prefix, digest.to_base32(), self.variant) {
183 Ok(address) => Addr::unchecked(address),
184 Err(reason) => panic!("Generating address failed with reason: {}", reason),
185 }
186 }
187}