Script opcode and data support

Overrides the shift-left operator for pushing opcodes onto the Script,
matching the notation used in zcashd.
This commit is contained in:
Jack Grigg 2019-05-24 12:30:14 +01:00
parent 1862354ea6
commit dab3c002b7
No known key found for this signature in database
GPG Key ID: 9E8255172BBF9898
5 changed files with 111 additions and 21 deletions

View File

@ -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<u8>);
impl Script {
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let script = Vector::read(&mut reader, |r| r.read_u8())?;
Ok(Script(script))
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
Vector::write(&mut writer, &self.0, |w, e| w.write_u8(*e))
}
}
impl Shl<OpCode> 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[..]);
}
}
}

View File

@ -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;

View File

@ -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<u8>);
impl Script {
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let script = Vector::read(&mut reader, |r| r.read_u8())?;
Ok(Script(script))
}
pub fn write<W: 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],

View File

@ -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";

View File

@ -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]