Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transaction builder #92

Merged
merged 19 commits into from
Aug 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion librustzcash/src/rustzcash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ use zcash_primitives::{
merkle_tree::CommitmentTreeWitness,
note_encryption::sapling_ka_agree,
sapling::{merkle_hash, spend_sig},
transaction::components::Amount,
zip32, JUBJUB,
};
use zcash_proofs::{
Expand Down Expand Up @@ -704,6 +705,11 @@ pub extern "system" fn librustzcash_sapling_final_check(
binding_sig: *const [c_uchar; 64],
sighash_value: *const [c_uchar; 32],
) -> bool {
let value_balance = match Amount::from_i64(value_balance) {
Ok(vb) => vb,
Err(()) => return false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please file a ticket about better error reporting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #101.

};

// Deserialize the signature
let binding_sig = match Signature::read(&(unsafe { &*binding_sig })[..]) {
Ok(sig) => sig,
Expand Down Expand Up @@ -1002,8 +1008,11 @@ pub extern "system" fn librustzcash_sapling_spend_sig(
Err(_) => return false,
};

// Initialize secure RNG
let mut rng = OsRng;

// Do the signing
let sig = spend_sig(ask, ar, unsafe { &*sighash }, &JUBJUB);
let sig = spend_sig(ask, ar, unsafe { &*sighash }, &mut rng, &JUBJUB);

// Write out the signature
sig.write(&mut (unsafe { &mut *result })[..])
Expand All @@ -1019,6 +1028,11 @@ pub extern "system" fn librustzcash_sapling_binding_sig(
sighash: *const [c_uchar; 32],
result: *mut [c_uchar; 64],
) -> bool {
let value_balance = match Amount::from_i64(value_balance) {
Ok(vb) => vb,
Err(()) => return false,
};

// Sign
let sig = match unsafe { &*ctx }.binding_sig(value_balance, unsafe { &*sighash }, &JUBJUB) {
Ok(s) => s,
Expand Down
1 change: 1 addition & 0 deletions zcash_primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fpe = "0.1"
hex = "0.3"
lazy_static = "1"
pairing = { path = "../pairing" }
rand = "0.7"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a dependency on rand separately from rand_core and rand_os because I wanted to access rand::seq::SliceRandom for shuffling the order of spends and outputs. I didn't bother replacing all the usages of rand_core and rand_os because it is unnecessary noise.

rand_core = "0.5"
rand_os = "0.2"
sapling-crypto = { path = "../sapling-crypto" }
Expand Down
168 changes: 168 additions & 0 deletions zcash_primitives/src/legacy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
//! 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;

/// Minimal subset of script opcodes.
enum OpCode {
// push value
PushData1 = 0x4c,
PushData2 = 0x4d,
PushData4 = 0x4e,

// stack ops
Dup = 0x76,

// bit logic
Equal = 0x87,
EqualVerify = 0x88,

// crypto
Hash160 = 0xa9,
CheckSig = 0xac,
}

/// 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sucks air through teeth at repeating this C++ antipattern

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use a method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to support concatenating different types with the same builder method (instead of a custom method per type), I would need to implement my own trait, which would result in more code than this. I also felt there was a benefit in having this legacy part be as close to the C++ code as possible for reviewability.

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
}
}

/// A transparent address corresponding to either a public key or a `Script`.
#[derive(Debug, PartialEq)]
pub enum TransparentAddress {
PublicKey([u8; 20]),
Script([u8; 20]),
}

impl TransparentAddress {
/// Generate the `scriptPubKey` corresponding to this address.
pub fn script(&self) -> Script {
match self {
TransparentAddress::PublicKey(key_id) => {
// P2PKH script
Script::default()
<< OpCode::Dup
<< OpCode::Hash160
<< &key_id[..]
<< OpCode::EqualVerify
<< OpCode::CheckSig
}
TransparentAddress::Script(script_id) => {
// P2SH script
Script::default() << OpCode::Hash160 << &script_id[..] << OpCode::Equal
}
}
}
}

#[cfg(test)]
mod tests {
use super::{OpCode, Script, TransparentAddress};

#[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[..]);
}
}

#[test]
fn p2pkh() {
let addr = TransparentAddress::PublicKey([4; 20]);
assert_eq!(
&addr.script().0,
&[
0x76, 0xa9, 0x14, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x88, 0xac,
]
)
}

#[test]
fn p2sh() {
let addr = TransparentAddress::Script([7; 20]);
assert_eq!(
&addr.script().0,
&[
0xa9, 0x14, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x87,
]
)
}
}
3 changes: 3 additions & 0 deletions zcash_primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ extern crate ff;
extern crate fpe;
extern crate hex;
extern crate pairing;
extern crate rand;
extern crate rand_core;
extern crate rand_os;
extern crate sapling_crypto;
Expand All @@ -18,8 +19,10 @@ 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;
pub mod sapling;
mod serialize;
pub mod transaction;
Expand Down
2 changes: 1 addition & 1 deletion zcash_primitives/src/merkle_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ impl<Node: Hashable> IncrementalWitness<Node> {

/// A witness to a path from a position in a particular commitment tree to the root of
/// that tree.
#[derive(Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub struct CommitmentTreeWitness<Node: Hashable> {
pub auth_path: Vec<Option<(Node, bool)>>,
pub position: u64,
Expand Down
Loading