Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add transaction version 5 #1824

Merged
merged 29 commits into from
Mar 3, 2021
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d10a2b7
add transaction V5 stub
oxarbitrage Feb 27, 2021
dc014d5
add v5_strategy
oxarbitrage Feb 27, 2021
402aa9e
deduplicate version group ids
oxarbitrage Feb 27, 2021
ba1890f
fix version in serialize
oxarbitrage Feb 27, 2021
5745db8
replace an unreachable
oxarbitrage Feb 27, 2021
7356b86
Update comment for V5 transactions
teor2345 Mar 1, 2021
32fdcff
Add V5 transactions to non_finalized_state
teor2345 Mar 1, 2021
092543b
Fix struct matches
teor2345 Mar 1, 2021
c19c8dc
Apply trivial panic message changes
teor2345 Mar 1, 2021
3817ecd
rustfmt
teor2345 Mar 1, 2021
7bf5356
use TX_V5_VERSION_GROUP_ID
oxarbitrage Mar 1, 2021
2d428ff
fix comment, spacing
oxarbitrage Mar 1, 2021
37eadb7
add zcash_deserialize for V5
oxarbitrage Mar 1, 2021
8c33382
change blocked code in arbitrary
oxarbitrage Mar 1, 2021
21fd59b
make all tx versions explicit in sprout and sapling nullifier functions
oxarbitrage Mar 1, 2021
e851047
rustfmt
oxarbitrage Mar 1, 2021
41d0c69
rustfmt
oxarbitrage Mar 1, 2021
68346ea
replace some unreacheables with unimplemented
oxarbitrage Mar 1, 2021
ef8f0f2
replace tx_in and tx_out with inputs and outputs
oxarbitrage Mar 2, 2021
02e1ff5
match exhaustively in sprout and sapling nullifier functions
oxarbitrage Mar 2, 2021
5fb9963
follow V4 let binding pattern
oxarbitrage Mar 2, 2021
8302d8d
fix matches in zebra-consensus
oxarbitrage Mar 2, 2021
d5f2519
change transaction fields order
oxarbitrage Mar 2, 2021
510b571
fix unblocked NU5 strategy
oxarbitrage Mar 2, 2021
21b85a3
change fields order in arbitrary
oxarbitrage Mar 3, 2021
9de491f
We're still deciding if v5 transactions support Sprout
teor2345 Mar 3, 2021
433626c
Minor log and comment tweaks
teor2345 Mar 3, 2021
48fb77e
Add a missing V5 transaction match case
teor2345 Mar 3, 2021
04c7def
Fix decision date
teor2345 Mar 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions zebra-chain/src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
mod genesis;
mod network;
mod network_upgrade;
mod transaction;

pub use genesis::*;
pub use network::Network;
pub use network_upgrade::*;
pub use transaction::*;

#[cfg(test)]
mod tests;
13 changes: 13 additions & 0 deletions zebra-chain/src/parameters/transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! Transaction consensus and utility parameters.

/// The version group ID for Overwinter transactions.
pub const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C4_8270;

/// The version group ID for Sapling transactions.
pub const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085;

/// The version group ID for version 5 transactions.
///
/// Orchard transactions must use transaction version 5 and this version
/// group ID. Sapling transactions can use v4 or v5 transactions.
pub const TX_V5_VERSION_GROUP_ID: u32 = 0x26A7_270A;
46 changes: 43 additions & 3 deletions zebra-chain/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use crate::{
/// internally by different enum variants. Because we checkpoint on Sapling
/// activation, we do not validate any pre-Sapling transaction types.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
// XXX consider boxing the Optional fields of V4 txs
// XXX consider boxing the Optional fields of V4 and V5 txs
#[allow(clippy::large_enum_variant)]
pub enum Transaction {
/// A fully transparent transaction (`version = 1`).
Expand Down Expand Up @@ -99,6 +99,20 @@ pub enum Transaction {
/// The shielded data for this transaction, if any.
shielded_data: Option<ShieldedData>,
},
/// A `version = 5` transaction, which supports `Sapling` and `Orchard`.
V5 {
/// The earliest time or block height that this transaction can be added to the
/// chain.
lock_time: LockTime,
/// The latest block height that this transaction can be added to the chain.
expiry_height: block::Height,
/// The transparent inputs to the transaction.
inputs: Vec<transparent::Input>,
/// The transparent outputs from the transaction.
outputs: Vec<transparent::Output>,
/// The rest of the transaction as bytes
rest: Vec<u8>,
},
}

impl Transaction {
Expand All @@ -114,6 +128,7 @@ impl Transaction {
Transaction::V2 { ref inputs, .. } => inputs,
Transaction::V3 { ref inputs, .. } => inputs,
Transaction::V4 { ref inputs, .. } => inputs,
Transaction::V5 { ref inputs, .. } => inputs,
}
}

Expand All @@ -124,6 +139,7 @@ impl Transaction {
Transaction::V2 { ref outputs, .. } => outputs,
Transaction::V3 { ref outputs, .. } => outputs,
Transaction::V4 { ref outputs, .. } => outputs,
Transaction::V5 { ref outputs, .. } => outputs,
}
}

Expand All @@ -134,6 +150,7 @@ impl Transaction {
Transaction::V2 { lock_time, .. } => *lock_time,
Transaction::V3 { lock_time, .. } => *lock_time,
Transaction::V4 { lock_time, .. } => *lock_time,
Transaction::V5 { lock_time, .. } => *lock_time,
}
}

Expand All @@ -144,6 +161,7 @@ impl Transaction {
Transaction::V2 { .. } => None,
Transaction::V3 { expiry_height, .. } => Some(*expiry_height),
Transaction::V4 { expiry_height, .. } => Some(*expiry_height),
Transaction::V5 { expiry_height, .. } => Some(*expiry_height),
}
}

teor2345 marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -175,7 +193,20 @@ impl Transaction {
.flat_map(|joinsplit| joinsplit.nullifiers.iter()),
),
// No JoinSplits
_ => Box::new(std::iter::empty()),
Transaction::V1 { .. }
| Transaction::V2 {
joinsplit_data: None,
..
}
| Transaction::V3 {
joinsplit_data: None,
..
}
| Transaction::V4 {
joinsplit_data: None,
..
}
| Transaction::V5 { .. } => Box::new(std::iter::empty()),
}
}

Expand All @@ -189,8 +220,17 @@ impl Transaction {
shielded_data: Some(shielded_data),
..
} => Box::new(shielded_data.nullifiers()),
Transaction::V5 { .. } => {
unimplemented!("v5 transaction format as specified in ZIP-225")
}
// No JoinSplits
_ => Box::new(std::iter::empty()),
Transaction::V1 { .. }
| Transaction::V2 { .. }
| Transaction::V3 { .. }
| Transaction::V4 {
shielded_data: None,
..
} => Box::new(std::iter::empty()),
}
}

Expand Down
28 changes: 25 additions & 3 deletions zebra-chain/src/transaction/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,26 @@ impl Transaction {
)
.boxed()
}
/// Generate a proptest strategy for V5 Transactions
pub fn v5_strategy(ledger_state: LedgerState) -> BoxedStrategy<Self> {
(
transparent::Input::vec_strategy(ledger_state, 10),
vec(any::<transparent::Output>(), 0..10),
any::<LockTime>(),
any::<block::Height>(),
any::<Vec<u8>>(),
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
)
.prop_map(
|(inputs, outputs, lock_time, expiry_height, rest)| Transaction::V5 {
inputs,
outputs,
lock_time,
expiry_height,
rest,
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved
},
)
.boxed()
}

/// Proptest Strategy for creating a Vector of transactions where the first
/// transaction is always the only coinbase transaction
Expand Down Expand Up @@ -236,9 +256,11 @@ impl Arbitrary for Transaction {
NetworkUpgrade::Blossom | NetworkUpgrade::Heartwood | NetworkUpgrade::Canopy => {
Self::v4_strategy(ledger_state)
}
NetworkUpgrade::NU5 => {
unimplemented!("NU5 upgrade can use v4 or v5 transactions, as specified in ZIP-225")
}
NetworkUpgrade::NU5 => prop_oneof![
Self::v4_strategy(ledger_state),
Self::v5_strategy(ledger_state)
]
.boxed(),
}
}

Expand Down
41 changes: 38 additions & 3 deletions zebra-chain/src/transaction/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::{io, sync::Arc};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

use crate::{
parameters::{OVERWINTER_VERSION_GROUP_ID, SAPLING_VERSION_GROUP_ID, TX_V5_VERSION_GROUP_ID},
primitives::ZkSnarkProof,
serialization::{
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashDeserializeInto,
Expand All @@ -16,9 +17,6 @@ use crate::{

use super::*;

const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C4_8270;
const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085;

impl ZcashDeserialize for jubjub::Fq {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let possible_scalar = jubjub::Fq::from_bytes(&reader.read_32_bytes()?);
Expand Down Expand Up @@ -182,6 +180,23 @@ impl ZcashSerialize for Transaction {
None => {}
}
}
Transaction::V5 {
lock_time,
expiry_height,
inputs,
outputs,
rest,
} => {
// Write version 5 and set the fOverwintered bit.
writer.write_u32::<LittleEndian>(5 | (1 << 31))?;
writer.write_u32::<LittleEndian>(TX_V5_VERSION_GROUP_ID)?;
lock_time.zcash_serialize(&mut writer)?;
writer.write_u32::<LittleEndian>(expiry_height.0)?;
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved
inputs.zcash_serialize(&mut writer)?;
outputs.zcash_serialize(&mut writer)?;
// write the rest
writer.write_all(rest)?;
}
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved
}
Ok(())
}
Expand Down Expand Up @@ -285,6 +300,26 @@ impl ZcashDeserialize for Transaction {
joinsplit_data,
})
}
(5, false) => {
let id = reader.read_u32::<LittleEndian>()?;
if id != TX_V5_VERSION_GROUP_ID {
return Err(SerializationError::Parse("expected TX_V5_VERSION_GROUP_ID"));
}
let lock_time = LockTime::zcash_deserialize(&mut reader)?;
let expiry_height = block::Height(reader.read_u32::<LittleEndian>()?);
let inputs = Vec::zcash_deserialize(&mut reader)?;
let outputs = Vec::zcash_deserialize(&mut reader)?;
let mut rest = Vec::new();
reader.read_to_end(&mut rest)?;

Ok(Transaction::V5 {
lock_time,
expiry_height,
inputs,
outputs,
rest,
})
}
(_, _) => Err(SerializationError::Parse("bad tx header")),
}
}
Expand Down
26 changes: 17 additions & 9 deletions zebra-chain/src/transaction/sighash.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use super::Transaction;
use crate::{
parameters::{ConsensusBranchId, NetworkUpgrade},
parameters::{
ConsensusBranchId, NetworkUpgrade, OVERWINTER_VERSION_GROUP_ID, SAPLING_VERSION_GROUP_ID,
TX_V5_VERSION_GROUP_ID,
},
serialization::{WriteZcashExt, ZcashSerialize},
transparent,
};
Expand All @@ -12,9 +15,6 @@ use std::io;
static ZIP143_EXPLANATION: &str = "Invalid transaction version: after Overwinter activation transaction versions 1 and 2 are rejected";
static ZIP243_EXPLANATION: &str = "Invalid transaction version: after Sapling activation transaction versions 1, 2, and 3 are rejected";

const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C4_8270;
const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085;

const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &[u8; 12] = b"ZcashSigHash";
const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashPrevoutHash";
const ZCASH_SEQUENCE_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSequencHash";
Expand Down Expand Up @@ -129,6 +129,7 @@ impl<'a> SigHasher<'a> {
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
Transaction::V3 { .. } => 3 | overwintered_flag,
Transaction::V4 { .. } => 4 | overwintered_flag,
Transaction::V5 { .. } => 5 | overwintered_flag,
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
})
}

Expand All @@ -137,6 +138,7 @@ impl<'a> SigHasher<'a> {
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
Transaction::V3 { .. } => OVERWINTER_VERSION_GROUP_ID,
Transaction::V4 { .. } => SAPLING_VERSION_GROUP_ID,
Transaction::V5 { .. } => TX_V5_VERSION_GROUP_ID,
})
}

Expand Down Expand Up @@ -243,6 +245,9 @@ impl<'a> SigHasher<'a> {
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
Transaction::V3 { joinsplit_data, .. } => joinsplit_data.is_some(),
Transaction::V4 { joinsplit_data, .. } => joinsplit_data.is_some(),
Transaction::V5 { .. } => {
unimplemented!("v5 transaction format as specified in ZIP-225")
}
};

if !has_joinsplits {
Expand Down Expand Up @@ -398,14 +403,15 @@ impl<'a> SigHasher<'a> {
use Transaction::*;

let shielded_data = match self.trans {
Transaction::V4 {
V4 {
shielded_data: Some(shielded_data),
..
} => shielded_data,
Transaction::V4 {
V4 {
shielded_data: None,
..
} => return writer.write_all(&[0; 32]),
V5 { .. } => unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244"),
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
};

Expand Down Expand Up @@ -434,14 +440,15 @@ impl<'a> SigHasher<'a> {
use Transaction::*;

let shielded_data = match self.trans {
Transaction::V4 {
V4 {
shielded_data: Some(shielded_data),
..
} => shielded_data,
Transaction::V4 {
V4 {
shielded_data: None,
..
} => return writer.write_all(&[0; 32]),
V5 { .. } => unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244"),
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
};

Expand All @@ -465,7 +472,8 @@ impl<'a> SigHasher<'a> {
use Transaction::*;

let value_balance = match self.trans {
Transaction::V4 { value_balance, .. } => value_balance,
V4 { value_balance, .. } => value_balance,
V5 { .. } => unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244"),
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
};

Expand Down
3 changes: 3 additions & 0 deletions zebra-consensus/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,9 @@ where

Ok(tx.hash())
}
Transaction::V5 { .. } => {
unimplemented!("v5 transaction validation as specified in ZIP-216, ZIP-224, ZIP-225, and ZIP-244")
}
}
}
.instrument(span)
Expand Down
7 changes: 7 additions & 0 deletions zebra-consensus/src/transaction/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ pub fn has_inputs_and_outputs(tx: &Transaction) -> Result<(), TransactionError>
Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } => {
unreachable!("tx version is checked first")
}
Transaction::V5 { .. } => {
unimplemented!("v5 transaction format as specified in ZIP-225")
}
}
}

Expand Down Expand Up @@ -114,6 +117,10 @@ pub fn coinbase_tx_no_joinsplit_or_spend(tx: &Transaction) -> Result<(), Transac
Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } => {
unreachable!("tx version is checked first")
}

Transaction::V5 { .. } => {
unimplemented!("v5 coinbase validation as specified in ZIP-225 and the draft spec")
}
}
} else {
Ok(())
Expand Down
Loading