2018-11-20 05:37:21 -08:00
//! Structs for building transactions.
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
2020-05-06 16:52:03 -07:00
use std ::boxed ::Box ;
2020-09-23 09:53:48 -07:00
2019-03-08 19:20:32 -08:00
use std ::error ;
use std ::fmt ;
2020-08-05 20:36:02 -07:00
use std ::marker ::PhantomData ;
2021-04-26 10:14:01 -07:00
use std ::sync ::mpsc ::Sender ;
2018-11-20 05:37:21 -08:00
2020-05-06 16:52:03 -07:00
use ff ::Field ;
use rand ::{ rngs ::OsRng , seq ::SliceRandom , CryptoRng , RngCore } ;
2018-11-20 05:37:21 -08:00
use crate ::{
2020-08-05 13:08:58 -07:00
consensus ::{ self , BlockHeight } ,
2019-05-24 05:32:55 -07:00
legacy ::TransparentAddress ,
2020-10-29 09:48:26 -07:00
memo ::MemoBytes ,
2020-02-07 16:25:24 -08:00
merkle_tree ::MerklePath ,
2021-03-04 16:45:45 -08:00
sapling ::{
2021-04-01 13:08:17 -07:00
keys ::OutgoingViewingKey ,
note_encryption ::sapling_note_encryption ,
prover ::TxProver ,
redjubjub ::{ PrivateKey , Signature } ,
spend_sig_internal ,
util ::generate_random_rseed_internal ,
2021-03-04 13:45:41 -08:00
Diversifier , Node , Note , PaymentAddress ,
2021-03-04 16:45:45 -08:00
} ,
2018-11-20 05:37:21 -08:00
transaction ::{
2020-05-06 16:52:03 -07:00
components ::{
2021-03-04 13:40:45 -08:00
amount ::{ Amount , DEFAULT_FEE } ,
2021-05-11 13:17:54 -07:00
transparent ::builder ::{ self as transparent , TransparentBuilder } ,
OutputDescription , SpendDescription ,
2020-05-06 16:52:03 -07:00
} ,
2021-04-01 13:08:17 -07:00
signature_hash_data , SignableInput , Transaction , TransactionData , TxVersion , SIGHASH_ALL ,
2018-11-20 05:37:21 -08:00
} ,
2020-05-06 16:52:03 -07:00
zip32 ::ExtendedSpendingKey ,
2018-11-20 05:37:21 -08:00
} ;
2019-07-31 08:20:13 -07:00
#[ cfg(feature = " transparent-inputs " ) ]
2021-05-11 13:17:54 -07:00
use crate ::transaction ::components ::{ OutPoint , TxOut } ;
2019-07-31 08:20:13 -07:00
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
use crate ::{
extensions ::transparent ::{ self as tze , ExtensionTxBuilder , ToPayload } ,
2021-03-04 08:35:53 -08:00
transaction ::components ::{ TzeIn , TzeOut , TzeOutPoint } ,
2020-09-23 09:53:48 -07:00
} ;
2020-10-21 11:37:21 -07:00
#[ cfg(any(test, feature = " test-dependencies " )) ]
2021-03-04 09:48:04 -08:00
use crate ::sapling ::prover ::mock ::MockTxProver ;
2020-10-21 11:37:21 -07:00
2018-11-20 05:37:21 -08:00
const DEFAULT_TX_EXPIRY_DELTA : u32 = 20 ;
/// If there are any shielded inputs, always have at least two shielded outputs, padding
2021-03-25 13:49:27 -07:00
/// with dummy outputs if necessary. See <https://github.com/zcash/zcash/issues/3615>.
2018-11-20 05:37:21 -08:00
const MIN_SHIELDED_OUTPUTS : usize = 2 ;
#[ derive(Debug, PartialEq) ]
2019-08-13 07:10:57 -07:00
pub enum Error {
2018-11-20 05:37:21 -08:00
AnchorMismatch ,
BindingSig ,
2019-07-25 12:53:42 -07:00
ChangeIsNegative ( Amount ) ,
2018-11-20 05:37:21 -08:00
InvalidAddress ,
InvalidAmount ,
NoChangeAddress ,
SpendProof ,
2020-09-09 09:54:18 -07:00
TzeWitnessModeMismatch ( u32 , u32 ) ,
2021-05-11 13:17:54 -07:00
TransparentBuildError ( transparent ::Error ) ,
2018-11-20 05:37:21 -08:00
}
2019-03-08 19:20:32 -08:00
impl fmt ::Display for Error {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
match self {
Error ::AnchorMismatch = > {
write! ( f , " Anchor mismatch (anchors for all spends must be equal) " )
}
Error ::BindingSig = > write! ( f , " Failed to create bindingSig " ) ,
Error ::ChangeIsNegative ( amount ) = > {
write! ( f , " Change is negative ({:?} zatoshis) " , amount )
}
Error ::InvalidAddress = > write! ( f , " Invalid address " ) ,
Error ::InvalidAmount = > write! ( f , " Invalid amount " ) ,
Error ::NoChangeAddress = > write! ( f , " No change address specified or discoverable " ) ,
Error ::SpendProof = > write! ( f , " Failed to create Sapling spend proof " ) ,
2020-09-09 09:54:18 -07:00
Error ::TzeWitnessModeMismatch ( expected , actual ) = >
write! ( f , " TZE witness builder returned a mode that did not match the mode with which the input was initially constructed: expected = {:?}, actual = {:?} " , expected , actual ) ,
2021-05-11 13:17:54 -07:00
Error ::TransparentBuildError ( err ) = > err . fmt ( f ) ,
2019-03-08 19:20:32 -08:00
}
}
}
impl error ::Error for Error { }
2018-11-20 05:37:21 -08:00
struct SpendDescriptionInfo {
extsk : ExtendedSpendingKey ,
diversifier : Diversifier ,
2020-07-01 13:26:54 -07:00
note : Note ,
alpha : jubjub ::Fr ,
2020-02-07 16:25:24 -08:00
merkle_path : MerklePath < Node > ,
2018-11-20 05:37:21 -08:00
}
2021-04-01 13:08:17 -07:00
#[ derive(Clone) ]
2021-03-17 17:22:21 -07:00
pub struct SaplingOutput < P : consensus ::Parameters > {
2020-08-28 08:12:37 -07:00
/// `None` represents the `ovk = ⊥` case.
ovk : Option < OutgoingViewingKey > ,
2020-07-01 13:26:54 -07:00
to : PaymentAddress ,
note : Note ,
2020-10-29 09:48:26 -07:00
memo : MemoBytes ,
2021-03-17 17:22:21 -07:00
_params : PhantomData < P > ,
2018-11-20 05:37:21 -08:00
}
2021-03-17 17:22:21 -07:00
impl < P : consensus ::Parameters > SaplingOutput < P > {
pub fn new < R : RngCore + CryptoRng > (
2020-08-05 13:27:40 -07:00
params : & P ,
2021-04-01 13:08:17 -07:00
target_height : BlockHeight ,
2018-11-20 05:37:21 -08:00
rng : & mut R ,
2020-08-28 08:12:37 -07:00
ovk : Option < OutgoingViewingKey > ,
2020-07-01 13:26:54 -07:00
to : PaymentAddress ,
2018-11-20 05:37:21 -08:00
value : Amount ,
2020-10-29 09:48:26 -07:00
memo : Option < MemoBytes > ,
2020-10-21 11:37:21 -07:00
) -> Result < Self , Error > {
2021-04-01 13:08:17 -07:00
Self ::new_internal ( params , target_height , rng , ovk , to , value , memo )
2020-10-21 11:37:21 -07:00
}
2021-03-17 17:22:21 -07:00
fn new_internal < R : RngCore > (
2020-10-21 11:37:21 -07:00
params : & P ,
2021-04-01 13:08:17 -07:00
target_height : BlockHeight ,
2020-10-21 11:37:21 -07:00
rng : & mut R ,
ovk : Option < OutgoingViewingKey > ,
to : PaymentAddress ,
value : Amount ,
2020-10-29 09:48:26 -07:00
memo : Option < MemoBytes > ,
2018-11-20 05:37:21 -08:00
) -> Result < Self , Error > {
2021-01-28 12:40:07 -08:00
let g_d = to . g_d ( ) . ok_or ( Error ::InvalidAddress ) ? ;
2019-07-25 12:50:17 -07:00
if value . is_negative ( ) {
2019-08-13 07:10:57 -07:00
return Err ( Error ::InvalidAmount ) ;
2018-11-20 05:37:21 -08:00
}
2021-04-01 13:08:17 -07:00
let rseed = generate_random_rseed_internal ( params , target_height , rng ) ;
2018-11-20 05:37:21 -08:00
let note = Note {
g_d ,
2020-10-30 06:27:49 -07:00
pk_d : * to . pk_d ( ) ,
2019-07-25 14:37:16 -07:00
value : value . into ( ) ,
2020-07-30 08:07:33 -07:00
rseed ,
2018-11-20 05:37:21 -08:00
} ;
Ok ( SaplingOutput {
ovk ,
to ,
note ,
2021-03-17 00:47:11 -07:00
memo : memo . unwrap_or_else ( MemoBytes ::empty ) ,
2021-03-17 17:22:21 -07:00
_params : PhantomData ::default ( ) ,
2018-11-20 05:37:21 -08:00
} )
}
2021-03-17 17:22:21 -07:00
pub fn build < Pr : TxProver , R : RngCore + CryptoRng > (
2018-11-20 05:37:21 -08:00
self ,
2021-03-17 17:22:21 -07:00
prover : & Pr ,
ctx : & mut Pr ::SaplingProvingContext ,
2019-06-12 15:11:06 -07:00
rng : & mut R ,
2018-11-20 05:37:21 -08:00
) -> OutputDescription {
2020-10-21 11:37:21 -07:00
self . build_internal ( prover , ctx , rng )
}
2021-03-17 17:22:21 -07:00
fn build_internal < Pr : TxProver , R : RngCore > (
2020-10-21 11:37:21 -07:00
self ,
2021-03-17 17:22:21 -07:00
prover : & Pr ,
ctx : & mut Pr ::SaplingProvingContext ,
2020-10-21 11:37:21 -07:00
rng : & mut R ,
) -> OutputDescription {
2021-04-08 09:08:00 -07:00
let encryptor = sapling_note_encryption ::< R , P > (
2019-06-12 15:11:06 -07:00
self . ovk ,
self . note . clone ( ) ,
self . to . clone ( ) ,
self . memo ,
2020-08-05 21:47:35 -07:00
rng ,
2019-06-12 15:11:06 -07:00
) ;
2018-11-20 05:37:21 -08:00
let ( zkproof , cv ) = prover . output_proof (
ctx ,
2020-10-30 06:27:49 -07:00
* encryptor . esk ( ) ,
2018-11-20 05:37:21 -08:00
self . to ,
2020-07-29 21:36:12 -07:00
self . note . rcm ( ) ,
2018-11-20 05:37:21 -08:00
self . note . value ,
) ;
2020-08-21 10:33:22 -07:00
let cmu = self . note . cmu ( ) ;
2018-11-20 05:37:21 -08:00
let enc_ciphertext = encryptor . encrypt_note_plaintext ( ) ;
2021-03-17 17:22:21 -07:00
let out_ciphertext = encryptor . encrypt_outgoing_plaintext ( & cv , & cmu , rng ) ;
let ephemeral_key = * encryptor . epk ( ) ;
2018-11-20 05:37:21 -08:00
OutputDescription {
cv ,
cmu ,
ephemeral_key ,
enc_ciphertext ,
out_ciphertext ,
zkproof ,
}
}
}
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
2021-02-03 11:46:47 -08:00
#[ allow(clippy::type_complexity) ]
2021-04-01 13:08:17 -07:00
struct TzeSigner < ' a , BuildCtx > {
2020-05-06 16:52:03 -07:00
prevout : TzeOut ,
2020-09-09 09:54:18 -07:00
builder : Box < dyn FnOnce ( & BuildCtx ) -> Result < ( u32 , Vec < u8 > ) , Error > + ' a > ,
2020-05-06 16:52:03 -07:00
}
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
2021-04-01 13:08:17 -07:00
struct TzeBuilder < ' a , BuildCtx > {
signers : Vec < TzeSigner < ' a , BuildCtx > > ,
tze_inputs : Vec < TzeIn > ,
tze_outputs : Vec < TzeOut > ,
2020-05-06 16:52:03 -07:00
}
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
2021-04-01 13:08:17 -07:00
impl < ' a , BuildCtx > TzeBuilder < ' a , BuildCtx > {
fn new ( ) -> Self {
TzeBuilder {
signers : vec ! [ ] ,
tze_inputs : vec ! [ ] ,
tze_outputs : vec ! [ ] ,
}
2020-05-18 19:27:19 -07:00
}
2021-04-01 13:08:17 -07:00
fn add_input < WBuilder , W : ToPayload > (
& mut self ,
extension_id : u32 ,
mode : u32 ,
( outpoint , prevout ) : ( TzeOutPoint , TzeOut ) ,
witness_builder : WBuilder ,
) where
2020-05-06 16:52:03 -07:00
WBuilder : ' a + FnOnce ( & BuildCtx ) -> Result < W , Error > ,
{
2021-04-01 13:08:17 -07:00
self . tze_inputs
. push ( TzeIn ::new ( outpoint , extension_id , mode ) ) ;
self . signers . push ( TzeSigner {
prevout ,
builder : Box ::new ( move | ctx | witness_builder ( & ctx ) . map ( | x | x . to_payload ( ) ) ) ,
2020-05-06 16:52:03 -07:00
} ) ;
}
2021-04-01 13:08:17 -07:00
fn add_output < G : ToPayload > (
& mut self ,
extension_id : u32 ,
value : Amount ,
guarded_by : & G ,
) -> Result < ( ) , Error > {
if value . is_negative ( ) {
return Err ( Error ::InvalidAmount ) ;
}
let ( mode , payload ) = guarded_by . to_payload ( ) ;
self . tze_outputs . push ( TzeOut {
value ,
precondition : tze ::Precondition {
extension_id ,
mode ,
payload ,
} ,
} ) ;
Ok ( ( ) )
}
fn value_balance ( & self ) -> Option < Amount > {
self . signers
. iter ( )
. map ( | s | s . prevout . value )
. sum ::< Option < Amount > > ( ) ?
- self
. tze_outputs
. iter ( )
. map ( | tzo | tzo . value )
. sum ::< Option < Amount > > ( ) ?
}
fn build ( & self ) -> ( Vec < TzeIn > , Vec < TzeOut > ) {
( self . tze_inputs . clone ( ) , self . tze_outputs . clone ( ) )
}
fn create_signatures ( self , mtx : & BuildCtx ) -> Result < Vec < Vec < u8 > > , Error > {
// Create TZE input witnesses
let tzein = self . tze_inputs ;
let payloads = self
. signers
. into_iter ( )
. enumerate ( )
. map ( | ( i , tze_in ) | {
// The witness builder function should have cached/closed over whatever data was
// necessary for the witness to commit to at the time it was added to the
// transaction builder; here, it then computes those commitments.
let ( mode , payload ) = ( tze_in . builder ) ( & mtx ) ? ;
let input_mode = tzein [ i ] . witness . mode ;
if mode ! = input_mode {
return Err ( Error ::TzeWitnessModeMismatch ( input_mode , mode ) ) ;
}
Ok ( payload )
} )
. collect ::< Result < Vec < _ > , Error > > ( ) ? ;
Ok ( payloads )
}
2020-05-06 16:52:03 -07:00
}
2018-11-20 05:37:21 -08:00
/// Metadata about a transaction created by a [`Builder`].
2019-08-13 07:24:08 -07:00
#[ derive(Debug, PartialEq) ]
2021-04-01 13:08:17 -07:00
pub struct SaplingMetadata {
2018-11-20 05:37:21 -08:00
spend_indices : Vec < usize > ,
output_indices : Vec < usize > ,
}
2021-04-01 13:08:17 -07:00
impl SaplingMetadata {
2018-11-20 05:37:21 -08:00
fn new ( ) -> Self {
2021-04-01 13:08:17 -07:00
SaplingMetadata {
2018-11-20 05:37:21 -08:00
spend_indices : vec ! [ ] ,
output_indices : vec ! [ ] ,
}
}
/// Returns the index within the transaction of the [`SpendDescription`] corresponding
/// to the `n`-th call to [`Builder::add_sapling_spend`].
///
/// Note positions are randomized when building transactions for indistinguishability.
/// This means that the transaction consumer cannot assume that e.g. the first spend
/// they added (via the first call to [`Builder::add_sapling_spend`]) is the first
/// [`SpendDescription`] in the transaction.
pub fn spend_index ( & self , n : usize ) -> Option < usize > {
2019-08-02 03:25:00 -07:00
self . spend_indices . get ( n ) . copied ( )
2018-11-20 05:37:21 -08:00
}
/// Returns the index within the transaction of the [`OutputDescription`] corresponding
/// to the `n`-th call to [`Builder::add_sapling_output`].
///
/// Note positions are randomized when building transactions for indistinguishability.
/// This means that the transaction consumer cannot assume that e.g. the first output
/// they added (via the first call to [`Builder::add_sapling_output`]) is the first
/// [`OutputDescription`] in the transaction.
pub fn output_index ( & self , n : usize ) -> Option < usize > {
2019-08-02 03:25:00 -07:00
self . output_indices . get ( n ) . copied ( )
2018-11-20 05:37:21 -08:00
}
}
2021-05-18 05:45:48 -07:00
/// Reports on the progress made by the builder towards building a transaction.
2021-04-26 10:14:01 -07:00
pub struct Progress {
2021-05-18 05:45:48 -07:00
/// The number of steps completed.
2021-04-26 10:14:01 -07:00
cur : u32 ,
2021-05-18 05:45:48 -07:00
/// The expected total number of steps (as of this progress update), if known.
2021-04-26 10:14:01 -07:00
end : Option < u32 > ,
}
impl Progress {
fn new ( cur : u32 , end : Option < u32 > ) -> Self {
Self { cur , end }
}
2021-05-18 05:45:48 -07:00
/// Returns the number of steps completed so far while building the transaction.
///
/// Note that each step may not be of the same complexity/duration.
2021-04-26 10:14:01 -07:00
pub fn cur ( & self ) -> u32 {
self . cur
}
2021-05-18 05:45:48 -07:00
/// Returns the total expected number of steps before this transaction will be ready,
/// or `None` if the end is unknown as of this progress update.
///
/// Note that each step may not be of the same complexity/duration.
2021-04-26 10:14:01 -07:00
pub fn end ( & self ) -> Option < u32 > {
self . end
}
}
2021-04-01 13:08:17 -07:00
pub struct SaplingBuilder < P : consensus ::Parameters > {
anchor : Option < bls12_381 ::Scalar > ,
value_balance : Amount ,
spends : Vec < SpendDescriptionInfo > ,
outputs : Vec < SaplingOutput < P > > ,
change_address : Option < ( OutgoingViewingKey , PaymentAddress ) > ,
}
impl < P : consensus ::Parameters > SaplingBuilder < P > {
fn new ( ) -> Self {
SaplingBuilder {
anchor : None ,
value_balance : Amount ::zero ( ) ,
spends : vec ! [ ] ,
outputs : vec ! [ ] ,
change_address : None ,
}
}
pub fn value_balance ( & self ) -> Amount {
self . value_balance
}
/// Adds a Sapling note to be spent in this transaction.
///
/// Returns an error if the given Merkle path does not have the same anchor as the
/// paths for previous Sapling notes.
fn add_spend < R : RngCore > (
& mut self ,
mut rng : R ,
extsk : ExtendedSpendingKey ,
diversifier : Diversifier ,
note : Note ,
merkle_path : MerklePath < Node > ,
) -> Result < ( ) , Error > {
// Consistency check: all anchors must equal the first one
let cmu = Node ::new ( note . cmu ( ) . into ( ) ) ;
if let Some ( anchor ) = self . anchor {
let path_root : bls12_381 ::Scalar = merkle_path . root ( cmu ) . into ( ) ;
if path_root ! = anchor {
return Err ( Error ::AnchorMismatch ) ;
}
} else {
self . anchor = Some ( merkle_path . root ( cmu ) . into ( ) )
}
let alpha = jubjub ::Fr ::random ( & mut rng ) ;
self . value_balance + = Amount ::from_u64 ( note . value ) . map_err ( | _ | Error ::InvalidAmount ) ? ;
self . spends . push ( SpendDescriptionInfo {
extsk ,
diversifier ,
note ,
alpha ,
merkle_path ,
} ) ;
Ok ( ( ) )
}
/// Adds a Sapling address to send funds to.
#[ allow(clippy::too_many_arguments) ]
pub fn add_output < R : RngCore > (
& mut self ,
mut rng : R ,
params : & P ,
target_height : BlockHeight ,
ovk : Option < OutgoingViewingKey > ,
to : PaymentAddress ,
value : Amount ,
memo : Option < MemoBytes > ,
) -> Result < ( ) , Error > {
let output =
SaplingOutput ::new_internal ( params , target_height , & mut rng , ovk , to , value , memo ) ? ;
self . value_balance - = value ;
self . outputs . push ( output ) ;
Ok ( ( ) )
}
/// Sets the Sapling address to which any change will be sent.
///
/// By default, change is sent to the Sapling address corresponding to the first note
/// being spent (i.e. the first call to [`Builder::add_sapling_spend`]).
pub fn send_change_to ( & mut self , ovk : OutgoingViewingKey , to : PaymentAddress ) {
self . change_address = Some ( ( ovk , to ) ) ;
}
/// Send change to the specified change address. If no change address
/// was set, send change to the first Sapling address given as input.
pub fn get_change_address ( & self ) -> Result < ( OutgoingViewingKey , PaymentAddress ) , Error > {
if let Some ( change_address ) = & self . change_address {
Ok ( change_address . clone ( ) )
} else if ! self . spends . is_empty ( ) {
PaymentAddress ::from_parts ( self . spends [ 0 ] . diversifier , self . spends [ 0 ] . note . pk_d )
. map ( | addr | ( self . spends [ 0 ] . extsk . expsk . ovk , addr ) )
. ok_or ( Error ::InvalidAddress )
} else {
Err ( Error ::NoChangeAddress )
}
}
pub fn build < Pr : TxProver , R : RngCore > (
& self ,
params : & P ,
prover : & Pr ,
ctx : & mut Pr ::SaplingProvingContext ,
mut rng : R ,
target_height : BlockHeight ,
) -> Result <
(
Vec < SpendDescription > ,
Vec < OutputDescription > ,
SaplingMetadata ,
) ,
Error ,
> {
// Record initial positions of spends and outputs
let mut indexed_spends : Vec < _ > = self . spends . iter ( ) . enumerate ( ) . collect ( ) ;
let mut indexed_outputs : Vec < _ > = self
. outputs
. iter ( )
. enumerate ( )
. map ( | ( i , o ) | Some ( ( i , o ) ) )
. collect ( ) ;
// Set up the transaction metadata that will be used to record how
// inputs and outputs are shuffled.
let mut tx_metadata = SaplingMetadata ::new ( ) ;
tx_metadata . spend_indices . resize ( indexed_spends . len ( ) , 0 ) ;
tx_metadata . output_indices . resize ( indexed_outputs . len ( ) , 0 ) ;
// Pad Sapling outputs
if ! indexed_spends . is_empty ( ) {
while indexed_outputs . len ( ) < MIN_SHIELDED_OUTPUTS {
indexed_outputs . push ( None ) ;
}
}
// Randomize order of inputs and outputs
indexed_spends . shuffle ( & mut rng ) ;
indexed_outputs . shuffle ( & mut rng ) ;
// Create Sapling SpendDescriptions
let spend_descs = if ! indexed_spends . is_empty ( ) {
let anchor = self
. anchor
. expect ( " Sapling anchor must be set if Sapling spends are present. " ) ;
indexed_spends
. iter ( )
. enumerate ( )
. map ( | ( i , ( pos , spend ) ) | {
let proof_generation_key = spend . extsk . expsk . proof_generation_key ( ) ;
let nullifier = spend . note . nf (
& proof_generation_key . to_viewing_key ( ) ,
spend . merkle_path . position ,
) ;
let ( zkproof , cv , rk ) = prover
. spend_proof (
ctx ,
proof_generation_key ,
spend . diversifier ,
spend . note . rseed ,
spend . alpha ,
spend . note . value ,
anchor ,
spend . merkle_path . clone ( ) ,
)
. map_err ( | _ | Error ::SpendProof ) ? ;
// Record the post-randomized spend location
tx_metadata . spend_indices [ * pos ] = i ;
Ok ( SpendDescription {
cv ,
anchor ,
nullifier ,
rk ,
zkproof ,
spend_auth_sig : None ,
} )
} )
. collect ::< Result < Vec < _ > , Error > > ( ) ?
} else {
vec! [ ]
} ;
// Create Sapling OutputDescriptions
let output_descs = indexed_outputs
. into_iter ( )
. enumerate ( )
. map ( | ( i , output ) | {
if let Some ( ( pos , output ) ) = output {
// Record the post-randomized output location
tx_metadata . output_indices [ pos ] = i ;
output . clone ( ) . build_internal ( prover , ctx , & mut rng )
} else {
// This is a dummy output
let ( dummy_to , dummy_note ) = {
let ( diversifier , g_d ) = {
let mut diversifier ;
let g_d ;
loop {
let mut d = [ 0 ; 11 ] ;
rng . fill_bytes ( & mut d ) ;
diversifier = Diversifier ( d ) ;
if let Some ( val ) = diversifier . g_d ( ) {
g_d = val ;
break ;
}
}
( diversifier , g_d )
} ;
let ( pk_d , payment_address ) = loop {
let dummy_ivk = jubjub ::Fr ::random ( & mut rng ) ;
let pk_d = g_d * dummy_ivk ;
if let Some ( addr ) = PaymentAddress ::from_parts ( diversifier , pk_d ) {
break ( pk_d , addr ) ;
}
} ;
let rseed = generate_random_rseed_internal ( params , target_height , & mut rng ) ;
(
payment_address ,
Note {
g_d ,
pk_d ,
rseed ,
value : 0 ,
} ,
)
} ;
let esk = dummy_note . generate_or_derive_esk_internal ( & mut rng ) ;
let epk = dummy_note . g_d * esk ;
let ( zkproof , cv ) =
prover . output_proof ( ctx , esk , dummy_to , dummy_note . rcm ( ) , dummy_note . value ) ;
let cmu = dummy_note . cmu ( ) ;
let mut enc_ciphertext = [ 0 u8 ; 580 ] ;
let mut out_ciphertext = [ 0 u8 ; 80 ] ;
rng . fill_bytes ( & mut enc_ciphertext [ .. ] ) ;
rng . fill_bytes ( & mut out_ciphertext [ .. ] ) ;
OutputDescription {
cv ,
cmu ,
ephemeral_key : epk . into ( ) ,
enc_ciphertext ,
out_ciphertext ,
zkproof ,
}
}
} )
. collect ( ) ;
Ok ( ( spend_descs , output_descs , tx_metadata ) )
}
fn create_signatures < Pr : TxProver , R : RngCore > (
self ,
prover : & Pr ,
ctx : & mut Pr ::SaplingProvingContext ,
rng : & mut R ,
sighash_bytes : & [ u8 ; 32 ] ,
tx_metadata : & SaplingMetadata ,
) -> Result < ( Vec < Option < Signature > > , Option < Signature > ) , Error > {
// Create Sapling spendAuth and binding signatures
let mut spend_sigs = vec! [ None ; self . spends . len ( ) ] ;
for ( i , spend ) in self . spends . into_iter ( ) . enumerate ( ) {
spend_sigs [ tx_metadata . spend_indices [ i ] ] = Some ( spend_sig_internal (
PrivateKey ( spend . extsk . expsk . ask ) ,
spend . alpha ,
sighash_bytes ,
rng ,
) ) ;
}
// Add a binding signature if needed
let binding_sig =
if tx_metadata . spend_indices . is_empty ( ) & & tx_metadata . output_indices . is_empty ( ) {
None
} else {
Some (
prover
. binding_sig ( ctx , self . value_balance , & sighash_bytes )
. map_err ( | _ | Error ::BindingSig ) ? ,
)
} ;
Ok ( ( spend_sigs , binding_sig ) )
}
}
2018-11-20 05:37:21 -08:00
/// Generates a [`Transaction`] from its inputs and outputs.
2020-10-21 11:37:21 -07:00
pub struct Builder < ' a , P : consensus ::Parameters , R : RngCore > {
2020-08-05 13:27:40 -07:00
params : P ,
2019-06-12 15:11:51 -07:00
rng : R ,
2021-04-01 13:08:17 -07:00
target_height : BlockHeight ,
expiry_height : BlockHeight ,
2018-11-20 05:37:21 -08:00
fee : Amount ,
2021-04-01 13:08:17 -07:00
transparent_builder : TransparentBuilder ,
sapling_builder : SaplingBuilder < P > ,
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
2021-04-01 13:08:17 -07:00
tze_builder : TzeBuilder < ' a , TransactionData > ,
#[ cfg(not(feature = " zfuture " )) ]
tze_builder : PhantomData < & ' a ( ) > ,
2021-05-26 13:58:08 -07:00
progress_notifier : Option < Sender < Progress > > ,
2018-11-20 05:37:21 -08:00
}
2020-05-06 16:52:03 -07:00
impl < ' a , P : consensus ::Parameters > Builder < ' a , P , OsRng > {
2020-08-05 20:36:02 -07:00
/// 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.
///
/// # Default values
///
/// The expiry height will be set to the given height plus the default transaction
/// expiry delta (20 blocks).
///
/// The fee will be set to the default fee (0.0001 ZEC).
2021-04-01 13:08:17 -07:00
pub fn new ( params : P , target_height : BlockHeight ) -> Self {
Builder ::new_with_rng ( params , target_height , OsRng )
2020-05-27 11:09:59 -07:00
}
2020-08-05 20:36:02 -07:00
}
2020-05-06 16:52:03 -07:00
impl < ' a , P : consensus ::Parameters , R : RngCore + CryptoRng > Builder < ' a , P , R > {
2020-09-08 15:53:27 -07:00
/// Creates a new `Builder` targeted for inclusion in the block with the given height
/// and randomness source, using default values for general transaction fields.
///
/// # Default values
///
/// The expiry height will be set to the given height plus the default transaction
/// expiry delta (20 blocks).
///
/// The fee will be set to the default fee (0.0001 ZEC).
2021-04-01 13:08:17 -07:00
pub fn new_with_rng ( params : P , target_height : BlockHeight , rng : R ) -> Builder < ' a , P , R > {
Self ::new_internal ( params , target_height , rng )
2020-09-08 15:53:27 -07:00
}
2020-10-21 11:37:21 -07:00
}
2020-09-08 15:53:27 -07:00
2020-10-21 11:37:21 -07:00
impl < ' a , P : consensus ::Parameters , R : RngCore > Builder < ' a , P , R > {
2020-09-08 15:53:27 -07:00
/// Common utility function for builder construction.
2020-10-21 11:37:21 -07:00
///
/// WARNING: THIS MUST REMAIN PRIVATE AS IT ALLOWS CONSTRUCTION
/// OF BUILDERS WITH NON-CryptoRng RNGs
2021-04-01 13:08:17 -07:00
fn new_internal ( params : P , target_height : BlockHeight , rng : R ) -> Builder < ' a , P , R > {
2018-11-20 05:37:21 -08:00
Builder {
2020-08-05 13:27:40 -07:00
params ,
2019-06-12 15:11:51 -07:00
rng ,
2021-04-01 13:08:17 -07:00
target_height ,
expiry_height : target_height + DEFAULT_TX_EXPIRY_DELTA ,
2018-11-20 05:37:21 -08:00
fee : DEFAULT_FEE ,
2021-05-11 13:17:54 -07:00
transparent_builder : TransparentBuilder ::empty ( ) ,
2021-04-01 13:08:17 -07:00
sapling_builder : SaplingBuilder ::new ( ) ,
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
2021-04-01 13:08:17 -07:00
tze_builder : TzeBuilder ::new ( ) ,
#[ cfg(not(feature = " zfuture " )) ]
tze_builder : PhantomData ,
2021-05-26 13:58:08 -07:00
progress_notifier : None ,
2018-11-20 05:37:21 -08:00
}
}
/// Adds a Sapling note to be spent in this transaction.
///
2020-02-07 16:25:24 -08:00
/// Returns an error if the given Merkle path does not have the same anchor as the
/// paths for previous Sapling notes.
2018-11-20 05:37:21 -08:00
pub fn add_sapling_spend (
& mut self ,
extsk : ExtendedSpendingKey ,
diversifier : Diversifier ,
2020-07-01 13:26:54 -07:00
note : Note ,
2020-02-07 16:25:24 -08:00
merkle_path : MerklePath < Node > ,
2018-11-20 05:37:21 -08:00
) -> Result < ( ) , Error > {
2021-04-01 13:08:17 -07:00
self . sapling_builder
. add_spend ( & mut self . rng , extsk , diversifier , note , merkle_path )
2018-11-20 05:37:21 -08:00
}
/// Adds a Sapling address to send funds to.
2020-08-05 20:36:02 -07:00
pub fn add_sapling_output (
2018-11-20 05:37:21 -08:00
& mut self ,
2020-08-28 08:12:37 -07:00
ovk : Option < OutgoingViewingKey > ,
2020-07-01 13:26:54 -07:00
to : PaymentAddress ,
2018-11-20 05:37:21 -08:00
value : Amount ,
2020-10-29 09:48:26 -07:00
memo : Option < MemoBytes > ,
2018-11-20 05:37:21 -08:00
) -> Result < ( ) , Error > {
2021-04-01 13:08:17 -07:00
self . sapling_builder . add_output (
2020-08-05 13:27:40 -07:00
& mut self . rng ,
2021-04-01 13:08:17 -07:00
& self . params ,
self . target_height ,
2020-08-05 13:27:40 -07:00
ovk ,
to ,
value ,
memo ,
2021-04-01 13:08:17 -07:00
)
2018-11-20 05:37:21 -08:00
}
2019-07-31 08:20:13 -07:00
/// Adds a transparent coin to be spent in this transaction.
#[ cfg(feature = " transparent-inputs " ) ]
2020-08-22 22:56:03 -07:00
#[ cfg_attr(docsrs, doc(cfg(feature = " transparent-inputs " ))) ]
2019-07-31 08:20:13 -07:00
pub fn add_transparent_input (
& mut self ,
sk : secp256k1 ::SecretKey ,
utxo : OutPoint ,
coin : TxOut ,
) -> Result < ( ) , Error > {
2021-05-11 13:17:54 -07:00
self . transparent_builder
. add_input ( sk , utxo , coin )
. map_err ( Error ::TransparentBuildError )
2019-07-31 08:20:13 -07:00
}
2019-05-24 05:32:55 -07:00
/// Adds a transparent address to send funds to.
pub fn add_transparent_output (
& mut self ,
to : & TransparentAddress ,
value : Amount ,
) -> Result < ( ) , Error > {
2021-05-11 13:17:54 -07:00
self . transparent_builder
. add_output ( to , value )
. map_err ( Error ::TransparentBuildError )
2019-05-24 05:32:55 -07:00
}
2018-11-20 05:37:21 -08:00
/// Sets the Sapling address to which any change will be sent.
///
/// By default, change is sent to the Sapling address corresponding to the first note
/// being spent (i.e. the first call to [`Builder::add_sapling_spend`]).
2020-07-01 13:26:54 -07:00
pub fn send_change_to ( & mut self , ovk : OutgoingViewingKey , to : PaymentAddress ) {
2021-04-01 13:08:17 -07:00
self . sapling_builder . send_change_to ( ovk , to )
2018-11-20 05:37:21 -08:00
}
2021-05-26 13:58:08 -07:00
/// Sets the notifier channel, where progress of building the transaction is sent.
2018-11-20 05:37:21 -08:00
///
2021-05-26 13:58:08 -07:00
/// An update is sent after every Spend or Output is computed, and the `u32` sent
/// represents the total steps completed so far. It will eventually send number of
/// spends + outputs. If there's an error building the transaction, the channel is
/// closed.
pub fn with_progress_notifier ( & mut self , progress_notifier : Sender < Progress > ) {
self . progress_notifier = Some ( progress_notifier ) ;
2021-04-26 10:14:01 -07:00
}
/// Builds a transaction from the configured spends and outputs.
///
/// Upon success, returns a tuple containing the final transaction, and the
2021-04-01 13:08:17 -07:00
/// [`SaplingMetadata`] generated during the build process.
2021-04-26 10:14:01 -07:00
///
/// `consensus_branch_id` must be valid for the block height that this transaction is
/// targeting. An invalid `consensus_branch_id` will *not* result in an error from
/// this function, and instead will generate a transaction that will be rejected by
/// the network.
2021-05-26 13:58:08 -07:00
pub fn build (
2018-11-20 05:37:21 -08:00
mut self ,
2021-04-01 13:08:17 -07:00
version : TxVersion ,
2019-11-25 07:41:14 -08:00
consensus_branch_id : consensus ::BranchId ,
2020-02-07 16:37:31 -08:00
prover : & impl TxProver ,
2021-04-01 13:08:17 -07:00
) -> Result < ( Transaction , SaplingMetadata ) , Error > {
2018-11-20 05:37:21 -08:00
//
// Consistency checks
//
// Valid change
2021-04-01 13:08:17 -07:00
let change = self
. transparent_builder
. value_balance ( )
. and_then ( | ta | ta + self . sapling_builder . value_balance ( ) )
. and_then ( | b | b - self . fee )
. ok_or ( Error ::InvalidAmount ) ? ;
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
2021-04-01 13:08:17 -07:00
let change = self
. tze_builder
. value_balance ( )
. and_then ( | b | change + b )
. ok_or ( Error ::InvalidAmount ) ? ;
2020-05-06 16:52:03 -07:00
2018-11-20 05:37:21 -08:00
if change . is_negative ( ) {
2019-08-13 07:10:57 -07:00
return Err ( Error ::ChangeIsNegative ( change ) ) ;
2018-11-20 05:37:21 -08:00
}
//
// Change output
//
if change . is_positive ( ) {
2021-04-01 13:08:17 -07:00
let change_address = self . sapling_builder . get_change_address ( ) ? ;
2020-08-28 08:12:37 -07:00
self . add_sapling_output ( Some ( change_address . 0 ) , change_address . 1 , change , None ) ? ;
2018-11-20 05:37:21 -08:00
}
2021-04-01 13:08:17 -07:00
let ( vin , vout ) = self . transparent_builder . build ( ) ;
2018-11-20 05:37:21 -08:00
let mut ctx = prover . new_sapling_proving_context ( ) ;
2021-04-01 13:08:17 -07:00
let ( spend_descs , output_descs , tx_metadata ) = self . sapling_builder . build (
& self . params ,
prover ,
& mut ctx ,
& mut self . rng ,
self . target_height ,
) ? ;
2018-11-20 05:37:21 -08:00
2021-04-01 13:08:17 -07:00
#[ cfg(feature = " zfuture " ) ]
let ( tze_inputs , tze_outputs ) = self . tze_builder . build ( ) ;
2021-04-26 10:14:01 -07:00
2021-04-01 13:08:17 -07:00
let mut mtx = TransactionData {
version ,
vin ,
vout ,
#[ cfg(feature = " zfuture " ) ]
tze_inputs ,
#[ cfg(feature = " zfuture " ) ]
tze_outputs ,
lock_time : 0 ,
expiry_height : self . expiry_height ,
value_balance : self . sapling_builder . value_balance ,
shielded_spends : spend_descs ,
shielded_outputs : output_descs ,
joinsplits : vec ! [ ] ,
joinsplit_pubkey : None ,
joinsplit_sig : None ,
binding_sig : None ,
} ;
2018-11-20 05:37:21 -08:00
//
2020-10-09 15:19:44 -07:00
// Signatures -- everything but the signatures must already have been added.
2018-11-20 05:37:21 -08:00
//
let mut sighash = [ 0 u8 ; 32 ] ;
sighash . copy_from_slice ( & signature_hash_data (
2021-04-01 13:08:17 -07:00
& mtx ,
2018-11-20 05:37:21 -08:00
consensus_branch_id ,
SIGHASH_ALL ,
2020-06-03 19:39:43 -07:00
SignableInput ::Shielded ,
2018-11-20 05:37:21 -08:00
) ) ;
2021-04-01 13:08:17 -07:00
let ( sapling_spend_auth_sigs , sapling_binding_sig ) = self
. sapling_builder
. create_signatures ( prover , & mut ctx , & mut self . rng , & sighash , & tx_metadata ) ? ;
2020-04-03 12:13:39 -07:00
2021-04-01 13:08:17 -07:00
for ( i , spend_auth_sig ) in sapling_spend_auth_sigs . into_iter ( ) . enumerate ( ) {
mtx . shielded_spends [ i ] . spend_auth_sig = spend_auth_sig ;
}
mtx . binding_sig = sapling_binding_sig ;
2018-11-20 05:37:21 -08:00
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
2021-04-01 13:08:17 -07:00
{
// Create TZE input witnesses
let tze_payloads = self . tze_builder . create_signatures ( & mtx ) ? ;
for ( i , payload ) in tze_payloads . into_iter ( ) . enumerate ( ) {
mtx . tze_inputs [ i ] . witness . payload = payload ;
2020-09-09 09:54:18 -07:00
}
2020-05-06 16:52:03 -07:00
}
2021-04-01 13:08:17 -07:00
#[ cfg(feature = " transparent-inputs " ) ]
{
let script_sigs = self
. transparent_builder
. create_signatures ( & mtx , consensus_branch_id ) ;
for ( i , sig ) in script_sigs . into_iter ( ) . enumerate ( ) {
mtx . vin [ i ] . script_sig = sig ;
}
}
2019-07-31 08:20:13 -07:00
2018-11-20 05:37:21 -08:00
Ok ( (
2021-04-01 13:08:17 -07:00
mtx . freeze ( ) . expect ( " Transaction should be complete " ) ,
2018-11-20 05:37:21 -08:00
tx_metadata ,
) )
}
}
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
2020-05-06 16:52:03 -07:00
impl < ' a , P : consensus ::Parameters , R : RngCore + CryptoRng > ExtensionTxBuilder < ' a >
for Builder < ' a , P , R >
{
type BuildCtx = TransactionData ;
type BuildError = Error ;
fn add_tze_input < WBuilder , W : ToPayload > (
& mut self ,
2020-06-03 19:38:05 -07:00
extension_id : u32 ,
2020-09-09 09:54:18 -07:00
mode : u32 ,
2021-04-01 13:08:17 -07:00
prevout : ( TzeOutPoint , TzeOut ) ,
2020-05-06 16:52:03 -07:00
witness_builder : WBuilder ,
) -> Result < ( ) , Self ::BuildError >
where
WBuilder : ' a + ( FnOnce ( & Self ::BuildCtx ) -> Result < W , Self ::BuildError > ) ,
{
2021-04-01 13:08:17 -07:00
self . tze_builder
. add_input ( extension_id , mode , prevout , witness_builder ) ;
2020-05-06 16:52:03 -07:00
Ok ( ( ) )
}
fn add_tze_output < G : ToPayload > (
& mut self ,
2020-06-03 19:38:05 -07:00
extension_id : u32 ,
2020-05-06 16:52:03 -07:00
value : Amount ,
guarded_by : & G ,
) -> Result < ( ) , Self ::BuildError > {
2021-04-01 13:08:17 -07:00
self . tze_builder . add_output ( extension_id , value , guarded_by )
2020-05-06 16:52:03 -07:00
}
}
2020-10-21 11:37:21 -07:00
#[ cfg(any(test, feature = " test-dependencies " )) ]
impl < ' a , P : consensus ::Parameters , R : RngCore > Builder < ' a , P , 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.
///
/// # Default values
///
/// The expiry height will be set to the given height plus the default transaction
/// expiry delta (20 blocks).
///
/// The fee will be set to the default fee (0.0001 ZEC).
///
/// WARNING: DO NOT USE IN PRODUCTION
pub fn test_only_new_with_rng ( params : P , height : BlockHeight , rng : R ) -> Builder < ' a , P , R > {
2021-04-01 13:08:17 -07:00
Self ::new_internal ( params , height , rng )
2020-10-21 11:37:21 -07:00
}
/// Creates a new `Builder` targeted for inclusion in the block with the given height,
/// and randomness source, using default values for general transaction fields
/// and the `ZFUTURE_TX_VERSION` and `ZFUTURE_VERSION_GROUP_ID` version identifiers.
///
/// # Default values
///
/// The expiry height will be set to the given height plus the default transaction
/// expiry delta (20 blocks).
///
/// The fee will be set to the default fee (0.0001 ZEC).
///
/// The transaction will be constructed and serialized according to the
/// NetworkUpgrade::ZFuture rules. This is intended only for use in
/// integration testing of new features.
///
/// WARNING: DO NOT USE IN PRODUCTION
#[ cfg(feature = " zfuture " ) ]
pub fn test_only_new_with_rng_zfuture (
params : P ,
height : BlockHeight ,
rng : R ,
) -> Builder < ' a , P , R > {
2021-04-01 13:08:17 -07:00
Self ::new_internal ( params , height , rng )
2020-10-21 11:37:21 -07:00
}
pub fn mock_build (
self ,
2021-04-01 13:08:17 -07:00
version : TxVersion ,
2020-10-21 11:37:21 -07:00
consensus_branch_id : consensus ::BranchId ,
2021-04-01 13:08:17 -07:00
) -> Result < ( Transaction , SaplingMetadata ) , Error > {
self . build ( version , consensus_branch_id , & MockTxProver )
2020-10-21 11:37:21 -07:00
}
}
2018-11-20 05:37:21 -08:00
#[ cfg(test) ]
mod tests {
use ff ::{ Field , PrimeField } ;
2019-09-28 00:48:43 -07:00
use rand_core ::OsRng ;
2019-08-06 02:46:40 -07:00
2018-11-20 05:37:21 -08:00
use crate ::{
2020-09-17 10:55:39 -07:00
consensus ::{ self , Parameters , H0 , TEST_NETWORK } ,
2019-05-24 05:32:55 -07:00
legacy ::TransparentAddress ,
2018-11-20 05:37:21 -08:00
merkle_tree ::{ CommitmentTree , IncrementalWitness } ,
2021-03-04 13:26:39 -08:00
sapling ::{ prover ::mock ::MockTxProver , Node , Rseed } ,
2021-04-01 13:08:17 -07:00
transaction ::{
2021-05-11 13:17:54 -07:00
components ::{
amount ::Amount , amount ::DEFAULT_FEE , transparent ::builder as transparent ,
} ,
2021-04-01 13:08:17 -07:00
TxVersion ,
} ,
2018-11-20 05:37:21 -08:00
zip32 ::{ ExtendedFullViewingKey , ExtendedSpendingKey } ,
} ;
2021-04-01 13:08:17 -07:00
use super ::{ Builder , Error , SaplingBuilder , DEFAULT_TX_EXPIRY_DELTA } ;
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
2021-04-01 13:08:17 -07:00
use super ::TzeBuilder ;
2020-05-18 19:27:19 -07:00
2018-11-20 05:37:21 -08:00
#[ test ]
fn fails_on_negative_output ( ) {
let extsk = ExtendedSpendingKey ::master ( & [ ] ) ;
let extfvk = ExtendedFullViewingKey ::from ( & extsk ) ;
let ovk = extfvk . fvk . ovk ;
let to = extfvk . default_address ( ) . unwrap ( ) . 1 ;
2020-09-17 10:55:39 -07:00
let mut builder = Builder ::new ( TEST_NETWORK , H0 ) ;
2019-08-13 07:24:08 -07:00
assert_eq! (
2020-08-28 08:12:37 -07:00
builder . add_sapling_output ( Some ( ovk ) , to , Amount ::from_i64 ( - 1 ) . unwrap ( ) , None ) ,
2019-08-13 07:24:08 -07:00
Err ( Error ::InvalidAmount )
) ;
2018-11-20 05:37:21 -08:00
}
2020-04-03 12:13:39 -07:00
#[ test ]
fn binding_sig_absent_if_no_shielded_spend_or_output ( ) {
2020-09-17 10:55:39 -07:00
use crate ::consensus ::NetworkUpgrade ;
2021-04-01 13:08:17 -07:00
use crate ::transaction ::builder ::{ self , TransparentBuilder } ;
2020-04-03 12:13:39 -07:00
2020-09-17 10:55:39 -07:00
let sapling_activation_height = TEST_NETWORK
2020-08-05 13:27:40 -07:00
. activation_height ( NetworkUpgrade ::Sapling )
. unwrap ( ) ;
2020-08-02 22:39:36 -07:00
2020-04-03 12:13:39 -07:00
// Create a builder with 0 fee, so we can construct t outputs
2020-08-05 13:27:40 -07:00
let mut builder = builder ::Builder {
2020-09-17 10:55:39 -07:00
params : TEST_NETWORK ,
2020-04-03 12:13:39 -07:00
rng : OsRng ,
2021-04-01 13:08:17 -07:00
target_height : sapling_activation_height ,
expiry_height : sapling_activation_height + DEFAULT_TX_EXPIRY_DELTA ,
2020-04-03 12:13:39 -07:00
fee : Amount ::zero ( ) ,
2021-05-11 13:17:54 -07:00
transparent_builder : TransparentBuilder ::empty ( ) ,
2021-04-01 13:08:17 -07:00
sapling_builder : SaplingBuilder ::new ( ) ,
2020-09-23 09:53:48 -07:00
#[ cfg(feature = " zfuture " ) ]
2021-04-01 13:08:17 -07:00
tze_builder : TzeBuilder ::new ( ) ,
2021-05-26 13:58:08 -07:00
progress_notifier : None ,
2020-04-03 12:13:39 -07:00
} ;
// Create a tx with only t output. No binding_sig should be present
builder
. add_transparent_output ( & TransparentAddress ::PublicKey ( [ 0 ; 20 ] ) , Amount ::zero ( ) )
. unwrap ( ) ;
let ( tx , _ ) = builder
2021-04-01 13:08:17 -07:00
. build (
TxVersion ::Sapling ,
consensus ::BranchId ::Sapling ,
& MockTxProver ,
)
2020-04-03 12:13:39 -07:00
. unwrap ( ) ;
// No binding signature, because only t input and outputs
assert! ( tx . binding_sig . is_none ( ) ) ;
}
#[ test ]
fn binding_sig_present_if_shielded_spend ( ) {
let extsk = ExtendedSpendingKey ::master ( & [ ] ) ;
let extfvk = ExtendedFullViewingKey ::from ( & extsk ) ;
let to = extfvk . default_address ( ) . unwrap ( ) . 1 ;
let mut rng = OsRng ;
let note1 = to
2020-07-01 13:26:54 -07:00
. create_note ( 50000 , Rseed ::BeforeZip212 ( jubjub ::Fr ::random ( & mut rng ) ) )
2020-04-03 12:13:39 -07:00
. unwrap ( ) ;
2020-08-21 10:33:22 -07:00
let cmu1 = Node ::new ( note1 . cmu ( ) . to_repr ( ) ) ;
2021-02-04 06:34:41 -08:00
let mut tree = CommitmentTree ::empty ( ) ;
2020-08-21 10:33:22 -07:00
tree . append ( cmu1 ) . unwrap ( ) ;
2020-04-03 12:13:39 -07:00
let witness1 = IncrementalWitness ::from_tree ( & tree ) ;
2020-09-17 10:55:39 -07:00
let mut builder = Builder ::new ( TEST_NETWORK , H0 ) ;
2020-04-03 12:13:39 -07:00
// Create a tx with a sapling spend. binding_sig should be present
builder
2020-10-30 06:27:49 -07:00
. add_sapling_spend ( extsk , * to . diversifier ( ) , note1 , witness1 . path ( ) . unwrap ( ) )
2020-04-03 12:13:39 -07:00
. unwrap ( ) ;
builder
. add_transparent_output ( & TransparentAddress ::PublicKey ( [ 0 ; 20 ] ) , Amount ::zero ( ) )
. unwrap ( ) ;
// Expect a binding signature error, because our inputs aren't valid, but this shows
// that a binding signature was attempted
assert_eq! (
2021-04-01 13:08:17 -07:00
builder . build (
TxVersion ::Sapling ,
consensus ::BranchId ::Sapling ,
& MockTxProver
) ,
2020-04-03 12:13:39 -07:00
Err ( Error ::BindingSig )
) ;
}
2019-05-24 05:32:55 -07:00
#[ test ]
fn fails_on_negative_transparent_output ( ) {
2020-09-17 10:55:39 -07:00
let mut builder = Builder ::new ( TEST_NETWORK , H0 ) ;
2019-08-13 07:24:08 -07:00
assert_eq! (
builder . add_transparent_output (
& TransparentAddress ::PublicKey ( [ 0 ; 20 ] ) ,
Amount ::from_i64 ( - 1 ) . unwrap ( ) ,
) ,
2021-05-11 13:17:54 -07:00
Err ( Error ::TransparentBuildError (
transparent ::Error ::InvalidAmount
) )
2019-08-13 07:24:08 -07:00
) ;
2019-05-24 05:32:55 -07:00
}
2018-11-20 05:37:21 -08:00
#[ test ]
fn fails_on_negative_change ( ) {
let mut rng = OsRng ;
// Just use the master key as the ExtendedSpendingKey for this test
let extsk = ExtendedSpendingKey ::master ( & [ ] ) ;
// Fails with no inputs or outputs
// 0.0001 t-ZEC fee
{
2020-09-17 10:55:39 -07:00
let builder = Builder ::new ( TEST_NETWORK , H0 ) ;
2019-08-13 07:24:08 -07:00
assert_eq! (
2021-04-01 13:08:17 -07:00
builder . build (
TxVersion ::Sapling ,
consensus ::BranchId ::Sapling ,
& MockTxProver
) ,
2021-05-11 08:57:28 -07:00
Err ( Error ::ChangeIsNegative (
( Amount ::zero ( ) - DEFAULT_FEE ) . unwrap ( )
) )
2019-08-13 07:24:08 -07:00
) ;
2018-11-20 05:37:21 -08:00
}
let extfvk = ExtendedFullViewingKey ::from ( & extsk ) ;
2020-08-28 08:12:37 -07:00
let ovk = Some ( extfvk . fvk . ovk ) ;
2018-11-20 05:37:21 -08:00
let to = extfvk . default_address ( ) . unwrap ( ) . 1 ;
// Fail if there is only a Sapling output
2020-11-23 09:41:30 -08:00
// 0.0005 z-ZEC out, 0.00001 t-ZEC fee
2018-11-20 05:37:21 -08:00
{
2020-09-17 10:55:39 -07:00
let mut builder = Builder ::new ( TEST_NETWORK , H0 ) ;
2018-11-20 05:37:21 -08:00
builder
2020-10-30 06:27:49 -07:00
. add_sapling_output ( ovk , to . clone ( ) , Amount ::from_u64 ( 50000 ) . unwrap ( ) , None )
2018-11-20 05:37:21 -08:00
. unwrap ( ) ;
2019-08-13 07:24:08 -07:00
assert_eq! (
2021-04-01 13:08:17 -07:00
builder . build (
TxVersion ::Sapling ,
consensus ::BranchId ::Sapling ,
& MockTxProver
) ,
2020-11-23 09:41:30 -08:00
Err ( Error ::ChangeIsNegative (
2021-05-11 08:57:28 -07:00
( Amount ::from_i64 ( - 50000 ) . unwrap ( ) - DEFAULT_FEE ) . unwrap ( )
2020-11-23 09:41:30 -08:00
) )
2019-08-13 07:24:08 -07:00
) ;
2018-11-20 05:37:21 -08:00
}
2019-05-24 05:32:55 -07:00
// Fail if there is only a transparent output
2020-11-23 09:41:30 -08:00
// 0.0005 t-ZEC out, 0.00001 t-ZEC fee
2019-05-24 05:32:55 -07:00
{
2020-09-17 10:55:39 -07:00
let mut builder = Builder ::new ( TEST_NETWORK , H0 ) ;
2019-05-24 05:32:55 -07:00
builder
2019-07-25 14:37:16 -07:00
. add_transparent_output (
& TransparentAddress ::PublicKey ( [ 0 ; 20 ] ) ,
Amount ::from_u64 ( 50000 ) . unwrap ( ) ,
)
2019-05-24 05:32:55 -07:00
. unwrap ( ) ;
2019-08-13 07:24:08 -07:00
assert_eq! (
2021-04-01 13:08:17 -07:00
builder . build (
TxVersion ::Sapling ,
consensus ::BranchId ::Sapling ,
& MockTxProver
) ,
2020-11-23 09:41:30 -08:00
Err ( Error ::ChangeIsNegative (
2021-05-11 08:57:28 -07:00
( Amount ::from_i64 ( - 50000 ) . unwrap ( ) - DEFAULT_FEE ) . unwrap ( )
2020-11-23 09:41:30 -08:00
) )
2019-08-13 07:24:08 -07:00
) ;
2019-05-24 05:32:55 -07:00
}
2018-11-20 05:37:21 -08:00
let note1 = to
2020-11-23 09:41:30 -08:00
. create_note ( 50999 , Rseed ::BeforeZip212 ( jubjub ::Fr ::random ( & mut rng ) ) )
2018-11-20 05:37:21 -08:00
. unwrap ( ) ;
2020-08-21 10:33:22 -07:00
let cmu1 = Node ::new ( note1 . cmu ( ) . to_repr ( ) ) ;
2021-02-04 06:34:41 -08:00
let mut tree = CommitmentTree ::empty ( ) ;
2020-08-21 10:33:22 -07:00
tree . append ( cmu1 ) . unwrap ( ) ;
2018-11-20 05:37:21 -08:00
let mut witness1 = IncrementalWitness ::from_tree ( & tree ) ;
2019-05-24 05:32:55 -07:00
// Fail if there is insufficient input
2020-11-23 09:41:30 -08:00
// 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.00001 t-ZEC fee, 0.00050999 z-ZEC in
2018-11-20 05:37:21 -08:00
{
2020-09-17 10:55:39 -07:00
let mut builder = Builder ::new ( TEST_NETWORK , H0 ) ;
2018-11-20 05:37:21 -08:00
builder
. add_sapling_spend (
extsk . clone ( ) ,
2019-08-23 15:08:09 -07:00
* to . diversifier ( ) ,
2018-11-20 05:37:21 -08:00
note1 . clone ( ) ,
2020-02-07 09:31:38 -08:00
witness1 . path ( ) . unwrap ( ) ,
2018-11-20 05:37:21 -08:00
)
. unwrap ( ) ;
builder
2020-10-30 06:27:49 -07:00
. add_sapling_output ( ovk , to . clone ( ) , Amount ::from_u64 ( 30000 ) . unwrap ( ) , None )
2019-05-24 05:32:55 -07:00
. unwrap ( ) ;
builder
2019-07-25 14:37:16 -07:00
. add_transparent_output (
& TransparentAddress ::PublicKey ( [ 0 ; 20 ] ) ,
Amount ::from_u64 ( 20000 ) . unwrap ( ) ,
)
2018-11-20 05:37:21 -08:00
. unwrap ( ) ;
2019-08-13 07:24:08 -07:00
assert_eq! (
2021-04-01 13:08:17 -07:00
builder . build (
TxVersion ::Sapling ,
consensus ::BranchId ::Sapling ,
& MockTxProver
) ,
2019-08-13 07:24:08 -07:00
Err ( Error ::ChangeIsNegative ( Amount ::from_i64 ( - 1 ) . unwrap ( ) ) )
) ;
2018-11-20 05:37:21 -08:00
}
2020-07-29 21:36:12 -07:00
let note2 = to
2020-07-01 13:26:54 -07:00
. create_note ( 1 , Rseed ::BeforeZip212 ( jubjub ::Fr ::random ( & mut rng ) ) )
2020-07-29 21:36:12 -07:00
. unwrap ( ) ;
2020-08-21 10:33:22 -07:00
let cmu2 = Node ::new ( note2 . cmu ( ) . to_repr ( ) ) ;
tree . append ( cmu2 ) . unwrap ( ) ;
witness1 . append ( cmu2 ) . unwrap ( ) ;
2018-11-20 05:37:21 -08:00
let witness2 = IncrementalWitness ::from_tree ( & tree ) ;
// Succeeds if there is sufficient input
2019-05-24 05:32:55 -07:00
// 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.0001 t-ZEC fee, 0.0006 z-ZEC in
2018-11-20 05:37:21 -08:00
//
// (Still fails because we are using a MockTxProver which doesn't correctly
// compute bindingSig.)
{
2020-09-17 10:55:39 -07:00
let mut builder = Builder ::new ( TEST_NETWORK , H0 ) ;
2018-11-20 05:37:21 -08:00
builder
2020-02-07 09:31:38 -08:00
. add_sapling_spend (
extsk . clone ( ) ,
* to . diversifier ( ) ,
note1 ,
witness1 . path ( ) . unwrap ( ) ,
)
2018-11-20 05:37:21 -08:00
. unwrap ( ) ;
builder
2020-02-07 09:31:38 -08:00
. add_sapling_spend ( extsk , * to . diversifier ( ) , note2 , witness2 . path ( ) . unwrap ( ) )
2018-11-20 05:37:21 -08:00
. unwrap ( ) ;
builder
2020-08-05 20:36:02 -07:00
. add_sapling_output ( ovk , to , Amount ::from_u64 ( 30000 ) . unwrap ( ) , None )
2019-05-24 05:32:55 -07:00
. unwrap ( ) ;
builder
2019-07-25 14:37:16 -07:00
. add_transparent_output (
& TransparentAddress ::PublicKey ( [ 0 ; 20 ] ) ,
Amount ::from_u64 ( 20000 ) . unwrap ( ) ,
)
2018-11-20 05:37:21 -08:00
. unwrap ( ) ;
2019-11-25 07:41:14 -08:00
assert_eq! (
2021-04-01 13:08:17 -07:00
builder . build (
TxVersion ::Sapling ,
consensus ::BranchId ::Sapling ,
& MockTxProver
) ,
2019-11-25 07:41:14 -08:00
Err ( Error ::BindingSig )
)
2018-11-20 05:37:21 -08:00
}
}
}