Use rust feature flagging to allow conditionally enabling TZE-related code.
This also uses the test-dependencies feature flag for conditional inclusion of proptest-related modules which may be used by dependent crates for generation of samples in testing.
This commit is contained in:
parent
debd5e6f5c
commit
790071953c
|
@ -10,7 +10,7 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
blake2b_simd = "0.5"
|
||||
zcash_primitives = { version = "0.4.0", path = "../zcash_primitives" }
|
||||
zcash_primitives = { version = "0.4.0", path = "../zcash_primitives", features = ["zfuture"] }
|
||||
|
||||
[dev-dependencies]
|
||||
ff = "0.8"
|
||||
|
|
|
@ -47,6 +47,7 @@ rand_xorshift = "0.2"
|
|||
[features]
|
||||
transparent-inputs = ["ripemd160", "secp256k1"]
|
||||
test-dependencies = ["proptest"]
|
||||
zfuture = []
|
||||
|
||||
[[bench]]
|
||||
name = "note_decryption"
|
||||
|
|
|
@ -164,6 +164,7 @@ impl Parameters for MainNetwork {
|
|||
NetworkUpgrade::Blossom => Some(BlockHeight(653_600)),
|
||||
NetworkUpgrade::Heartwood => Some(BlockHeight(903_000)),
|
||||
NetworkUpgrade::Canopy => Some(BlockHeight(1_046_400)),
|
||||
#[cfg(feature = "zfuture")]
|
||||
NetworkUpgrade::ZFuture => None,
|
||||
}
|
||||
}
|
||||
|
@ -199,6 +200,7 @@ impl Parameters for TestNetwork {
|
|||
NetworkUpgrade::Blossom => Some(BlockHeight(584_000)),
|
||||
NetworkUpgrade::Heartwood => Some(BlockHeight(903_800)),
|
||||
NetworkUpgrade::Canopy => Some(BlockHeight(1_028_500)),
|
||||
#[cfg(feature = "zfuture")]
|
||||
NetworkUpgrade::ZFuture => None,
|
||||
}
|
||||
}
|
||||
|
@ -294,6 +296,7 @@ pub enum NetworkUpgrade {
|
|||
/// This upgrade is expected never to activate on mainnet;
|
||||
/// it is intended for use in integration testing of functionality
|
||||
/// that is a candidate for integration in a future network upgrade.
|
||||
#[cfg(feature = "zfuture")]
|
||||
ZFuture,
|
||||
}
|
||||
|
||||
|
@ -305,6 +308,7 @@ impl fmt::Display for NetworkUpgrade {
|
|||
NetworkUpgrade::Blossom => write!(f, "Blossom"),
|
||||
NetworkUpgrade::Heartwood => write!(f, "Heartwood"),
|
||||
NetworkUpgrade::Canopy => write!(f, "Canopy"),
|
||||
#[cfg(feature = "zfuture")]
|
||||
NetworkUpgrade::ZFuture => write!(f, "ZFUTURE"),
|
||||
}
|
||||
}
|
||||
|
@ -318,6 +322,7 @@ impl NetworkUpgrade {
|
|||
NetworkUpgrade::Blossom => BranchId::Blossom,
|
||||
NetworkUpgrade::Heartwood => BranchId::Heartwood,
|
||||
NetworkUpgrade::Canopy => BranchId::Canopy,
|
||||
#[cfg(feature = "zfuture")]
|
||||
NetworkUpgrade::ZFuture => BranchId::ZFuture,
|
||||
}
|
||||
}
|
||||
|
@ -366,6 +371,7 @@ pub enum BranchId {
|
|||
Canopy,
|
||||
/// Candidates for future consensus rules; this branch will never
|
||||
/// activate on mainnet.
|
||||
#[cfg(feature = "zfuture")]
|
||||
ZFuture,
|
||||
}
|
||||
|
||||
|
@ -380,6 +386,7 @@ impl TryFrom<u32> for BranchId {
|
|||
0x2bb4_0e60 => Ok(BranchId::Blossom),
|
||||
0xf5b9_230b => Ok(BranchId::Heartwood),
|
||||
0xe9ff_75a6 => Ok(BranchId::Canopy),
|
||||
#[cfg(feature = "zfuture")]
|
||||
0xffff_ffff => Ok(BranchId::ZFuture),
|
||||
_ => Err("Unknown consensus branch ID"),
|
||||
}
|
||||
|
@ -395,6 +402,7 @@ impl From<BranchId> for u32 {
|
|||
BranchId::Blossom => 0x2bb4_0e60,
|
||||
BranchId::Heartwood => 0xf5b9_230b,
|
||||
BranchId::Canopy => 0xe9ff_75a6,
|
||||
#[cfg(feature = "zfuture")]
|
||||
BranchId::ZFuture => 0xffff_ffff,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
pub mod block;
|
||||
pub mod consensus;
|
||||
pub mod constants;
|
||||
pub mod extensions;
|
||||
pub mod group_hash;
|
||||
pub mod keys;
|
||||
pub mod legacy;
|
||||
|
@ -26,5 +25,8 @@ pub mod transaction;
|
|||
pub mod util;
|
||||
pub mod zip32;
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub mod extensions;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vectors;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! Structs for building transactions.
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use std::boxed::Box;
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
@ -10,7 +12,6 @@ use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore};
|
|||
|
||||
use crate::{
|
||||
consensus::{self, BlockHeight},
|
||||
extensions::transparent::{self as tze, ExtensionTxBuilder, ToPayload},
|
||||
keys::OutgoingViewingKey,
|
||||
legacy::TransparentAddress,
|
||||
merkle_tree::MerklePath,
|
||||
|
@ -21,8 +22,7 @@ use crate::{
|
|||
sapling::{spend_sig, Node},
|
||||
transaction::{
|
||||
components::{
|
||||
amount::Amount, amount::DEFAULT_FEE, OutPoint, OutputDescription, SpendDescription,
|
||||
TxOut, TzeIn, TzeOut,
|
||||
amount::Amount, amount::DEFAULT_FEE, OutputDescription, SpendDescription, TxOut,
|
||||
},
|
||||
signature_hash_data, SignableInput, Transaction, TransactionData, SIGHASH_ALL,
|
||||
},
|
||||
|
@ -33,6 +33,15 @@ use crate::{
|
|||
#[cfg(feature = "transparent-inputs")]
|
||||
use crate::{legacy::Script, transaction::components::TxIn};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use crate::{
|
||||
extensions::transparent::{self as tze, ExtensionTxBuilder, ToPayload},
|
||||
transaction::components::{TzeIn, TzeOut},
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "transparent-inputs", feature = "zfuture"))]
|
||||
use crate::transaction::components::OutPoint;
|
||||
|
||||
const DEFAULT_TX_EXPIRY_DELTA: u32 = 20;
|
||||
|
||||
/// If there are any shielded inputs, always have at least two shielded outputs, padding
|
||||
|
@ -265,15 +274,18 @@ impl TransparentInputs {
|
|||
fn apply_signatures(&self, _: &mut TransactionData, _: consensus::BranchId) {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
struct TzeInputInfo<'a, BuildCtx> {
|
||||
prevout: TzeOut,
|
||||
builder: Box<dyn FnOnce(&BuildCtx) -> Result<(u32, Vec<u8>), Error> + 'a>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
struct TzeInputs<'a, BuildCtx> {
|
||||
builders: Vec<TzeInputInfo<'a, BuildCtx>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
impl<'a, BuildCtx> TzeInputs<'a, BuildCtx> {
|
||||
fn default() -> Self {
|
||||
TzeInputs { builders: vec![] }
|
||||
|
@ -339,9 +351,10 @@ pub struct Builder<'a, P: consensus::Parameters, R: RngCore + CryptoRng> {
|
|||
spends: Vec<SpendDescriptionInfo>,
|
||||
outputs: Vec<SaplingOutput>,
|
||||
transparent_inputs: TransparentInputs,
|
||||
#[cfg(feature = "zfuture")]
|
||||
tze_inputs: TzeInputs<'a, TransactionData>,
|
||||
change_address: Option<(OutgoingViewingKey, PaymentAddress)>,
|
||||
phantom: PhantomData<P>,
|
||||
_phantom: &'a PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<'a, P: consensus::Parameters> Builder<'a, P, OsRng> {
|
||||
|
@ -372,6 +385,7 @@ impl<'a, P: consensus::Parameters> Builder<'a, P, OsRng> {
|
|||
/// 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.
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub fn new_zfuture(params: P, height: BlockHeight) -> Self {
|
||||
Builder::new_with_rng_zfuture(params, height, OsRng)
|
||||
}
|
||||
|
@ -405,6 +419,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
|
|||
/// 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.
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub fn new_with_rng_zfuture(params: P, height: BlockHeight, rng: R) -> Builder<'a, P, R> {
|
||||
Self::new_with_mtx(params, height, rng, TransactionData::zfuture())
|
||||
}
|
||||
|
@ -428,9 +443,10 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
|
|||
spends: vec![],
|
||||
outputs: vec![],
|
||||
transparent_inputs: TransparentInputs::default(),
|
||||
#[cfg(feature = "zfuture")]
|
||||
tze_inputs: TzeInputs::default(),
|
||||
change_address: None,
|
||||
phantom: PhantomData,
|
||||
_phantom: &PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -558,7 +574,10 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
|
|||
|
||||
// Valid change
|
||||
let change = self.mtx.value_balance - self.fee + self.transparent_inputs.value_sum()
|
||||
- self.mtx.vout.iter().map(|vo| vo.value).sum::<Amount>()
|
||||
- self.mtx.vout.iter().map(|vo| vo.value).sum::<Amount>();
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
let change = change
|
||||
+ self
|
||||
.tze_inputs
|
||||
.builders
|
||||
|
@ -786,6 +805,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
|
|||
};
|
||||
|
||||
// Create TZE input witnesses
|
||||
#[cfg(feature = "zfuture")]
|
||||
for (i, tze_in) in self.tze_inputs.builders.into_iter().enumerate() {
|
||||
// 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
|
||||
|
@ -810,6 +830,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a>
|
||||
for Builder<'a, P, R>
|
||||
{
|
||||
|
@ -874,7 +895,10 @@ mod tests {
|
|||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||
};
|
||||
|
||||
use super::{Builder, Error, TzeInputs};
|
||||
use super::{Builder, Error};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use super::TzeInputs;
|
||||
|
||||
#[test]
|
||||
fn fails_on_negative_output() {
|
||||
|
@ -913,9 +937,10 @@ mod tests {
|
|||
spends: vec![],
|
||||
outputs: vec![],
|
||||
transparent_inputs: TransparentInputs::default(),
|
||||
#[cfg(feature = "zfuture")]
|
||||
tze_inputs: TzeInputs::default(),
|
||||
change_address: None,
|
||||
phantom: PhantomData,
|
||||
_phantom: &PhantomData,
|
||||
};
|
||||
|
||||
// Create a tx with only t output. No binding_sig should be present
|
||||
|
|
|
@ -5,13 +5,21 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|||
use ff::PrimeField;
|
||||
use group::GroupEncoding;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use crate::extensions::transparent as tze;
|
||||
use crate::legacy::Script;
|
||||
use crate::redjubjub::{PublicKey, Signature};
|
||||
use crate::serialize::{CompactSize, Vector};
|
||||
#[cfg(feature = "zfuture")]
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::{
|
||||
legacy::Script,
|
||||
redjubjub::{PublicKey, Signature},
|
||||
};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use crate::{
|
||||
extensions::transparent as tze,
|
||||
serialize::{CompactSize, Vector},
|
||||
};
|
||||
|
||||
pub mod amount;
|
||||
pub use self::amount::Amount;
|
||||
|
@ -121,19 +129,22 @@ impl TxOut {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
fn to_io_error(_: std::num::TryFromIntError) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::InvalidData, "value out of range")
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub struct TzeIn {
|
||||
pub prevout: OutPoint,
|
||||
pub witness: tze::Witness,
|
||||
}
|
||||
|
||||
fn to_io_error(_: std::num::TryFromIntError) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::InvalidData, "value out of range")
|
||||
}
|
||||
|
||||
/// Transaction encoding and decoding functions conforming to ZIP-222
|
||||
///
|
||||
/// https://zips.z.cash/zip-0222#encoding-in-transactions
|
||||
#[cfg(feature = "zfuture")]
|
||||
impl TzeIn {
|
||||
/// Convenience constructor
|
||||
pub fn new(prevout: OutPoint, extension_id: u32, mode: u32) -> Self {
|
||||
|
@ -198,11 +209,13 @@ impl TzeIn {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub struct TzeOut {
|
||||
pub value: Amount,
|
||||
pub precondition: tze::Precondition,
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
impl TzeOut {
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
let value = {
|
||||
|
|
|
@ -21,9 +21,10 @@ mod tests;
|
|||
|
||||
pub use self::sighash::{signature_hash, signature_hash_data, SignableInput, SIGHASH_ALL};
|
||||
|
||||
use self::components::{
|
||||
Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut, TzeIn, TzeOut,
|
||||
};
|
||||
use self::components::{Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use self::components::{TzeIn, TzeOut};
|
||||
|
||||
const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C48270;
|
||||
const OVERWINTER_TX_VERSION: u32 = 3;
|
||||
|
@ -36,7 +37,9 @@ const SAPLING_TX_VERSION: u32 = 4;
|
|||
/// using these constants should be inspected, and use of these constants
|
||||
/// should be removed as appropriate in favor of the new consensus
|
||||
/// transaction version and group.
|
||||
#[cfg(feature = "zfuture")]
|
||||
const ZFUTURE_VERSION_GROUP_ID: u32 = 0xFFFFFFFF;
|
||||
#[cfg(feature = "zfuture")]
|
||||
const ZFUTURE_TX_VERSION: u32 = 0x0000FFFF;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
|
@ -78,7 +81,9 @@ pub struct TransactionData {
|
|||
pub version_group_id: u32,
|
||||
pub vin: Vec<TxIn>,
|
||||
pub vout: Vec<TxOut>,
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub tze_inputs: Vec<TzeIn>,
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub tze_outputs: Vec<TzeOut>,
|
||||
pub lock_time: u32,
|
||||
pub expiry_height: BlockHeight,
|
||||
|
@ -100,9 +105,7 @@ impl std::fmt::Debug for TransactionData {
|
|||
version = {:?},
|
||||
version_group_id = {:?},
|
||||
vin = {:?},
|
||||
vout = {:?},
|
||||
tze_inputs = {:?},
|
||||
tze_outputs = {:?},
|
||||
vout = {:?},{}
|
||||
lock_time = {:?},
|
||||
expiry_height = {:?},
|
||||
value_balance = {:?},
|
||||
|
@ -116,8 +119,19 @@ impl std::fmt::Debug for TransactionData {
|
|||
self.version_group_id,
|
||||
self.vin,
|
||||
self.vout,
|
||||
self.tze_inputs,
|
||||
self.tze_outputs,
|
||||
{
|
||||
#[cfg(feature = "zfuture")]
|
||||
{
|
||||
format!(
|
||||
"
|
||||
tze_inputs = {:?},
|
||||
tze_outputs = {:?},",
|
||||
self.tze_inputs, self.tze_outputs
|
||||
)
|
||||
}
|
||||
#[cfg(not(feature = "zfuture"))]
|
||||
""
|
||||
},
|
||||
self.lock_time,
|
||||
self.expiry_height,
|
||||
self.value_balance,
|
||||
|
@ -144,7 +158,9 @@ impl TransactionData {
|
|||
version_group_id: SAPLING_VERSION_GROUP_ID,
|
||||
vin: vec![],
|
||||
vout: vec![],
|
||||
#[cfg(feature = "zfuture")]
|
||||
tze_inputs: vec![],
|
||||
#[cfg(feature = "zfuture")]
|
||||
tze_outputs: vec![],
|
||||
lock_time: 0,
|
||||
expiry_height: 0u32.into(),
|
||||
|
@ -158,6 +174,7 @@ impl TransactionData {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub fn zfuture() -> Self {
|
||||
TransactionData {
|
||||
overwintered: true,
|
||||
|
@ -227,9 +244,13 @@ impl Transaction {
|
|||
let is_sapling_v4 = overwintered
|
||||
&& version_group_id == SAPLING_VERSION_GROUP_ID
|
||||
&& version == SAPLING_TX_VERSION;
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
let has_tze = overwintered
|
||||
&& version_group_id == ZFUTURE_VERSION_GROUP_ID
|
||||
&& version == ZFUTURE_TX_VERSION;
|
||||
#[cfg(not(feature = "zfuture"))]
|
||||
let has_tze = false;
|
||||
|
||||
if overwintered && !(is_overwinter_v3 || is_sapling_v4 || has_tze) {
|
||||
return Err(io::Error::new(
|
||||
|
@ -240,6 +261,7 @@ impl Transaction {
|
|||
|
||||
let vin = Vector::read(&mut reader, TxIn::read)?;
|
||||
let vout = Vector::read(&mut reader, TxOut::read)?;
|
||||
#[cfg(feature = "zfuture")]
|
||||
let (tze_inputs, tze_outputs) = if has_tze {
|
||||
let wi = Vector::read(&mut reader, TzeIn::read)?;
|
||||
let wo = Vector::read(&mut reader, TzeOut::read)?;
|
||||
|
@ -306,7 +328,9 @@ impl Transaction {
|
|||
version_group_id,
|
||||
vin,
|
||||
vout,
|
||||
#[cfg(feature = "zfuture")]
|
||||
tze_inputs,
|
||||
#[cfg(feature = "zfuture")]
|
||||
tze_outputs,
|
||||
lock_time,
|
||||
expiry_height,
|
||||
|
@ -333,9 +357,13 @@ impl Transaction {
|
|||
let is_sapling_v4 = self.overwintered
|
||||
&& self.version_group_id == SAPLING_VERSION_GROUP_ID
|
||||
&& self.version == SAPLING_TX_VERSION;
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
let has_tze = self.overwintered
|
||||
&& self.version_group_id == ZFUTURE_VERSION_GROUP_ID
|
||||
&& self.version == ZFUTURE_TX_VERSION;
|
||||
#[cfg(not(feature = "zfuture"))]
|
||||
let has_tze = false;
|
||||
|
||||
if self.overwintered && !(is_overwinter_v3 || is_sapling_v4 || has_tze) {
|
||||
return Err(io::Error::new(
|
||||
|
@ -346,6 +374,7 @@ impl Transaction {
|
|||
|
||||
Vector::write(&mut writer, &self.vin, |w, e| e.write(w))?;
|
||||
Vector::write(&mut writer, &self.vout, |w, e| e.write(w))?;
|
||||
#[cfg(feature = "zfuture")]
|
||||
if has_tze {
|
||||
Vector::write(&mut writer, &self.tze_inputs, |w, e| e.write(w))?;
|
||||
Vector::write(&mut writer, &self.tze_outputs, |w, e| e.write(w))?;
|
||||
|
@ -422,3 +451,147 @@ impl Transaction {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-dependencies")]
|
||||
pub mod testing {
|
||||
use proptest::collection::vec;
|
||||
use proptest::prelude::*;
|
||||
use proptest::sample::select;
|
||||
|
||||
use crate::{consensus::BranchId, legacy::Script};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use crate::extensions::transparent as tze;
|
||||
|
||||
use super::{
|
||||
components::{amount::MAX_MONEY, Amount, OutPoint, TxIn, TxOut},
|
||||
Transaction, TransactionData, OVERWINTER_TX_VERSION, OVERWINTER_VERSION_GROUP_ID,
|
||||
SAPLING_TX_VERSION, SAPLING_VERSION_GROUP_ID,
|
||||
};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use super::{
|
||||
components::{TzeIn, TzeOut},
|
||||
ZFUTURE_TX_VERSION, ZFUTURE_VERSION_GROUP_ID,
|
||||
};
|
||||
|
||||
pub const VALID_OPCODES: [u8; 8] = [
|
||||
0x00, // OP_FALSE,
|
||||
0x51, // OP_1,
|
||||
0x52, // OP_2,
|
||||
0x53, // OP_3,
|
||||
0xac, // OP_CHECKSIG,
|
||||
0x63, // OP_IF,
|
||||
0x65, // OP_VERIF,
|
||||
0x6a, // OP_RETURN,
|
||||
];
|
||||
|
||||
prop_compose! {
|
||||
pub fn arb_outpoint()(hash in prop::array::uniform32(1u8..), n in 1..(100 as u32)) -> OutPoint {
|
||||
OutPoint::new(hash, n)
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
pub fn arb_script()(v in vec(select(&VALID_OPCODES[..]), 1..256)) -> Script {
|
||||
Script(v)
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
pub fn arb_txin()(prevout in arb_outpoint(), script_sig in arb_script(), sequence in any::<u32>()) -> TxIn {
|
||||
TxIn { prevout, script_sig, sequence }
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
pub fn arb_amount()(value in 0..MAX_MONEY) -> Amount {
|
||||
Amount::from_i64(value).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
pub fn arb_txout()(value in arb_amount(), script_pubkey in arb_script()) -> TxOut {
|
||||
TxOut { value, script_pubkey }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
prop_compose! {
|
||||
pub fn arb_witness()(extension_id in 0..(100 as u32), mode in (0..100 as u32), payload in vec(any::<u8>(), 32..256)) -> tze::Witness {
|
||||
tze::Witness { extension_id, mode, payload }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
prop_compose! {
|
||||
pub fn arb_tzein()(prevout in arb_outpoint(), witness in arb_witness()) -> TzeIn {
|
||||
TzeIn { prevout, witness }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
prop_compose! {
|
||||
pub fn arb_precondition()(extension_id in 0..(100 as u32), mode in (0..100 as u32), payload in vec(any::<u8>(), 32..256)) -> tze::Precondition {
|
||||
tze::Precondition { extension_id, mode, payload }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
prop_compose! {
|
||||
fn arb_tzeout()(value in arb_amount(), precondition in arb_precondition()) -> TzeOut {
|
||||
TzeOut { value, precondition }
|
||||
}
|
||||
}
|
||||
|
||||
fn tx_versions(branch_id: BranchId) -> impl Strategy<Value = (u32, u32)> {
|
||||
match branch_id {
|
||||
BranchId::Sprout => (1..(2 as u32)).prop_map(|i| (i, 0)).boxed(),
|
||||
BranchId::Overwinter => {
|
||||
Just((OVERWINTER_TX_VERSION, OVERWINTER_VERSION_GROUP_ID)).boxed()
|
||||
}
|
||||
#[cfg(feature = "zfuture")]
|
||||
BranchId::ZFuture => Just((ZFUTURE_TX_VERSION, ZFUTURE_VERSION_GROUP_ID)).boxed(),
|
||||
_otherwise => Just((SAPLING_TX_VERSION, SAPLING_VERSION_GROUP_ID)).boxed(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
prop_compose! {
|
||||
pub fn arb_txdata(branch_id: BranchId)(
|
||||
(version, version_group_id) in tx_versions(branch_id),
|
||||
vin in vec(arb_txin(), 0..10),
|
||||
vout in vec(arb_txout(), 0..10),
|
||||
tze_inputs in vec(arb_tzein(), 0..10),
|
||||
tze_outputs in vec(arb_tzeout(), 0..10),
|
||||
lock_time in any::<u32>(),
|
||||
expiry_height in any::<u32>(),
|
||||
value_balance in arb_amount(),
|
||||
) -> TransactionData {
|
||||
TransactionData {
|
||||
overwintered: branch_id != BranchId::Sprout,
|
||||
version,
|
||||
version_group_id,
|
||||
vin, vout,
|
||||
tze_inputs: if branch_id == BranchId::ZFuture { tze_inputs } else { vec![] },
|
||||
tze_outputs: if branch_id == BranchId::ZFuture { tze_outputs } else { vec![] },
|
||||
lock_time,
|
||||
expiry_height: expiry_height.into(),
|
||||
value_balance,
|
||||
shielded_spends: vec![], //FIXME
|
||||
shielded_outputs: vec![], //FIXME
|
||||
joinsplits: vec![], //FIXME
|
||||
joinsplit_pubkey: None, //FIXME
|
||||
joinsplit_sig: None, //FIXME
|
||||
binding_sig: None, //FIXME
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
prop_compose! {
|
||||
pub fn arb_tx(branch_id: BranchId)(tx_data in arb_txdata(branch_id)) -> Transaction {
|
||||
Transaction::from_data(tx_data).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#[cfg(feature = "zfuture")]
|
||||
use std::convert::TryInto;
|
||||
|
||||
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
|
||||
|
@ -5,19 +6,24 @@ use byteorder::{LittleEndian, WriteBytesExt};
|
|||
use ff::PrimeField;
|
||||
use group::GroupEncoding;
|
||||
|
||||
use crate::{consensus, legacy::Script};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use crate::{
|
||||
consensus,
|
||||
extensions::transparent::Precondition,
|
||||
legacy::Script,
|
||||
serialize::{CompactSize, Vector},
|
||||
};
|
||||
|
||||
use super::{
|
||||
components::{
|
||||
Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut, TzeIn, TzeOut,
|
||||
},
|
||||
components::{Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut},
|
||||
Transaction, TransactionData, OVERWINTER_VERSION_GROUP_ID, SAPLING_TX_VERSION,
|
||||
SAPLING_VERSION_GROUP_ID, ZFUTURE_VERSION_GROUP_ID,
|
||||
SAPLING_VERSION_GROUP_ID,
|
||||
};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use super::{
|
||||
components::{TzeIn, TzeOut},
|
||||
ZFUTURE_VERSION_GROUP_ID,
|
||||
};
|
||||
|
||||
const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &[u8; 12] = b"ZcashSigHash";
|
||||
|
@ -27,10 +33,15 @@ const ZCASH_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashOutputsHash";
|
|||
const ZCASH_JOINSPLITS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashJSplitsHash";
|
||||
const ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSSpendsHash";
|
||||
const ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSOutputHash";
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
const ZCASH_TZE_INPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"Zcash_TzeInsHash";
|
||||
#[cfg(feature = "zfuture")]
|
||||
const ZCASH_TZE_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashTzeOutsHash";
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
const ZCASH_TZE_SIGNED_INPUT_TAG: &[u8; 1] = &[0x00];
|
||||
#[cfg(feature = "zfuture")]
|
||||
const ZCASH_TRANSPARENT_SIGNED_INPUT_TAG: &[u8; 1] = &[0x01];
|
||||
|
||||
pub const SIGHASH_ALL: u32 = 1;
|
||||
|
@ -56,11 +67,14 @@ macro_rules! update_hash {
|
|||
};
|
||||
}
|
||||
|
||||
/// This is a private enum; when `cfg(feature = "zfuture")` is not
|
||||
/// enabled, SigHashVersion::ZFuture is not constructable.
|
||||
#[derive(PartialEq)]
|
||||
enum SigHashVersion {
|
||||
Sprout,
|
||||
Overwinter,
|
||||
Sapling,
|
||||
#[cfg(feature = "zfuture")]
|
||||
ZFuture,
|
||||
}
|
||||
|
||||
|
@ -70,6 +84,7 @@ impl SigHashVersion {
|
|||
match tx.version_group_id {
|
||||
OVERWINTER_VERSION_GROUP_ID => SigHashVersion::Overwinter,
|
||||
SAPLING_VERSION_GROUP_ID => SigHashVersion::Sapling,
|
||||
#[cfg(feature = "zfuture")]
|
||||
ZFUTURE_VERSION_GROUP_ID => SigHashVersion::ZFuture,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
@ -172,6 +187,7 @@ fn shielded_outputs_hash(shielded_outputs: &[OutputDescription]) -> Blake2bHash
|
|||
.hash(&data)
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
fn tze_inputs_hash(tze_inputs: &[TzeIn]) -> Blake2bHash {
|
||||
let mut data = vec![];
|
||||
for tzein in tze_inputs {
|
||||
|
@ -183,6 +199,7 @@ fn tze_inputs_hash(tze_inputs: &[TzeIn]) -> Blake2bHash {
|
|||
.hash(&data)
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
fn tze_outputs_hash(tze_outputs: &[TzeOut]) -> Blake2bHash {
|
||||
let mut data = vec![];
|
||||
for tzeout in tze_outputs {
|
||||
|
@ -201,6 +218,7 @@ pub enum SignableInput<'a> {
|
|||
script_code: &'a Script,
|
||||
value: Amount,
|
||||
},
|
||||
#[cfg(feature = "zfuture")]
|
||||
Tze {
|
||||
index: usize,
|
||||
precondition: &'a Precondition,
|
||||
|
@ -217,6 +235,7 @@ impl<'a> SignableInput<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub fn tze(index: usize, precondition: &'a Precondition, value: Amount) -> Self {
|
||||
SignableInput::Tze {
|
||||
index,
|
||||
|
@ -234,7 +253,14 @@ pub fn signature_hash_data<'a>(
|
|||
) -> Vec<u8> {
|
||||
let sigversion = SigHashVersion::from_tx(tx);
|
||||
match sigversion {
|
||||
SigHashVersion::Overwinter | SigHashVersion::Sapling | SigHashVersion::ZFuture => {
|
||||
SigHashVersion::Sprout => unimplemented!(),
|
||||
|
||||
// ZIP-243 is implemented as a patch to ZIP-143; ZFuture is temporarily added
|
||||
// as an additional patch to ZIP-243 but that match will likely be removed
|
||||
// due to the need for transaction malleability fixes for TZE deployment.
|
||||
//
|
||||
// SigHashVersion::Overwinter | SigHashVersion::Sapling | SigHashVersion::ZFuture => {
|
||||
_ => {
|
||||
let mut personal = [0; 16];
|
||||
(&mut personal[..12]).copy_from_slice(ZCASH_SIGHASH_PERSONALIZATION_PREFIX);
|
||||
(&mut personal[12..])
|
||||
|
@ -276,6 +302,7 @@ pub fn signature_hash_data<'a>(
|
|||
} else {
|
||||
h.update(&[0; 32]);
|
||||
};
|
||||
#[cfg(feature = "zfuture")]
|
||||
if sigversion == SigHashVersion::ZFuture {
|
||||
update_hash!(
|
||||
h,
|
||||
|
@ -293,7 +320,14 @@ pub fn signature_hash_data<'a>(
|
|||
!tx.joinsplits.is_empty(),
|
||||
joinsplits_hash(tx.version, &tx.joinsplits, &tx.joinsplit_pubkey.unwrap())
|
||||
);
|
||||
if sigversion == SigHashVersion::Sapling || sigversion == SigHashVersion::ZFuture {
|
||||
if sigversion == SigHashVersion::Sapling || {
|
||||
#[cfg(feature = "zfuture")]
|
||||
{
|
||||
sigversion == SigHashVersion::ZFuture
|
||||
}
|
||||
#[cfg(not(feature = "zfuture"))]
|
||||
false
|
||||
} {
|
||||
update_hash!(
|
||||
h,
|
||||
!tx.shielded_spends.is_empty(),
|
||||
|
@ -307,7 +341,14 @@ pub fn signature_hash_data<'a>(
|
|||
}
|
||||
update_u32!(h, tx.lock_time, tmp);
|
||||
update_u32!(h, tx.expiry_height.into(), tmp);
|
||||
if sigversion == SigHashVersion::Sapling || sigversion == SigHashVersion::ZFuture {
|
||||
if sigversion == SigHashVersion::Sapling || {
|
||||
#[cfg(feature = "zfuture")]
|
||||
{
|
||||
sigversion == SigHashVersion::ZFuture
|
||||
}
|
||||
#[cfg(not(feature = "zfuture"))]
|
||||
false
|
||||
} {
|
||||
h.update(&tx.value_balance.to_i64_le_bytes());
|
||||
}
|
||||
update_u32!(h, hash_type, tmp);
|
||||
|
@ -318,6 +359,7 @@ pub fn signature_hash_data<'a>(
|
|||
script_code,
|
||||
value,
|
||||
} => {
|
||||
#[cfg(feature = "zfuture")]
|
||||
let mut data = if sigversion == SigHashVersion::ZFuture {
|
||||
// domain separation here is to avoid collision attacks
|
||||
// between transparent and TZE inputs.
|
||||
|
@ -326,6 +368,9 @@ pub fn signature_hash_data<'a>(
|
|||
vec![]
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "zfuture"))]
|
||||
let mut data = vec![];
|
||||
|
||||
tx.vin[index].prevout.write(&mut data).unwrap();
|
||||
script_code.write(&mut data).unwrap();
|
||||
data.extend_from_slice(&value.to_i64_le_bytes());
|
||||
|
@ -335,6 +380,7 @@ pub fn signature_hash_data<'a>(
|
|||
h.update(&data);
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
SignableInput::Tze {
|
||||
index,
|
||||
precondition,
|
||||
|
@ -353,6 +399,7 @@ pub fn signature_hash_data<'a>(
|
|||
h.update(&data);
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
SignableInput::Tze { .. } => {
|
||||
panic!("A request has been made to sign a TZE input, but the signature hash version is not ZFuture");
|
||||
}
|
||||
|
@ -362,7 +409,6 @@ pub fn signature_hash_data<'a>(
|
|||
|
||||
h.finalize().as_ref().to_vec()
|
||||
}
|
||||
SigHashVersion::Sprout => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,133 +1,22 @@
|
|||
use ff::Field;
|
||||
use rand_core::OsRng;
|
||||
|
||||
use proptest::collection::vec;
|
||||
#[cfg(all(feature = "test-dependencies", feature = "zfuture"))]
|
||||
use proptest::prelude::*;
|
||||
use proptest::sample::select;
|
||||
|
||||
use crate::{
|
||||
consensus::BranchId, constants::SPENDING_KEY_GENERATOR, extensions::transparent as tze,
|
||||
legacy::Script, redjubjub::PrivateKey,
|
||||
};
|
||||
use crate::{constants::SPENDING_KEY_GENERATOR, redjubjub::PrivateKey};
|
||||
|
||||
#[cfg(all(feature = "test-dependencies", feature = "zfuture"))]
|
||||
use crate::consensus::BranchId;
|
||||
|
||||
use super::{
|
||||
components::amount::MAX_MONEY,
|
||||
components::{Amount, OutPoint, TxIn, TxOut, TzeIn, TzeOut},
|
||||
components::Amount,
|
||||
sighash::{signature_hash, SignableInput},
|
||||
Transaction, TransactionData, OVERWINTER_TX_VERSION, OVERWINTER_VERSION_GROUP_ID,
|
||||
SAPLING_TX_VERSION, SAPLING_VERSION_GROUP_ID, ZFUTURE_TX_VERSION, ZFUTURE_VERSION_GROUP_ID,
|
||||
Transaction, TransactionData,
|
||||
};
|
||||
|
||||
prop_compose! {
|
||||
fn arb_outpoint()(hash in prop::array::uniform32(1u8..), n in 1..(100 as u32)) -> OutPoint {
|
||||
OutPoint::new(hash, n)
|
||||
}
|
||||
}
|
||||
|
||||
const VALID_OPCODES: [u8; 8] = [
|
||||
0x00, // OP_FALSE,
|
||||
0x51, // OP_1,
|
||||
0x52, // OP_2,
|
||||
0x53, // OP_3,
|
||||
0xac, // OP_CHECKSIG,
|
||||
0x63, // OP_IF,
|
||||
0x65, // OP_VERIF,
|
||||
0x6a, // OP_RETURN,
|
||||
];
|
||||
|
||||
prop_compose! {
|
||||
fn arb_script()(v in vec(select(&VALID_OPCODES[..]), 1..256)) -> Script {
|
||||
Script(v)
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_txin()(prevout in arb_outpoint(), script_sig in arb_script(), sequence in any::<u32>()) -> TxIn {
|
||||
TxIn { prevout, script_sig, sequence }
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_amount()(value in 0..MAX_MONEY) -> Amount {
|
||||
Amount::from_i64(value).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_txout()(value in arb_amount(), script_pubkey in arb_script()) -> TxOut {
|
||||
TxOut { value, script_pubkey }
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_witness()(extension_id in 0..(100 as u32), mode in (0..100 as u32), payload in vec(any::<u8>(), 32..256)) -> tze::Witness {
|
||||
tze::Witness { extension_id, mode, payload }
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_tzein()(prevout in arb_outpoint(), witness in arb_witness()) -> TzeIn {
|
||||
TzeIn { prevout, witness }
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_precondition()(extension_id in 0..(100 as u32), mode in (0..100 as u32), payload in vec(any::<u8>(), 32..256)) -> tze::Precondition {
|
||||
tze::Precondition { extension_id, mode, payload }
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_tzeout()(value in arb_amount(), precondition in arb_precondition()) -> TzeOut {
|
||||
TzeOut { value, precondition }
|
||||
}
|
||||
}
|
||||
|
||||
fn tx_versions(branch_id: BranchId) -> impl Strategy<Value = (u32, u32)> {
|
||||
match branch_id {
|
||||
BranchId::Sprout => (1..(2 as u32)).prop_map(|i| (i, 0)).boxed(),
|
||||
BranchId::Overwinter => Just((OVERWINTER_TX_VERSION, OVERWINTER_VERSION_GROUP_ID)).boxed(),
|
||||
BranchId::ZFuture => Just((ZFUTURE_TX_VERSION, ZFUTURE_VERSION_GROUP_ID)).boxed(),
|
||||
_otherwise => Just((SAPLING_TX_VERSION, SAPLING_VERSION_GROUP_ID)).boxed(),
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_txdata(branch_id: BranchId)(
|
||||
(version, version_group_id) in tx_versions(branch_id),
|
||||
vin in vec(arb_txin(), 0..10),
|
||||
vout in vec(arb_txout(), 0..10),
|
||||
tze_inputs in vec(arb_tzein(), 0..10),
|
||||
tze_outputs in vec(arb_tzeout(), 0..10),
|
||||
lock_time in any::<u32>(),
|
||||
expiry_height in any::<u32>(),
|
||||
value_balance in arb_amount(),
|
||||
) -> TransactionData {
|
||||
TransactionData {
|
||||
overwintered: branch_id != BranchId::Sprout,
|
||||
version,
|
||||
version_group_id,
|
||||
vin, vout,
|
||||
tze_inputs: if branch_id == BranchId::ZFuture { tze_inputs } else { vec![] },
|
||||
tze_outputs: if branch_id == BranchId::ZFuture { tze_outputs } else { vec![] },
|
||||
lock_time,
|
||||
expiry_height: expiry_height.into(),
|
||||
value_balance,
|
||||
shielded_spends: vec![], //FIXME
|
||||
shielded_outputs: vec![], //FIXME
|
||||
joinsplits: vec![], //FIXME
|
||||
joinsplit_pubkey: None, //FIXME
|
||||
joinsplit_sig: None, //FIXME
|
||||
binding_sig: None, //FIXME
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_tx(branch_id: BranchId)(tx_data in arb_txdata(branch_id)) -> Transaction {
|
||||
Transaction::from_data(tx_data).unwrap()
|
||||
}
|
||||
}
|
||||
#[cfg(all(feature = "test-dependencies", feature = "zfuture"))]
|
||||
use super::testing::arb_tx;
|
||||
|
||||
#[test]
|
||||
fn tx_read_write() {
|
||||
|
@ -186,6 +75,7 @@ fn tx_write_rejects_unexpected_binding_sig() {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "zfuture", feature = "test-dependencies"))]
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_tze_roundtrip(tx in arb_tx(BranchId::ZFuture)) {
|
||||
|
@ -207,6 +97,7 @@ proptest! {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "zfuture")]
|
||||
fn test_tze_tx_parse() {
|
||||
let txn_bytes = vec![
|
||||
0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0x52, 0x52, 0x52, 0x52,
|
||||
|
|
Loading…
Reference in New Issue