use std::pin::Pin;
use proptest::prelude::*;
use tower::ServiceExt;
use super::{
downloads::TransactionDownloadVerifyError, error::MempoolError, queue_source_log_label,
storage::Storage, transaction_error_peer_log_label, ActiveState, InboundTxDownloads, Mempool,
Request,
};
use crate::{
components::sync::{RecentSyncLengths, SyncStatus},
BoxError,
};
use zakura_chain::{
amount::{Amount, NonNegative},
parameters::NetworkKind,
transaction::{Transaction, UnminedTx, VerifiedUnminedTx},
transparent::{self, Address},
};
use zakura_node_services::mempool::QueueSource;
mod prop;
mod vector;
#[test]
fn legacy_queue_source_log_labels_require_explicit_opt_in() {
let source = QueueSource::LegacySocket("192.0.2.1:8233".parse().expect("valid test socket"));
assert_eq!(queue_source_log_label(&source, false), "legacy:redacted");
assert_eq!(
queue_source_log_label(&source, true),
"legacy:192.0.2.1:8233"
);
let source = QueueSource::Zakura(vec![7, 8, 9]);
assert_eq!(queue_source_log_label(&source, false), "zakura:[7, 8, 9]");
assert_eq!(queue_source_log_label(&source, true), "zakura:[7, 8, 9]");
}
#[test]
fn transaction_error_peer_log_labels_require_explicit_opt_in() {
let error = TransactionDownloadVerifyError::Invalid {
error: zakura_consensus::error::TransactionError::WrongVersion,
advertiser_addr: Some("192.0.2.1:8233".parse().expect("valid test socket")),
};
assert_eq!(
transaction_error_peer_log_label(&error, false).as_deref(),
Some("legacy:redacted")
);
assert_eq!(
transaction_error_peer_log_label(&error, true).as_deref(),
Some("legacy:192.0.2.1:8233")
);
}
impl Mempool {
pub fn storage(&mut self) -> &mut Storage {
match &mut self.active_state {
ActiveState::Disabled => panic!("mempool must be enabled"),
ActiveState::Enabled { storage, .. } => storage,
}
}
pub fn tx_downloads(&self) -> &Pin<Box<InboundTxDownloads>> {
match &self.active_state {
ActiveState::Disabled => panic!("mempool must be enabled"),
ActiveState::Enabled { tx_downloads, .. } => tx_downloads,
}
}
pub async fn enable(&mut self, recent_syncs: &mut RecentSyncLengths) {
SyncStatus::sync_close_to_tip(recent_syncs);
self.dummy_call().await;
}
async fn sync_far_from_tip(&mut self, recent_syncs: &mut RecentSyncLengths) {
SyncStatus::sync_far_from_tip(recent_syncs);
self.dummy_call().await;
}
pub async fn dummy_call(&mut self) {
self.oneshot(Request::CheckForVerifiedTransactions)
.await
.expect("unexpected failure when checking for verified transactions");
}
}
pub trait UnboxMempoolError {
fn unbox_mempool_error(self) -> MempoolError;
}
impl UnboxMempoolError for MempoolError {
fn unbox_mempool_error(self) -> MempoolError {
self
}
}
impl UnboxMempoolError for BoxError {
fn unbox_mempool_error(self) -> MempoolError {
self.downcast::<MempoolError>()
.expect("error is not an expected `MempoolError`")
.as_ref()
.clone()
}
}
impl<T, E> UnboxMempoolError for Result<T, E>
where
E: UnboxMempoolError,
{
fn unbox_mempool_error(self) -> MempoolError {
match self {
Ok(_) => panic!("expected a mempool error, but got a success instead"),
Err(error) => error.unbox_mempool_error(),
}
}
}
pub fn standard_verified_unmined_tx_strategy() -> BoxedStrategy<VerifiedUnminedTx> {
any::<Transaction>()
.prop_map(|mut transaction| {
standardize_transaction(&mut transaction);
let unmined_tx = UnminedTx::from(transaction);
let miner_fee = unmined_tx.conventional_fee;
VerifiedUnminedTx::new(unmined_tx, miner_fee, 0, 0, std::sync::Arc::new(vec![]))
.expect("standardized transaction should pass ZIP-317 checks")
})
.boxed()
}
pub fn standardize_transaction(transaction: &mut Transaction) {
let lock_script = standard_lock_script();
let output_value = Amount::<NonNegative>::try_from(10_000).expect("valid amount");
for input in transaction.inputs_mut() {
if let transparent::Input::PrevOut { unlock_script, .. } = input {
*unlock_script = transparent::Script::new(&[]);
}
}
for output in transaction.outputs_mut() {
output.lock_script = lock_script.clone();
output.value = output_value;
}
}
fn standard_lock_script() -> transparent::Script {
Address::from_pub_key_hash(NetworkKind::Mainnet, [0u8; 20]).script()
}