rustywallet_psbt/
types.rs

1//! PSBT types and constants
2
3use std::collections::BTreeMap;
4
5/// PSBT magic bytes: "psbt" + 0xff separator
6pub const PSBT_MAGIC: [u8; 5] = [0x70, 0x73, 0x62, 0x74, 0xff];
7
8/// PSBT separator byte
9pub const PSBT_SEPARATOR: u8 = 0x00;
10
11// Global key types
12pub const PSBT_GLOBAL_UNSIGNED_TX: u8 = 0x00;
13pub const PSBT_GLOBAL_XPUB: u8 = 0x01;
14pub const PSBT_GLOBAL_TX_VERSION: u8 = 0x02;
15pub const PSBT_GLOBAL_FALLBACK_LOCKTIME: u8 = 0x03;
16pub const PSBT_GLOBAL_INPUT_COUNT: u8 = 0x04;
17pub const PSBT_GLOBAL_OUTPUT_COUNT: u8 = 0x05;
18pub const PSBT_GLOBAL_TX_MODIFIABLE: u8 = 0x06;
19pub const PSBT_GLOBAL_VERSION: u8 = 0xFB;
20pub const PSBT_GLOBAL_PROPRIETARY: u8 = 0xFC;
21
22// Input key types
23pub const PSBT_IN_NON_WITNESS_UTXO: u8 = 0x00;
24pub const PSBT_IN_WITNESS_UTXO: u8 = 0x01;
25pub const PSBT_IN_PARTIAL_SIG: u8 = 0x02;
26pub const PSBT_IN_SIGHASH_TYPE: u8 = 0x03;
27pub const PSBT_IN_REDEEM_SCRIPT: u8 = 0x04;
28pub const PSBT_IN_WITNESS_SCRIPT: u8 = 0x05;
29pub const PSBT_IN_BIP32_DERIVATION: u8 = 0x06;
30pub const PSBT_IN_FINAL_SCRIPTSIG: u8 = 0x07;
31pub const PSBT_IN_FINAL_SCRIPTWITNESS: u8 = 0x08;
32pub const PSBT_IN_POR_COMMITMENT: u8 = 0x09;
33pub const PSBT_IN_RIPEMD160: u8 = 0x0A;
34pub const PSBT_IN_SHA256: u8 = 0x0B;
35pub const PSBT_IN_HASH160: u8 = 0x0C;
36pub const PSBT_IN_HASH256: u8 = 0x0D;
37pub const PSBT_IN_PREVIOUS_TXID: u8 = 0x0E;
38pub const PSBT_IN_OUTPUT_INDEX: u8 = 0x0F;
39pub const PSBT_IN_SEQUENCE: u8 = 0x10;
40pub const PSBT_IN_REQUIRED_TIME_LOCKTIME: u8 = 0x11;
41pub const PSBT_IN_REQUIRED_HEIGHT_LOCKTIME: u8 = 0x12;
42pub const PSBT_IN_TAP_KEY_SIG: u8 = 0x13;
43pub const PSBT_IN_TAP_SCRIPT_SIG: u8 = 0x14;
44pub const PSBT_IN_TAP_LEAF_SCRIPT: u8 = 0x15;
45pub const PSBT_IN_TAP_BIP32_DERIVATION: u8 = 0x16;
46pub const PSBT_IN_TAP_INTERNAL_KEY: u8 = 0x17;
47pub const PSBT_IN_TAP_MERKLE_ROOT: u8 = 0x18;
48pub const PSBT_IN_PROPRIETARY: u8 = 0xFC;
49
50// Output key types
51pub const PSBT_OUT_REDEEM_SCRIPT: u8 = 0x00;
52pub const PSBT_OUT_WITNESS_SCRIPT: u8 = 0x01;
53pub const PSBT_OUT_BIP32_DERIVATION: u8 = 0x02;
54pub const PSBT_OUT_AMOUNT: u8 = 0x03;
55pub const PSBT_OUT_SCRIPT: u8 = 0x04;
56pub const PSBT_OUT_TAP_INTERNAL_KEY: u8 = 0x05;
57pub const PSBT_OUT_TAP_TREE: u8 = 0x06;
58pub const PSBT_OUT_TAP_BIP32_DERIVATION: u8 = 0x07;
59pub const PSBT_OUT_PROPRIETARY: u8 = 0xFC;
60
61/// Sighash types for PSBT signing
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
63pub enum PsbtSighashType {
64    /// SIGHASH_ALL (0x01)
65    #[default]
66    All,
67    /// SIGHASH_NONE (0x02)
68    None,
69    /// SIGHASH_SINGLE (0x03)
70    Single,
71    /// SIGHASH_ALL | SIGHASH_ANYONECANPAY (0x81)
72    AllAnyoneCanPay,
73    /// SIGHASH_NONE | SIGHASH_ANYONECANPAY (0x82)
74    NoneAnyoneCanPay,
75    /// SIGHASH_SINGLE | SIGHASH_ANYONECANPAY (0x83)
76    SingleAnyoneCanPay,
77    /// SIGHASH_DEFAULT for Taproot (0x00)
78    Default,
79}
80
81impl PsbtSighashType {
82    /// Convert to u32 for serialization
83    pub fn to_u32(self) -> u32 {
84        match self {
85            Self::All => 0x01,
86            Self::None => 0x02,
87            Self::Single => 0x03,
88            Self::AllAnyoneCanPay => 0x81,
89            Self::NoneAnyoneCanPay => 0x82,
90            Self::SingleAnyoneCanPay => 0x83,
91            Self::Default => 0x00,
92        }
93    }
94
95    /// Parse from u32
96    pub fn from_u32(value: u32) -> Option<Self> {
97        match value {
98            0x00 => Some(Self::Default),
99            0x01 => Some(Self::All),
100            0x02 => Some(Self::None),
101            0x03 => Some(Self::Single),
102            0x81 => Some(Self::AllAnyoneCanPay),
103            0x82 => Some(Self::NoneAnyoneCanPay),
104            0x83 => Some(Self::SingleAnyoneCanPay),
105            _ => None,
106        }
107    }
108}
109
110/// Key source information (master fingerprint + derivation path)
111#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct KeySource {
113    /// Master key fingerprint (first 4 bytes of hash160 of master pubkey)
114    pub fingerprint: [u8; 4],
115    /// BIP32 derivation path
116    pub path: Vec<u32>,
117}
118
119impl KeySource {
120    /// Create a new key source
121    pub fn new(fingerprint: [u8; 4], path: Vec<u32>) -> Self {
122        Self { fingerprint, path }
123    }
124
125    /// Serialize to bytes
126    pub fn to_bytes(&self) -> Vec<u8> {
127        let mut bytes = Vec::with_capacity(4 + self.path.len() * 4);
128        bytes.extend_from_slice(&self.fingerprint);
129        for &index in &self.path {
130            bytes.extend_from_slice(&index.to_le_bytes());
131        }
132        bytes
133    }
134
135    /// Parse from bytes
136    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
137        if bytes.len() < 4 || !((bytes.len() - 4).is_multiple_of(4)) {
138            return None;
139        }
140
141        let mut fingerprint = [0u8; 4];
142        fingerprint.copy_from_slice(&bytes[0..4]);
143
144        let path_len = (bytes.len() - 4) / 4;
145        let mut path = Vec::with_capacity(path_len);
146        for i in 0..path_len {
147            let start = 4 + i * 4;
148            let index = u32::from_le_bytes([
149                bytes[start],
150                bytes[start + 1],
151                bytes[start + 2],
152                bytes[start + 3],
153            ]);
154            path.push(index);
155        }
156
157        Some(Self { fingerprint, path })
158    }
159
160    /// Format path as string (e.g., "m/84'/0'/0'/0/0")
161    pub fn path_string(&self) -> String {
162        let mut s = String::from("m");
163        for &index in &self.path {
164            if index >= 0x80000000 {
165                s.push_str(&format!("/{}'", index - 0x80000000));
166            } else {
167                s.push_str(&format!("/{}", index));
168            }
169        }
170        s
171    }
172}
173
174/// Proprietary key identifier
175#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
176pub struct ProprietaryKey {
177    /// Identifier prefix
178    pub prefix: Vec<u8>,
179    /// Subtype
180    pub subtype: u8,
181    /// Key data
182    pub key: Vec<u8>,
183}
184
185impl ProprietaryKey {
186    /// Create a new proprietary key
187    pub fn new(prefix: Vec<u8>, subtype: u8, key: Vec<u8>) -> Self {
188        Self { prefix, subtype, key }
189    }
190
191    /// Serialize to bytes
192    pub fn to_bytes(&self) -> Vec<u8> {
193        let mut bytes = Vec::new();
194        // Compact size for prefix length
195        bytes.push(self.prefix.len() as u8);
196        bytes.extend_from_slice(&self.prefix);
197        bytes.push(self.subtype);
198        bytes.extend_from_slice(&self.key);
199        bytes
200    }
201
202    /// Parse from bytes (after key type byte)
203    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
204        if bytes.is_empty() {
205            return None;
206        }
207
208        let prefix_len = bytes[0] as usize;
209        if bytes.len() < 1 + prefix_len + 1 {
210            return None;
211        }
212
213        let prefix = bytes[1..1 + prefix_len].to_vec();
214        let subtype = bytes[1 + prefix_len];
215        let key = bytes[2 + prefix_len..].to_vec();
216
217        Some(Self { prefix, subtype, key })
218    }
219}
220
221/// Raw key in PSBT map
222#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
223pub struct RawKey {
224    /// Key type
225    pub key_type: u8,
226    /// Key data (may be empty)
227    pub key_data: Vec<u8>,
228}
229
230impl RawKey {
231    /// Create a new raw key
232    pub fn new(key_type: u8, key_data: Vec<u8>) -> Self {
233        Self { key_type, key_data }
234    }
235
236    /// Create a key with no data
237    pub fn type_only(key_type: u8) -> Self {
238        Self {
239            key_type,
240            key_data: Vec::new(),
241        }
242    }
243}
244
245/// Unknown fields storage
246pub type UnknownFields = BTreeMap<RawKey, Vec<u8>>;
247
248/// Proprietary fields storage
249pub type ProprietaryFields = BTreeMap<ProprietaryKey, Vec<u8>>;
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254
255    #[test]
256    fn test_sighash_type_roundtrip() {
257        let types = [
258            PsbtSighashType::All,
259            PsbtSighashType::None,
260            PsbtSighashType::Single,
261            PsbtSighashType::AllAnyoneCanPay,
262            PsbtSighashType::NoneAnyoneCanPay,
263            PsbtSighashType::SingleAnyoneCanPay,
264            PsbtSighashType::Default,
265        ];
266
267        for sighash in types {
268            let value = sighash.to_u32();
269            let parsed = PsbtSighashType::from_u32(value).unwrap();
270            assert_eq!(sighash, parsed);
271        }
272    }
273
274    #[test]
275    fn test_key_source_roundtrip() {
276        let source = KeySource::new(
277            [0x01, 0x02, 0x03, 0x04],
278            vec![0x80000054, 0x80000000, 0x80000000, 0, 0],
279        );
280
281        let bytes = source.to_bytes();
282        let parsed = KeySource::from_bytes(&bytes).unwrap();
283
284        assert_eq!(source, parsed);
285        assert_eq!(parsed.path_string(), "m/84'/0'/0'/0/0");
286    }
287
288    #[test]
289    fn test_proprietary_key_roundtrip() {
290        let key = ProprietaryKey::new(b"test".to_vec(), 0x01, b"data".to_vec());
291        let bytes = key.to_bytes();
292        let parsed = ProprietaryKey::from_bytes(&bytes).unwrap();
293        assert_eq!(key, parsed);
294    }
295}