1use 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#[derive(Debug)]
18enum OpCode {
19 Op0 = 0x00, PushData1 = 0x4c,
22 PushData2 = 0x4d,
23 PushData4 = 0x4e,
24 Negative1 = 0x4f,
25 Reserved = 0x50,
26 Op1 = 0x51, 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 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 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 Cat = 0x7e, Substr = 0x7f, Left = 0x80, Right = 0x81, Size = 0x82,
82
83 Invert = 0x83, And = 0x84, Or = 0x85, Xor = 0x86, Equal = 0x87,
89 EqualVerify = 0x88,
90 Reserved1 = 0x89,
91 Reserved2 = 0x8a,
92
93 Add1 = 0x8b,
95 Sub1 = 0x8c,
96 Mul2 = 0x8d, Div2 = 0x8e, Negate = 0x8f,
99 Abs = 0x90,
100 Not = 0x91,
101 NotEqual0 = 0x92,
102
103 Add = 0x93,
104 Sub = 0x94,
105 Mul = 0x95, Div = 0x96, Mod = 0x97, LShift = 0x98, RShift = 0x99, 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 Ripemd160 = 0xa6,
127 Sha1 = 0xa7,
128 Sha256 = 0xa8,
129 Hash160 = 0xa9,
130 Hash256 = 0xaa,
131 CodeSeparator = 0xab, CheckSig = 0xac,
133 CheckSigVerify = 0xad,
134 CheckMultisig = 0xae,
135 CheckMultisigVerify = 0xaf,
136
137 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#[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 pub fn serialized_size(&self) -> usize {
329 Vector::serialized_size_of_u8_vec(&self.0)
330 }
331
332 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#[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 pub fn script(&self) -> Script {
394 match self {
395 TransparentAddress::PublicKeyHash(key_id) => {
396 Script::default()
398 << OpCode::Dup
399 << OpCode::Hash160
400 << &key_id[..]
401 << OpCode::EqualVerify
402 << OpCode::CheckSig
403 }
404 TransparentAddress::ScriptHash(script_id) => {
405 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}