diff --git a/Cargo.toml b/Cargo.toml index 3bfd0677f2..a66aba1d7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = [ "group", "pairing", "zcash_client_backend", - "zcash_extensions_api", + "zcash_extensions", "zcash_history", "zcash_primitives", "zcash_proofs", diff --git a/zcash_extensions_api/Cargo.toml b/zcash_extensions/Cargo.toml similarity index 53% rename from zcash_extensions_api/Cargo.toml rename to zcash_extensions/Cargo.toml index 2deb5dadf4..5cb922ba14 100644 --- a/zcash_extensions_api/Cargo.toml +++ b/zcash_extensions/Cargo.toml @@ -1,11 +1,13 @@ [package] -name = "zcash_extensions_api" +name = "zcash_extensions" description = "TBD" version = "0.0.0" -authors = ["Jack Grigg "] +authors = ["Jack Grigg ", "Kris Nuttycombe "] homepage = "https://github.com/zcash/librustzcash" repository = "https://github.com/zcash/librustzcash" license = "MIT OR Apache-2.0" edition = "2018" [dependencies] +blake2b_simd = "0.5" +zcash_primitives = { version = "0.2", path = "../zcash_primitives" } diff --git a/zcash_extensions_api/src/lib.rs b/zcash_extensions/src/consensus.rs similarity index 100% rename from zcash_extensions_api/src/lib.rs rename to zcash_extensions/src/consensus.rs diff --git a/zcash_primitives/src/consensus/extensions/transparent.rs b/zcash_extensions/src/consensus/transparent.rs similarity index 92% rename from zcash_primitives/src/consensus/extensions/transparent.rs rename to zcash_extensions/src/consensus/transparent.rs index 2c3e21745d..c0f1ab20af 100644 --- a/zcash_primitives/src/consensus/extensions/transparent.rs +++ b/zcash_extensions/src/consensus/transparent.rs @@ -1,11 +1,11 @@ //! Consensus logic for Transparent Zcash Extensions. use std::convert::TryFrom; -use zcash_extensions_api::transparent::{Error, Extension, Precondition, Witness}; +use zcash_primitives::transaction::components::TzeOut; +use zcash_primitives::transaction::Transaction; +use zcash_primitives::extensions::transparent::{Error, Extension, Precondition, Witness}; -use crate::extensions::transparent::demo; -use crate::transaction::components::TzeOut; -use crate::transaction::Transaction; +use crate::transparent::demo; /// The set of programs that have assigned type IDs within the Zcash consensus rules. #[derive(Debug, Clone, Copy)] diff --git a/zcash_extensions/src/lib.rs b/zcash_extensions/src/lib.rs new file mode 100644 index 0000000000..19f72ffcaf --- /dev/null +++ b/zcash_extensions/src/lib.rs @@ -0,0 +1,3 @@ +pub mod transparent; +pub mod consensus; + diff --git a/zcash_extensions/src/transparent.rs b/zcash_extensions/src/transparent.rs new file mode 100644 index 0000000000..48aca679f4 --- /dev/null +++ b/zcash_extensions/src/transparent.rs @@ -0,0 +1,3 @@ +//! Zcash transparent extensions. + +pub mod demo; diff --git a/zcash_primitives/src/extensions/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs similarity index 81% rename from zcash_primitives/src/extensions/transparent/demo.rs rename to zcash_extensions/src/transparent/demo.rs index cadd020dfd..168c62a2d8 100644 --- a/zcash_primitives/src/extensions/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -21,9 +21,9 @@ use blake2b_simd::Params; use std::convert::TryFrom; use std::fmt; -use zcash_extensions_api::transparent::{Extension, FromPayload, ToPayload}; -use crate::transaction::components::TzeOut; +use zcash_primitives::transaction::components::{OutPoint, TzeOut, amount::Amount}; +use zcash_primitives::extensions::transparent::{Extension, FromPayload, ToPayload, ExtensionTxBuilder}; mod open { pub const MODE: usize = 0; @@ -273,6 +273,94 @@ impl Extension for Program { } } +fn builder_hashes(preimage_1: &[u8; 32], preimage_2: &[u8; 32]) -> ([u8; 32], [u8; 32]) { + let hash_2 = { + let mut hash = [0; 32]; + hash.copy_from_slice(Params::new().hash_length(32).hash(preimage_2).as_bytes()); + hash + }; + + let hash_1 = { + let mut hash = [0; 32]; + hash.copy_from_slice( + Params::new() + .hash_length(32) + .to_state() + .update(preimage_1) + .update(&hash_2) + .finalize() + .as_bytes(), + ); + hash + }; + + (hash_1, hash_2) +} + +pub struct DemoBuilder<'a, B> { + txn_builder: &'a mut B, + extension_id: usize, +} + +impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<'a, B> { + pub fn demo_open( + &mut self, + value: Amount, + preimage_1: [u8; 32], + preimage_2: [u8; 32] + ) -> Result<(), B::BuildError> { + let (hash_1, _) = builder_hashes(&preimage_1, &preimage_2); + + // Call through to the generic builder. + self.txn_builder.add_tze_output( + self.extension_id, + value, + &Precondition::open(hash_1), + ) + } + + pub fn demo_transfer_to_close( + &mut self, + prevout: (OutPoint, TzeOut), + transfer_amount: Amount, + preimage_1: [u8; 32], + preimage_2: [u8; 32] + ) -> Result<(), B::BuildError> { + let (_, hash_2) = builder_hashes(&preimage_1, &preimage_2); + + // should we eagerly validate the relationship between prevout.1 and preimage_1? + self.txn_builder.add_tze_input( + self.extension_id, + prevout, + move |_| Ok(Witness::open(preimage_1)) + )?; + + self.txn_builder.add_tze_output( + self.extension_id, + transfer_amount, // can this be > prevout.1.value? + &Precondition::close(hash_2) + ) + } + + pub fn demo_close( + &mut self, + prevout: (OutPoint, TzeOut), + preimage: [u8; 32] + ) -> Result<(), B::BuildError> { + let hash_2 = { + let mut hash = [0; 32]; + hash.copy_from_slice(Params::new().hash_length(32).hash(&preimage).as_bytes()); + hash + }; + + self.txn_builder.add_tze_input( + self.extension_id, + prevout, + move |_| Ok(Witness::close(hash_2)) + ) + } +} + #[cfg(test)] mod tests { use blake2b_simd::Params; @@ -382,31 +470,52 @@ mod tests { hash }; - let mut mtx_a = TransactionData::nu4(); - mtx_a.tze_outputs.push(TzeOut { + // + // Opening transaction + // + + let out_a = TzeOut { value: Amount::from_u64(1).unwrap(), precondition: tze::Precondition::from(0, &Precondition::open(hash_1)), - }); + }; + + println!("{:x?}", precondition.payload); + + let mut mtx_a = TransactionData::nu4(); + mtx_a.tze_outputs.push(out_a); let tx_a = mtx_a.freeze().unwrap(); - let mut mtx_b = TransactionData::nu4(); - mtx_b.tze_inputs.push(TzeIn { + // + // Transfer + // + + let in_b = TzeIn { prevout: OutPoint::new(tx_a.txid().0, 0), witness: tze::Witness::from(0, &Witness::open(preimage_1)), - }); - mtx_b.tze_outputs.push(TzeOut { + }; + let out_b = TzeOut { value: Amount::from_u64(1).unwrap(), precondition: tze::Precondition::from(0, &Precondition::close(hash_2)), - }); + }; + let mut mtx_b = TransactionData::nu4(); + mtx_b.tze_inputs.push(in_b); + mtx_b.tze_outputs.push(out_b); let tx_b = mtx_b.freeze().unwrap(); - let mut mtx_c = TransactionData::nu4(); - mtx_c.tze_inputs.push(TzeIn { + // + // Closing transaction + // + + let in_c = TzeIn { prevout: OutPoint::new(tx_b.txid().0, 0), witness: tze::Witness::from(0, &Witness::close(preimage_2)), - }); + }; + + let mut mtx_c = TransactionData::nu4(); + mtx_c.tze_inputs.push(in_c); let tx_c = mtx_c.freeze().unwrap(); + // Verify tx_b { let ctx = Ctx { tx: &tx_b }; diff --git a/zcash_extensions_api/src/transparent.rs b/zcash_extensions_api/src/transparent.rs deleted file mode 100644 index 028075f192..0000000000 --- a/zcash_extensions_api/src/transparent.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! Core traits and structs for Transparent Zcash Extensions. - -use std::fmt; - -pub trait FromPayload: Sized { - /// Parses an extension type from a mode and payload. - fn from_payload(mode: usize, payload: &[u8]) -> Result; -} - -pub trait ToPayload { - /// Returns a serialized payload and its corresponding mode. - fn to_payload(&self) -> (usize, Vec); -} - -/// A condition that can be used to encumber transparent funds. -#[derive(Debug)] -pub struct Precondition { - pub extension_id: usize, - pub mode: usize, - pub payload: Vec, -} - -impl Precondition { - pub fn from(extension_id: usize, value: &P) -> Precondition { - let (mode, payload) = value.to_payload(); - Precondition { - extension_id, - mode, - payload, - } - } -} - -/// Data that satisfies the precondition for prior encumbered funds, enabling them to be -/// spent. -#[derive(Debug)] -pub struct Witness { - pub extension_id: usize, - pub mode: usize, - pub payload: Vec, -} - -impl Witness { - pub fn from(extension_id: usize, value: &P) -> Witness { - let (mode, payload) = value.to_payload(); - Witness { - extension_id, - mode, - payload, - } - } -} - -#[derive(Debug, PartialEq)] -pub enum Error { - InvalidForEpoch(u32, usize), - InvalidExtensionId(usize), - ProgramError(E), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::InvalidForEpoch(cid, ptype) => write!( - f, - "Program type {} is invalid for consensus branch id {}", - ptype, cid - ), - - Error::InvalidExtensionId(extension_id) => { - write!(f, "Unrecognized program type id {}", extension_id) - } - - Error::ProgramError(err) => write!(f, "Program error: {}", err), - } - } -} - -pub trait Extension { - type P; - type W; - type Error; - - fn verify_inner( - &self, - precondition: &Self::P, - witness: &Self::W, - context: &C, - ) -> Result<(), Self::Error>; - - fn verify( - &self, - precondition: &Precondition, - witness: &Witness, - context: &C, - ) -> Result<(), Self::Error> - where - Self::P: FromPayload, - Self::W: FromPayload, - { - self.verify_inner( - &Self::P::from_payload(precondition.mode, &precondition.payload)?, - &Self::W::from_payload(witness.mode, &witness.payload)?, - &context, - ) - } -} diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 11a00c8b80..b40744db01 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -29,7 +29,6 @@ ripemd160 = { version = "0.8", optional = true } secp256k1 = { version = "=0.15.0", optional = true } sha2 = "0.8" subtle = "2.2.1" -zcash_extensions_api = { version = "0.0", path = "../zcash_extensions_api" } [dev-dependencies] criterion = "0.3" diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 42657c35c9..67d328e589 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -3,8 +3,6 @@ use std::convert::TryFrom; use std::fmt; -pub mod extensions; - /// Zcash consensus parameters. pub trait Parameters { fn activation_height(nu: NetworkUpgrade) -> Option; diff --git a/zcash_primitives/src/consensus/extensions.rs b/zcash_primitives/src/consensus/extensions.rs deleted file mode 100644 index 6210d3fcdd..0000000000 --- a/zcash_primitives/src/consensus/extensions.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Consensus logic for Zcash extensions. - -pub mod transparent; diff --git a/zcash_primitives/src/extensions.rs b/zcash_primitives/src/extensions.rs index 1aa5804f34..7d540a8bac 100644 --- a/zcash_primitives/src/extensions.rs +++ b/zcash_primitives/src/extensions.rs @@ -1,3 +1 @@ -//! Zcash extensions. - pub mod transparent; diff --git a/zcash_primitives/src/extensions/transparent.rs b/zcash_primitives/src/extensions/transparent.rs index 48aca679f4..bf83dc26f3 100644 --- a/zcash_primitives/src/extensions/transparent.rs +++ b/zcash_primitives/src/extensions/transparent.rs @@ -1,3 +1,158 @@ -//! Zcash transparent extensions. +//! Core traits and structs for Transparent Zcash Extensions. + +use std::fmt; +use crate::transaction::components::{Amount, OutPoint, TzeOut}; + +pub trait FromPayload: Sized { + /// Parses an extension type from a mode and payload. + fn from_payload(mode: usize, payload: &[u8]) -> Result; +} + +pub trait ToPayload { + /// Returns a serialized payload and its corresponding mode. + fn to_payload(&self) -> (usize, Vec); +} + +/// A condition that can be used to encumber transparent funds. +#[derive(Clone, Debug)] +pub struct Precondition { + pub extension_id: usize, + pub mode: usize, + pub payload: Vec, +} + +impl Precondition { + pub fn from(extension_id: usize, value: &P) -> Precondition { + let (mode, payload) = value.to_payload(); + Precondition { + extension_id, + mode, + payload, + } + } +} + +/// Data that satisfies the precondition for prior encumbered funds, enabling them to be +/// spent. +#[derive(Clone, Debug)] +pub struct Witness { + pub extension_id: usize, + pub mode: usize, + pub payload: Vec, +} + +impl Witness { + pub fn from(extension_id: usize, value: &P) -> Witness { + let (mode, payload) = value.to_payload(); + Witness { + extension_id, + mode, + payload, + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Error { + InvalidForEpoch(u32, usize), + InvalidExtensionId(usize), + ProgramError(E), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::InvalidForEpoch(cid, ptype) => write!( + f, + "Program type {} is invalid for consensus branch id {}", + ptype, cid + ), + + Error::InvalidExtensionId(extension_id) => { + write!(f, "Unrecognized program type id {}", extension_id) + } + + Error::ProgramError(err) => write!(f, "Program error: {}", err), + } + } +} + +pub trait Extension { + type P; + type W; + type Error; + + fn verify_inner( + &self, + precondition: &Self::P, + witness: &Self::W, + context: &C, + ) -> Result<(), Self::Error>; + + fn verify( + &self, + precondition: &Precondition, + witness: &Witness, + context: &C, + ) -> Result<(), Self::Error> + where + Self::P: FromPayload, + Self::W: FromPayload, + { + self.verify_inner( + &Self::P::from_payload(precondition.mode, &precondition.payload)?, + &Self::W::from_payload(witness.mode, &witness.payload)?, + &context, + ) + } +} + +// pub trait WitnessBuilder { +// type Error; +// type Witness: ToPayload; +// +// fn build_witness(ctx: BuildCtx) -> Result; +// } + +// This extension trait is satisfied by the transaction::builder::Builder type. It provides a +// minimal contract for interacting with the transaction builder, that extension library authors +// can use to add extension-specific builder traits that may be used to interact with the +// transaction builder. This may make it simpler for projects that include transaction-builder +// functionality to integrate with third-party extensions without those extensions being coupled to +// a particular transaction or builder representation. +pub trait ExtensionTxBuilder<'a> { + type BuildCtx; + type BuildError; + + fn add_tze_input( + &mut self, + extension_id: usize, + prevout: (OutPoint, TzeOut), + witness_builder: WBuilder + ) -> Result<(), Self::BuildError> + where WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result); + //where WBuilder: WitnessBuilder; + + fn add_tze_output( + &mut self, + extension_id: usize, + value: Amount, + guarded_by: &P, + ) -> Result<(), Self::BuildError>; +} + +pub trait Epoch { + type VerifyError; + + // Implementation of this method should check that the provided witness + // satisfies the specified precondition, given the context. This verification + // becomes part of the consensus rules. + fn verify( + &self, + precondition: &Precondition, + witness: &Witness, + ctx: &VerifyCtx + ) -> Result<(), Error>; +} + -pub mod demo; diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index d45c546e10..16b961c296 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -9,8 +9,8 @@ use lazy_static::lazy_static; pub mod block; -pub mod consensus; pub mod constants; +pub mod consensus; pub mod extensions; pub mod group_hash; pub mod jubjub; diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 9acb996d41..cd07c8d9e3 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -1,9 +1,11 @@ //! Structs for building transactions. +use std::boxed::Box; use crate::zip32::ExtendedSpendingKey; use crate::{ jubjub::fs::Fs, primitives::{Diversifier, Note, PaymentAddress}, + extensions::transparent::ExtensionTxBuilder, }; use ff::Field; use pairing::bls12_381::{Bls12, Fr}; @@ -12,6 +14,7 @@ use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore}; use crate::{ consensus, keys::OutgoingViewingKey, + extensions::transparent::{self as tze, ToPayload}, legacy::TransparentAddress, merkle_tree::MerklePath, note_encryption::{generate_esk, Memo, SaplingNoteEncryption}, @@ -19,7 +22,10 @@ use crate::{ redjubjub::PrivateKey, sapling::{spend_sig, Node}, transaction::{ - components::{amount::DEFAULT_FEE, Amount, OutputDescription, SpendDescription, TxOut}, + components::{ + amount::DEFAULT_FEE, amount::Amount, + OutputDescription, SpendDescription, TxOut, TzeIn, TzeOut, OutPoint + }, signature_hash_data, Transaction, TransactionData, SIGHASH_ALL, }, JUBJUB, @@ -167,15 +173,16 @@ impl TransparentInputs { #[cfg(feature = "transparent-inputs")] fn push( &mut self, - mtx: &mut TransactionData, sk: secp256k1::SecretKey, - utxo: OutPoint, coin: TxOut, ) -> Result<(), Error> { if coin.value.is_negative() { return Err(Error::InvalidAmount); } + // ensure that the ripemd160 digest of the public key associated with the + // provided secret key matches that of the address to which the provided + // output may be spent let pubkey = secp256k1::PublicKey::from_secret_key(&self.secp, &sk).serialize(); match coin.script_pubkey.address() { Some(TransparentAddress::PublicKey(hash)) => { @@ -189,7 +196,6 @@ impl TransparentInputs { _ => return Err(Error::InvalidAddress), } - mtx.vin.push(TxIn::new(utxo)); self.inputs.push(TransparentInputInfo { sk, pubkey, coin }); Ok(()) @@ -223,6 +229,7 @@ impl TransparentInputs { consensus_branch_id, SIGHASH_ALL, Some((i, &info.coin.script_pubkey, info.coin.value)), + // tze equivalent is ??? )); let msg = secp256k1::Message::from_slice(&sighash).expect("32 bytes"); @@ -241,6 +248,38 @@ impl TransparentInputs { fn apply_signatures(&self, _: &mut TransactionData, _: consensus::BranchId) {} } + +struct TzeInputInfo<'a, BuildCtx> { + prevout: TzeOut, + builder: Box Result + 'a>, +} + +struct TzeInputs<'a, BuildCtx> { + builders: Vec>, +} + +impl<'a, BuildCtx> TzeInputs<'a, BuildCtx> { + fn push( + &mut self, + extension_id: usize, + prevout: (OutPoint, TzeOut), + builder: WBuilder) + where WBuilder: 'a + FnOnce(&BuildCtx) -> Result { + let (outpoint, tzeout) = prevout; + self.builders.push(TzeInputInfo { + prevout: tzeout, + builder: Box::new( + move |ctx| { + let (mode, payload) = builder(&ctx).map(|x| x.to_payload())?; + Ok(TzeIn { + prevout: outpoint, + witness: tze::Witness { extension_id, mode, payload } + }) + }) + }); + } +} + /// Metadata about a transaction created by a [`Builder`]. #[derive(Debug, PartialEq)] pub struct TransactionMetadata { @@ -280,7 +319,7 @@ impl TransactionMetadata { } /// Generates a [`Transaction`] from its inputs and outputs. -pub struct Builder { +pub struct Builder<'a, R: RngCore + CryptoRng> { rng: R, mtx: TransactionData, fee: Amount, @@ -288,10 +327,11 @@ pub struct Builder { spends: Vec, outputs: Vec, transparent_inputs: TransparentInputs, + tze_inputs: TzeInputs<'a, TransactionData>, change_address: Option<(OutgoingViewingKey, PaymentAddress)>, } -impl Builder { +impl Builder<'_, OsRng> { /// Creates a new `Builder` targeted for inclusion in the block with the given height, /// using default values for general transaction fields and the default OS random. /// @@ -306,7 +346,7 @@ impl Builder { } } -impl Builder { +impl<'a, R: RngCore + CryptoRng> Builder<'a, R> { /// Creates a new `Builder` targeted for inclusion in the block with the given height /// and randomness source, using default values for general transaction fields. /// @@ -316,7 +356,7 @@ impl Builder { /// expiry delta (20 blocks). /// /// The fee will be set to the default fee (0.0001 ZEC). - pub fn new_with_rng(height: u32, rng: R) -> Builder { + pub fn new_with_rng(height: u32, rng: R) -> Builder<'a, R> { let mut mtx = TransactionData::new(); mtx.expiry_height = height + DEFAULT_TX_EXPIRY_DELTA; @@ -328,6 +368,7 @@ impl Builder { spends: vec![], outputs: vec![], transparent_inputs: TransparentInputs::default(), + tze_inputs: TzeInputs { builders: vec![] }, change_address: None, } } @@ -394,7 +435,8 @@ impl Builder { utxo: OutPoint, coin: TxOut, ) -> Result<(), Error> { - self.transparent_inputs.push(&mut self.mtx, sk, utxo, coin) + self.mtx.vin.push(TxIn::new(utxo)); + self.transparent_inputs.push(sk, coin) } /// Adds a transparent address to send funds to. @@ -436,7 +478,8 @@ impl Builder { mut self, consensus_branch_id: consensus::BranchId, prover: &impl TxProver, - ) -> Result<(Transaction, TransactionMetadata), Error> { + // epoch: &Epoch + ) -> Result<(Transaction, TransactionMetadata), Error> { let mut tx_metadata = TransactionMetadata::new(); // @@ -444,13 +487,14 @@ impl Builder { // // Valid change - let change = self.mtx.value_balance - self.fee + self.transparent_inputs.value_sum() - - self - .mtx - .vout - .iter() - .map(|output| output.value) - .sum::(); + let change = + self.mtx.value_balance + - self.fee + + self.transparent_inputs.value_sum() + - self.mtx.vout.iter().map(|vo| vo.value).sum::() + + self.tze_inputs.builders.iter().map(|ein| ein.prevout.value).sum::() + - self.mtx.tze_outputs.iter().map(|tzo| tzo.value).sum::(); + if change.is_negative() { return Err(Error::ChangeIsNegative(change)); } @@ -623,7 +667,7 @@ impl Builder { } // - // Signatures + // Signatures -- all effects must have been applied. // let mut sighash = [0u8; 32]; @@ -650,6 +694,19 @@ impl Builder { .map_err(|()| Error::BindingSig)?, ); + // // Create TZE input witnesses + for tze_in in self.tze_inputs.builders { + // Need to enable witness to commit to the amount. + // - So hardware wallets "know" the amount without having to be sent all the + // prior TZE outputs to which this witness gives evidence. + // + // The witness is expected to commit to the precommitment internally? + // (Or make it part of the sighash?) + // - TODO: Check whether transparent inputs committing to script_pubkey was + // only so that hardware wallets "knew" what address was being spent from. + self.mtx.tze_inputs.push((tze_in.builder)(&self.mtx)?); + } + // Transparent signatures self.transparent_inputs .apply_signatures(&mut self.mtx, consensus_branch_id); @@ -661,6 +718,49 @@ impl Builder { } } + + +impl<'a, R: RngCore + CryptoRng> ExtensionTxBuilder<'a> for Builder<'a, R> { + type BuildCtx = TransactionData; + type BuildError = Error; + + fn add_tze_input( + &mut self, + extension_id: usize, + prevout: (OutPoint, TzeOut), + witness_builder: WBuilder + ) -> Result<(), Self::BuildError> + where WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result) { + // where WBuilder: WitnessBuilder { + self.tze_inputs.push(extension_id, prevout, witness_builder); + Ok(()) + } + + fn add_tze_output( + &mut self, + extension_id: usize, + value: Amount, + guarded_by: &P + ) -> Result<(), Self::BuildError> { + if value.is_negative() { + return Err(Error::InvalidAmount); + } + + let (mode, payload) = guarded_by.to_payload(); + self.mtx.tze_outputs.push(TzeOut { + value, + precondition: tze::Precondition { + extension_id, + mode, + payload, + }, + }); + + Ok(()) + } +} + + #[cfg(test)] mod tests { use ff::{Field, PrimeField}; diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index cfc9aa2285..ff8ad53918 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -5,8 +5,8 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use ff::{PrimeField, PrimeFieldRepr}; use pairing::bls12_381::{Bls12, Fr, FrRepr}; use std::io::{self, Read, Write}; -use zcash_extensions_api::transparent as tze; +use crate::extensions::transparent as tze; use crate::legacy::Script; use crate::redjubjub::{PublicKey, Signature}; use crate::serialize::{CompactSize, Vector}; @@ -111,7 +111,7 @@ impl TxOut { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct TzeIn { pub prevout: OutPoint, pub witness: tze::Witness, @@ -144,7 +144,7 @@ impl TzeIn { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct TzeOut { pub value: Amount, pub precondition: tze::Precondition, @@ -178,7 +178,9 @@ impl TzeOut { CompactSize::write(&mut writer, self.precondition.extension_id)?; CompactSize::write(&mut writer, self.precondition.mode)?; - Vector::write(&mut writer, &self.precondition.payload, |w, b| w.write_u8(*b)) + Vector::write(&mut writer, &self.precondition.payload, |w, b| { + w.write_u8(*b) + }) } }