3. change(rpc): Add fee and sigops fields to getblocktemplate transactions (#5508)

* Add a legacy_sigop_count field to VerifiedUnminedTx

* Add conversions from Vec<VerifiedUnminedTx> to block header roots

* Add fee and sigops field to block template transactions

* Fix up mempool request names

* Increase existing snapshot test coverage

* Document a new method parameter

Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com>

Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com>
This commit is contained in:
teor 2022-11-04 03:03:41 +10:00 committed by GitHub
parent 142411508b
commit 71f5e63e64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 161 additions and 73 deletions

View File

@ -6,7 +6,7 @@ use hex::{FromHex, ToHex};
use crate::{ use crate::{
serialization::sha256d, serialization::sha256d,
transaction::{self, Transaction, UnminedTx, UnminedTxId}, transaction::{self, Transaction, UnminedTx, UnminedTxId, VerifiedUnminedTx},
}; };
#[cfg(any(any(test, feature = "proptest-impl"), feature = "proptest-impl"))] #[cfg(any(any(test, feature = "proptest-impl"), feature = "proptest-impl"))]
@ -204,6 +204,18 @@ impl std::iter::FromIterator<UnminedTxId> for Root {
} }
} }
impl std::iter::FromIterator<VerifiedUnminedTx> for Root {
fn from_iter<I>(transactions: I) -> Self
where
I: IntoIterator<Item = VerifiedUnminedTx>,
{
transactions
.into_iter()
.map(|tx| tx.transaction.id.mined_id())
.collect()
}
}
impl std::iter::FromIterator<transaction::Hash> for Root { impl std::iter::FromIterator<transaction::Hash> for Root {
/// # Panics /// # Panics
/// ///
@ -351,6 +363,23 @@ impl std::iter::FromIterator<UnminedTx> for AuthDataRoot {
} }
} }
impl std::iter::FromIterator<VerifiedUnminedTx> for AuthDataRoot {
fn from_iter<I>(transactions: I) -> Self
where
I: IntoIterator<Item = VerifiedUnminedTx>,
{
transactions
.into_iter()
.map(|tx| {
tx.transaction
.id
.auth_digest()
.unwrap_or(AUTH_DIGEST_PLACEHOLDER)
})
.collect()
}
}
impl std::iter::FromIterator<UnminedTxId> for AuthDataRoot { impl std::iter::FromIterator<UnminedTxId> for AuthDataRoot {
fn from_iter<I>(tx_ids: I) -> Self fn from_iter<I>(tx_ids: I) -> Self
where where

View File

@ -284,6 +284,10 @@ pub struct VerifiedUnminedTx {
/// The transaction fee for this unmined transaction. /// The transaction fee for this unmined transaction.
pub miner_fee: Amount<NonNegative>, pub miner_fee: Amount<NonNegative>,
/// The number of legacy signature operations in this transaction's
/// transparent inputs and outputs.
pub legacy_sigop_count: u64,
} }
impl fmt::Display for VerifiedUnminedTx { impl fmt::Display for VerifiedUnminedTx {
@ -291,16 +295,22 @@ impl fmt::Display for VerifiedUnminedTx {
f.debug_struct("VerifiedUnminedTx") f.debug_struct("VerifiedUnminedTx")
.field("transaction", &self.transaction) .field("transaction", &self.transaction)
.field("miner_fee", &self.miner_fee) .field("miner_fee", &self.miner_fee)
.field("legacy_sigop_count", &self.legacy_sigop_count)
.finish() .finish()
} }
} }
impl VerifiedUnminedTx { impl VerifiedUnminedTx {
/// Create a new verified unmined transaction from a transaction and its fee. /// Create a new verified unmined transaction from a transaction, its fee and the legacy sigop count.
pub fn new(transaction: UnminedTx, miner_fee: Amount<NonNegative>) -> Self { pub fn new(
transaction: UnminedTx,
miner_fee: Amount<NonNegative>,
legacy_sigop_count: u64,
) -> Self {
Self { Self {
transaction, transaction,
miner_fee, miner_fee,
legacy_sigop_count,
} }
} }

View File

@ -229,9 +229,7 @@ where
response response
); );
legacy_sigop_count += response legacy_sigop_count += response.legacy_sigop_count();
.legacy_sigop_count()
.expect("block transaction responses must have a legacy sigop count");
// Coinbase transactions consume the miner fee, // Coinbase transactions consume the miner fee,
// so they don't add any value to the block's total miner fee. // so they don't add any value to the block's total miner fee.

View File

@ -249,7 +249,9 @@ impl Response {
/// The miner fee for the transaction in this response. /// The miner fee for the transaction in this response.
/// ///
/// Coinbase transactions do not have a miner fee. /// Coinbase transactions do not have a miner fee,
/// and they don't need UTXOs to calculate their value balance,
/// because they don't spend any inputs.
pub fn miner_fee(&self) -> Option<Amount<NonNegative>> { pub fn miner_fee(&self) -> Option<Amount<NonNegative>> {
match self { match self {
Response::Block { miner_fee, .. } => *miner_fee, Response::Block { miner_fee, .. } => *miner_fee,
@ -259,15 +261,12 @@ impl Response {
/// The number of legacy transparent signature operations in this transaction's /// The number of legacy transparent signature operations in this transaction's
/// inputs and outputs. /// inputs and outputs.
/// pub fn legacy_sigop_count(&self) -> u64 {
/// Zebra does not check the legacy sigop count for mempool transactions,
/// because it is a standard rule (not a consensus rule).
pub fn legacy_sigop_count(&self) -> Option<u64> {
match self { match self {
Response::Block { Response::Block {
legacy_sigop_count, .. legacy_sigop_count, ..
} => Some(*legacy_sigop_count), } => *legacy_sigop_count,
Response::Mempool { .. } => None, Response::Mempool { transaction, .. } => transaction.legacy_sigop_count,
} }
} }
@ -420,11 +419,13 @@ where
}; };
} }
let legacy_sigop_count = cached_ffi_transaction.legacy_sigop_count()?;
let rsp = match req { let rsp = match req {
Request::Block { .. } => Response::Block { Request::Block { .. } => Response::Block {
tx_id, tx_id,
miner_fee, miner_fee,
legacy_sigop_count: cached_ffi_transaction.legacy_sigop_count()?, legacy_sigop_count,
}, },
Request::Mempool { transaction, .. } => Response::Mempool { Request::Mempool { transaction, .. } => Response::Mempool {
transaction: VerifiedUnminedTx::new( transaction: VerifiedUnminedTx::new(
@ -432,6 +433,7 @@ where
miner_fee.expect( miner_fee.expect(
"unexpected mempool coinbase transaction: should have already rejected", "unexpected mempool coinbase transaction: should have already rejected",
), ),
legacy_sigop_count,
), ),
}, },
}; };

View File

@ -4,7 +4,10 @@
use std::collections::HashSet; use std::collections::HashSet;
use zebra_chain::transaction::{Hash, UnminedTx, UnminedTxId}; use zebra_chain::transaction::{self, UnminedTx, UnminedTxId};
#[cfg(feature = "getblocktemplate-rpcs")]
use zebra_chain::transaction::VerifiedUnminedTx;
use crate::BoxError; use crate::BoxError;
@ -22,24 +25,28 @@ pub use self::gossip::Gossip;
/// because all mempool transactions must be verified. /// because all mempool transactions must be verified.
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub enum Request { pub enum Request {
/// Query all transaction IDs in the mempool. /// Query all [`UnminedTxId`]s in the mempool.
TransactionIds, TransactionIds,
/// Query matching transactions in the mempool, /// Query matching [`UnminedTx`] in the mempool,
/// using a unique set of [`UnminedTxId`]s. /// using a unique set of [`UnminedTxId`]s.
TransactionsById(HashSet<UnminedTxId>), TransactionsById(HashSet<UnminedTxId>),
/// Query matching transactions in the mempool, /// Query matching [`UnminedTx`] in the mempool,
/// using a unique set of [`struct@Hash`]s. Pre-V5 transactions are matched /// using a unique set of [`transaction::Hash`]es. Pre-V5 transactions are matched
/// directly; V5 transaction are matched just by the Hash, disregarding /// directly; V5 transaction are matched just by the Hash, disregarding
/// the [`AuthDigest`](zebra_chain::transaction::AuthDigest). /// the [`AuthDigest`](zebra_chain::transaction::AuthDigest).
TransactionsByMinedId(HashSet<Hash>), TransactionsByMinedId(HashSet<transaction::Hash>),
/// Get all the transactions in the mempool. /// Get all the [`VerifiedUnminedTx`] in the mempool.
/// ///
/// Equivalent to `TransactionsById(TransactionIds)`. /// Equivalent to `TransactionsById(TransactionIds)`,
/// but each transaction also includes the `miner_fee` and `legacy_sigop_count` fields.
//
// TODO: make the Transactions response return VerifiedUnminedTx,
// and remove the FullTransactions variant
#[cfg(feature = "getblocktemplate-rpcs")] #[cfg(feature = "getblocktemplate-rpcs")]
Transactions, FullTransactions,
/// Query matching cached rejected transaction IDs in the mempool, /// Query matching cached rejected transaction IDs in the mempool,
/// using a unique set of [`UnminedTxId`]s. /// using a unique set of [`UnminedTxId`]s.
@ -80,10 +87,10 @@ pub enum Request {
/// confirm that the mempool has been checked for newly verified transactions. /// confirm that the mempool has been checked for newly verified transactions.
#[derive(Debug)] #[derive(Debug)]
pub enum Response { pub enum Response {
/// Returns all transaction IDs from the mempool. /// Returns all [`UnminedTxId`]s from the mempool.
TransactionIds(HashSet<UnminedTxId>), TransactionIds(HashSet<UnminedTxId>),
/// Returns matching transactions from the mempool. /// Returns matching [`UnminedTx`] from the mempool.
/// ///
/// Since the [`Request::TransactionsById`] request is unique, /// Since the [`Request::TransactionsById`] request is unique,
/// the response transactions are also unique. The same applies to /// the response transactions are also unique. The same applies to
@ -91,7 +98,14 @@ pub enum Response {
/// different transactions with different mined IDs. /// different transactions with different mined IDs.
Transactions(Vec<UnminedTx>), Transactions(Vec<UnminedTx>),
/// Returns matching cached rejected transaction IDs from the mempool, /// Returns all [`VerifiedUnminedTx`] in the mempool.
//
// TODO: make the Transactions response return VerifiedUnminedTx,
// and remove the FullTransactions variant
#[cfg(feature = "getblocktemplate-rpcs")]
FullTransactions(Vec<VerifiedUnminedTx>),
/// Returns matching cached rejected [`UnminedTxId`]s from the mempool,
RejectedTransactionIds(HashSet<UnminedTxId>), RejectedTransactionIds(HashSet<UnminedTxId>),
/// Returns a list of queue results. /// Returns a list of queue results.

View File

@ -214,18 +214,18 @@ where
// 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 {
// TODO: put this in a separate get_mempool_transactions() function // TODO: put this in a separate get_mempool_transactions() function
let request = mempool::Request::Transactions; let request = mempool::Request::FullTransactions;
let response = mempool.oneshot(request).await.map_err(|error| Error { let response = mempool.oneshot(request).await.map_err(|error| Error {
code: ErrorCode::ServerError(0), code: ErrorCode::ServerError(0),
message: error.to_string(), message: error.to_string(),
data: None, data: None,
})?; })?;
let transactions = if let mempool::Response::Transactions(transactions) = response { let transactions = if let mempool::Response::FullTransactions(transactions) = response {
// TODO: select transactions according to ZIP-317 (#5473) // TODO: select transactions according to ZIP-317 (#5473)
transactions transactions
} else { } else {
unreachable!("unmatched response to a mempool::Transactions request"); unreachable!("unmatched response to a mempool::FullTransactions request");
}; };
let merkle_root; let merkle_root;

View File

@ -3,7 +3,7 @@
use zebra_chain::{ use zebra_chain::{
amount::{self, Amount, NonNegative}, amount::{self, Amount, NonNegative},
block::merkle::AUTH_DIGEST_PLACEHOLDER, block::merkle::AUTH_DIGEST_PLACEHOLDER,
transaction::{self, SerializedTransaction, UnminedTx}, transaction::{self, SerializedTransaction, VerifiedUnminedTx},
}; };
/// Transaction data and fields needed to generate blocks using the `getblocktemplate` RPC. /// Transaction data and fields needed to generate blocks using the `getblocktemplate` RPC.
@ -39,13 +39,9 @@ where
/// Non-coinbase transactions must be `NonNegative`. /// Non-coinbase transactions must be `NonNegative`.
/// The Coinbase transaction `fee` is the negative sum of the fees of the transactions in /// The Coinbase transaction `fee` is the negative sum of the fees of the transactions in
/// the block, so their fee must be `NegativeOrZero`. /// the block, so their fee must be `NegativeOrZero`.
//
// TODO: add a fee field to mempool transactions, based on the verifier response.
pub(crate) fee: Amount<FeeConstraint>, pub(crate) fee: Amount<FeeConstraint>,
/// The number of transparent signature operations in this transaction. /// The number of transparent signature operations in this transaction.
//
// TODO: add a sigops field to mempool transactions, based on the verifier response.
pub(crate) sigops: u64, pub(crate) sigops: u64,
/// Is this transaction required in the block? /// Is this transaction required in the block?
@ -55,21 +51,23 @@ where
} }
// Convert from a mempool transaction to a transaction template. // Convert from a mempool transaction to a transaction template.
impl From<&UnminedTx> for TransactionTemplate<NonNegative> { impl From<&VerifiedUnminedTx> for TransactionTemplate<NonNegative> {
fn from(tx: &UnminedTx) -> Self { fn from(tx: &VerifiedUnminedTx) -> Self {
Self { Self {
data: tx.transaction.as_ref().into(), data: tx.transaction.transaction.as_ref().into(),
hash: tx.id.mined_id(), hash: tx.transaction.id.mined_id(),
auth_digest: tx.id.auth_digest().unwrap_or(AUTH_DIGEST_PLACEHOLDER), auth_digest: tx
.transaction
.id
.auth_digest()
.unwrap_or(AUTH_DIGEST_PLACEHOLDER),
// Always empty, not supported by Zebra's mempool. // Always empty, not supported by Zebra's mempool.
depends: Vec::new(), depends: Vec::new(),
// TODO: add a fee field to mempool transactions, based on the verifier response. fee: tx.miner_fee,
fee: Amount::zero(),
// TODO: add a sigops field to mempool transactions, based on the verifier response. sigops: tx.legacy_sigop_count,
sigops: 0,
// Zebra does not require any transactions except the coinbase transaction. // Zebra does not require any transactions except the coinbase transaction.
required: false, required: false,
@ -77,8 +75,8 @@ impl From<&UnminedTx> for TransactionTemplate<NonNegative> {
} }
} }
impl From<UnminedTx> for TransactionTemplate<NonNegative> { impl From<VerifiedUnminedTx> for TransactionTemplate<NonNegative> {
fn from(tx: UnminedTx) -> Self { fn from(tx: VerifiedUnminedTx) -> Self {
Self::from(&tx) Self::from(&tx)
} }
} }

View File

@ -128,7 +128,7 @@ async fn test_rpc_response_data_for_network(network: Network) {
// - as we have the mempool mocked we need to expect a request and wait for a response, // - as we have the mempool mocked we need to expect a request and wait for a response,
// which will be an empty mempool in this case. // which will be an empty mempool in this case.
let mempool_req = mempool let mempool_req = mempool
.expect_request_that(|_request| true) .expect_request_that(|request| matches!(request, mempool::Request::TransactionIds))
.map(|responder| { .map(|responder| {
responder.respond(mempool::Response::TransactionIds( responder.respond(mempool::Response::TransactionIds(
std::collections::HashSet::new(), std::collections::HashSet::new(),
@ -152,9 +152,11 @@ async fn test_rpc_response_data_for_network(network: Network) {
// `getrawtransaction` // `getrawtransaction`
// //
// - similar to `getrawmempool` described above, a mempool request will be made to get the requested // - similar to `getrawmempool` described above, a mempool request will be made to get the requested
// transaction from the mempoo, response will be empty as we have this transaction in state // transaction from the mempool, response will be empty as we have this transaction in state
let mempool_req = mempool let mempool_req = mempool
.expect_request_that(|_request| true) .expect_request_that(|request| {
matches!(request, mempool::Request::TransactionsByMinedId(_))
})
.map(|responder| { .map(|responder| {
responder.respond(mempool::Response::Transactions(vec![])); responder.respond(mempool::Response::Transactions(vec![]));
}); });

View File

@ -61,9 +61,9 @@ pub async fn test_responses<State>(
let get_block_template = tokio::spawn(get_block_template_rpc.get_block_template()); let get_block_template = tokio::spawn(get_block_template_rpc.get_block_template());
mempool mempool
.expect_request(mempool::Request::Transactions) .expect_request(mempool::Request::FullTransactions)
.await .await
.respond(mempool::Response::Transactions(vec![])); .respond(mempool::Response::FullTransactions(vec![]));
let get_block_template = get_block_template let get_block_template = get_block_template
.await .await

View File

@ -758,9 +758,9 @@ async fn rpc_getblocktemplate() {
let get_block_template = tokio::spawn(get_block_template_rpc.get_block_template()); let get_block_template = tokio::spawn(get_block_template_rpc.get_block_template());
mempool mempool
.expect_request(mempool::Request::Transactions) .expect_request(mempool::Request::FullTransactions)
.await .await
.respond(mempool::Response::Transactions(vec![])); .respond(mempool::Response::FullTransactions(vec![]));
let get_block_template = get_block_template let get_block_template = get_block_template
.await .await

View File

@ -166,10 +166,11 @@ async fn mempool_push_transaction() -> Result<(), crate::BoxError> {
.into_mempool_transaction() .into_mempool_transaction()
.expect("unexpected non-mempool request"); .expect("unexpected non-mempool request");
// Set a dummy fee. // Set a dummy fee and sigops.
responder.respond(transaction::Response::from(VerifiedUnminedTx::new( responder.respond(transaction::Response::from(VerifiedUnminedTx::new(
transaction, transaction,
Amount::zero(), Amount::zero(),
0,
))); )));
}); });
@ -269,10 +270,11 @@ async fn mempool_advertise_transaction_ids() -> Result<(), crate::BoxError> {
.into_mempool_transaction() .into_mempool_transaction()
.expect("unexpected non-mempool request"); .expect("unexpected non-mempool request");
// Set a dummy fee. // Set a dummy fee and sigops.
responder.respond(transaction::Response::from(VerifiedUnminedTx::new( responder.respond(transaction::Response::from(VerifiedUnminedTx::new(
transaction, transaction,
Amount::zero(), Amount::zero(),
0,
))); )));
}); });
@ -369,10 +371,11 @@ async fn mempool_transaction_expiration() -> Result<(), crate::BoxError> {
.into_mempool_transaction() .into_mempool_transaction()
.expect("unexpected non-mempool request"); .expect("unexpected non-mempool request");
// Set a dummy fee. // Set a dummy fee and sigops.
responder.respond(transaction::Response::from(VerifiedUnminedTx::new( responder.respond(transaction::Response::from(VerifiedUnminedTx::new(
transaction, transaction,
Amount::zero(), Amount::zero(),
0,
))); )));
}); });
@ -502,10 +505,11 @@ async fn mempool_transaction_expiration() -> Result<(), crate::BoxError> {
.into_mempool_transaction() .into_mempool_transaction()
.expect("unexpected non-mempool request"); .expect("unexpected non-mempool request");
// Set a dummy fee. // Set a dummy fee and sigops.
responder.respond(transaction::Response::from(VerifiedUnminedTx::new( responder.respond(transaction::Response::from(VerifiedUnminedTx::new(
transaction, transaction,
Amount::zero(), Amount::zero(),
0,
))); )));
}); });

View File

@ -446,15 +446,16 @@ impl Service<Request> for Mempool {
async move { Ok(Response::Transactions(res)) }.boxed() async move { Ok(Response::Transactions(res)) }.boxed()
} }
#[cfg(feature = "getblocktemplate-rpcs")] #[cfg(feature = "getblocktemplate-rpcs")]
Request::Transactions => { Request::FullTransactions => {
trace!(?req, "got mempool request"); trace!(?req, "got mempool request");
let res: Vec<_> = storage.transactions().cloned().collect(); let res: Vec<_> = storage.full_transactions().cloned().collect();
trace!(?req, res_count = ?res.len(), "answered mempool request"); trace!(?req, res_count = ?res.len(), "answered mempool request");
async move { Ok(Response::Transactions(res)) }.boxed() async move { Ok(Response::FullTransactions(res)) }.boxed()
} }
Request::RejectedTransactionIds(ref ids) => { Request::RejectedTransactionIds(ref ids) => {
@ -501,19 +502,19 @@ impl Service<Request> for Mempool {
// by the peer connection handler. Therefore, return successful // by the peer connection handler. Therefore, return successful
// empty responses. // empty responses.
let resp = match req { let resp = match req {
// Empty Queries // Return empty responses for queries.
Request::TransactionIds => Response::TransactionIds(Default::default()), Request::TransactionIds => Response::TransactionIds(Default::default()),
Request::TransactionsById(_) => Response::Transactions(Default::default()), Request::TransactionsById(_) => Response::Transactions(Default::default()),
Request::TransactionsByMinedId(_) => Response::Transactions(Default::default()), Request::TransactionsByMinedId(_) => Response::Transactions(Default::default()),
#[cfg(feature = "getblocktemplate-rpcs")] #[cfg(feature = "getblocktemplate-rpcs")]
Request::Transactions => Response::Transactions(Default::default()), Request::FullTransactions => Response::FullTransactions(Default::default()),
Request::RejectedTransactionIds(_) => { Request::RejectedTransactionIds(_) => {
Response::RejectedTransactionIds(Default::default()) Response::RejectedTransactionIds(Default::default())
} }
// Don't queue mempool candidates // Don't queue mempool candidates, because there is no queue.
Request::Queue(gossiped_txs) => Response::Queued( Request::Queue(gossiped_txs) => Response::Queued(
// Special case; we can signal the error inside the response, // Special case; we can signal the error inside the response,
// because the inbound service ignores inner errors. // because the inbound service ignores inner errors.

View File

@ -410,11 +410,23 @@ impl Storage {
self.verified.transactions().map(|tx| tx.id) self.verified.transactions().map(|tx| tx.id)
} }
/// Returns the set of [`UnminedTx`]s in the mempool. /// Returns an iterator over the [`UnminedTx`]s in the mempool.
//
// TODO: make the transactions() method return VerifiedUnminedTx,
// and remove the full_transactions() method
pub fn transactions(&self) -> impl Iterator<Item = &UnminedTx> { pub fn transactions(&self) -> impl Iterator<Item = &UnminedTx> {
self.verified.transactions() self.verified.transactions()
} }
/// Returns an iterator over the [`VerifiedUnminedTx`] in the set.
///
/// Each [`VerifiedUnminedTx`] contains an [`UnminedTx`],
/// and adds extra fields from the transaction verifier result.
#[allow(dead_code)]
pub fn full_transactions(&self) -> impl Iterator<Item = &VerifiedUnminedTx> + '_ {
self.verified.full_transactions()
}
/// Returns the number of transactions in the mempool. /// Returns the number of transactions in the mempool.
#[allow(dead_code)] #[allow(dead_code)]
pub fn transaction_count(&self) -> usize { pub fn transaction_count(&self) -> usize {

View File

@ -1,3 +1,5 @@
//! Tests and test utility functions for mempool storage.
use std::ops::RangeBounds; use std::ops::RangeBounds;
use zebra_chain::{ use zebra_chain::{
@ -30,9 +32,10 @@ pub fn unmined_transactions_in_blocks(
}); });
// Extract the transactions from the blocks and wrap each one as an unmined transaction. // Extract the transactions from the blocks and wrap each one as an unmined transaction.
// Use a fake zero miner fee, because we don't have the UTXOs to calculate the correct fee. // Use a fake zero miner fee and sigops, because we don't have the UTXOs to calculate
// the correct fee.
selected_blocks selected_blocks
.flat_map(|block| block.transactions) .flat_map(|block| block.transactions)
.map(UnminedTx::from) .map(UnminedTx::from)
.map(|transaction| VerifiedUnminedTx::new(transaction, Amount::zero())) .map(|transaction| VerifiedUnminedTx::new(transaction, Amount::zero(), 0))
} }

View File

@ -1,4 +1,6 @@
use std::{collections::HashSet, convert::TryFrom, env, fmt::Debug, thread, time::Duration}; //! Randomised property tests for mempool storage.
use std::{collections::HashSet, env, fmt::Debug, thread, time::Duration};
use proptest::{collection::vec, prelude::*}; use proptest::{collection::vec, prelude::*};
use proptest_derive::Arbitrary; use proptest_derive::Arbitrary;
@ -473,8 +475,8 @@ impl SpendConflictTestInput {
}; };
( (
VerifiedUnminedTx::new(first.0.into(), Amount::zero()), VerifiedUnminedTx::new(first.0.into(), Amount::zero(), 0),
VerifiedUnminedTx::new(second.0.into(), Amount::zero()), VerifiedUnminedTx::new(second.0.into(), Amount::zero(), 0),
) )
} }
@ -491,8 +493,8 @@ impl SpendConflictTestInput {
Self::remove_orchard_conflicts(&mut first, &mut second); Self::remove_orchard_conflicts(&mut first, &mut second);
( (
VerifiedUnminedTx::new(first.0.into(), Amount::zero()), VerifiedUnminedTx::new(first.0.into(), Amount::zero(), 0),
VerifiedUnminedTx::new(second.0.into(), Amount::zero()), VerifiedUnminedTx::new(second.0.into(), Amount::zero(), 0),
) )
} }

View File

@ -1,3 +1,5 @@
//! Fixed test vectors for mempool storage.
use std::iter; use std::iter;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
@ -269,8 +271,8 @@ fn mempool_expired_basic_for_network(network: Network) -> Result<()> {
let tx_id = tx.unmined_id(); let tx_id = tx.unmined_id();
// Insert the transaction into the mempool, with a fake zero miner fee // Insert the transaction into the mempool, with a fake zero miner fee and sigops
storage.insert(VerifiedUnminedTx::new(tx.into(), Amount::zero()))?; storage.insert(VerifiedUnminedTx::new(tx.into(), Amount::zero(), 0))?;
assert_eq!(storage.transaction_count(), 1); assert_eq!(storage.transaction_count(), 1);

View File

@ -54,11 +54,22 @@ impl Drop for VerifiedSet {
} }
impl VerifiedSet { impl VerifiedSet {
/// Returns an iterator over the transactions in the set. /// Returns an iterator over the [`UnminedTx`] in the set.
//
// TODO: make the transactions() method return VerifiedUnminedTx,
// and remove the full_transactions() method
pub fn transactions(&self) -> impl Iterator<Item = &UnminedTx> + '_ { pub fn transactions(&self) -> impl Iterator<Item = &UnminedTx> + '_ {
self.transactions.iter().map(|tx| &tx.transaction) self.transactions.iter().map(|tx| &tx.transaction)
} }
/// Returns an iterator over the [`VerifiedUnminedTx`] in the set.
///
/// Each [`VerifiedUnminedTx`] contains an [`UnminedTx`],
/// and adds extra fields from the transaction verifier result.
pub fn full_transactions(&self) -> impl Iterator<Item = &VerifiedUnminedTx> + '_ {
self.transactions.iter()
}
/// Returns the number of verified transactions in the set. /// Returns the number of verified transactions in the set.
pub fn transaction_count(&self) -> usize { pub fn transaction_count(&self) -> usize {
self.transactions.len() self.transactions.len()