Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix tx-pool remove expired #3894

Merged
Merged
5 changes: 2 additions & 3 deletions resource/ckb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,8 @@ enable_deprecated_rpc = false # {{
# }}

[tx_pool]
max_mem_size = 20_000_000 # 20mb
max_cycles = 200_000_000_000
min_fee_rate = 1_000 # shannons/KB
max_tx_pool_size = 180_000_000 # 180mb
min_fee_rate = 1_000 # Here fee_rate are calculated directly using size in units of shannons/KB
max_tx_verify_cycles = 70_000_000
max_ancestors_count = 25

Expand Down
21 changes: 17 additions & 4 deletions rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4435,14 +4435,16 @@ Response
"jsonrpc": "2.0",
"result": {
"last_txs_updated_at": "0x0",
"min_fee_rate": "0x0",
"min_fee_rate": "0x3e8",
"max_tx_pool_size": "0xaba9500",
"orphan": "0x0",
"pending": "0x1",
"proposed": "0x0",
"tip_hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
"tip_number": "0x400",
"total_tx_cycles": "0x219",
"total_tx_size": "0x112"
"total_tx_size": "0x112",
"tx_size_limit": "0x7d000"
}
}
```
Expand Down Expand Up @@ -4986,6 +4988,10 @@ For example, a cellbase transaction is not allowed in `send_transaction` RPC.

(-1109): The transaction is expired from tx-pool after `expiry_hours`.

### Error `PoolRejectedTransactionBySizeLimit`

(-1110): The transaction exceeded maximum size limit.

### Error `Indexer`

(-1200): The indexer error.
Expand Down Expand Up @@ -6329,14 +6335,15 @@ TX reject message

`PoolTransactionReject` is a JSON object with following fields.

* `type`: `"LowFeeRate" | "ExceededMaximumAncestorsCount" | "Full" | "Duplicated" | "Malformed" | "DeclaredWrongCycles" | "Resolve" | "Verification" | "Expiry"` - Reject type.
* `type`: `"LowFeeRate" | "ExceededMaximumAncestorsCount" | "ExceededTransactionSizeLimit" | "Full" | "Duplicated" | "Malformed" | "DeclaredWrongCycles" | "Resolve" | "Verification" | "Expiry"` - Reject type.
* `description`: `string` - Detailed description about why the transaction is rejected.

Different reject types:

* `LowFeeRate`: Transaction fee lower than config
* `ExceededMaximumAncestorsCount`: Transaction exceeded maximum ancestors count limit
* `Full`: Transaction pool exceeded maximum size or cycles limit,
* `ExceededTransactionSizeLimit`: Transaction exceeded maximum size limit
* `Full`: Transaction are replaced because the pool is full
* `Duplicated`: Transaction already exist in transaction_pool
* `Malformed`: Malformed transaction
* `DeclaredWrongCycles`: Declared wrong cycles
Expand Down Expand Up @@ -6911,6 +6918,12 @@ Transaction pool information.

* `last_txs_updated_at`: [`Timestamp`](#type-timestamp) - Last updated time. This is the Unix timestamp in milliseconds.

* `tx_size_limit`: [`Uint64`](#type-uint64) - Limiting transactions to tx_size_limit

Transactions with a large size close to the block size limit may not be packaged, because the block header and cellbase are occupied, so the tx-pool is limited to accepting transaction up to tx_size_limit.

* `max_tx_pool_size`: [`Uint64`](#type-uint64) - Total limit on the size of transactions in the tx-pool


### Type `TxStatus`

Expand Down
7 changes: 6 additions & 1 deletion rpc/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ pub enum RPCError {
PoolRejectedMalformedTransaction = -1108,
/// (-1109): The transaction is expired from tx-pool after `expiry_hours`.
TransactionExpired = -1109,
/// (-1110): The transaction exceeded maximum size limit.
PoolRejectedTransactionBySizeLimit = -1110,
/// (-1200): The indexer error.
Indexer = -1200,
}
Expand Down Expand Up @@ -165,12 +167,15 @@ impl RPCError {
Reject::ExceededMaximumAncestorsCount => {
RPCError::PoolRejectedTransactionByMaxAncestorsCountLimit
}
Reject::Full(_, _) => RPCError::PoolIsFull,
Reject::Full(_) => RPCError::PoolIsFull,
Reject::Duplicated(_) => RPCError::PoolRejectedDuplicatedTransaction,
Reject::Malformed(_) => RPCError::PoolRejectedMalformedTransaction,
Reject::DeclaredWrongCycles(..) => RPCError::PoolRejectedMalformedTransaction,
Reject::Resolve(_) => RPCError::TransactionFailedToResolve,
Reject::Verification(_) => RPCError::TransactionFailedToVerify,
Reject::ExceededTransactionSizeLimit(_, _) => {
RPCError::PoolRejectedTransactionBySizeLimit
}
Reject::Expiry(_) => RPCError::TransactionExpired,
};
RPCError::custom_with_error(code, reject)
Expand Down
21 changes: 5 additions & 16 deletions rpc/src/module/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,16 @@ pub trait PoolRpc {
/// "jsonrpc": "2.0",
/// "result": {
/// "last_txs_updated_at": "0x0",
/// "min_fee_rate": "0x0",
/// "min_fee_rate": "0x3e8",
/// "max_tx_pool_size": "0xaba9500",
/// "orphan": "0x0",
/// "pending": "0x1",
/// "proposed": "0x0",
/// "tip_hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
/// "tip_number": "0x400",
/// "total_tx_cycles": "0x219",
/// "total_tx_size": "0x112"
/// "total_tx_size": "0x112",
/// "tx_size_limit": "0x7d000"
/// }
/// }
/// ```
Expand Down Expand Up @@ -276,15 +278,13 @@ pub trait PoolRpc {

pub(crate) struct PoolRpcImpl {
shared: Shared,
min_fee_rate: core::FeeRate,
well_known_lock_scripts: Vec<packed::Script>,
well_known_type_scripts: Vec<packed::Script>,
}

impl PoolRpcImpl {
pub fn new(
shared: Shared,
min_fee_rate: core::FeeRate,
mut extra_well_known_lock_scripts: Vec<packed::Script>,
mut extra_well_known_type_scripts: Vec<packed::Script>,
) -> PoolRpcImpl {
Expand All @@ -298,7 +298,6 @@ impl PoolRpcImpl {

PoolRpcImpl {
shared,
min_fee_rate,
well_known_lock_scripts,
well_known_type_scripts,
}
Expand Down Expand Up @@ -449,17 +448,7 @@ impl PoolRpc for PoolRpcImpl {

let tx_pool_info = get_tx_pool_info.unwrap();

Ok(TxPoolInfo {
tip_hash: tx_pool_info.tip_hash.unpack(),
tip_number: tx_pool_info.tip_number.into(),
pending: (tx_pool_info.pending_size as u64).into(),
proposed: (tx_pool_info.proposed_size as u64).into(),
orphan: (tx_pool_info.orphan_size as u64).into(),
total_tx_size: (tx_pool_info.total_tx_size as u64).into(),
total_tx_cycles: tx_pool_info.total_tx_cycles.into(),
min_fee_rate: self.min_fee_rate.as_u64().into(),
last_txs_updated_at: tx_pool_info.last_txs_updated_at.into(),
})
Ok(tx_pool_info.into())
}

fn clear_tx_pool(&self) -> Result<()> {
Expand Down
4 changes: 1 addition & 3 deletions rpc/src/service_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use ckb_network_alert::{notifier::Notifier as AlertNotifier, verifier::Verifier
use ckb_pow::Pow;
use ckb_shared::shared::Shared;
use ckb_sync::SyncShared;
use ckb_types::{core::FeeRate, packed::Script};
use ckb_types::packed::Script;
use ckb_util::Mutex;
use jsonrpc_core::RemoteProcedure;
use std::sync::Arc;
Expand Down Expand Up @@ -52,13 +52,11 @@ impl<'a> ServiceBuilder<'a> {
pub fn enable_pool(
mut self,
shared: Shared,
min_fee_rate: FeeRate,
extra_well_known_lock_scripts: Vec<Script>,
extra_well_known_type_scripts: Vec<Script>,
) -> Self {
let rpc_methods = PoolRpcImpl::new(
shared,
min_fee_rate,
extra_well_known_lock_scripts,
extra_well_known_type_scripts,
)
Expand Down
15 changes: 12 additions & 3 deletions rpc/src/tests/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn test_submit_transaction_error() {
let min_fee_rate = FeeRate::from_u64(500);
let reject = Reject::LowFeeRate(min_fee_rate, 100, 50);
assert_eq!(
"PoolRejectedTransactionByMinFeeRate: The min fee rate is 500 shannons/KB, so the transaction fee should be 100 shannons at least, but only got 50",
"PoolRejectedTransactionByMinFeeRate: The min fee rate is 500 shannons/KW, so the transaction fee should be 100 shannons at least, but only got 50",
RPCError::from_submit_transaction_reject(&reject).message
);

Expand All @@ -32,9 +32,12 @@ fn test_submit_transaction_error() {
RPCError::from_submit_transaction_reject(&reject).message
);

let reject = Reject::Full("size".to_owned(), 10);
let reject = Reject::Full(format!(
"the fee_rate for this transaction is: {}",
FeeRate::from_u64(500)
));
assert_eq!(
"PoolIsFull: Transaction pool exceeded maximum size limit(10), try send it later",
"PoolIsFull: Transaction are replaced because the pool is full, the fee_rate for this transaction is: 500 shannons/KW",
RPCError::from_submit_transaction_reject(&reject).message
);

Expand All @@ -49,6 +52,12 @@ fn test_submit_transaction_error() {
"PoolRejectedMalformedTransaction: Malformed cellbase like transaction",
RPCError::from_submit_transaction_reject(&reject).message
);

let reject = Reject::ExceededTransactionSizeLimit(10, 9);
assert_eq!(
"PoolRejectedTransactionBySizeLimit: Transaction size 10 exceeded maximum limit 9",
RPCError::from_submit_transaction_reject(&reject).message
);
}

#[test]
Expand Down
6 changes: 3 additions & 3 deletions rpc/src/tests/examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ use ckb_sync::SyncShared;
use ckb_test_chain_utils::always_success_cell;
use ckb_types::{
core::{
capacity_bytes, BlockBuilder, Capacity, EpochNumberWithFraction, FeeRate, Ratio,
TransactionBuilder, TransactionView,
capacity_bytes, BlockBuilder, Capacity, EpochNumberWithFraction, Ratio, TransactionBuilder,
TransactionView,
},
h256,
packed::{AlertBuilder, CellDep, CellInput, CellOutputBuilder, OutPoint, RawAlertBuilder},
Expand Down Expand Up @@ -244,7 +244,7 @@ fn setup_rpc_test_suite(height: u64) -> RpcTestSuite {

let builder = ServiceBuilder::new(&rpc_config)
.enable_chain(shared.clone())
.enable_pool(shared.clone(), FeeRate::zero(), vec![], vec![])
.enable_pool(shared.clone(), vec![], vec![])
.enable_miner(
shared.clone(),
network_controller.clone(),
Expand Down
6 changes: 3 additions & 3 deletions rpc/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use ckb_test_chain_utils::{
};
use ckb_types::{
core::{
cell::resolve_transaction, BlockBuilder, BlockView, FeeRate, HeaderView,
TransactionBuilder, TransactionView,
cell::resolve_transaction, BlockBuilder, BlockView, HeaderView, TransactionBuilder,
TransactionView,
},
h256,
packed::{CellInput, OutPoint},
Expand Down Expand Up @@ -290,7 +290,7 @@ fn setup() -> RpcTestSuite {

let builder = ServiceBuilder::new(&rpc_config)
.enable_chain(shared.clone())
.enable_pool(shared.clone(), FeeRate::zero(), vec![], vec![])
.enable_pool(shared.clone(), vec![], vec![])
.enable_miner(
shared.clone(),
network_controller.clone(),
Expand Down
1 change: 0 additions & 1 deletion test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,6 @@ fn all_specs() -> Vec<Box<dyn Spec>> {
Box::new(CompactBlockRelayLessThenSharedBestKnown),
Box::new(InvalidLocatorSize),
Box::new(SizeLimit),
Box::new(CyclesLimit),
Box::new(SendDefectedBinary::new(
"send_defected_binary_reject_known_bugs",
true,
Expand Down
2 changes: 1 addition & 1 deletion test/src/specs/relay/transaction_relay_low_fee_rate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl Spec for TransactionRelayLowFeeRate {
node1,
0,
10,
"reject tx The min fee rate is 1000 shannons/KB, so the transaction fee should be 242 shannons at least, but only got 0"
"reject tx The min fee rate is 1000 shannons/KW, so the transaction fee should be 242 shannons at least, but only got 0"
)
.is_some());
}
Expand Down
78 changes: 9 additions & 69 deletions test/src/specs/tx_pool/limit.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use crate::utils::assert_send_transaction_fail;
use crate::{Node, Spec};

use ckb_logger::info;
use ckb_types::core::FeeRate;
use std::{thread::sleep, time::Duration};

pub struct SizeLimit;

const MAX_CYCLES_FOR_SIZE_LIMIT: u64 = 200_000_000_000;
const MAX_MEM_SIZE_FOR_SIZE_LIMIT: usize = 2000;

impl Spec for SizeLimit {
Expand Down Expand Up @@ -35,87 +34,28 @@ impl Spec for SizeLimit {

let max_tx_num = (MAX_MEM_SIZE_FOR_SIZE_LIMIT as u64) / one_tx_size;

assert!(one_tx_cycles * max_tx_num < MAX_CYCLES_FOR_SIZE_LIMIT);

info!("Generate as much as possible txs on node");
(0..(max_tx_num - 1)).for_each(|_| {
let tx = node.new_transaction(hash.clone());
hash = node.rpc_client().send_transaction(tx.data().into());
txs_hash.push(hash.clone());
sleep(Duration::from_millis(10));
});

info!("The next tx reach size limit");
let tx = node.new_transaction(hash);
assert_send_transaction_fail(node, &tx, "Transaction pool exceeded maximum size limit");

let _hash = node.rpc_client().send_transaction(tx.data().into());
node.assert_tx_pool_serialized_size((max_tx_num + 1) * one_tx_size);
let last = node
.mine_with_blocking(|template| template.proposals.len() != (max_tx_num + 1) as usize);
node.assert_tx_pool_serialized_size(max_tx_num * one_tx_size);
node.mine_until_transactions_confirm();
node.mine(1);
node.assert_tx_pool_serialized_size(0);
}

fn modify_app_config(&self, config: &mut ckb_app_config::CKBAppConfig) {
config.tx_pool.max_mem_size = MAX_MEM_SIZE_FOR_SIZE_LIMIT;
config.tx_pool.max_cycles = MAX_CYCLES_FOR_SIZE_LIMIT;
config.tx_pool.min_fee_rate = FeeRate::zero();
}
}

pub struct CyclesLimit;

const MAX_CYCLES_FOR_CYCLE_LIMIT: u64 = 6000;
const MAX_MEM_SIZE_FOR_CYCLE_LIMIT: usize = 20_000_000;

impl Spec for CyclesLimit {
fn run(&self, nodes: &mut Vec<Node>) {
let node = &nodes[0];

info!("Generate DEFAULT_TX_PROPOSAL_WINDOW block on node");
node.mine_until_out_bootstrap_period();

info!("Generate 1 tx on node");
let mut txs_hash = Vec::new();
let tx = node.new_transaction_spend_tip_cellbase();
let mut hash = node.submit_transaction(&tx);
txs_hash.push(hash.clone());

let tx_pool_info = node.get_tip_tx_pool_info();
let one_tx_cycles = tx_pool_info.total_tx_cycles.value();
let one_tx_size = tx.data().serialized_size_in_block();

info!(
"one_tx_cycles: {}, one_tx_size: {}",
one_tx_cycles, one_tx_size
);

assert!(MAX_CYCLES_FOR_CYCLE_LIMIT > one_tx_cycles * 2);

let max_tx_num = MAX_CYCLES_FOR_CYCLE_LIMIT / one_tx_cycles;

assert!(one_tx_size * (max_tx_num as usize) < MAX_MEM_SIZE_FOR_CYCLE_LIMIT);

info!("Generate as much as possible txs on node");
(0..(max_tx_num - 1)).for_each(|_| {
let tx = node.new_transaction(hash.clone());
hash = node.rpc_client().send_transaction(tx.data().into());
txs_hash.push(hash.clone());
});

info!("The next tx reach cycles limit");
let tx = node.new_transaction(hash);
assert_send_transaction_fail(node, &tx, "Transaction pool exceeded maximum cycles limit");

node.assert_tx_pool_cycles(max_tx_num * one_tx_cycles);
let proposed =
node.mine_with_blocking(|template| template.proposals.len() != max_tx_num as usize);
node.mine_with_blocking(|template| template.number.value() != (proposed + 1));
node.mine_with_blocking(|template| template.number.value() != (last + 1));
node.mine_with_blocking(|template| template.transactions.len() != max_tx_num as usize);
node.assert_tx_pool_cycles(0);
node.assert_tx_pool_serialized_size(0);
}

fn modify_app_config(&self, config: &mut ckb_app_config::CKBAppConfig) {
config.tx_pool.max_mem_size = MAX_MEM_SIZE_FOR_CYCLE_LIMIT;
config.tx_pool.max_cycles = MAX_CYCLES_FOR_CYCLE_LIMIT;
config.tx_pool.max_tx_pool_size = MAX_MEM_SIZE_FOR_SIZE_LIMIT;
config.tx_pool.min_fee_rate = FeeRate::zero();
}
}
5 changes: 2 additions & 3 deletions test/template/ckb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,8 @@ reject_ill_transactions = true
enable_deprecated_rpc = true

[tx_pool]
max_mem_size = 20_000_000 # 20mb
max_cycles = 200_000_000_000
min_fee_rate = 0 # shannons/KB
max_tx_pool_size = 180_000_000 # 180mb
min_fee_rate = 0 # Here fee_rate are calculated directly using size in units of shannons/KB
max_tx_verify_cycles = 70_000_000
max_ancestors_count = 25

Expand Down
Loading