Skip to content

Commit

Permalink
Merge of #5580
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Nov 9, 2022
2 parents ff81432 + 6cbc9f9 commit 89aa8cb
Show file tree
Hide file tree
Showing 21 changed files with 426 additions and 114 deletions.
3 changes: 3 additions & 0 deletions zebra-chain/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ mod sighash;
mod txid;
mod unmined;

#[cfg(feature = "getblocktemplate-rpcs")]
pub mod builder;

#[cfg(any(test, feature = "proptest-impl"))]
#[allow(clippy::unwrap_in_result)]
pub mod arbitrary;
Expand Down
86 changes: 86 additions & 0 deletions zebra-chain/src/transaction/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//! Methods for building transactions.
use crate::{
amount::{Amount, NonNegative},
block::Height,
parameters::{Network, NetworkUpgrade},
transaction::{LockTime, Transaction},
transparent,
};

impl Transaction {
/// Returns a new version 5 coinbase transaction for `network` and `height`,
/// which contains the specified `outputs`.
pub fn new_v5_coinbase(
network: Network,
height: Height,
outputs: impl IntoIterator<Item = (Amount<NonNegative>, transparent::Script)>,
) -> Transaction {
// # Consensus
//
// These consensus rules apply to v5 coinbase transactions after NU5 activation:
//
// > 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. ...
// > 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).
//
// > A coinbase transaction script MUST have length in {2 .. 100} bytes.
//
// Zebra does not add any extra coinbase data.
//
// Since we're not using a lock time, any sequence number is valid here.
// See `Transaction::lock_time()` for the relevant consensus rules.
//
// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
let inputs = vec![transparent::Input::new_coinbase(height, None, None)];

// > The block subsidy is composed of a miner subsidy and a series of funding streams.
//
// <https://zips.z.cash/protocol/protocol.pdf#subsidyconcepts>
//
// > The total value in zatoshi of transparent outputs from a coinbase transaction,
// > 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>
let outputs = outputs
.into_iter()
.map(|(amount, lock_script)| transparent::Output::new_coinbase(amount, lock_script))
.collect();

Transaction::V5 {
// > The transaction version number MUST be 4 or 5. ...
// > If the transaction version number is 5 then the version group ID MUST be 0x26A7270A.
// > If effectiveVersion ≥ 5, the nConsensusBranchId field MUST match the consensus
// > branch ID used for SIGHASH transaction hashes, as specified in [ZIP-244].
network_upgrade: NetworkUpgrade::current(network, height),

// There is no documented consensus rule for the lock time field in coinbase transactions,
// so we just leave it unlocked.
lock_time: LockTime::unlocked(),

// > The nExpiryHeight field of a coinbase transaction MUST be equal to its block height.
expiry_height: height,

inputs,
outputs,

// Zebra does not support shielded coinbase yet.
//
// > In a version 5 coinbase transaction, the enableSpendsOrchard flag MUST be 0.
// > In a version 5 transaction, the reserved bits 2 .. 7 of the flagsOrchard field
// > MUST be zero.
//
// See the Zcash spec for additional shielded coinbase consensus rules.
sapling_shielded_data: None,
orchard_shielded_data: None,
}
}
}
28 changes: 28 additions & 0 deletions zebra-chain/src/transparent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,25 @@ impl fmt::Display for Input {
}

impl Input {
/// Returns a new coinbase input for `height` with optional `data` and `sequence`.
#[cfg(feature = "getblocktemplate-rpcs")]
pub fn new_coinbase(
height: block::Height,
data: Option<Vec<u8>>,
sequence: Option<u32>,
) -> Input {
Input::Coinbase {
height,

// "No extra coinbase data" is the default.
data: CoinbaseData(data.unwrap_or_default()),

// If the caller does not specify the sequence number,
// use a sequence number that activates the LockTime.
sequence: sequence.unwrap_or(0),
}
}

/// Returns the input's sequence number.
pub fn sequence(&self) -> u32 {
match self {
Expand Down Expand Up @@ -333,6 +352,15 @@ pub struct Output {
}

impl Output {
/// Returns a new coinbase output that pays `amount` using `lock_script`.
#[cfg(feature = "getblocktemplate-rpcs")]
pub fn new_coinbase(amount: Amount<NonNegative>, lock_script: Script) -> Output {
Output {
value: amount,
lock_script,
}
}

/// Get the value contained in this output.
/// This amount is subtracted from the transaction value pool by this output.
pub fn value(&self) -> Amount<NonNegative> {
Expand Down
13 changes: 8 additions & 5 deletions zebra-chain/src/transparent/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,22 @@ use super::{CoinbaseData, Input, OutPoint, Output, Script};
///
/// Includes the encoded coinbase height, if any.
///
/// > The number of bytes in the coinbase script, up to a maximum of 100 bytes.
/// # Consensus
///
/// > A coinbase transaction script MUST have length in {2 .. 100} bytes.
///
/// <https://developer.bitcoin.org/reference/transactions.html#coinbase-input-the-input-of-the-first-transaction-in-a-block>
/// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
pub const MAX_COINBASE_DATA_LEN: usize = 100;

/// The minimum length of the coinbase data.
///
/// Includes the encoded coinbase height, if any.
///
// TODO: Update the link below once the constant is documented in the
// protocol.
/// # Consensus
///
/// > A coinbase transaction script MUST have length in {2 .. 100} bytes.
///
/// <https://github.com/zcash/zips/issues/589>
/// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
pub const MIN_COINBASE_DATA_LEN: usize = 2;

/// The coinbase data for a genesis block.
Expand Down
2 changes: 1 addition & 1 deletion zebra-consensus/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use zebra_state as zs;
use crate::{error::*, transaction as tx, BoxError};

pub mod check;
mod subsidy;
pub mod subsidy;

#[cfg(test)]
mod tests;
Expand Down
45 changes: 34 additions & 11 deletions zebra-consensus/src/block/subsidy/funding_streams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,20 +124,43 @@ pub fn funding_stream_address(
///
/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
pub fn check_script_form(lock_script: &Script, address: Address) -> bool {
let mut address_hash = address
// TODO: Verify P2SH multisig funding stream addresses (#5577).
// As of NU5, the funding streams do not use multisig addresses,
// so this is optional.
//
// # Consensus
//
// > The standard redeem script hash is specified in [Bitcoin-Multisig] for P2SH multisig
// > addresses...
// [protocol specification §7.10][7.10]
//
// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
// [Bitcoin-Multisig]: https://developer.bitcoin.org/ devguide/transactions.html#multisig

// Verify a Bitcoin P2SH single address.
let standard_script_hash = new_coinbase_script(address);

lock_script == &standard_script_hash
}

/// Returns a new funding stream coinbase output lock script, which pays to `address`.
pub fn new_coinbase_script(address: Address) -> Script {
let address_hash = address
.zcash_serialize_to_vec()
.expect("we should get address bytes here");

address_hash = address_hash[2..22].to_vec();
address_hash.insert(0, OpCode::Push20Bytes as u8);
address_hash.insert(0, OpCode::Hash160 as u8);
address_hash.insert(address_hash.len(), OpCode::Equal as u8);
if lock_script.as_raw_bytes().len() == address_hash.len()
&& *lock_script == Script::new(&address_hash)
{
return true;
}
false
// > The “prescribed way” to pay a transparent P2SH address is to use a standard P2SH script
// > of the form OP_HASH160 fs.RedeemScriptHash(height) OP_EQUAL as the scriptPubKey.
//
// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
let mut script_bytes = Vec::new();

script_bytes.push(OpCode::Hash160 as u8);
script_bytes.push(OpCode::Push20Bytes as u8);
script_bytes.extend(&address_hash[2..22]);
script_bytes.push(OpCode::Equal as u8);

Script::new(&script_bytes)
}

/// Returns a list of outputs in `Transaction`, which have a script address equal to `Address`.
Expand Down
12 changes: 11 additions & 1 deletion zebra-consensus/src/block/subsidy/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use zebra_chain::{
transaction::Transaction,
};

use crate::parameters::subsidy::*;
use crate::{funding_stream_values, parameters::subsidy::*};

/// The divisor used for halvings.
///
Expand Down Expand Up @@ -68,6 +68,16 @@ pub fn block_subsidy(height: Height, network: Network) -> Result<Amount<NonNegat
}
}

/// `MinerSubsidy(height)` as described in [protocol specification §7.8][7.8]
///
/// [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies
pub fn miner_subsidy(height: Height, network: Network) -> Result<Amount<NonNegative>, Error> {
let total_funding_stream_amount: Result<Amount<NonNegative>, _> =
funding_stream_values(height, network)?.values().sum();

block_subsidy(height, network)? - total_funding_stream_amount?
}

/// Returns all output amounts in `Transaction`.
pub fn output_amounts(transaction: &Transaction) -> HashSet<Amount<NonNegative>> {
transaction
Expand Down
6 changes: 5 additions & 1 deletion zebra-consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ pub mod chain;
#[allow(missing_docs)]
pub mod error;

pub use block::{VerifyBlockError, MAX_BLOCK_SIGOPS};
pub use block::{
subsidy::funding_streams::funding_stream_address,
subsidy::funding_streams::funding_stream_values, subsidy::funding_streams::new_coinbase_script,
subsidy::general::miner_subsidy, VerifyBlockError, MAX_BLOCK_SIGOPS,
};
pub use chain::VerifyChainError;
pub use checkpoint::{
CheckpointList, VerifyCheckpointError, MAX_CHECKPOINT_BYTE_COUNT, MAX_CHECKPOINT_HEIGHT_GAP,
Expand Down
Loading

0 comments on commit 89aa8cb

Please sign in to comment.