jk_cosmwasm_std/
mock.rs

1use serde::de::DeserializeOwned;
2#[cfg(feature = "stargate")]
3use serde::Serialize;
4use std::collections::HashMap;
5
6use crate::addresses::{Addr, CanonicalAddr};
7use crate::binary::Binary;
8use crate::coins::Coin;
9use crate::deps::OwnedDeps;
10use crate::errors::{RecoverPubkeyError, StdError, StdResult, SystemError, VerificationError};
11#[cfg(feature = "stargate")]
12use crate::ibc::{
13    IbcAcknowledgement, IbcChannel, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg,
14    IbcEndpoint, IbcOrder, IbcPacket, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg,
15    IbcTimeoutBlock,
16};
17use crate::query::{
18    AllBalanceResponse, BalanceResponse, BankQuery, CustomQuery, QueryRequest, WasmQuery,
19};
20#[cfg(feature = "staking")]
21use crate::query::{
22    AllDelegationsResponse, AllValidatorsResponse, BondedDenomResponse, DelegationResponse,
23    FullDelegation, StakingQuery, Validator, ValidatorResponse,
24};
25use crate::results::{ContractResult, Empty, SystemResult};
26use crate::serde::{from_slice, to_binary};
27use crate::storage::MemoryStorage;
28use crate::timestamp::Timestamp;
29use crate::traits::{Api, Querier, QuerierResult};
30use crate::types::{BlockInfo, ContractInfo, Env, MessageInfo};
31use crate::Attribute;
32
33pub const MOCK_CONTRACT_ADDR: &str = "cosmos2contract";
34
35/// All external requirements that can be injected for unit tests.
36/// It sets the given balance for the contract itself, nothing else
37pub fn mock_dependencies(
38    contract_balance: &[Coin],
39) -> OwnedDeps<MockStorage, MockApi, MockQuerier> {
40    OwnedDeps {
41        storage: MockStorage::default(),
42        api: MockApi::default(),
43        querier: MockQuerier::new(&[(MOCK_CONTRACT_ADDR, contract_balance)]),
44    }
45}
46
47/// Initializes the querier along with the mock_dependencies.
48/// Sets all balances provided (yoy must explicitly set contract balance if desired)
49pub fn mock_dependencies_with_balances(
50    balances: &[(&str, &[Coin])],
51) -> OwnedDeps<MockStorage, MockApi, MockQuerier> {
52    OwnedDeps {
53        storage: MockStorage::default(),
54        api: MockApi::default(),
55        querier: MockQuerier::new(balances),
56    }
57}
58
59// Use MemoryStorage implementation (which is valid in non-testcode)
60// We can later make simplifications here if needed
61pub type MockStorage = MemoryStorage;
62
63/// Length of canonical addresses created with this API. Contracts should not make any assumtions
64/// what this value is.
65/// The value here must be restorable with `SHUFFLES_ENCODE` + `SHUFFLES_DECODE` in-shuffles.
66const CANONICAL_LENGTH: usize = 54;
67
68const SHUFFLES_ENCODE: usize = 18;
69const SHUFFLES_DECODE: usize = 2;
70
71// MockPrecompiles zero pads all human addresses to make them fit the canonical_length
72// it trims off zeros for the reverse operation.
73// not really smart, but allows us to see a difference (and consistent length for canonical adddresses)
74#[derive(Copy, Clone)]
75pub struct MockApi {
76    /// Length of canonical addresses created with this API. Contracts should not make any assumtions
77    /// what this value is.
78    canonical_length: usize,
79}
80
81impl Default for MockApi {
82    fn default() -> Self {
83        MockApi {
84            canonical_length: CANONICAL_LENGTH,
85        }
86    }
87}
88
89impl Api for MockApi {
90    fn addr_validate(&self, human: &str) -> StdResult<Addr> {
91        self.addr_canonicalize(human).map(|_canonical| ())?;
92        Ok(Addr::unchecked(human))
93    }
94
95    fn addr_canonicalize(&self, human: &str) -> StdResult<CanonicalAddr> {
96        // Dummy input validation. This is more sophisticated for formats like bech32, where format and checksum are validated.
97        if human.len() < 3 {
98            return Err(StdError::generic_err(
99                "Invalid input: human address too short",
100            ));
101        }
102        if human.len() > self.canonical_length {
103            return Err(StdError::generic_err(
104                "Invalid input: human address too long",
105            ));
106        }
107
108        let mut out = Vec::from(human);
109
110        // pad to canonical length with NULL bytes
111        out.resize(self.canonical_length, 0x00);
112        // content-dependent rotate followed by shuffle to destroy
113        // the most obvious structure (https://github.com/CosmWasm/cosmwasm/issues/552)
114        let rotate_by = digit_sum(&out) % self.canonical_length;
115        out.rotate_left(rotate_by);
116        for _ in 0..SHUFFLES_ENCODE {
117            out = riffle_shuffle(&out);
118        }
119        Ok(out.into())
120    }
121
122    fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult<Addr> {
123        if canonical.len() != self.canonical_length {
124            return Err(StdError::generic_err(
125                "Invalid input: canonical address length not correct",
126            ));
127        }
128
129        let mut tmp: Vec<u8> = canonical.clone().into();
130        // Shuffle two more times which restored the original value (24 elements are back to original after 20 rounds)
131        for _ in 0..SHUFFLES_DECODE {
132            tmp = riffle_shuffle(&tmp);
133        }
134        // Rotate back
135        let rotate_by = digit_sum(&tmp) % self.canonical_length;
136        tmp.rotate_right(rotate_by);
137        // Remove NULL bytes (i.e. the padding)
138        let trimmed = tmp.into_iter().filter(|&x| x != 0x00).collect();
139        // decode UTF-8 bytes into string
140        let human = String::from_utf8(trimmed)?;
141        Ok(Addr::unchecked(human))
142    }
143
144    fn secp256k1_verify(
145        &self,
146        message_hash: &[u8],
147        signature: &[u8],
148        public_key: &[u8],
149    ) -> Result<bool, VerificationError> {
150        Ok(cosmwasm_crypto::secp256k1_verify(
151            message_hash,
152            signature,
153            public_key,
154        )?)
155    }
156
157    fn secp256k1_recover_pubkey(
158        &self,
159        message_hash: &[u8],
160        signature: &[u8],
161        recovery_param: u8,
162    ) -> Result<Vec<u8>, RecoverPubkeyError> {
163        let pubkey =
164            cosmwasm_crypto::secp256k1_recover_pubkey(message_hash, signature, recovery_param)?;
165        Ok(pubkey.to_vec())
166    }
167
168    fn ed25519_verify(
169        &self,
170        message: &[u8],
171        signature: &[u8],
172        public_key: &[u8],
173    ) -> Result<bool, VerificationError> {
174        Ok(cosmwasm_crypto::ed25519_verify(
175            message, signature, public_key,
176        )?)
177    }
178
179    fn ed25519_batch_verify(
180        &self,
181        messages: &[&[u8]],
182        signatures: &[&[u8]],
183        public_keys: &[&[u8]],
184    ) -> Result<bool, VerificationError> {
185        Ok(cosmwasm_crypto::ed25519_batch_verify(
186            messages,
187            signatures,
188            public_keys,
189        )?)
190    }
191
192    fn debug(&self, message: &str) {
193        println!("{}", message);
194    }
195}
196
197/// Returns a default enviroment with height, time, chain_id, and contract address
198/// You can submit as is to most contracts, or modify height/time if you want to
199/// test for expiration.
200///
201/// This is intended for use in test code only.
202pub fn mock_env() -> Env {
203    Env {
204        block: BlockInfo {
205            height: 12_345,
206            time: Timestamp::from_nanos(1_571_797_419_879_305_533),
207            chain_id: "cosmos-testnet-14002".to_string(),
208        },
209        contract: ContractInfo {
210            address: Addr::unchecked(MOCK_CONTRACT_ADDR),
211        },
212    }
213}
214
215/// Just set sender and funds for the message.
216/// This is intended for use in test code only.
217pub fn mock_info(sender: &str, funds: &[Coin]) -> MessageInfo {
218    MessageInfo {
219        sender: Addr::unchecked(sender),
220        funds: funds.to_vec(),
221    }
222}
223
224/// Creates an IbcChannel for testing. You set a few key parameters for handshaking,
225/// If you want to set more, use this as a default and mutate other fields
226#[cfg(feature = "stargate")]
227pub fn mock_ibc_channel(my_channel_id: &str, order: IbcOrder, version: &str) -> IbcChannel {
228    IbcChannel {
229        endpoint: IbcEndpoint {
230            port_id: "my_port".to_string(),
231            channel_id: my_channel_id.to_string(),
232        },
233        counterparty_endpoint: IbcEndpoint {
234            port_id: "their_port".to_string(),
235            channel_id: "channel-7".to_string(),
236        },
237        order,
238        version: version.to_string(),
239        connection_id: "connection-2".to_string(),
240    }
241}
242
243/// Creates a IbcChannelOpenMsg::OpenInit for testing ibc_channel_open.
244#[cfg(feature = "stargate")]
245pub fn mock_ibc_channel_open_init(
246    my_channel_id: &str,
247    order: IbcOrder,
248    version: &str,
249) -> IbcChannelOpenMsg {
250    IbcChannelOpenMsg::new_init(mock_ibc_channel(my_channel_id, order, version))
251}
252
253/// Creates a IbcChannelOpenMsg::OpenTry for testing ibc_channel_open.
254#[cfg(feature = "stargate")]
255pub fn mock_ibc_channel_open_try(
256    my_channel_id: &str,
257    order: IbcOrder,
258    version: &str,
259) -> IbcChannelOpenMsg {
260    IbcChannelOpenMsg::new_try(mock_ibc_channel(my_channel_id, order, version), version)
261}
262
263/// Creates a IbcChannelConnectMsg::ConnectAck for testing ibc_channel_connect.
264#[cfg(feature = "stargate")]
265pub fn mock_ibc_channel_connect_ack(
266    my_channel_id: &str,
267    order: IbcOrder,
268    version: &str,
269) -> IbcChannelConnectMsg {
270    IbcChannelConnectMsg::new_ack(mock_ibc_channel(my_channel_id, order, version), version)
271}
272
273/// Creates a IbcChannelConnectMsg::ConnectConfirm for testing ibc_channel_connect.
274#[cfg(feature = "stargate")]
275pub fn mock_ibc_channel_connect_confirm(
276    my_channel_id: &str,
277    order: IbcOrder,
278    version: &str,
279) -> IbcChannelConnectMsg {
280    IbcChannelConnectMsg::new_confirm(mock_ibc_channel(my_channel_id, order, version))
281}
282
283/// Creates a IbcChannelCloseMsg::CloseInit for testing ibc_channel_close.
284#[cfg(feature = "stargate")]
285pub fn mock_ibc_channel_close_init(
286    my_channel_id: &str,
287    order: IbcOrder,
288    version: &str,
289) -> IbcChannelCloseMsg {
290    IbcChannelCloseMsg::new_init(mock_ibc_channel(my_channel_id, order, version))
291}
292
293/// Creates a IbcChannelCloseMsg::CloseConfirm for testing ibc_channel_close.
294#[cfg(feature = "stargate")]
295pub fn mock_ibc_channel_close_confirm(
296    my_channel_id: &str,
297    order: IbcOrder,
298    version: &str,
299) -> IbcChannelCloseMsg {
300    IbcChannelCloseMsg::new_confirm(mock_ibc_channel(my_channel_id, order, version))
301}
302
303/// Creates a IbcPacketReceiveMsg for testing ibc_packet_receive. You set a few key parameters that are
304/// often parsed. If you want to set more, use this as a default and mutate other fields
305#[cfg(feature = "stargate")]
306pub fn mock_ibc_packet_recv(
307    my_channel_id: &str,
308    data: &impl Serialize,
309) -> StdResult<IbcPacketReceiveMsg> {
310    Ok(IbcPacketReceiveMsg::new(IbcPacket {
311        data: to_binary(data)?,
312        src: IbcEndpoint {
313            port_id: "their-port".to_string(),
314            channel_id: "channel-1234".to_string(),
315        },
316        dest: IbcEndpoint {
317            port_id: "our-port".to_string(),
318            channel_id: my_channel_id.into(),
319        },
320        sequence: 27,
321        timeout: IbcTimeoutBlock {
322            revision: 1,
323            height: 12345678,
324        }
325        .into(),
326    }))
327}
328
329/// Creates a IbcPacket for testing ibc_packet_{ack,timeout}. You set a few key parameters that are
330/// often parsed. If you want to set more, use this as a default and mutate other fields.
331/// The difference from mock_ibc_packet_recv is if `my_channel_id` is src or dest.
332#[cfg(feature = "stargate")]
333fn mock_ibc_packet(my_channel_id: &str, data: &impl Serialize) -> StdResult<IbcPacket> {
334    Ok(IbcPacket {
335        data: to_binary(data)?,
336        src: IbcEndpoint {
337            port_id: "their-port".to_string(),
338            channel_id: my_channel_id.into(),
339        },
340        dest: IbcEndpoint {
341            port_id: "our-port".to_string(),
342            channel_id: "channel-1234".to_string(),
343        },
344        sequence: 29,
345        timeout: IbcTimeoutBlock {
346            revision: 1,
347            height: 432332552,
348        }
349        .into(),
350    })
351}
352
353/// Creates a IbcPacketAckMsg for testing ibc_packet_ack. You set a few key parameters that are
354/// often parsed. If you want to set more, use this as a default and mutate other fields.
355/// The difference from mock_ibc_packet_recv is if `my_channel_id` is src or dest.
356#[cfg(feature = "stargate")]
357pub fn mock_ibc_packet_ack(
358    my_channel_id: &str,
359    data: &impl Serialize,
360    ack: IbcAcknowledgement,
361) -> StdResult<IbcPacketAckMsg> {
362    let packet = mock_ibc_packet(my_channel_id, data)?;
363
364    Ok(IbcPacketAckMsg::new(ack, packet))
365}
366
367/// Creates a IbcPacketTimeoutMsg for testing ibc_packet_timeout. You set a few key parameters that are
368/// often parsed. If you want to set more, use this as a default and mutate other fields.
369/// The difference from mock_ibc_packet_recv is if `my_channel_id` is src or dest./
370#[cfg(feature = "stargate")]
371pub fn mock_ibc_packet_timeout(
372    my_channel_id: &str,
373    data: &impl Serialize,
374) -> StdResult<IbcPacketTimeoutMsg> {
375    mock_ibc_packet(my_channel_id, data).map(IbcPacketTimeoutMsg::new)
376}
377
378/// The same type as cosmwasm-std's QuerierResult, but easier to reuse in
379/// cosmwasm-vm. It might diverge from QuerierResult at some point.
380pub type MockQuerierCustomHandlerResult = SystemResult<ContractResult<Binary>>;
381
382/// MockQuerier holds an immutable table of bank balances
383/// TODO: also allow querying contracts
384pub struct MockQuerier<C: DeserializeOwned = Empty> {
385    bank: BankQuerier,
386    #[cfg(feature = "staking")]
387    staking: StakingQuerier,
388    // placeholder to add support later
389    wasm: NoWasmQuerier,
390    /// A handler to handle custom queries. This is set to a dummy handler that
391    /// always errors by default. Update it via `with_custom_handler`.
392    ///
393    /// Use box to avoid the need of another generic type
394    custom_handler: Box<dyn for<'a> Fn(&'a C) -> MockQuerierCustomHandlerResult>,
395}
396
397impl<C: DeserializeOwned> MockQuerier<C> {
398    pub fn new(balances: &[(&str, &[Coin])]) -> Self {
399        MockQuerier {
400            bank: BankQuerier::new(balances),
401            #[cfg(feature = "staking")]
402            staking: StakingQuerier::default(),
403            wasm: NoWasmQuerier {},
404            // strange argument notation suggested as a workaround here: https://github.com/rust-lang/rust/issues/41078#issuecomment-294296365
405            custom_handler: Box::from(|_: &_| -> MockQuerierCustomHandlerResult {
406                SystemResult::Err(SystemError::UnsupportedRequest {
407                    kind: "custom".to_string(),
408                })
409            }),
410        }
411    }
412
413    // set a new balance for the given address and return the old balance
414    pub fn update_balance(
415        &mut self,
416        addr: impl Into<String>,
417        balance: Vec<Coin>,
418    ) -> Option<Vec<Coin>> {
419        self.bank.balances.insert(addr.into(), balance)
420    }
421
422    #[cfg(feature = "staking")]
423    pub fn update_staking(
424        &mut self,
425        denom: &str,
426        validators: &[crate::query::Validator],
427        delegations: &[crate::query::FullDelegation],
428    ) {
429        self.staking = StakingQuerier::new(denom, validators, delegations);
430    }
431
432    pub fn with_custom_handler<CH: 'static>(mut self, handler: CH) -> Self
433    where
434        CH: Fn(&C) -> MockQuerierCustomHandlerResult,
435    {
436        self.custom_handler = Box::from(handler);
437        self
438    }
439}
440
441impl<C: CustomQuery + DeserializeOwned> Querier for MockQuerier<C> {
442    fn raw_query(&self, bin_request: &[u8]) -> QuerierResult {
443        let request: QueryRequest<C> = match from_slice(bin_request) {
444            Ok(v) => v,
445            Err(e) => {
446                return SystemResult::Err(SystemError::InvalidRequest {
447                    error: format!("Parsing query request: {}", e),
448                    request: bin_request.into(),
449                })
450            }
451        };
452        self.handle_query(&request)
453    }
454}
455
456impl<C: CustomQuery + DeserializeOwned> MockQuerier<C> {
457    pub fn handle_query(&self, request: &QueryRequest<C>) -> QuerierResult {
458        match &request {
459            QueryRequest::Bank(bank_query) => self.bank.query(bank_query),
460            QueryRequest::Custom(custom_query) => (*self.custom_handler)(custom_query),
461            #[cfg(feature = "staking")]
462            QueryRequest::Staking(staking_query) => self.staking.query(staking_query),
463            QueryRequest::Wasm(msg) => self.wasm.query(msg),
464            #[cfg(feature = "stargate")]
465            QueryRequest::Stargate { .. } => SystemResult::Err(SystemError::UnsupportedRequest {
466                kind: "Stargate".to_string(),
467            }),
468            #[cfg(feature = "stargate")]
469            QueryRequest::Ibc(_) => SystemResult::Err(SystemError::UnsupportedRequest {
470                kind: "Ibc".to_string(),
471            }),
472        }
473    }
474}
475
476#[derive(Clone, Default)]
477struct NoWasmQuerier {
478    // FIXME: actually provide a way to call out
479}
480
481impl NoWasmQuerier {
482    fn query(&self, request: &WasmQuery) -> QuerierResult {
483        let addr = match request {
484            WasmQuery::Smart { contract_addr, .. } => contract_addr,
485            WasmQuery::Raw { contract_addr, .. } => contract_addr,
486        }
487        .clone();
488        SystemResult::Err(SystemError::NoSuchContract { addr })
489    }
490}
491
492#[derive(Clone, Default)]
493pub struct BankQuerier {
494    balances: HashMap<String, Vec<Coin>>,
495}
496
497impl BankQuerier {
498    pub fn new(balances: &[(&str, &[Coin])]) -> Self {
499        let mut map = HashMap::new();
500        for (addr, coins) in balances.iter() {
501            map.insert(addr.to_string(), coins.to_vec());
502        }
503        BankQuerier { balances: map }
504    }
505
506    pub fn query(&self, request: &BankQuery) -> QuerierResult {
507        let contract_result: ContractResult<Binary> = match request {
508            BankQuery::Balance { address, denom } => {
509                // proper error on not found, serialize result on found
510                let amount = self
511                    .balances
512                    .get(address)
513                    .and_then(|v| v.iter().find(|c| &c.denom == denom).map(|c| c.amount))
514                    .unwrap_or_default();
515                let bank_res = BalanceResponse {
516                    amount: Coin {
517                        amount,
518                        denom: denom.to_string(),
519                    },
520                };
521                to_binary(&bank_res).into()
522            }
523            BankQuery::AllBalances { address } => {
524                // proper error on not found, serialize result on found
525                let bank_res = AllBalanceResponse {
526                    amount: self.balances.get(address).cloned().unwrap_or_default(),
527                };
528                to_binary(&bank_res).into()
529            }
530        };
531        // system result is always ok in the mock implementation
532        SystemResult::Ok(contract_result)
533    }
534}
535
536#[cfg(feature = "staking")]
537#[derive(Clone, Default)]
538pub struct StakingQuerier {
539    denom: String,
540    validators: Vec<Validator>,
541    delegations: Vec<FullDelegation>,
542}
543
544#[cfg(feature = "staking")]
545impl StakingQuerier {
546    pub fn new(denom: &str, validators: &[Validator], delegations: &[FullDelegation]) -> Self {
547        StakingQuerier {
548            denom: denom.to_string(),
549            validators: validators.to_vec(),
550            delegations: delegations.to_vec(),
551        }
552    }
553
554    pub fn query(&self, request: &StakingQuery) -> QuerierResult {
555        let contract_result: ContractResult<Binary> = match request {
556            StakingQuery::BondedDenom {} => {
557                let res = BondedDenomResponse {
558                    denom: self.denom.clone(),
559                };
560                to_binary(&res).into()
561            }
562            StakingQuery::AllValidators {} => {
563                let res = AllValidatorsResponse {
564                    validators: self.validators.clone(),
565                };
566                to_binary(&res).into()
567            }
568            StakingQuery::Validator { address } => {
569                let validator: Option<Validator> = self
570                    .validators
571                    .iter()
572                    .find(|validator| validator.address == *address)
573                    .cloned();
574                let res = ValidatorResponse { validator };
575                to_binary(&res).into()
576            }
577            StakingQuery::AllDelegations { delegator } => {
578                let delegations: Vec<_> = self
579                    .delegations
580                    .iter()
581                    .filter(|d| d.delegator.as_str() == delegator)
582                    .cloned()
583                    .map(|d| d.into())
584                    .collect();
585                let res = AllDelegationsResponse { delegations };
586                to_binary(&res).into()
587            }
588            StakingQuery::Delegation {
589                delegator,
590                validator,
591            } => {
592                let delegation = self
593                    .delegations
594                    .iter()
595                    .find(|d| d.delegator.as_str() == delegator && d.validator == *validator);
596                let res = DelegationResponse {
597                    delegation: delegation.cloned(),
598                };
599                to_binary(&res).into()
600            }
601        };
602        // system result is always ok in the mock implementation
603        SystemResult::Ok(contract_result)
604    }
605}
606
607/// Performs a perfect shuffle (in shuffle)
608///
609/// https://en.wikipedia.org/wiki/Riffle_shuffle_permutation#Perfect_shuffles
610/// https://en.wikipedia.org/wiki/In_shuffle
611///
612/// The number of shuffles required to restore the original order are listed in
613/// https://oeis.org/A002326, e.g.:
614///
615/// ```ignore
616/// 2: 2
617/// 4: 4
618/// 6: 3
619/// 8: 6
620/// 10: 10
621/// 12: 12
622/// 14: 4
623/// 16: 8
624/// 18: 18
625/// 20: 6
626/// 22: 11
627/// 24: 20
628/// 26: 18
629/// 28: 28
630/// 30: 5
631/// 32: 10
632/// 34: 12
633/// 36: 36
634/// 38: 12
635/// 40: 20
636/// 42: 14
637/// 44: 12
638/// 46: 23
639/// 48: 21
640/// 50: 8
641/// 52: 52
642/// 54: 20
643/// 56: 18
644/// 58: 58
645/// 60: 60
646/// 62: 6
647/// 64: 12
648/// 66: 66
649/// 68: 22
650/// 70: 35
651/// 72: 9
652/// 74: 20
653/// ```
654pub fn riffle_shuffle<T: Clone>(input: &[T]) -> Vec<T> {
655    assert!(
656        input.len() % 2 == 0,
657        "Method only defined for even number of elements"
658    );
659    let mid = input.len() / 2;
660    let (left, right) = input.split_at(mid);
661    let mut out = Vec::<T>::with_capacity(input.len());
662    for i in 0..mid {
663        out.push(right[i].clone());
664        out.push(left[i].clone());
665    }
666    out
667}
668
669pub fn digit_sum(input: &[u8]) -> usize {
670    input.iter().fold(0, |sum, val| sum + (*val as usize))
671}
672
673/// Only for test code. This bypasses assertions in new, allowing us to create _*
674/// Attributes to simulate responses from the blockchain
675pub fn mock_wasmd_attr(key: impl Into<String>, value: impl Into<String>) -> Attribute {
676    Attribute {
677        key: key.into(),
678        value: value.into(),
679    }
680}
681
682#[cfg(test)]
683mod tests {
684    use super::*;
685    use crate::{coin, coins, from_binary};
686    #[cfg(feature = "staking")]
687    use crate::{Decimal, Delegation};
688    use hex_literal::hex;
689
690    const SECP256K1_MSG_HASH_HEX: &str =
691        "5ae8317d34d1e595e3fa7247db80c0af4320cce1116de187f8f7e2e099c0d8d0";
692    const SECP256K1_SIG_HEX: &str = "207082eb2c3dfa0b454e0906051270ba4074ac93760ba9e7110cd9471475111151eb0dbbc9920e72146fb564f99d039802bf6ef2561446eb126ef364d21ee9c4";
693    const SECP256K1_PUBKEY_HEX: &str = "04051c1ee2190ecfb174bfe4f90763f2b4ff7517b70a2aec1876ebcfd644c4633fb03f3cfbd94b1f376e34592d9d41ccaf640bb751b00a1fadeb0c01157769eb73";
694
695    const ED25519_MSG_HEX: &str = "72";
696    const ED25519_SIG_HEX: &str = "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00";
697    const ED25519_PUBKEY_HEX: &str =
698        "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c";
699
700    #[test]
701    fn mock_info_works() {
702        let info = mock_info("my name", &coins(100, "atom"));
703        assert_eq!(
704            info,
705            MessageInfo {
706                sender: Addr::unchecked("my name"),
707                funds: vec![Coin {
708                    amount: 100u128.into(),
709                    denom: "atom".into(),
710                }]
711            }
712        );
713    }
714
715    #[test]
716    fn canonicalize_and_humanize_restores_original() {
717        let api = MockApi::default();
718
719        let original = String::from("shorty");
720        let canonical = api.addr_canonicalize(&original).unwrap();
721        let recovered = api.addr_humanize(&canonical).unwrap();
722        assert_eq!(recovered, original);
723    }
724
725    #[test]
726    #[should_panic(expected = "address too short")]
727    fn addr_canonicalize_min_input_length() {
728        let api = MockApi::default();
729        let human = String::from("1");
730        let _ = api.addr_canonicalize(&human).unwrap();
731    }
732
733    #[test]
734    #[should_panic(expected = "address too long")]
735    fn addr_canonicalize_max_input_length() {
736        let api = MockApi::default();
737        let human =
738            String::from("some-extremely-long-address-not-supported-by-this-api-longer-than-54");
739        let _ = api.addr_canonicalize(&human).unwrap();
740    }
741
742    #[test]
743    fn addr_canonicalize_works_with_string_inputs() {
744        let api = MockApi::default();
745
746        let input = String::from("foobar123");
747        api.addr_canonicalize(&input).unwrap();
748
749        let input = "foobar456";
750        api.addr_canonicalize(input).unwrap();
751    }
752
753    #[test]
754    #[should_panic(expected = "length not correct")]
755    fn addr_humanize_input_length() {
756        let api = MockApi::default();
757        let input = CanonicalAddr(Binary(vec![61; 11]));
758        api.addr_humanize(&input).unwrap();
759    }
760
761    // Basic "works" test. Exhaustive tests on VM's side (packages/vm/src/imports.rs)
762    #[test]
763    fn secp256k1_verify_works() {
764        let api = MockApi::default();
765
766        let hash = hex::decode(SECP256K1_MSG_HASH_HEX).unwrap();
767        let signature = hex::decode(SECP256K1_SIG_HEX).unwrap();
768        let public_key = hex::decode(SECP256K1_PUBKEY_HEX).unwrap();
769
770        assert!(api
771            .secp256k1_verify(&hash, &signature, &public_key)
772            .unwrap());
773    }
774
775    // Basic "fails" test. Exhaustive tests on VM's side (packages/vm/src/imports.rs)
776    #[test]
777    fn secp256k1_verify_fails() {
778        let api = MockApi::default();
779
780        let mut hash = hex::decode(SECP256K1_MSG_HASH_HEX).unwrap();
781        // alter hash
782        hash[0] ^= 0x01;
783        let signature = hex::decode(SECP256K1_SIG_HEX).unwrap();
784        let public_key = hex::decode(SECP256K1_PUBKEY_HEX).unwrap();
785
786        assert!(!api
787            .secp256k1_verify(&hash, &signature, &public_key)
788            .unwrap());
789    }
790
791    // Basic "errors" test. Exhaustive tests on VM's side (packages/vm/src/imports.rs)
792    #[test]
793    fn secp256k1_verify_errs() {
794        let api = MockApi::default();
795
796        let hash = hex::decode(SECP256K1_MSG_HASH_HEX).unwrap();
797        let signature = hex::decode(SECP256K1_SIG_HEX).unwrap();
798        let public_key = vec![];
799
800        let res = api.secp256k1_verify(&hash, &signature, &public_key);
801        assert_eq!(res.unwrap_err(), VerificationError::InvalidPubkeyFormat);
802    }
803
804    #[test]
805    fn secp256k1_recover_pubkey_works() {
806        let api = MockApi::default();
807
808        // https://gist.github.com/webmaster128/130b628d83621a33579751846699ed15
809        let hash = hex!("5ae8317d34d1e595e3fa7247db80c0af4320cce1116de187f8f7e2e099c0d8d0");
810        let signature = hex!("45c0b7f8c09a9e1f1cea0c25785594427b6bf8f9f878a8af0b1abbb48e16d0920d8becd0c220f67c51217eecfd7184ef0732481c843857e6bc7fc095c4f6b788");
811        let recovery_param = 1;
812        let expected = hex!("044a071e8a6e10aada2b8cf39fa3b5fb3400b04e99ea8ae64ceea1a977dbeaf5d5f8c8fbd10b71ab14cd561f7df8eb6da50f8a8d81ba564342244d26d1d4211595");
813
814        let pubkey = api
815            .secp256k1_recover_pubkey(&hash, &signature, recovery_param)
816            .unwrap();
817        assert_eq!(pubkey, expected);
818    }
819
820    #[test]
821    fn secp256k1_recover_pubkey_fails_for_wrong_recovery_param() {
822        let api = MockApi::default();
823
824        // https://gist.github.com/webmaster128/130b628d83621a33579751846699ed15
825        let hash = hex!("5ae8317d34d1e595e3fa7247db80c0af4320cce1116de187f8f7e2e099c0d8d0");
826        let signature = hex!("45c0b7f8c09a9e1f1cea0c25785594427b6bf8f9f878a8af0b1abbb48e16d0920d8becd0c220f67c51217eecfd7184ef0732481c843857e6bc7fc095c4f6b788");
827        let _recovery_param = 1;
828        let expected = hex!("044a071e8a6e10aada2b8cf39fa3b5fb3400b04e99ea8ae64ceea1a977dbeaf5d5f8c8fbd10b71ab14cd561f7df8eb6da50f8a8d81ba564342244d26d1d4211595");
829
830        // Wrong recovery param leads to different pubkey
831        let pubkey = api.secp256k1_recover_pubkey(&hash, &signature, 0).unwrap();
832        assert_eq!(pubkey.len(), 65);
833        assert_ne!(pubkey, expected);
834
835        // Invalid recovery param leads to error
836        let result = api.secp256k1_recover_pubkey(&hash, &signature, 42);
837        match result.unwrap_err() {
838            RecoverPubkeyError::InvalidRecoveryParam => {}
839            err => panic!("Unexpected error: {:?}", err),
840        }
841    }
842
843    #[test]
844    fn secp256k1_recover_pubkey_fails_for_wrong_hash() {
845        let api = MockApi::default();
846
847        // https://gist.github.com/webmaster128/130b628d83621a33579751846699ed15
848        let hash = hex!("5ae8317d34d1e595e3fa7247db80c0af4320cce1116de187f8f7e2e099c0d8d0");
849        let signature = hex!("45c0b7f8c09a9e1f1cea0c25785594427b6bf8f9f878a8af0b1abbb48e16d0920d8becd0c220f67c51217eecfd7184ef0732481c843857e6bc7fc095c4f6b788");
850        let recovery_param = 1;
851        let expected = hex!("044a071e8a6e10aada2b8cf39fa3b5fb3400b04e99ea8ae64ceea1a977dbeaf5d5f8c8fbd10b71ab14cd561f7df8eb6da50f8a8d81ba564342244d26d1d4211595");
852
853        // Wrong hash
854        let mut corrupted_hash = hash;
855        corrupted_hash[0] ^= 0x01;
856        let pubkey = api
857            .secp256k1_recover_pubkey(&corrupted_hash, &signature, recovery_param)
858            .unwrap();
859        assert_eq!(pubkey.len(), 65);
860        assert_ne!(pubkey, expected);
861
862        // Malformed hash
863        let mut malformed_hash = hash.to_vec();
864        malformed_hash.push(0x8a);
865        let result = api.secp256k1_recover_pubkey(&malformed_hash, &signature, recovery_param);
866        match result.unwrap_err() {
867            RecoverPubkeyError::InvalidHashFormat => {}
868            err => panic!("Unexpected error: {:?}", err),
869        }
870    }
871
872    // Basic "works" test. Exhaustive tests on VM's side (packages/vm/src/imports.rs)
873    #[test]
874    fn ed25519_verify_works() {
875        let api = MockApi::default();
876
877        let msg = hex::decode(ED25519_MSG_HEX).unwrap();
878        let signature = hex::decode(ED25519_SIG_HEX).unwrap();
879        let public_key = hex::decode(ED25519_PUBKEY_HEX).unwrap();
880
881        assert!(api.ed25519_verify(&msg, &signature, &public_key).unwrap());
882    }
883
884    // Basic "fails" test. Exhaustive tests on VM's side (packages/vm/src/imports.rs)
885    #[test]
886    fn ed25519_verify_fails() {
887        let api = MockApi::default();
888
889        let mut msg = hex::decode(ED25519_MSG_HEX).unwrap();
890        // alter msg
891        msg[0] ^= 0x01;
892        let signature = hex::decode(ED25519_SIG_HEX).unwrap();
893        let public_key = hex::decode(ED25519_PUBKEY_HEX).unwrap();
894
895        assert!(!api.ed25519_verify(&msg, &signature, &public_key).unwrap());
896    }
897
898    // Basic "errors" test. Exhaustive tests on VM's side (packages/vm/src/imports.rs)
899    #[test]
900    fn ed25519_verify_errs() {
901        let api = MockApi::default();
902
903        let msg = hex::decode(ED25519_MSG_HEX).unwrap();
904        let signature = hex::decode(ED25519_SIG_HEX).unwrap();
905        let public_key = vec![];
906
907        let res = api.ed25519_verify(&msg, &signature, &public_key);
908        assert_eq!(res.unwrap_err(), VerificationError::InvalidPubkeyFormat);
909    }
910
911    // Basic "works" test.
912    #[test]
913    fn ed25519_batch_verify_works() {
914        let api = MockApi::default();
915
916        let msg = hex::decode(ED25519_MSG_HEX).unwrap();
917        let signature = hex::decode(ED25519_SIG_HEX).unwrap();
918        let public_key = hex::decode(ED25519_PUBKEY_HEX).unwrap();
919
920        let msgs: Vec<&[u8]> = vec![&msg];
921        let signatures: Vec<&[u8]> = vec![&signature];
922        let public_keys: Vec<&[u8]> = vec![&public_key];
923
924        assert!(api
925            .ed25519_batch_verify(&msgs, &signatures, &public_keys)
926            .unwrap());
927    }
928
929    // Basic "fails" test.
930    #[test]
931    fn ed25519_batch_verify_fails() {
932        let api = MockApi::default();
933
934        let mut msg = hex::decode(ED25519_MSG_HEX).unwrap();
935        // alter msg
936        msg[0] ^= 0x01;
937        let signature = hex::decode(ED25519_SIG_HEX).unwrap();
938        let public_key = hex::decode(ED25519_PUBKEY_HEX).unwrap();
939
940        let msgs: Vec<&[u8]> = vec![&msg];
941        let signatures: Vec<&[u8]> = vec![&signature];
942        let public_keys: Vec<&[u8]> = vec![&public_key];
943
944        assert!(!api
945            .ed25519_batch_verify(&msgs, &signatures, &public_keys)
946            .unwrap());
947    }
948
949    // Basic "errors" test.
950    #[test]
951    fn ed25519_batch_verify_errs() {
952        let api = MockApi::default();
953
954        let msg = hex::decode(ED25519_MSG_HEX).unwrap();
955        let signature = hex::decode(ED25519_SIG_HEX).unwrap();
956        let public_key: Vec<u8> = vec![0u8; 0];
957
958        let msgs: Vec<&[u8]> = vec![msg.as_slice()];
959        let signatures: Vec<&[u8]> = vec![signature.as_slice()];
960        let public_keys: Vec<&[u8]> = vec![&public_key];
961
962        let res = api.ed25519_batch_verify(&msgs, &signatures, &public_keys);
963        assert_eq!(res.unwrap_err(), VerificationError::InvalidPubkeyFormat);
964    }
965
966    #[test]
967    fn bank_querier_all_balances() {
968        let addr = String::from("foobar");
969        let balance = vec![coin(123, "ELF"), coin(777, "FLY")];
970        let bank = BankQuerier::new(&[(&addr, &balance)]);
971
972        let all = bank
973            .query(&BankQuery::AllBalances { address: addr })
974            .unwrap()
975            .unwrap();
976        let res: AllBalanceResponse = from_binary(&all).unwrap();
977        assert_eq!(&res.amount, &balance);
978    }
979
980    #[test]
981    fn bank_querier_one_balance() {
982        let addr = String::from("foobar");
983        let balance = vec![coin(123, "ELF"), coin(777, "FLY")];
984        let bank = BankQuerier::new(&[(&addr, &balance)]);
985
986        // one match
987        let fly = bank
988            .query(&BankQuery::Balance {
989                address: addr.clone(),
990                denom: "FLY".to_string(),
991            })
992            .unwrap()
993            .unwrap();
994        let res: BalanceResponse = from_binary(&fly).unwrap();
995        assert_eq!(res.amount, coin(777, "FLY"));
996
997        // missing denom
998        let miss = bank
999            .query(&BankQuery::Balance {
1000                address: addr,
1001                denom: "MISS".to_string(),
1002            })
1003            .unwrap()
1004            .unwrap();
1005        let res: BalanceResponse = from_binary(&miss).unwrap();
1006        assert_eq!(res.amount, coin(0, "MISS"));
1007    }
1008
1009    #[test]
1010    fn bank_querier_missing_account() {
1011        let addr = String::from("foobar");
1012        let balance = vec![coin(123, "ELF"), coin(777, "FLY")];
1013        let bank = BankQuerier::new(&[(&addr, &balance)]);
1014
1015        // all balances on empty account is empty vec
1016        let all = bank
1017            .query(&BankQuery::AllBalances {
1018                address: String::from("elsewhere"),
1019            })
1020            .unwrap()
1021            .unwrap();
1022        let res: AllBalanceResponse = from_binary(&all).unwrap();
1023        assert_eq!(res.amount, vec![]);
1024
1025        // any denom on balances on empty account is empty coin
1026        let miss = bank
1027            .query(&BankQuery::Balance {
1028                address: String::from("elsewhere"),
1029                denom: "ELF".to_string(),
1030            })
1031            .unwrap()
1032            .unwrap();
1033        let res: BalanceResponse = from_binary(&miss).unwrap();
1034        assert_eq!(res.amount, coin(0, "ELF"));
1035    }
1036
1037    #[cfg(feature = "staking")]
1038    #[test]
1039    fn staking_querier_all_validators() {
1040        let val1 = Validator {
1041            address: String::from("validator-one"),
1042            commission: Decimal::percent(1),
1043            max_commission: Decimal::percent(3),
1044            max_change_rate: Decimal::percent(1),
1045        };
1046        let val2 = Validator {
1047            address: String::from("validator-two"),
1048            commission: Decimal::permille(15),
1049            max_commission: Decimal::permille(40),
1050            max_change_rate: Decimal::permille(5),
1051        };
1052
1053        let staking = StakingQuerier::new("ustake", &[val1.clone(), val2.clone()], &[]);
1054
1055        // one match
1056        let raw = staking
1057            .query(&StakingQuery::AllValidators {})
1058            .unwrap()
1059            .unwrap();
1060        let vals: AllValidatorsResponse = from_binary(&raw).unwrap();
1061        assert_eq!(vals.validators, vec![val1, val2]);
1062    }
1063
1064    #[cfg(feature = "staking")]
1065    #[test]
1066    fn staking_querier_validator() {
1067        let address1 = String::from("validator-one");
1068        let address2 = String::from("validator-two");
1069        let address_non_existent = String::from("wannabe-validator");
1070
1071        let val1 = Validator {
1072            address: address1.clone(),
1073            commission: Decimal::percent(1),
1074            max_commission: Decimal::percent(3),
1075            max_change_rate: Decimal::percent(1),
1076        };
1077        let val2 = Validator {
1078            address: address2.clone(),
1079            commission: Decimal::permille(15),
1080            max_commission: Decimal::permille(40),
1081            max_change_rate: Decimal::permille(5),
1082        };
1083
1084        let staking = StakingQuerier::new("ustake", &[val1.clone(), val2.clone()], &[]);
1085
1086        // query 1
1087        let raw = staking
1088            .query(&StakingQuery::Validator { address: address1 })
1089            .unwrap()
1090            .unwrap();
1091        let res: ValidatorResponse = from_binary(&raw).unwrap();
1092        assert_eq!(res.validator, Some(val1));
1093
1094        // query 2
1095        let raw = staking
1096            .query(&StakingQuery::Validator { address: address2 })
1097            .unwrap()
1098            .unwrap();
1099        let res: ValidatorResponse = from_binary(&raw).unwrap();
1100        assert_eq!(res.validator, Some(val2));
1101
1102        // query non-existent
1103        let raw = staking
1104            .query(&StakingQuery::Validator {
1105                address: address_non_existent,
1106            })
1107            .unwrap()
1108            .unwrap();
1109        let res: ValidatorResponse = from_binary(&raw).unwrap();
1110        assert_eq!(res.validator, None);
1111    }
1112
1113    #[cfg(feature = "staking")]
1114    // gets delegators from query or panic
1115    fn get_all_delegators(
1116        staking: &StakingQuerier,
1117        delegator: impl Into<String>,
1118    ) -> Vec<Delegation> {
1119        let raw = staking
1120            .query(&StakingQuery::AllDelegations {
1121                delegator: delegator.into(),
1122            })
1123            .unwrap()
1124            .unwrap();
1125        let dels: AllDelegationsResponse = from_binary(&raw).unwrap();
1126        dels.delegations
1127    }
1128
1129    #[cfg(feature = "staking")]
1130    // gets full delegators from query or panic
1131    fn get_delegator(
1132        staking: &StakingQuerier,
1133        delegator: impl Into<String>,
1134        validator: impl Into<String>,
1135    ) -> Option<FullDelegation> {
1136        let raw = staking
1137            .query(&StakingQuery::Delegation {
1138                delegator: delegator.into(),
1139                validator: validator.into(),
1140            })
1141            .unwrap()
1142            .unwrap();
1143        let dels: DelegationResponse = from_binary(&raw).unwrap();
1144        dels.delegation
1145    }
1146
1147    #[cfg(feature = "staking")]
1148    #[test]
1149    fn staking_querier_delegations() {
1150        let val1 = String::from("validator-one");
1151        let val2 = String::from("validator-two");
1152
1153        let user_a = Addr::unchecked("investor");
1154        let user_b = Addr::unchecked("speculator");
1155        let user_c = Addr::unchecked("hodler");
1156
1157        // we need multiple validators per delegator, so the queries provide different results
1158        let del1a = FullDelegation {
1159            delegator: user_a.clone(),
1160            validator: val1.clone(),
1161            amount: coin(100, "ustake"),
1162            can_redelegate: coin(100, "ustake"),
1163            accumulated_rewards: coins(5, "ustake"),
1164        };
1165        let del2a = FullDelegation {
1166            delegator: user_a.clone(),
1167            validator: val2.clone(),
1168            amount: coin(500, "ustake"),
1169            can_redelegate: coin(500, "ustake"),
1170            accumulated_rewards: coins(20, "ustake"),
1171        };
1172
1173        // note we cannot have multiple delegations on one validator, they are collapsed into one
1174        let del1b = FullDelegation {
1175            delegator: user_b.clone(),
1176            validator: val1.clone(),
1177            amount: coin(500, "ustake"),
1178            can_redelegate: coin(0, "ustake"),
1179            accumulated_rewards: coins(0, "ustake"),
1180        };
1181
1182        // and another one on val2
1183        let del2c = FullDelegation {
1184            delegator: user_c.clone(),
1185            validator: val2.clone(),
1186            amount: coin(8888, "ustake"),
1187            can_redelegate: coin(4567, "ustake"),
1188            accumulated_rewards: coins(900, "ustake"),
1189        };
1190
1191        let staking = StakingQuerier::new(
1192            "ustake",
1193            &[],
1194            &[del1a.clone(), del1b.clone(), del2a.clone(), del2c.clone()],
1195        );
1196
1197        // get all for user a
1198        let dels = get_all_delegators(&staking, user_a.clone());
1199        assert_eq!(dels, vec![del1a.clone().into(), del2a.clone().into()]);
1200
1201        // get all for user b
1202        let dels = get_all_delegators(&staking, user_b.clone());
1203        assert_eq!(dels, vec![del1b.clone().into()]);
1204
1205        // get all for user c
1206        let dels = get_all_delegators(&staking, user_c.clone());
1207        assert_eq!(dels, vec![del2c.clone().into()]);
1208
1209        // for user with no delegations...
1210        let dels = get_all_delegators(&staking, String::from("no one"));
1211        assert_eq!(dels, vec![]);
1212
1213        // filter a by validator (1 and 1)
1214        let dels = get_delegator(&staking, user_a.clone(), val1.clone());
1215        assert_eq!(dels, Some(del1a));
1216        let dels = get_delegator(&staking, user_a, val2.clone());
1217        assert_eq!(dels, Some(del2a));
1218
1219        // filter b by validator (2 and 0)
1220        let dels = get_delegator(&staking, user_b.clone(), val1.clone());
1221        assert_eq!(dels, Some(del1b));
1222        let dels = get_delegator(&staking, user_b, val2.clone());
1223        assert_eq!(dels, None);
1224
1225        // filter c by validator (0 and 1)
1226        let dels = get_delegator(&staking, user_c.clone(), val1);
1227        assert_eq!(dels, None);
1228        let dels = get_delegator(&staking, user_c, val2);
1229        assert_eq!(dels, Some(del2c));
1230    }
1231
1232    #[test]
1233    fn riffle_shuffle_works() {
1234        // Example from https://en.wikipedia.org/wiki/In_shuffle
1235        let start = [0xA, 0x2, 0x3, 0x4, 0x5, 0x6];
1236        let round1 = riffle_shuffle(&start);
1237        assert_eq!(round1, [0x4, 0xA, 0x5, 0x2, 0x6, 0x3]);
1238        let round2 = riffle_shuffle(&round1);
1239        assert_eq!(round2, [0x2, 0x4, 0x6, 0xA, 0x3, 0x5]);
1240        let round3 = riffle_shuffle(&round2);
1241        assert_eq!(round3, start);
1242
1243        // For 14 elements, the original order is restored after 4 executions
1244        // See https://en.wikipedia.org/wiki/In_shuffle#Mathematics and https://oeis.org/A002326
1245        let original = [12, 33, 76, 576, 0, 44, 1, 14, 78, 99, 871212, -7, 2, -1];
1246        let mut result = Vec::from(original);
1247        for _ in 0..4 {
1248            result = riffle_shuffle(&result);
1249        }
1250        assert_eq!(result, original);
1251
1252        // For 24 elements, the original order is restored after 20 executions
1253        let original = [
1254            7, 4, 2, 4656, 23, 45, 23, 1, 12, 76, 576, 0, 12, 1, 14, 78, 99, 12, 1212, 444, 31,
1255            111, 424, 34,
1256        ];
1257        let mut result = Vec::from(original);
1258        for _ in 0..20 {
1259            result = riffle_shuffle(&result);
1260        }
1261        assert_eq!(result, original);
1262    }
1263
1264    #[test]
1265    fn digit_sum_works() {
1266        assert_eq!(digit_sum(&[]), 0);
1267        assert_eq!(digit_sum(&[0]), 0);
1268        assert_eq!(digit_sum(&[0, 0]), 0);
1269        assert_eq!(digit_sum(&[0, 0, 0]), 0);
1270
1271        assert_eq!(digit_sum(&[1, 0, 0]), 1);
1272        assert_eq!(digit_sum(&[0, 1, 0]), 1);
1273        assert_eq!(digit_sum(&[0, 0, 1]), 1);
1274
1275        assert_eq!(digit_sum(&[1, 2, 3]), 6);
1276
1277        assert_eq!(digit_sum(&[255, 1]), 256);
1278    }
1279}