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:
parent
9be13a4fb7
commit
e703bd3512
|
@ -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> {
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue