change(rpc): generate coinbase transactions in the getblocktemplate RPC (#5580)
* Add a getblocktemplate-rpcs feature to zebra-chain, and fix missing feature deps * Add a coinbase transaction creation stub * Add coinbase creation to zebra-chain * Add coinbase creation and miner subsidy to zebra-consensus * Add the miner config to the GetBlockTemplateRpcImpl * Generate the coinbase transaction in the getblocktemplate RPC * Provide fake valid block heights to getblocktemplate RPC tests * Update getblocktemplate RPC snapshots * Add a getblocktemplate.coinbase_tx deserialized transaction snapshot test * Update snapshots * Return funding stream outputs in the same order every time * Update snapshots * Fix a script bytes bug * Update snapshots
This commit is contained in:
parent
ff81432582
commit
7e13677197
|
@ -14,6 +14,9 @@ mod sighash;
|
||||||
mod txid;
|
mod txid;
|
||||||
mod unmined;
|
mod unmined;
|
||||||
|
|
||||||
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
pub mod builder;
|
||||||
|
|
||||||
#[cfg(any(test, feature = "proptest-impl"))]
|
#[cfg(any(test, feature = "proptest-impl"))]
|
||||||
#[allow(clippy::unwrap_in_result)]
|
#[allow(clippy::unwrap_in_result)]
|
||||||
pub mod arbitrary;
|
pub mod arbitrary;
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -180,6 +180,25 @@ impl fmt::Display for Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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.
|
/// Returns the input's sequence number.
|
||||||
pub fn sequence(&self) -> u32 {
|
pub fn sequence(&self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
|
@ -333,6 +352,15 @@ pub struct Output {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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.
|
/// Get the value contained in this output.
|
||||||
/// This amount is subtracted from the transaction value pool by this output.
|
/// This amount is subtracted from the transaction value pool by this output.
|
||||||
pub fn value(&self) -> Amount<NonNegative> {
|
pub fn value(&self) -> Amount<NonNegative> {
|
||||||
|
|
|
@ -19,19 +19,22 @@ use super::{CoinbaseData, Input, OutPoint, Output, Script};
|
||||||
///
|
///
|
||||||
/// Includes the encoded coinbase height, if any.
|
/// Includes the encoded coinbase height, if any.
|
||||||
///
|
///
|
||||||
/// > The number of bytes in the coinbase script, up to a maximum of 100 bytes.
|
/// # Consensus
|
||||||
///
|
///
|
||||||
/// <https://developer.bitcoin.org/reference/transactions.html#coinbase-input-the-input-of-the-first-transaction-in-a-block>
|
/// > A coinbase transaction script MUST have length in {2 .. 100} bytes.
|
||||||
|
///
|
||||||
|
/// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
|
||||||
pub const MAX_COINBASE_DATA_LEN: usize = 100;
|
pub const MAX_COINBASE_DATA_LEN: usize = 100;
|
||||||
|
|
||||||
/// The minimum length of the coinbase data.
|
/// The minimum length of the coinbase data.
|
||||||
///
|
///
|
||||||
/// Includes the encoded coinbase height, if any.
|
/// Includes the encoded coinbase height, if any.
|
||||||
///
|
///
|
||||||
// TODO: Update the link below once the constant is documented in the
|
/// # Consensus
|
||||||
// protocol.
|
|
||||||
///
|
///
|
||||||
/// <https://github.com/zcash/zips/issues/589>
|
/// > A coinbase transaction script MUST have length in {2 .. 100} bytes.
|
||||||
|
///
|
||||||
|
/// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
|
||||||
pub const MIN_COINBASE_DATA_LEN: usize = 2;
|
pub const MIN_COINBASE_DATA_LEN: usize = 2;
|
||||||
|
|
||||||
/// The coinbase data for a genesis block.
|
/// The coinbase data for a genesis block.
|
||||||
|
|
|
@ -33,7 +33,7 @@ use zebra_state as zs;
|
||||||
use crate::{error::*, transaction as tx, BoxError};
|
use crate::{error::*, transaction as tx, BoxError};
|
||||||
|
|
||||||
pub mod check;
|
pub mod check;
|
||||||
mod subsidy;
|
pub mod subsidy;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
|
@ -124,20 +124,43 @@ pub fn funding_stream_address(
|
||||||
///
|
///
|
||||||
/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
|
/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
|
||||||
pub fn check_script_form(lock_script: &Script, address: Address) -> bool {
|
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()
|
.zcash_serialize_to_vec()
|
||||||
.expect("we should get address bytes here");
|
.expect("we should get address bytes here");
|
||||||
|
|
||||||
address_hash = address_hash[2..22].to_vec();
|
// > The “prescribed way” to pay a transparent P2SH address is to use a standard P2SH script
|
||||||
address_hash.insert(0, OpCode::Push20Bytes as u8);
|
// > of the form OP_HASH160 fs.RedeemScriptHash(height) OP_EQUAL as the scriptPubKey.
|
||||||
address_hash.insert(0, OpCode::Hash160 as u8);
|
//
|
||||||
address_hash.insert(address_hash.len(), OpCode::Equal as u8);
|
// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
|
||||||
if lock_script.as_raw_bytes().len() == address_hash.len()
|
let mut script_bytes = Vec::new();
|
||||||
&& *lock_script == Script::new(&address_hash)
|
|
||||||
{
|
script_bytes.push(OpCode::Hash160 as u8);
|
||||||
return true;
|
script_bytes.push(OpCode::Push20Bytes as u8);
|
||||||
}
|
script_bytes.extend(&address_hash[2..22]);
|
||||||
false
|
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`.
|
/// Returns a list of outputs in `Transaction`, which have a script address equal to `Address`.
|
||||||
|
|
|
@ -11,7 +11,7 @@ use zebra_chain::{
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::parameters::subsidy::*;
|
use crate::{funding_stream_values, parameters::subsidy::*};
|
||||||
|
|
||||||
/// The divisor used for halvings.
|
/// The divisor used for halvings.
|
||||||
///
|
///
|
||||||
|
@ -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`.
|
/// Returns all output amounts in `Transaction`.
|
||||||
pub fn output_amounts(transaction: &Transaction) -> HashSet<Amount<NonNegative>> {
|
pub fn output_amounts(transaction: &Transaction) -> HashSet<Amount<NonNegative>> {
|
||||||
transaction
|
transaction
|
||||||
|
|
|
@ -46,7 +46,11 @@ pub mod chain;
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub mod error;
|
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 chain::VerifyChainError;
|
||||||
pub use checkpoint::{
|
pub use checkpoint::{
|
||||||
CheckpointList, VerifyCheckpointError, MAX_CHECKPOINT_BYTE_COUNT, MAX_CHECKPOINT_HEIGHT_GAP,
|
CheckpointList, VerifyCheckpointError, MAX_CHECKPOINT_BYTE_COUNT, MAX_CHECKPOINT_HEIGHT_GAP,
|
||||||
|
|
|
@ -18,10 +18,12 @@ use zebra_chain::{
|
||||||
chain_tip::ChainTip,
|
chain_tip::ChainTip,
|
||||||
parameters::Network,
|
parameters::Network,
|
||||||
serialization::ZcashDeserializeInto,
|
serialization::ZcashDeserializeInto,
|
||||||
transaction::{UnminedTx, VerifiedUnminedTx},
|
transaction::{Transaction, UnminedTx, VerifiedUnminedTx},
|
||||||
|
transparent,
|
||||||
};
|
};
|
||||||
use zebra_consensus::{
|
use zebra_consensus::{
|
||||||
BlockError, VerifyBlockError, VerifyChainError, VerifyCheckpointError, MAX_BLOCK_SIGOPS,
|
funding_stream_address, funding_stream_values, miner_subsidy, new_coinbase_script, BlockError,
|
||||||
|
VerifyBlockError, VerifyChainError, VerifyCheckpointError, MAX_BLOCK_SIGOPS,
|
||||||
};
|
};
|
||||||
use zebra_node_services::mempool;
|
use zebra_node_services::mempool;
|
||||||
|
|
||||||
|
@ -134,10 +136,13 @@ where
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
//
|
//
|
||||||
// TODO: add mining config for getblocktemplate RPC miner address
|
|
||||||
//
|
|
||||||
/// The configured network for this RPC service.
|
/// The configured network for this RPC service.
|
||||||
_network: Network,
|
network: Network,
|
||||||
|
|
||||||
|
/// The configured miner address for this RPC service.
|
||||||
|
///
|
||||||
|
/// Zebra currently only supports single-signature P2SH transparent addresses.
|
||||||
|
miner_address: Option<transparent::Address>,
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
//
|
//
|
||||||
|
@ -179,13 +184,15 @@ where
|
||||||
/// Create a new instance of the handler for getblocktemplate RPCs.
|
/// Create a new instance of the handler for getblocktemplate RPCs.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
network: Network,
|
network: Network,
|
||||||
|
mining_config: config::Config,
|
||||||
mempool: Buffer<Mempool, mempool::Request>,
|
mempool: Buffer<Mempool, mempool::Request>,
|
||||||
state: State,
|
state: State,
|
||||||
latest_chain_tip: Tip,
|
latest_chain_tip: Tip,
|
||||||
chain_verifier: ChainVerifier,
|
chain_verifier: ChainVerifier,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
_network: network,
|
network,
|
||||||
|
miner_address: mining_config.miner_address,
|
||||||
mempool,
|
mempool,
|
||||||
state,
|
state,
|
||||||
latest_chain_tip,
|
latest_chain_tip,
|
||||||
|
@ -258,66 +265,31 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block_template(&self) -> BoxFuture<Result<GetBlockTemplate>> {
|
fn get_block_template(&self) -> BoxFuture<Result<GetBlockTemplate>> {
|
||||||
|
let network = self.network;
|
||||||
|
let miner_address = self.miner_address;
|
||||||
|
|
||||||
let mempool = self.mempool.clone();
|
let mempool = self.mempool.clone();
|
||||||
let latest_chain_tip = self.latest_chain_tip.clone();
|
let latest_chain_tip = self.latest_chain_tip.clone();
|
||||||
|
|
||||||
// Since this is a very large RPC, we use separate functions for each group of fields.
|
// Since this is a very large RPC, we use separate functions for each group of fields.
|
||||||
async move {
|
async move {
|
||||||
let _tip_height = best_chain_tip_height(&latest_chain_tip)?;
|
let miner_address = miner_address.ok_or_else(|| Error {
|
||||||
|
code: ErrorCode::ServerError(0),
|
||||||
|
message: "configure mining.miner_address in zebrad.toml \
|
||||||
|
with a transparent P2SH single signature address"
|
||||||
|
.to_string(),
|
||||||
|
data: None,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let tip_height = best_chain_tip_height(&latest_chain_tip)?;
|
||||||
let mempool_txs = select_mempool_transactions(mempool).await?;
|
let mempool_txs = select_mempool_transactions(mempool).await?;
|
||||||
|
|
||||||
let miner_fee = miner_fee(&mempool_txs);
|
let miner_fee = miner_fee(&mempool_txs);
|
||||||
|
|
||||||
/*
|
let block_height = (tip_height + 1).expect("tip is far below Height::MAX");
|
||||||
Fake a "coinbase" transaction by duplicating a mempool transaction,
|
let outputs =
|
||||||
or fake a response.
|
standard_coinbase_outputs(network, block_height, miner_address, miner_fee);
|
||||||
|
let coinbase_tx = Transaction::new_v5_coinbase(network, block_height, outputs).into();
|
||||||
This is temporarily required for the tests to pass.
|
|
||||||
|
|
||||||
TODO: create a method Transaction::new_v5_coinbase(network, tip_height, miner_fee),
|
|
||||||
and call it here.
|
|
||||||
*/
|
|
||||||
let coinbase_tx = if mempool_txs.is_empty() {
|
|
||||||
let empty_string = String::from("");
|
|
||||||
return Ok(GetBlockTemplate {
|
|
||||||
capabilities: vec![],
|
|
||||||
version: ZCASH_BLOCK_VERSION,
|
|
||||||
previous_block_hash: GetBlockHash([0; 32].into()),
|
|
||||||
block_commitments_hash: [0; 32].into(),
|
|
||||||
light_client_root_hash: [0; 32].into(),
|
|
||||||
final_sapling_root_hash: [0; 32].into(),
|
|
||||||
default_roots: DefaultRoots {
|
|
||||||
merkle_root: [0; 32].into(),
|
|
||||||
chain_history_root: [0; 32].into(),
|
|
||||||
auth_data_root: [0; 32].into(),
|
|
||||||
block_commitments_hash: [0; 32].into(),
|
|
||||||
},
|
|
||||||
transactions: Vec::new(),
|
|
||||||
coinbase_txn: TransactionTemplate {
|
|
||||||
data: Vec::new().into(),
|
|
||||||
hash: [0; 32].into(),
|
|
||||||
auth_digest: [0; 32].into(),
|
|
||||||
depends: Vec::new(),
|
|
||||||
fee: Amount::zero(),
|
|
||||||
sigops: 0,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
target: empty_string.clone(),
|
|
||||||
min_time: 0,
|
|
||||||
mutable: constants::GET_BLOCK_TEMPLATE_MUTABLE_FIELD
|
|
||||||
.iter()
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.collect(),
|
|
||||||
nonce_range: constants::GET_BLOCK_TEMPLATE_NONCE_RANGE_FIELD.to_string(),
|
|
||||||
sigop_limit: MAX_BLOCK_SIGOPS,
|
|
||||||
size_limit: MAX_BLOCK_BYTES,
|
|
||||||
cur_time: 0,
|
|
||||||
bits: empty_string,
|
|
||||||
height: 0,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
mempool_txs[0].transaction.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let (merkle_root, auth_data_root) =
|
let (merkle_root, auth_data_root) =
|
||||||
calculate_transaction_roots(&coinbase_tx, &mempool_txs);
|
calculate_transaction_roots(&coinbase_tx, &mempool_txs);
|
||||||
|
@ -490,6 +462,42 @@ pub fn miner_fee(mempool_txs: &[VerifiedUnminedTx]) -> Amount<NonNegative> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the standard funding stream and miner reward transparent output scripts
|
||||||
|
/// for `network`, `height` and `miner_fee`.
|
||||||
|
///
|
||||||
|
/// Only works for post-Canopy heights.
|
||||||
|
pub fn standard_coinbase_outputs(
|
||||||
|
network: Network,
|
||||||
|
height: Height,
|
||||||
|
miner_address: transparent::Address,
|
||||||
|
miner_fee: Amount<NonNegative>,
|
||||||
|
) -> Vec<(Amount<NonNegative>, transparent::Script)> {
|
||||||
|
let funding_streams = funding_stream_values(height, network)
|
||||||
|
.expect("funding stream value calculations are valid for reasonable chain heights");
|
||||||
|
|
||||||
|
let mut funding_streams: Vec<(Amount<NonNegative>, transparent::Address)> = funding_streams
|
||||||
|
.iter()
|
||||||
|
.map(|(receiver, amount)| (*amount, funding_stream_address(height, network, *receiver)))
|
||||||
|
.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)
|
||||||
|
.expect("reward calculations are valid for reasonable chain heights")
|
||||||
|
+ miner_fee;
|
||||||
|
let miner_reward =
|
||||||
|
miner_reward.expect("reward calculations are valid for reasonable chain heights");
|
||||||
|
|
||||||
|
let mut coinbase_outputs = funding_streams;
|
||||||
|
coinbase_outputs.push((miner_reward, miner_address));
|
||||||
|
|
||||||
|
coinbase_outputs
|
||||||
|
.iter()
|
||||||
|
.map(|(amount, address)| (*amount, new_coinbase_script(*address)))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the transaction effecting and authorizing roots
|
/// Returns the transaction effecting and authorizing roots
|
||||||
/// for `coinbase_tx` and `mempool_txs`.
|
/// for `coinbase_tx` and `mempool_txs`.
|
||||||
//
|
//
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use zebra_chain::transparent::Address;
|
use zebra_chain::transparent;
|
||||||
|
|
||||||
/// Mining configuration section.
|
/// Mining configuration section.
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
#[serde(deny_unknown_fields, default)]
|
#[serde(deny_unknown_fields, default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// The address used for miner payouts.
|
/// The address used for miner payouts.
|
||||||
/// Currently, Zebra only supports transparent addresses.
|
/// Zebra currently only supports single-signature P2SH transparent addresses.
|
||||||
///
|
///
|
||||||
/// Zebra sends mining fees and miner rewards to this address in the
|
/// Zebra sends mining fees and miner rewards to this address in the
|
||||||
/// `getblocktemplate` RPC coinbase transaction.
|
/// `getblocktemplate` RPC coinbase transaction.
|
||||||
pub miner_address: Option<Address>,
|
pub miner_address: Option<transparent::Address>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,15 +8,22 @@
|
||||||
use insta::Settings;
|
use insta::Settings;
|
||||||
use tower::{buffer::Buffer, Service};
|
use tower::{buffer::Buffer, Service};
|
||||||
|
|
||||||
use zebra_chain::parameters::Network;
|
use zebra_chain::{
|
||||||
|
chain_tip::mock::MockChainTip,
|
||||||
|
parameters::{Network, NetworkUpgrade},
|
||||||
|
serialization::ZcashDeserializeInto,
|
||||||
|
transaction::Transaction,
|
||||||
|
transparent,
|
||||||
|
};
|
||||||
use zebra_node_services::mempool;
|
use zebra_node_services::mempool;
|
||||||
use zebra_state::LatestChainTip;
|
use zebra_state::LatestChainTip;
|
||||||
|
|
||||||
use zebra_test::mock_service::{MockService, PanicAssertion};
|
use zebra_test::mock_service::{MockService, PanicAssertion};
|
||||||
|
|
||||||
use crate::methods::{
|
use crate::methods::{
|
||||||
get_block_template_rpcs::types::{
|
get_block_template_rpcs::{
|
||||||
get_block_template::GetBlockTemplate, hex_data::HexData, submit_block,
|
self,
|
||||||
|
types::{get_block_template::GetBlockTemplate, hex_data::HexData, submit_block},
|
||||||
},
|
},
|
||||||
GetBlockHash, GetBlockTemplateRpc, GetBlockTemplateRpcImpl,
|
GetBlockHash, GetBlockTemplateRpc, GetBlockTemplateRpcImpl,
|
||||||
};
|
};
|
||||||
|
@ -31,7 +38,7 @@ pub async fn test_responses<State, ReadState>(
|
||||||
>,
|
>,
|
||||||
state: State,
|
state: State,
|
||||||
read_state: ReadState,
|
read_state: ReadState,
|
||||||
latest_chain_tip: LatestChainTip,
|
_latest_chain_tip: LatestChainTip,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
) where
|
) where
|
||||||
State: Service<
|
State: Service<
|
||||||
|
@ -66,11 +73,19 @@ pub async fn test_responses<State, ReadState>(
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let mining_config = get_block_template_rpcs::config::Config {
|
||||||
|
miner_address: Some(transparent::Address::from_script_hash(network, [0xad; 20])),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mock_chain_tip, mock_chain_tip_sender) = MockChainTip::new();
|
||||||
|
mock_chain_tip_sender.send_best_tip_height(NetworkUpgrade::Nu5.activation_height(network));
|
||||||
|
|
||||||
let get_block_template_rpc = GetBlockTemplateRpcImpl::new(
|
let get_block_template_rpc = GetBlockTemplateRpcImpl::new(
|
||||||
network,
|
network,
|
||||||
|
mining_config,
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
read_state,
|
read_state,
|
||||||
latest_chain_tip,
|
mock_chain_tip,
|
||||||
chain_verifier,
|
chain_verifier,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -102,7 +117,14 @@ pub async fn test_responses<State, ReadState>(
|
||||||
.expect("unexpected panic in getblocktemplate RPC task")
|
.expect("unexpected panic in getblocktemplate RPC task")
|
||||||
.expect("unexpected error in getblocktemplate RPC call");
|
.expect("unexpected error in getblocktemplate RPC call");
|
||||||
|
|
||||||
snapshot_rpc_getblocktemplate(get_block_template, &settings);
|
let coinbase_tx: Transaction = get_block_template
|
||||||
|
.coinbase_txn
|
||||||
|
.data
|
||||||
|
.as_ref()
|
||||||
|
.zcash_deserialize_into()
|
||||||
|
.expect("coinbase bytes are valid");
|
||||||
|
|
||||||
|
snapshot_rpc_getblocktemplate(get_block_template, coinbase_tx, &settings);
|
||||||
|
|
||||||
// `submitblock`
|
// `submitblock`
|
||||||
let submit_block = get_block_template_rpc
|
let submit_block = get_block_template_rpc
|
||||||
|
@ -124,8 +146,13 @@ fn snapshot_rpc_getblockhash(block_hash: GetBlockHash, settings: &insta::Setting
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Snapshot `getblocktemplate` response, using `cargo insta` and JSON serialization.
|
/// Snapshot `getblocktemplate` response, using `cargo insta` and JSON serialization.
|
||||||
fn snapshot_rpc_getblocktemplate(block_template: GetBlockTemplate, settings: &insta::Settings) {
|
fn snapshot_rpc_getblocktemplate(
|
||||||
|
block_template: GetBlockTemplate,
|
||||||
|
coinbase_tx: Transaction,
|
||||||
|
settings: &insta::Settings,
|
||||||
|
) {
|
||||||
settings.bind(|| insta::assert_json_snapshot!("get_block_template", block_template));
|
settings.bind(|| insta::assert_json_snapshot!("get_block_template", block_template));
|
||||||
|
settings.bind(|| insta::assert_json_snapshot!("get_block_template.coinbase_tx", coinbase_tx));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Snapshot `submitblock` response, using `cargo insta` and JSON serialization.
|
/// Snapshot `submitblock` response, using `cargo insta` and JSON serialization.
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
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: block_count
|
expression: block_count
|
||||||
---
|
---
|
||||||
10
|
1687104
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
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: block_count
|
expression: block_count
|
||||||
---
|
---
|
||||||
10
|
1842420
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||||
|
expression: coinbase_tx
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"V5": {
|
||||||
|
"network_upgrade": "Nu5",
|
||||||
|
"lock_time": {
|
||||||
|
"Height": 0
|
||||||
|
},
|
||||||
|
"expiry_height": 1687105,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"Coinbase": {
|
||||||
|
"height": 1687105,
|
||||||
|
"data": [],
|
||||||
|
"sequence": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"value": 15625000,
|
||||||
|
"lock_script": "a914d45cb1adffb5215a42720532a076f02c7c778c9087"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 21875000,
|
||||||
|
"lock_script": "a91469a9f95a98fe581b6eb52841ef4806dc4402eb9087"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 25000000,
|
||||||
|
"lock_script": "a914931fec54c1fea86e574462cc32013f5400b8912987"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 250000000,
|
||||||
|
"lock_script": "a914adadadadadadadadadadadadadadadadadadadad87"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sapling_shielded_data": null,
|
||||||
|
"orchard_shielded_data": null
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||||
|
expression: coinbase_tx
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"V5": {
|
||||||
|
"network_upgrade": "Nu5",
|
||||||
|
"lock_time": {
|
||||||
|
"Height": 0
|
||||||
|
},
|
||||||
|
"expiry_height": 1842421,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"Coinbase": {
|
||||||
|
"height": 1842421,
|
||||||
|
"data": [],
|
||||||
|
"sequence": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"value": 15625000,
|
||||||
|
"lock_script": "a9140c0bcca02f3cba01a5d7423ac3903d40586399eb87"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 21875000,
|
||||||
|
"lock_script": "a9144e3f0d9a33a2721604cbae2de8d9171e21f8fbe487"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 25000000,
|
||||||
|
"lock_script": "a91471e1df05024288a00802de81e08c437859586c8787"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 250000000,
|
||||||
|
"lock_script": "a914adadadadadadadadadadadadadadadadadadadad87"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sapling_shielded_data": null,
|
||||||
|
"orchard_shielded_data": null
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
---
|
---
|
||||||
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||||
assertion_line: 128
|
|
||||||
expression: block_template
|
expression: block_template
|
||||||
---
|
---
|
||||||
{
|
{
|
||||||
|
@ -11,16 +10,16 @@ expression: block_template
|
||||||
"lightclientroothash": "0000000000000000000000000000000000000000000000000000000000000000",
|
"lightclientroothash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"finalsaplingroothash": "0000000000000000000000000000000000000000000000000000000000000000",
|
"finalsaplingroothash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"defaultroots": {
|
"defaultroots": {
|
||||||
"merkleroot": "0000000000000000000000000000000000000000000000000000000000000000",
|
"merkleroot": "6b370584714ab567c9c014ce72d325ab6c5927e181ac891acb35e6d4b6cc19a1",
|
||||||
"chainhistoryroot": "0000000000000000000000000000000000000000000000000000000000000000",
|
"chainhistoryroot": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"authdataroot": "0000000000000000000000000000000000000000000000000000000000000000",
|
"authdataroot": "0dbb78de9fdcd494307971e36dd049fc82d0ee9ee53aec8fd2a54dc0e426289b",
|
||||||
"blockcommitmentshash": "0000000000000000000000000000000000000000000000000000000000000000"
|
"blockcommitmentshash": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
},
|
},
|
||||||
"transactions": [],
|
"transactions": [],
|
||||||
"coinbasetxn": {
|
"coinbasetxn": {
|
||||||
"data": "",
|
"data": "050000800a27a726b4d0d6c20000000041be1900010000000000000000000000000000000000000000000000000000000000000000ffffffff040341be190000000004286bee000000000017a914d45cb1adffb5215a42720532a076f02c7c778c908738c94d010000000017a91469a9f95a98fe581b6eb52841ef4806dc4402eb908740787d010000000017a914931fec54c1fea86e574462cc32013f5400b891298780b2e60e0000000017a914adadadadadadadadadadadadadadadadadadadad87000000",
|
||||||
"hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
"hash": "6b370584714ab567c9c014ce72d325ab6c5927e181ac891acb35e6d4b6cc19a1",
|
||||||
"authdigest": "0000000000000000000000000000000000000000000000000000000000000000",
|
"authdigest": "0dbb78de9fdcd494307971e36dd049fc82d0ee9ee53aec8fd2a54dc0e426289b",
|
||||||
"depends": [],
|
"depends": [],
|
||||||
"fee": 0,
|
"fee": 0,
|
||||||
"sigops": 0,
|
"sigops": 0,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
---
|
---
|
||||||
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||||
assertion_line: 128
|
|
||||||
expression: block_template
|
expression: block_template
|
||||||
---
|
---
|
||||||
{
|
{
|
||||||
|
@ -11,16 +10,16 @@ expression: block_template
|
||||||
"lightclientroothash": "0000000000000000000000000000000000000000000000000000000000000000",
|
"lightclientroothash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"finalsaplingroothash": "0000000000000000000000000000000000000000000000000000000000000000",
|
"finalsaplingroothash": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"defaultroots": {
|
"defaultroots": {
|
||||||
"merkleroot": "0000000000000000000000000000000000000000000000000000000000000000",
|
"merkleroot": "623400cc122baa015d3a4209f5903ebe215170c7e6e74831dce8372c5fd5b3cc",
|
||||||
"chainhistoryroot": "0000000000000000000000000000000000000000000000000000000000000000",
|
"chainhistoryroot": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"authdataroot": "0000000000000000000000000000000000000000000000000000000000000000",
|
"authdataroot": "a44375f0c0dd5ba612bd7b0efd77683cde8edf5055aff9fbfda443cc8d46bd3e",
|
||||||
"blockcommitmentshash": "0000000000000000000000000000000000000000000000000000000000000000"
|
"blockcommitmentshash": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
},
|
},
|
||||||
"transactions": [],
|
"transactions": [],
|
||||||
"coinbasetxn": {
|
"coinbasetxn": {
|
||||||
"data": "",
|
"data": "050000800a27a726b4d0d6c200000000f51c1c00010000000000000000000000000000000000000000000000000000000000000000ffffffff0403f51c1c0000000004286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8738c94d010000000017a9144e3f0d9a33a2721604cbae2de8d9171e21f8fbe48740787d010000000017a91471e1df05024288a00802de81e08c437859586c878780b2e60e0000000017a914adadadadadadadadadadadadadadadadadadadad87000000",
|
||||||
"hash": "0000000000000000000000000000000000000000000000000000000000000000",
|
"hash": "623400cc122baa015d3a4209f5903ebe215170c7e6e74831dce8372c5fd5b3cc",
|
||||||
"authdigest": "0000000000000000000000000000000000000000000000000000000000000000",
|
"authdigest": "a44375f0c0dd5ba612bd7b0efd77683cde8edf5055aff9fbfda443cc8d46bd3e",
|
||||||
"depends": [],
|
"depends": [],
|
||||||
"fee": 0,
|
"fee": 0,
|
||||||
"sigops": 0,
|
"sigops": 0,
|
||||||
|
|
|
@ -651,6 +651,7 @@ async fn rpc_getblockcount() {
|
||||||
// Init RPC
|
// Init RPC
|
||||||
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
||||||
Mainnet,
|
Mainnet,
|
||||||
|
Default::default(),
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
read_state,
|
read_state,
|
||||||
latest_chain_tip.clone(),
|
latest_chain_tip.clone(),
|
||||||
|
@ -695,6 +696,7 @@ async fn rpc_getblockcount_empty_state() {
|
||||||
// Init RPC
|
// Init RPC
|
||||||
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
||||||
Mainnet,
|
Mainnet,
|
||||||
|
Default::default(),
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
read_state,
|
read_state,
|
||||||
latest_chain_tip.clone(),
|
latest_chain_tip.clone(),
|
||||||
|
@ -745,6 +747,7 @@ async fn rpc_getblockhash() {
|
||||||
// Init RPC
|
// Init RPC
|
||||||
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
||||||
Mainnet,
|
Mainnet,
|
||||||
|
Default::default(),
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
read_state,
|
read_state,
|
||||||
latest_chain_tip.clone(),
|
latest_chain_tip.clone(),
|
||||||
|
@ -785,7 +788,11 @@ async fn rpc_getblocktemplate() {
|
||||||
use crate::methods::get_block_template_rpcs::constants::{
|
use crate::methods::get_block_template_rpcs::constants::{
|
||||||
GET_BLOCK_TEMPLATE_MUTABLE_FIELD, GET_BLOCK_TEMPLATE_NONCE_RANGE_FIELD,
|
GET_BLOCK_TEMPLATE_MUTABLE_FIELD, GET_BLOCK_TEMPLATE_NONCE_RANGE_FIELD,
|
||||||
};
|
};
|
||||||
use zebra_chain::block::{MAX_BLOCK_BYTES, ZCASH_BLOCK_VERSION};
|
use zebra_chain::{
|
||||||
|
amount::{Amount, NonNegative},
|
||||||
|
block::{MAX_BLOCK_BYTES, ZCASH_BLOCK_VERSION},
|
||||||
|
chain_tip::mock::MockChainTip,
|
||||||
|
};
|
||||||
use zebra_consensus::MAX_BLOCK_SIGOPS;
|
use zebra_consensus::MAX_BLOCK_SIGOPS;
|
||||||
|
|
||||||
let _init_guard = zebra_test::init();
|
let _init_guard = zebra_test::init();
|
||||||
|
@ -798,7 +805,7 @@ async fn rpc_getblocktemplate() {
|
||||||
|
|
||||||
let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests();
|
let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests();
|
||||||
// Create a populated state service
|
// Create a populated state service
|
||||||
let (state, read_state, latest_chain_tip, _chain_tip_change) =
|
let (state, read_state, _latest_chain_tip, _chain_tip_change) =
|
||||||
zebra_state::populated_state(blocks.clone(), Mainnet).await;
|
zebra_state::populated_state(blocks.clone(), Mainnet).await;
|
||||||
|
|
||||||
let (
|
let (
|
||||||
|
@ -814,12 +821,20 @@ async fn rpc_getblocktemplate() {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let mining_config = get_block_template_rpcs::config::Config {
|
||||||
|
miner_address: Some(transparent::Address::from_script_hash(Mainnet, [0x7e; 20])),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mock_chain_tip, mock_chain_tip_sender) = MockChainTip::new();
|
||||||
|
mock_chain_tip_sender.send_best_tip_height(NetworkUpgrade::Nu5.activation_height(Mainnet));
|
||||||
|
|
||||||
// Init RPC
|
// Init RPC
|
||||||
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
||||||
Mainnet,
|
Mainnet,
|
||||||
|
mining_config,
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
read_state,
|
read_state,
|
||||||
latest_chain_tip.clone(),
|
mock_chain_tip,
|
||||||
tower::ServiceBuilder::new().service(chain_verifier),
|
tower::ServiceBuilder::new().service(chain_verifier),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -859,6 +874,18 @@ async fn rpc_getblocktemplate() {
|
||||||
assert!(get_block_template.bits.is_empty());
|
assert!(get_block_template.bits.is_empty());
|
||||||
assert_eq!(get_block_template.height, 0);
|
assert_eq!(get_block_template.height, 0);
|
||||||
|
|
||||||
|
// Coinbase transaction checks.
|
||||||
|
assert!(get_block_template.coinbase_txn.required);
|
||||||
|
assert!(!get_block_template.coinbase_txn.data.as_ref().is_empty());
|
||||||
|
assert_eq!(get_block_template.coinbase_txn.depends.len(), 0);
|
||||||
|
// TODO: should a coinbase transaction have sigops?
|
||||||
|
assert_eq!(get_block_template.coinbase_txn.sigops, 0);
|
||||||
|
// Coinbase transaction checks for empty blocks.
|
||||||
|
assert_eq!(
|
||||||
|
get_block_template.coinbase_txn.fee,
|
||||||
|
Amount::<NonNegative>::zero()
|
||||||
|
);
|
||||||
|
|
||||||
mempool.expect_no_requests().await;
|
mempool.expect_no_requests().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -879,15 +906,6 @@ async fn rpc_submitblock_errors() {
|
||||||
zebra_state::populated_state(blocks, Mainnet).await;
|
zebra_state::populated_state(blocks, Mainnet).await;
|
||||||
|
|
||||||
// Init RPCs
|
// Init RPCs
|
||||||
let _rpc = RpcImpl::new(
|
|
||||||
"RPC test",
|
|
||||||
Mainnet,
|
|
||||||
false,
|
|
||||||
Buffer::new(mempool.clone(), 1),
|
|
||||||
Buffer::new(read_state.clone(), 1),
|
|
||||||
latest_chain_tip.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let (
|
let (
|
||||||
chain_verifier,
|
chain_verifier,
|
||||||
_transaction_verifier,
|
_transaction_verifier,
|
||||||
|
@ -904,6 +922,7 @@ async fn rpc_submitblock_errors() {
|
||||||
// Init RPC
|
// Init RPC
|
||||||
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
||||||
Mainnet,
|
Mainnet,
|
||||||
|
Default::default(),
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
read_state,
|
read_state,
|
||||||
latest_chain_tip.clone(),
|
latest_chain_tip.clone(),
|
||||||
|
|
|
@ -31,7 +31,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
use crate::methods::{GetBlockTemplateRpc, GetBlockTemplateRpcImpl};
|
use crate::methods::{get_block_template_rpcs, GetBlockTemplateRpc, GetBlockTemplateRpcImpl};
|
||||||
|
|
||||||
pub mod compatibility;
|
pub mod compatibility;
|
||||||
mod tracing_middleware;
|
mod tracing_middleware;
|
||||||
|
@ -44,9 +44,17 @@ mod tests;
|
||||||
pub struct RpcServer;
|
pub struct RpcServer;
|
||||||
|
|
||||||
impl RpcServer {
|
impl RpcServer {
|
||||||
/// Start a new RPC server endpoint
|
/// Start a new RPC server endpoint.
|
||||||
|
//
|
||||||
|
// TODO: put some of the configs or services in their own struct?
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn spawn<Version, Mempool, State, Tip, ChainVerifier>(
|
pub fn spawn<Version, Mempool, State, Tip, ChainVerifier>(
|
||||||
config: Config,
|
config: Config,
|
||||||
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
mining_config: get_block_template_rpcs::config::Config,
|
||||||
|
#[cfg(not(feature = "getblocktemplate-rpcs"))]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
mining_config: (),
|
||||||
app_version: Version,
|
app_version: Version,
|
||||||
mempool: Buffer<Mempool, mempool::Request>,
|
mempool: Buffer<Mempool, mempool::Request>,
|
||||||
state: State,
|
state: State,
|
||||||
|
@ -92,6 +100,7 @@ impl RpcServer {
|
||||||
// Initialize the getblocktemplate rpc method handler
|
// Initialize the getblocktemplate rpc method handler
|
||||||
let get_block_template_rpc_impl = GetBlockTemplateRpcImpl::new(
|
let get_block_template_rpc_impl = GetBlockTemplateRpcImpl::new(
|
||||||
network,
|
network,
|
||||||
|
mining_config,
|
||||||
mempool.clone(),
|
mempool.clone(),
|
||||||
state.clone(),
|
state.clone(),
|
||||||
latest_chain_tip.clone(),
|
latest_chain_tip.clone(),
|
||||||
|
|
|
@ -53,6 +53,7 @@ fn rpc_server_spawn(parallel_cpu_threads: bool) {
|
||||||
|
|
||||||
let (rpc_server_task_handle, rpc_tx_queue_task_handle) = RpcServer::spawn(
|
let (rpc_server_task_handle, rpc_tx_queue_task_handle) = RpcServer::spawn(
|
||||||
config,
|
config,
|
||||||
|
Default::default(),
|
||||||
"RPC server test",
|
"RPC server test",
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
Buffer::new(state.clone(), 1),
|
Buffer::new(state.clone(), 1),
|
||||||
|
@ -124,6 +125,7 @@ fn rpc_server_spawn_unallocated_port(parallel_cpu_threads: bool) {
|
||||||
|
|
||||||
let (rpc_server_task_handle, rpc_tx_queue_task_handle) = RpcServer::spawn(
|
let (rpc_server_task_handle, rpc_tx_queue_task_handle) = RpcServer::spawn(
|
||||||
config,
|
config,
|
||||||
|
Default::default(),
|
||||||
"RPC server test",
|
"RPC server test",
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
Buffer::new(state.clone(), 1),
|
Buffer::new(state.clone(), 1),
|
||||||
|
@ -182,6 +184,7 @@ fn rpc_server_spawn_port_conflict() {
|
||||||
|
|
||||||
let (_rpc_server_1_task_handle, _rpc_tx_queue_1_task_handle) = RpcServer::spawn(
|
let (_rpc_server_1_task_handle, _rpc_tx_queue_1_task_handle) = RpcServer::spawn(
|
||||||
config.clone(),
|
config.clone(),
|
||||||
|
Default::default(),
|
||||||
"RPC server 1 test",
|
"RPC server 1 test",
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
Buffer::new(state.clone(), 1),
|
Buffer::new(state.clone(), 1),
|
||||||
|
@ -196,6 +199,7 @@ fn rpc_server_spawn_port_conflict() {
|
||||||
|
|
||||||
let (rpc_server_2_task_handle, _rpc_tx_queue_2_task_handle) = RpcServer::spawn(
|
let (rpc_server_2_task_handle, _rpc_tx_queue_2_task_handle) = RpcServer::spawn(
|
||||||
config,
|
config,
|
||||||
|
Default::default(),
|
||||||
"RPC server 2 conflict test",
|
"RPC server 2 conflict test",
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
Buffer::new(state.clone(), 1),
|
Buffer::new(state.clone(), 1),
|
||||||
|
@ -283,6 +287,7 @@ fn rpc_server_spawn_port_conflict_parallel_auto() {
|
||||||
|
|
||||||
let (_rpc_server_1_task_handle, _rpc_tx_queue_1_task_handle) = RpcServer::spawn(
|
let (_rpc_server_1_task_handle, _rpc_tx_queue_1_task_handle) = RpcServer::spawn(
|
||||||
config.clone(),
|
config.clone(),
|
||||||
|
Default::default(),
|
||||||
"RPC server 1 test",
|
"RPC server 1 test",
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
Buffer::new(state.clone(), 1),
|
Buffer::new(state.clone(), 1),
|
||||||
|
@ -297,6 +302,7 @@ fn rpc_server_spawn_port_conflict_parallel_auto() {
|
||||||
|
|
||||||
let (rpc_server_2_task_handle, _rpc_tx_queue_2_task_handle) = RpcServer::spawn(
|
let (rpc_server_2_task_handle, _rpc_tx_queue_2_task_handle) = RpcServer::spawn(
|
||||||
config,
|
config,
|
||||||
|
Default::default(),
|
||||||
"RPC server 2 conflict test",
|
"RPC server 2 conflict test",
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
Buffer::new(state.clone(), 1),
|
Buffer::new(state.clone(), 1),
|
||||||
|
|
|
@ -178,6 +178,10 @@ impl StartCmd {
|
||||||
// Launch RPC server
|
// Launch RPC server
|
||||||
let (rpc_task_handle, rpc_tx_queue_task_handle) = RpcServer::spawn(
|
let (rpc_task_handle, rpc_tx_queue_task_handle) = RpcServer::spawn(
|
||||||
config.rpc,
|
config.rpc,
|
||||||
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
config.mining,
|
||||||
|
#[cfg(not(feature = "getblocktemplate-rpcs"))]
|
||||||
|
(),
|
||||||
app_version(),
|
app_version(),
|
||||||
mempool.clone(),
|
mempool.clone(),
|
||||||
read_only_state_service,
|
read_only_state_service,
|
||||||
|
|
Loading…
Reference in New Issue