Skip to content

Commit

Permalink
Merge pull request #4079 from chenyukang/tx_pool_rbf
Browse files Browse the repository at this point in the history
Tx pool Replace-by-fee
  • Loading branch information
zhangsoledad authored Aug 17, 2023
2 parents a709291 + 3e4b947 commit 835fc8e
Show file tree
Hide file tree
Showing 35 changed files with 1,169 additions and 261 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
### Features
* #4079, **tx-pol:** Implement `Replace-by-Fee(RBF)` for tx-pool (@chenyukang)
This feature enables users to replace a transaction with a higher fee rate, which is useful when the transaction is stuck in the tx-pool:
* Add `min_rbf_rate` in `ckb.toml` with default value `1500`, which means the minimum extra fee rate for RBF, the unit is `shannons/KB`
* Add fields `fee` and `min_replace_fee` in `get_transaction`, which means the the minimal fee need to pay for RBF for a specific transaction
* The replaced transaction will be removed from `tx-pool` and with the status `Rejected`.

### Improvements
* #3993, **tx-pool:** Almost reimplemented `tx-pool` with `multi_index_map`, with the following improvements (@chenyukang):
* Sort txs in pool by `score` in `Pending` stage, `txs` with higher `score` be processed first
* Evict `txs` from pool with `descendants_count` and `fee_rate`
* Eliminate redundant code for clean and consistent code

# [v0.110.0](https://github.com/nervosnetwork/ckb/compare/v0.109.0...v0.110.0) (2023-05-15)

### Features
Expand Down
41 changes: 25 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ pub enum Pow {
/// Mocking dummy PoW engine
Dummy,
/// The Eaglesong PoW engine
/// Check details of Eaglesong from: https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0010-eaglesong/0010-eaglesong.md
/// Check details of Eaglesong from: <https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0010-eaglesong/0010-eaglesong.md>
Eaglesong,
/// The Eaglesong PoW engine, similar to `Eaglesong`, but using `blake2b` hash as the final output.
/// Check details of blake2b from: https://tools.ietf.org/html/rfc7693 and blake2b-rs from: https://github.com/nervosnetwork/blake2b-rs
/// Check details of blake2b from: <https://tools.ietf.org/html/rfc7693> and blake2b-rs from: <https://github.com/nervosnetwork/blake2b-rs>
EaglesongBlake2b,
}

Expand Down
2 changes: 2 additions & 0 deletions resource/ckb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ enable_deprecated_rpc = false # {{
[tx_pool]
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
# min_rbf_rate > min_fee_rate means RBF is enabled
min_rbf_rate = 1_500 # 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
18 changes: 17 additions & 1 deletion rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,8 @@ Response
},
"cycles": "0x219",
"time_added_to_pool" : "0x187b3d137a1",
"fee": "0x16923f7dcf",
"min_replace_fee": "0x16923f7f6a",
"tx_status": {
"block_hash": null,
"status": "pending",
Expand Down Expand Up @@ -4510,6 +4512,7 @@ Response
"result": {
"last_txs_updated_at": "0x0",
"min_fee_rate": "0x3e8",
"min_rbf_rate": "0x5dc",
"max_tx_pool_size": "0xaba9500",
"orphan": "0x0",
"pending": "0x1",
Expand Down Expand Up @@ -5072,6 +5075,10 @@ For example, a cellbase transaction is not allowed in `send_transaction` RPC.

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

### Error `PoolRejectedRBF`

(-1111): The transaction is rejected for RBF checking.

### Error `Indexer`

(-1200): The indexer error.
Expand Down Expand Up @@ -6426,7 +6433,7 @@ TX reject message

`PoolTransactionReject` is a JSON object with following fields.

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

Different reject types:
Expand All @@ -6441,6 +6448,7 @@ Different reject types:
* `Resolve`: Resolve failed
* `Verification`: Verification failed
* `Expiry`: Transaction expired
* `RBFRejected`: RBF rejected


### Type `ProposalShortId`
Expand Down Expand Up @@ -6949,6 +6957,10 @@ The JSON view of a transaction as well as its status.

* `tx_status`: [`TxStatus`](#type-txstatus) - The Transaction status.

* `fee`: [`Capacity`](#type-capacity) `|` `null` - The transaction fee of the transaction

* `min_replace_fee`: [`Capacity`](#type-capacity) `|` `null` - The minimal fee required to replace this transaction


### Type `TxPoolEntries`

Expand Down Expand Up @@ -7033,6 +7045,10 @@ Transaction pool information.

The unit is Shannons per 1000 bytes transaction serialization size in the block.

* `min_rbf_rate`: [`Uint64`](#type-uint64) - RBF rate threshold. The pool reject to replace for transactions which fee rate is below this threshold. if min_rbf_rate > min_fee_rate then RBF is enabled on the node.

The unit is Shannons per 1000 bytes transaction serialization size in the block.

* `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
Expand Down
3 changes: 3 additions & 0 deletions rpc/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ pub enum RPCError {
TransactionExpired = -1109,
/// (-1110): The transaction exceeded maximum size limit.
PoolRejectedTransactionBySizeLimit = -1110,
/// (-1111): The transaction is rejected for RBF checking.
PoolRejectedRBF = -1111,
/// (-1200): The indexer error.
Indexer = -1200,
}
Expand Down Expand Up @@ -173,6 +175,7 @@ impl RPCError {
Reject::DeclaredWrongCycles(..) => RPCError::PoolRejectedMalformedTransaction,
Reject::Resolve(_) => RPCError::TransactionFailedToResolve,
Reject::Verification(_) => RPCError::TransactionFailedToVerify,
Reject::RBFRejected(_) => RPCError::PoolRejectedRBF,
Reject::ExceededTransactionSizeLimit(_, _) => {
RPCError::PoolRejectedTransactionBySizeLimit
}
Expand Down
5 changes: 4 additions & 1 deletion rpc/src/module/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,8 @@ pub trait ChainRpc {
/// },
/// "cycles": "0x219",
/// "time_added_to_pool" : "0x187b3d137a1",
/// "fee": "0x16923f7dcf",
/// "min_replace_fee": "0x16923f7f6a",
/// "tx_status": {
/// "block_hash": null,
/// "status": "pending",
Expand Down Expand Up @@ -2130,11 +2132,11 @@ impl ChainRpcImpl {
.and_then(|v| v.get(tx_info.index.saturating_sub(1)).copied())
})
};

return Ok(TransactionWithStatus::with_committed(
None,
tx_info.block_hash.unpack(),
cycles,
None,
));
}

Expand Down Expand Up @@ -2181,6 +2183,7 @@ impl ChainRpcImpl {
Some(tx),
tx_info.block_hash.unpack(),
cycles,
None,
));
}

Expand Down
1 change: 1 addition & 0 deletions rpc/src/module/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ pub trait PoolRpc {
/// "result": {
/// "last_txs_updated_at": "0x0",
/// "min_fee_rate": "0x3e8",
/// "min_rbf_rate": "0x5dc",
/// "max_tx_pool_size": "0xaba9500",
/// "orphan": "0x0",
/// "pending": "0x1",
Expand Down
4 changes: 2 additions & 2 deletions sync/src/synchronizer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! CKB node has initial block download phase (IBD mode) like Bitcoin:
//! https://btcinformation.org/en/glossary/initial-block-download
//! <https://btcinformation.org/en/glossary/initial-block-download>
//!
//! When CKB node is in IBD mode, it will respond `packed::InIBD` to `GetHeaders` and `GetBlocks` requests
//!
//! And CKB has a headers-first synchronization style like Bitcoin:
//! https://btcinformation.org/en/glossary/headers-first-sync
//! <https://btcinformation.org/en/glossary/headers-first-sync>
//!
mod block_fetcher;
mod block_process;
Expand Down
11 changes: 10 additions & 1 deletion test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,16 @@ fn all_specs() -> Vec<Box<dyn Spec>> {
Box::new(RelayWithWrongTx::new()),
Box::new(TxsRelayOrder),
Box::new(SendTxChain),
Box::new(DifferentTxsWithSameInput),
Box::new(DifferentTxsWithSameInputWithOutRBF),
Box::new(RbfEnable),
Box::new(RbfBasic),
Box::new(RbfSameInput),
Box::new(RbfOnlyForResolveDead),
Box::new(RbfSameInputwithLessFee),
Box::new(RbfTooManyDescendants),
Box::new(RbfContainNewTx),
Box::new(RbfContainInvalidInput),
Box::new(RbfRejectReplaceProposed),
Box::new(CompactBlockEmpty),
Box::new(CompactBlockEmptyParentUnknown),
Box::new(CompactBlockPrefilled),
Expand Down
23 changes: 17 additions & 6 deletions test/src/specs/tx_pool/descendant.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use ckb_jsonrpc_types::Status;

use crate::specs::tx_pool::utils::prepare_tx_family;
use crate::utils::{blank, commit, propose};
use crate::{Node, Spec};
Expand Down Expand Up @@ -166,21 +168,30 @@ impl Spec for SubmitTransactionWhenItsParentInProposed {

// 1. Propose `tx_family.a` into proposed-pool.
let family = prepare_tx_family(node);
node.submit_transaction(family.a());
node.submit_block(&propose(node, &[family.a()]));
let tx_a = family.a();
node.submit_transaction(tx_a);
node.submit_block(&propose(node, &[tx_a]));
(0..=window.closest()).for_each(|_| {
node.submit_block(&blank(node));
});

// tx_a should in Proposed status
let tx_a_status = node.get_transaction(tx_a.hash());
assert_eq!(tx_a_status.status, Status::Proposed);

// 2. Submit `tx_family.b` into pending-pool. Then we expect that miner propose it.
node.submit_transaction(family.b());
let block = node.new_block_with_blocking(|template| template.proposals.is_empty());
let union_proposal_ids = block.union_proposal_ids();
assert!(
block
.union_proposal_ids()
.contains(&family.b().proposal_short_id()),
union_proposal_ids.contains(&family.b().proposal_short_id()),
"Miner should propose tx_family.b since it has never been proposed, actual: {:?}",
block.union_proposal_ids(),
union_proposal_ids,
);
assert!(
!union_proposal_ids.contains(&tx_a.proposal_short_id()),
"Miner should not propose tx_family.a since it has been proposed, actual: {:?}",
union_proposal_ids,
);
node.submit_block(&block);
}
Expand Down
7 changes: 4 additions & 3 deletions test/src/specs/tx_pool/different_txs_with_same_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use ckb_types::{
prelude::*,
};

pub struct DifferentTxsWithSameInput;
pub struct DifferentTxsWithSameInputWithOutRBF;

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

Expand All @@ -19,6 +19,7 @@ impl Spec for DifferentTxsWithSameInput {
info!("Generate 2 txs with same input");
let tx1 = node0.new_transaction(tx_hash_0.clone());
let tx2_temp = node0.new_transaction(tx_hash_0);

// Set tx2 fee to a higher value, tx1 capacity is 100, set tx2 capacity to 80 for +20 fee.
let output = CellOutputBuilder::default()
.capacity(capacity_bytes!(80).pack())
Expand Down Expand Up @@ -46,7 +47,7 @@ impl Spec for DifferentTxsWithSameInput {
.map(TransactionView::hash)
.collect();

// RBF (Replace-By-Fees) is not implemented
// RBF (Replace-By-Fees) is not enabled
assert!(commit_txs_hash.contains(&tx1.hash()));
assert!(!commit_txs_hash.contains(&tx2.hash()));

Expand Down
2 changes: 2 additions & 0 deletions test/src/specs/tx_pool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod pool_resurrect;
mod proposal_expire_rule;
mod remove_tx;
mod reorg_proposals;
mod replace;
mod send_defected_binary;
mod send_large_cycles_tx;
mod send_low_fee_rate_tx;
Expand Down Expand Up @@ -42,6 +43,7 @@ pub use pool_resurrect::*;
pub use proposal_expire_rule::*;
pub use remove_tx::*;
pub use reorg_proposals::*;
pub use replace::*;
pub use send_defected_binary::*;
pub use send_large_cycles_tx::*;
pub use send_low_fee_rate_tx::*;
Expand Down
Loading

0 comments on commit 835fc8e

Please sign in to comment.