mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 09:51:10 +00:00
Produce block always on updated transaction pool state (#5227)
* make sure return ready iterator once state is updated * update sc_basic_authorship tests * update node tests * fix manual seal * actually fix service test * add tests * Update client/basic-authorship/src/basic_authorship.rs Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * helper function * review suggestions * warning and continue * add debug log * use futures::chennel::oneshot * use declaration bound * no option for updated_at * no allocation * ready_at / ready * Update client/transaction-pool/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/transaction-pool/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/transaction-pool/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/transaction-pool/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/transaction-pool/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/transaction-pool/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
@@ -31,12 +31,12 @@ pub use sc_transaction_graph as txpool;
|
||||
pub use crate::api::{FullChainApi, LightChainApi};
|
||||
|
||||
use std::{collections::HashMap, sync::Arc, pin::Pin};
|
||||
use futures::{Future, FutureExt, future::ready};
|
||||
use futures::{Future, FutureExt, future::ready, channel::oneshot};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, NumberFor, AtLeast32Bit, Extrinsic},
|
||||
traits::{Block as BlockT, NumberFor, AtLeast32Bit, Extrinsic, Zero},
|
||||
};
|
||||
use sp_transaction_pool::{
|
||||
TransactionPool, PoolStatus, ImportNotificationStream, TxHash, TransactionFor,
|
||||
@@ -44,6 +44,12 @@ use sp_transaction_pool::{
|
||||
};
|
||||
use wasm_timer::Instant;
|
||||
|
||||
type BoxedReadyIterator<Hash, Data> = Box<dyn Iterator<Item=Arc<sc_transaction_graph::base_pool::Transaction<Hash, Data>>> + Send>;
|
||||
|
||||
type ReadyIteratorFor<PoolApi> = BoxedReadyIterator<sc_transaction_graph::ExHash<PoolApi>, sc_transaction_graph::ExtrinsicFor<PoolApi>>;
|
||||
|
||||
type PolledIterator<PoolApi> = Pin<Box<dyn Future<Output=ReadyIteratorFor<PoolApi>> + Send>>;
|
||||
|
||||
/// Basic implementation of transaction pool that can be customized by providing PoolApi.
|
||||
pub struct BasicPool<PoolApi, Block>
|
||||
where
|
||||
@@ -54,6 +60,48 @@ pub struct BasicPool<PoolApi, Block>
|
||||
api: Arc<PoolApi>,
|
||||
revalidation_strategy: Arc<Mutex<RevalidationStrategy<NumberFor<Block>>>>,
|
||||
revalidation_queue: Arc<revalidation::RevalidationQueue<PoolApi>>,
|
||||
ready_poll: Arc<Mutex<ReadyPoll<ReadyIteratorFor<PoolApi>, Block>>>,
|
||||
}
|
||||
|
||||
struct ReadyPoll<T, Block: BlockT> {
|
||||
updated_at: NumberFor<Block>,
|
||||
pollers: Vec<(NumberFor<Block>, oneshot::Sender<T>)>,
|
||||
}
|
||||
|
||||
impl<T, Block: BlockT> Default for ReadyPoll<T, Block> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
updated_at: NumberFor::<Block>::zero(),
|
||||
pollers: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Block: BlockT> ReadyPoll<T, Block> {
|
||||
fn trigger(&mut self, number: NumberFor<Block>, iterator_factory: impl Fn() -> T) {
|
||||
self.updated_at = number;
|
||||
|
||||
let mut idx = 0;
|
||||
while idx < self.pollers.len() {
|
||||
if self.pollers[idx].0 <= number {
|
||||
let poller_sender = self.pollers.swap_remove(idx);
|
||||
log::debug!(target: "txpool", "Sending ready signal at block {}", number);
|
||||
let _ = poller_sender.1.send(iterator_factory());
|
||||
} else {
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add(&mut self, number: NumberFor<Block>) -> oneshot::Receiver<T> {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
self.pollers.push((number, sender));
|
||||
receiver
|
||||
}
|
||||
|
||||
fn updated_at(&self) -> NumberFor<Block> {
|
||||
self.updated_at
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "unknown"))]
|
||||
@@ -128,6 +176,7 @@ impl<PoolApi, Block> BasicPool<PoolApi, Block>
|
||||
RevalidationType::Full => RevalidationStrategy::Always,
|
||||
}
|
||||
)),
|
||||
ready_poll: Default::default(),
|
||||
},
|
||||
background_task,
|
||||
)
|
||||
@@ -196,10 +245,6 @@ impl<PoolApi, Block> TransactionPool for BasicPool<PoolApi, Block>
|
||||
self.pool.validated_pool().status()
|
||||
}
|
||||
|
||||
fn ready(&self) -> Box<dyn Iterator<Item=Arc<Self::InPoolTransaction>>> {
|
||||
Box::new(self.pool.validated_pool().ready())
|
||||
}
|
||||
|
||||
fn import_notification_stream(&self) -> ImportNotificationStream<TxHash<Self>> {
|
||||
self.pool.validated_pool().import_notification_stream()
|
||||
}
|
||||
@@ -215,6 +260,27 @@ impl<PoolApi, Block> TransactionPool for BasicPool<PoolApi, Block>
|
||||
fn ready_transaction(&self, hash: &TxHash<Self>) -> Option<Arc<Self::InPoolTransaction>> {
|
||||
self.pool.validated_pool().ready_by_hash(hash)
|
||||
}
|
||||
|
||||
fn ready_at(&self, at: NumberFor<Self::Block>) -> PolledIterator<PoolApi> {
|
||||
if self.ready_poll.lock().updated_at() >= at {
|
||||
let iterator: ReadyIteratorFor<PoolApi> = Box::new(self.pool.validated_pool().ready());
|
||||
return Box::pin(futures::future::ready(iterator));
|
||||
}
|
||||
|
||||
Box::pin(
|
||||
self.ready_poll
|
||||
.lock()
|
||||
.add(at)
|
||||
.map(|received| received.unwrap_or_else(|e| {
|
||||
log::warn!("Error receiving pending set: {:?}", e);
|
||||
Box::new(vec![].into_iter())
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
fn ready(&self) -> ReadyIteratorFor<PoolApi> {
|
||||
Box::new(self.pool.validated_pool().ready())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
@@ -329,6 +395,7 @@ impl<PoolApi, Block> MaintainedTransactionPool for BasicPool<PoolApi, Block>
|
||||
let revalidation_strategy = self.revalidation_strategy.clone();
|
||||
let retracted = retracted.clone();
|
||||
let revalidation_queue = self.revalidation_queue.clone();
|
||||
let ready_poll = self.ready_poll.clone();
|
||||
|
||||
async move {
|
||||
// We don't query block if we won't prune anything
|
||||
@@ -348,6 +415,10 @@ impl<PoolApi, Block> MaintainedTransactionPool for BasicPool<PoolApi, Block>
|
||||
}
|
||||
}
|
||||
|
||||
let extra_pool = pool.clone();
|
||||
// After #5200 lands, this arguably might be moved to the handler of "all blocks notification".
|
||||
ready_poll.lock().trigger(block_number, move || Box::new(extra_pool.validated_pool().ready()));
|
||||
|
||||
if next_action.resubmit {
|
||||
let mut resubmit_transactions = Vec::new();
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ use substrate_test_runtime_client::{
|
||||
};
|
||||
use substrate_test_runtime_transaction_pool::{TestApi, uxt};
|
||||
use crate::revalidation::BACKGROUND_REVALIDATION_INTERVAL;
|
||||
use futures::task::Poll;
|
||||
|
||||
fn pool() -> Pool<TestApi> {
|
||||
Pool::new(Default::default(), TestApi::with_alice_nonce(209).into())
|
||||
@@ -600,5 +601,56 @@ fn fork_aware_finalization() {
|
||||
assert_eq!(stream.next(), Some(TransactionStatus::Finalized(e1.clone())));
|
||||
assert_eq!(stream.next(), None);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ready_set_should_not_resolve_before_block_update() {
|
||||
let (pool, _guard) = maintained_pool();
|
||||
let xt1 = uxt(Alice, 209);
|
||||
block_on(pool.submit_one(&BlockId::number(1), xt1.clone())).expect("1. Imported");
|
||||
|
||||
assert!(pool.ready_at(1).now_or_never().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ready_set_should_resolve_after_block_update() {
|
||||
let (pool, _guard) = maintained_pool();
|
||||
pool.api.push_block(1, vec![]);
|
||||
|
||||
let xt1 = uxt(Alice, 209);
|
||||
|
||||
block_on(pool.submit_one(&BlockId::number(1), xt1.clone())).expect("1. Imported");
|
||||
block_on(pool.maintain(block_event(1)));
|
||||
|
||||
assert!(pool.ready_at(1).now_or_never().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ready_set_should_eventually_resolve_when_block_update_arrives() {
|
||||
let (pool, _guard) = maintained_pool();
|
||||
pool.api.push_block(1, vec![]);
|
||||
|
||||
let xt1 = uxt(Alice, 209);
|
||||
|
||||
block_on(pool.submit_one(&BlockId::number(1), xt1.clone())).expect("1. Imported");
|
||||
|
||||
let noop_waker = futures::task::noop_waker();
|
||||
let mut context = futures::task::Context::from_waker(&noop_waker);
|
||||
|
||||
let mut ready_set_future = pool.ready_at(1);
|
||||
if let Poll::Ready(_) = ready_set_future.poll_unpin(&mut context) {
|
||||
panic!("Ready set should not be ready before block update!");
|
||||
}
|
||||
|
||||
block_on(pool.maintain(block_event(1)));
|
||||
|
||||
match ready_set_future.poll_unpin(&mut context) {
|
||||
Poll::Pending => {
|
||||
panic!("Ready set should become ready after block update!");
|
||||
},
|
||||
Poll::Ready(iterator) => {
|
||||
let data = iterator.collect::<Vec<_>>();
|
||||
assert_eq!(data.len(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user