whisky_pallas/wrapper/transaction_body/
drep.rs

1use bech32::Hrp;
2use pallas::crypto::hash::Hash;
3use pallas::ledger::primitives::conway::DRep as PallasDRep;
4use pallas::ledger::primitives::Fragment;
5use std::str::FromStr;
6use whisky_common::WError;
7
8pub enum DRepKind {
9    Key { addr_key_hash: String },
10    Script { script_hash: String },
11    Abstain,
12    NoConfidence,
13}
14
15#[derive(Debug, PartialEq, Eq, Clone)]
16pub struct DRep {
17    pub inner: PallasDRep,
18}
19
20impl DRep {
21    pub fn new(drep: DRepKind) -> Result<Self, WError> {
22        let pallas_drep = match drep {
23            DRepKind::Key { addr_key_hash } => {
24                let key_hash = Hash::<28>::from_str(&addr_key_hash)
25                    .map_err(|e| WError::new("DRep - Invalid key hash length", &e.to_string()))?;
26                PallasDRep::Key(key_hash)
27            }
28
29            DRepKind::Script { script_hash } => {
30                let script_hash = Hash::<28>::from_str(&script_hash).map_err(|e| {
31                    WError::new("DRep - Invalid script hash length", &e.to_string())
32                })?;
33                PallasDRep::Script(script_hash)
34            }
35
36            DRepKind::Abstain => PallasDRep::Abstain,
37            DRepKind::NoConfidence => PallasDRep::NoConfidence,
38        };
39
40        Ok(Self { inner: pallas_drep })
41    }
42
43    pub fn from_bech32(bech32_str: &str) -> Result<Self, WError> {
44        let (hrp, data) = bech32::decode(bech32_str)
45            .map_err(|e| WError::new("DRep - Bech32 decode error", &e.to_string()))?;
46        // If data length is 28, it's in CIP-105 format
47        if data.len() == 28 {
48            return match hrp.as_str() {
49                "drep_vkh" => Ok(Self {
50                    inner: PallasDRep::Key(Hash::<28>::from(&data[..])),
51                }),
52                "drep" => Ok(Self {
53                    inner: PallasDRep::Key(Hash::<28>::from(&data[..])),
54                }),
55                "drep_script" => Ok(Self {
56                    inner: PallasDRep::Script(Hash::<28>::from(&data[..])),
57                }),
58                _ => Err(WError::new("DRep - Bech32 decode error", "Invalid HRP")),
59            };
60        } else {
61            // Otherwise follow CIP-129
62            let header_byte = data
63                .first()
64                .ok_or_else(|| WError::new("DRep - Bech32 decode error", "Empty data part"))?;
65
66            // Check the header byte starts with 0010
67            if header_byte >> 4 != 0b0010 {
68                return Err(WError::new(
69                    "DRep - Bech32 decode error",
70                    "Invalid DRep header byte",
71                ));
72            } else {
73                // If the final 4 bits are 0010, it's a key hash; if it's 0011, it's a script hash
74                let is_script_hash = (header_byte & 0b0000_1111) == 0b0011;
75                if is_script_hash {
76                    let script_hash = Hash::<28>::from(&data[1..]);
77                    Ok(Self {
78                        inner: PallasDRep::Script(script_hash),
79                    })
80                } else {
81                    let key_hash = Hash::<28>::from(&data[1..]);
82                    Ok(Self {
83                        inner: PallasDRep::Key(key_hash),
84                    })
85                }
86            }
87        }
88    }
89
90    pub fn to_bech32_cip129(&self) -> Result<String, WError> {
91        let data: Vec<u8> = match &self.inner {
92            PallasDRep::Key(key_hash) => {
93                let mut d = vec![0b0010_0010]; // header byte for key hash
94                d.extend_from_slice(key_hash.as_ref());
95                d
96            }
97            PallasDRep::Script(script_hash) => {
98                let mut d = vec![0b0010_0011]; // header byte for script hash
99                d.extend_from_slice(script_hash.as_ref());
100                d
101            }
102            PallasDRep::Abstain => {
103                return Err(WError::new(
104                    "DRep - Bech32 encode error",
105                    "Cannot encode Abstain DRep to bech32",
106                ))
107            }
108            PallasDRep::NoConfidence => {
109                return Err(WError::new(
110                    "DRep - Bech32 encode error",
111                    "Cannot encode NoConfidence DRep to bech32",
112                ))
113            }
114        };
115
116        let bech32_str = bech32::encode::<bech32::Bech32>(Hrp::parse("drep").unwrap(), &data)
117            .map_err(|e| WError::new("DRep - Bech32 encode error", &e.to_string()))?;
118        Ok(bech32_str)
119    }
120
121    pub fn encode(&self) -> Result<String, WError> {
122        let encoded_fragment = self
123            .inner
124            .encode_fragment()
125            .map_err(|e| WError::new("DRep - Fragment encode error", &e.to_string()))?;
126        Ok(hex::encode(encoded_fragment))
127    }
128
129    pub fn decode_bytes(bytes: &[u8]) -> Result<Self, WError> {
130        let inner = PallasDRep::decode_fragment(&bytes)
131            .map_err(|e| WError::new("DRep - Fragment decode error", &e.to_string()))?;
132        Ok(Self { inner })
133    }
134}