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}