feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,341 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tests for fork-aware transaction pool.
|
||||
|
||||
use pezsc_transaction_pool::{ChainApi, PoolLimit};
|
||||
use pezsc_transaction_pool_api::ChainEvent;
|
||||
use pezsp_runtime::transaction_validity::TransactionSource;
|
||||
use std::sync::Arc;
|
||||
use bizinikiwi_test_runtime_client::{
|
||||
runtime::{Block, Hash, Header},
|
||||
Sr25519Keyring::*,
|
||||
};
|
||||
use bizinikiwi_test_runtime_transaction_pool::{uxt, TestApi};
|
||||
pub const LOG_TARGET: &str = "txpool";
|
||||
|
||||
use pezsc_transaction_pool::ForkAwareTxPool;
|
||||
|
||||
pub fn invalid_hash() -> Hash {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn new_best_block_event(
|
||||
pool: &ForkAwareTxPool<TestApi, Block>,
|
||||
from: Option<Hash>,
|
||||
to: Hash,
|
||||
) -> ChainEvent<Block> {
|
||||
ChainEvent::NewBestBlock {
|
||||
hash: to,
|
||||
tree_route: from.map(|from| {
|
||||
// note: real tree route in NewBestBlock event does not contain 'to' block.
|
||||
Arc::from(
|
||||
pool.api()
|
||||
.tree_route(from, pool.api().block_header(to).unwrap().unwrap().parent_hash)
|
||||
.expect("Tree route exists"),
|
||||
)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finalized_block_event(
|
||||
pool: &ForkAwareTxPool<TestApi, Block>,
|
||||
from: Hash,
|
||||
to: Hash,
|
||||
) -> ChainEvent<Block> {
|
||||
let t = pool.api().tree_route(from, to).expect("Tree route exists");
|
||||
|
||||
let e = t.enacted().iter().map(|h| h.hash).collect::<Vec<_>>();
|
||||
ChainEvent::Finalized { hash: to, tree_route: Arc::from(&e[0..e.len() - 1]) }
|
||||
}
|
||||
|
||||
pub struct TestPoolBuilder {
|
||||
api: Option<Arc<TestApi>>,
|
||||
use_default_limits: bool,
|
||||
ready_limits: pezsc_transaction_pool::PoolLimit,
|
||||
future_limits: pezsc_transaction_pool::PoolLimit,
|
||||
mempool_max_transactions_count: usize,
|
||||
finality_timeout_threshold: Option<usize>,
|
||||
}
|
||||
|
||||
impl Default for TestPoolBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
api: None,
|
||||
use_default_limits: true,
|
||||
ready_limits: PoolLimit { count: 8192, total_bytes: 20 * 1024 * 1024 },
|
||||
future_limits: PoolLimit { count: 512, total_bytes: 1 * 1024 * 1024 },
|
||||
mempool_max_transactions_count: usize::MAX,
|
||||
finality_timeout_threshold: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TestPoolBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn with_api(mut self, api: Arc<TestApi>) -> Self {
|
||||
self.api = Some(api);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_mempool_count_limit(mut self, mempool_count_limit: usize) -> Self {
|
||||
self.mempool_max_transactions_count = mempool_count_limit;
|
||||
self.use_default_limits = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_ready_count(mut self, ready_count: usize) -> Self {
|
||||
self.ready_limits.count = ready_count;
|
||||
self.use_default_limits = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_ready_bytes_size(mut self, ready_bytes_size: usize) -> Self {
|
||||
self.ready_limits.total_bytes = ready_bytes_size;
|
||||
self.use_default_limits = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_future_count(mut self, future_count: usize) -> Self {
|
||||
self.future_limits.count = future_count;
|
||||
self.use_default_limits = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_future_bytes_size(mut self, future_bytes_size: usize) -> Self {
|
||||
self.future_limits.total_bytes = future_bytes_size;
|
||||
self.use_default_limits = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_finality_timeout_threshold(mut self, threshold: usize) -> Self {
|
||||
self.finality_timeout_threshold = Some(threshold);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(
|
||||
self,
|
||||
) -> (ForkAwareTxPool<TestApi, Block>, Arc<TestApi>, futures::executor::ThreadPool) {
|
||||
let api = self
|
||||
.api
|
||||
.unwrap_or(Arc::from(TestApi::with_alice_nonce(200).enable_stale_check()));
|
||||
|
||||
let genesis_hash = api
|
||||
.chain()
|
||||
.read()
|
||||
.block_by_number
|
||||
.get(&0)
|
||||
.map(|blocks| blocks[0].0.header.hash())
|
||||
.expect("there is block 0. qed");
|
||||
|
||||
let (pool, [txpool_task, blocking_task]) = if self.use_default_limits {
|
||||
ForkAwareTxPool::new_test(
|
||||
api.clone(),
|
||||
genesis_hash,
|
||||
genesis_hash,
|
||||
self.finality_timeout_threshold,
|
||||
)
|
||||
} else {
|
||||
ForkAwareTxPool::new_test_with_limits(
|
||||
api.clone(),
|
||||
genesis_hash,
|
||||
genesis_hash,
|
||||
self.ready_limits,
|
||||
self.future_limits,
|
||||
self.mempool_max_transactions_count,
|
||||
self.finality_timeout_threshold,
|
||||
)
|
||||
};
|
||||
|
||||
let thread_pool = futures::executor::ThreadPool::new().unwrap();
|
||||
thread_pool.spawn_ok(txpool_task);
|
||||
thread_pool.spawn_ok(blocking_task);
|
||||
|
||||
(pool, api, thread_pool)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pool_with_api(
|
||||
test_api: Arc<TestApi>,
|
||||
) -> (ForkAwareTxPool<TestApi, Block>, futures::executor::ThreadPool) {
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, _, threadpool) = builder.with_api(test_api).build();
|
||||
(pool, threadpool)
|
||||
}
|
||||
|
||||
pub fn pool() -> (ForkAwareTxPool<TestApi, Block>, Arc<TestApi>, futures::executor::ThreadPool) {
|
||||
let builder = TestPoolBuilder::new();
|
||||
builder.build()
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_pool_status {
|
||||
($hash:expr, $pool:expr, $ready:expr, $future:expr) => {
|
||||
{
|
||||
tracing::debug!(target: LOG_TARGET, stats = ?$pool.status_all(), "Pool stats");
|
||||
let status = &$pool.status_all()[&$hash];
|
||||
assert_eq!(status.ready, $ready, "ready");
|
||||
assert_eq!(status.future, $future, "future");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_ready_at_light_iterator {
|
||||
($hash:expr, $pool:expr, [$( $xt:expr ),*]) => {{
|
||||
let ready_iterator = $pool.ready_at_light($hash).now_or_never().unwrap();
|
||||
let expected = vec![ $($pool.api().hash_and_length(&$xt).0),*];
|
||||
let output: Vec<_> = ready_iterator.collect();
|
||||
tracing::debug!(target: LOG_TARGET, ?expected, "expected");
|
||||
tracing::debug!(target: LOG_TARGET, ?output, "output");
|
||||
let output = output.into_iter().map(|t|t.hash).collect::<Vec<_>>();
|
||||
assert_eq!(expected.len(), output.len());
|
||||
assert_eq!(output,expected);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_ready_iterator {
|
||||
($hash:expr, $pool:expr, [$( $xt:expr ),*]) => {{
|
||||
let ready_iterator = $pool.ready_at($hash).now_or_never().unwrap();
|
||||
let expected = vec![ $($pool.api().hash_and_length(&$xt).0),*];
|
||||
let output: Vec<_> = ready_iterator.collect();
|
||||
tracing::debug!(target: LOG_TARGET, ?expected, "expected");
|
||||
tracing::debug!(target: LOG_TARGET, ?output, "output");
|
||||
let output = output.into_iter().map(|t|t.hash).collect::<Vec<_>>();
|
||||
assert_eq!(expected.len(), output.len());
|
||||
assert_eq!(output,expected);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_future_iterator {
|
||||
($hash:expr, $pool:expr, [$( $xt:expr ),*]) => {{
|
||||
let futures = $pool.futures_at($hash).unwrap();
|
||||
let expected = vec![ $($pool.api().hash_and_length(&$xt).0),*];
|
||||
tracing::debug!(target: LOG_TARGET, ?expected, "expected");
|
||||
tracing::debug!(target: LOG_TARGET, ?futures, "output");
|
||||
assert_eq!(expected.len(), futures.len());
|
||||
let hsf = futures.iter().map(|a| a.hash).collect::<std::collections::HashSet<_>>();
|
||||
let hse = expected.into_iter().collect::<std::collections::HashSet<_>>();
|
||||
assert_eq!(hse,hsf);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_watcher_stream {
|
||||
($stream:ident, [$( $event:expr ),*]) => {{
|
||||
let expected = vec![ $($event),*];
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
?expected,
|
||||
expected_len = expected.len(),
|
||||
"block now"
|
||||
);
|
||||
let output = futures::executor::block_on_stream($stream).take(expected.len()).collect::<Vec<_>>();
|
||||
tracing::debug!(target: LOG_TARGET, ?output, "Output");
|
||||
assert_eq!(expected.len(), output.len());
|
||||
assert_eq!(output, expected);
|
||||
}};
|
||||
}
|
||||
|
||||
pub const SOURCE: TransactionSource = TransactionSource::External;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test_chain_with_forks {
|
||||
use super::*;
|
||||
use tracing::debug;
|
||||
|
||||
pub fn chain(
|
||||
include_xts: Option<&dyn Fn(usize, usize) -> bool>,
|
||||
) -> (Arc<TestApi>, Vec<Vec<Header>>) {
|
||||
// Fork layout:
|
||||
//
|
||||
// (fork 0)
|
||||
// F01 - F02 - F03 - F04 - F05 | Alice nonce increasing, alice's txs
|
||||
// /
|
||||
// F00
|
||||
// \ (fork 1)
|
||||
// F11 - F12 - F13 - F14 - F15 | Bob nonce increasing, Bob's txs
|
||||
//
|
||||
//
|
||||
// e.g. F03 contains uxt(Alice, 202), nonces: Alice = 203, Bob = 200
|
||||
// F12 contains uxt(Bob, 201), nonces: Alice = 200, Bob = 202
|
||||
|
||||
let api = Arc::from(TestApi::empty().enable_stale_check());
|
||||
|
||||
let genesis = api.genesis_hash();
|
||||
|
||||
let mut forks = vec![Vec::with_capacity(6), Vec::with_capacity(6)];
|
||||
let accounts = vec![Alice, Bob];
|
||||
accounts.iter().for_each(|a| api.set_nonce(genesis, (*a).into(), 200));
|
||||
|
||||
for fork in 0..2 {
|
||||
let account = accounts[fork];
|
||||
forks[fork].push(api.block_header(genesis).unwrap().unwrap());
|
||||
let mut parent = genesis;
|
||||
for block in 1..6 {
|
||||
let xts = if include_xts.map_or(true, |v| v(fork, block)) {
|
||||
debug!(fork = %fork, block = %block, "-> add");
|
||||
vec![uxt(account, (200 + block - 1) as u64)]
|
||||
} else {
|
||||
debug!(fork = %fork, block = %block, "-> skip");
|
||||
vec![]
|
||||
};
|
||||
let header = api.push_block_with_parent(parent, xts, true);
|
||||
parent = header.hash();
|
||||
api.set_nonce(header.hash(), account.into(), (200 + block) as u64);
|
||||
forks[fork].push(header);
|
||||
}
|
||||
}
|
||||
|
||||
(api, forks)
|
||||
}
|
||||
|
||||
pub fn print_block(api: Arc<TestApi>, hash: Hash) {
|
||||
let accounts = vec![Alice.into(), Bob.into()];
|
||||
let header = api.block_header(hash).unwrap().unwrap();
|
||||
|
||||
let nonces = accounts
|
||||
.iter()
|
||||
.map(|a| api.chain().read().nonces.get(&hash).unwrap().get(a).map(Clone::clone))
|
||||
.collect::<Vec<_>>();
|
||||
debug!(
|
||||
number = ?header.number,
|
||||
hash = ?header.hash(),
|
||||
parent = ?header.parent_hash,
|
||||
?nonces
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chain_works() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
let (api, f) = chain(None);
|
||||
debug!(forks = ?f, "forks");
|
||||
f[0].iter().for_each(|h| print_block(api.clone(), h.hash()));
|
||||
f[1].iter().for_each(|h| print_block(api.clone(), h.hash()));
|
||||
let tr = api.tree_route(f[0][5].hash(), f[1][5].hash()).unwrap();
|
||||
debug!(?tr);
|
||||
debug!(enacted = ?tr.enacted());
|
||||
debug!(retracted = ?tr.retracted());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tests for finality timeout handling for fork-aware transaction pool.
|
||||
|
||||
pub mod fatp_common;
|
||||
|
||||
use std::cmp::min;
|
||||
|
||||
use fatp_common::{
|
||||
finalized_block_event, invalid_hash, new_best_block_event, TestPoolBuilder, LOG_TARGET, SOURCE,
|
||||
};
|
||||
use futures::{executor::block_on, FutureExt};
|
||||
use pezsc_transaction_pool::ChainApi;
|
||||
use pezsc_transaction_pool_api::{MaintainedTransactionPool, TransactionPool, TransactionStatus};
|
||||
use bizinikiwi_test_runtime_client::Sr25519Keyring::*;
|
||||
use bizinikiwi_test_runtime_transaction_pool::uxt;
|
||||
|
||||
#[test]
|
||||
fn fatp_finality_timeout_works() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
const FINALITY_TIMEOUT_THRESHOLD: usize = 10;
|
||||
|
||||
let (pool, api, _) = TestPoolBuilder::new()
|
||||
.with_finality_timeout_threshold(FINALITY_TIMEOUT_THRESHOLD)
|
||||
.build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
let xt3 = uxt(Dave, 500);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 4, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1, xt2, xt3]);
|
||||
|
||||
let header02a = api.push_block_with_parent(
|
||||
header01.hash(),
|
||||
vec![xt0.clone(), xt1.clone(), xt2.clone(), xt3.clone()],
|
||||
true,
|
||||
);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02a.hash())));
|
||||
assert_pool_status!(header02a.hash(), &pool, 0, 0);
|
||||
|
||||
let header02b = api.push_block_with_parent(header01.hash(), vec![xt0, xt1, xt2, xt3], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02a.hash()), header02b.hash())));
|
||||
assert_pool_status!(header02b.hash(), &pool, 0, 0);
|
||||
|
||||
let mut prev_header = header02b.clone();
|
||||
for n in 3..66 {
|
||||
let header = api.push_block_with_parent(prev_header.hash(), vec![], true);
|
||||
let event = new_best_block_event(&pool, Some(prev_header.hash()), header.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
prev_header = header;
|
||||
if n < 3 + FINALITY_TIMEOUT_THRESHOLD {
|
||||
assert_eq!(pool.active_views_count(), 2);
|
||||
} else {
|
||||
assert_eq!(pool.active_views_count(), 1);
|
||||
assert_eq!(pool.inactive_views_count(), FINALITY_TIMEOUT_THRESHOLD);
|
||||
}
|
||||
}
|
||||
|
||||
for (i, watcher) in
|
||||
vec![xt0_watcher, xt1_watcher, xt2_watcher, xt3_watcher].into_iter().enumerate()
|
||||
{
|
||||
assert_watcher_stream!(
|
||||
watcher,
|
||||
[
|
||||
TransactionStatus::Ready,
|
||||
TransactionStatus::InBlock((header02a.hash(), i)),
|
||||
TransactionStatus::InBlock((header02b.hash(), i)),
|
||||
TransactionStatus::FinalityTimeout(min(header02a.hash(), header02b.hash()))
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_finalized_still_works_after_finality_stall() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
const FINALITY_TIMEOUT_THRESHOLD: usize = 10;
|
||||
|
||||
let (pool, api, _) = TestPoolBuilder::new()
|
||||
.with_finality_timeout_threshold(FINALITY_TIMEOUT_THRESHOLD)
|
||||
.build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
block_on(pool.maintain(finalized_block_event(&pool, api.genesis_hash(), header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
let xt3 = uxt(Dave, 500);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 4, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1, xt2, xt3]);
|
||||
|
||||
let header02a = api.push_block_with_parent(
|
||||
header01.hash(),
|
||||
vec![xt0.clone(), xt1.clone(), xt2.clone()],
|
||||
true,
|
||||
);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02a.hash())));
|
||||
assert_pool_status!(header02a.hash(), &pool, 1, 0);
|
||||
|
||||
let header02b = api.push_block_with_parent(header01.hash(), vec![xt0, xt1, xt2], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02a.hash()), header02b.hash())));
|
||||
assert_pool_status!(header02b.hash(), &pool, 1, 0);
|
||||
|
||||
let header03b = api.push_block_with_parent(header02b.hash(), vec![xt3], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02b.hash()), header03b.hash())));
|
||||
assert_pool_status!(header03b.hash(), &pool, 0, 0);
|
||||
|
||||
let mut prev_header = header03b.clone();
|
||||
for block_n in 4..=3 + FINALITY_TIMEOUT_THRESHOLD {
|
||||
let header = api.push_block_with_parent(prev_header.hash(), vec![], true);
|
||||
let event = new_best_block_event(&pool, Some(prev_header.hash()), header.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
prev_header = header;
|
||||
if block_n == 3 + FINALITY_TIMEOUT_THRESHOLD {
|
||||
//finality timeout triggered
|
||||
assert_eq!(pool.active_views_count(), 1);
|
||||
assert_eq!(pool.inactive_views_count(), FINALITY_TIMEOUT_THRESHOLD);
|
||||
} else {
|
||||
assert_eq!(pool.active_views_count(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
block_on(pool.maintain(finalized_block_event(&pool, header01.hash(), header03b.hash())));
|
||||
|
||||
for (i, watcher) in vec![xt0_watcher, xt1_watcher, xt2_watcher].into_iter().enumerate() {
|
||||
assert_watcher_stream!(
|
||||
watcher,
|
||||
[
|
||||
TransactionStatus::Ready,
|
||||
TransactionStatus::InBlock((header02a.hash(), i)),
|
||||
TransactionStatus::InBlock((header02b.hash(), i)),
|
||||
TransactionStatus::FinalityTimeout(min(header02a.hash(), header02b.hash()))
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
assert_watcher_stream!(
|
||||
xt3_watcher,
|
||||
[
|
||||
TransactionStatus::Ready,
|
||||
TransactionStatus::InBlock((header03b.hash(), 0)),
|
||||
TransactionStatus::Finalized((header03b.hash(), 0))
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_finality_timeout_works_for_txs_included_before_finalized() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
const FINALITY_TIMEOUT_THRESHOLD: usize = 10;
|
||||
|
||||
let (pool, api, _) = TestPoolBuilder::new()
|
||||
.with_finality_timeout_threshold(FINALITY_TIMEOUT_THRESHOLD)
|
||||
.build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
block_on(pool.maintain(finalized_block_event(&pool, api.genesis_hash(), header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
let xt3 = uxt(Dave, 500);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 4, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1, xt2, xt3]);
|
||||
|
||||
let header02a = api.push_block_with_parent(
|
||||
header01.hash(),
|
||||
vec![xt0.clone(), xt1.clone(), xt2.clone()],
|
||||
true,
|
||||
);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02a.hash())));
|
||||
assert_pool_status!(header02a.hash(), &pool, 1, 0);
|
||||
|
||||
let header02b = api.push_block_with_parent(header01.hash(), vec![xt0, xt1, xt2], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02a.hash()), header02b.hash())));
|
||||
assert_pool_status!(header02b.hash(), &pool, 1, 0);
|
||||
|
||||
let header03b = api.push_block_with_parent(header02b.hash(), vec![xt3], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02b.hash()), header03b.hash())));
|
||||
assert_pool_status!(header03b.hash(), &pool, 0, 0);
|
||||
|
||||
block_on(pool.maintain(finalized_block_event(&pool, header01.hash(), header02b.hash())));
|
||||
|
||||
let mut prev_header = header03b.clone();
|
||||
for block_n in 4..=4 + FINALITY_TIMEOUT_THRESHOLD {
|
||||
let header = api.push_block_with_parent(prev_header.hash(), vec![], true);
|
||||
let event = new_best_block_event(&pool, Some(prev_header.hash()), header.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
prev_header = header;
|
||||
assert_eq!(pool.active_views_count(), 1);
|
||||
if block_n == 4 + FINALITY_TIMEOUT_THRESHOLD {
|
||||
//finality timeout triggered
|
||||
assert_eq!(pool.inactive_views_count(), FINALITY_TIMEOUT_THRESHOLD);
|
||||
}
|
||||
}
|
||||
|
||||
for (i, watcher) in vec![xt0_watcher, xt1_watcher, xt2_watcher].into_iter().enumerate() {
|
||||
assert_watcher_stream!(
|
||||
watcher,
|
||||
[
|
||||
TransactionStatus::Ready,
|
||||
TransactionStatus::InBlock((header02a.hash(), i)),
|
||||
TransactionStatus::InBlock((header02b.hash(), i)),
|
||||
TransactionStatus::Finalized((header02b.hash(), i))
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
assert_watcher_stream!(
|
||||
xt3_watcher,
|
||||
[
|
||||
TransactionStatus::Ready,
|
||||
TransactionStatus::InBlock((header03b.hash(), 0)),
|
||||
TransactionStatus::FinalityTimeout(header03b.hash())
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,673 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tests of invalid transactions handling for fork-aware transaction pool.
|
||||
|
||||
pub mod fatp_common;
|
||||
|
||||
use fatp_common::{
|
||||
finalized_block_event, invalid_hash, new_best_block_event, pool, TestPoolBuilder, LOG_TARGET,
|
||||
SOURCE,
|
||||
};
|
||||
use futures::{executor::block_on, FutureExt};
|
||||
use pezsc_transaction_pool::ChainApi;
|
||||
use pezsc_transaction_pool_api::{
|
||||
error::{Error as TxPoolError, IntoPoolError},
|
||||
MaintainedTransactionPool, TransactionPool, TransactionStatus,
|
||||
};
|
||||
use pezsp_runtime::transaction_validity::{InvalidTransaction, TransactionValidityError};
|
||||
use bizinikiwi_test_runtime_client::Sr25519Keyring::*;
|
||||
use bizinikiwi_test_runtime_transaction_pool::uxt;
|
||||
use tracing::debug;
|
||||
|
||||
#[test]
|
||||
fn fatp_invalid_three_views_stale_gets_rejected() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = pool();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Alice, 200);
|
||||
|
||||
let header02a = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
let header02b = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
let header02c = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
api.set_nonce(header02a.hash(), Alice.into(), 201);
|
||||
api.set_nonce(header02b.hash(), Alice.into(), 201);
|
||||
api.set_nonce(header02c.hash(), Alice.into(), 201);
|
||||
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02a.hash())));
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02a.hash()), header02b.hash())));
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02b.hash()), header02c.hash())));
|
||||
|
||||
let result0 = block_on(pool.submit_one(invalid_hash(), SOURCE, xt0.clone()));
|
||||
let result1 = block_on(pool.submit_one(invalid_hash(), SOURCE, xt1.clone()));
|
||||
|
||||
assert!(matches!(
|
||||
result0.as_ref().unwrap_err().0,
|
||||
TxPoolError::InvalidTransaction(InvalidTransaction::Stale)
|
||||
));
|
||||
assert!(matches!(
|
||||
result1.as_ref().unwrap_err().0,
|
||||
TxPoolError::InvalidTransaction(InvalidTransaction::Stale)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_invalid_three_views_invalid_gets_rejected() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = pool();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Alice, 200);
|
||||
let header02a = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
let header02b = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
let header02c = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02a.hash())));
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02a.hash()), header02b.hash())));
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02b.hash()), header02c.hash())));
|
||||
|
||||
api.add_invalid(&xt0);
|
||||
api.add_invalid(&xt1);
|
||||
|
||||
let result0 = block_on(pool.submit_one(invalid_hash(), SOURCE, xt0.clone()));
|
||||
let result1 = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).map(|_| ());
|
||||
|
||||
assert!(matches!(
|
||||
result0.as_ref().unwrap_err().0,
|
||||
TxPoolError::InvalidTransaction(InvalidTransaction::Custom(_))
|
||||
));
|
||||
assert!(matches!(
|
||||
result1.as_ref().unwrap_err().0,
|
||||
TxPoolError::InvalidTransaction(InvalidTransaction::Custom(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_transactions_purging_invalid_on_finalization_works() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = pool();
|
||||
|
||||
let xt1 = uxt(Alice, 200);
|
||||
let xt2 = uxt(Alice, 201);
|
||||
let xt3 = uxt(Alice, 202);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let watcher1 = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let watcher2 = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let watcher3 = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_eq!(api.validation_requests().len(), 3);
|
||||
assert_eq!(pool.status_all()[&header01.hash()].ready, 3);
|
||||
assert_eq!(block_on(pool.mempool_len()), (0, 3));
|
||||
|
||||
let header02 = api.push_block(2, vec![], true);
|
||||
api.add_invalid(&xt1);
|
||||
api.add_invalid(&xt2);
|
||||
api.add_invalid(&xt3);
|
||||
block_on(pool.maintain(finalized_block_event(&pool, header01.hash(), header02.hash())));
|
||||
|
||||
// wait 10 blocks for revalidation
|
||||
let mut prev_header = header02.clone();
|
||||
for n in 3..=11 {
|
||||
let header = api.push_block(n, vec![], true);
|
||||
let event = finalized_block_event(&pool, prev_header.hash(), header.hash());
|
||||
block_on(pool.maintain(event));
|
||||
prev_header = header;
|
||||
}
|
||||
|
||||
assert_eq!(block_on(pool.mempool_len()), (0, 0));
|
||||
|
||||
assert_watcher_stream!(watcher1, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
assert_watcher_stream!(watcher2, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
assert_watcher_stream!(watcher3, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_transactions_purging_invalid_on_finalization_works2() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = pool();
|
||||
let xt1 = uxt(Alice, 200);
|
||||
let xt2 = uxt(Alice, 201);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
block_on(pool.submit_one(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
|
||||
let watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
assert_eq!(pool.status_all()[&header01.hash()].ready, 2);
|
||||
assert_eq!(api.validation_requests().len(), 2);
|
||||
|
||||
let header02 = api.push_block(2, vec![xt1.clone()], true);
|
||||
api.add_invalid(&xt2);
|
||||
block_on(pool.maintain(finalized_block_event(&pool, api.genesis_hash(), header02.hash())));
|
||||
|
||||
assert_eq!(pool.status_all()[&header02.hash()].ready, 1);
|
||||
|
||||
// wait 10 blocks for revalidation
|
||||
let mut prev_header = header02.clone();
|
||||
for _ in 3..=11 {
|
||||
let header = api.push_block_with_parent(prev_header.hash(), vec![], true);
|
||||
let event = finalized_block_event(&pool, prev_header.hash(), header.hash());
|
||||
block_on(pool.maintain(event));
|
||||
prev_header = header;
|
||||
}
|
||||
|
||||
assert_watcher_stream!(watcher, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
assert_eq!(pool.status_all()[&prev_header.hash()].ready, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_retain_invalid_hashes_from_retracted() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = pool();
|
||||
let xt = uxt(Alice, 200);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
let watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt.clone())).unwrap();
|
||||
|
||||
let header02a = api.push_block_with_parent(header01.hash(), vec![xt.clone()], true);
|
||||
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02a.hash())));
|
||||
assert_eq!(pool.status_all()[&header02a.hash()].ready, 0);
|
||||
|
||||
api.add_invalid(&xt);
|
||||
let header02b = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(finalized_block_event(&pool, api.genesis_hash(), header02b.hash())));
|
||||
|
||||
// wait 10 blocks for revalidation
|
||||
let mut prev_header = header02b.clone();
|
||||
for _ in 3..=11 {
|
||||
let header = api.push_block_with_parent(prev_header.hash(), vec![], true);
|
||||
let event = finalized_block_event(&pool, prev_header.hash(), header.hash());
|
||||
block_on(pool.maintain(event));
|
||||
prev_header = header;
|
||||
}
|
||||
|
||||
assert_watcher_stream!(
|
||||
watcher,
|
||||
[
|
||||
TransactionStatus::Ready,
|
||||
TransactionStatus::InBlock((header02a.hash(), 0)),
|
||||
TransactionStatus::Invalid
|
||||
]
|
||||
);
|
||||
|
||||
//todo: shall revalidation check finalized (fork's tip) view?
|
||||
assert_eq!(pool.status_all()[&prev_header.hash()].ready, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_watcher_invalid_many_revalidation() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = pool();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Alice, 201);
|
||||
let xt2 = uxt(Alice, 202);
|
||||
let xt3 = uxt(Alice, 203);
|
||||
let xt4 = uxt(Alice, 204);
|
||||
|
||||
let submissions = vec![
|
||||
pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone()),
|
||||
pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone()),
|
||||
pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone()),
|
||||
pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone()),
|
||||
pool.submit_and_watch(invalid_hash(), SOURCE, xt4.clone()),
|
||||
];
|
||||
|
||||
let submissions = block_on(futures::future::join_all(submissions));
|
||||
assert_eq!(pool.status_all()[&header01.hash()].ready, 5);
|
||||
|
||||
let mut watchers = submissions.into_iter().map(Result::unwrap).collect::<Vec<_>>();
|
||||
let xt4_watcher = watchers.remove(4);
|
||||
let xt3_watcher = watchers.remove(3);
|
||||
let xt2_watcher = watchers.remove(2);
|
||||
let xt1_watcher = watchers.remove(1);
|
||||
let xt0_watcher = watchers.remove(0);
|
||||
|
||||
api.add_invalid(&xt3);
|
||||
api.add_invalid(&xt4);
|
||||
|
||||
let header02 = api.push_block(2, vec![], true);
|
||||
block_on(pool.maintain(finalized_block_event(&pool, header01.hash(), header02.hash())));
|
||||
|
||||
//todo: shall revalidation check finalized (fork's tip) view?
|
||||
assert_eq!(pool.status_all()[&header02.hash()].ready, 5);
|
||||
|
||||
let header03 = api.push_block(3, vec![xt0.clone(), xt1.clone(), xt2.clone()], true);
|
||||
block_on(pool.maintain(finalized_block_event(&pool, header02.hash(), header03.hash())));
|
||||
|
||||
// wait 10 blocks for revalidation
|
||||
let mut prev_header = header03.clone();
|
||||
for n in 4..=11 {
|
||||
let header = api.push_block(n, vec![], true);
|
||||
let event = finalized_block_event(&pool, prev_header.hash(), header.hash());
|
||||
block_on(pool.maintain(event));
|
||||
prev_header = header;
|
||||
}
|
||||
|
||||
assert_watcher_stream!(
|
||||
xt0_watcher,
|
||||
[
|
||||
TransactionStatus::Ready,
|
||||
TransactionStatus::InBlock((header03.hash(), 0)),
|
||||
TransactionStatus::Finalized((header03.hash(), 0))
|
||||
]
|
||||
);
|
||||
assert_watcher_stream!(
|
||||
xt1_watcher,
|
||||
[
|
||||
TransactionStatus::Ready,
|
||||
TransactionStatus::InBlock((header03.hash(), 1)),
|
||||
TransactionStatus::Finalized((header03.hash(), 1))
|
||||
]
|
||||
);
|
||||
assert_watcher_stream!(
|
||||
xt2_watcher,
|
||||
[
|
||||
TransactionStatus::Ready,
|
||||
TransactionStatus::InBlock((header03.hash(), 2)),
|
||||
TransactionStatus::Finalized((header03.hash(), 2))
|
||||
]
|
||||
);
|
||||
assert_watcher_stream!(xt3_watcher, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
assert_watcher_stream!(xt4_watcher, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_watcher_invalid_fails_on_submission() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = pool();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 150);
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone()));
|
||||
let xt0_watcher = xt0_watcher.map(|_| ());
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 0, 0);
|
||||
// Alice's nonce in state is 200, tx is 150.
|
||||
assert!(matches!(
|
||||
xt0_watcher.unwrap_err().into_pool_error(),
|
||||
Ok(TxPoolError::InvalidTransaction(InvalidTransaction::Stale))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_watcher_invalid_single_revalidation() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = pool();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, Some(api.genesis_hash()), header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
|
||||
api.add_invalid(&xt0);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
let event = finalized_block_event(&pool, header01.hash(), header02.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
// wait 10 blocks for revalidation
|
||||
let mut prev_header = header02;
|
||||
for n in 3..=11 {
|
||||
let header = api.push_block(n, vec![], true);
|
||||
let event = finalized_block_event(&pool, prev_header.hash(), header.hash());
|
||||
block_on(pool.maintain(event));
|
||||
prev_header = header;
|
||||
}
|
||||
|
||||
let xt0_events = futures::executor::block_on_stream(xt0_watcher).collect::<Vec<_>>();
|
||||
debug!(target: LOG_TARGET, ?xt0_events, "xt0_events");
|
||||
assert_eq!(xt0_events, vec![TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_watcher_invalid_single_revalidation2() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = pool();
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
assert_eq!(block_on(pool.mempool_len()), (0, 1));
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = finalized_block_event(&pool, api.genesis_hash(), header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
api.add_invalid(&xt0);
|
||||
|
||||
// note: the tx will be revalidated in view::revalidation, not in mempool revalidation (which
|
||||
// would require waiting 10 blocks).
|
||||
// waiting 10 blocks is excessive, but we may want to keep it.
|
||||
let mut prev_header = header01;
|
||||
for n in 2..=11 {
|
||||
let header = api.push_block(n, vec![], true);
|
||||
let event = finalized_block_event(&pool, prev_header.hash(), header.hash());
|
||||
block_on(pool.maintain(event));
|
||||
prev_header = header;
|
||||
}
|
||||
|
||||
let xt0_events = futures::executor::block_on_stream(xt0_watcher).collect::<Vec<_>>();
|
||||
debug!(target: LOG_TARGET, ?xt0_events, "xt0_events");
|
||||
assert_eq!(xt0_events, vec![TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
assert_eq!(block_on(pool.mempool_len()), (0, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_invalid_report_stale_or_future_works_as_expected() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = TestPoolBuilder::new().build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
let xt3 = uxt(Dave, 500);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 4, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1, xt2, xt3]);
|
||||
|
||||
// future/stale are ignored when at is None
|
||||
let xt0_report = (
|
||||
pool.api().hash_and_length(&xt0).0,
|
||||
Some(TransactionValidityError::Invalid(InvalidTransaction::Future)),
|
||||
);
|
||||
let invalid_txs = [xt0_report].into();
|
||||
let result = block_on(pool.report_invalid(None, invalid_txs));
|
||||
assert!(result.is_empty());
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1, xt2, xt3]);
|
||||
|
||||
// future/stale are applied when at is provided
|
||||
let xt0_report = (
|
||||
pool.api().hash_and_length(&xt0).0,
|
||||
Some(TransactionValidityError::Invalid(InvalidTransaction::Future)),
|
||||
);
|
||||
let xt1_report = (
|
||||
pool.api().hash_and_length(&xt1).0,
|
||||
Some(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
let invalid_txs = [xt0_report, xt1_report].into();
|
||||
let result = block_on(pool.report_invalid(Some(header01.hash()), invalid_txs));
|
||||
// stale/future does not cause tx to be removed from the pool
|
||||
assert!(result.is_empty());
|
||||
// assert_eq!(result[0].hash, pool.api().hash_and_length(&xt0).0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt2, xt3]);
|
||||
|
||||
// None error means force removal
|
||||
// todo
|
||||
|
||||
assert_watcher_stream!(xt0_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt1_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt2_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt3_watcher, [TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_invalid_report_future_dont_remove_from_pool() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = TestPoolBuilder::new().build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
api.set_nonce(api.genesis_hash(), Eve.into(), 600);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
let xt3 = uxt(Dave, 500);
|
||||
let xt4 = uxt(Eve, 600);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
let xt4_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt4.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 5, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1, xt2, xt3, xt4]);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02.hash())));
|
||||
|
||||
assert_pool_status!(header02.hash(), &pool, 5, 0);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt0, xt1, xt2, xt3, xt4]);
|
||||
|
||||
let xt0_report = (
|
||||
pool.api().hash_and_length(&xt0).0,
|
||||
Some(TransactionValidityError::Invalid(InvalidTransaction::Stale)),
|
||||
);
|
||||
let xt1_report = (
|
||||
pool.api().hash_and_length(&xt1).0,
|
||||
Some(TransactionValidityError::Invalid(InvalidTransaction::Future)),
|
||||
);
|
||||
let xt4_report = (
|
||||
pool.api().hash_and_length(&xt4).0,
|
||||
Some(TransactionValidityError::Invalid(InvalidTransaction::BadProof)),
|
||||
);
|
||||
let invalid_txs = [xt0_report, xt1_report, xt4_report].into();
|
||||
let result = block_on(pool.report_invalid(Some(header01.hash()), invalid_txs));
|
||||
|
||||
assert_watcher_stream!(xt4_watcher, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
|
||||
// future does not cause tx to be removed from the pool
|
||||
assert!(result.len() == 1);
|
||||
assert!(result[0].hash == pool.api().hash_and_length(&xt4).0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt2, xt3]);
|
||||
|
||||
assert_pool_status!(header02.hash(), &pool, 4, 0);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt0, xt1, xt2, xt3]);
|
||||
|
||||
assert_watcher_stream!(xt0_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt1_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt2_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt3_watcher, [TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_invalid_tx_is_removed_from_the_pool() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = TestPoolBuilder::new().build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
let xt3 = uxt(Dave, 500);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 4, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1, xt2, xt3]);
|
||||
|
||||
let xt0_report = (
|
||||
pool.api().hash_and_length(&xt0).0,
|
||||
Some(TransactionValidityError::Invalid(InvalidTransaction::BadProof)),
|
||||
);
|
||||
let xt1_report = (pool.api().hash_and_length(&xt1).0, None);
|
||||
let invalid_txs = [xt0_report, xt1_report].into();
|
||||
let result = block_on(pool.report_invalid(Some(header01.hash()), invalid_txs));
|
||||
assert!(result.iter().any(|tx| tx.hash == pool.api().hash_and_length(&xt0).0));
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt2, xt3]);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02.hash())));
|
||||
assert_pool_status!(header02.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt2, xt3]);
|
||||
|
||||
assert_watcher_stream!(xt0_watcher, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
assert_watcher_stream!(xt1_watcher, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
assert_watcher_stream!(xt2_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt3_watcher, [TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_invalid_tx_is_removed_from_the_pool_future_subtree_stays() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = TestPoolBuilder::new().build();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Alice, 201);
|
||||
let xt2 = uxt(Alice, 202);
|
||||
let xt3 = uxt(Alice, 203);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 4, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1, xt2, xt3]);
|
||||
|
||||
let xt0_report = (
|
||||
pool.api().hash_and_length(&xt0).0,
|
||||
Some(TransactionValidityError::Invalid(InvalidTransaction::BadProof)),
|
||||
);
|
||||
let invalid_txs = [xt0_report].into();
|
||||
let result = block_on(pool.report_invalid(Some(header01.hash()), invalid_txs));
|
||||
assert_eq!(result[0].hash, pool.api().hash_and_length(&xt0).0);
|
||||
assert_pool_status!(header01.hash(), &pool, 0, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, []);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02.hash())));
|
||||
assert_pool_status!(header02.hash(), &pool, 0, 3);
|
||||
assert_future_iterator!(header02.hash(), pool, [xt1, xt2, xt3]);
|
||||
|
||||
assert_watcher_stream!(xt0_watcher, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
assert_watcher_stream!(xt1_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt2_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt3_watcher, [TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_invalid_tx_is_removed_from_the_pool2() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let (pool, api, _) = TestPoolBuilder::new().build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
let xt3 = uxt(Dave, 500);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 4, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1, xt2, xt3]);
|
||||
|
||||
let header02a = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02a.hash())));
|
||||
assert_pool_status!(header02a.hash(), &pool, 4, 0);
|
||||
assert_ready_iterator!(header02a.hash(), pool, [xt0, xt1, xt2, xt3]);
|
||||
|
||||
let header02b = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02a.hash()), header02b.hash())));
|
||||
|
||||
assert_pool_status!(header02b.hash(), &pool, 4, 0);
|
||||
assert_ready_iterator!(header02b.hash(), pool, [xt0, xt1, xt2, xt3]);
|
||||
|
||||
let xt0_report = (
|
||||
pool.api().hash_and_length(&xt0).0,
|
||||
Some(TransactionValidityError::Invalid(InvalidTransaction::BadProof)),
|
||||
);
|
||||
let xt1_report = (pool.api().hash_and_length(&xt1).0, None);
|
||||
let invalid_txs = [xt0_report, xt1_report].into();
|
||||
let result = block_on(pool.report_invalid(Some(header01.hash()), invalid_txs));
|
||||
assert!(result.iter().any(|tx| tx.hash == pool.api().hash_and_length(&xt0).0));
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt2, xt3]);
|
||||
assert_pool_status!(header02a.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header02a.hash(), pool, [xt2, xt3]);
|
||||
assert_pool_status!(header02b.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header02b.hash(), pool, [xt2, xt3]);
|
||||
|
||||
let header03 = api.push_block_with_parent(header02b.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02b.hash()), header03.hash())));
|
||||
assert_pool_status!(header03.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header03.hash(), pool, [xt2, xt3]);
|
||||
|
||||
assert_watcher_stream!(xt0_watcher, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
assert_watcher_stream!(xt1_watcher, [TransactionStatus::Ready, TransactionStatus::Invalid]);
|
||||
assert_watcher_stream!(xt2_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt3_watcher, [TransactionStatus::Ready]);
|
||||
}
|
||||
@@ -0,0 +1,832 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tests of limits for fork-aware transaction pool.
|
||||
|
||||
pub mod fatp_common;
|
||||
|
||||
use fatp_common::{
|
||||
finalized_block_event, invalid_hash, new_best_block_event, TestPoolBuilder, LOG_TARGET, SOURCE,
|
||||
};
|
||||
use futures::{executor::block_on, FutureExt};
|
||||
use pezsc_transaction_pool::ChainApi;
|
||||
use pezsc_transaction_pool_api::{
|
||||
error::Error as TxPoolError, MaintainedTransactionPool, TransactionPool, TransactionStatus,
|
||||
};
|
||||
use std::thread::sleep;
|
||||
use bizinikiwi_test_runtime_client::Sr25519Keyring::*;
|
||||
use bizinikiwi_test_runtime_transaction_pool::uxt;
|
||||
use tracing::debug;
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_no_views_mempool_count() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(2).build();
|
||||
|
||||
let header = api.push_block(1, vec![], true);
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Alice, 201);
|
||||
let xt2 = uxt(Alice, 202);
|
||||
|
||||
let submissions = vec![
|
||||
pool.submit_one(header.hash(), SOURCE, xt0.clone()),
|
||||
pool.submit_one(header.hash(), SOURCE, xt1.clone()),
|
||||
pool.submit_one(header.hash(), SOURCE, xt2.clone()),
|
||||
];
|
||||
|
||||
let results = block_on(futures::future::join_all(submissions));
|
||||
let mut results = results.iter();
|
||||
|
||||
assert!(results.next().unwrap().is_ok());
|
||||
assert!(results.next().unwrap().is_ok());
|
||||
assert!(matches!(
|
||||
results.next().unwrap().as_ref().unwrap_err().0,
|
||||
TxPoolError::ImmediatelyDropped
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_ready_count_works() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(3).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 200);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
//note: we need Charlie to be first as the oldest is removed.
|
||||
//For 3x alice, all tree would be removed.
|
||||
//(alice,bob,charlie would work too)
|
||||
let xt0 = uxt(Charlie, 500);
|
||||
let xt1 = uxt(Alice, 200);
|
||||
let xt2 = uxt(Alice, 201);
|
||||
|
||||
let submissions = vec![
|
||||
pool.submit_one(header01.hash(), SOURCE, xt0.clone()),
|
||||
pool.submit_one(header01.hash(), SOURCE, xt1.clone()),
|
||||
pool.submit_one(header01.hash(), SOURCE, xt2.clone()),
|
||||
];
|
||||
|
||||
let results = block_on(futures::future::join_all(submissions));
|
||||
assert!(results.iter().all(Result::is_ok));
|
||||
//charlie was not included into view:
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt1, xt2]);
|
||||
//todo: can we do better? We don't have API to check if event was processed internally.
|
||||
let mut counter = 0;
|
||||
while block_on(pool.mempool_len()).0 == 3 {
|
||||
sleep(std::time::Duration::from_millis(1));
|
||||
counter = counter + 1;
|
||||
if counter > 20 {
|
||||
assert!(false, "timeout");
|
||||
}
|
||||
}
|
||||
assert_eq!(block_on(pool.mempool_len()).0, 2);
|
||||
|
||||
//branch with alice transactions:
|
||||
let header02b = api.push_block(2, vec![xt1.clone(), xt2.clone()], true);
|
||||
let event = new_best_block_event(&pool, Some(header01.hash()), header02b.hash());
|
||||
block_on(pool.maintain(event));
|
||||
assert_eq!(block_on(pool.mempool_len()).0, 2);
|
||||
assert_pool_status!(header02b.hash(), &pool, 0, 0);
|
||||
assert_ready_iterator!(header02b.hash(), pool, []);
|
||||
|
||||
//branch with alice/charlie transactions shall also work:
|
||||
let header02a = api.push_block(2, vec![xt0.clone(), xt1.clone()], true);
|
||||
api.set_nonce(header02a.hash(), Alice.into(), 201);
|
||||
let event = new_best_block_event(&pool, Some(header02b.hash()), header02a.hash());
|
||||
block_on(pool.maintain(event));
|
||||
assert_eq!(block_on(pool.mempool_len()).0, 2);
|
||||
// assert_pool_status!(header02a.hash(), &pool, 1, 0);
|
||||
assert_ready_iterator!(header02a.hash(), pool, [xt2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_ready_count_works_for_submit_at() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(3).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 200);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Charlie, 500);
|
||||
let xt1 = uxt(Alice, 200);
|
||||
let xt2 = uxt(Alice, 201);
|
||||
|
||||
let results = block_on(pool.submit_at(
|
||||
header01.hash(),
|
||||
SOURCE,
|
||||
vec![xt0.clone(), xt1.clone(), xt2.clone()],
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
assert!(matches!(results[0].as_ref().unwrap_err().0, TxPoolError::ImmediatelyDropped));
|
||||
assert!(results[1].as_ref().is_ok());
|
||||
assert!(results[2].as_ref().is_ok());
|
||||
assert_eq!(block_on(pool.mempool_len()).0, 2);
|
||||
//charlie was not included into view:
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt1, xt2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_ready_count_works_for_submit_and_watch() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(3).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Charlie, 500);
|
||||
let xt1 = uxt(Alice, 200);
|
||||
let xt2 = uxt(Bob, 300);
|
||||
api.set_priority(&xt0, 2);
|
||||
api.set_priority(&xt1, 2);
|
||||
api.set_priority(&xt2, 1);
|
||||
|
||||
let result0 = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone()));
|
||||
let result1 = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone()));
|
||||
let result2 = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).map(|_| ());
|
||||
|
||||
assert!(matches!(result2.unwrap_err().0, TxPoolError::ImmediatelyDropped));
|
||||
assert!(result0.is_ok());
|
||||
assert!(result1.is_ok());
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 2);
|
||||
//charlie was not included into view:
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_future_count_works() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(3).with_future_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 200);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
|
||||
let xt1 = uxt(Charlie, 501);
|
||||
let xt2 = uxt(Alice, 201);
|
||||
let xt3 = uxt(Alice, 202);
|
||||
|
||||
block_on(pool.submit_one(header01.hash(), SOURCE, xt1.clone())).unwrap();
|
||||
block_on(pool.submit_one(header01.hash(), SOURCE, xt2.clone())).unwrap();
|
||||
block_on(pool.submit_one(header01.hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
//charlie was not included into view due to limits:
|
||||
assert_pool_status!(header01.hash(), &pool, 0, 2);
|
||||
//todo: can we do better? We don't have API to check if event was processed internally.
|
||||
let mut counter = 0;
|
||||
while block_on(pool.mempool_len()).0 != 2 {
|
||||
sleep(std::time::Duration::from_millis(1));
|
||||
counter = counter + 1;
|
||||
if counter > 20 {
|
||||
assert!(false, "timeout");
|
||||
}
|
||||
}
|
||||
|
||||
let header02 = api.push_block(2, vec![xt0], true);
|
||||
api.set_nonce(header02.hash(), Alice.into(), 201); //redundant
|
||||
let event = new_best_block_event(&pool, Some(header01.hash()), header02.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
assert_pool_status!(header02.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).0, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_watcher_mempool_doesnt_prevent_dropping() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(3).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Charlie, 400);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Alice, 200);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
|
||||
let xt0_status = futures::executor::block_on_stream(xt0_watcher).take(2).collect::<Vec<_>>();
|
||||
debug!(target: LOG_TARGET, ?xt0_status, "xt0_status");
|
||||
assert_eq!(xt0_status, vec![TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
let xt1_status = futures::executor::block_on_stream(xt1_watcher).take(1).collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(xt1_status, vec![TransactionStatus::Ready]);
|
||||
|
||||
let xt2_status = futures::executor::block_on_stream(xt2_watcher).take(1).collect::<Vec<_>>();
|
||||
debug!(target: LOG_TARGET, ?xt2_status, "xt2_status");
|
||||
|
||||
assert_eq!(xt2_status, vec![TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_watcher_non_intial_view_drops_transaction() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Dave, 500);
|
||||
let xt1 = uxt(Charlie, 400);
|
||||
let xt2 = uxt(Bob, 300);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
|
||||
// make sure tx0 is actually dropped before checking iterator
|
||||
let xt0_status = futures::executor::block_on_stream(xt0_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(xt0_status, vec![TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt1, xt2]);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(finalized_block_event(&pool, api.genesis_hash(), header02.hash())));
|
||||
assert_pool_status!(header02.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt1, xt2]);
|
||||
|
||||
let xt1_status = futures::executor::block_on_stream(xt1_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt1_status, vec![TransactionStatus::Ready]);
|
||||
|
||||
let xt2_status = futures::executor::block_on_stream(xt2_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt2_status, vec![TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_watcher_finalized_transaction_frees_ready_space() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Dave, 500);
|
||||
let xt1 = uxt(Charlie, 400);
|
||||
let xt2 = uxt(Bob, 300);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt1, xt2]);
|
||||
|
||||
let xt0_status = futures::executor::block_on_stream(xt0_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(xt0_status, vec![TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![xt0.clone()], true);
|
||||
block_on(pool.maintain(finalized_block_event(&pool, api.genesis_hash(), header02.hash())));
|
||||
assert_pool_status!(header02.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt1, xt2]);
|
||||
|
||||
let xt1_status = futures::executor::block_on_stream(xt1_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt1_status, vec![TransactionStatus::Ready]);
|
||||
|
||||
let xt2_status = futures::executor::block_on_stream(xt2_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt2_status, vec![TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_watcher_view_can_drop_transcation() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(3).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Dave, 500);
|
||||
let xt1 = uxt(Charlie, 400);
|
||||
let xt2 = uxt(Bob, 300);
|
||||
let xt3 = uxt(Alice, 200);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
|
||||
let xt0_status = futures::executor::block_on_stream(xt0_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(xt0_status, vec![TransactionStatus::Ready, TransactionStatus::Dropped,]);
|
||||
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt1, xt2]);
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(finalized_block_event(&pool, api.genesis_hash(), header02.hash())));
|
||||
|
||||
let xt1_status = futures::executor::block_on_stream(xt1_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(xt1_status, vec![TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
|
||||
assert_pool_status!(header02.hash(), pool, 2, 0);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt2, xt3]);
|
||||
|
||||
let xt2_status = futures::executor::block_on_stream(xt2_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt2_status, vec![TransactionStatus::Ready]);
|
||||
|
||||
let xt3_status = futures::executor::block_on_stream(xt3_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt3_status, vec![TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_watcher_empty_and_full_view_immediately_drops() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(4).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
api.set_nonce(api.genesis_hash(), Eve.into(), 600);
|
||||
api.set_nonce(api.genesis_hash(), Ferdie.into(), 700);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
|
||||
let xt3 = uxt(Dave, 500);
|
||||
let xt4 = uxt(Eve, 600);
|
||||
let xt5 = uxt(Ferdie, 700);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
|
||||
let xt0_status = futures::executor::block_on_stream(xt0_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(xt0_status, vec![TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 2);
|
||||
|
||||
let header02e = api.push_block_with_parent(
|
||||
header01.hash(),
|
||||
vec![xt0.clone(), xt1.clone(), xt2.clone()],
|
||||
true,
|
||||
);
|
||||
api.set_nonce(header02e.hash(), Alice.into(), 201);
|
||||
api.set_nonce(header02e.hash(), Bob.into(), 301);
|
||||
api.set_nonce(header02e.hash(), Charlie.into(), 401);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02e.hash())));
|
||||
|
||||
assert_pool_status!(header02e.hash(), &pool, 0, 0);
|
||||
|
||||
let header02f = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02f.hash())));
|
||||
assert_pool_status!(header02f.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header02f.hash(), pool, [xt1, xt2]);
|
||||
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
let xt4_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt4.clone())).unwrap();
|
||||
let result5 = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt5.clone())).map(|_| ());
|
||||
|
||||
//xt5 hits internal mempool limit
|
||||
assert!(matches!(result5.unwrap_err().0, TxPoolError::ImmediatelyDropped));
|
||||
|
||||
assert_pool_status!(header02e.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header02e.hash(), pool, [xt3, xt4]);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 4);
|
||||
|
||||
let xt1_status = futures::executor::block_on_stream(xt1_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
xt1_status,
|
||||
vec![TransactionStatus::Ready, TransactionStatus::InBlock((header02e.hash(), 1))]
|
||||
);
|
||||
|
||||
let xt2_status = futures::executor::block_on_stream(xt2_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
xt2_status,
|
||||
vec![TransactionStatus::Ready, TransactionStatus::InBlock((header02e.hash(), 2))]
|
||||
);
|
||||
|
||||
let xt3_status = futures::executor::block_on_stream(xt3_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt3_status, vec![TransactionStatus::Ready]);
|
||||
let xt4_status = futures::executor::block_on_stream(xt4_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt4_status, vec![TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_watcher_empty_and_full_view_drops_with_event() {
|
||||
// it is almost copy of fatp_limits_watcher_empty_and_full_view_immediately_drops, but the
|
||||
// mempool_count limit is set to 5 (vs 4).
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(5).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
api.set_nonce(api.genesis_hash(), Eve.into(), 600);
|
||||
api.set_nonce(api.genesis_hash(), Ferdie.into(), 700);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
|
||||
let xt3 = uxt(Dave, 500);
|
||||
let xt4 = uxt(Eve, 600);
|
||||
let xt5 = uxt(Ferdie, 700);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
|
||||
let xt0_status = futures::executor::block_on_stream(xt0_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(xt0_status, vec![TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 2);
|
||||
|
||||
let header02e = api.push_block_with_parent(
|
||||
header01.hash(),
|
||||
vec![xt0.clone(), xt1.clone(), xt2.clone()],
|
||||
true,
|
||||
);
|
||||
api.set_nonce(header02e.hash(), Alice.into(), 201);
|
||||
api.set_nonce(header02e.hash(), Bob.into(), 301);
|
||||
api.set_nonce(header02e.hash(), Charlie.into(), 401);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02e.hash())));
|
||||
|
||||
assert_pool_status!(header02e.hash(), &pool, 0, 0);
|
||||
|
||||
let header02f = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02f.hash())));
|
||||
assert_pool_status!(header02f.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header02f.hash(), pool, [xt1, xt2]);
|
||||
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
let xt4_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt4.clone())).unwrap();
|
||||
let xt5_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt5.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header02e.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header02e.hash(), pool, [xt4, xt5]);
|
||||
|
||||
let xt3_status = futures::executor::block_on_stream(xt3_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(xt3_status, vec![TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
|
||||
//xt5 got dropped
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 4);
|
||||
|
||||
let xt1_status = futures::executor::block_on_stream(xt1_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
xt1_status,
|
||||
vec![TransactionStatus::Ready, TransactionStatus::InBlock((header02e.hash(), 1))]
|
||||
);
|
||||
|
||||
let xt2_status = futures::executor::block_on_stream(xt2_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
xt2_status,
|
||||
vec![TransactionStatus::Ready, TransactionStatus::InBlock((header02e.hash(), 2))]
|
||||
);
|
||||
|
||||
let xt4_status = futures::executor::block_on_stream(xt4_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt4_status, vec![TransactionStatus::Ready]);
|
||||
|
||||
let xt5_status = futures::executor::block_on_stream(xt5_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt5_status, vec![TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
fn large_uxt(x: usize) -> bizinikiwi_test_runtime::Extrinsic {
|
||||
bizinikiwi_test_runtime::ExtrinsicBuilder::new_include_data(vec![x as u8; 1024]).build()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_ready_size_works() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_ready_bytes_size(3390).with_future_bytes_size(0).build();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = large_uxt(0);
|
||||
let xt1 = large_uxt(1);
|
||||
let xt2 = large_uxt(2);
|
||||
|
||||
let submissions = vec![
|
||||
pool.submit_one(header01.hash(), SOURCE, xt0.clone()),
|
||||
pool.submit_one(header01.hash(), SOURCE, xt1.clone()),
|
||||
pool.submit_one(header01.hash(), SOURCE, xt2.clone()),
|
||||
];
|
||||
|
||||
let results = block_on(futures::future::join_all(submissions));
|
||||
assert!(results.iter().all(Result::is_ok));
|
||||
//charlie was not included into view:
|
||||
assert_pool_status!(header01.hash(), &pool, 3, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1, xt2]);
|
||||
|
||||
let xt3 = large_uxt(3);
|
||||
let result3 = block_on(pool.submit_one(header01.hash(), SOURCE, xt3.clone()));
|
||||
assert!(matches!(result3.as_ref().unwrap_err().0, TxPoolError::ImmediatelyDropped));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_future_size_works() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
const UXT_SIZE: usize = 137;
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder
|
||||
.with_ready_bytes_size(UXT_SIZE)
|
||||
.with_future_bytes_size(3 * UXT_SIZE)
|
||||
.build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 200);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Bob, 201);
|
||||
let xt1 = uxt(Charlie, 501);
|
||||
let xt2 = uxt(Alice, 201);
|
||||
let xt3 = uxt(Alice, 202);
|
||||
assert_eq!(api.hash_and_length(&xt0).1, UXT_SIZE);
|
||||
assert_eq!(api.hash_and_length(&xt1).1, UXT_SIZE);
|
||||
assert_eq!(api.hash_and_length(&xt2).1, UXT_SIZE);
|
||||
assert_eq!(api.hash_and_length(&xt3).1, UXT_SIZE);
|
||||
|
||||
let _ = block_on(pool.submit_one(header01.hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let _ = block_on(pool.submit_one(header01.hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let _ = block_on(pool.submit_one(header01.hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let _ = block_on(pool.submit_one(header01.hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
//todo: can we do better? We don't have API to check if event was processed internally.
|
||||
let mut counter = 0;
|
||||
while block_on(pool.mempool_len()).0 == 4 {
|
||||
sleep(std::time::Duration::from_millis(1));
|
||||
counter = counter + 1;
|
||||
if counter > 20 {
|
||||
assert!(false, "timeout");
|
||||
}
|
||||
}
|
||||
assert_pool_status!(header01.hash(), &pool, 0, 3);
|
||||
assert_eq!(block_on(pool.mempool_len()).0, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_watcher_ready_transactions_are_not_droped_when_view_is_dropped() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(6).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
api.set_nonce(api.genesis_hash(), Eve.into(), 600);
|
||||
api.set_nonce(api.genesis_hash(), Ferdie.into(), 700);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
|
||||
let xt3 = uxt(Dave, 500);
|
||||
let xt4 = uxt(Eve, 600);
|
||||
let xt5 = uxt(Ferdie, 700);
|
||||
|
||||
let _xt0_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let _xt1_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 2);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02.hash())));
|
||||
|
||||
let _xt2_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let _xt3_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header02.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 4);
|
||||
|
||||
let header03 = api.push_block_with_parent(header02.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02.hash()), header03.hash())));
|
||||
|
||||
let _xt4_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt4.clone())).unwrap();
|
||||
let _xt5_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt5.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header03.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 6);
|
||||
|
||||
let header04 =
|
||||
api.push_block_with_parent(header03.hash(), vec![xt4.clone(), xt5.clone()], true);
|
||||
api.set_nonce(header04.hash(), Alice.into(), 201);
|
||||
api.set_nonce(header04.hash(), Bob.into(), 301);
|
||||
api.set_nonce(header04.hash(), Charlie.into(), 401);
|
||||
api.set_nonce(header04.hash(), Dave.into(), 501);
|
||||
api.set_nonce(header04.hash(), Eve.into(), 601);
|
||||
api.set_nonce(header04.hash(), Ferdie.into(), 701);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header03.hash()), header04.hash())));
|
||||
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1]);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt2, xt3]);
|
||||
assert_ready_iterator!(header03.hash(), pool, [xt4, xt5]);
|
||||
assert_ready_iterator!(header04.hash(), pool, []);
|
||||
|
||||
block_on(pool.maintain(finalized_block_event(&pool, api.genesis_hash(), header01.hash())));
|
||||
assert!(!pool.status_all().contains_key(&header01.hash()));
|
||||
|
||||
block_on(pool.maintain(finalized_block_event(&pool, header01.hash(), header02.hash())));
|
||||
assert!(!pool.status_all().contains_key(&header02.hash()));
|
||||
|
||||
//view 01 was dropped
|
||||
assert!(pool.ready_at(header01.hash()).now_or_never().is_none());
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 6);
|
||||
|
||||
block_on(pool.maintain(finalized_block_event(&pool, header02.hash(), header03.hash())));
|
||||
|
||||
//no revalidation has happened yet, all txs are kept
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 6);
|
||||
|
||||
//view 03 is still there
|
||||
assert!(!pool.status_all().contains_key(&header03.hash()));
|
||||
|
||||
//view 02 was dropped
|
||||
assert!(pool.ready_at(header02.hash()).now_or_never().is_none());
|
||||
|
||||
let mut prev_header = header03;
|
||||
for n in 5..=11 {
|
||||
let header = api.push_block(n, vec![], true);
|
||||
let event = finalized_block_event(&pool, prev_header.hash(), header.hash());
|
||||
block_on(pool.maintain(event));
|
||||
prev_header = header;
|
||||
}
|
||||
|
||||
//now revalidation has happened, all txs are dropped
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_limits_watcher_future_transactions_are_droped_when_view_is_dropped() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(6).with_future_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
api.set_nonce(api.genesis_hash(), Eve.into(), 600);
|
||||
api.set_nonce(api.genesis_hash(), Ferdie.into(), 700);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 201);
|
||||
let xt1 = uxt(Bob, 301);
|
||||
let xt2 = uxt(Charlie, 401);
|
||||
|
||||
let xt3 = uxt(Dave, 501);
|
||||
let xt4 = uxt(Eve, 601);
|
||||
let xt5 = uxt(Ferdie, 701);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 0, 2);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 2);
|
||||
assert_future_iterator!(header01.hash(), pool, [xt0, xt1]);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02.hash())));
|
||||
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header02.hash(), &pool, 0, 2);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 4);
|
||||
assert_future_iterator!(header02.hash(), pool, [xt2, xt3]);
|
||||
|
||||
let header03 = api.push_block_with_parent(header02.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02.hash()), header03.hash())));
|
||||
|
||||
let xt4_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt4.clone())).unwrap();
|
||||
let xt5_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt5.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header03.hash(), &pool, 0, 2);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 6);
|
||||
assert_future_iterator!(header03.hash(), pool, [xt4, xt5]);
|
||||
|
||||
let header04 = api.push_block_with_parent(header03.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header03.hash()), header04.hash())));
|
||||
|
||||
assert_pool_status!(header04.hash(), &pool, 0, 2);
|
||||
assert_eq!(pool.futures().len(), 2);
|
||||
assert_future_iterator!(header04.hash(), pool, [xt4, xt5]);
|
||||
|
||||
block_on(pool.maintain(finalized_block_event(&pool, api.genesis_hash(), header04.hash())));
|
||||
assert_eq!(pool.active_views_count(), 1);
|
||||
assert_eq!(pool.inactive_views_count(), 0);
|
||||
//todo: can we do better? We don't have API to check if event was processed internally.
|
||||
let mut counter = 0;
|
||||
while block_on(pool.mempool_len()).1 != 2 {
|
||||
sleep(std::time::Duration::from_millis(1));
|
||||
counter = counter + 1;
|
||||
if counter > 20 {
|
||||
assert!(false, "timeout {}", block_on(pool.mempool_len()).1);
|
||||
}
|
||||
}
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 2);
|
||||
assert_pool_status!(header04.hash(), &pool, 0, 2);
|
||||
assert_eq!(pool.futures().len(), 2);
|
||||
|
||||
let to_be_checked = vec![xt0_watcher, xt1_watcher, xt2_watcher, xt3_watcher];
|
||||
for x in to_be_checked {
|
||||
let x_status = futures::executor::block_on_stream(x).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(x_status, vec![TransactionStatus::Future, TransactionStatus::Dropped]);
|
||||
}
|
||||
|
||||
let to_be_checked = vec![xt4_watcher, xt5_watcher];
|
||||
for x in to_be_checked {
|
||||
let x_status = futures::executor::block_on_stream(x).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(x_status, vec![TransactionStatus::Future]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,561 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tests of priorities for fork-aware transaction pool.
|
||||
|
||||
pub mod fatp_common;
|
||||
|
||||
use fatp_common::{invalid_hash, new_best_block_event, TestPoolBuilder, LOG_TARGET, SOURCE};
|
||||
use futures::{executor::block_on, FutureExt};
|
||||
use pezsc_transaction_pool::ChainApi;
|
||||
use pezsc_transaction_pool_api::{
|
||||
error::Error as TxPoolError, LocalTransactionPool, MaintainedTransactionPool, TransactionPool,
|
||||
TransactionStatus,
|
||||
};
|
||||
use bizinikiwi_test_runtime_client::Sr25519Keyring::*;
|
||||
use bizinikiwi_test_runtime_transaction_pool::uxt;
|
||||
use tracing::info;
|
||||
#[test]
|
||||
fn fatp_prio_ready_higher_evicts_lower() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(3).with_ready_count(2).build();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Alice, 200);
|
||||
|
||||
api.set_priority(&xt0, 2);
|
||||
api.set_priority(&xt1, 3);
|
||||
|
||||
let result0 = block_on(pool.submit_one(header01.hash(), SOURCE, xt0.clone()));
|
||||
let result1 = block_on(pool.submit_one(header01.hash(), SOURCE, xt1.clone()));
|
||||
|
||||
info!(target: LOG_TARGET, ?result0, "r0");
|
||||
info!(target: LOG_TARGET, ?result1, "r1");
|
||||
info!(target: LOG_TARGET, len = ?block_on(pool.mempool_len()), "len");
|
||||
info!(target: LOG_TARGET, status = ?pool.status_all()[&header01.hash()], "len");
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt1]);
|
||||
assert_pool_status!(header01.hash(), &pool, 1, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_prio_watcher_ready_higher_evicts_lower() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(3).with_ready_count(2).build();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Alice, 200);
|
||||
|
||||
api.set_priority(&xt0, 2);
|
||||
api.set_priority(&xt1, 3);
|
||||
|
||||
let xt0_watcher =
|
||||
block_on(pool.submit_and_watch(header01.hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher =
|
||||
block_on(pool.submit_and_watch(header01.hash(), SOURCE, xt1.clone())).unwrap();
|
||||
|
||||
let xt0_status = futures::executor::block_on_stream(xt0_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
xt0_status,
|
||||
vec![TransactionStatus::Ready, TransactionStatus::Usurped(api.hash_and_length(&xt1).0)]
|
||||
);
|
||||
let xt1_status = futures::executor::block_on_stream(xt1_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt1_status, vec![TransactionStatus::Ready]);
|
||||
|
||||
info!(target: LOG_TARGET, len = ?block_on(pool.mempool_len()), "len");
|
||||
info!(target: LOG_TARGET, pool_status = ?pool.status_all()[&header01.hash()], "len");
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt1]);
|
||||
assert_pool_status!(header01.hash(), &pool, 1, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_prio_watcher_future_higher_evicts_lower() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(3).with_ready_count(3).build();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 201);
|
||||
let xt1 = uxt(Alice, 201);
|
||||
let xt2 = uxt(Alice, 200);
|
||||
|
||||
api.set_priority(&xt0, 2);
|
||||
api.set_priority(&xt1, 3);
|
||||
|
||||
let xt0_watcher =
|
||||
block_on(pool.submit_and_watch(header01.hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher =
|
||||
block_on(pool.submit_and_watch(header01.hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher =
|
||||
block_on(pool.submit_and_watch(header01.hash(), SOURCE, xt2.clone())).unwrap();
|
||||
|
||||
let xt0_status = futures::executor::block_on_stream(xt0_watcher).take(2).collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
xt0_status,
|
||||
vec![TransactionStatus::Future, TransactionStatus::Usurped(api.hash_and_length(&xt2).0)]
|
||||
);
|
||||
let xt1_status = futures::executor::block_on_stream(xt1_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(xt1_status, vec![TransactionStatus::Future, TransactionStatus::Ready]);
|
||||
let xt2_status = futures::executor::block_on_stream(xt2_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt2_status, vec![TransactionStatus::Ready]);
|
||||
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 2);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt2, xt1]);
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_prio_watcher_ready_lower_prio_gets_dropped_from_all_views() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(3).with_ready_count(2).build();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Alice, 200);
|
||||
|
||||
api.set_priority(&xt0, 2);
|
||||
api.set_priority(&xt1, 3);
|
||||
|
||||
let xt0_watcher =
|
||||
block_on(pool.submit_and_watch(header01.hash(), SOURCE, xt0.clone())).unwrap();
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02.hash())));
|
||||
|
||||
let header03a = api.push_block_with_parent(header02.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header03a.hash())));
|
||||
|
||||
let header03b = api.push_block_with_parent(header02.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header03a.hash()), header03b.hash())));
|
||||
|
||||
assert_pool_status!(header03a.hash(), &pool, 1, 0);
|
||||
assert_ready_iterator!(header03a.hash(), pool, [xt0]);
|
||||
assert_pool_status!(header03b.hash(), &pool, 1, 0);
|
||||
assert_ready_iterator!(header03b.hash(), pool, [xt0]);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0]);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt0]);
|
||||
|
||||
let xt1_watcher =
|
||||
block_on(pool.submit_and_watch(header01.hash(), SOURCE, xt1.clone())).unwrap();
|
||||
|
||||
let xt1_status = futures::executor::block_on_stream(xt1_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt1_status, vec![TransactionStatus::Ready]);
|
||||
let xt0_status = futures::executor::block_on_stream(xt0_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
xt0_status,
|
||||
vec![TransactionStatus::Ready, TransactionStatus::Usurped(api.hash_and_length(&xt1).0)]
|
||||
);
|
||||
assert_ready_iterator!(header03a.hash(), pool, [xt1]);
|
||||
assert_ready_iterator!(header03b.hash(), pool, [xt1]);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt1]);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_prio_watcher_future_lower_prio_gets_dropped_from_all_views() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(3).with_ready_count(2).build();
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, None, header01.hash())));
|
||||
|
||||
let xt0 = uxt(Alice, 201);
|
||||
let xt1 = uxt(Alice, 201);
|
||||
let xt2 = uxt(Alice, 200);
|
||||
|
||||
api.set_priority(&xt0, 2);
|
||||
api.set_priority(&xt1, 3);
|
||||
|
||||
let xt0_watcher =
|
||||
block_on(pool.submit_and_watch(header01.hash(), SOURCE, xt0.clone())).unwrap();
|
||||
|
||||
let xt1_watcher =
|
||||
block_on(pool.submit_and_watch(header01.hash(), SOURCE, xt1.clone())).unwrap();
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02.hash())));
|
||||
|
||||
let header03a = api.push_block_with_parent(header02.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header03a.hash())));
|
||||
|
||||
let header03b = api.push_block_with_parent(header02.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header03a.hash()), header03b.hash())));
|
||||
|
||||
assert_pool_status!(header03a.hash(), &pool, 0, 2);
|
||||
assert_future_iterator!(header03a.hash(), pool, [xt0, xt1]);
|
||||
assert_pool_status!(header03b.hash(), &pool, 0, 2);
|
||||
assert_future_iterator!(header03b.hash(), pool, [xt0, xt1]);
|
||||
assert_future_iterator!(header01.hash(), pool, [xt0, xt1]);
|
||||
assert_future_iterator!(header02.hash(), pool, [xt0, xt1]);
|
||||
|
||||
let xt2_watcher =
|
||||
block_on(pool.submit_and_watch(header01.hash(), SOURCE, xt2.clone())).unwrap();
|
||||
|
||||
let xt2_status = futures::executor::block_on_stream(xt2_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt2_status, vec![TransactionStatus::Ready]);
|
||||
let xt1_status = futures::executor::block_on_stream(xt1_watcher).take(1).collect::<Vec<_>>();
|
||||
assert_eq!(xt1_status, vec![TransactionStatus::Future]);
|
||||
let xt0_status = futures::executor::block_on_stream(xt0_watcher).take(2).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
xt0_status,
|
||||
vec![TransactionStatus::Future, TransactionStatus::Usurped(api.hash_and_length(&xt2).0)]
|
||||
);
|
||||
assert_future_iterator!(header03a.hash(), pool, []);
|
||||
assert_future_iterator!(header03b.hash(), pool, []);
|
||||
assert_future_iterator!(header01.hash(), pool, []);
|
||||
assert_future_iterator!(header02.hash(), pool, []);
|
||||
|
||||
assert_ready_iterator!(header03a.hash(), pool, [xt2, xt1]);
|
||||
assert_ready_iterator!(header03b.hash(), pool, [xt2, xt1]);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt2, xt1]);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt2, xt1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_prios_watcher_full_mempool_higher_prio_is_accepted() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(4).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
api.set_nonce(api.genesis_hash(), Eve.into(), 600);
|
||||
api.set_nonce(api.genesis_hash(), Ferdie.into(), 700);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
|
||||
let xt3 = uxt(Dave, 500);
|
||||
|
||||
let xt4 = uxt(Eve, 600);
|
||||
let xt5 = uxt(Ferdie, 700);
|
||||
|
||||
api.set_priority(&xt0, 1);
|
||||
api.set_priority(&xt1, 2);
|
||||
api.set_priority(&xt2, 3);
|
||||
api.set_priority(&xt3, 4);
|
||||
|
||||
api.set_priority(&xt4, 5);
|
||||
api.set_priority(&xt5, 6);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 2);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02.hash())));
|
||||
|
||||
let _xt2_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let _xt3_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header02.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 4);
|
||||
|
||||
let header03 = api.push_block_with_parent(header02.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02.hash()), header03.hash())));
|
||||
|
||||
let _xt4_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt4.clone())).unwrap();
|
||||
let _xt5_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt5.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header03.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 4);
|
||||
|
||||
assert_watcher_stream!(xt0_watcher, [TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
assert_watcher_stream!(xt1_watcher, [TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
|
||||
assert_ready_iterator!(header01.hash(), pool, []);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt3, xt2]);
|
||||
assert_ready_iterator!(header03.hash(), pool, [xt5, xt4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_prios_watcher_full_mempool_higher_prio_is_accepted_with_subtree() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(4).with_ready_count(4).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Alice, 201);
|
||||
let xt2 = uxt(Alice, 202);
|
||||
let xt3 = uxt(Bob, 300);
|
||||
let xt4 = uxt(Charlie, 400);
|
||||
|
||||
api.set_priority(&xt0, 1);
|
||||
api.set_priority(&xt1, 3);
|
||||
api.set_priority(&xt2, 3);
|
||||
api.set_priority(&xt3, 2);
|
||||
api.set_priority(&xt4, 2);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt3, xt0, xt1, xt2]);
|
||||
assert_pool_status!(header01.hash(), &pool, 4, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 4);
|
||||
|
||||
let xt4_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt4.clone())).unwrap();
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt3, xt4]);
|
||||
|
||||
assert_watcher_stream!(xt0_watcher, [TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
assert_watcher_stream!(xt1_watcher, [TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
assert_watcher_stream!(xt2_watcher, [TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
assert_watcher_stream!(xt3_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt4_watcher, [TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_prios_watcher_full_mempool_higher_prio_is_accepted_with_subtree2() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(4).with_ready_count(4).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Alice, 201);
|
||||
let xt2 = uxt(Alice, 202);
|
||||
let xt3 = uxt(Bob, 300);
|
||||
let xt4 = uxt(Charlie, 400);
|
||||
|
||||
api.set_priority(&xt0, 1);
|
||||
api.set_priority(&xt1, 3);
|
||||
api.set_priority(&xt2, 3);
|
||||
api.set_priority(&xt3, 2);
|
||||
api.set_priority(&xt4, 2);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt3, xt0, xt1, xt2]);
|
||||
assert_pool_status!(header01.hash(), &pool, 4, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 4);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02.hash())));
|
||||
|
||||
let xt4_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt4.clone())).unwrap();
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt3]);
|
||||
assert_pool_status!(header02.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt3, xt4]);
|
||||
|
||||
assert_watcher_stream!(xt0_watcher, [TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
assert_watcher_stream!(xt1_watcher, [TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
assert_watcher_stream!(xt2_watcher, [TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
assert_watcher_stream!(xt3_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt4_watcher, [TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_prios_watcher_full_mempool_lower_prio_gets_rejected() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(2).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
let xt3 = uxt(Dave, 500);
|
||||
|
||||
api.set_priority(&xt0, 2);
|
||||
api.set_priority(&xt1, 2);
|
||||
api.set_priority(&xt2, 2);
|
||||
api.set_priority(&xt3, 1);
|
||||
|
||||
let _xt0_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let _xt1_watcher =
|
||||
block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 2);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02.hash())));
|
||||
|
||||
assert_pool_status!(header02.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).1, 2);
|
||||
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt0, xt1]);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt0, xt1]);
|
||||
|
||||
let result2 = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).map(|_| ());
|
||||
assert!(matches!(result2.as_ref().unwrap_err().0, TxPoolError::ImmediatelyDropped));
|
||||
let result3 = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).map(|_| ());
|
||||
assert!(matches!(result3.as_ref().unwrap_err().0, TxPoolError::ImmediatelyDropped));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_prios_watcher_full_mempool_does_not_keep_dropped_transaction() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(4).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
let xt3 = uxt(Dave, 500);
|
||||
|
||||
api.set_priority(&xt0, 2);
|
||||
api.set_priority(&xt1, 2);
|
||||
api.set_priority(&xt2, 2);
|
||||
api.set_priority(&xt3, 2);
|
||||
|
||||
let xt0_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt0.clone())).unwrap();
|
||||
let xt1_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt1.clone())).unwrap();
|
||||
let xt2_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt2.clone())).unwrap();
|
||||
let xt3_watcher = block_on(pool.submit_and_watch(invalid_hash(), SOURCE, xt3.clone())).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_ready_iterator!(header01.hash(), pool, [xt2, xt3]);
|
||||
|
||||
assert_watcher_stream!(xt0_watcher, [TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
assert_watcher_stream!(xt1_watcher, [TransactionStatus::Ready, TransactionStatus::Dropped]);
|
||||
assert_watcher_stream!(xt2_watcher, [TransactionStatus::Ready]);
|
||||
assert_watcher_stream!(xt3_watcher, [TransactionStatus::Ready]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fatp_prios_submit_local_full_mempool_higher_prio_is_accepted() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = TestPoolBuilder::new();
|
||||
let (pool, api, _) = builder.with_mempool_count_limit(4).with_ready_count(2).build();
|
||||
api.set_nonce(api.genesis_hash(), Bob.into(), 300);
|
||||
api.set_nonce(api.genesis_hash(), Charlie.into(), 400);
|
||||
api.set_nonce(api.genesis_hash(), Dave.into(), 500);
|
||||
api.set_nonce(api.genesis_hash(), Eve.into(), 600);
|
||||
api.set_nonce(api.genesis_hash(), Ferdie.into(), 700);
|
||||
|
||||
let header01 = api.push_block(1, vec![], true);
|
||||
let event = new_best_block_event(&pool, None, header01.hash());
|
||||
block_on(pool.maintain(event));
|
||||
|
||||
let xt0 = uxt(Alice, 200);
|
||||
let xt1 = uxt(Bob, 300);
|
||||
let xt2 = uxt(Charlie, 400);
|
||||
|
||||
let xt3 = uxt(Dave, 500);
|
||||
|
||||
let xt4 = uxt(Eve, 600);
|
||||
let xt5 = uxt(Ferdie, 700);
|
||||
|
||||
api.set_priority(&xt0, 1);
|
||||
api.set_priority(&xt1, 2);
|
||||
api.set_priority(&xt2, 3);
|
||||
api.set_priority(&xt3, 4);
|
||||
|
||||
api.set_priority(&xt4, 5);
|
||||
api.set_priority(&xt5, 6);
|
||||
pool.submit_local(invalid_hash(), xt0.clone()).unwrap();
|
||||
pool.submit_local(invalid_hash(), xt1.clone()).unwrap();
|
||||
|
||||
assert_pool_status!(header01.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).0, 2);
|
||||
|
||||
let header02 = api.push_block_with_parent(header01.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header01.hash()), header02.hash())));
|
||||
|
||||
pool.submit_local(invalid_hash(), xt2.clone()).unwrap();
|
||||
pool.submit_local(invalid_hash(), xt3.clone()).unwrap();
|
||||
|
||||
assert_pool_status!(header02.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).0, 4);
|
||||
|
||||
let header03 = api.push_block_with_parent(header02.hash(), vec![], true);
|
||||
block_on(pool.maintain(new_best_block_event(&pool, Some(header02.hash()), header03.hash())));
|
||||
|
||||
pool.submit_local(invalid_hash(), xt4.clone()).unwrap();
|
||||
pool.submit_local(invalid_hash(), xt5.clone()).unwrap();
|
||||
|
||||
assert_pool_status!(header03.hash(), &pool, 2, 0);
|
||||
assert_eq!(block_on(pool.mempool_len()).0, 4);
|
||||
|
||||
assert_ready_iterator!(header01.hash(), pool, []);
|
||||
assert_ready_iterator!(header02.hash(), pool, [xt3, xt2]);
|
||||
assert_ready_iterator!(header03.hash(), pool, [xt5, xt4]);
|
||||
}
|
||||
@@ -0,0 +1,540 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Testsuite of transaction pool integration tests.
|
||||
|
||||
pub mod zombienet;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::zombienet::{
|
||||
default_zn_scenario_builder, relaychain_pezkuwichain_local_network_spec as relay,
|
||||
relaychain_pezkuwichain_local_network_spec::teyrchain_asset_hub_network_spec as para,
|
||||
BlockSubscriptionType, NetworkSpawner,
|
||||
};
|
||||
use futures::future::join_all;
|
||||
use tracing::info;
|
||||
use txtesttool::{execution_log::ExecutionLog, scenario::ScenarioExecutor};
|
||||
use zombienet::DEFAULT_SEND_FUTURE_AND_READY_TXS_TESTS_TIMEOUT_IN_SECS;
|
||||
|
||||
// Test which sends future and ready txs from many accounts
|
||||
// to an unlimited pool of a teyrchain collator based on the asset-hub-pezkuwichain runtime.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore]
|
||||
async fn send_future_and_ready_from_many_accounts_to_teyrchain() {
|
||||
let net = NetworkSpawner::from_toml_with_env_logger(para::HIGH_POOL_LIMIT_FATP)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait for the teyrchain collator to start block production.
|
||||
net.wait_for_block("charlie", BlockSubscriptionType::Best).await.unwrap();
|
||||
|
||||
// Create future & ready txs executors.
|
||||
let ws = net.node_rpc_uri("charlie").unwrap();
|
||||
let future_scenario_executor = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws.clone())
|
||||
.with_start_id(0)
|
||||
.with_last_id(99)
|
||||
.with_nonce_from(Some(100))
|
||||
.with_txs_count(100)
|
||||
.with_executor_id("future-txs-executor".to_string())
|
||||
.with_timeout_in_secs(DEFAULT_SEND_FUTURE_AND_READY_TXS_TESTS_TIMEOUT_IN_SECS)
|
||||
.build()
|
||||
.await;
|
||||
let ready_scenario_executor = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws)
|
||||
.with_start_id(0)
|
||||
.with_last_id(99)
|
||||
.with_nonce_from(Some(0))
|
||||
.with_txs_count(100)
|
||||
.with_executor_id("ready-txs-executor".to_string())
|
||||
.with_timeout_in_secs(DEFAULT_SEND_FUTURE_AND_READY_TXS_TESTS_TIMEOUT_IN_SECS)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
// Execute transactions and fetch the execution logs.
|
||||
let (future_logs, ready_logs) = futures::future::join(
|
||||
future_scenario_executor.execute(),
|
||||
ready_scenario_executor.execute(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let finalized_future =
|
||||
future_logs.values().filter_map(|default_log| default_log.finalized()).count();
|
||||
let finalized_ready =
|
||||
ready_logs.values().filter_map(|default_log| default_log.finalized()).count();
|
||||
|
||||
assert_eq!(finalized_future, 10_000);
|
||||
assert_eq!(finalized_ready, 10_000);
|
||||
}
|
||||
|
||||
// Test which sends future and ready txs from many accounts
|
||||
// to an unlimited pool of a relaychain node based on `pezkuwichain-local` runtime.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore]
|
||||
async fn send_future_and_ready_from_many_accounts_to_relaychain() {
|
||||
let net = NetworkSpawner::from_toml_with_env_logger(relay::HIGH_POOL_LIMIT_FATP)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait for the paracha validator to start block production & have its genesis block
|
||||
// finalized.
|
||||
net.wait_for_block("alice", BlockSubscriptionType::Best).await.unwrap();
|
||||
|
||||
// Create future & ready txs executors.
|
||||
let ws = net.node_rpc_uri("alice").unwrap();
|
||||
let future_scenario_executor = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws.clone())
|
||||
.with_start_id(0)
|
||||
.with_last_id(99)
|
||||
.with_nonce_from(Some(100))
|
||||
.with_txs_count(100)
|
||||
.with_executor_id("future-txs-executor".to_string())
|
||||
.with_timeout_in_secs(DEFAULT_SEND_FUTURE_AND_READY_TXS_TESTS_TIMEOUT_IN_SECS)
|
||||
.build()
|
||||
.await;
|
||||
let ready_scenario_executor = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws)
|
||||
.with_start_id(0)
|
||||
.with_last_id(99)
|
||||
.with_nonce_from(Some(0))
|
||||
.with_txs_count(100)
|
||||
.with_executor_id("ready-txs-executor".to_string())
|
||||
.with_timeout_in_secs(DEFAULT_SEND_FUTURE_AND_READY_TXS_TESTS_TIMEOUT_IN_SECS)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
// Execute transactions and fetch the execution logs.
|
||||
// Execute transactions and fetch the execution logs.
|
||||
let (future_logs, ready_logs) = futures::future::join(
|
||||
future_scenario_executor.execute(),
|
||||
ready_scenario_executor.execute(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let finalized_future =
|
||||
future_logs.values().filter_map(|default_log| default_log.finalized()).count();
|
||||
let finalized_ready =
|
||||
ready_logs.values().filter_map(|default_log| default_log.finalized()).count();
|
||||
|
||||
assert_eq!(finalized_future, 10_000);
|
||||
assert_eq!(finalized_ready, 10_000);
|
||||
}
|
||||
|
||||
// Send immortal and mortal txs. Some of the mortal txs are configured to get dropped
|
||||
// while others to succeed. Mortal txs are future so not being able to become ready in time and
|
||||
// included in blocks result in their dropping.
|
||||
//
|
||||
// Block length for pezkuwichain for user txs is 75% of maximum 5MB (per pezframe-system setup),
|
||||
// so we get 3750KB. In the test scenario we aim for 5 txs per block roughly (not precesily)
|
||||
// so to fill a block each user tx must have around 750kb.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore]
|
||||
async fn send_future_mortal_txs() {
|
||||
let net = NetworkSpawner::from_toml_with_env_logger(relay::HIGH_POOL_LIMIT_FATP)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait for the teyrchain collator to start block production.
|
||||
net.wait_for_block("alice", BlockSubscriptionType::Finalized).await.unwrap();
|
||||
|
||||
// Create txs executors.
|
||||
let ws = net.node_rpc_uri("alice").unwrap();
|
||||
let ready_scenario_executor = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws.clone())
|
||||
.with_start_id(0)
|
||||
.with_nonce_from(Some(0))
|
||||
.with_txs_count(50)
|
||||
// Block length for pezkuwichain for user txs is 75% of maximum 5MB (per pezframe-system
|
||||
// setup), so we get 3750KB. In the test scenario we aim for 5 txs per block roughly (not
|
||||
// precesily) so to fill a block each user tx must have around 750kb. We aim for 5 txs per
|
||||
// block because we send 50 ready txs which we want to distribute over 10 blocks, so
|
||||
// mortal txs with lifetime lower than 10 should be declared invalid after the ready txs
|
||||
// finalize, while mortal txs with bigger lifetime should be finalized.
|
||||
.with_remark_recipe(750)
|
||||
.with_executor_id("ready-txs-executor".to_string())
|
||||
.build()
|
||||
.await;
|
||||
|
||||
let mortal_scenario_invalid = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws.clone())
|
||||
.with_start_id(0)
|
||||
.with_nonce_from(Some(60))
|
||||
.with_txs_count(10)
|
||||
.with_executor_id("mortal-tx-executor-invalid".to_string())
|
||||
.with_mortality(5)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
let mortal_scenario_success = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws)
|
||||
.with_start_id(0)
|
||||
.with_nonce_from(Some(50))
|
||||
.with_txs_count(10)
|
||||
.with_executor_id("mortal-tx-executor-success".to_string())
|
||||
.with_mortality(25)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
// Execute transactions and fetch the execution logs.
|
||||
let (mortal_invalid_logs, ready_logs, mortal_succes_logs) = tokio::join!(
|
||||
mortal_scenario_invalid.execute(),
|
||||
ready_scenario_executor.execute(),
|
||||
mortal_scenario_success.execute(),
|
||||
);
|
||||
|
||||
let mortal_invalid = mortal_invalid_logs
|
||||
.values()
|
||||
.filter(|default_log| default_log.get_invalid_reason().len() > 0)
|
||||
.count();
|
||||
let mortal_succesfull = mortal_succes_logs
|
||||
.values()
|
||||
.filter_map(|default_log| default_log.finalized())
|
||||
.count();
|
||||
let finalized_ready =
|
||||
ready_logs.values().filter_map(|default_log| default_log.finalized()).count();
|
||||
|
||||
assert_eq!(mortal_invalid, 10);
|
||||
assert_eq!(mortal_succesfull, 10);
|
||||
assert_eq!(finalized_ready, 50);
|
||||
}
|
||||
|
||||
// Send immortal and mortal txs. Some mortal txs have lower priority so they shouldn't get into
|
||||
// blocks during their lifetime, and will be considered invalid, while other mortal txs have
|
||||
// sufficient lifetime to be included in blocks, and are finalized successfully.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore]
|
||||
async fn send_lower_priority_mortal_txs() {
|
||||
let net = NetworkSpawner::from_toml_with_env_logger(relay::HIGH_POOL_LIMIT_FATP)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait for the teyrchain collator to start block production.
|
||||
net.wait_for_block("alice", BlockSubscriptionType::Finalized).await.unwrap();
|
||||
|
||||
// Create txs executors.
|
||||
let ws = net.node_rpc_uri("alice").unwrap();
|
||||
let ready_scenario_executor = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws.clone())
|
||||
.with_start_id(0)
|
||||
.with_nonce_from(Some(0))
|
||||
.with_txs_count(50)
|
||||
.with_executor_id("ready-txs-executor".to_string())
|
||||
// Block length for pezkuwichain for user txs is 75% of maximum 5MB (per pezframe-system
|
||||
// setup), so we get 3750KB. In the test scenario we aim for 5 txs per block roughly (not
|
||||
// precesily) so to fill a block each user tx must have around 750kb. We aim for 5 txs per
|
||||
// block because we send 50 ready txs which we want to distribute over 10 blocks, so
|
||||
// mortal txs with lifetime lower than 10 should be declared invalid after the ready txs
|
||||
// finalize, while mortal txs with bigger lifetime should be finalized.
|
||||
.with_remark_recipe(750)
|
||||
.with_tip(150)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
let mortal_scenario_invalid = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws.clone())
|
||||
.with_start_id(1)
|
||||
.with_nonce_from(Some(0))
|
||||
.with_txs_count(10)
|
||||
.with_executor_id("mortal-tx-executor-invalid".to_string())
|
||||
.with_mortality(5)
|
||||
// Make it very hard for these mortal txs to be included in blocks, by making them big
|
||||
// enough to not let other txs be part of the same block as them, but also make sure they
|
||||
// have the lowest priority so that they are not included in a single tx block over other
|
||||
// txs. At some point they'll be starved and their lifetime will pass.
|
||||
.with_remark_recipe(3500)
|
||||
.with_tip(50)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
let mortal_scenario_success = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws)
|
||||
.with_start_id(2)
|
||||
.with_nonce_from(Some(0))
|
||||
.with_txs_count(10)
|
||||
.with_executor_id("mortal-tx-executor-success".to_string())
|
||||
.with_mortality(20)
|
||||
// Same reasoning as for the ready immortal txs, we want these txs to be similarly big as
|
||||
// the immortal txs, so at all times if it comes to pick a ready txs to include it in a
|
||||
// block, an immortal tx should be picked instead (leaving these mortal txs to be picked
|
||||
// only after, which is fine for these mortal txs).
|
||||
.with_remark_recipe(750)
|
||||
.with_tip(100)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
// Execute transactions and fetch the execution logs.
|
||||
let (mortal_invalid_logs, ready_logs, mortal_success_logs) = tokio::join!(
|
||||
mortal_scenario_invalid.execute(),
|
||||
ready_scenario_executor.execute(),
|
||||
mortal_scenario_success.execute(),
|
||||
);
|
||||
|
||||
let mortal_invalid = mortal_invalid_logs
|
||||
.values()
|
||||
.filter(|default_log| default_log.get_invalid_reason().len() > 0)
|
||||
.count();
|
||||
let mortal_succesfull = mortal_success_logs
|
||||
.values()
|
||||
.filter_map(|default_log| default_log.finalized())
|
||||
.count();
|
||||
let finalized_ready =
|
||||
ready_logs.values().filter_map(|default_log| default_log.finalized()).count();
|
||||
|
||||
assert_eq!(mortal_invalid, 10);
|
||||
assert_eq!(mortal_succesfull, 10);
|
||||
assert_eq!(finalized_ready, 50);
|
||||
}
|
||||
|
||||
// Test which sends 5m transactions to teyrchain. Long execution time expected.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore]
|
||||
async fn send_5m_from_many_accounts_to_teyrchain() {
|
||||
let net = NetworkSpawner::from_toml_with_env_logger(para::HIGH_POOL_LIMIT_FATP)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait for the teyrchain collator to start block production.
|
||||
net.wait_for_block("charlie", BlockSubscriptionType::Best).await.unwrap();
|
||||
|
||||
// Create txs executor.
|
||||
let ws = net.node_rpc_uri("charlie").unwrap();
|
||||
let executor = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws)
|
||||
.with_start_id(0)
|
||||
.with_last_id(999)
|
||||
.with_txs_count(5_000)
|
||||
.with_executor_id("txs-executor".to_string())
|
||||
.with_send_threshold(7500)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
// Execute transactions and fetch the execution logs.
|
||||
let execution_logs = executor.execute().await;
|
||||
let finalized_txs = execution_logs.values().filter_map(|tx_log| tx_log.finalized()).count();
|
||||
|
||||
assert_eq!(finalized_txs, 5_000_000);
|
||||
}
|
||||
|
||||
// Test which sends 5m transactions to relaychain. Long execution time expected.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore]
|
||||
async fn send_5m_from_many_accounts_to_relaychain() {
|
||||
let net = NetworkSpawner::from_toml_with_env_logger(relay::HIGH_POOL_LIMIT_FATP)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait for the teyrchain collator to start block production.
|
||||
net.wait_for_block("alice", BlockSubscriptionType::Best).await.unwrap();
|
||||
|
||||
// Create txs executor.
|
||||
let ws = net.node_rpc_uri("alice").unwrap();
|
||||
let executor = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws.clone())
|
||||
.with_start_id(0)
|
||||
.with_last_id(999)
|
||||
.with_txs_count(5000)
|
||||
.with_executor_id("txs-executor".to_string())
|
||||
.with_send_threshold(7500)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
// Execute transactions and fetch the execution logs.
|
||||
let execution_logs = executor.execute().await;
|
||||
let finalized_txs = execution_logs.values().filter_map(|tx_log| tx_log.finalized()).count();
|
||||
|
||||
assert_eq!(finalized_txs, 5_000_000);
|
||||
}
|
||||
|
||||
/// Internal test that allows to observe how transcactions are gossiped in the network. Requires
|
||||
/// external tool to track transactions presence at nodes. Was used to evaluate some metrics of
|
||||
/// existing transaction protocol.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore]
|
||||
async fn gossiping() {
|
||||
let net = NetworkSpawner::from_toml_with_env_logger(relay::HIGH_POOL_LIMIT_FATP_TRACE)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait for the teyrchain collator to start block production.
|
||||
net.wait_for_block("a00", BlockSubscriptionType::Best).await.unwrap();
|
||||
|
||||
// Create the txs executor.
|
||||
let ws = net.node_rpc_uri("a00").unwrap();
|
||||
let executor = default_zn_scenario_builder(&net)
|
||||
.with_rpc_uri(ws)
|
||||
.with_start_id(0)
|
||||
.with_last_id(999)
|
||||
.with_executor_id("txs-executor".to_string())
|
||||
.build()
|
||||
.await;
|
||||
|
||||
// Execute transactions and fetch the execution logs.
|
||||
let execution_logs = executor.execute().await;
|
||||
let finalized_txs = execution_logs.values().filter_map(|tx_log| tx_log.finalized()).count();
|
||||
|
||||
assert_eq!(finalized_txs, 1000);
|
||||
|
||||
tracing::info!("BASEDIR: {:?}", net.base_dir_path());
|
||||
}
|
||||
|
||||
/// Creates new transaction scenario executor and sends given batch of ready transactions to the
|
||||
/// specified node. Single transaction is sent from single account.
|
||||
async fn send_batch(
|
||||
net: &NetworkSpawner,
|
||||
node_name: &str,
|
||||
from: u32,
|
||||
to: u32,
|
||||
prio: u32,
|
||||
) -> ScenarioExecutor {
|
||||
let ws = net.node_rpc_uri(node_name).unwrap();
|
||||
info!(from, to, prio, "send_batch");
|
||||
default_zn_scenario_builder(net)
|
||||
.with_rpc_uri(ws)
|
||||
.with_start_id(from)
|
||||
.with_last_id(to)
|
||||
.with_txs_count(1)
|
||||
.with_tip(prio.into())
|
||||
.with_executor_id(format!("txs-executor_{}_{}_{}", from, to, prio))
|
||||
.with_send_threshold(usize::MAX)
|
||||
.with_legacy_backend(true)
|
||||
.build()
|
||||
.await
|
||||
}
|
||||
|
||||
/// Repeatedly sends batches of transactions to the specified node with priority provided by
|
||||
/// closure.
|
||||
///
|
||||
/// This function loops indefinitely, adjusting the priority of the transaction batch each time
|
||||
/// based on the provided function. Each batch is executed by an executor that times out after
|
||||
/// period duration if not completed.
|
||||
///
|
||||
/// The progress of transactions is intentionally not monitored; the utility is intended for
|
||||
/// transaction pool limits testing, where the accuracy of execution is challenging to monitor.
|
||||
async fn batch_loop<F>(
|
||||
net: &NetworkSpawner,
|
||||
node_name: &str,
|
||||
from: u32,
|
||||
to: u32,
|
||||
priority: F,
|
||||
period: std::time::Duration,
|
||||
) where
|
||||
F: Fn(u32) -> u32,
|
||||
{
|
||||
let mut prio = 0;
|
||||
loop {
|
||||
prio = priority(prio);
|
||||
let executor = send_batch(&net, node_name, from, to, prio).await;
|
||||
let start = std::time::Instant::now();
|
||||
let _results = tokio::time::timeout(period, executor.execute()).await;
|
||||
let elapsed = start.elapsed();
|
||||
if elapsed < period {
|
||||
tokio::time::sleep(period - elapsed).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests the transaction pool limits by continuously sending transaction batches to a teyrchain
|
||||
/// network node. This test checks the pool's behavior under high load by simulating multiple
|
||||
/// senders with increasing priorities.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore]
|
||||
async fn test_limits_increasing_prio_teyrchain() {
|
||||
let net = NetworkSpawner::from_toml_with_env_logger(para::LOW_POOL_LIMIT_FATP)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
net.wait_for_block("charlie", BlockSubscriptionType::Best).await.unwrap();
|
||||
|
||||
let mut executors = vec![];
|
||||
let senders_count = 25;
|
||||
let sender_batch = 2000;
|
||||
|
||||
for i in 0..senders_count {
|
||||
let from = 0 + i * sender_batch;
|
||||
let to = from + sender_batch - 1;
|
||||
executors.push(batch_loop(
|
||||
&net,
|
||||
"charlie",
|
||||
from,
|
||||
to,
|
||||
|prio| prio + 1,
|
||||
Duration::from_secs(60),
|
||||
));
|
||||
}
|
||||
|
||||
let _results = join_all(executors).await;
|
||||
}
|
||||
|
||||
/// Tests the transaction pool limits by continuously sending transaction batches to a relaychain
|
||||
/// network node. This test checks the pool's behavior under high load by simulating multiple
|
||||
/// senders with increasing priorities.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore]
|
||||
async fn test_limits_increasing_prio_relaychain() {
|
||||
let net = NetworkSpawner::from_toml_with_env_logger(relay::LOW_POOL_LIMIT_FATP)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
net.wait_for_block("alice", BlockSubscriptionType::Best).await.unwrap();
|
||||
|
||||
let mut executors = vec![];
|
||||
//this looks like current limit of what we can handle. A bit choky but almost no empty blocks.
|
||||
let senders_count = 50;
|
||||
let sender_batch = 2000;
|
||||
|
||||
for i in 0..senders_count {
|
||||
let from = 0 + i * sender_batch;
|
||||
let to = from + sender_batch - 1;
|
||||
executors.push(batch_loop(
|
||||
&net,
|
||||
"alice",
|
||||
from,
|
||||
to,
|
||||
|prio| prio + 1,
|
||||
Duration::from_secs(15),
|
||||
));
|
||||
}
|
||||
|
||||
let _results = join_all(executors).await;
|
||||
}
|
||||
|
||||
/// Tests the transaction pool limits by continuously sending transaction batches to a relaychain
|
||||
/// network node. This test checks the pool's behavior under high load by simulating multiple
|
||||
/// senders with increasing priorities.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore]
|
||||
async fn test_limits_same_prio_relaychain() {
|
||||
let net = NetworkSpawner::from_toml_with_env_logger(relay::LOW_POOL_LIMIT_FATP)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
net.wait_for_block("alice", BlockSubscriptionType::Best).await.unwrap();
|
||||
|
||||
let mut executors = vec![];
|
||||
let senders_count = 50;
|
||||
let sender_batch = 2000;
|
||||
|
||||
for i in 0..senders_count {
|
||||
let from = 0 + i * sender_batch;
|
||||
let to = from + sender_batch - 1;
|
||||
executors.push(batch_loop(&net, "alice", from, to, |prio| prio, Duration::from_secs(15)));
|
||||
}
|
||||
|
||||
let _results = join_all(executors).await;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,250 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! The zombienet spawner for integration tests for a transaction pool. Holds shared logic used
|
||||
//! across integration tests for transaction pool.
|
||||
|
||||
use anyhow::anyhow;
|
||||
use std::time::SystemTime;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use txtesttool::scenario::{ChainType, ScenarioBuilder};
|
||||
use zombienet_sdk::{
|
||||
subxt::BizinikiwiConfig, GlobalSettingsBuilder, LocalFileSystem, Network, NetworkConfig,
|
||||
NetworkConfigBuilder, NetworkConfigExt, WithRelaychain,
|
||||
};
|
||||
|
||||
/// Gathers TOML files paths for relaychains and for teyrchains' (that use pezkuwichain-local based
|
||||
/// relaychains) zombienet network specs for testing in relation to fork aware transaction pool.
|
||||
pub mod relaychain_pezkuwichain_local_network_spec {
|
||||
pub const HIGH_POOL_LIMIT_FATP: &'static str =
|
||||
"tests/zombienet/network-specs/pezkuwichain-local-high-pool-limit-fatp.toml";
|
||||
pub const LOW_POOL_LIMIT_FATP: &'static str =
|
||||
"tests/zombienet/network-specs/pezkuwichain-local-low-pool-limit-fatp.toml";
|
||||
pub const HIGH_POOL_LIMIT_FATP_TRACE: &'static str =
|
||||
"tests/zombienet/network-specs/pezkuwichain-local-gossiping.toml";
|
||||
|
||||
/// Network specs used for fork-aware tx pool testing of teyrchains.
|
||||
pub mod teyrchain_asset_hub_network_spec {
|
||||
pub const LOW_POOL_LIMIT_FATP: &'static str =
|
||||
"tests/zombienet/network-specs/asset-hub-low-pool-limit-fatp.toml";
|
||||
pub const HIGH_POOL_LIMIT_FATP: &'static str =
|
||||
"tests/zombienet/network-specs/asset-hub-high-pool-limit-fatp.toml";
|
||||
}
|
||||
}
|
||||
|
||||
mod yap_test;
|
||||
|
||||
/// Default time that we expect to need for a full run of current tests that send future and ready
|
||||
/// txs to teyrchain or relaychain networks.
|
||||
pub const DEFAULT_SEND_FUTURE_AND_READY_TXS_TESTS_TIMEOUT_IN_SECS: u64 = 1500;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Network initialization failure: {0}")]
|
||||
NetworkInit(anyhow::Error),
|
||||
#[error("Node couldn't be found as part of the network: {0}")]
|
||||
NodeNotFound(anyhow::Error),
|
||||
#[error("Failed to get node online client")]
|
||||
FailedToGetOnlineClinet,
|
||||
#[error("Failed to get node blocks stream")]
|
||||
FailedToGetBlocksStream,
|
||||
}
|
||||
|
||||
/// Result of work related to network spawning.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Environment variable defining the location of zombienet network base dir.
|
||||
const TXPOOL_TEST_DIR_ENV: &str = "TXPOOL_TEST_DIR";
|
||||
|
||||
/// Type for block subscription modes.
|
||||
pub enum BlockSubscriptionType {
|
||||
Finalized,
|
||||
Best,
|
||||
}
|
||||
|
||||
/// Provides logic to spawn a network based on a Zombienet toml file.
|
||||
pub struct NetworkSpawner {
|
||||
network: Network<LocalFileSystem>,
|
||||
}
|
||||
|
||||
impl NetworkSpawner {
|
||||
/// Initialize the network spawner using given `builder` closure.
|
||||
pub async fn with_closure<F>(builder: F) -> Result<NetworkSpawner>
|
||||
where
|
||||
F: FnOnce() -> NetworkConfigBuilder<WithRelaychain>,
|
||||
{
|
||||
let _ = env_logger::try_init_from_env(
|
||||
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
|
||||
);
|
||||
|
||||
let config_builder = builder();
|
||||
|
||||
let net_config = config_builder
|
||||
.with_global_settings(|global_settings| match NetworkSpawner::base_dir_from_env() {
|
||||
Some(val) => global_settings.with_base_dir(val),
|
||||
_ => global_settings,
|
||||
})
|
||||
.build()
|
||||
.map_err(|errs| {
|
||||
let msg = errs.into_iter().map(|e| e.to_string()).collect::<Vec<_>>().join(", ");
|
||||
Error::NetworkInit(anyhow!(msg))
|
||||
})?;
|
||||
|
||||
Ok(NetworkSpawner {
|
||||
network: net_config
|
||||
.spawn_native()
|
||||
.await
|
||||
.map_err(|err| Error::NetworkInit(anyhow!(err.to_string())))?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates a directory path from an environment variable and the current timestamp.
|
||||
/// The format is "<TENV>/test_YMD_HMS"
|
||||
pub fn base_dir_from_env() -> Option<String> {
|
||||
std::env::var(TXPOOL_TEST_DIR_ENV)
|
||||
.map(|pool_test_dir| {
|
||||
let datetime: chrono::DateTime<chrono::Local> = SystemTime::now().into();
|
||||
let formatted_date = datetime.format("%Y%m%d_%H%M%S");
|
||||
format!("{}/test_{}", pool_test_dir, formatted_date)
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
|
||||
/// Initialize the network spawner based on a Zombienet toml file
|
||||
pub async fn from_toml_with_env_logger(toml_path: &'static str) -> Result<NetworkSpawner> {
|
||||
// Initialize the subscriber with a default log level of INFO if RUST_LOG is not set
|
||||
let env_filter =
|
||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
// Set up the subscriber with the formatter and the environment filter
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(env_filter) // Use the env filter
|
||||
.init();
|
||||
|
||||
let net_config = if let Some(base_dir) = Self::base_dir_from_env() {
|
||||
let settings = GlobalSettingsBuilder::new().with_base_dir(base_dir).build().unwrap();
|
||||
NetworkConfig::load_from_toml_with_settings(toml_path, &settings)
|
||||
.map_err(Error::NetworkInit)?
|
||||
} else {
|
||||
tracing::info!("'{TXPOOL_TEST_DIR_ENV}' env not set, proceeding with defaults.");
|
||||
NetworkConfig::load_from_toml(toml_path).map_err(Error::NetworkInit)?
|
||||
};
|
||||
|
||||
Ok(NetworkSpawner {
|
||||
network: net_config
|
||||
.spawn_native()
|
||||
.await
|
||||
.map_err(|err| Error::NetworkInit(anyhow!(err.to_string())))?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the spawned network.
|
||||
pub fn network(&self) -> &Network<LocalFileSystem> {
|
||||
&self.network
|
||||
}
|
||||
|
||||
/// Waits for blocks production/import to kick-off on given node.
|
||||
///
|
||||
/// It subscribes to best/finalized blocks on the given node to determine whether
|
||||
/// the blocks were considered as best/finalized.
|
||||
pub async fn wait_for_block(
|
||||
&self,
|
||||
node_name: &str,
|
||||
subscription_type: BlockSubscriptionType,
|
||||
) -> Result<()> {
|
||||
let node = self
|
||||
.network
|
||||
.get_node(node_name)
|
||||
.map_err(|_| Error::NodeNotFound(anyhow!("{node_name}")))?;
|
||||
let client = node
|
||||
.wait_client::<BizinikiwiConfig>()
|
||||
.await
|
||||
.map_err(|_| Error::FailedToGetOnlineClinet)?;
|
||||
let mut stream = match subscription_type {
|
||||
BlockSubscriptionType::Best => client
|
||||
.blocks()
|
||||
.subscribe_finalized()
|
||||
.await
|
||||
.map_err(|_| Error::FailedToGetBlocksStream)?,
|
||||
BlockSubscriptionType::Finalized => client
|
||||
.blocks()
|
||||
.subscribe_best()
|
||||
.await
|
||||
.map_err(|_| Error::FailedToGetBlocksStream)?,
|
||||
};
|
||||
|
||||
// It should take at most two iterations to return with the best block, if any.
|
||||
for _ in 0..=1 {
|
||||
let Some(block) = stream.next().await else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(block) = block.ok().filter(|block| block.number() == 1) {
|
||||
tracing::info!("[{node_name}] found first block: {:#?}", block.hash());
|
||||
break;
|
||||
}
|
||||
|
||||
tracing::info!("[{node_name}] waiting for first block");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the network filesystem base dir path.
|
||||
pub fn base_dir_path(&self) -> Option<&str> {
|
||||
self.network.base_dir()
|
||||
}
|
||||
|
||||
/// Get a certain node rpc uri.
|
||||
pub fn node_rpc_uri(&self, node_name: &str) -> Result<String> {
|
||||
self.network
|
||||
.get_node(node_name)
|
||||
.and_then(|node| Ok(node.ws_uri().to_string()))
|
||||
.map_err(|_| Error::NodeNotFound(anyhow!("{node_name}")))
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared params usually set in same way for most of the scenarios.
|
||||
pub struct ScenarioBuilderSharedParams {
|
||||
watched_txs: bool,
|
||||
does_block_monitoring: bool,
|
||||
send_threshold: usize,
|
||||
chain_type: ChainType,
|
||||
}
|
||||
|
||||
impl Default for ScenarioBuilderSharedParams {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
watched_txs: true,
|
||||
does_block_monitoring: false,
|
||||
send_threshold: 20000,
|
||||
chain_type: ChainType::Sub,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`txtesttool::scenario::ScenarioBuilder`] with a set of default parameters defined
|
||||
/// with [`ScenarioBuilderSharedParams::default`].
|
||||
pub fn default_zn_scenario_builder(net_spawner: &NetworkSpawner) -> ScenarioBuilder {
|
||||
let shared_params = ScenarioBuilderSharedParams::default();
|
||||
ScenarioBuilder::new()
|
||||
.with_watched_txs(shared_params.watched_txs)
|
||||
.with_send_threshold(shared_params.send_threshold)
|
||||
.with_block_monitoring(shared_params.does_block_monitoring)
|
||||
.with_chain_type(shared_params.chain_type)
|
||||
.with_base_dir_path(net_spawner.base_dir_path().unwrap().to_string())
|
||||
.with_timeout_in_secs(21600) //6 hours
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
[settings]
|
||||
timeout = 1500
|
||||
|
||||
[relaychain]
|
||||
default_image = "pezkuwichain/pezkuwi:latest"
|
||||
default_command = "pezkuwi"
|
||||
chain = "pezkuwichain-local"
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "alice"
|
||||
rpc_port = 9944
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "bob"
|
||||
rpc_port = 9945
|
||||
validator = true
|
||||
|
||||
[[teyrchains]]
|
||||
id = 2000
|
||||
chain = "asset-hub-pezkuwichain-local"
|
||||
default_command = "pezkuwi-teyrchain"
|
||||
default_image = "pezkuwichain/pezkuwi-teyrchain:latest"
|
||||
cumulus_based = true
|
||||
default_args = [
|
||||
"--force-authoring",
|
||||
"--pool-kbytes 2048000",
|
||||
"--pool-limit 500000",
|
||||
"--pool-type=fork-aware",
|
||||
"--rpc-max-connections 15000",
|
||||
"--rpc-max-response-size 150",
|
||||
"--rpc-max-subscriptions-per-connection=128000",
|
||||
"--state-pruning=1024",
|
||||
"-laura::pezcumulus=info",
|
||||
"-lbasic-authorship=info",
|
||||
"-lpeerset=info",
|
||||
"-lsub-libp2p=info",
|
||||
"-lsync=info",
|
||||
"-ltxpool=debug",
|
||||
]
|
||||
[teyrchains.genesis.runtimeGenesis.patch.balances]
|
||||
devAccounts = [1000, 1000000000000000000, "//Sender//{}"]
|
||||
|
||||
[[teyrchains.collators]]
|
||||
name = "charlie"
|
||||
validator = false
|
||||
rpc_port = 9933
|
||||
|
||||
[[teyrchains.collators]]
|
||||
name = "dave"
|
||||
validator = true
|
||||
rpc_port = 9934
|
||||
|
||||
[[teyrchains.collators]]
|
||||
name = "eve"
|
||||
validator = true
|
||||
rpc_port = 9935
|
||||
|
||||
[[teyrchains.collators]]
|
||||
name = "ferdie"
|
||||
validator = true
|
||||
rpc_port = 9936
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
[settings]
|
||||
timeout = 1500
|
||||
|
||||
[relaychain]
|
||||
default_image = "pezkuwichain/pezkuwi:latest"
|
||||
default_command = "pezkuwi"
|
||||
chain = "pezkuwichain-local"
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "alice"
|
||||
rpc_port = 9944
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "bob"
|
||||
validator = true
|
||||
|
||||
[[teyrchains]]
|
||||
id = 2000
|
||||
cumulus_based = true
|
||||
chain = "asset-hub-pezkuwichain-local"
|
||||
default_image = "pezkuwichain/pezkuwi-teyrchain:latest"
|
||||
default_command = "pezkuwi-teyrchain"
|
||||
default_args = [
|
||||
"--force-authoring",
|
||||
"--pool-kbytes 2048000",
|
||||
"--pool-limit 10000",
|
||||
"--pool-type=fork-aware",
|
||||
"--rpc-max-connections 100000",
|
||||
"--rpc-max-response-size 150",
|
||||
"--rpc-max-subscriptions-per-connection=128000",
|
||||
"--state-pruning=1024",
|
||||
"-laura::pezcumulus=info",
|
||||
"-lbasic-authorship=info",
|
||||
"-lpeerset=info",
|
||||
"-lsub-libp2p=info",
|
||||
"-lsync=info",
|
||||
"-ltxpool=debug",
|
||||
"-ltxpoolstat=debug",
|
||||
]
|
||||
[teyrchains.genesis.runtimeGenesis.patch.balances]
|
||||
devAccounts = [80000, 1000000000000000000, "//Sender//{}"]
|
||||
|
||||
# run charlie as teyrchain collator
|
||||
[[teyrchains.collators]]
|
||||
name = "charlie"
|
||||
validator = false
|
||||
rpc_port = 9933
|
||||
|
||||
[[teyrchains.collators]]
|
||||
name = "dave"
|
||||
validator = true
|
||||
rpc_port = 9934
|
||||
|
||||
[[teyrchains.collators]]
|
||||
name = "eve"
|
||||
validator = true
|
||||
rpc_port = 9935
|
||||
|
||||
[[teyrchains.collators]]
|
||||
name = "ferdie"
|
||||
validator = true
|
||||
rpc_port = 9936
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
[settings]
|
||||
timeout = 1500
|
||||
|
||||
[relaychain]
|
||||
default_image = "pezkuwichain/pezkuwi:latest"
|
||||
default_command = "pezkuwi"
|
||||
chain = "pezkuwichain-local"
|
||||
default_args = [
|
||||
# "--network-backend litep2p",
|
||||
"--pool-kbytes 2048000",
|
||||
"--pool-limit 500000",
|
||||
"--pool-type=fork-aware",
|
||||
"--rpc-max-connections 15000",
|
||||
"--rpc-max-response-size 150",
|
||||
"--rpc-max-subscriptions-per-connection=128000",
|
||||
"--state-pruning=1024",
|
||||
"-ltxpool=trace",
|
||||
"-lsync=trace",
|
||||
"--out-peers=3",
|
||||
"--in-peers=3",
|
||||
]
|
||||
[relaychain.genesis.runtimeGenesis.patch.balances]
|
||||
devAccounts = [1000, 1000000000000000000, "//Sender//{}"]
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "a00"
|
||||
rpc_port = 9944
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b00"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b01"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b02"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b03"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b04"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b05"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b06"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b07"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b08"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b09"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b10"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b11"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b12"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b13"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b14"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b15"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b16"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b17"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b18"
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "b19"
|
||||
validator = true
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
[settings]
|
||||
timeout = 1500
|
||||
|
||||
[relaychain]
|
||||
default_image = "pezkuwichain/pezkuwi:latest"
|
||||
default_command = "pezkuwi"
|
||||
chain = "pezkuwichain-local"
|
||||
default_args = [
|
||||
"--pool-kbytes 2048000",
|
||||
"--pool-limit 500000",
|
||||
"--pool-type=fork-aware",
|
||||
"--rpc-max-connections 15000",
|
||||
"--rpc-max-response-size 1500",
|
||||
"--rpc-max-subscriptions-per-connection=128000",
|
||||
"--state-pruning=1024",
|
||||
"-lsync=info",
|
||||
"-ltxpool=debug",
|
||||
]
|
||||
[relaychain.genesis.runtimeGenesis.patch.balances]
|
||||
devAccounts = [1000, 1000000000000000000, "//Sender//{}"]
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "alice"
|
||||
rpc_port = 9944
|
||||
validator = true
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "bob"
|
||||
rpc_port = 9945
|
||||
validator = true
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
[settings]
|
||||
timeout = 1500
|
||||
|
||||
[relaychain]
|
||||
default_image = "pezkuwichain/pezkuwi:latest"
|
||||
default_command = "pezkuwi"
|
||||
chain = "pezkuwichain-local"
|
||||
default_args = [
|
||||
"--pool-kbytes 2048000",
|
||||
"--pool-limit 100000",
|
||||
"--pool-type=fork-aware",
|
||||
"--rpc-max-connections 15000",
|
||||
"--rpc-max-response-size 150",
|
||||
"--rpc-max-subscriptions-per-connection=128000",
|
||||
"--state-pruning=1024",
|
||||
"-lsync=info",
|
||||
"-ltxpool=debug",
|
||||
"-ltxpoolstat=debug",
|
||||
]
|
||||
[relaychain.genesis.runtimeGenesis.patch.balances]
|
||||
devAccounts = [100000, 1000000000000000000, "//Sender//{}"]
|
||||
|
||||
[[relaychain.nodes]]
|
||||
# command = "/home/miszka/parity/14-txpool-forks/pezkuwi-sdk-master-02/target/release-tokio-console/pezkuwi"
|
||||
name = "alice"
|
||||
rpc_port = 9944
|
||||
validator = false
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "bob"
|
||||
rpc_port = 9945
|
||||
validator = true
|
||||
|
||||
|
||||
[[relaychain.nodes]]
|
||||
name = "charlie"
|
||||
rpc_port = 9946
|
||||
validator = true
|
||||
@@ -0,0 +1,145 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Test inspired (copied) from:
|
||||
// https://github.com/pezkuwichain/pezkuwi-sdk/blob/85b71daf7aac59da4d2186b45d589c7c619f0981/polkadot/zombienet-sdk-tests/tests/elastic_scaling/slot_based_3cores.rs#L21
|
||||
// and patched as in:
|
||||
// https://github.com/pezkuwichain/kurdistan-sdk/issues/124#issuecomment-2808830472
|
||||
|
||||
use crate::zombienet::{BlockSubscriptionType, NetworkSpawner, ScenarioBuilderSharedParams};
|
||||
use cumulus_zombienet_sdk_helpers::create_assign_core_call;
|
||||
use serde_json::json;
|
||||
use txtesttool::{execution_log::ExecutionLog, scenario::ScenarioBuilder};
|
||||
use zombienet_sdk::{
|
||||
subxt::{OnlineClient, PolkadotConfig},
|
||||
subxt_signer::sr25519::dev,
|
||||
NetworkConfigBuilder,
|
||||
};
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore]
|
||||
async fn slot_based_3cores_test() -> Result<(), anyhow::Error> {
|
||||
let spawner = NetworkSpawner::with_closure(|| {
|
||||
let images = zombienet_sdk::environment::get_images_from_env();
|
||||
let names = ["alice", "bob", "charlie"];
|
||||
NetworkConfigBuilder::new()
|
||||
.with_relaychain(|r| {
|
||||
let r = r
|
||||
.with_chain("pezkuwichain-local")
|
||||
.with_default_command("pezkuwi")
|
||||
.with_default_image(images.pezkuwi.as_str())
|
||||
.with_default_args(vec![("-lteyrchain=debug").into()])
|
||||
.with_genesis_overrides(json!({
|
||||
"configuration": {
|
||||
"config": {
|
||||
"scheduler_params": {
|
||||
// Num cores is 2, because 1 extra will be added automatically when registering the para.
|
||||
"num_cores": 2,
|
||||
"max_validators_per_core": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
.with_default_resources(|resources| {
|
||||
resources.with_request_cpu(4).with_request_memory("4G")
|
||||
})
|
||||
// Have to set a `with_node` outside of the loop below, so that `r` has the
|
||||
// right type.
|
||||
.with_node(|node| node.with_name(names[0]));
|
||||
|
||||
(1..3).fold(r, |acc, i| acc.with_node(|node| node.with_name(names[i])))
|
||||
})
|
||||
.with_teyrchain(|p| {
|
||||
// Para 2200 uses the new RFC103-enabled collator which sends the UMP signal
|
||||
// commitment for selecting the core index
|
||||
p.with_id(2200)
|
||||
.with_default_command("pezkuwi-teyrchain")
|
||||
.with_default_image(images.pezcumulus.as_str())
|
||||
.with_chain("yap-pezkuwichain-local-2200")
|
||||
.with_genesis_overrides(json!({
|
||||
"balances": {
|
||||
"devAccounts": [
|
||||
100000, 1000000000000000000u64, "//Sender//{}"
|
||||
]
|
||||
}
|
||||
}))
|
||||
.with_default_args(vec![
|
||||
"--authoring=slot-based".into(),
|
||||
"--rpc-max-subscriptions-per-connection=256000".into(),
|
||||
"--rpc-max-connections=128000".into(),
|
||||
"--rpc-max-response-size=150".into(),
|
||||
"--pool-limit=2500000".into(),
|
||||
"--pool-kbytes=4048000".into(),
|
||||
"--pool-type=fork-aware".into(),
|
||||
("-lteyrchain=debug,aura=debug,txpool=debug,txpoolstat=debug").into(),
|
||||
])
|
||||
.with_collator(|n| n.with_name("dave").with_rpc_port(9944))
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let relay_node = spawner.network().get_node("alice")?;
|
||||
|
||||
let relay_client: OnlineClient<PolkadotConfig> = relay_node.wait_client().await?;
|
||||
let alice = dev::alice();
|
||||
|
||||
let assign_cores_call = create_assign_core_call(&[(0, 2200), (1, 2200)]);
|
||||
// Assign two extra cores to each teyrchain.
|
||||
relay_client
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&assign_cores_call, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
|
||||
tracing::info!("2 more cores assigned to the teyrchain");
|
||||
|
||||
// Wait for the teyrchain collator to start block production.
|
||||
spawner.wait_for_block("dave", BlockSubscriptionType::Best).await.unwrap();
|
||||
|
||||
// Create txs executor.
|
||||
let ws = spawner.node_rpc_uri("dave").unwrap();
|
||||
let executor = {
|
||||
let shared_params = ScenarioBuilderSharedParams::default();
|
||||
ScenarioBuilder::new()
|
||||
.with_watched_txs(shared_params.watched_txs)
|
||||
.with_send_threshold(shared_params.send_threshold)
|
||||
.with_block_monitoring(shared_params.does_block_monitoring)
|
||||
.with_chain_type(shared_params.chain_type)
|
||||
.with_base_dir_path(spawner.base_dir_path().unwrap().to_string())
|
||||
.with_timeout_in_secs(21600) //6 hours
|
||||
.with_legacy_backend(true)
|
||||
}
|
||||
.with_rpc_uri(ws)
|
||||
.with_start_id(0)
|
||||
.with_last_id(99999)
|
||||
.with_txs_count(150)
|
||||
.with_executor_id("txs-executor".to_string())
|
||||
.with_send_threshold(25000)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
// Execute transactions and fetch the execution logs.
|
||||
let execution_logs = executor.execute().await;
|
||||
let finalized_txs = execution_logs.values().filter_map(|tx_log| tx_log.finalized()).count();
|
||||
|
||||
assert_eq!(finalized_txs, 15_000_000);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user