Files
pezkuwi-subxt/substrate/client/rpc-spec-v2/src/transaction/tests/transaction_broadcast_tests.rs
T
Dcompoze 002d9260f9 Fix spelling mistakes across the whole repository (#3808)
**Update:** Pushed additional changes based on the review comments.

**This pull request fixes various spelling mistakes in this
repository.**

Most of the changes are contained in the first **3** commits:

- `Fix spelling mistakes in comments and docs`

- `Fix spelling mistakes in test names`

- `Fix spelling mistakes in error messages, panic messages, logs and
tracing`

Other source code spelling mistakes are separated into individual
commits for easier reviewing:

- `Fix the spelling of 'authority'`

- `Fix the spelling of 'REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY'`

- `Fix the spelling of 'prev_enqueud_messages'`

- `Fix the spelling of 'endpoint'`

- `Fix the spelling of 'children'`

- `Fix the spelling of 'PenpalSiblingSovereignAccount'`

- `Fix the spelling of 'PenpalSudoAccount'`

- `Fix the spelling of 'insufficient'`

- `Fix the spelling of 'PalletXcmExtrinsicsBenchmark'`

- `Fix the spelling of 'subtracted'`

- `Fix the spelling of 'CandidatePendingAvailability'`

- `Fix the spelling of 'exclusive'`

- `Fix the spelling of 'until'`

- `Fix the spelling of 'discriminator'`

- `Fix the spelling of 'nonexistent'`

- `Fix the spelling of 'subsystem'`

- `Fix the spelling of 'indices'`

- `Fix the spelling of 'committed'`

- `Fix the spelling of 'topology'`

- `Fix the spelling of 'response'`

- `Fix the spelling of 'beneficiary'`

- `Fix the spelling of 'formatted'`

- `Fix the spelling of 'UNKNOWN_PROOF_REQUEST'`

- `Fix the spelling of 'succeeded'`

- `Fix the spelling of 'reopened'`

- `Fix the spelling of 'proposer'`

- `Fix the spelling of 'InstantiationNonce'`

- `Fix the spelling of 'depositor'`

- `Fix the spelling of 'expiration'`

- `Fix the spelling of 'phantom'`

- `Fix the spelling of 'AggregatedKeyValue'`

- `Fix the spelling of 'randomness'`

- `Fix the spelling of 'defendant'`

- `Fix the spelling of 'AquaticMammal'`

- `Fix the spelling of 'transactions'`

- `Fix the spelling of 'PassingTracingSubscriber'`

- `Fix the spelling of 'TxSignaturePayload'`

- `Fix the spelling of 'versioning'`

- `Fix the spelling of 'descendant'`

- `Fix the spelling of 'overridden'`

- `Fix the spelling of 'network'`

Let me know if this structure is adequate.

**Note:** The usage of the words `Merkle`, `Merkelize`, `Merklization`,
`Merkelization`, `Merkleization`, is somewhat inconsistent but I left it
as it is.

~~**Note:** In some places the term `Receival` is used to refer to
message reception, IMO `Reception` is the correct word here, but I left
it as it is.~~

~~**Note:** In some places the term `Overlayed` is used instead of the
more acceptable version `Overlaid` but I also left it as it is.~~

~~**Note:** In some places the term `Applyable` is used instead of the
correct version `Applicable` but I also left it as it is.~~

**Note:** Some usage of British vs American english e.g. `judgement` vs
`judgment`, `initialise` vs `initialize`, `optimise` vs `optimize` etc.
are both present in different places, but I suppose that's
understandable given the number of contributors.

~~**Note:** There is a spelling mistake in `.github/CODEOWNERS` but it
triggers errors in CI when I make changes to it, so I left it as it
is.~~
2024-03-26 13:57:57 +00:00

524 lines
18 KiB
Rust

// This file is part of Substrate.
// 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/>.
use crate::{hex_string, transaction::error::json_rpc_spec};
use assert_matches::assert_matches;
use codec::Encode;
use jsonrpsee::{rpc_params, MethodsError as Error};
use sc_transaction_pool::{Options, PoolLimit};
use sc_transaction_pool_api::{ChainEvent, MaintainedTransactionPool, TransactionPool};
use std::sync::Arc;
use substrate_test_runtime_client::AccountKeyring::*;
use substrate_test_runtime_transaction_pool::uxt;
// Test helpers.
use crate::transaction::tests::{
middleware_pool::{MiddlewarePoolEvent, TxStatusTypeTest},
setup::{setup_api, ALICE_NONCE},
};
#[tokio::test]
async fn tx_broadcast_enters_pool() {
let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) =
setup_api(Default::default());
// Start at block 1.
let block_1_header = api.push_block(1, vec![], true);
let uxt = uxt(Alice, ALICE_NONCE);
let xt = hex_string(&uxt.encode());
let operation_id: String =
tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap();
// Announce block 1 to `transaction_unstable_broadcast`.
client_mock.trigger_import_stream(block_1_header).await;
// Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction pool.
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: xt.clone(),
status: TxStatusTypeTest::Ready
}
);
assert_eq!(1, pool.inner_pool.status().ready);
assert_eq!(uxt.encode().len(), pool.inner_pool.status().ready_bytes);
// Import block 2 with the transaction included.
let block_2_header = api.push_block(2, vec![uxt.clone()], true);
let block_2 = block_2_header.hash();
// Announce block 2 to the pool.
let event = ChainEvent::NewBestBlock { hash: block_2, tree_route: None };
pool.inner_pool.maintain(event).await;
assert_eq!(0, pool.inner_pool.status().ready);
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: xt.clone(),
status: TxStatusTypeTest::InBlock((block_2, 0))
}
);
// The future broadcast awaits for the finalized status to be reached.
// Force the future to exit by calling stop.
let _: () = tx_api
.call("transaction_unstable_stop", rpc_params![&operation_id])
.await
.unwrap();
// Ensure the broadcast future finishes.
let _ = get_next_event!(&mut exec_middleware.recv);
assert_eq!(0, exec_middleware.num_tasks());
}
#[tokio::test]
async fn tx_broadcast_invalid_tx() {
let (_, pool, _, tx_api, mut exec_middleware, _) = setup_api(Default::default());
// Invalid parameters.
let err = tx_api
.call::<_, serde_json::Value>("transaction_unstable_broadcast", [1u8])
.await
.unwrap_err();
assert_matches!(err,
Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid params"
);
assert_eq!(0, pool.status().ready);
// Invalid transaction that cannot be decoded. The broadcast silently exits.
let xt = "0xdeadbeef";
let operation_id: String =
tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap();
assert_eq!(0, pool.status().ready);
// Await the broadcast future to exit.
// Without this we'd be subject to races, where we try to call the stop before the tx is
// dropped.
let _ = get_next_event!(&mut exec_middleware.recv);
assert_eq!(0, exec_middleware.num_tasks());
// The broadcast future was dropped, and the operation is no longer active.
// When the operation is not active, either from the tx being finalized or a
// terminal error; the stop method should return an error.
let err = tx_api
.call::<_, serde_json::Value>("transaction_unstable_stop", rpc_params![&operation_id])
.await
.unwrap_err();
assert_matches!(err,
Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid operation id"
);
}
#[tokio::test]
async fn tx_stop_with_invalid_operation_id() {
let (_, _, _, tx_api, _, _) = setup_api(Default::default());
// Make an invalid stop call.
let err = tx_api
.call::<_, serde_json::Value>("transaction_unstable_stop", ["invalid_operation_id"])
.await
.unwrap_err();
assert_matches!(err,
Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid operation id"
);
}
#[tokio::test]
async fn tx_broadcast_resubmits_future_nonce_tx() {
let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) =
setup_api(Default::default());
// Start at block 1.
let block_1_header = api.push_block(1, vec![], true);
let block_1 = block_1_header.hash();
let current_uxt = uxt(Alice, ALICE_NONCE);
let current_xt = hex_string(&current_uxt.encode());
// This lives in the future.
let future_uxt = uxt(Alice, ALICE_NONCE + 1);
let future_xt = hex_string(&future_uxt.encode());
let future_operation_id: String = tx_api
.call("transaction_unstable_broadcast", rpc_params![&future_xt])
.await
.unwrap();
// Announce block 1 to `transaction_unstable_broadcast`.
client_mock.trigger_import_stream(block_1_header).await;
// Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction pool.
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: future_xt.clone(),
status: TxStatusTypeTest::Future
}
);
let event = ChainEvent::NewBestBlock { hash: block_1, tree_route: None };
pool.inner_pool.maintain(event).await;
assert_eq!(0, pool.inner_pool.status().ready);
// Ensure the tx is in the future.
assert_eq!(1, pool.inner_pool.status().future);
let block_2_header = api.push_block(2, vec![], true);
let block_2 = block_2_header.hash();
let operation_id: String = tx_api
.call("transaction_unstable_broadcast", rpc_params![&current_xt])
.await
.unwrap();
assert_ne!(future_operation_id, operation_id);
// Announce block 2 to `transaction_unstable_broadcast`.
client_mock.trigger_import_stream(block_2_header).await;
// Collect the events of both transactions.
let events = get_next_tx_events!(&mut pool_middleware, 2);
// Transactions entered the ready queue.
assert_eq!(events.get(&current_xt).unwrap(), &vec![TxStatusTypeTest::Ready]);
assert_eq!(events.get(&future_xt).unwrap(), &vec![TxStatusTypeTest::Ready]);
let event = ChainEvent::NewBestBlock { hash: block_2, tree_route: None };
pool.inner_pool.maintain(event).await;
assert_eq!(2, pool.inner_pool.status().ready);
assert_eq!(0, pool.inner_pool.status().future);
// Finalize transactions.
let block_3_header = api.push_block(3, vec![current_uxt, future_uxt], true);
let block_3 = block_3_header.hash();
client_mock.trigger_import_stream(block_3_header).await;
let event = ChainEvent::Finalized { hash: block_3, tree_route: Arc::from(vec![]) };
pool.inner_pool.maintain(event).await;
assert_eq!(0, pool.inner_pool.status().ready);
assert_eq!(0, pool.inner_pool.status().future);
let events = get_next_tx_events!(&mut pool_middleware, 4);
assert_eq!(
events.get(&current_xt).unwrap(),
&vec![TxStatusTypeTest::InBlock((block_3, 0)), TxStatusTypeTest::Finalized((block_3, 0))]
);
assert_eq!(
events.get(&future_xt).unwrap(),
&vec![TxStatusTypeTest::InBlock((block_3, 1)), TxStatusTypeTest::Finalized((block_3, 1))]
);
// Both broadcast futures must exit.
let _ = get_next_event!(&mut exec_middleware.recv);
let _ = get_next_event!(&mut exec_middleware.recv);
assert_eq!(0, exec_middleware.num_tasks());
}
/// This test is similar to `tx_broadcast_enters_pool`
/// However the last block is announced as finalized to force the
/// broadcast future to exit before the `stop` is called.
#[tokio::test]
async fn tx_broadcast_stop_after_broadcast_finishes() {
let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) =
setup_api(Default::default());
// Start at block 1.
let block_1_header = api.push_block(1, vec![], true);
let uxt = uxt(Alice, ALICE_NONCE);
let xt = hex_string(&uxt.encode());
let operation_id: String =
tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap();
// Announce block 1 to `transaction_unstable_broadcast`.
client_mock.trigger_import_stream(block_1_header).await;
// Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction
// pool.inner_pool.
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: xt.clone(),
status: TxStatusTypeTest::Ready
}
);
assert_eq!(1, pool.inner_pool.status().ready);
assert_eq!(uxt.encode().len(), pool.inner_pool.status().ready_bytes);
// Import block 2 with the transaction included.
let block_2_header = api.push_block(2, vec![uxt.clone()], true);
let block_2 = block_2_header.hash();
// Announce block 2 to the pool.inner_pool.
let event = ChainEvent::Finalized { hash: block_2, tree_route: Arc::from(vec![]) };
pool.inner_pool.maintain(event).await;
assert_eq!(0, pool.inner_pool.status().ready);
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: xt.clone(),
status: TxStatusTypeTest::InBlock((block_2, 0))
}
);
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: xt.clone(),
status: TxStatusTypeTest::Finalized((block_2, 0))
}
);
// Ensure the broadcast future terminated properly.
let _ = get_next_event!(&mut exec_middleware.recv);
assert_eq!(0, exec_middleware.num_tasks());
// The operation ID is no longer valid, check that the broadcast future
// cleared out the inner state of the operation.
let err = tx_api
.call::<_, serde_json::Value>("transaction_unstable_stop", rpc_params![&operation_id])
.await
.unwrap_err();
assert_matches!(err,
Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid operation id"
);
}
#[tokio::test]
async fn tx_broadcast_resubmits_invalid_tx() {
let limits = PoolLimit { count: 8192, total_bytes: 20 * 1024 * 1024 };
let options = Options {
ready: limits.clone(),
future: limits,
reject_future_transactions: false,
// This ensures that a transaction is not banned.
ban_time: std::time::Duration::ZERO,
};
let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) =
setup_api(options);
let uxt = uxt(Alice, ALICE_NONCE);
let xt = hex_string(&uxt.encode());
let _operation_id: String =
tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap();
let block_1_header = api.push_block(1, vec![], true);
let block_1 = block_1_header.hash();
// Announce block 1 to `transaction_unstable_broadcast`.
client_mock.trigger_import_stream(block_1_header).await;
// Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction pool.
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: xt.clone(),
status: TxStatusTypeTest::Ready,
}
);
assert_eq!(1, pool.inner_pool.status().ready);
assert_eq!(uxt.encode().len(), pool.inner_pool.status().ready_bytes);
// Mark the transaction as invalid from the API, causing a temporary ban.
api.add_invalid(&uxt);
// Push an event to the pool to ensure the transaction is excluded.
let event = ChainEvent::NewBestBlock { hash: block_1, tree_route: None };
pool.inner_pool.maintain(event).await;
assert_eq!(1, pool.inner_pool.status().ready);
// Ensure the `transaction_unstable_broadcast` is aware of the invalid transaction.
let event = get_next_event!(&mut pool_middleware);
// Because we have received an `Invalid` status, we try to broadcast the transaction with the
// next announced block.
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: xt.clone(),
status: TxStatusTypeTest::Invalid
}
);
// Import block 2.
let block_2_header = api.push_block(2, vec![], true);
client_mock.trigger_import_stream(block_2_header).await;
// Ensure we propagate the temporary ban error to `submit_and_watch`.
// This ensures we'll loop again with the next announced block and try to resubmit the
// transaction. The transaction remains temporarily banned until the pool is maintained.
let event = get_next_event!(&mut pool_middleware);
assert_matches!(event, MiddlewarePoolEvent::PoolError { transaction, err } if transaction == xt && err.contains("Transaction temporarily Banned"));
// Import block 3.
let block_3_header = api.push_block(3, vec![], true);
let block_3 = block_3_header.hash();
// Remove the invalid transaction from the pool to allow it to pass through.
api.remove_invalid(&uxt);
let event = ChainEvent::NewBestBlock { hash: block_3, tree_route: None };
// We have to maintain the pool to ensure the transaction is no longer invalid.
// This clears out the banned transactions.
pool.inner_pool.maintain(event).await;
assert_eq!(0, pool.inner_pool.status().ready);
// Announce block to `transaction_unstable_broadcast`.
client_mock.trigger_import_stream(block_3_header).await;
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: xt.clone(),
status: TxStatusTypeTest::Ready,
}
);
assert_eq!(1, pool.inner_pool.status().ready);
let block_4_header = api.push_block(4, vec![uxt], true);
let block_4 = block_4_header.hash();
let event = ChainEvent::Finalized { hash: block_4, tree_route: Arc::from(vec![]) };
pool.inner_pool.maintain(event).await;
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: xt.clone(),
status: TxStatusTypeTest::InBlock((block_4, 0)),
}
);
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: xt.clone(),
status: TxStatusTypeTest::Finalized((block_4, 0)),
}
);
// Ensure the broadcast future terminated properly.
let _ = get_next_event!(&mut exec_middleware.recv);
assert_eq!(0, exec_middleware.num_tasks());
}
/// This is similar to `tx_broadcast_resubmits_invalid_tx`.
/// However, it forces the tx to be resubmitted because of the pool
/// limits. Which is a different code path than the invalid tx.
#[tokio::test]
async fn tx_broadcast_resubmits_dropped_tx() {
let limits = PoolLimit { count: 1, total_bytes: 1000 };
let options = Options {
ready: limits.clone(),
future: limits,
reject_future_transactions: false,
// This ensures that a transaction is not banned.
ban_time: std::time::Duration::ZERO,
};
let (api, pool, client_mock, tx_api, _, mut pool_middleware) = setup_api(options);
let current_uxt = uxt(Alice, ALICE_NONCE);
let current_xt = hex_string(&current_uxt.encode());
// This lives in the future.
let future_uxt = uxt(Alice, ALICE_NONCE + 1);
let future_xt = hex_string(&future_uxt.encode());
// By default the `validate_transaction` mock uses priority 1 for
// transactions. Bump the priority to ensure other transactions
// are immediately dropped.
api.set_priority(&current_uxt, 10);
let current_operation_id: String = tx_api
.call("transaction_unstable_broadcast", rpc_params![&current_xt])
.await
.unwrap();
// Announce block 1 to `transaction_unstable_broadcast`.
let block_1_header = api.push_block(1, vec![], true);
let event =
ChainEvent::Finalized { hash: block_1_header.hash(), tree_route: Arc::from(vec![]) };
pool.inner_pool.maintain(event).await;
client_mock.trigger_import_stream(block_1_header).await;
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::TransactionStatus {
transaction: current_xt.clone(),
status: TxStatusTypeTest::Ready,
}
);
assert_eq!(1, pool.inner_pool.status().ready);
// The future tx has priority 2, smaller than the current 10.
api.set_priority(&future_uxt, 2);
let future_operation_id: String = tx_api
.call("transaction_unstable_broadcast", rpc_params![&future_xt])
.await
.unwrap();
assert_ne!(current_operation_id, future_operation_id);
let block_2_header = api.push_block(2, vec![], true);
let event =
ChainEvent::Finalized { hash: block_2_header.hash(), tree_route: Arc::from(vec![]) };
pool.inner_pool.maintain(event).await;
client_mock.trigger_import_stream(block_2_header).await;
// We must have at most 1 transaction in the pool, as per limits above.
assert_eq!(1, pool.inner_pool.status().ready);
let event = get_next_event!(&mut pool_middleware);
assert_eq!(
event,
MiddlewarePoolEvent::PoolError {
transaction: future_xt.clone(),
err: "Transaction couldn't enter the pool because of the limit".into()
}
);
let block_3_header = api.push_block(3, vec![current_uxt], true);
let event =
ChainEvent::Finalized { hash: block_3_header.hash(), tree_route: Arc::from(vec![]) };
pool.inner_pool.maintain(event).await;
client_mock.trigger_import_stream(block_3_header.clone()).await;
// The first tx is in a finalized block; the future tx must enter the pool.
let events = get_next_tx_events!(&mut pool_middleware, 3);
assert_eq!(
events.get(&current_xt).unwrap(),
&vec![
TxStatusTypeTest::InBlock((block_3_header.hash(), 0)),
TxStatusTypeTest::Finalized((block_3_header.hash(), 0))
]
);
// The dropped transaction was resubmitted.
assert_eq!(events.get(&future_xt).unwrap(), &vec![TxStatusTypeTest::Ready]);
}