rpc-v2/tx/tests: Add transaction broadcast tests and check propagated tx status (#3193)

This PR adds tests for the `transaction_broadcast` method.


The testing needs to coordinate the following components:
- The `TestApi` marks transactions as invalid and implements
`ChainApi::validate_transaction`
- this is what dictates if a transaction is valid or not and is called
from within the `BasicPool`
- The `BasicPool` which maintains the transactions and implements
`submit_and_watch` needed by the tx broadcast to submit the transaction
- The status of the transaction pool is exposed by mocking the BasicPool
- The `ChainHeadMockClient` which mocks the
`BlockchainEvents::import_notification_stream` needed by the tx
broadcast to know to which blocks the transaction is submitted

The following changes have been added to the substrate testing to
accommodate this:
- `TestApi` gets ` remove_invalid`, counterpart to `add_invalid` to
ensure an invalid transaction can become valid again; as well as a
priority setter for extrinsics
- `BasicPool` test constructor is extended with options for the
`PoolRotator`
- this mechanism is needed because transactions are banned for 30mins
(default) after they are declared invalid
  - testing bypasses this by providing a `Duration::ZERO`

### Testing Scenarios

- Capture the status of the transaction as it is normally broadcasted
- `transaction_stop` is valid while the transaction is in progress
- A future transaction is handled when the dependencies are completed
- Try to resubmit the transaction at a later block (currently invalid)
- An invalid transaction status is propagated; the transaction is marked
as temporarily banned; then the ban expires and transaction is
resubmitted
  
This builds on top of:
https://github.com/paritytech/polkadot-sdk/pull/3079
Part of: https://github.com/paritytech/polkadot-sdk/issues/3084

cc @paritytech/subxt-team

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
Alexandru Vasile
2024-02-28 11:43:58 +02:00
committed by GitHub
parent 12ce4f7d04
commit f1b2189e83
10 changed files with 985 additions and 244 deletions
@@ -81,6 +81,7 @@ pub struct ChainState {
pub block_by_hash: HashMap<Hash, Block>,
pub nonces: HashMap<AccountId, u64>,
pub invalid_hashes: HashSet<Hash>,
pub priorities: HashMap<Hash, u64>,
}
/// Test Api for transaction pool.
@@ -214,6 +215,22 @@ impl TestApi {
self.chain.write().invalid_hashes.insert(Self::hash_and_length_inner(xts).0);
}
/// Remove a transaction that was previously declared as invalid via `[Self::add_invalid]`.
///
/// Next time transaction pool will try to validate this
/// extrinsic, api will succeed.
pub fn remove_invalid(&self, xts: &Extrinsic) {
self.chain.write().invalid_hashes.remove(&Self::hash_and_length_inner(xts).0);
}
/// Set a transaction priority.
pub fn set_priority(&self, xts: &Extrinsic, priority: u64) {
self.chain
.write()
.priorities
.insert(Self::hash_and_length_inner(xts).0, priority);
}
/// Query validation requests received.
pub fn validation_requests(&self) -> Vec<Extrinsic> {
self.validation_requests.read().clone()
@@ -300,8 +317,14 @@ impl ChainApi for TestApi {
return ready(Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(0)))))
}
let mut validity =
ValidTransaction { priority: 1, requires, provides, longevity: 64, propagate: true };
let priority = self.chain.read().priorities.get(&self.hash_and_length(&uxt).0).cloned();
let mut validity = ValidTransaction {
priority: priority.unwrap_or(1),
requires,
provides,
longevity: 64,
propagate: true,
};
(self.valid_modifier.read())(&mut validity);