radix_transactions/model/hash/
encoder.rs

1use bech32::{ToBase32, Variant};
2
3use crate::internal_prelude::*;
4
5pub struct TransactionHashBech32Encoder {
6    pub hrp_set: HrpSet,
7}
8
9impl TransactionHashBech32Encoder {
10    pub fn for_simulator() -> Self {
11        Self::new(&NetworkDefinition::simulator())
12    }
13
14    pub fn new(network: &NetworkDefinition) -> Self {
15        Self {
16            hrp_set: network.into(),
17        }
18    }
19
20    pub fn encode<T>(&self, hash: &T) -> Result<String, TransactionHashBech32EncodeError>
21    where
22        T: IsTransactionHash,
23    {
24        let mut buf = String::new();
25        self.encode_to_fmt(&mut buf, hash)?;
26        Ok(buf)
27    }
28
29    pub fn encode_to_fmt<T, F>(
30        &self,
31        fmt: &mut F,
32        hash: &T,
33    ) -> Result<(), TransactionHashBech32EncodeError>
34    where
35        T: IsTransactionHash,
36        F: fmt::Write,
37    {
38        let hrp = hash.hrp(&self.hrp_set);
39        let data = hash.as_inner_hash().as_slice();
40        Self::encode_to_fmt_raw(fmt, hrp, data)
41    }
42
43    fn encode_to_fmt_raw<F: fmt::Write>(
44        fmt: &mut F,
45        hrp: &str,
46        data: &[u8],
47    ) -> Result<(), TransactionHashBech32EncodeError> {
48        match bech32_encode_to_fmt(fmt, hrp, data.to_base32(), Variant::Bech32m) {
49            Ok(Ok(())) => Ok(()),
50            Ok(Err(format_error)) => {
51                Err(TransactionHashBech32EncodeError::FormatError(format_error))
52            }
53            Err(encoding_error) => Err(TransactionHashBech32EncodeError::Bech32mEncodingError(
54                encoding_error,
55            )),
56        }
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use crate::internal_prelude::*;
63
64    #[test]
65    fn intent_hash_is_bech32_encoded_as_expected() {
66        // Arrange
67        let encoder = TransactionHashBech32Encoder::for_simulator();
68        let transaction = transaction();
69        let hash = transaction
70            .prepare(PreparationSettings::latest_ref())
71            .unwrap()
72            .transaction_intent_hash();
73
74        // Act
75        let encoded = encoder.encode(&hash).unwrap();
76
77        // Assert
78        assert_eq!(
79            encoded,
80            "txid_sim1vrjkzlt8pekg5s46tum5na8lzpulvc3p72p92nkdm2dd8p0vkx2svr7ejr"
81        )
82    }
83
84    #[test]
85    fn signed_intent_hash_is_bech32_encoded_as_expected() {
86        // Arrange
87        let encoder = TransactionHashBech32Encoder::for_simulator();
88        let transaction = transaction();
89        let hash = transaction
90            .prepare(PreparationSettings::latest_ref())
91            .unwrap()
92            .signed_transaction_intent_hash();
93
94        // Act
95        let encoded = encoder.encode(&hash).unwrap();
96
97        // Assert
98        assert_eq!(
99            encoded,
100            "signedintent_sim1c3f6q287pvw2pfs2extnh4yfmtc6ephgga7shf23nck85467026qrzn64x"
101        )
102    }
103
104    #[test]
105    fn notarized_transaction_hash_is_bech32_encoded_as_expected() {
106        // Arrange
107        let encoder = TransactionHashBech32Encoder::for_simulator();
108        let transaction = transaction();
109        let hash = transaction
110            .prepare(PreparationSettings::latest_ref())
111            .unwrap()
112            .notarized_transaction_hash();
113
114        // Act
115        let encoded = encoder.encode(&hash).unwrap();
116
117        // Assert
118        assert_eq!(
119            encoded,
120            "notarizedtransaction_sim16aya9aqejr35u23g4gklcs3mya5nllxyy4y2y4yw9lur3wq6cdfsgpgkww"
121        )
122    }
123
124    fn transaction() -> NotarizedTransactionV1 {
125        let pk = Secp256k1PrivateKey::from_u64(1).unwrap();
126        let manifest = ManifestBuilder::new().build();
127        let header = TransactionHeaderV1 {
128            network_id: 0xf2,
129            start_epoch_inclusive: Epoch::of(0),
130            end_epoch_exclusive: Epoch::of(10),
131            nonce: 10,
132            notary_is_signatory: true,
133            notary_public_key: pk.public_key().into(),
134            tip_percentage: 0,
135        };
136        TransactionBuilder::new()
137            .manifest(manifest)
138            .header(header)
139            .notarize(&pk)
140            .build()
141    }
142}