namada_core/
masp.rs

1//! MASP types
2
3use std::collections::BTreeMap;
4use std::fmt::Display;
5use std::num::ParseIntError;
6use std::str::FromStr;
7
8use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
9use masp_primitives::asset_type::AssetType;
10use masp_primitives::sapling::ViewingKey;
11use masp_primitives::transaction::TransparentAddress;
12pub use masp_primitives::transaction::{
13    Transaction as MaspTransaction, TxId as TxIdInner,
14};
15use masp_primitives::zip32::{ExtendedKey, PseudoExtendedKey};
16use namada_macros::BorshDeserializer;
17#[cfg(feature = "migrations")]
18use namada_migrations::*;
19use ripemd::Digest as RipemdDigest;
20use serde::{Deserialize, Deserializer, Serialize, Serializer};
21use sha2::Sha256;
22
23use crate::address::{Address, DecodeError, HASH_HEX_LEN, IBC, MASP};
24use crate::borsh::BorshSerializeExt;
25use crate::chain::Epoch;
26use crate::impl_display_and_from_str_via_format;
27use crate::string_encoding::{
28    self, MASP_EXT_FULL_VIEWING_KEY_HRP, MASP_EXT_SPENDING_KEY_HRP,
29    MASP_PAYMENT_ADDRESS_HRP,
30};
31use crate::token::{Denomination, MaspDigitPos};
32
33/// Serialize the given TxId
34pub fn serialize_txid<S>(txid: &TxIdInner, s: S) -> Result<S::Ok, S::Error>
35where
36    S: Serializer,
37{
38    s.serialize_bytes(txid.as_ref())
39}
40
41/// Deserialize the given TxId
42pub fn deserialize_txid<'de, D>(deserializer: D) -> Result<TxIdInner, D::Error>
43where
44    D: Deserializer<'de>,
45{
46    Ok(TxIdInner::from_bytes(Deserialize::deserialize(
47        deserializer,
48    )?))
49}
50
51/// Wrapper for masp_primitive's TxId
52#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
53#[derive(
54    Serialize,
55    Deserialize,
56    Clone,
57    BorshSerialize,
58    BorshDeserialize,
59    BorshSchema,
60    Debug,
61    Eq,
62    PartialEq,
63    Copy,
64    Ord,
65    PartialOrd,
66    Hash,
67)]
68pub struct MaspTxId(
69    #[serde(
70        serialize_with = "serialize_txid",
71        deserialize_with = "deserialize_txid"
72    )]
73    pub TxIdInner,
74);
75
76impl From<TxIdInner> for MaspTxId {
77    fn from(txid: TxIdInner) -> Self {
78        Self(txid)
79    }
80}
81
82impl Display for MaspTxId {
83    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84        write!(f, "{}", self.0)
85    }
86}
87
88/// Wrapper type around `Epoch` for type safe operations involving the masp
89/// epoch
90#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
91#[derive(
92    BorshSerialize,
93    BorshDeserialize,
94    BorshDeserializer,
95    BorshSchema,
96    Clone,
97    Copy,
98    Debug,
99    PartialOrd,
100    Ord,
101    PartialEq,
102    Eq,
103    Hash,
104    Serialize,
105    Deserialize,
106)]
107pub struct MaspEpoch(Epoch);
108
109impl Display for MaspEpoch {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        write!(f, "{}", self.0)
112    }
113}
114
115impl FromStr for MaspEpoch {
116    type Err = ParseIntError;
117
118    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
119        let inner: Epoch = Epoch::from_str(s)?;
120        Ok(Self(inner))
121    }
122}
123
124impl MaspEpoch {
125    /// Converts and `Epoch` into a `MaspEpoch` based on the provided conversion
126    /// rate
127    pub fn try_from_epoch(
128        epoch: Epoch,
129        masp_epoch_multiplier: u64,
130    ) -> Result<Self, &'static str> {
131        Ok(Self(
132            epoch
133                .checked_div(masp_epoch_multiplier)
134                .ok_or("Masp epoch multiplier cannot be 0")?,
135        ))
136    }
137
138    /// Returns a 0 masp epoch
139    pub const fn zero() -> Self {
140        Self(Epoch(0))
141    }
142
143    /// Change to the previous masp epoch.
144    pub fn prev(&self) -> Option<Self> {
145        Some(Self(self.0.checked_sub(1)?))
146    }
147
148    /// Change to the next masp epoch.
149    pub fn next(&self) -> Option<Self> {
150        Some(Self(self.0.checked_add(1)?))
151    }
152
153    /// Initialize a new masp epoch from the provided one
154    #[cfg(any(test, feature = "testing"))]
155    pub const fn new(epoch: u64) -> Self {
156        Self(Epoch(epoch))
157    }
158}
159
160/// The plain representation of a MASP aaset
161#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
162#[derive(
163    BorshSerialize,
164    BorshDeserialize,
165    BorshDeserializer,
166    BorshSchema,
167    Clone,
168    Debug,
169    PartialOrd,
170    Ord,
171    PartialEq,
172    Eq,
173    Hash,
174    Serialize,
175    Deserialize,
176)]
177pub struct AssetData {
178    /// The token associated with this asset type
179    pub token: Address,
180    /// The denomination associated with the above toke
181    pub denom: Denomination,
182    /// The digit position covered by this asset type
183    pub position: MaspDigitPos,
184    /// The epoch of the asset type, if any
185    pub epoch: Option<MaspEpoch>,
186}
187
188impl AssetData {
189    /// Make asset type corresponding to given address and epoch
190    pub fn encode(&self) -> Result<AssetType, std::io::Error> {
191        // Timestamp the chosen token with the current epoch
192        let token_bytes = self.serialize_to_vec();
193        // Generate the unique asset identifier from the unique token address
194        AssetType::new(token_bytes.as_ref()).map_err(|_| {
195            std::io::Error::new(
196                std::io::ErrorKind::Other,
197                "unable to create asset type".to_string(),
198            )
199        })
200    }
201
202    /// Give this pre-asset type the given epoch if already has an epoch. Return
203    /// the replaced value.
204    pub fn redate(self, to: MaspEpoch) -> Self {
205        if self.epoch.is_some() {
206            Self {
207                epoch: Some(to),
208                ..self
209            }
210        } else {
211            self
212        }
213    }
214
215    /// Update the MaspEpoch to the next one
216    pub fn redate_to_next_epoch(self) -> Self {
217        if let Some(next) = self.epoch.as_ref().and_then(MaspEpoch::next) {
218            Self {
219                epoch: Some(next),
220                ..self
221            }
222        } else {
223            self
224        }
225    }
226
227    /// Remove the epoch associated with this pre-asset type
228    pub fn undate(self) -> Self {
229        Self {
230            epoch: None,
231            ..self
232        }
233    }
234}
235
236/// Make asset type corresponding to given address and epoch
237pub fn encode_asset_type(
238    token: Address,
239    denom: Denomination,
240    position: MaspDigitPos,
241    epoch: Option<MaspEpoch>,
242) -> Result<AssetType, std::io::Error> {
243    AssetData {
244        token,
245        denom,
246        position,
247        epoch,
248    }
249    .encode()
250}
251
252/// Encode the assets that are used for masp rewards
253pub fn encode_reward_asset_types(
254    native_token: &Address,
255) -> Result<[AssetType; 4], std::io::Error> {
256    use crate::token::{MaspDigitPos, NATIVE_MAX_DECIMAL_PLACES};
257    Ok([
258        encode_asset_type(
259            native_token.clone(),
260            NATIVE_MAX_DECIMAL_PLACES.into(),
261            MaspDigitPos::Zero,
262            Some(MaspEpoch::zero()),
263        )?,
264        encode_asset_type(
265            native_token.clone(),
266            NATIVE_MAX_DECIMAL_PLACES.into(),
267            MaspDigitPos::One,
268            Some(MaspEpoch::zero()),
269        )?,
270        encode_asset_type(
271            native_token.clone(),
272            NATIVE_MAX_DECIMAL_PLACES.into(),
273            MaspDigitPos::Two,
274            Some(MaspEpoch::zero()),
275        )?,
276        encode_asset_type(
277            native_token.clone(),
278            NATIVE_MAX_DECIMAL_PLACES.into(),
279            MaspDigitPos::Three,
280            Some(MaspEpoch::zero()),
281        )?,
282    ])
283}
284
285/// MASP token map
286pub type TokenMap = BTreeMap<String, Address>;
287
288// enough capacity to store the payment address
289const PAYMENT_ADDRESS_SIZE: usize = 43;
290
291/// Wrapper for masp_primitive's FullViewingKey
292#[derive(
293    Clone,
294    Debug,
295    Copy,
296    Hash,
297    BorshSerialize,
298    BorshDeserialize,
299    BorshDeserializer,
300    Eq,
301    PartialEq,
302    PartialOrd,
303    Ord,
304)]
305pub struct ExtendedViewingKey(masp_primitives::zip32::ExtendedFullViewingKey);
306
307impl ExtendedViewingKey {
308    /// Encode `Self` to bytes
309    pub fn to_bytes(&self) -> Vec<u8> {
310        let mut bytes = [0; 169];
311        self.0
312            .write(&mut bytes[..])
313            .expect("should be able to serialize an ExtendedFullViewingKey");
314        bytes.to_vec()
315    }
316
317    /// Try to decode `Self` from bytes
318    pub fn decode_bytes(bytes: &[u8]) -> Result<Self, std::io::Error> {
319        masp_primitives::zip32::ExtendedFullViewingKey::read(&mut &bytes[..])
320            .map(Self)
321    }
322
323    /// Get the underlying viewing key
324    pub fn as_viewing_key(&self) -> ViewingKey {
325        self.0.fvk.vk
326    }
327}
328
329impl string_encoding::Format for ExtendedViewingKey {
330    type EncodedBytes<'a> = Vec<u8>;
331
332    const HRP: string_encoding::Hrp =
333        string_encoding::Hrp::parse_unchecked(MASP_EXT_FULL_VIEWING_KEY_HRP);
334
335    fn to_bytes(&self) -> Vec<u8> {
336        self.to_bytes()
337    }
338
339    fn decode_bytes(
340        bytes: &[u8],
341    ) -> Result<Self, string_encoding::DecodeError> {
342        Self::decode_bytes(bytes).map_err(DecodeError::InvalidBytes)
343    }
344}
345
346impl_display_and_from_str_via_format!(ExtendedViewingKey);
347
348impl string_encoding::Format for PaymentAddress {
349    type EncodedBytes<'a> = Vec<u8>;
350
351    const HRP: string_encoding::Hrp =
352        string_encoding::Hrp::parse_unchecked(MASP_PAYMENT_ADDRESS_HRP);
353
354    fn to_bytes(&self) -> Vec<u8> {
355        let mut bytes = Vec::with_capacity(PAYMENT_ADDRESS_SIZE);
356        bytes.extend_from_slice(self.0.to_bytes().as_slice());
357        bytes
358    }
359
360    fn decode_bytes(
361        bytes: &[u8],
362    ) -> Result<Self, string_encoding::DecodeError> {
363        if bytes.len() != PAYMENT_ADDRESS_SIZE {
364            return Err(DecodeError::InvalidInnerEncoding(format!(
365                "expected {PAYMENT_ADDRESS_SIZE} bytes for the payment address"
366            )));
367        }
368        let payment_addr =
369            masp_primitives::sapling::PaymentAddress::from_bytes(&{
370                let mut payment_addr = [0u8; PAYMENT_ADDRESS_SIZE];
371                payment_addr.copy_from_slice(&bytes[0..]);
372                payment_addr
373            })
374            .ok_or_else(|| {
375                DecodeError::InvalidInnerEncoding(
376                    "invalid payment address provided".to_string(),
377                )
378            })?;
379        Ok(Self(payment_addr))
380    }
381}
382
383impl_display_and_from_str_via_format!(PaymentAddress);
384
385impl From<ExtendedViewingKey>
386    for masp_primitives::zip32::ExtendedFullViewingKey
387{
388    fn from(key: ExtendedViewingKey) -> Self {
389        key.0
390    }
391}
392
393impl From<masp_primitives::zip32::ExtendedFullViewingKey>
394    for ExtendedViewingKey
395{
396    fn from(key: masp_primitives::zip32::ExtendedFullViewingKey) -> Self {
397        Self(key)
398    }
399}
400
401impl From<ExtendedViewingKey> for masp_primitives::sapling::ViewingKey {
402    fn from(value: ExtendedViewingKey) -> Self {
403        let fvk = masp_primitives::zip32::ExtendedFullViewingKey::from(value);
404        fvk.fvk.vk
405    }
406}
407
408impl serde::Serialize for ExtendedViewingKey {
409    fn serialize<S>(
410        &self,
411        serializer: S,
412    ) -> std::result::Result<S::Ok, S::Error>
413    where
414        S: serde::Serializer,
415    {
416        let encoded = self.to_string();
417        serde::Serialize::serialize(&encoded, serializer)
418    }
419}
420
421impl<'de> serde::Deserialize<'de> for ExtendedViewingKey {
422    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
423    where
424        D: serde::Deserializer<'de>,
425    {
426        use serde::de::Error;
427        let encoded: String = serde::Deserialize::deserialize(deserializer)?;
428        Self::from_str(&encoded).map_err(D::Error::custom)
429    }
430}
431
432/// Wrapper for masp_primitive's PaymentAddress
433#[derive(
434    Clone,
435    Debug,
436    Copy,
437    PartialOrd,
438    Ord,
439    Eq,
440    PartialEq,
441    Hash,
442    BorshSerialize,
443    BorshDeserialize,
444    BorshDeserializer,
445)]
446pub struct PaymentAddress(masp_primitives::sapling::PaymentAddress);
447
448impl PaymentAddress {
449    /// Hash this payment address
450    pub fn hash(&self) -> String {
451        let bytes = self.0.serialize_to_vec();
452        let mut hasher = Sha256::new();
453        hasher.update(bytes);
454        // hex of the first 40 chars of the hash
455        format!("{:.width$X}", hasher.finalize(), width = HASH_HEX_LEN)
456    }
457}
458
459impl From<PaymentAddress> for masp_primitives::sapling::PaymentAddress {
460    fn from(addr: PaymentAddress) -> Self {
461        addr.0
462    }
463}
464
465impl From<masp_primitives::sapling::PaymentAddress> for PaymentAddress {
466    fn from(addr: masp_primitives::sapling::PaymentAddress) -> Self {
467        Self(addr)
468    }
469}
470
471impl serde::Serialize for PaymentAddress {
472    fn serialize<S>(
473        &self,
474        serializer: S,
475    ) -> std::result::Result<S::Ok, S::Error>
476    where
477        S: serde::Serializer,
478    {
479        let encoded = self.to_string();
480        serde::Serialize::serialize(&encoded, serializer)
481    }
482}
483
484impl<'de> serde::Deserialize<'de> for PaymentAddress {
485    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
486    where
487        D: serde::Deserializer<'de>,
488    {
489        use serde::de::Error;
490        let encoded: String = serde::Deserialize::deserialize(deserializer)?;
491        Self::from_str(&encoded).map_err(D::Error::custom)
492    }
493}
494
495/// Wrapper for masp_primitive's ExtendedSpendingKey
496#[derive(
497    Clone,
498    Debug,
499    Copy,
500    BorshSerialize,
501    BorshDeserialize,
502    BorshDeserializer,
503    Hash,
504    Eq,
505    PartialEq,
506)]
507pub struct ExtendedSpendingKey(masp_primitives::zip32::ExtendedSpendingKey);
508
509impl string_encoding::Format for ExtendedSpendingKey {
510    type EncodedBytes<'a> = Vec<u8>;
511
512    const HRP: string_encoding::Hrp =
513        string_encoding::Hrp::parse_unchecked(MASP_EXT_SPENDING_KEY_HRP);
514
515    fn to_bytes(&self) -> Vec<u8> {
516        let mut bytes = [0; 169];
517        self.0
518            .write(&mut &mut bytes[..])
519            .expect("should be able to serialize an ExtendedSpendingKey");
520        bytes.to_vec()
521    }
522
523    fn decode_bytes(
524        bytes: &[u8],
525    ) -> Result<Self, string_encoding::DecodeError> {
526        masp_primitives::zip32::ExtendedSpendingKey::read(&mut &bytes[..])
527            .map_err(|op| DecodeError::InvalidInnerEncoding(op.to_string()))
528            .map(Self)
529    }
530}
531
532impl_display_and_from_str_via_format!(ExtendedSpendingKey);
533
534impl ExtendedSpendingKey {
535    /// Derive a viewing key
536    pub fn to_viewing_key(&self) -> ExtendedViewingKey {
537        ExtendedViewingKey::from(
538            #[allow(deprecated)]
539            {
540                self.0.to_extended_full_viewing_key()
541            },
542        )
543    }
544}
545
546impl From<ExtendedSpendingKey> for masp_primitives::zip32::ExtendedSpendingKey {
547    fn from(key: ExtendedSpendingKey) -> Self {
548        key.0
549    }
550}
551
552impl From<masp_primitives::zip32::ExtendedSpendingKey> for ExtendedSpendingKey {
553    fn from(key: masp_primitives::zip32::ExtendedSpendingKey) -> Self {
554        Self(key)
555    }
556}
557
558impl serde::Serialize for ExtendedSpendingKey {
559    fn serialize<S>(
560        &self,
561        serializer: S,
562    ) -> std::result::Result<S::Ok, S::Error>
563    where
564        S: serde::Serializer,
565    {
566        let encoded = self.to_string();
567        serde::Serialize::serialize(&encoded, serializer)
568    }
569}
570
571impl<'de> serde::Deserialize<'de> for ExtendedSpendingKey {
572    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
573    where
574        D: serde::Deserializer<'de>,
575    {
576        use serde::de::Error;
577        let encoded: String = serde::Deserialize::deserialize(deserializer)?;
578        Self::from_str(&encoded).map_err(D::Error::custom)
579    }
580}
581
582/// Represents a source of funds for a transfer
583#[allow(clippy::large_enum_variant)]
584#[derive(Debug, Clone, Hash, Eq, PartialEq)]
585pub enum TransferSource {
586    /// A transfer coming from a transparent address
587    Address(Address),
588    /// A transfer coming from a shielded address
589    ExtendedKey(PseudoExtendedKey),
590}
591
592impl TransferSource {
593    /// Get the transparent address that this source would effectively draw from
594    pub fn effective_address(&self) -> Address {
595        match self {
596            Self::Address(x) => x.clone(),
597            // An ExtendedSpendingKey for a source effectively means that
598            // assets will be drawn from the MASP
599            Self::ExtendedKey(_) => MASP,
600        }
601    }
602
603    /// Get the contained extended key, if any
604    pub fn spending_key(&self) -> Option<PseudoExtendedKey> {
605        match self {
606            Self::ExtendedKey(x) => Some(*x),
607            _ => None,
608        }
609    }
610
611    /// Get the contained extended key, if any
612    pub fn spending_key_mut(&mut self) -> Option<&mut PseudoExtendedKey> {
613        match self {
614            Self::ExtendedKey(x) => Some(x),
615            _ => None,
616        }
617    }
618
619    /// Get the contained transparent address, if any
620    pub fn address(&self) -> Option<Address> {
621        match self {
622            Self::Address(x) => Some(x.clone()),
623            _ => None,
624        }
625    }
626
627    /// Get the contained transparent address data, if any
628    pub fn t_addr_data(&self) -> Option<TAddrData> {
629        match self {
630            Self::Address(x) => Some(TAddrData::Addr(x.clone())),
631            _ => None,
632        }
633    }
634}
635
636impl Display for TransferSource {
637    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
638        match self {
639            Self::Address(x) => x.fmt(f),
640            Self::ExtendedKey(x) => {
641                ExtendedViewingKey::from(x.to_viewing_key()).fmt(f)
642            }
643        }
644    }
645}
646
647/// Represents the pre-image to a TransparentAddress
648#[derive(
649    Debug,
650    Clone,
651    PartialEq,
652    Eq,
653    BorshDeserialize,
654    BorshSerialize,
655    BorshDeserializer,
656)]
657pub enum TAddrData {
658    /// A transparent address within Namada
659    Addr(Address),
660    /// An IBC address
661    Ibc(String),
662}
663
664impl TAddrData {
665    /// Get the transparent address that this target would effectively go to
666    pub fn effective_address(&self) -> Address {
667        match self {
668            Self::Addr(x) => x.clone(),
669            // An IBC signer address effectively means that assets are
670            // associated with the IBC internal address
671            Self::Ibc(_) => IBC,
672        }
673    }
674
675    /// Get the contained IBC receiver, if any
676    pub fn ibc_receiver_address(&self) -> Option<String> {
677        match self {
678            Self::Ibc(address) => Some(address.clone()),
679            _ => None,
680        }
681    }
682
683    /// Get the contained Address, if any
684    pub fn address(&self) -> Option<Address> {
685        match self {
686            Self::Addr(x) => Some(x.clone()),
687            _ => None,
688        }
689    }
690
691    /// Convert transparent address data into a transparent address
692    pub fn taddress(&self) -> TransparentAddress {
693        TransparentAddress(<[u8; 20]>::from(ripemd::Ripemd160::digest(
694            sha2::Sha256::digest(self.serialize_to_vec()),
695        )))
696    }
697}
698
699/// Convert a receiver string to a TransparentAddress
700pub fn ibc_taddr(receiver: String) -> TransparentAddress {
701    TAddrData::Ibc(receiver).taddress()
702}
703
704/// Convert a Namada Address to a TransparentAddress
705pub fn addr_taddr(addr: Address) -> TransparentAddress {
706    TAddrData::Addr(addr).taddress()
707}
708
709/// Represents a target for the funds of a transfer
710#[derive(
711    Debug,
712    Clone,
713    BorshDeserialize,
714    BorshSerialize,
715    BorshDeserializer,
716    Hash,
717    Eq,
718    PartialEq,
719)]
720pub enum TransferTarget {
721    /// A transfer going to a transparent address
722    Address(Address),
723    /// A transfer going to a shielded address
724    PaymentAddress(PaymentAddress),
725    /// A transfer going to an IBC address
726    Ibc(String),
727}
728
729impl TransferTarget {
730    /// Get the transparent address that this target would effectively go to
731    pub fn effective_address(&self) -> Address {
732        match self {
733            Self::Address(x) => x.clone(),
734            // A PaymentAddress for a target effectively means that assets will
735            // be sent to the MASP
736            Self::PaymentAddress(_) => MASP,
737            // An IBC signer address for a target effectively means that assets
738            // will be sent to the IBC internal address
739            Self::Ibc(_) => IBC,
740        }
741    }
742
743    /// Get the contained PaymentAddress, if any
744    pub fn payment_address(&self) -> Option<PaymentAddress> {
745        match self {
746            Self::PaymentAddress(address) => Some(*address),
747            _ => None,
748        }
749    }
750
751    /// Get the contained Address, if any
752    pub fn address(&self) -> Option<Address> {
753        match self {
754            Self::Address(x) => Some(x.clone()),
755            _ => None,
756        }
757    }
758
759    /// Get the contained TAddrData, if any
760    pub fn t_addr_data(&self) -> Option<TAddrData> {
761        match self {
762            Self::Address(x) => Some(TAddrData::Addr(x.clone())),
763            Self::Ibc(x) => Some(TAddrData::Ibc(x.clone())),
764            _ => None,
765        }
766    }
767}
768
769impl Display for TransferTarget {
770    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
771        match self {
772            Self::Address(x) => x.fmt(f),
773            Self::PaymentAddress(address) => address.fmt(f),
774            Self::Ibc(x) => x.fmt(f),
775        }
776    }
777}
778
779/// Represents the owner of arbitrary funds
780#[allow(clippy::large_enum_variant)]
781#[derive(
782    Debug,
783    Clone,
784    Hash,
785    BorshSerialize,
786    BorshDeserialize,
787    BorshDeserializer,
788    PartialEq,
789    Eq,
790    PartialOrd,
791    Ord,
792)]
793pub enum BalanceOwner {
794    /// A balance stored at a transparent address
795    Address(Address),
796    /// A balance stored at a shielded address
797    FullViewingKey(ExtendedViewingKey),
798}
799
800impl BalanceOwner {
801    /// Get the contained Address, if any
802    pub fn address(&self) -> Option<Address> {
803        match self {
804            Self::Address(x) => Some(x.clone()),
805            _ => None,
806        }
807    }
808
809    /// Get the contained FullViewingKey, if any
810    pub fn full_viewing_key(&self) -> Option<ExtendedViewingKey> {
811        match self {
812            Self::FullViewingKey(x) => Some(*x),
813            _ => None,
814        }
815    }
816}
817
818impl Display for BalanceOwner {
819    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
820        match self {
821            BalanceOwner::Address(addr) => addr.fmt(f),
822            BalanceOwner::FullViewingKey(fvk) => fvk.fmt(f),
823        }
824    }
825}
826
827/// Represents any MASP value
828#[allow(clippy::large_enum_variant)]
829#[derive(Debug, Clone)]
830pub enum MaspValue {
831    /// A MASP PaymentAddress
832    PaymentAddress(PaymentAddress),
833    /// A MASP ExtendedSpendingKey
834    ExtendedSpendingKey(ExtendedSpendingKey),
835    /// A MASP FullViewingKey
836    FullViewingKey(ExtendedViewingKey),
837}
838
839impl FromStr for MaspValue {
840    type Err = DecodeError;
841
842    fn from_str(s: &str) -> Result<Self, Self::Err> {
843        // Try to decode this value first as a PaymentAddress, then as an
844        // ExtendedSpendingKey, then as FullViewingKey
845        PaymentAddress::from_str(s)
846            .map(Self::PaymentAddress)
847            .or_else(|_err| {
848                ExtendedSpendingKey::from_str(s).map(Self::ExtendedSpendingKey)
849            })
850            .or_else(|_err| {
851                ExtendedViewingKey::from_str(s).map(Self::FullViewingKey)
852            })
853    }
854}
855
856#[cfg(test)]
857mod test {
858    use super::*;
859    use crate::address;
860
861    #[test]
862    fn test_extended_spending_key_serialize() {
863        let sk = ExtendedSpendingKey::from(
864            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]),
865        );
866        let serialized = serde_json::to_string(&sk).unwrap();
867        let deserialized: ExtendedSpendingKey =
868            serde_json::from_str(&serialized).unwrap();
869        assert_eq!(sk, deserialized);
870    }
871
872    #[test]
873    fn test_transfer_source_display() {
874        let addr = address::testing::established_address_1();
875        assert_eq!(addr.to_string(), TransferSource::Address(addr).to_string());
876
877        let sk = masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]);
878        assert_eq!(
879            ExtendedViewingKey::from(sk.to_viewing_key()).to_string(),
880            TransferSource::ExtendedKey(sk.into()).to_string()
881        );
882    }
883
884    #[test]
885    fn test_transfer_source_address() {
886        let addr =
887            TransferSource::Address(address::testing::established_address_1())
888                .address();
889        assert_eq!(addr.unwrap(), address::testing::established_address_1());
890
891        let addr = TransferSource::ExtendedKey(
892            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]).into(),
893        )
894        .address();
895        assert!(addr.is_none());
896    }
897
898    #[test]
899    fn test_transfer_source_t_addr_data() {
900        let addr =
901            TransferSource::Address(address::testing::established_address_1())
902                .t_addr_data();
903        assert_eq!(
904            addr.unwrap(),
905            TAddrData::Addr(address::testing::established_address_1())
906        );
907
908        let addr = TransferSource::ExtendedKey(
909            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]).into(),
910        )
911        .address();
912        assert!(addr.is_none());
913    }
914
915    #[test]
916    fn test_transfer_source_effective_address() {
917        let source =
918            TransferSource::Address(address::testing::established_address_1());
919        assert_eq!(
920            source.effective_address(),
921            address::testing::established_address_1()
922        );
923
924        let sk = masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]);
925        let source = TransferSource::ExtendedKey(sk.into());
926        assert_eq!(source.effective_address(), MASP);
927    }
928
929    #[test]
930    fn test_pa_hash() {
931        let sk = ExtendedSpendingKey::from(
932            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]),
933        );
934        let (_diversifier, pa) = sk.0.default_address();
935        let pa = PaymentAddress::from(pa);
936
937        assert_eq!(pa.hash(), "F902054A142024BA72998F7AA6D5F7DB1700E489");
938    }
939
940    #[test]
941    fn test_taddrdata_address() {
942        let addr = TAddrData::Addr(address::testing::established_address_1())
943            .address();
944        assert_eq!(addr.unwrap(), address::testing::established_address_1());
945
946        let addr = TAddrData::Ibc(String::new()).address();
947        assert!(addr.is_none());
948    }
949
950    #[test]
951    fn test_taddrdata_ibc_receiver_address() {
952        let addr = TAddrData::Addr(address::testing::established_address_1())
953            .ibc_receiver_address();
954        assert!(addr.is_none());
955
956        let addr = TAddrData::Ibc("test".to_owned()).ibc_receiver_address();
957        assert_eq!(addr.unwrap(), "test");
958    }
959
960    #[test]
961    fn test_taddrdata_effective_address() {
962        let data = TAddrData::Addr(address::testing::established_address_1());
963        assert_eq!(
964            data.effective_address(),
965            address::testing::established_address_1()
966        );
967
968        let data = TAddrData::Ibc(String::new());
969        assert_eq!(data.effective_address(), IBC);
970    }
971
972    #[test]
973    fn test_transfer_target_effective_address() {
974        let target =
975            TransferTarget::Address(address::testing::established_address_1());
976        assert_eq!(
977            target.effective_address(),
978            address::testing::established_address_1()
979        );
980
981        let sk = ExtendedSpendingKey::from(
982            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]),
983        );
984        let (_diversifier, pa) = sk.0.default_address();
985        let pa = PaymentAddress::from(pa);
986        let target = TransferTarget::PaymentAddress(pa);
987        assert_eq!(target.effective_address(), MASP);
988
989        let target = TransferTarget::Ibc(String::new());
990        assert_eq!(target.effective_address(), IBC);
991    }
992
993    #[test]
994    fn test_transfer_target_address() {
995        let target =
996            TransferTarget::Address(address::testing::established_address_1())
997                .address();
998        assert_eq!(target.unwrap(), address::testing::established_address_1());
999
1000        let sk = ExtendedSpendingKey::from(
1001            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]),
1002        );
1003        let (_diversifier, pa) = sk.0.default_address();
1004        let pa = PaymentAddress::from(pa);
1005        let target = TransferTarget::PaymentAddress(pa).address();
1006        assert!(target.is_none());
1007
1008        let target = TransferTarget::Ibc(String::new()).address();
1009        assert!(target.is_none());
1010    }
1011
1012    #[test]
1013    fn test_transfer_target_t_addr_data() {
1014        let target =
1015            TransferTarget::Address(address::testing::established_address_1())
1016                .t_addr_data();
1017        assert_eq!(
1018            target.unwrap(),
1019            TAddrData::Addr(address::testing::established_address_1())
1020        );
1021
1022        let sk = ExtendedSpendingKey::from(
1023            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]),
1024        );
1025        let (_diversifier, pa) = sk.0.default_address();
1026        let pa = PaymentAddress::from(pa);
1027        let target = TransferTarget::PaymentAddress(pa).t_addr_data();
1028        assert!(target.is_none());
1029
1030        let target = TransferTarget::Ibc(String::new()).t_addr_data();
1031        assert_eq!(target.unwrap(), TAddrData::Ibc(String::new()));
1032    }
1033
1034    #[test]
1035    fn test_transfer_target_display() {
1036        let addr = address::testing::established_address_1();
1037
1038        let sk = ExtendedSpendingKey::from(
1039            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]),
1040        );
1041        let (_diversifier, pa) = sk.0.default_address();
1042        let pa = PaymentAddress::from(pa);
1043
1044        const IBC_ADDR: &str = "noble18st0wqx84av8y6xdlss9d6m2nepyqwj6nfxxuv";
1045
1046        assert_eq!(addr.to_string(), TransferTarget::Address(addr).to_string());
1047
1048        assert_eq!(
1049            pa.to_string(),
1050            TransferTarget::PaymentAddress(pa).to_string()
1051        );
1052
1053        assert_eq!(
1054            IBC_ADDR.to_owned(),
1055            TransferTarget::Ibc(IBC_ADDR.to_owned()).to_string()
1056        );
1057    }
1058
1059    #[test]
1060    fn test_balance_owner_full_viewing_key() {
1061        let sk = ExtendedSpendingKey::from(
1062            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]),
1063        );
1064        let vk = sk.to_viewing_key();
1065        assert_eq!(
1066            vk.clone(),
1067            BalanceOwner::FullViewingKey(vk).full_viewing_key().unwrap()
1068        );
1069
1070        let addr = address::testing::established_address_1();
1071        assert!(BalanceOwner::Address(addr).full_viewing_key().is_none());
1072    }
1073
1074    #[test]
1075    fn test_balance_owner_display() {
1076        let addr = address::testing::established_address_1();
1077
1078        let sk = ExtendedSpendingKey::from(
1079            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]),
1080        );
1081        let vk = sk.to_viewing_key();
1082
1083        assert_eq!(addr.to_string(), BalanceOwner::Address(addr).to_string());
1084
1085        assert_eq!(
1086            vk.to_string(),
1087            BalanceOwner::FullViewingKey(vk).to_string()
1088        );
1089    }
1090
1091    #[test]
1092    fn test_balance_owner_borsh() {
1093        let addr = address::testing::established_address_1();
1094
1095        let owner = BalanceOwner::Address(addr);
1096        let serialized = owner.serialize_to_vec();
1097        let deserialized =
1098            BalanceOwner::try_from_slice(&serialized[..]).unwrap();
1099        assert_eq!(owner, deserialized);
1100
1101        let sk = ExtendedSpendingKey::from(
1102            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]),
1103        );
1104        let vk = sk.to_viewing_key();
1105
1106        let owner = BalanceOwner::FullViewingKey(vk);
1107        let serialized = owner.serialize_to_vec();
1108        let deserialized =
1109            BalanceOwner::try_from_slice(&serialized[..]).unwrap();
1110        assert_eq!(owner, deserialized);
1111    }
1112
1113    #[test]
1114    fn test_transfer_target_borsh() {
1115        let addr = address::testing::established_address_1();
1116
1117        let target = TransferTarget::Address(addr);
1118        let serialized = target.serialize_to_vec();
1119        let deserialized =
1120            TransferTarget::try_from_slice(&serialized[..]).unwrap();
1121        assert_eq!(target, deserialized);
1122
1123        let sk = ExtendedSpendingKey::from(
1124            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]),
1125        );
1126        let (_diversifier, pa) = sk.0.default_address();
1127        let pa = PaymentAddress::from(pa);
1128
1129        let target = TransferTarget::PaymentAddress(pa);
1130        let serialized = target.serialize_to_vec();
1131        let deserialized =
1132            TransferTarget::try_from_slice(&serialized[..]).unwrap();
1133        assert_eq!(target, deserialized);
1134
1135        const IBC_ADDR: &str = "noble18st0wqx84av8y6xdlss9d6m2nepyqwj6nfxxuv";
1136
1137        let target = TransferTarget::Ibc(IBC_ADDR.to_owned());
1138        let serialized = target.serialize_to_vec();
1139        let deserialized =
1140            TransferTarget::try_from_slice(&serialized[..]).unwrap();
1141        assert_eq!(target, deserialized);
1142    }
1143
1144    #[test]
1145    fn test_masp_tx_id_display() {
1146        let tx_id = MaspTxId::from(TxIdInner::from_bytes([
1147            10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1148            0, 0, 0, 0, 0, 0, 0, 0, 12, 11,
1149        ]));
1150        assert_eq!(
1151            tx_id.to_string(),
1152            "0b0c00000000000000000000000000000000000000000000000000000000000a"
1153        );
1154    }
1155
1156    #[test]
1157    fn test_masp_tx_id_basics() {
1158        let tx_id = MaspTxId::from(TxIdInner::from_bytes([
1159            0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1160            0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1161        ]));
1162        let tx_id_str = serde_json::to_string(&tx_id).unwrap();
1163        let decoded: MaspTxId = serde_json::from_str(&tx_id_str).unwrap();
1164        assert_eq!(tx_id, decoded);
1165    }
1166
1167    #[test]
1168    fn test_masp_epoch_basics() {
1169        let epoch = MaspEpoch::new(123);
1170        let epoch_str = epoch.to_string();
1171        assert_eq!(&epoch_str, "123");
1172        let decoded = MaspEpoch::from_str(&epoch_str).unwrap();
1173        assert_eq!(epoch, decoded);
1174    }
1175
1176    #[test]
1177    fn test_masp_asset_data_basics() {
1178        let data = AssetData {
1179            token: address::testing::nam(),
1180            denom: Denomination(6),
1181            position: MaspDigitPos::One,
1182            epoch: None,
1183        };
1184
1185        let data = data.undate();
1186        assert!(data.epoch.is_none());
1187
1188        let epoch_0 = MaspEpoch::new(3);
1189        let mut data = data.redate(epoch_0);
1190        assert!(data.epoch.is_none());
1191        data.epoch = Some(epoch_0);
1192
1193        let epoch_1 = MaspEpoch::new(5);
1194        let data = data.redate(epoch_1);
1195        assert_eq!(data.epoch, Some(epoch_1));
1196    }
1197
1198    #[test]
1199    fn test_masp_keys_basics() {
1200        let sk = ExtendedSpendingKey::from(
1201            masp_primitives::zip32::ExtendedSpendingKey::master(&[0_u8]),
1202        );
1203        string_encoding::testing::test_string_formatting(&sk);
1204
1205        let vk = sk.to_viewing_key();
1206        string_encoding::testing::test_string_formatting(&vk);
1207
1208        let (_diversifier, pa) = sk.0.default_address();
1209        let pa = PaymentAddress::from(pa);
1210        string_encoding::testing::test_string_formatting(&pa);
1211    }
1212}