change(rpc): Match `zcashd`'s block template exactly (#5867)

* Make Zebra's getblocktemplate like zcashd's

* Update snapshots

* Add missing docs

* Fix typo

* Sort coinbase outputs by serialized script for zcashd

* Sort excluding the length byte, make transaction order always stable

* Update snapshots

* Explain that `zcashd` doesn't seem to have a fixed transaction order
This commit is contained in:
teor 2022-12-20 04:52:43 +10:00 committed by GitHub
parent b8448c7eed
commit b08a0b6f08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 285 additions and 108 deletions

View File

@ -20,6 +20,10 @@ impl Transaction {
// //
// These consensus rules apply to v5 coinbase transactions after NU5 activation: // These consensus rules apply to v5 coinbase transactions after NU5 activation:
// //
// > If effectiveVersion ≥ 5 then this condition MUST hold:
// > tx_in_count > 0 or nSpendsSapling > 0 or
// > (nActionsOrchard > 0 and enableSpendsOrchard = 1).
//
// > A coinbase transaction for a block at block height greater than 0 MUST have // > 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. ... // > a script that, as its first item, encodes the block height height as follows. ...
// > let heightBytes be the signed little-endian representation of height, // > let heightBytes be the signed little-endian representation of height,
@ -49,24 +53,34 @@ impl Transaction {
// > the value in zatoshi of block subsidy plus the transaction fees // > the value in zatoshi of block subsidy plus the transaction fees
// > paid by transactions in this block. // > paid by transactions in this block.
// //
// > 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> // <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
let outputs = outputs let outputs: Vec<_> = outputs
.into_iter() .into_iter()
.map(|(amount, lock_script)| transparent::Output::new_coinbase(amount, lock_script)) .map(|(amount, lock_script)| transparent::Output::new_coinbase(amount, lock_script))
.collect(); .collect();
assert!(
!outputs.is_empty(),
"invalid coinbase transaction: must have at least one output"
);
Transaction::V5 { Transaction::V5 {
// > The transaction version number MUST be 4 or 5. ... // > 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 the transaction version number is 5 then the version group ID
// > MUST be 0x26A7270A.
// > If effectiveVersion ≥ 5, the nConsensusBranchId field MUST match the consensus // > If effectiveVersion ≥ 5, the nConsensusBranchId field MUST match the consensus
// > branch ID used for SIGHASH transaction hashes, as specified in [ZIP-244]. // > branch ID used for SIGHASH transaction hashes, as specified in [ZIP-244].
network_upgrade: NetworkUpgrade::current(network, height), network_upgrade: NetworkUpgrade::current(network, height),
// There is no documented consensus rule for the lock time field in coinbase transactions, // There is no documented consensus rule for the lock time field in coinbase
// so we just leave it unlocked. (We could also set it to `height`.) // transactions, so we just leave it unlocked. (We could also set it to `height`.)
lock_time: LockTime::unlocked(), lock_time: LockTime::unlocked(),
// > The nExpiryHeight field of a coinbase transaction MUST be equal to its block height. // > The nExpiryHeight field of a coinbase transaction MUST be equal to its
// > block height.
expiry_height: height, expiry_height: height,
inputs, inputs,
@ -83,4 +97,62 @@ impl Transaction {
orchard_shielded_data: None, orchard_shielded_data: None,
} }
} }
/// Returns a new version 4 coinbase transaction for `network` and `height`,
/// which contains the specified `outputs`.
///
/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
/// in the `getblocktemplate` RPC.
pub fn new_v4_coinbase(
_network: Network,
height: Height,
outputs: impl IntoIterator<Item = (Amount<NonNegative>, transparent::Script)>,
like_zcashd: bool,
) -> Transaction {
// `zcashd` includes an extra byte after the coinbase height in the coinbase data,
// and a sequence number of u32::MAX.
let mut extra_data = None;
let mut sequence = None;
if like_zcashd {
extra_data = Some(vec![0x00]);
sequence = Some(u32::MAX);
}
// # Consensus
//
// See the other consensus rules above in new_v5_coinbase().
//
// > If effectiveVersion < 5, then at least one of tx_in_count, nSpendsSapling,
// > and nJoinSplit MUST be nonzero.
let inputs = vec![transparent::Input::new_coinbase(
height, extra_data, sequence,
)];
// > If effectiveVersion < 5, then at least one of tx_out_count, nOutputsSapling,
// > and nJoinSplit MUST be nonzero.
let outputs: Vec<_> = outputs
.into_iter()
.map(|(amount, lock_script)| transparent::Output::new_coinbase(amount, lock_script))
.collect();
assert!(
!outputs.is_empty(),
"invalid coinbase transaction: must have at least one output"
);
// > The transaction version number MUST be 4 or 5. ...
// > If the transaction version number is 4 then the version group ID MUST be 0x892F2085.
Transaction::V4 {
lock_time: LockTime::unlocked(),
expiry_height: height,
inputs,
outputs,
// Zebra does not support shielded coinbase yet.
joinsplit_data: None,
sapling_shielded_data: None,
}
}
} }

View File

@ -990,7 +990,9 @@ impl TrustedPreallocate for transparent::Output {
/// A serialized transaction. /// A serialized transaction.
/// ///
/// Stores bytes that are guaranteed to be deserializable into a [`Transaction`]. /// Stores bytes that are guaranteed to be deserializable into a [`Transaction`].
#[derive(Clone, Debug, Eq, Hash, PartialEq)] ///
/// Sorts in lexicographic order of the transaction's serialized data.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct SerializedTransaction { pub struct SerializedTransaction {
bytes: Vec<u8>, bytes: Vec<u8>,
} }

View File

@ -9,7 +9,7 @@ use crate::serialization::{
}; };
/// An encoding of a Bitcoin script. /// An encoding of a Bitcoin script.
#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
#[cfg_attr( #[cfg_attr(
any(test, feature = "proptest-impl"), any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary) derive(proptest_derive::Arbitrary)

View File

@ -2,6 +2,8 @@
//! //!
//! [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies //! [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies
use std::{collections::HashMap, str::FromStr};
use zebra_chain::{ use zebra_chain::{
amount::{Amount, Error, NonNegative}, amount::{Amount, Error, NonNegative},
block::Height, block::Height,
@ -12,8 +14,6 @@ use zebra_chain::{
use crate::{block::subsidy::general::block_subsidy, parameters::subsidy::*}; use crate::{block::subsidy::general::block_subsidy, parameters::subsidy::*};
use std::{collections::HashMap, str::FromStr};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View File

@ -47,9 +47,11 @@ pub mod chain;
pub mod error; pub mod error;
pub use block::{ pub use block::{
subsidy::funding_streams::funding_stream_address, subsidy::{
subsidy::funding_streams::funding_stream_values, subsidy::funding_streams::new_coinbase_script, funding_streams::{funding_stream_address, funding_stream_values, new_coinbase_script},
subsidy::general::miner_subsidy, VerifyBlockError, MAX_BLOCK_SIGOPS, general::miner_subsidy,
},
VerifyBlockError, MAX_BLOCK_SIGOPS,
}; };
pub use chain::VerifyChainError; pub use chain::VerifyChainError;
pub use checkpoint::{ pub use checkpoint::{
@ -57,6 +59,7 @@ pub use checkpoint::{
}; };
pub use config::Config; pub use config::Config;
pub use error::BlockError; pub use error::BlockError;
pub use parameters::FundingStreamReceiver;
pub use primitives::{ed25519, groth16, halo2, redjubjub, redpallas}; pub use primitives::{ed25519, groth16, halo2, redjubjub, redpallas};
/// A boxed [`std::error::Error`]. /// A boxed [`std::error::Error`].

View File

@ -48,8 +48,13 @@ pub const FIRST_HALVING_TESTNET: Height = Height(1_116_000);
/// The funding stream receiver categories. /// The funding stream receiver categories.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum FundingStreamReceiver { pub enum FundingStreamReceiver {
/// The Electric Coin Company (Bootstrap Foundation) funding stream.
Ecc, Ecc,
/// The Zcash Foundation funding stream.
ZcashFoundation, ZcashFoundation,
/// The Major Grants (Zcash Community Grants) funding stream.
MajorGrants, MajorGrants,
} }

View File

@ -317,6 +317,11 @@ where
&self, &self,
parameters: Option<get_block_template::JsonParameters>, parameters: Option<get_block_template::JsonParameters>,
) -> BoxFuture<Result<GetBlockTemplate>> { ) -> BoxFuture<Result<GetBlockTemplate>> {
// Should we generate coinbase transactions that are exactly like zcashd's?
//
// This is useful for testing, but either way Zebra should obey the consensus rules.
const COINBASE_LIKE_ZCASHD: bool = true;
// Clone Config // Clone Config
let network = self.network; let network = self.network;
let miner_address = self.miner_address; let miner_address = self.miner_address;
@ -549,6 +554,7 @@ where
next_block_height, next_block_height,
miner_address, miner_address,
mempool_txs, mempool_txs,
COINBASE_LIKE_ZCASHD,
) )
.await; .await;
@ -563,16 +569,17 @@ where
miner_address, miner_address,
&mempool_txs, &mempool_txs,
chain_tip_and_local_time.history_tree.clone(), chain_tip_and_local_time.history_tree.clone(),
COINBASE_LIKE_ZCASHD,
); );
let response = GetBlockTemplate::new( let response = GetBlockTemplate::new(
next_block_height,
&chain_tip_and_local_time, &chain_tip_and_local_time,
server_long_poll_id, server_long_poll_id,
coinbase_txn, coinbase_txn,
&mempool_txs, &mempool_txs,
default_roots, default_roots,
submit_old, submit_old,
COINBASE_LIKE_ZCASHD,
); );
Ok(response) Ok(response)

View File

@ -1,6 +1,6 @@
//! Support functions for the `get_block_template()` RPC. //! Support functions for the `get_block_template()` RPC.
use std::{iter, sync::Arc}; use std::{collections::HashMap, iter, sync::Arc};
use jsonrpc_core::{Error, ErrorCode, Result}; use jsonrpc_core::{Error, ErrorCode, Result};
use tower::{Service, ServiceExt}; use tower::{Service, ServiceExt};
@ -17,7 +17,9 @@ use zebra_chain::{
transaction::{Transaction, UnminedTx, VerifiedUnminedTx}, transaction::{Transaction, UnminedTx, VerifiedUnminedTx},
transparent, transparent,
}; };
use zebra_consensus::{funding_stream_address, funding_stream_values, miner_subsidy}; use zebra_consensus::{
funding_stream_address, funding_stream_values, miner_subsidy, FundingStreamReceiver,
};
use zebra_node_services::mempool; use zebra_node_services::mempool;
use zebra_state::GetBlockTemplateChainInfo; use zebra_state::GetBlockTemplateChainInfo;
@ -175,16 +177,21 @@ where
// - Response processing // - Response processing
/// Generates and returns the coinbase transaction and default roots. /// Generates and returns the coinbase transaction and default roots.
///
/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
/// in the `getblocktemplate` RPC.
pub fn generate_coinbase_and_roots( pub fn generate_coinbase_and_roots(
network: Network, network: Network,
height: Height, height: Height,
miner_address: transparent::Address, miner_address: transparent::Address,
mempool_txs: &[VerifiedUnminedTx], mempool_txs: &[VerifiedUnminedTx],
history_tree: Arc<zebra_chain::history_tree::HistoryTree>, history_tree: Arc<zebra_chain::history_tree::HistoryTree>,
like_zcashd: bool,
) -> (TransactionTemplate<NegativeOrZero>, DefaultRoots) { ) -> (TransactionTemplate<NegativeOrZero>, DefaultRoots) {
// Generate the coinbase transaction // Generate the coinbase transaction
let miner_fee = calculate_miner_fee(mempool_txs); let miner_fee = calculate_miner_fee(mempool_txs);
let coinbase_txn = generate_coinbase_transaction(network, height, miner_address, miner_fee); let coinbase_txn =
generate_coinbase_transaction(network, height, miner_address, miner_fee, like_zcashd);
// Calculate block default roots // Calculate block default roots
// //
@ -199,15 +206,23 @@ pub fn generate_coinbase_and_roots(
// - Coinbase transaction processing // - Coinbase transaction processing
/// Returns a coinbase transaction for the supplied parameters. /// Returns a coinbase transaction for the supplied parameters.
///
/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
/// in the `getblocktemplate` RPC.
pub fn generate_coinbase_transaction( pub fn generate_coinbase_transaction(
network: Network, network: Network,
height: Height, height: Height,
miner_address: transparent::Address, miner_address: transparent::Address,
miner_fee: Amount<NonNegative>, miner_fee: Amount<NonNegative>,
like_zcashd: bool,
) -> UnminedTx { ) -> UnminedTx {
let outputs = standard_coinbase_outputs(network, height, miner_address, miner_fee); let outputs = standard_coinbase_outputs(network, height, miner_address, miner_fee, like_zcashd);
Transaction::new_v5_coinbase(network, height, outputs).into() if like_zcashd {
Transaction::new_v4_coinbase(network, height, outputs, like_zcashd).into()
} else {
Transaction::new_v5_coinbase(network, height, outputs).into()
}
} }
/// Returns the total miner fee for `mempool_txs`. /// Returns the total miner fee for `mempool_txs`.
@ -225,22 +240,32 @@ pub fn calculate_miner_fee(mempool_txs: &[VerifiedUnminedTx]) -> Amount<NonNegat
/// for `network`, `height` and `miner_fee`. /// for `network`, `height` and `miner_fee`.
/// ///
/// Only works for post-Canopy heights. /// Only works for post-Canopy heights.
///
/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
/// in the `getblocktemplate` RPC.
pub fn standard_coinbase_outputs( pub fn standard_coinbase_outputs(
network: Network, network: Network,
height: Height, height: Height,
miner_address: transparent::Address, miner_address: transparent::Address,
miner_fee: Amount<NonNegative>, miner_fee: Amount<NonNegative>,
like_zcashd: bool,
) -> Vec<(Amount<NonNegative>, transparent::Script)> { ) -> Vec<(Amount<NonNegative>, transparent::Script)> {
let funding_streams = funding_stream_values(height, network) let funding_streams = funding_stream_values(height, network)
.expect("funding stream value calculations are valid for reasonable chain heights"); .expect("funding stream value calculations are valid for reasonable chain heights");
let mut funding_streams: Vec<(Amount<NonNegative>, transparent::Address)> = funding_streams // Optional TODO: move this into a zebra_consensus function?
.iter() let funding_streams: HashMap<
.map(|(receiver, amount)| (*amount, funding_stream_address(height, network, *receiver))) FundingStreamReceiver,
(Amount<NonNegative>, transparent::Address),
> = funding_streams
.into_iter()
.map(|(receiver, amount)| {
(
receiver,
(amount, funding_stream_address(height, network, receiver)),
)
})
.collect(); .collect();
// The HashMap returns funding streams in an arbitrary order,
// but Zebra's snapshot tests expect the same order every time.
funding_streams.sort_by_key(|(amount, _address)| *amount);
let miner_reward = miner_subsidy(height, network) let miner_reward = miner_subsidy(height, network)
.expect("reward calculations are valid for reasonable chain heights") .expect("reward calculations are valid for reasonable chain heights")
@ -248,13 +273,45 @@ pub fn standard_coinbase_outputs(
let miner_reward = let miner_reward =
miner_reward.expect("reward calculations are valid for reasonable chain heights"); miner_reward.expect("reward calculations are valid for reasonable chain heights");
let mut coinbase_outputs = funding_streams; combine_coinbase_outputs(funding_streams, miner_address, miner_reward, like_zcashd)
}
/// Combine the miner reward and funding streams into a list of coinbase amounts and addresses.
///
/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
/// in the `getblocktemplate` RPC.
fn combine_coinbase_outputs(
funding_streams: HashMap<FundingStreamReceiver, (Amount<NonNegative>, transparent::Address)>,
miner_address: transparent::Address,
miner_reward: Amount<NonNegative>,
like_zcashd: bool,
) -> Vec<(Amount<NonNegative>, transparent::Script)> {
// Combine all the funding streams with the miner reward.
let mut coinbase_outputs: Vec<(Amount<NonNegative>, transparent::Address)> = funding_streams
.into_iter()
.map(|(_receiver, (amount, address))| (amount, address))
.collect();
coinbase_outputs.push((miner_reward, miner_address)); coinbase_outputs.push((miner_reward, miner_address));
coinbase_outputs let mut coinbase_outputs: Vec<(Amount<NonNegative>, transparent::Script)> = coinbase_outputs
.iter() .iter()
.map(|(amount, address)| (*amount, address.create_script_from_address())) .map(|(amount, address)| (*amount, address.create_script_from_address()))
.collect() .collect();
// The HashMap returns funding streams in an arbitrary order,
// but Zebra's snapshot tests expect the same order every time.
if like_zcashd {
// zcashd sorts outputs in serialized data order, excluding the length field
coinbase_outputs.sort_by_key(|(_amount, script)| script.clone());
} else {
// Zebra sorts by amount then script.
//
// Since the sort is stable, equal amounts will remain sorted by script.
coinbase_outputs.sort_by_key(|(_amount, script)| script.clone());
coinbase_outputs.sort_by_key(|(amount, _script)| *amount);
}
coinbase_outputs
} }
// - Transaction roots processing // - Transaction roots processing

View File

@ -2,7 +2,7 @@
use zebra_chain::{ use zebra_chain::{
amount, amount,
block::{ChainHistoryBlockTxAuthCommitmentHash, Height, MAX_BLOCK_BYTES, ZCASH_BLOCK_VERSION}, block::{ChainHistoryBlockTxAuthCommitmentHash, MAX_BLOCK_BYTES, ZCASH_BLOCK_VERSION},
serialization::DateTime32, serialization::DateTime32,
transaction::VerifiedUnminedTx, transaction::VerifiedUnminedTx,
work::difficulty::{CompactDifficulty, ExpandedDifficulty}, work::difficulty::{CompactDifficulty, ExpandedDifficulty},
@ -165,17 +165,36 @@ impl GetBlockTemplate {
/// Returns a new [`GetBlockTemplate`] struct, based on the supplied arguments and defaults. /// Returns a new [`GetBlockTemplate`] struct, based on the supplied arguments and defaults.
/// ///
/// The result of this method only depends on the supplied arguments and constants. /// The result of this method only depends on the supplied arguments and constants.
///
/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
/// in the `getblocktemplate` RPC.
pub fn new( pub fn new(
next_block_height: Height,
chain_tip_and_local_time: &GetBlockTemplateChainInfo, chain_tip_and_local_time: &GetBlockTemplateChainInfo,
long_poll_id: LongPollId, long_poll_id: LongPollId,
coinbase_txn: TransactionTemplate<amount::NegativeOrZero>, coinbase_txn: TransactionTemplate<amount::NegativeOrZero>,
mempool_txs: &[VerifiedUnminedTx], mempool_txs: &[VerifiedUnminedTx],
default_roots: DefaultRoots, default_roots: DefaultRoots,
submit_old: Option<bool>, submit_old: Option<bool>,
like_zcashd: bool,
) -> Self { ) -> Self {
// Calculate the next block height.
let next_block_height =
(chain_tip_and_local_time.tip_height + 1).expect("tip is far below Height::MAX");
// Convert transactions into TransactionTemplates // Convert transactions into TransactionTemplates
let mempool_txs = mempool_txs.iter().map(Into::into).collect(); let mut mempool_txs: Vec<TransactionTemplate<amount::NonNegative>> =
mempool_txs.iter().map(Into::into).collect();
// Transaction selection returns transactions in an arbitrary order,
// but Zebra's snapshot tests expect the same order every time.
if like_zcashd {
// Sort in serialized data order, excluding the length byte.
// `zcashd` sometimes seems to do this, but other times the order is arbitrary.
mempool_txs.sort_by_key(|tx| tx.data.clone());
} else {
// Sort by hash, this is faster.
mempool_txs.sort_by_key(|tx| tx.hash.bytes_in_display_order());
}
// Convert difficulty // Convert difficulty
let target = chain_tip_and_local_time let target = chain_tip_and_local_time

View File

@ -15,13 +15,13 @@ use zebra_chain::{
amount::NegativeOrZero, amount::NegativeOrZero,
block::{Height, MAX_BLOCK_BYTES}, block::{Height, MAX_BLOCK_BYTES},
parameters::Network, parameters::Network,
transaction::{Transaction, VerifiedUnminedTx}, transaction::VerifiedUnminedTx,
transparent, transparent,
}; };
use zebra_consensus::MAX_BLOCK_SIGOPS; use zebra_consensus::MAX_BLOCK_SIGOPS;
use crate::methods::get_block_template_rpcs::{ use crate::methods::get_block_template_rpcs::{
get_block_template::standard_coinbase_outputs, types::transaction::TransactionTemplate, get_block_template::generate_coinbase_transaction, types::transaction::TransactionTemplate,
}; };
/// The ZIP-317 recommended limit on the number of unpaid actions per block. /// The ZIP-317 recommended limit on the number of unpaid actions per block.
@ -34,6 +34,8 @@ pub const BLOCK_PRODUCTION_UNPAID_ACTION_LIMIT: u32 = 50;
/// The fake coinbase transaction's serialized size and sigops must be at least as large /// The fake coinbase transaction's serialized size and sigops must be at least as large
/// as the real coinbase transaction. (The real coinbase transaction depends on the total /// as the real coinbase transaction. (The real coinbase transaction depends on the total
/// fees from the transactions returned by this function.) /// fees from the transactions returned by this function.)
/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
/// in the `getblocktemplate` RPC.
/// ///
/// Returns selected transactions from `mempool_txs`. /// Returns selected transactions from `mempool_txs`.
/// ///
@ -43,10 +45,12 @@ pub async fn select_mempool_transactions(
next_block_height: Height, next_block_height: Height,
miner_address: transparent::Address, miner_address: transparent::Address,
mempool_txs: Vec<VerifiedUnminedTx>, mempool_txs: Vec<VerifiedUnminedTx>,
like_zcashd: bool,
) -> Vec<VerifiedUnminedTx> { ) -> Vec<VerifiedUnminedTx> {
// Use a fake coinbase transaction to break the dependency between transaction // Use a fake coinbase transaction to break the dependency between transaction
// selection, the miner fee, and the fee payment in the coinbase transaction. // selection, the miner fee, and the fee payment in the coinbase transaction.
let fake_coinbase_tx = fake_coinbase_transaction(network, next_block_height, miner_address); let fake_coinbase_tx =
fake_coinbase_transaction(network, next_block_height, miner_address, like_zcashd);
// Setup the transaction lists. // Setup the transaction lists.
let (conventional_fee_txs, low_fee_txs): (Vec<_>, Vec<_>) = mempool_txs let (conventional_fee_txs, low_fee_txs): (Vec<_>, Vec<_>) = mempool_txs
@ -105,10 +109,14 @@ pub async fn select_mempool_transactions(
/// ///
/// This transaction's serialized size and sigops must be at least as large as the real coinbase /// This transaction's serialized size and sigops must be at least as large as the real coinbase
/// transaction with the correct height and fee. /// transaction with the correct height and fee.
///
/// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
/// in the `getblocktemplate` RPC.
pub fn fake_coinbase_transaction( pub fn fake_coinbase_transaction(
network: Network, network: Network,
block_height: Height, height: Height,
miner_address: transparent::Address, miner_address: transparent::Address,
like_zcashd: bool,
) -> TransactionTemplate<NegativeOrZero> { ) -> TransactionTemplate<NegativeOrZero> {
// Block heights are encoded as variable-length (script) and `u32` (lock time, expiry height). // Block heights are encoded as variable-length (script) and `u32` (lock time, expiry height).
// They can also change the `u32` consensus branch id. // They can also change the `u32` consensus branch id.
@ -121,8 +129,8 @@ pub fn fake_coinbase_transaction(
// https://developer.bitcoin.org/reference/transactions.html#txout-a-transaction-output // https://developer.bitcoin.org/reference/transactions.html#txout-a-transaction-output
let miner_fee = 1.try_into().expect("amount is valid and non-negative"); let miner_fee = 1.try_into().expect("amount is valid and non-negative");
let outputs = standard_coinbase_outputs(network, block_height, miner_address, miner_fee); let coinbase_tx =
let coinbase_tx = Transaction::new_v5_coinbase(network, block_height, outputs).into(); generate_coinbase_transaction(network, height, miner_address, miner_fee, like_zcashd);
TransactionTemplate::from_coinbase(&coinbase_tx, miner_fee) TransactionTemplate::from_coinbase(&coinbase_tx, miner_fee)
} }

View File

@ -2,22 +2,17 @@
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
expression: coinbase_tx expression: coinbase_tx
--- ---
V5( V4(
network_upgrade: Nu5,
lock_time: Height(Height(0)),
expiry_height: Height(1687105),
inputs: [ inputs: [
Coinbase( Coinbase(
height: Height(1687105), height: Height(1687105),
data: CoinbaseData([]), data: CoinbaseData([
sequence: 0, 0,
]),
sequence: 4294967295,
), ),
], ],
outputs: [ outputs: [
Output(
value: 15625000,
lock_script: Script("a914d45cb1adffb5215a42720532a076f02c7c778c9087"),
),
Output( Output(
value: 21875000, value: 21875000,
lock_script: Script("a91469a9f95a98fe581b6eb52841ef4806dc4402eb9087"), lock_script: Script("a91469a9f95a98fe581b6eb52841ef4806dc4402eb9087"),
@ -30,7 +25,13 @@ V5(
value: 250000000, value: 250000000,
lock_script: Script("a914adadadadadadadadadadadadadadadadadadadad87"), lock_script: Script("a914adadadadadadadadadadadadadadadadadadadad87"),
), ),
Output(
value: 15625000,
lock_script: Script("a914d45cb1adffb5215a42720532a076f02c7c778c9087"),
),
], ],
lock_time: Height(Height(0)),
expiry_height: Height(1687105),
joinsplit_data: None,
sapling_shielded_data: None, sapling_shielded_data: None,
orchard_shielded_data: None,
) )

View File

@ -2,15 +2,14 @@
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
expression: coinbase_tx expression: coinbase_tx
--- ---
V5( V4(
network_upgrade: Nu5,
lock_time: Height(Height(0)),
expiry_height: Height(1842421),
inputs: [ inputs: [
Coinbase( Coinbase(
height: Height(1842421), height: Height(1842421),
data: CoinbaseData([]), data: CoinbaseData([
sequence: 0, 0,
]),
sequence: 4294967295,
), ),
], ],
outputs: [ outputs: [
@ -31,6 +30,8 @@ V5(
lock_script: Script("a914adadadadadadadadadadadadadadadadadadadad87"), lock_script: Script("a914adadadadadadadadadadadadadadadadadadadad87"),
), ),
], ],
lock_time: Height(Height(0)),
expiry_height: Height(1842421),
joinsplit_data: None,
sapling_shielded_data: None, sapling_shielded_data: None,
orchard_shielded_data: None,
) )

View File

@ -6,20 +6,20 @@ expression: block_template
"capabilities": [], "capabilities": [],
"version": 4, "version": 4,
"previousblockhash": "0000000000d723156d9b65ffcf4984da7a19675ed7e2f06d9e5d5188af087bf8", "previousblockhash": "0000000000d723156d9b65ffcf4984da7a19675ed7e2f06d9e5d5188af087bf8",
"blockcommitmentshash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82", "blockcommitmentshash": "02990723c6b62a724651322d141b4a72a4ffd66518167d809badbd5117d5518a",
"lightclientroothash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82", "lightclientroothash": "02990723c6b62a724651322d141b4a72a4ffd66518167d809badbd5117d5518a",
"finalsaplingroothash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82", "finalsaplingroothash": "02990723c6b62a724651322d141b4a72a4ffd66518167d809badbd5117d5518a",
"defaultroots": { "defaultroots": {
"merkleroot": "6b370584714ab567c9c014ce72d325ab6c5927e181ac891acb35e6d4b6cc19a1", "merkleroot": "e049ed10466f566a045702ad712bbb596c6863cd08cdb4646da749b2287bc219",
"chainhistoryroot": "94470fa66ebd1a5fdb109a5aa3f3204f14de3a42135e71aa7f4c44055847e0b5", "chainhistoryroot": "94470fa66ebd1a5fdb109a5aa3f3204f14de3a42135e71aa7f4c44055847e0b5",
"authdataroot": "0dbb78de9fdcd494307971e36dd049fc82d0ee9ee53aec8fd2a54dc0e426289b", "authdataroot": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"blockcommitmentshash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82" "blockcommitmentshash": "02990723c6b62a724651322d141b4a72a4ffd66518167d809badbd5117d5518a"
}, },
"transactions": [], "transactions": [],
"coinbasetxn": { "coinbasetxn": {
"data": "050000800a27a726b4d0d6c20000000041be1900010000000000000000000000000000000000000000000000000000000000000000ffffffff040341be190000000004286bee000000000017a914d45cb1adffb5215a42720532a076f02c7c778c908738c94d010000000017a91469a9f95a98fe581b6eb52841ef4806dc4402eb908740787d010000000017a914931fec54c1fea86e574462cc32013f5400b891298780b2e60e0000000017a914adadadadadadadadadadadadadadadadadadadad87000000", "data": "0400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff050341be1900ffffffff0438c94d010000000017a91469a9f95a98fe581b6eb52841ef4806dc4402eb908740787d010000000017a914931fec54c1fea86e574462cc32013f5400b891298780b2e60e0000000017a914adadadadadadadadadadadadadadadadadadadad87286bee000000000017a914d45cb1adffb5215a42720532a076f02c7c778c90870000000041be19000000000000000000000000",
"hash": "6b370584714ab567c9c014ce72d325ab6c5927e181ac891acb35e6d4b6cc19a1", "hash": "e049ed10466f566a045702ad712bbb596c6863cd08cdb4646da749b2287bc219",
"authdigest": "0dbb78de9fdcd494307971e36dd049fc82d0ee9ee53aec8fd2a54dc0e426289b", "authdigest": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"depends": [], "depends": [],
"fee": 0, "fee": 0,
"sigops": 0, "sigops": 0,

View File

@ -6,20 +6,20 @@ expression: block_template
"capabilities": [], "capabilities": [],
"version": 4, "version": 4,
"previousblockhash": "0000000000d723156d9b65ffcf4984da7a19675ed7e2f06d9e5d5188af087bf8", "previousblockhash": "0000000000d723156d9b65ffcf4984da7a19675ed7e2f06d9e5d5188af087bf8",
"blockcommitmentshash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422", "blockcommitmentshash": "3b25791957f9383b6ce851d728a78309664d5d7a82ca87b6a9125a2f2c529792",
"lightclientroothash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422", "lightclientroothash": "3b25791957f9383b6ce851d728a78309664d5d7a82ca87b6a9125a2f2c529792",
"finalsaplingroothash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422", "finalsaplingroothash": "3b25791957f9383b6ce851d728a78309664d5d7a82ca87b6a9125a2f2c529792",
"defaultroots": { "defaultroots": {
"merkleroot": "623400cc122baa015d3a4209f5903ebe215170c7e6e74831dce8372c5fd5b3cc", "merkleroot": "f1f2db76c33c4a81f799d0c5cfba83c99c20cc8c51958a615d410fe7fbf92b34",
"chainhistoryroot": "03bc75f00c307a05aed2023819e18c2672cbe15fbd3200944997def141967387", "chainhistoryroot": "03bc75f00c307a05aed2023819e18c2672cbe15fbd3200944997def141967387",
"authdataroot": "a44375f0c0dd5ba612bd7b0efd77683cde8edf5055aff9fbfda443cc8d46bd3e", "authdataroot": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"blockcommitmentshash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422" "blockcommitmentshash": "3b25791957f9383b6ce851d728a78309664d5d7a82ca87b6a9125a2f2c529792"
}, },
"transactions": [], "transactions": [],
"coinbasetxn": { "coinbasetxn": {
"data": "050000800a27a726b4d0d6c200000000f51c1c00010000000000000000000000000000000000000000000000000000000000000000ffffffff0403f51c1c0000000004286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8738c94d010000000017a9144e3f0d9a33a2721604cbae2de8d9171e21f8fbe48740787d010000000017a91471e1df05024288a00802de81e08c437859586c878780b2e60e0000000017a914adadadadadadadadadadadadadadadadadadadad87000000", "data": "0400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0503f51c1c00ffffffff04286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8738c94d010000000017a9144e3f0d9a33a2721604cbae2de8d9171e21f8fbe48740787d010000000017a91471e1df05024288a00802de81e08c437859586c878780b2e60e0000000017a914adadadadadadadadadadadadadadadadadadadad8700000000f51c1c000000000000000000000000",
"hash": "623400cc122baa015d3a4209f5903ebe215170c7e6e74831dce8372c5fd5b3cc", "hash": "f1f2db76c33c4a81f799d0c5cfba83c99c20cc8c51958a615d410fe7fbf92b34",
"authdigest": "a44375f0c0dd5ba612bd7b0efd77683cde8edf5055aff9fbfda443cc8d46bd3e", "authdigest": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"depends": [], "depends": [],
"fee": 0, "fee": 0,
"sigops": 0, "sigops": 0,

View File

@ -2,22 +2,17 @@
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
expression: coinbase_tx expression: coinbase_tx
--- ---
V5( V4(
network_upgrade: Nu5,
lock_time: Height(Height(0)),
expiry_height: Height(1687105),
inputs: [ inputs: [
Coinbase( Coinbase(
height: Height(1687105), height: Height(1687105),
data: CoinbaseData([]), data: CoinbaseData([
sequence: 0, 0,
]),
sequence: 4294967295,
), ),
], ],
outputs: [ outputs: [
Output(
value: 15625000,
lock_script: Script("a914d45cb1adffb5215a42720532a076f02c7c778c9087"),
),
Output( Output(
value: 21875000, value: 21875000,
lock_script: Script("a91469a9f95a98fe581b6eb52841ef4806dc4402eb9087"), lock_script: Script("a91469a9f95a98fe581b6eb52841ef4806dc4402eb9087"),
@ -30,7 +25,13 @@ V5(
value: 250000000, value: 250000000,
lock_script: Script("a914adadadadadadadadadadadadadadadadadadadad87"), lock_script: Script("a914adadadadadadadadadadadadadadadadadadadad87"),
), ),
Output(
value: 15625000,
lock_script: Script("a914d45cb1adffb5215a42720532a076f02c7c778c9087"),
),
], ],
lock_time: Height(Height(0)),
expiry_height: Height(1687105),
joinsplit_data: None,
sapling_shielded_data: None, sapling_shielded_data: None,
orchard_shielded_data: None,
) )

View File

@ -2,15 +2,14 @@
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
expression: coinbase_tx expression: coinbase_tx
--- ---
V5( V4(
network_upgrade: Nu5,
lock_time: Height(Height(0)),
expiry_height: Height(1842421),
inputs: [ inputs: [
Coinbase( Coinbase(
height: Height(1842421), height: Height(1842421),
data: CoinbaseData([]), data: CoinbaseData([
sequence: 0, 0,
]),
sequence: 4294967295,
), ),
], ],
outputs: [ outputs: [
@ -31,6 +30,8 @@ V5(
lock_script: Script("a914adadadadadadadadadadadadadadadadadadadad87"), lock_script: Script("a914adadadadadadadadadadadadadadadadadadadad87"),
), ),
], ],
lock_time: Height(Height(0)),
expiry_height: Height(1842421),
joinsplit_data: None,
sapling_shielded_data: None, sapling_shielded_data: None,
orchard_shielded_data: None,
) )

View File

@ -6,20 +6,20 @@ expression: block_template
"capabilities": [], "capabilities": [],
"version": 4, "version": 4,
"previousblockhash": "0000000000d723156d9b65ffcf4984da7a19675ed7e2f06d9e5d5188af087bf8", "previousblockhash": "0000000000d723156d9b65ffcf4984da7a19675ed7e2f06d9e5d5188af087bf8",
"blockcommitmentshash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82", "blockcommitmentshash": "02990723c6b62a724651322d141b4a72a4ffd66518167d809badbd5117d5518a",
"lightclientroothash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82", "lightclientroothash": "02990723c6b62a724651322d141b4a72a4ffd66518167d809badbd5117d5518a",
"finalsaplingroothash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82", "finalsaplingroothash": "02990723c6b62a724651322d141b4a72a4ffd66518167d809badbd5117d5518a",
"defaultroots": { "defaultroots": {
"merkleroot": "6b370584714ab567c9c014ce72d325ab6c5927e181ac891acb35e6d4b6cc19a1", "merkleroot": "e049ed10466f566a045702ad712bbb596c6863cd08cdb4646da749b2287bc219",
"chainhistoryroot": "94470fa66ebd1a5fdb109a5aa3f3204f14de3a42135e71aa7f4c44055847e0b5", "chainhistoryroot": "94470fa66ebd1a5fdb109a5aa3f3204f14de3a42135e71aa7f4c44055847e0b5",
"authdataroot": "0dbb78de9fdcd494307971e36dd049fc82d0ee9ee53aec8fd2a54dc0e426289b", "authdataroot": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"blockcommitmentshash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82" "blockcommitmentshash": "02990723c6b62a724651322d141b4a72a4ffd66518167d809badbd5117d5518a"
}, },
"transactions": [], "transactions": [],
"coinbasetxn": { "coinbasetxn": {
"data": "050000800a27a726b4d0d6c20000000041be1900010000000000000000000000000000000000000000000000000000000000000000ffffffff040341be190000000004286bee000000000017a914d45cb1adffb5215a42720532a076f02c7c778c908738c94d010000000017a91469a9f95a98fe581b6eb52841ef4806dc4402eb908740787d010000000017a914931fec54c1fea86e574462cc32013f5400b891298780b2e60e0000000017a914adadadadadadadadadadadadadadadadadadadad87000000", "data": "0400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff050341be1900ffffffff0438c94d010000000017a91469a9f95a98fe581b6eb52841ef4806dc4402eb908740787d010000000017a914931fec54c1fea86e574462cc32013f5400b891298780b2e60e0000000017a914adadadadadadadadadadadadadadadadadadadad87286bee000000000017a914d45cb1adffb5215a42720532a076f02c7c778c90870000000041be19000000000000000000000000",
"hash": "6b370584714ab567c9c014ce72d325ab6c5927e181ac891acb35e6d4b6cc19a1", "hash": "e049ed10466f566a045702ad712bbb596c6863cd08cdb4646da749b2287bc219",
"authdigest": "0dbb78de9fdcd494307971e36dd049fc82d0ee9ee53aec8fd2a54dc0e426289b", "authdigest": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"depends": [], "depends": [],
"fee": 0, "fee": 0,
"sigops": 0, "sigops": 0,

View File

@ -6,20 +6,20 @@ expression: block_template
"capabilities": [], "capabilities": [],
"version": 4, "version": 4,
"previousblockhash": "0000000000d723156d9b65ffcf4984da7a19675ed7e2f06d9e5d5188af087bf8", "previousblockhash": "0000000000d723156d9b65ffcf4984da7a19675ed7e2f06d9e5d5188af087bf8",
"blockcommitmentshash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422", "blockcommitmentshash": "3b25791957f9383b6ce851d728a78309664d5d7a82ca87b6a9125a2f2c529792",
"lightclientroothash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422", "lightclientroothash": "3b25791957f9383b6ce851d728a78309664d5d7a82ca87b6a9125a2f2c529792",
"finalsaplingroothash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422", "finalsaplingroothash": "3b25791957f9383b6ce851d728a78309664d5d7a82ca87b6a9125a2f2c529792",
"defaultroots": { "defaultroots": {
"merkleroot": "623400cc122baa015d3a4209f5903ebe215170c7e6e74831dce8372c5fd5b3cc", "merkleroot": "f1f2db76c33c4a81f799d0c5cfba83c99c20cc8c51958a615d410fe7fbf92b34",
"chainhistoryroot": "03bc75f00c307a05aed2023819e18c2672cbe15fbd3200944997def141967387", "chainhistoryroot": "03bc75f00c307a05aed2023819e18c2672cbe15fbd3200944997def141967387",
"authdataroot": "a44375f0c0dd5ba612bd7b0efd77683cde8edf5055aff9fbfda443cc8d46bd3e", "authdataroot": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"blockcommitmentshash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422" "blockcommitmentshash": "3b25791957f9383b6ce851d728a78309664d5d7a82ca87b6a9125a2f2c529792"
}, },
"transactions": [], "transactions": [],
"coinbasetxn": { "coinbasetxn": {
"data": "050000800a27a726b4d0d6c200000000f51c1c00010000000000000000000000000000000000000000000000000000000000000000ffffffff0403f51c1c0000000004286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8738c94d010000000017a9144e3f0d9a33a2721604cbae2de8d9171e21f8fbe48740787d010000000017a91471e1df05024288a00802de81e08c437859586c878780b2e60e0000000017a914adadadadadadadadadadadadadadadadadadadad87000000", "data": "0400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0503f51c1c00ffffffff04286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8738c94d010000000017a9144e3f0d9a33a2721604cbae2de8d9171e21f8fbe48740787d010000000017a91471e1df05024288a00802de81e08c437859586c878780b2e60e0000000017a914adadadadadadadadadadadadadadadadadadadad8700000000f51c1c000000000000000000000000",
"hash": "623400cc122baa015d3a4209f5903ebe215170c7e6e74831dce8372c5fd5b3cc", "hash": "f1f2db76c33c4a81f799d0c5cfba83c99c20cc8c51958a615d410fe7fbf92b34",
"authdigest": "a44375f0c0dd5ba612bd7b0efd77683cde8edf5055aff9fbfda443cc8d46bd3e", "authdigest": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"depends": [], "depends": [],
"fee": 0, "fee": 0,
"sigops": 0, "sigops": 0,