radix_transactions/model/hash/
decoder.rs

1use bech32::{FromBase32, Variant};
2
3use crate::internal_prelude::*;
4
5pub struct TransactionHashBech32Decoder {
6    pub hrp_set: HrpSet,
7}
8
9impl TransactionHashBech32Decoder {
10    pub fn for_simulator() -> Self {
11        Self::new(&NetworkDefinition::simulator())
12    }
13
14    /// Instantiates a new TransactionHashBech32Decoder with the HRP corresponding to the passed network.
15    pub fn new(network: &NetworkDefinition) -> Self {
16        Self {
17            hrp_set: network.into(),
18        }
19    }
20
21    pub fn validate_and_decode<T>(&self, hash: &str) -> Result<T, TransactionHashBech32DecodeError>
22    where
23        T: IsTransactionHash,
24    {
25        // Decode the hash string
26        let (hrp, data, variant) =
27            bech32::decode(hash).map_err(TransactionHashBech32DecodeError::Bech32mDecodingError)?;
28
29        // Validate the Bech32 variant to ensure that is is Bech32m
30        match variant {
31            Variant::Bech32m => {}
32            _ => return Err(TransactionHashBech32DecodeError::InvalidVariant(variant)),
33        };
34
35        // Convert the data to u8 from u5.
36        let data = Vec::<u8>::from_base32(&data)
37            .map_err(TransactionHashBech32DecodeError::Bech32mDecodingError)?;
38
39        // Validate the length
40        let hash = data
41            .try_into()
42            .map(Hash)
43            .map_err(|_| TransactionHashBech32DecodeError::InvalidLength)?;
44
45        // Validation complete, return data bytes
46        T::create_from_hrp_and_hash(&hrp, hash, &self.hrp_set).map_err(|err| match err {
47            HashCreationError::InvalidHrp => TransactionHashBech32DecodeError::InvalidHrp,
48        })
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use crate::internal_prelude::*;
55
56    #[test]
57    fn intent_hash_is_decoded_as_expected() {
58        // Arrange
59        let decoder = TransactionHashBech32Decoder::for_simulator();
60        let encoded_hash = "txid_sim1vrjkzlt8pekg5s46tum5na8lzpulvc3p72p92nkdm2dd8p0vkx2svr7ejr";
61        let expected_hash =
62            Hash::from_str("60e5617d670e6c8a42ba5f3749f4ff1079f66221f282554ecdda9ad385ecb195")
63                .unwrap();
64
65        // Act
66        let decoded = decoder
67            .validate_and_decode::<TransactionIntentHash>(encoded_hash)
68            .unwrap();
69
70        // Assert
71        assert_eq!(decoded.0, expected_hash)
72    }
73
74    #[test]
75    fn signed_intent_hash_is_decoded_as_expected() {
76        // Arrange
77        let decoder = TransactionHashBech32Decoder::for_simulator();
78        let encoded_hash =
79            "signedintent_sim1c3f6q287pvw2pfs2extnh4yfmtc6ephgga7shf23nck85467026qrzn64x";
80        let expected_hash =
81            Hash::from_str("c453a028fe0b1ca0a60ac9973bd489daf1ac86e8477d0ba5519e2c7a575e7ab4")
82                .unwrap();
83
84        // Act
85        let decoded = decoder
86            .validate_and_decode::<SignedTransactionIntentHash>(encoded_hash)
87            .unwrap();
88
89        // Assert
90        assert_eq!(decoded.0, expected_hash)
91    }
92
93    #[test]
94    fn notarized_transaction_hash_is_decoded_as_expected() {
95        // Arrange
96        let decoder = TransactionHashBech32Decoder::for_simulator();
97        let encoded_hash =
98            "notarizedtransaction_sim16aya9aqejr35u23g4gklcs3mya5nllxyy4y2y4yw9lur3wq6cdfsgpgkww";
99        let expected_hash =
100            Hash::from_str("d749d2f41990e34e2a28aa2dfc423b27693ffcc42548a2548e2ff838b81ac353")
101                .unwrap();
102
103        // Act
104        let decoded = decoder
105            .validate_and_decode::<NotarizedTransactionHash>(encoded_hash)
106            .unwrap();
107
108        // Assert
109        assert_eq!(decoded.0, expected_hash)
110    }
111}