Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 452726a

Browse files
tomusdrwgavofyork
authored andcommitted
Time-based transaction banning (#594)
* Allow replacing transactions. * Clear old transactions and ban them temporarily. * Move to a separate module and add some tests. * Add bound to banned transactions. * Remove unnecessary block and double PoolRotator.
1 parent add8729 commit 452726a

File tree

3 files changed

+227
-11
lines changed

3 files changed

+227
-11
lines changed

polkadot/transaction-pool/src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ error_chain! {
5555
description("Unrecognised address in extrinsic"),
5656
display("Unrecognised address in extrinsic: {}", who),
5757
}
58+
/// Temporarily banned
59+
TemporarilyBanned {
60+
description("Extrinsic is temporarily banned"),
61+
display("Extrinsic is temporarily banned"),
62+
}
5863
}
5964
}
6065

polkadot/transaction-pool/src/lib.rs

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ extern crate error_chain;
3535
extern crate log;
3636

3737
mod error;
38+
mod rotator;
3839

3940
use std::{
4041
cmp::Ordering,
4142
collections::HashMap,
4243
ops::Deref,
4344
sync::Arc,
45+
time::{Duration, Instant},
4446
};
4547

4648
use codec::{Decode, Encode};
@@ -53,12 +55,18 @@ use extrinsic_pool::{
5355
};
5456
use polkadot_api::PolkadotApi;
5557
use primitives::{AccountId, BlockId, Hash, Index, UncheckedExtrinsic as FutureProofUncheckedExtrinsic};
58+
use rotator::PoolRotator;
5659
use runtime::{Address, UncheckedExtrinsic};
5760
use substrate_runtime_primitives::traits::{Bounded, Checkable, Hash as HashT, BlakeTwo256};
5861

5962
pub use extrinsic_pool::txpool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps};
6063
pub use error::{Error, ErrorKind, Result};
6164

65+
/// Maximum time the transaction will be kept in the pool.
66+
///
67+
/// Transactions that don't get included within the limit are removed from the pool.
68+
const POOL_TIME: Duration = Duration::from_secs(60 * 5);
69+
6270
/// Type alias for convenience.
6371
pub type CheckedExtrinsic = <UncheckedExtrinsic as Checkable<fn(Address) -> std::result::Result<AccountId, &'static str>>>::Checked;
6472

@@ -70,6 +78,7 @@ pub struct VerifiedTransaction {
7078
sender: Option<AccountId>,
7179
hash: Hash,
7280
encoded_size: usize,
81+
valid_till: Instant,
7382
}
7483

7584
impl VerifiedTransaction {
@@ -148,8 +157,8 @@ impl txpool::Scoring<VerifiedTransaction> for Scoring {
148157
if old.is_fully_verified() {
149158
assert!(new.is_fully_verified(), "Scoring::choose called with transactions from different senders");
150159
if old.index() == new.index() {
151-
// TODO [ToDr] Do we allow replacement? If yes then it should be Choice::ReplaceOld
152-
return Choice::RejectNew;
160+
// Allow replacing transactions so that we never fail to import to the pool.
161+
return Choice::ReplaceOld;
153162
}
154163
}
155164

@@ -187,17 +196,21 @@ impl txpool::Scoring<VerifiedTransaction> for Scoring {
187196
pub struct Ready<'a, A: 'a + PolkadotApi> {
188197
at_block: BlockId,
189198
api: &'a A,
199+
rotator: &'a PoolRotator,
190200
known_nonces: HashMap<AccountId, ::primitives::Index>,
201+
now: Instant,
191202
}
192203

193204
impl<'a, A: 'a + PolkadotApi> Ready<'a, A> {
194205
/// Create a new readiness evaluator at the given block. Requires that
195206
/// the ID has already been checked for local corresponding and available state.
196-
fn create(at: BlockId, api: &'a A) -> Self {
207+
fn create(at: BlockId, api: &'a A, rotator: &'a PoolRotator) -> Self {
197208
Ready {
198209
at_block: at,
199210
api,
200-
known_nonces: HashMap::new(),
211+
rotator,
212+
known_nonces: Default::default(),
213+
now: Instant::now(),
201214
}
202215
}
203216
}
@@ -207,7 +220,9 @@ impl<'a, T: 'a + PolkadotApi> Clone for Ready<'a, T> {
207220
Ready {
208221
at_block: self.at_block.clone(),
209222
api: self.api,
223+
rotator: self.rotator,
210224
known_nonces: self.known_nonces.clone(),
225+
now: self.now.clone(),
211226
}
212227
}
213228
}
@@ -230,6 +245,11 @@ impl<'a, A: 'a + PolkadotApi> txpool::Ready<VerifiedTransaction> for Ready<'a, A
230245

231246
trace!(target: "transaction-pool", "Next index for sender is {}; xt index is {}", next_index, xt.original.extrinsic.index);
232247

248+
if self.rotator.ban_if_stale(&self.now, xt) {
249+
debug!(target: "transaction-pool", "[{}] Banning as stale.", xt.hash);
250+
return Readiness::Stale;
251+
}
252+
233253
let result = match xt.original.extrinsic.index.cmp(&next_index) {
234254
// TODO: this won't work perfectly since accounts can now be killed, returning the nonce
235255
// to zero.
@@ -251,11 +271,12 @@ impl<'a, A: 'a + PolkadotApi> txpool::Ready<VerifiedTransaction> for Ready<'a, A
251271

252272
pub struct Verifier<'a, A: 'a> {
253273
api: &'a A,
274+
rotator: &'a PoolRotator,
254275
at_block: BlockId,
255276
}
256277

257278
impl<'a, A> Verifier<'a, A> where
258-
A: 'a + PolkadotApi,
279+
A: 'a + PolkadotApi,
259280
{
260281
const NO_ACCOUNT: &'static str = "Account not found.";
261282

@@ -289,6 +310,11 @@ impl<'a, A> txpool::Verifier<UncheckedExtrinsic> for Verifier<'a, A> where
289310

290311
debug!(target: "transaction-pool", "Transaction submitted: {}", ::substrate_primitives::hexdisplay::HexDisplay::from(&encoded));
291312

313+
if self.rotator.is_banned(&hash) {
314+
debug!(target: "transaction-pool", "[{}] Transaction is temporarily banned", hash);
315+
bail!(ErrorKind::TemporarilyBanned);
316+
}
317+
292318
let inner = match uxt.clone().check_with(|a| self.lookup(a)) {
293319
Ok(xt) => Some(xt),
294320
// keep the transaction around in the future pool and attempt to promote it later.
@@ -298,17 +324,18 @@ impl<'a, A> txpool::Verifier<UncheckedExtrinsic> for Verifier<'a, A> where
298324
let sender = inner.as_ref().map(|x| x.signed.clone());
299325

300326
if encoded_size < 1024 {
301-
debug!(target: "transaction-pool", "Transaction verified: {} => {:?}", hash, uxt);
327+
debug!(target: "transaction-pool", "[{}] Transaction verified: {:?}", hash, uxt);
302328
} else {
303-
debug!(target: "transaction-pool", "Transaction verified: {} ({} bytes is too large to display)", hash, encoded_size);
329+
debug!(target: "transaction-pool", "[{}] Transaction verified: ({} bytes is too large to display)", hash, encoded_size);
304330
}
305331

306332
Ok(VerifiedTransaction {
307333
original: uxt,
308334
inner,
309335
sender,
310336
hash,
311-
encoded_size
337+
encoded_size,
338+
valid_till: Instant::now() + POOL_TIME,
312339
})
313340
}
314341
}
@@ -319,16 +346,18 @@ impl<'a, A> txpool::Verifier<UncheckedExtrinsic> for Verifier<'a, A> where
319346
pub struct TransactionPool<A> {
320347
inner: Pool<Hash, VerifiedTransaction, Scoring, Error>,
321348
api: Arc<A>,
349+
rotator: PoolRotator,
322350
}
323351

324352
impl<A> TransactionPool<A> where
325-
A: PolkadotApi,
353+
A: PolkadotApi,
326354
{
327355
/// Create a new transaction pool.
328356
pub fn new(options: Options, api: Arc<A>) -> Self {
329357
TransactionPool {
330358
inner: Pool::new(options, Scoring),
331359
api,
360+
rotator: Default::default(),
332361
}
333362
}
334363

@@ -337,6 +366,7 @@ impl<A> TransactionPool<A> where
337366
let verifier = Verifier {
338367
api: &*self.api,
339368
at_block: block,
369+
rotator: &self.rotator,
340370
};
341371
self.inner.submit(verifier, vec![uxt]).map(|mut v| v.swap_remove(0))
342372
}
@@ -347,6 +377,7 @@ impl<A> TransactionPool<A> where
347377
let verifier = Verifier {
348378
api: &*self.api,
349379
at_block: block,
380+
rotator: &self.rotator,
350381
};
351382

352383
self.inner.submit(verifier, to_reverify.into_iter().map(|tx| tx.original.clone()))?;
@@ -372,15 +403,18 @@ impl<A> TransactionPool<A> where
372403

373404
/// Cull old transactions from the queue.
374405
pub fn cull(&self, block: BlockId) -> Result<usize> {
375-
let ready = Ready::create(block, &*self.api);
406+
self.rotator.clear_timeouts(&Instant::now());
407+
408+
let ready = Ready::create(block, &*self.api, &self.rotator);
376409
Ok(self.inner.cull(None, ready))
377410
}
378411

379412
/// Cull transactions from the queue and then compute the pending set.
380413
pub fn cull_and_get_pending<F, T>(&self, block: BlockId, f: F) -> Result<T> where
381414
F: FnOnce(txpool::PendingIterator<VerifiedTransaction, Ready<A>, Scoring, Listener<Hash>>) -> T,
382415
{
383-
let ready = Ready::create(block, &*self.api);
416+
self.rotator.clear_timeouts(&Instant::now());
417+
let ready = Ready::create(block, &*self.api, &self.rotator);
384418
self.inner.cull(None, ready.clone());
385419
Ok(self.inner.pending(ready, f))
386420
}
@@ -424,6 +458,7 @@ impl<A> ExtrinsicPool<FutureProofUncheckedExtrinsic, BlockId, Hash> for Transact
424458

425459
let verifier = Verifier {
426460
api: &*self.api,
461+
rotator: &self.rotator,
427462
at_block: block,
428463
};
429464

0 commit comments

Comments
 (0)