zcash_transparent/
address.rs

1//! Support for legacy transparent addresses and scripts.
2
3use alloc::string::String;
4use alloc::vec::Vec;
5use core::fmt;
6use core::ops::Shl;
7use core2::io::{self, Read, Write};
8use zcash_address::TryFromAddress;
9use zcash_protocol::consensus::NetworkType;
10
11use zcash_encoding::Vector;
12
13/// Defined script opcodes.
14///
15/// Most of the opcodes are unused by this crate, but we define them so that the alternate
16/// `Debug` impl for [`Script`] renders correctly for unexpected scripts.
17#[derive(Debug)]
18enum OpCode {
19    // push value
20    Op0 = 0x00, // False
21    PushData1 = 0x4c,
22    PushData2 = 0x4d,
23    PushData4 = 0x4e,
24    Negative1 = 0x4f,
25    Reserved = 0x50,
26    Op1 = 0x51, // True
27    Op2 = 0x52,
28    Op3 = 0x53,
29    Op4 = 0x54,
30    Op5 = 0x55,
31    Op6 = 0x56,
32    Op7 = 0x57,
33    Op8 = 0x58,
34    Op9 = 0x59,
35    Op10 = 0x5a,
36    Op11 = 0x5b,
37    Op12 = 0x5c,
38    Op13 = 0x5d,
39    Op14 = 0x5e,
40    Op15 = 0x5f,
41    Op16 = 0x60,
42
43    // control
44    Nop = 0x61,
45    Ver = 0x62,
46    If = 0x63,
47    NotIf = 0x64,
48    VerIf = 0x65,
49    VerNotIf = 0x66,
50    Else = 0x67,
51    EndIf = 0x68,
52    Verify = 0x69,
53    Return = 0x6a,
54
55    // stack ops
56    ToAltStack = 0x6b,
57    FromAltStack = 0x6c,
58    Drop2 = 0x6d,
59    Dup2 = 0x6e,
60    Dup3 = 0x6f,
61    Over2 = 0x70,
62    Rot2 = 0x71,
63    Swap2 = 0x72,
64    IfDup = 0x73,
65    Depth = 0x74,
66    Drop = 0x75,
67    Dup = 0x76,
68    Nip = 0x77,
69    Over = 0x78,
70    Pick = 0x79,
71    Roll = 0x7a,
72    Rot = 0x7b,
73    Swap = 0x7c,
74    Tuck = 0x7d,
75
76    // splice ops
77    Cat = 0x7e,    // Disabled
78    Substr = 0x7f, // Disabled
79    Left = 0x80,   // Disabled
80    Right = 0x81,  // Disabled
81    Size = 0x82,
82
83    // bit logic
84    Invert = 0x83, // Disabled
85    And = 0x84,    // Disabled
86    Or = 0x85,     // Disabled
87    Xor = 0x86,    // Disabled
88    Equal = 0x87,
89    EqualVerify = 0x88,
90    Reserved1 = 0x89,
91    Reserved2 = 0x8a,
92
93    // numeric
94    Add1 = 0x8b,
95    Sub1 = 0x8c,
96    Mul2 = 0x8d, // Disabled
97    Div2 = 0x8e, // Disabled
98    Negate = 0x8f,
99    Abs = 0x90,
100    Not = 0x91,
101    NotEqual0 = 0x92,
102
103    Add = 0x93,
104    Sub = 0x94,
105    Mul = 0x95,    // Disabled
106    Div = 0x96,    // Disabled
107    Mod = 0x97,    // Disabled
108    LShift = 0x98, // Disabled
109    RShift = 0x99, // Disabled
110
111    BoolAnd = 0x9a,
112    BoolOr = 0x9b,
113    NumEqual = 0x9c,
114    NumEqualVerify = 0x9d,
115    NumNotEqual = 0x9e,
116    LessThan = 0x9f,
117    GreaterThan = 0xa0,
118    LessThanOrEqual = 0xa1,
119    GreaterThanOrEqual = 0xa2,
120    Min = 0xa3,
121    Max = 0xa4,
122
123    Within = 0xa5,
124
125    // crypto
126    Ripemd160 = 0xa6,
127    Sha1 = 0xa7,
128    Sha256 = 0xa8,
129    Hash160 = 0xa9,
130    Hash256 = 0xaa,
131    CodeSeparator = 0xab, // Disabled
132    CheckSig = 0xac,
133    CheckSigVerify = 0xad,
134    CheckMultisig = 0xae,
135    CheckMultisigVerify = 0xaf,
136
137    // expansion
138    Nop1 = 0xb0,
139    CheckLockTimeVerify = 0xb1,
140    Nop3 = 0xb2,
141    Nop4 = 0xb3,
142    Nop5 = 0xb4,
143    Nop6 = 0xb5,
144    Nop7 = 0xb6,
145    Nop8 = 0xb7,
146    Nop9 = 0xb8,
147    Nop10 = 0xb9,
148
149    Invalid = 0xff,
150}
151
152impl OpCode {
153    fn parse(b: u8) -> Option<Self> {
154        match b {
155            0x00 => Some(OpCode::Op0),
156            0x4c => Some(OpCode::PushData1),
157            0x4d => Some(OpCode::PushData2),
158            0x4e => Some(OpCode::PushData4),
159            0x4f => Some(OpCode::Negative1),
160            0x50 => Some(OpCode::Reserved),
161            0x51 => Some(OpCode::Op1),
162            0x52 => Some(OpCode::Op2),
163            0x53 => Some(OpCode::Op3),
164            0x54 => Some(OpCode::Op4),
165            0x55 => Some(OpCode::Op5),
166            0x56 => Some(OpCode::Op6),
167            0x57 => Some(OpCode::Op7),
168            0x58 => Some(OpCode::Op8),
169            0x59 => Some(OpCode::Op9),
170            0x5a => Some(OpCode::Op10),
171            0x5b => Some(OpCode::Op11),
172            0x5c => Some(OpCode::Op12),
173            0x5d => Some(OpCode::Op13),
174            0x5e => Some(OpCode::Op14),
175            0x5f => Some(OpCode::Op15),
176            0x60 => Some(OpCode::Op16),
177            0x61 => Some(OpCode::Nop),
178            0x62 => Some(OpCode::Ver),
179            0x63 => Some(OpCode::If),
180            0x64 => Some(OpCode::NotIf),
181            0x65 => Some(OpCode::VerIf),
182            0x66 => Some(OpCode::VerNotIf),
183            0x67 => Some(OpCode::Else),
184            0x68 => Some(OpCode::EndIf),
185            0x69 => Some(OpCode::Verify),
186            0x6a => Some(OpCode::Return),
187            0x6b => Some(OpCode::ToAltStack),
188            0x6c => Some(OpCode::FromAltStack),
189            0x6d => Some(OpCode::Drop2),
190            0x6e => Some(OpCode::Dup2),
191            0x6f => Some(OpCode::Dup3),
192            0x70 => Some(OpCode::Over2),
193            0x71 => Some(OpCode::Rot2),
194            0x72 => Some(OpCode::Swap2),
195            0x73 => Some(OpCode::IfDup),
196            0x74 => Some(OpCode::Depth),
197            0x75 => Some(OpCode::Drop),
198            0x76 => Some(OpCode::Dup),
199            0x77 => Some(OpCode::Nip),
200            0x78 => Some(OpCode::Over),
201            0x79 => Some(OpCode::Pick),
202            0x7a => Some(OpCode::Roll),
203            0x7b => Some(OpCode::Rot),
204            0x7c => Some(OpCode::Swap),
205            0x7d => Some(OpCode::Tuck),
206            0x7e => Some(OpCode::Cat),
207            0x7f => Some(OpCode::Substr),
208            0x80 => Some(OpCode::Left),
209            0x81 => Some(OpCode::Right),
210            0x82 => Some(OpCode::Size),
211            0x83 => Some(OpCode::Invert),
212            0x84 => Some(OpCode::And),
213            0x85 => Some(OpCode::Or),
214            0x86 => Some(OpCode::Xor),
215            0x87 => Some(OpCode::Equal),
216            0x88 => Some(OpCode::EqualVerify),
217            0x89 => Some(OpCode::Reserved1),
218            0x8a => Some(OpCode::Reserved2),
219            0x8b => Some(OpCode::Add1),
220            0x8c => Some(OpCode::Sub1),
221            0x8d => Some(OpCode::Mul2),
222            0x8e => Some(OpCode::Div2),
223            0x8f => Some(OpCode::Negate),
224            0x90 => Some(OpCode::Abs),
225            0x91 => Some(OpCode::Not),
226            0x92 => Some(OpCode::NotEqual0),
227            0x93 => Some(OpCode::Add),
228            0x94 => Some(OpCode::Sub),
229            0x95 => Some(OpCode::Mul),
230            0x96 => Some(OpCode::Div),
231            0x97 => Some(OpCode::Mod),
232            0x98 => Some(OpCode::LShift),
233            0x99 => Some(OpCode::RShift),
234            0x9a => Some(OpCode::BoolAnd),
235            0x9b => Some(OpCode::BoolOr),
236            0x9c => Some(OpCode::NumEqual),
237            0x9d => Some(OpCode::NumEqualVerify),
238            0x9e => Some(OpCode::NumNotEqual),
239            0x9f => Some(OpCode::LessThan),
240            0xa0 => Some(OpCode::GreaterThan),
241            0xa1 => Some(OpCode::LessThanOrEqual),
242            0xa2 => Some(OpCode::GreaterThanOrEqual),
243            0xa3 => Some(OpCode::Min),
244            0xa4 => Some(OpCode::Max),
245            0xa5 => Some(OpCode::Within),
246            0xa6 => Some(OpCode::Ripemd160),
247            0xa7 => Some(OpCode::Sha1),
248            0xa8 => Some(OpCode::Sha256),
249            0xa9 => Some(OpCode::Hash160),
250            0xaa => Some(OpCode::Hash256),
251            0xab => Some(OpCode::CodeSeparator),
252            0xac => Some(OpCode::CheckSig),
253            0xad => Some(OpCode::CheckSigVerify),
254            0xae => Some(OpCode::CheckMultisig),
255            0xaf => Some(OpCode::CheckMultisigVerify),
256            0xb0 => Some(OpCode::Nop1),
257            0xb1 => Some(OpCode::CheckLockTimeVerify),
258            0xb2 => Some(OpCode::Nop3),
259            0xb3 => Some(OpCode::Nop4),
260            0xb4 => Some(OpCode::Nop5),
261            0xb5 => Some(OpCode::Nop6),
262            0xb6 => Some(OpCode::Nop7),
263            0xb7 => Some(OpCode::Nop8),
264            0xb8 => Some(OpCode::Nop9),
265            0xb9 => Some(OpCode::Nop10),
266            0xff => Some(OpCode::Invalid),
267            _ => None,
268        }
269    }
270}
271
272/// A serialized script, used inside transparent inputs and outputs of a transaction.
273#[derive(Clone, Default, PartialEq, Eq)]
274pub struct Script(pub Vec<u8>);
275
276impl fmt::Debug for Script {
277    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278        struct ScriptPrinter<'s>(&'s [u8]);
279        impl fmt::Debug for ScriptPrinter<'_> {
280            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281                let mut l = f.debug_list();
282                let mut unknown: Option<String> = None;
283                for b in self.0 {
284                    if let Some(opcode) = OpCode::parse(*b) {
285                        if let Some(s) = unknown.take() {
286                            l.entry(&s);
287                        }
288                        l.entry(&opcode);
289                    } else {
290                        let encoded = format!("{:02x}", b);
291                        if let Some(s) = &mut unknown {
292                            s.push_str(&encoded);
293                        } else {
294                            unknown = Some(encoded);
295                        }
296                    }
297                }
298                l.finish()
299            }
300        }
301
302        if f.alternate() {
303            f.debug_tuple("Script")
304                .field(&ScriptPrinter(&self.0))
305                .finish()
306        } else {
307            f.debug_tuple("Script")
308                .field(&hex::encode(&self.0))
309                .finish()
310        }
311    }
312}
313
314impl Script {
315    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
316        let script = Vector::read(&mut reader, |r| {
317            let mut bytes = [0; 1];
318            r.read_exact(&mut bytes).map(|_| bytes[0])
319        })?;
320        Ok(Script(script))
321    }
322
323    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
324        Vector::write(&mut writer, &self.0, |w, e| w.write_all(&[*e]))
325    }
326
327    /// Returns the length of this script as encoded (including the initial CompactSize).
328    pub fn serialized_size(&self) -> usize {
329        Vector::serialized_size_of_u8_vec(&self.0)
330    }
331
332    /// Returns the address that this Script contains, if any.
333    pub fn address(&self) -> Option<TransparentAddress> {
334        if self.0.len() == 25
335            && self.0[0..3] == [OpCode::Dup as u8, OpCode::Hash160 as u8, 0x14]
336            && self.0[23..25] == [OpCode::EqualVerify as u8, OpCode::CheckSig as u8]
337        {
338            let mut hash = [0; 20];
339            hash.copy_from_slice(&self.0[3..23]);
340            Some(TransparentAddress::PublicKeyHash(hash))
341        } else if self.0.len() == 23
342            && self.0[0..2] == [OpCode::Hash160 as u8, 0x14]
343            && self.0[22] == OpCode::Equal as u8
344        {
345            let mut hash = [0; 20];
346            hash.copy_from_slice(&self.0[2..22]);
347            Some(TransparentAddress::ScriptHash(hash))
348        } else {
349            None
350        }
351    }
352}
353
354impl Shl<OpCode> for Script {
355    type Output = Self;
356
357    fn shl(mut self, rhs: OpCode) -> Self {
358        self.0.push(rhs as u8);
359        self
360    }
361}
362
363impl Shl<&[u8]> for Script {
364    type Output = Self;
365
366    fn shl(mut self, data: &[u8]) -> Self {
367        if data.len() < OpCode::PushData1 as usize {
368            self.0.push(data.len() as u8);
369        } else if data.len() <= 0xff {
370            self.0.push(OpCode::PushData1 as u8);
371            self.0.push(data.len() as u8);
372        } else if data.len() <= 0xffff {
373            self.0.push(OpCode::PushData2 as u8);
374            self.0.extend((data.len() as u16).to_le_bytes());
375        } else {
376            self.0.push(OpCode::PushData4 as u8);
377            self.0.extend((data.len() as u32).to_le_bytes());
378        }
379        self.0.extend(data);
380        self
381    }
382}
383
384/// A transparent address corresponding to either a public key hash or a script hash.
385#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
386pub enum TransparentAddress {
387    PublicKeyHash([u8; 20]),
388    ScriptHash([u8; 20]),
389}
390
391impl TransparentAddress {
392    /// Generate the `scriptPubKey` corresponding to this address.
393    pub fn script(&self) -> Script {
394        match self {
395            TransparentAddress::PublicKeyHash(key_id) => {
396                // P2PKH script
397                Script::default()
398                    << OpCode::Dup
399                    << OpCode::Hash160
400                    << &key_id[..]
401                    << OpCode::EqualVerify
402                    << OpCode::CheckSig
403            }
404            TransparentAddress::ScriptHash(script_id) => {
405                // P2SH script
406                Script::default() << OpCode::Hash160 << &script_id[..] << OpCode::Equal
407            }
408        }
409    }
410}
411
412impl TryFromAddress for TransparentAddress {
413    type Error = ();
414
415    fn try_from_transparent_p2pkh(
416        _net: NetworkType,
417        data: [u8; 20],
418    ) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
419        Ok(TransparentAddress::PublicKeyHash(data))
420    }
421
422    fn try_from_transparent_p2sh(
423        _net: NetworkType,
424        data: [u8; 20],
425    ) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
426        Ok(TransparentAddress::ScriptHash(data))
427    }
428}
429
430#[cfg(any(test, feature = "test-dependencies"))]
431pub mod testing {
432    use proptest::prelude::{any, prop_compose};
433
434    use super::TransparentAddress;
435
436    prop_compose! {
437        pub fn arb_transparent_addr()(v in proptest::array::uniform20(any::<u8>())) -> TransparentAddress {
438            TransparentAddress::PublicKeyHash(v)
439        }
440    }
441}
442
443#[cfg(test)]
444mod tests {
445    use super::{OpCode, Script, TransparentAddress};
446
447    #[test]
448    fn script_opcode() {
449        {
450            let script = Script::default() << OpCode::PushData1;
451            assert_eq!(&script.0, &[OpCode::PushData1 as u8]);
452        }
453    }
454
455    #[test]
456    fn script_pushdata() {
457        {
458            let script = Script::default() << &[1, 2, 3, 4][..];
459            assert_eq!(&script.0, &[4, 1, 2, 3, 4]);
460        }
461
462        {
463            let short_data = [2; 100];
464            let script = Script::default() << &short_data[..];
465            assert_eq!(script.0[0], OpCode::PushData1 as u8);
466            assert_eq!(script.0[1] as usize, 100);
467            assert_eq!(&script.0[2..], &short_data[..]);
468        }
469
470        {
471            let medium_data = vec![7; 1024];
472            let script = Script::default() << &medium_data[..];
473            assert_eq!(script.0[0], OpCode::PushData2 as u8);
474            assert_eq!(&script.0[1..3], &[0x00, 0x04][..]);
475            assert_eq!(&script.0[3..], &medium_data[..]);
476        }
477
478        {
479            let long_data = vec![42; 1_000_000];
480            let script = Script::default() << &long_data[..];
481            assert_eq!(script.0[0], OpCode::PushData4 as u8);
482            assert_eq!(&script.0[1..5], &[0x40, 0x42, 0x0f, 0x00][..]);
483            assert_eq!(&script.0[5..], &long_data[..]);
484        }
485    }
486
487    #[test]
488    fn p2pkh() {
489        let addr = TransparentAddress::PublicKeyHash([4; 20]);
490        assert_eq!(
491            &addr.script().0,
492            &[
493                0x76, 0xa9, 0x14, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
494                0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x88, 0xac,
495            ]
496        );
497        assert_eq!(addr.script().address(), Some(addr));
498    }
499
500    #[test]
501    fn p2sh() {
502        let addr = TransparentAddress::ScriptHash([7; 20]);
503        assert_eq!(
504            &addr.script().0,
505            &[
506                0xa9, 0x14, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
507                0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x87,
508            ]
509        );
510        assert_eq!(addr.script().address(), Some(addr));
511    }
512}