1use byteorder::{ReadBytesExt, WriteBytesExt};
4use std::io::{self, Read, Write};
5use std::ops::Shl;
6
7use zcash_encoding::Vector;
8
9#[cfg(feature = "transparent-inputs")]
10pub mod keys;
11
12enum OpCode {
14 PushData1 = 0x4c,
16 PushData2 = 0x4d,
17 PushData4 = 0x4e,
18
19 Dup = 0x76,
21
22 Equal = 0x87,
24 EqualVerify = 0x88,
25
26 Hash160 = 0xa9,
28 CheckSig = 0xac,
29}
30
31#[derive(Clone, Debug, Default, PartialEq)]
33pub struct Script(pub Vec<u8>);
34
35impl Script {
36 pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
37 let script = Vector::read(&mut reader, |r| r.read_u8())?;
38 Ok(Script(script))
39 }
40
41 pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
42 Vector::write(&mut writer, &self.0, |w, e| w.write_u8(*e))
43 }
44
45 pub fn address(&self) -> Option<TransparentAddress> {
47 if self.0.len() == 25
48 && self.0[0..3] == [OpCode::Dup as u8, OpCode::Hash160 as u8, 0x14]
49 && self.0[23..25] == [OpCode::EqualVerify as u8, OpCode::CheckSig as u8]
50 {
51 let mut hash = [0; 20];
52 hash.copy_from_slice(&self.0[3..23]);
53 Some(TransparentAddress::PublicKey(hash))
54 } else if self.0.len() == 23
55 && self.0[0..2] == [OpCode::Hash160 as u8, 0x14]
56 && self.0[22] == OpCode::Equal as u8
57 {
58 let mut hash = [0; 20];
59 hash.copy_from_slice(&self.0[2..22]);
60 Some(TransparentAddress::Script(hash))
61 } else {
62 None
63 }
64 }
65}
66
67impl Shl<OpCode> for Script {
68 type Output = Self;
69
70 fn shl(mut self, rhs: OpCode) -> Self {
71 self.0.push(rhs as u8);
72 self
73 }
74}
75
76impl Shl<&[u8]> for Script {
77 type Output = Self;
78
79 fn shl(mut self, data: &[u8]) -> Self {
80 if data.len() < OpCode::PushData1 as usize {
81 self.0.push(data.len() as u8);
82 } else if data.len() <= 0xff {
83 self.0.push(OpCode::PushData1 as u8);
84 self.0.push(data.len() as u8);
85 } else if data.len() <= 0xffff {
86 self.0.push(OpCode::PushData2 as u8);
87 self.0.extend(&(data.len() as u16).to_le_bytes());
88 } else {
89 self.0.push(OpCode::PushData4 as u8);
90 self.0.extend(&(data.len() as u32).to_le_bytes());
91 }
92 self.0.extend(data);
93 self
94 }
95}
96
97#[derive(Debug, PartialEq, PartialOrd, Hash, Clone)]
99pub enum TransparentAddress {
100 PublicKey([u8; 20]), Script([u8; 20]), }
103
104impl TransparentAddress {
105 pub fn script(&self) -> Script {
107 match self {
108 TransparentAddress::PublicKey(key_id) => {
109 Script::default()
111 << OpCode::Dup
112 << OpCode::Hash160
113 << &key_id[..]
114 << OpCode::EqualVerify
115 << OpCode::CheckSig
116 }
117 TransparentAddress::Script(script_id) => {
118 Script::default() << OpCode::Hash160 << &script_id[..] << OpCode::Equal
120 }
121 }
122 }
123}
124
125#[cfg(any(test, feature = "test-dependencies"))]
126pub mod testing {
127 use proptest::prelude::{any, prop_compose};
128
129 use super::TransparentAddress;
130
131 prop_compose! {
132 pub fn arb_transparent_addr()(v in proptest::array::uniform20(any::<u8>())) -> TransparentAddress {
133 TransparentAddress::PublicKey(v)
134 }
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::{OpCode, Script, TransparentAddress};
141
142 #[test]
143 fn script_opcode() {
144 {
145 let script = Script::default() << OpCode::PushData1;
146 assert_eq!(&script.0, &[OpCode::PushData1 as u8]);
147 }
148 }
149
150 #[test]
151 fn script_pushdata() {
152 {
153 let script = Script::default() << &[1, 2, 3, 4][..];
154 assert_eq!(&script.0, &[4, 1, 2, 3, 4]);
155 }
156
157 {
158 let short_data = vec![2; 100];
159 let script = Script::default() << &short_data[..];
160 assert_eq!(script.0[0], OpCode::PushData1 as u8);
161 assert_eq!(script.0[1] as usize, 100);
162 assert_eq!(&script.0[2..], &short_data[..]);
163 }
164
165 {
166 let medium_data = vec![7; 1024];
167 let script = Script::default() << &medium_data[..];
168 assert_eq!(script.0[0], OpCode::PushData2 as u8);
169 assert_eq!(&script.0[1..3], &[0x00, 0x04][..]);
170 assert_eq!(&script.0[3..], &medium_data[..]);
171 }
172
173 {
174 let long_data = vec![42; 1_000_000];
175 let script = Script::default() << &long_data[..];
176 assert_eq!(script.0[0], OpCode::PushData4 as u8);
177 assert_eq!(&script.0[1..5], &[0x40, 0x42, 0x0f, 0x00][..]);
178 assert_eq!(&script.0[5..], &long_data[..]);
179 }
180 }
181
182 #[test]
183 fn p2pkh() {
184 let addr = TransparentAddress::PublicKey([4; 20]);
185 assert_eq!(
186 &addr.script().0,
187 &[
188 0x76, 0xa9, 0x14, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
189 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x88, 0xac,
190 ]
191 );
192 assert_eq!(addr.script().address(), Some(addr));
193 }
194
195 #[test]
196 fn p2sh() {
197 let addr = TransparentAddress::Script([7; 20]);
198 assert_eq!(
199 &addr.script().0,
200 &[
201 0xa9, 0x14, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
202 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x87,
203 ]
204 );
205 assert_eq!(addr.script().address(), Some(addr));
206 }
207}