docs: Transaction consensus rules: Coinbase rules (#3464)

* refactor coinbase transaction consensus rules docs

* add missing rule

* nit

Co-authored-by: teor <teor@riseup.net>

Co-authored-by: teor <teor@riseup.net>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
Alfredo Garcia 2022-02-07 23:20:08 -03:00 committed by GitHub
parent 9be13a4fb7
commit e703bd3512
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 56 additions and 20 deletions

View File

@ -65,6 +65,21 @@ impl ZcashDeserialize for OutPoint {
/// ///
/// The height may consume `0..=5` bytes at the stat of the coinbase data. /// The height may consume `0..=5` bytes at the stat of the coinbase data.
/// The genesis block does not include an encoded coinbase height. /// The genesis block does not include an encoded coinbase height.
///
/// # Consensus
///
/// > A coinbase transaction for a block at block height greater than 0 MUST have
/// > a script that, as its first item, encodes the block height height as follows.
/// > For height in the range {1..16}, the encoding is a single byte of value
/// > 0x50 height. Otherwise, let heightBytes be the signed little-endian
/// > representation of height, using the minimum nonzero number of bytes such that
/// > the most significant byte is < 0x80.
/// > The length of heightBytes MUST be in the range {1..5}.
/// > Then the encoding is the length of heightBytes encoded as one byte,
/// > followed by heightBytes itself. This matches the encoding used by Bitcoin in the
/// > implementation of [BIP-34] (but the description here is to be considered normative).
///
/// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
pub(crate) fn parse_coinbase_height( pub(crate) fn parse_coinbase_height(
mut data: Vec<u8>, mut data: Vec<u8>,
) -> Result<(block::Height, CoinbaseData), SerializationError> { ) -> Result<(block::Height, CoinbaseData), SerializationError> {

View File

@ -184,10 +184,12 @@ pub fn miner_fees_are_valid(
let block_subsidy = subsidy::general::block_subsidy(height, network) let block_subsidy = subsidy::general::block_subsidy(height, network)
.expect("a valid block subsidy for this height and network"); .expect("a valid block subsidy for this height and network");
// Consensus rule: The total value in zatoshi of transparent outputs from a // # Consensus
// coinbase transaction, minus vbalanceSapling, minus vbalanceOrchard, MUST NOT //
// be greater than the value in zatoshi of block subsidy plus the transaction fees // > The total value in zatoshi of transparent outputs from a coinbase transaction,
// paid by transactions in this block. // > minus vbalanceSapling, minus vbalanceOrchard, MUST NOT be greater than the value
// > in zatoshi of block subsidy plus the transaction fees paid by transactions in this block.
//
// https://zips.z.cash/protocol/protocol.pdf#txnconsensus // https://zips.z.cash/protocol/protocol.pdf#txnconsensus
let left = (transparent_value_balance - sapling_value_balance - orchard_value_balance) let left = (transparent_value_balance - sapling_value_balance - orchard_value_balance)
.map_err(|_| SubsidyError::SumOverflow)?; .map_err(|_| SubsidyError::SumOverflow)?;

View File

@ -99,14 +99,20 @@ pub fn has_enough_orchard_flags(tx: &Transaction) -> Result<(), TransactionError
/// Check that a coinbase transaction has no PrevOut inputs, JoinSplits, or spends. /// Check that a coinbase transaction has no PrevOut inputs, JoinSplits, or spends.
/// ///
/// A coinbase transaction MUST NOT have any transparent inputs, JoinSplit descriptions, /// # Consensus
/// or Spend descriptions.
/// ///
/// In a version 5 coinbase transaction, the enableSpendsOrchard flag MUST be 0. /// > A coinbase transaction MUST NOT have any transparent inputs with non-null prevout fields,
/// > JoinSplit descriptions, or Spend descriptions.
///
/// > [NU5 onward] In a version 5 coinbase transaction, the enableSpendsOrchard flag MUST be 0.
/// ///
/// This check only counts `PrevOut` transparent inputs. /// This check only counts `PrevOut` transparent inputs.
/// ///
/// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus /// > [Pre-Heartwood] A coinbase transaction also MUST NOT have any Output descriptions.
///
/// Zebra does not validate this last rule explicitly because we checkpoint until Canopy activation.
///
/// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
pub fn coinbase_tx_no_prevout_joinsplit_spend(tx: &Transaction) -> Result<(), TransactionError> { pub fn coinbase_tx_no_prevout_joinsplit_spend(tx: &Transaction) -> Result<(), TransactionError> {
if tx.has_valid_coinbase_transaction_inputs() { if tx.has_valid_coinbase_transaction_inputs() {
if tx.contains_prevout_input() { if tx.contains_prevout_input() {
@ -245,6 +251,8 @@ where
/// ///
/// This is used to validate coinbase transactions: /// This is used to validate coinbase transactions:
/// ///
/// # Consensus
///
/// > [Heartwood onward] All Sapling and Orchard outputs in coinbase transactions MUST decrypt to a note /// > [Heartwood onward] All Sapling and Orchard outputs in coinbase transactions MUST decrypt to a note
/// > plaintext, i.e. the procedure in § 4.19.3 Decryption using a Full Viewing Key ( Sapling and Orchard ) on p. 67 /// > plaintext, i.e. the procedure in § 4.19.3 Decryption using a Full Viewing Key ( Sapling and Orchard ) on p. 67
/// > does not return ⊥, using a sequence of 32 zero bytes as the outgoing viewing key. (This implies that before /// > does not return ⊥, using a sequence of 32 zero bytes as the outgoing viewing key. (This implies that before
@ -255,7 +263,8 @@ where
/// > according to the preceding rule MUST have note plaintext lead byte equal to 0x02. (This applies even during /// > according to the preceding rule MUST have note plaintext lead byte equal to 0x02. (This applies even during
/// > the "grace period" specified in [ZIP-212].) /// > the "grace period" specified in [ZIP-212].)
/// ///
/// [3.10]: https://zips.z.cash/protocol/protocol.pdf#coinbasetransactions /// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
///
/// [ZIP-212]: https://zips.z.cash/zip-0212#consensus-rule-change-for-coinbase-transactions /// [ZIP-212]: https://zips.z.cash/zip-0212#consensus-rule-change-for-coinbase-transactions
/// ///
/// TODO: Currently, a 0x01 lead byte is allowed in the "grace period" mentioned since we're /// TODO: Currently, a 0x01 lead byte is allowed in the "grace period" mentioned since we're
@ -298,8 +307,12 @@ pub fn coinbase_expiry_height(
// If Nu5 does not have a height, apply the pre-Nu5 rule. // If Nu5 does not have a height, apply the pre-Nu5 rule.
None => validate_expiry_height_max(expiry_height, true, block_height, coinbase), None => validate_expiry_height_max(expiry_height, true, block_height, coinbase),
Some(activation_height) => { Some(activation_height) => {
// Consensus rule: from NU5 activation, the nExpiryHeight field of a // # Consensus
// coinbase transaction MUST be set equal to the block height. //
// > [NU5 onward] The nExpiryHeight field of a coinbase transaction MUST be equal
// > to its block height.
//
// https://zips.z.cash/protocol/protocol.pdf#txnconsensus
if *block_height >= activation_height { if *block_height >= activation_height {
match expiry_height { match expiry_height {
None => Err(TransactionError::CoinbaseExpiryBlockHeight { None => Err(TransactionError::CoinbaseExpiryBlockHeight {

View File

@ -168,16 +168,19 @@ fn transparent_spend_chain_order(
/// Check that `utxo` is spendable, based on the coinbase `spend_restriction`. /// Check that `utxo` is spendable, based on the coinbase `spend_restriction`.
/// ///
/// "A transaction with one or more transparent inputs from coinbase transactions /// # Consensus
/// MUST have no transparent outputs (i.e.tx_out_count MUST be 0)."
/// ///
/// "A transaction MUST NOT spend a transparent output of a coinbase transaction /// > A transaction with one or more transparent inputs from coinbase transactions
/// from a block less than 100 blocks prior to the spend. /// > MUST have no transparent outputs (i.e. tx_out_count MUST be 0).
/// > Inputs from coinbase transactions include Founders Reward outputs and
/// > funding stream outputs.
/// ///
/// Note that transparent outputs of coinbase transactions include Founders /// > A transaction MUST NOT spend a transparent output of a coinbase transaction
/// Reward outputs and transparent funding stream outputs." /// > from a block less than 100 blocks prior to the spend.
/// > Note that transparent outputs of coinbase transactions include
/// > Founders Reward outputs and transparent funding stream outputs.
/// ///
/// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus /// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
pub fn transparent_coinbase_spend( pub fn transparent_coinbase_spend(
outpoint: transparent::OutPoint, outpoint: transparent::OutPoint,
spend_restriction: transparent::CoinbaseSpendRestriction, spend_restriction: transparent::CoinbaseSpendRestriction,

View File

@ -344,8 +344,11 @@ impl FinalizedState {
batch.zs_insert(height_by_hash, hash, height); batch.zs_insert(height_by_hash, hash, height);
batch.zs_insert(block_by_height, height, &block); batch.zs_insert(block_by_height, height, &block);
// "A transaction MUST NOT spend an output of the genesis block coinbase transaction. // # Consensus
// (There is one such zero-valued output, on each of Testnet and Mainnet .)" //
// > A transaction MUST NOT spend an output of the genesis block coinbase transaction.
// > (There is one such zero-valued output, on each of Testnet and Mainnet.)
//
// https://zips.z.cash/protocol/protocol.pdf#txnconsensus // https://zips.z.cash/protocol/protocol.pdf#txnconsensus
if block.header.previous_block_hash == GENESIS_PREVIOUS_BLOCK_HASH { if block.header.previous_block_hash == GENESIS_PREVIOUS_BLOCK_HASH {
// Insert empty note commitment trees. Note that these can't be // Insert empty note commitment trees. Note that these can't be