whisky_pallas/wrapper/transaction_body/
reward_account.rs

1use std::str::FromStr;
2
3use pallas::{
4    codec::utils::Bytes,
5    ledger::primitives::{
6        conway::RewardAccount as PallasRewardAccount, Fragment,
7        StakeCredential as PallasStakeCredential,
8    },
9};
10use pallas_crypto::hash::Hash;
11use whisky_common::WError;
12
13use crate::wrapper::transaction_body::StakeCredential;
14
15#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
16pub struct RewardAccount {
17    pub inner: PallasRewardAccount,
18}
19
20impl RewardAccount {
21    pub fn new(reward_account: String) -> Result<Self, WError> {
22        let inner = Bytes::from_str(&reward_account)
23            .map_err(|e| WError::new("RewardAccount - Invalid reward account", &e.to_string()))?;
24        Ok(Self { inner })
25    }
26
27    pub fn from_bech32(bech32_str: &str) -> Result<Self, WError> {
28        let (_hrp, data) = bech32::decode(bech32_str)
29            .map_err(|e| WError::new("Bech32 decode error", &format!("{}", e)))?;
30
31        Ok(Self {
32            inner: Bytes::from(data),
33        })
34    }
35
36    pub fn to_bech32(&self) -> Result<String, WError> {
37        let bytes = self.inner.to_vec();
38        let header_byte = bytes.first().ok_or_else(|| {
39            WError::new("StakeCredential - Bech32 decode error", "Empty data part")
40        })?;
41        // Check the header byte starts with 111
42        if header_byte >> 5 != 0b111 {
43            return Err(WError::new(
44                "StakeCredential - Bech32 decode error",
45                "Invalid StakeCredential header byte",
46            ));
47        } else {
48            // Determine HRP based on header byte, if last bit is 0, it's testnet, else mainnet
49            let hrp = if header_byte & 0b1 == 0 {
50                "stake_test"
51            } else {
52                "stake"
53            };
54            let bech32_str =
55                bech32::encode::<bech32::Bech32>(bech32::Hrp::parse(hrp).unwrap(), &bytes)
56                    .map_err(|e| WError::new("Bech32 encode error", &format!("{}", e)))?;
57            Ok(bech32_str)
58        }
59    }
60
61    pub fn from_bytes(bytes: &[u8]) -> Result<Self, WError> {
62        let inner = Bytes::from(bytes.to_vec());
63        Ok(Self { inner })
64    }
65
66    pub fn to_stake_cred(&self) -> Result<StakeCredential, WError> {
67        let bytes = self.inner.to_vec();
68        let header_byte = bytes.first().ok_or_else(|| {
69            WError::new("StakeCredential - Bech32 decode error", "Empty data part")
70        })?;
71
72        // Check the header byte starts with 111
73        if header_byte >> 5 != 0b111 {
74            return Err(WError::new(
75                "StakeCredential - Bech32 decode error",
76                "Invalid StakeCredential header byte",
77            ));
78        } else {
79            // If the 3rd bit is 0, it's a key hash; if it's 1, it's a script hash
80            let is_script_hash = (header_byte >> 4) & 0b1 == 1;
81            if is_script_hash {
82                Ok(StakeCredential {
83                    inner: PallasStakeCredential::ScriptHash(Hash::from(&bytes[1..])),
84                })
85            } else {
86                Ok(StakeCredential {
87                    inner: PallasStakeCredential::AddrKeyhash(Hash::from(&bytes[1..])),
88                })
89            }
90        }
91    }
92
93    pub fn encode(&self) -> Result<String, WError> {
94        let encoded_fragment = self
95            .inner
96            .encode_fragment()
97            .map_err(|e| WError::new("RewardAccount - Fragment encode error", &e.to_string()))?;
98        Ok(hex::encode(encoded_fragment))
99    }
100
101    pub fn decode_bytes(bytes: &[u8]) -> Result<Self, WError> {
102        let inner = PallasRewardAccount::decode_fragment(&bytes)
103            .map_err(|e| WError::new("RewardAccount - Fragment decode error", &e.to_string()))?;
104        Ok(Self { inner })
105    }
106}