Experiment: `Transaction::V6` variants only (#9339)
* Update main.yml * feat: auto-sync upstream * fix: merge errors * Merge pull request #6 from ShieldedLabs/aphelionz/v6-transactions Add Transaction::V6 Variants * fix: enable tx_v6 on zebra-chain when it's enabled on zebra-state * fix: more feature flag dependencies * cleanup: remove prop.txt * Update zebra-chain/src/transaction.rs Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> * cleanup: removing SL-specific workflow * fix: skip some windows-related denies * Update deny.toml Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> * fix: better deny.toml entry for windows-core * Update zebra-chain/src/transaction/serialize.rs Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> * Update zebra-chain/src/transaction/serialize.rs Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> * Update zebra-chain/src/transaction/tests/vectors.rs Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> * Update zebra-chain/src/transaction.rs Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> * feat: passthrough functions for v6 -> v5 * fix: rust fmt --------- Co-authored-by: Mark Henderson <mark@allspice.io> Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com>
This commit is contained in:
parent
cc5c5edd35
commit
d061232312
546
Cargo.lock
546
Cargo.lock
File diff suppressed because it is too large
Load Diff
|
@ -98,6 +98,12 @@ skip-tree = [
|
|||
|
||||
# wait until zcash_client_backend update rustix
|
||||
{ name = "rustix", version = "=0.38.44" },
|
||||
|
||||
# wait for reqwest to update windows-registry
|
||||
{ name = "windows-strings", version = "=0.3.1" },
|
||||
|
||||
# wait for sentry to update windows-core
|
||||
{ name = "windows-core", version = "=0.52.0" }
|
||||
]
|
||||
|
||||
# This section is considered when running `cargo deny check sources`.
|
||||
|
|
|
@ -60,6 +60,8 @@ proptest-impl = [
|
|||
|
||||
bench = ["zebra-test"]
|
||||
|
||||
tx_v6 = []
|
||||
|
||||
[dependencies]
|
||||
|
||||
# Cryptography
|
||||
|
|
|
@ -143,6 +143,28 @@ pub enum Transaction {
|
|||
/// The orchard data for this transaction, if any.
|
||||
orchard_shielded_data: Option<orchard::ShieldedData>,
|
||||
},
|
||||
/// A `version = 6` transaction, which is reserved for current development.
|
||||
#[cfg(feature = "tx_v6")]
|
||||
V6 {
|
||||
/// The Network Upgrade for this transaction.
|
||||
///
|
||||
/// Derived from the ConsensusBranchId field.
|
||||
network_upgrade: NetworkUpgrade,
|
||||
/// 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 sapling shielded data for this transaction, if any.
|
||||
sapling_shielded_data: Option<sapling::ShieldedData<sapling::SharedAnchor>>,
|
||||
/// The orchard data for this transaction, if any.
|
||||
orchard_shielded_data: Option<orchard::ShieldedData>,
|
||||
// TODO: Add the rest of the v6 fields.
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for Transaction {
|
||||
|
@ -253,6 +275,8 @@ impl Transaction {
|
|||
| Transaction::V3 { .. }
|
||||
| Transaction::V4 { .. } => None,
|
||||
Transaction::V5 { .. } => Some(AuthDigest::from(self)),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => Some(AuthDigest::from(self)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,6 +366,8 @@ impl Transaction {
|
|||
match self {
|
||||
Transaction::V1 { .. } | Transaction::V2 { .. } => false,
|
||||
Transaction::V3 { .. } | Transaction::V4 { .. } | Transaction::V5 { .. } => true,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -363,6 +389,8 @@ impl Transaction {
|
|||
Transaction::V3 { .. } => 3,
|
||||
Transaction::V4 { .. } => 4,
|
||||
Transaction::V5 { .. } => 5,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => 6,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,6 +402,8 @@ impl Transaction {
|
|||
| Transaction::V3 { lock_time, .. }
|
||||
| Transaction::V4 { lock_time, .. }
|
||||
| Transaction::V5 { lock_time, .. } => *lock_time,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { lock_time, .. } => *lock_time,
|
||||
};
|
||||
|
||||
// `zcashd` checks that the block height is greater than the lock height.
|
||||
|
@ -421,6 +451,8 @@ impl Transaction {
|
|||
| Transaction::V3 { lock_time, .. }
|
||||
| Transaction::V4 { lock_time, .. }
|
||||
| Transaction::V5 { lock_time, .. } => *lock_time,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { lock_time, .. } => *lock_time,
|
||||
};
|
||||
let mut lock_time_bytes = Vec::new();
|
||||
lock_time
|
||||
|
@ -457,6 +489,15 @@ impl Transaction {
|
|||
block::Height(0) => None,
|
||||
block::Height(expiry_height) => Some(block::Height(*expiry_height)),
|
||||
},
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { expiry_height, .. } => match expiry_height {
|
||||
// # Consensus
|
||||
//
|
||||
// > No limit: To set no limit on transactions (so that they do not expire), nExpiryHeight should be set to 0.
|
||||
// https://zips.z.cash/zip-0203#specification
|
||||
block::Height(0) => None,
|
||||
block::Height(expiry_height) => Some(block::Height(*expiry_height)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,6 +514,10 @@ impl Transaction {
|
|||
Transaction::V5 {
|
||||
network_upgrade, ..
|
||||
} => Some(*network_upgrade),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
network_upgrade, ..
|
||||
} => Some(*network_upgrade),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,6 +531,8 @@ impl Transaction {
|
|||
Transaction::V3 { ref inputs, .. } => inputs,
|
||||
Transaction::V4 { ref inputs, .. } => inputs,
|
||||
Transaction::V5 { ref inputs, .. } => inputs,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { ref inputs, .. } => inputs,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -504,6 +551,8 @@ impl Transaction {
|
|||
Transaction::V3 { ref outputs, .. } => outputs,
|
||||
Transaction::V4 { ref outputs, .. } => outputs,
|
||||
Transaction::V5 { ref outputs, .. } => outputs,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { ref outputs, .. } => outputs,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,6 +601,8 @@ impl Transaction {
|
|||
..
|
||||
}
|
||||
| Transaction::V5 { .. } => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -587,6 +638,8 @@ impl Transaction {
|
|||
..
|
||||
}
|
||||
| Transaction::V5 { .. } => 0,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,6 +679,8 @@ impl Transaction {
|
|||
..
|
||||
}
|
||||
| Transaction::V5 { .. } => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -662,6 +717,8 @@ impl Transaction {
|
|||
..
|
||||
}
|
||||
| Transaction::V5 { .. } => None,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -670,6 +727,8 @@ impl Transaction {
|
|||
match self {
|
||||
// No JoinSplits
|
||||
Transaction::V1 { .. } | Transaction::V5 { .. } => false,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => false,
|
||||
|
||||
// JoinSplits-on-BCTV14
|
||||
Transaction::V2 { joinsplit_data, .. } | Transaction::V3 { joinsplit_data, .. } => {
|
||||
|
@ -717,6 +776,8 @@ impl Transaction {
|
|||
}
|
||||
| Transaction::V1 { .. }
|
||||
| Transaction::V5 { .. } => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -738,6 +799,12 @@ impl Transaction {
|
|||
..
|
||||
} => Box::new(sapling_shielded_data.anchors()),
|
||||
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => Box::new(sapling_shielded_data.anchors()),
|
||||
|
||||
// No Spends
|
||||
Transaction::V1 { .. }
|
||||
| Transaction::V2 { .. }
|
||||
|
@ -750,6 +817,11 @@ impl Transaction {
|
|||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -775,6 +847,11 @@ impl Transaction {
|
|||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => Box::new(sapling_shielded_data.spends_per_anchor()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => Box::new(sapling_shielded_data.spends_per_anchor()),
|
||||
|
||||
// No Spends
|
||||
Transaction::V1 { .. }
|
||||
|
@ -788,6 +865,11 @@ impl Transaction {
|
|||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -803,6 +885,11 @@ impl Transaction {
|
|||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => Box::new(sapling_shielded_data.outputs()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => Box::new(sapling_shielded_data.outputs()),
|
||||
|
||||
// No Outputs
|
||||
Transaction::V1 { .. }
|
||||
|
@ -816,6 +903,11 @@ impl Transaction {
|
|||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -833,6 +925,11 @@ impl Transaction {
|
|||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => Box::new(sapling_shielded_data.nullifiers()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => Box::new(sapling_shielded_data.nullifiers()),
|
||||
|
||||
// No Spends
|
||||
Transaction::V1 { .. }
|
||||
|
@ -846,6 +943,11 @@ impl Transaction {
|
|||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -863,6 +965,11 @@ impl Transaction {
|
|||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => Box::new(sapling_shielded_data.note_commitments()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => Box::new(sapling_shielded_data.note_commitments()),
|
||||
|
||||
// No Spends
|
||||
Transaction::V1 { .. }
|
||||
|
@ -876,6 +983,11 @@ impl Transaction {
|
|||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -891,6 +1003,11 @@ impl Transaction {
|
|||
sapling_shielded_data,
|
||||
..
|
||||
} => sapling_shielded_data.is_some(),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data,
|
||||
..
|
||||
} => sapling_shielded_data.is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -905,6 +1022,11 @@ impl Transaction {
|
|||
orchard_shielded_data,
|
||||
..
|
||||
} => orchard_shielded_data.as_ref(),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
orchard_shielded_data,
|
||||
..
|
||||
} => orchard_shielded_data.as_ref(),
|
||||
|
||||
// No Orchard shielded data
|
||||
Transaction::V1 { .. }
|
||||
|
@ -1029,6 +1151,8 @@ impl Transaction {
|
|||
..
|
||||
}
|
||||
| Transaction::V5 { .. } => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1076,6 +1200,8 @@ impl Transaction {
|
|||
..
|
||||
}
|
||||
| Transaction::V5 { .. } => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1117,6 +1243,8 @@ impl Transaction {
|
|||
..
|
||||
}
|
||||
| Transaction::V5 { .. } => Box::new(iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => Box::new(iter::empty()),
|
||||
};
|
||||
|
||||
joinsplit_value_balances.map(ValueBalance::from_sprout_amount)
|
||||
|
@ -1158,6 +1286,11 @@ impl Transaction {
|
|||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => sapling_shielded_data.value_balance,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => sapling_shielded_data.value_balance,
|
||||
|
||||
Transaction::V1 { .. }
|
||||
| Transaction::V2 { .. }
|
||||
|
@ -1170,6 +1303,11 @@ impl Transaction {
|
|||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Amount::zero(),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: None,
|
||||
..
|
||||
} => Amount::zero(),
|
||||
};
|
||||
|
||||
ValueBalance::from_sapling_amount(sapling_value_balance)
|
||||
|
@ -1290,6 +1428,14 @@ impl Transaction {
|
|||
*network_upgrade = nu;
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
ref mut network_upgrade,
|
||||
..
|
||||
} => {
|
||||
*network_upgrade = nu;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1315,6 +1461,11 @@ impl Transaction {
|
|||
ref mut expiry_height,
|
||||
..
|
||||
} => expiry_height,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
ref mut expiry_height,
|
||||
..
|
||||
} => expiry_height,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1326,6 +1477,8 @@ impl Transaction {
|
|||
Transaction::V3 { ref mut inputs, .. } => inputs,
|
||||
Transaction::V4 { ref mut inputs, .. } => inputs,
|
||||
Transaction::V5 { ref mut inputs, .. } => inputs,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { ref mut inputs, .. } => inputs,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1352,6 +1505,11 @@ impl Transaction {
|
|||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => Some(&mut sapling_shielded_data.value_balance),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: Some(sapling_shielded_data),
|
||||
..
|
||||
} => Some(&mut sapling_shielded_data.value_balance),
|
||||
Transaction::V1 { .. }
|
||||
| Transaction::V2 { .. }
|
||||
| Transaction::V3 { .. }
|
||||
|
@ -1363,6 +1521,11 @@ impl Transaction {
|
|||
sapling_shielded_data: None,
|
||||
..
|
||||
} => None,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data: None,
|
||||
..
|
||||
} => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1411,6 +1574,8 @@ impl Transaction {
|
|||
..
|
||||
}
|
||||
| Transaction::V5 { .. } => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1459,6 +1624,8 @@ impl Transaction {
|
|||
..
|
||||
}
|
||||
| Transaction::V5 { .. } => Box::new(std::iter::empty()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1477,6 +1644,11 @@ impl Transaction {
|
|||
orchard_shielded_data: Some(orchard_shielded_data),
|
||||
..
|
||||
} => Some(orchard_shielded_data),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
orchard_shielded_data: Some(orchard_shielded_data),
|
||||
..
|
||||
} => Some(orchard_shielded_data),
|
||||
|
||||
Transaction::V1 { .. }
|
||||
| Transaction::V2 { .. }
|
||||
|
@ -1486,6 +1658,11 @@ impl Transaction {
|
|||
orchard_shielded_data: None,
|
||||
..
|
||||
} => None,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
orchard_shielded_data: None,
|
||||
..
|
||||
} => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1507,6 +1684,10 @@ impl Transaction {
|
|||
Transaction::V5 {
|
||||
ref mut outputs, ..
|
||||
} => outputs,
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
ref mut outputs, ..
|
||||
} => outputs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -924,6 +924,8 @@ pub fn transaction_to_fake_v5(
|
|||
orchard_shielded_data: None,
|
||||
},
|
||||
v5 @ V5 { .. } => v5.clone(),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
v6 @ V6 { .. } => v6.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1008,6 +1010,8 @@ pub fn v5_transactions<'b>(
|
|||
| Transaction::V3 { .. }
|
||||
| Transaction::V4 { .. } => None,
|
||||
ref tx @ Transaction::V5 { .. } => Some(tx.clone()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
ref tx @ Transaction::V6 { .. } => Some(tx.clone()),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -672,6 +672,55 @@ impl ZcashSerialize for Transaction {
|
|||
// `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`.
|
||||
orchard_shielded_data.zcash_serialize(&mut writer)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
network_upgrade,
|
||||
lock_time,
|
||||
expiry_height,
|
||||
inputs,
|
||||
outputs,
|
||||
sapling_shielded_data,
|
||||
orchard_shielded_data,
|
||||
} => {
|
||||
// Transaction V6 spec:
|
||||
// https://zips.z.cash/zip-0230#specification
|
||||
|
||||
// Denoted as `nVersionGroupId` in the spec.
|
||||
writer.write_u32::<LittleEndian>(TX_V5_VERSION_GROUP_ID)?;
|
||||
|
||||
// Denoted as `nConsensusBranchId` in the spec.
|
||||
writer.write_u32::<LittleEndian>(u32::from(
|
||||
network_upgrade
|
||||
.branch_id()
|
||||
.expect("valid transactions must have a network upgrade with a branch id"),
|
||||
))?;
|
||||
|
||||
// Denoted as `lock_time` in the spec.
|
||||
lock_time.zcash_serialize(&mut writer)?;
|
||||
|
||||
// Denoted as `nExpiryHeight` in the spec.
|
||||
writer.write_u32::<LittleEndian>(expiry_height.0)?;
|
||||
|
||||
// Denoted as `tx_in_count` and `tx_in` in the spec.
|
||||
inputs.zcash_serialize(&mut writer)?;
|
||||
|
||||
// Denoted as `tx_out_count` and `tx_out` in the spec.
|
||||
outputs.zcash_serialize(&mut writer)?;
|
||||
|
||||
// A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`,
|
||||
// `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`,
|
||||
// `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and
|
||||
// `bindingSigSapling`.
|
||||
sapling_shielded_data.zcash_serialize(&mut writer)?;
|
||||
|
||||
// A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`,
|
||||
// `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`,
|
||||
// `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`.
|
||||
orchard_shielded_data.zcash_serialize(&mut writer)?;
|
||||
|
||||
// TODO: Add the rest of v6 transaction fields.
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -908,6 +908,36 @@ fn binding_signatures() {
|
|||
)
|
||||
.expect("a valid redjubjub::VerificationKey");
|
||||
|
||||
bvk.verify(sighash.as_ref(), &sapling_shielded_data.binding_sig)
|
||||
.expect("verification passes");
|
||||
|
||||
at_least_one_v5_checked = true;
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data,
|
||||
..
|
||||
} => {
|
||||
if let Some(sapling_shielded_data) = sapling_shielded_data {
|
||||
// V6 txs have the outputs spent by their transparent inputs hashed into
|
||||
// their SIGHASH, so we need to exclude txs with transparent inputs.
|
||||
//
|
||||
// References:
|
||||
//
|
||||
// <https://zips.z.cash/zip-0244#s-2c-amounts-sig-digest>
|
||||
// <https://zips.z.cash/zip-0244#s-2d-scriptpubkeys-sig-digest>
|
||||
if tx.has_transparent_inputs() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let sighash = tx.sighash(nu, HashType::ALL, &[], None);
|
||||
|
||||
let bvk = redjubjub::VerificationKey::try_from(
|
||||
sapling_shielded_data.binding_verification_key(),
|
||||
)
|
||||
.expect("a valid redjubjub::VerificationKey");
|
||||
|
||||
bvk.verify(sighash.as_ref(), &sapling_shielded_data.binding_sig)
|
||||
.expect("verification passes");
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ impl<'a> TxIdBuilder<'a> {
|
|||
| Transaction::V3 { .. }
|
||||
| Transaction::V4 { .. } => self.txid_v1_to_v4(),
|
||||
Transaction::V5 { .. } => self.txid_v5(),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => self.txid_v6(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,4 +50,10 @@ impl<'a> TxIdBuilder<'a> {
|
|||
// We compute v5 txid (from ZIP-244) using librustzcash.
|
||||
Some(Hash(*self.trans.to_librustzcash(nu).ok()?.txid().as_ref()))
|
||||
}
|
||||
|
||||
/// Passthrough to txid_v5 for V6 transactions.
|
||||
#[cfg(feature = "tx_v6")]
|
||||
fn txid_v6(self) -> Option<Hash> {
|
||||
self.txid_v5()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,6 +142,8 @@ impl From<&Transaction> for UnminedTxId {
|
|||
match transaction {
|
||||
V1 { .. } | V2 { .. } | V3 { .. } | V4 { .. } => Legacy(transaction.into()),
|
||||
V5 { .. } => Witnessed(transaction.into()),
|
||||
#[cfg(feature = "tx_v6")]
|
||||
V6 { .. } => Witnessed(transaction.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ getblocktemplate-rpcs = [
|
|||
"zebra-chain/getblocktemplate-rpcs",
|
||||
]
|
||||
|
||||
tx_v6 = ["zebra-chain/tx_v6", "zebra-state/tx_v6"]
|
||||
|
||||
# Test-only features
|
||||
proptest-impl = ["proptest", "proptest-derive", "zebra-chain/proptest-impl", "zebra-state/proptest-impl"]
|
||||
|
||||
|
|
|
@ -523,6 +523,19 @@ where
|
|||
sapling_shielded_data,
|
||||
orchard_shielded_data,
|
||||
)?,
|
||||
#[cfg(feature="tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data,
|
||||
orchard_shielded_data,
|
||||
..
|
||||
} => Self::verify_v6_transaction(
|
||||
&req,
|
||||
&network,
|
||||
script_verifier,
|
||||
cached_ffi_transaction.clone(),
|
||||
sapling_shielded_data,
|
||||
orchard_shielded_data,
|
||||
)?,
|
||||
};
|
||||
|
||||
if let Some(unmined_tx) = req.mempool_transaction() {
|
||||
|
@ -1027,6 +1040,26 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Passthrough to verify_v5_transaction, but for V6 transactions.
|
||||
#[cfg(feature = "tx_v6")]
|
||||
fn verify_v6_transaction(
|
||||
request: &Request,
|
||||
network: &Network,
|
||||
script_verifier: script::Verifier,
|
||||
cached_ffi_transaction: Arc<CachedFfiTransaction>,
|
||||
sapling_shielded_data: &Option<sapling::ShieldedData<sapling::SharedAnchor>>,
|
||||
orchard_shielded_data: &Option<orchard::ShieldedData>,
|
||||
) -> Result<AsyncChecks, TransactionError> {
|
||||
Self::verify_v5_transaction(
|
||||
request,
|
||||
network,
|
||||
script_verifier,
|
||||
cached_ffi_transaction,
|
||||
sapling_shielded_data,
|
||||
orchard_shielded_data,
|
||||
)
|
||||
}
|
||||
|
||||
/// Verifies if a transaction's transparent inputs are valid using the provided
|
||||
/// `script_verifier` and `cached_ffi_transaction`.
|
||||
///
|
||||
|
|
|
@ -48,6 +48,8 @@ elasticsearch = [
|
|||
"zebra-chain/elasticsearch",
|
||||
]
|
||||
|
||||
tx_v6 = ["zebra-chain/tx_v6"]
|
||||
|
||||
[dependencies]
|
||||
bincode = { workspace = true }
|
||||
chrono = { workspace = true, features = ["clock", "std"] }
|
||||
|
|
|
@ -1563,6 +1563,22 @@ impl Chain {
|
|||
sapling_shielded_data,
|
||||
orchard_shielded_data,
|
||||
),
|
||||
#[cfg(feature="tx_v6")]
|
||||
V6 {
|
||||
inputs,
|
||||
outputs,
|
||||
sapling_shielded_data,
|
||||
orchard_shielded_data,
|
||||
..
|
||||
} => (
|
||||
inputs,
|
||||
outputs,
|
||||
&None,
|
||||
&None,
|
||||
sapling_shielded_data,
|
||||
orchard_shielded_data,
|
||||
),
|
||||
|
||||
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(
|
||||
"older transaction versions only exist in finalized blocks, because of the mandatory canopy checkpoint",
|
||||
),
|
||||
|
@ -1731,6 +1747,22 @@ impl UpdateWith<ContextuallyVerifiedBlock> for Chain {
|
|||
sapling_shielded_data,
|
||||
orchard_shielded_data,
|
||||
),
|
||||
#[cfg(feature="tx_v6")]
|
||||
V6 {
|
||||
inputs,
|
||||
outputs,
|
||||
sapling_shielded_data,
|
||||
orchard_shielded_data,
|
||||
..
|
||||
} => (
|
||||
inputs,
|
||||
outputs,
|
||||
&None,
|
||||
&None,
|
||||
sapling_shielded_data,
|
||||
orchard_shielded_data,
|
||||
),
|
||||
|
||||
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(
|
||||
"older transaction versions only exist in finalized blocks, because of the mandatory canopy checkpoint",
|
||||
),
|
||||
|
|
|
@ -34,6 +34,8 @@ impl FakeChainHelper for Arc<Block> {
|
|||
Transaction::V3 { inputs, .. } => &mut inputs[0],
|
||||
Transaction::V4 { inputs, .. } => &mut inputs[0],
|
||||
Transaction::V5 { inputs, .. } => &mut inputs[0],
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { inputs, .. } => &mut inputs[0],
|
||||
};
|
||||
|
||||
match input {
|
||||
|
|
|
@ -158,6 +158,8 @@ test_sync_to_mandatory_checkpoint_testnet = []
|
|||
test_sync_past_mandatory_checkpoint_mainnet = []
|
||||
test_sync_past_mandatory_checkpoint_testnet = []
|
||||
|
||||
tx_v6 = ["zebra-chain/tx_v6", "zebra-state/tx_v6", "zebra-consensus/tx_v6"]
|
||||
|
||||
[dependencies]
|
||||
zebra-chain = { path = "../zebra-chain", version = "1.0.0-beta.45" }
|
||||
zebra-consensus = { path = "../zebra-consensus", version = "1.0.0-beta.45" }
|
||||
|
|
|
@ -568,6 +568,8 @@ impl SpendConflictTestInput {
|
|||
|
||||
// No JoinSplits
|
||||
Transaction::V1 { .. } | Transaction::V5 { .. } => {}
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -638,6 +640,14 @@ impl SpendConflictTestInput {
|
|||
Self::remove_sapling_transfers_with_conflicts(sapling_shielded_data, &conflicts)
|
||||
}
|
||||
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
sapling_shielded_data,
|
||||
..
|
||||
} => {
|
||||
Self::remove_sapling_transfers_with_conflicts(sapling_shielded_data, &conflicts)
|
||||
}
|
||||
|
||||
// No Spends
|
||||
Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } => {}
|
||||
}
|
||||
|
@ -709,6 +719,12 @@ impl SpendConflictTestInput {
|
|||
..
|
||||
} => Self::remove_orchard_actions_with_conflicts(orchard_shielded_data, &conflicts),
|
||||
|
||||
#[cfg(feature = "tx_v6")]
|
||||
Transaction::V6 {
|
||||
orchard_shielded_data,
|
||||
..
|
||||
} => Self::remove_orchard_actions_with_conflicts(orchard_shielded_data, &conflicts),
|
||||
|
||||
// No Spends
|
||||
Transaction::V1 { .. }
|
||||
| Transaction::V2 { .. }
|
||||
|
|
Loading…
Reference in New Issue