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

docs: Transaction consensus rules: Size rules #3461

Merged
merged 6 commits into from
Feb 8, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
16 changes: 11 additions & 5 deletions zebra-chain/src/orchard/shielded_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,12 @@ impl TrustedPreallocate for Action {
// and the signature is required,
// a valid max allocation can never exceed this size
const MAX: u64 = (MAX_BLOCK_BYTES - 1) / AUTHORIZED_ACTION_SIZE;
// # Consensus
//
// > [NU5 onward] nSpendsSapling, nOutputsSapling, and nActionsOrchard MUST all be less than 2^16.
// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
//
// https://zips.z.cash/protocol/protocol.pdf#txnconsensus
//
// This acts as nActionsOrchard and is therefore subject to the rule.
// The maximum value is actually smaller due to the block size limit,
// but we ensure the 2^16 limit with a static assertion.
Expand All @@ -206,13 +210,15 @@ bitflags! {
/// The spend and output flags are passed to the `Halo2Proof` verifier, which verifies
/// the relevant note spending and creation consensus rules.
///
/// Consensus rules:
/// # Consensus
///
/// > [NU5 onward] In a version 5 transaction, the reserved bits 2..7 of the flagsOrchard
/// > field MUST be zero.
///
/// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
///
/// - "In a version 5 transaction, the reserved bits 2..7 of the flagsOrchard field MUST be zero."
/// ([`bitflags`](https://docs.rs/bitflags/1.2.1/bitflags/index.html) restricts its values to the
/// set of valid flags)
/// - "In a version 5 coinbase transaction, the enableSpendsOrchard flag MUST be 0."
/// (Checked in zebra-consensus)
#[derive(Deserialize, Serialize)]
pub struct Flags: u8 {
/// Enable spending non-zero valued Orchard notes.
Expand Down
6 changes: 5 additions & 1 deletion zebra-chain/src/sapling/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,12 @@ impl TrustedPreallocate for OutputInTransactionV4 {
// Since a serialized Vec<Output> uses at least one byte for its length,
// the max allocation can never exceed (MAX_BLOCK_BYTES - 1) / OUTPUT_SIZE
const MAX: u64 = (MAX_BLOCK_BYTES - 1) / OUTPUT_SIZE;
// # Consensus
//
// > [NU5 onward] nSpendsSapling, nOutputsSapling, and nActionsOrchard MUST all be less than 2^16.
// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
//
// https://zips.z.cash/protocol/protocol.pdf#txnconsensus
//
// This acts as nOutputsSapling and is therefore subject to the rule.
// The maximum value is actually smaller due to the block size limit,
// but we ensure the 2^16 limit with a static assertion.
Expand Down
15 changes: 0 additions & 15 deletions zebra-chain/src/sapling/shielded_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,6 @@ where
AnchorV: AnchorVariant + Clone,
{
/// The net value of Sapling spend transfers minus output transfers.
///
/// [`ShieldedData`] validates this [value balance consensus
/// rule](https://zips.z.cash/protocol/nu5.pdf#txnencodingandconsensus):
///
/// "If effectiveVersion = 4 and there are no Spend descriptions or Output
/// descriptions, then valueBalanceSapling MUST be 0."
///
/// During deserialization, this rule is checked when there are no spends and
/// no outputs.
///
/// During serialization, this rule is structurally validated by [`ShieldedData`].
/// `value_balance` is a field in [`ShieldedData`], which must have at least
/// one spend or output in its `transfers` field. If [`ShieldedData`] is `None`
/// then there can not possibly be any spends or outputs, and the
/// `value_balance` is always serialized as zero.
pub value_balance: Amount,

/// A bundle of spends and outputs, containing at least one spend or
Expand Down
6 changes: 5 additions & 1 deletion zebra-chain/src/sapling/spend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,12 @@ impl TrustedPreallocate for SpendPrefixInTransactionV5 {
// and the associated fields are required,
// a valid max allocation can never exceed this size
const MAX: u64 = (MAX_BLOCK_BYTES - 1) / SHARED_ANCHOR_SPEND_SIZE;
// # Consensus
//
// > [NU5 onward] nSpendsSapling, nOutputsSapling, and nActionsOrchard MUST all be less than 2^16.
// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
//
// https://zips.z.cash/protocol/protocol.pdf#txnconsensus
//
// This acts as nSpendsSapling and is therefore subject to the rule.
// The maximum value is actually smaller due to the block size limit,
// but we ensure the 2^16 limit with a static assertion.
Expand Down
21 changes: 0 additions & 21 deletions zebra-chain/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,14 +223,6 @@ impl Transaction {
// other properties

/// Does this transaction have transparent or shielded inputs?
///
/// "[Sapling onward] If effectiveVersion < 5, then at least one of tx_in_count,
/// nSpendsSapling, and nJoinSplit MUST be nonzero.
///
/// [NU5 onward] If effectiveVersion ≥ 5 then this condition MUST hold:
/// tx_in_count > 0 or nSpendsSapling > 0 or (nActionsOrchard > 0 and enableSpendsOrchard = 1)."
///
/// https://zips.z.cash/protocol/protocol.pdf#txnconsensus
pub fn has_transparent_or_shielded_inputs(&self) -> bool {
!self.inputs().is_empty() || self.has_shielded_inputs()
}
Expand All @@ -249,14 +241,6 @@ impl Transaction {
}

/// Does this transaction have transparent or shielded outputs?
///
/// "[Sapling onward] If effectiveVersion < 5, then at least one of tx_out_count,
/// nOutputsSapling, and nJoinSplit MUST be nonzero.
///
/// [NU5 onward] If effectiveVersion ≥ 5 then this condition MUST hold:
/// tx_out_count > 0 or nOutputsSapling > 0 or (nActionsOrchard > 0 and enableOutputsOrchard = 1)."
///
/// https://zips.z.cash/protocol/protocol.pdf#txnconsensus
pub fn has_transparent_or_shielded_outputs(&self) -> bool {
!self.outputs().is_empty() || self.has_shielded_outputs()
}
Expand All @@ -275,11 +259,6 @@ impl Transaction {
}

/// Does this transaction has at least one flag when we have at least one orchard action?
///
/// [NU5 onward] If effectiveVersion >= 5 and nActionsOrchard > 0, then at least one
/// of enableSpendsOrchard and enableOutputsOrchard MUST be 1.
///
/// https://zips.z.cash/protocol/protocol.pdf#txnconsensus
pub fn has_enough_orchard_flags(&self) -> bool {
if self.version() < 5 || self.orchard_actions().count() == 0 {
return true;
Expand Down
20 changes: 17 additions & 3 deletions zebra-chain/src/transaction/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,17 @@ impl ZcashSerialize for Transaction {

impl ZcashDeserialize for Transaction {
fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
// # Consensus
//
// > [Pre-Sapling] The encoded size of the transaction MUST be less than or
// > equal to 100000 bytes.
//
// https://zips.z.cash/protocol/protocol.pdf#txnconsensus
//
// Zebra does not verify this rule because we checkpoint up to Canopy blocks, but:
// Since transactions must get mined into a block to be useful,
// we reject transactions that are larger than blocks.
//
// If the limit is reached, we'll get an UnexpectedEof error.
let mut limited_reader = reader.take(MAX_BLOCK_BYTES);

Expand Down Expand Up @@ -638,9 +649,12 @@ impl ZcashDeserialize for Transaction {
outputs: shielded_outputs.try_into().expect("checked for outputs"),
})
} else {
// There are no shielded outputs and no shielded spends, so the value balance
// MUST be zero:
// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
// # Consensus
//
// > [Sapling onward] If effectiveVersion = 4 and there are no Spend
// > descriptions or Output descriptions, then valueBalanceSapling MUST be 0.
//
// https://zips.z.cash/protocol/protocol.pdf#txnconsensus
if value_balance != 0 {
return Err(SerializationError::BadTransactionBalance);
}
Expand Down
32 changes: 21 additions & 11 deletions zebra-consensus/src/transaction/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,27 @@ pub fn lock_time_has_passed(

/// Checks that the transaction has inputs and outputs.
///
/// # Consensus
///
/// For `Transaction::V4`:
/// * At least one of `tx_in_count`, `nSpendsSapling`, and `nJoinSplit` MUST be non-zero.
/// * At least one of `tx_out_count`, `nOutputsSapling`, and `nJoinSplit` MUST be non-zero.
///
/// > [Sapling onward] If effectiveVersion < 5, then at least one of
/// > tx_in_count, nSpendsSapling, and nJoinSplit MUST be nonzero.
///
/// > [Sapling onward] If effectiveVersion < 5, then at least one of
/// > tx_out_count, nOutputsSapling, and nJoinSplit MUST be nonzero.
///
/// For `Transaction::V5`:
/// * This condition must hold: `tx_in_count` > 0 or `nSpendsSapling` > 0 or
/// (`nActionsOrchard` > 0 and `enableSpendsOrchard` = 1)
/// * This condition must hold: `tx_out_count` > 0 or `nOutputsSapling` > 0 or
/// (`nActionsOrchard` > 0 and `enableOutputsOrchard` = 1)
///
/// This check counts both `Coinbase` and `PrevOut` transparent inputs.
/// > [NU5 onward] If effectiveVersion >= 5 then this condition MUST hold:
/// > tx_in_count > 0 or nSpendsSapling > 0 or (nActionsOrchard > 0 and enableSpendsOrchard = 1).
///
/// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
/// > [NU5 onward] If effectiveVersion >= 5 then this condition MUST hold:
/// > tx_out_count > 0 or nOutputsSapling > 0 or (nActionsOrchard > 0 and enableOutputsOrchard = 1).
///
/// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
///
/// This check counts both `Coinbase` and `PrevOut` transparent inputs.
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved
pub fn has_inputs_and_outputs(tx: &Transaction) -> Result<(), TransactionError> {
if !tx.has_transparent_or_shielded_inputs() {
Err(TransactionError::NoInputs)
Expand All @@ -85,11 +93,13 @@ pub fn has_inputs_and_outputs(tx: &Transaction) -> Result<(), TransactionError>

/// Checks that the transaction has enough orchard flags.
///
/// # Consensus
///
/// For `Transaction::V5` only:
/// * If `orchard_actions_count` > 0 then at least one of
/// `ENABLE_SPENDS|ENABLE_OUTPUTS` must be active.
///
/// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
/// > [NU5 onward] If effectiveVersion >= 5 and nActionsOrchard > 0, then at least one of enableSpendsOrchard and enableOutputsOrchard MUST be 1.
///
/// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved
pub fn has_enough_orchard_flags(tx: &Transaction) -> Result<(), TransactionError> {
if !tx.has_enough_orchard_flags() {
return Err(TransactionError::NotEnoughFlags);
Expand Down