docs: Transaction consensus rules: Size rules (#3461)
* refactor transaction size consensus rules * quote mssing consensus rule * nit Co-authored-by: teor <teor@riseup.net> * move consensus rule doc Co-authored-by: teor <teor@riseup.net> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
fc3cba24f8
commit
29ad801a35
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -323,8 +323,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.
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -567,6 +567,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);
|
||||
|
||||
|
|
@ -687,9 +698,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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
///
|
||||
/// > [NU5 onward] If effectiveVersion >= 5 then this condition MUST hold:
|
||||
/// > tx_in_count > 0 or nSpendsSapling > 0 or (nActionsOrchard > 0 and enableSpendsOrchard = 1).
|
||||
///
|
||||
/// > [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.
|
||||
///
|
||||
/// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
|
||||
pub fn has_inputs_and_outputs(tx: &Transaction) -> Result<(), TransactionError> {
|
||||
if !tx.has_transparent_or_shielded_inputs() {
|
||||
Err(TransactionError::NoInputs)
|
||||
|
|
@ -85,11 +93,13 @@ pub fn has_inputs_and_outputs(tx: &Transaction) -> Result<(), TransactionError>
|
|||
|
||||
/// Checks that the transaction has enough orchard flags.
|
||||
///
|
||||
/// For `Transaction::V5` only:
|
||||
/// * If `orchard_actions_count` > 0 then at least one of
|
||||
/// `ENABLE_SPENDS|ENABLE_OUTPUTS` must be active.
|
||||
/// # Consensus
|
||||
///
|
||||
/// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
|
||||
/// For `Transaction::V5` only:
|
||||
///
|
||||
/// > [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(tx: &Transaction) -> Result<(), TransactionError> {
|
||||
if !tx.has_enough_orchard_flags() {
|
||||
return Err(TransactionError::NotEnoughFlags);
|
||||
|
|
|
|||
Loading…
Reference in New Issue