diff --git a/zcash_primitives/src/legacy.rs b/zcash_primitives/src/legacy.rs new file mode 100644 index 0000000..ac22c6b --- /dev/null +++ b/zcash_primitives/src/legacy.rs @@ -0,0 +1,105 @@ +//! Support for legacy transparent addresses and scripts. + +use byteorder::{ReadBytesExt, WriteBytesExt}; +use std::io::{self, Read, Write}; +use std::ops::Shl; + +use crate::serialize::Vector; + +/// Script opcodes. +enum OpCode { + // push value + PushData1 = 0x4c, + PushData2 = 0x4d, + PushData4 = 0x4e, +} + +/// A serialized script, used inside transparent inputs and outputs of a transaction. +#[derive(Debug, Default)] +pub struct Script(pub Vec); + +impl Script { + pub fn read(mut reader: R) -> io::Result { + let script = Vector::read(&mut reader, |r| r.read_u8())?; + Ok(Script(script)) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + Vector::write(&mut writer, &self.0, |w, e| w.write_u8(*e)) + } +} + +impl Shl for Script { + type Output = Self; + + fn shl(mut self, rhs: OpCode) -> Self { + self.0.push(rhs as u8); + self + } +} + +impl Shl<&[u8]> for Script { + type Output = Self; + + fn shl(mut self, data: &[u8]) -> Self { + if data.len() < OpCode::PushData1 as usize { + self.0.push(data.len() as u8); + } else if data.len() <= 0xff { + self.0.push(OpCode::PushData1 as u8); + self.0.push(data.len() as u8); + } else if data.len() <= 0xffff { + self.0.push(OpCode::PushData2 as u8); + self.0.extend(&(data.len() as u16).to_le_bytes()); + } else { + self.0.push(OpCode::PushData4 as u8); + self.0.extend(&(data.len() as u32).to_le_bytes()); + } + self.0.extend(data); + self + } +} + +#[cfg(test)] +mod tests { + use super::{OpCode, Script}; + + #[test] + fn script_opcode() { + { + let script = Script::default() << OpCode::PushData1; + assert_eq!(&script.0, &[OpCode::PushData1 as u8]); + } + } + + #[test] + fn script_pushdata() { + { + let script = Script::default() << &[1, 2, 3, 4][..]; + assert_eq!(&script.0, &[4, 1, 2, 3, 4]); + } + + { + let short_data = vec![2; 100]; + let script = Script::default() << &short_data[..]; + assert_eq!(script.0[0], OpCode::PushData1 as u8); + assert_eq!(script.0[1] as usize, 100); + assert_eq!(&script.0[2..], &short_data[..]); + } + + { + let medium_data = vec![7; 1024]; + let script = Script::default() << &medium_data[..]; + assert_eq!(script.0[0], OpCode::PushData2 as u8); + assert_eq!(&script.0[1..3], &[0x00, 0x04][..]); + assert_eq!(&script.0[3..], &medium_data[..]); + } + + { + let long_data = vec![42; 1_000_000]; + let script = Script::default() << &long_data[..]; + assert_eq!(script.0[0], OpCode::PushData4 as u8); + assert_eq!(&script.0[1..5], &[0x40, 0x42, 0x0f, 0x00][..]); + assert_eq!(&script.0[5..], &long_data[..]); + } + } +} diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index d6ddcc9..a95d000 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -19,6 +19,7 @@ use sapling_crypto::jubjub::JubjubBls12; pub mod block; pub mod keys; +pub mod legacy; pub mod merkle_tree; pub mod note_encryption; pub mod prover; diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index 141d0ff..d4479b5 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -7,7 +7,7 @@ use sapling_crypto::{ }; use std::io::{self, Read, Write}; -use serialize::Vector; +use legacy::Script; use JUBJUB; // π_A + π_B + π_C @@ -58,20 +58,6 @@ impl Amount { } } -#[derive(Debug)] -pub struct Script(pub Vec); - -impl Script { - pub fn read(mut reader: R) -> io::Result { - let script = Vector::read(&mut reader, |r| r.read_u8())?; - Ok(Script(script)) - } - - pub fn write(&self, mut writer: W) -> io::Result<()> { - Vector::write(&mut writer, &self.0, |w, e| w.write_u8(*e)) - } -} - #[derive(Debug)] pub struct OutPoint { hash: [u8; 32], diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index 774e7b4..bcc971a 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -3,10 +3,11 @@ use byteorder::{LittleEndian, WriteBytesExt}; use ff::{PrimeField, PrimeFieldRepr}; use super::{ - components::{Amount, Script, TxOut}, + components::{Amount, TxOut}, Transaction, TransactionData, OVERWINTER_VERSION_GROUP_ID, SAPLING_TX_VERSION, SAPLING_VERSION_GROUP_ID, }; +use legacy::Script; const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &'static [u8; 12] = b"ZcashSigHash"; const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashPrevoutHash"; diff --git a/zcash_primitives/src/transaction/tests.rs b/zcash_primitives/src/transaction/tests.rs index d9788ff..1ebac3a 100644 --- a/zcash_primitives/src/transaction/tests.rs +++ b/zcash_primitives/src/transaction/tests.rs @@ -6,11 +6,8 @@ use sapling_crypto::{ redjubjub::PrivateKey, }; -use super::{ - components::{Amount, Script}, - sighash::signature_hash, - Transaction, TransactionData, -}; +use super::{components::Amount, sighash::signature_hash, Transaction, TransactionData}; +use legacy::Script; use JUBJUB; #[test]