ckb_jsonrpc_types/
pool.rs

1use crate::{BlockNumber, Capacity, Cycle, Timestamp, TransactionView, Uint64};
2use ckb_types::H256;
3use ckb_types::core::tx_pool::{
4    AncestorsScoreSortKey as CoreAncestorsScoreSortKey,
5    PoolTransactionEntry as CorePoolTransactionEntry, PoolTxDetailInfo as CorePoolTxDetailInfo,
6    Reject, TxEntryInfo, TxPoolEntryInfo, TxPoolIds as CoreTxPoolIds, TxPoolInfo as CoreTxPoolInfo,
7};
8use schemars::JsonSchema;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12/// Transaction pool information.
13#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug, JsonSchema)]
14pub struct TxPoolInfo {
15    /// The associated chain tip block hash.
16    ///
17    /// The transaction pool is stateful. It manages the transactions which are valid to be
18    /// committed after this block.
19    pub tip_hash: H256,
20    /// The block number of the block `tip_hash`.
21    pub tip_number: BlockNumber,
22    /// Count of transactions in the pending state.
23    ///
24    /// The pending transactions must be proposed in a new block first.
25    pub pending: Uint64,
26    /// Count of transactions in the proposed state.
27    ///
28    /// The proposed transactions are ready to be committed in the new block after the block
29    /// `tip_hash`.
30    pub proposed: Uint64,
31    /// Count of orphan transactions.
32    ///
33    /// An orphan transaction has an input cell from the transaction which is neither in the chain
34    /// nor in the transaction pool.
35    pub orphan: Uint64,
36    /// Total count of transactions in the pool of all the different kinds of states (excluding orphan transactions).
37    pub total_tx_size: Uint64,
38    /// Total consumed VM cycles of all the transactions in the pool (excluding orphan transactions).
39    pub total_tx_cycles: Uint64,
40    /// Fee rate threshold. The pool rejects transactions which fee rate is below this threshold.
41    ///
42    /// The unit is Shannons per 1000 bytes transaction serialization size in the block.
43    pub min_fee_rate: Uint64,
44    /// RBF rate threshold.
45    ///
46    /// The pool reject to replace for transactions which fee rate is below this threshold.
47    /// if min_rbf_rate > min_fee_rate then RBF is enabled on the node.
48    ///
49    /// The unit is Shannons per 1000 bytes transaction serialization size in the block.
50    pub min_rbf_rate: Uint64,
51    /// Last updated time. This is the Unix timestamp in milliseconds.
52    pub last_txs_updated_at: Timestamp,
53    /// Limiting transactions to tx_size_limit
54    ///
55    /// Transactions with a large size close to the block size limit may not be packaged,
56    /// because the block header and cellbase are occupied,
57    /// so the tx-pool is limited to accepting transaction up to tx_size_limit.
58    pub tx_size_limit: Uint64,
59    /// Total limit on the size of transactions in the tx-pool
60    pub max_tx_pool_size: Uint64,
61
62    /// verify_queue size
63    pub verify_queue_size: Uint64,
64}
65
66impl From<CoreTxPoolInfo> for TxPoolInfo {
67    fn from(tx_pool_info: CoreTxPoolInfo) -> Self {
68        TxPoolInfo {
69            tip_hash: tx_pool_info.tip_hash.into(),
70            tip_number: tx_pool_info.tip_number.into(),
71            pending: (tx_pool_info.pending_size as u64).into(),
72            proposed: (tx_pool_info.proposed_size as u64).into(),
73            orphan: (tx_pool_info.orphan_size as u64).into(),
74            total_tx_size: (tx_pool_info.total_tx_size as u64).into(),
75            total_tx_cycles: tx_pool_info.total_tx_cycles.into(),
76            min_fee_rate: tx_pool_info.min_fee_rate.as_u64().into(),
77            min_rbf_rate: tx_pool_info.min_rbf_rate.as_u64().into(),
78            last_txs_updated_at: tx_pool_info.last_txs_updated_at.into(),
79            tx_size_limit: tx_pool_info.tx_size_limit.into(),
80            max_tx_pool_size: tx_pool_info.max_tx_pool_size.into(),
81            verify_queue_size: (tx_pool_info.verify_queue_size as u64).into(),
82        }
83    }
84}
85
86/// The transaction entry in the pool.
87#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
88pub struct PoolTransactionEntry {
89    /// The transaction.
90    pub transaction: TransactionView,
91    /// Consumed cycles.
92    pub cycles: Cycle,
93    /// The transaction serialized size in block.
94    pub size: Uint64,
95    /// The transaction fee.
96    pub fee: Capacity,
97    /// The unix timestamp when entering the Txpool, unit: Millisecond
98    pub timestamp: Uint64,
99}
100
101impl From<CorePoolTransactionEntry> for PoolTransactionEntry {
102    fn from(entry: CorePoolTransactionEntry) -> Self {
103        PoolTransactionEntry {
104            transaction: entry.transaction.into(),
105            cycles: entry.cycles.into(),
106            size: (entry.size as u64).into(),
107            fee: entry.fee.into(),
108            timestamp: entry.timestamp.into(),
109        }
110    }
111}
112
113/// Transaction output validators that prevent common mistakes.
114#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug, JsonSchema)]
115#[serde(rename_all = "snake_case")]
116pub enum OutputsValidator {
117    /// the default validator, bypass output checking, thus allow any kind of transaction outputs.
118    Passthrough,
119    /// restricts the lock script and type script usage, see more information on <https://github.com/nervosnetwork/ckb/wiki/Transaction-%C2%BB-Default-Outputs-Validator>
120    WellKnownScriptsOnly,
121}
122
123impl OutputsValidator {
124    /// Gets the name of the validator when it is serialized into JSON string.
125    pub fn json_display(&self) -> String {
126        let v = serde_json::to_value(self).expect("OutputsValidator to JSON should never fail");
127        v.as_str().unwrap_or_default().to_string()
128    }
129}
130
131/// Array of transaction ids
132#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug, JsonSchema)]
133pub struct TxPoolIds {
134    /// Pending transaction ids
135    pub pending: Vec<H256>,
136    /// Proposed transaction ids
137    pub proposed: Vec<H256>,
138}
139
140impl From<CoreTxPoolIds> for TxPoolIds {
141    fn from(ids: CoreTxPoolIds) -> Self {
142        let CoreTxPoolIds { pending, proposed } = ids;
143        TxPoolIds {
144            pending: pending.iter().map(Into::into).collect(),
145            proposed: proposed.iter().map(Into::into).collect(),
146        }
147    }
148}
149
150/// Transaction entry info
151#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug, JsonSchema)]
152pub struct TxPoolEntry {
153    /// Consumed cycles.
154    pub cycles: Uint64,
155    /// The transaction serialized size in block.
156    pub size: Uint64,
157    /// The transaction fee.
158    pub fee: Capacity,
159    /// Size of in-tx-pool ancestor transactions
160    pub ancestors_size: Uint64,
161    /// Cycles of in-tx-pool ancestor transactions
162    pub ancestors_cycles: Uint64,
163    /// Number of in-tx-pool ancestor transactions
164    pub ancestors_count: Uint64,
165    /// The unix timestamp when entering the Txpool, unit: Millisecond
166    pub timestamp: Uint64,
167}
168
169impl From<TxEntryInfo> for TxPoolEntry {
170    fn from(info: TxEntryInfo) -> Self {
171        TxPoolEntry {
172            cycles: info.cycles.into(),
173            size: info.size.into(),
174            fee: info.fee.into(),
175            ancestors_size: info.ancestors_size.into(),
176            ancestors_cycles: info.ancestors_cycles.into(),
177            ancestors_count: info.ancestors_count.into(),
178            timestamp: info.timestamp.into(),
179        }
180    }
181}
182
183/// Tx-pool entries object
184#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, JsonSchema)]
185pub struct TxPoolEntries {
186    /// Pending tx verbose info
187    pub pending: HashMap<H256, TxPoolEntry>,
188    /// Proposed tx verbose info
189    pub proposed: HashMap<H256, TxPoolEntry>,
190    /// Conflicted tx hash vec
191    pub conflicted: Vec<H256>,
192}
193
194impl From<TxPoolEntryInfo> for TxPoolEntries {
195    fn from(info: TxPoolEntryInfo) -> Self {
196        let TxPoolEntryInfo {
197            pending,
198            proposed,
199            conflicted,
200        } = info;
201
202        TxPoolEntries {
203            pending: pending
204                .into_iter()
205                .map(|(hash, entry)| (hash.into(), entry.into()))
206                .collect(),
207            proposed: proposed
208                .into_iter()
209                .map(|(hash, entry)| (hash.into(), entry.into()))
210                .collect(),
211            conflicted: conflicted.iter().map(Into::into).collect(),
212        }
213    }
214}
215
216/// All transactions in tx-pool.
217///
218/// `RawTxPool` is equivalent to [`TxPoolIds`][] `|` [`TxPoolEntries`][].
219///
220/// [`TxPoolIds`]: struct.TxPoolIds.html
221/// [`TxPoolEntries`]: struct.TxPoolEntries.html
222#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, JsonSchema)]
223#[serde(untagged)]
224pub enum RawTxPool {
225    /// verbose = false
226    Ids(TxPoolIds),
227    /// verbose = true
228    Verbose(TxPoolEntries),
229}
230
231/// A struct as a sorted key for tx-pool
232#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, JsonSchema)]
233pub struct AncestorsScoreSortKey {
234    /// Fee
235    pub fee: Uint64,
236    /// Weight
237    pub weight: Uint64,
238    /// Ancestors fee
239    pub ancestors_fee: Uint64,
240    /// Ancestors weight
241    pub ancestors_weight: Uint64,
242}
243
244impl From<CoreAncestorsScoreSortKey> for AncestorsScoreSortKey {
245    fn from(value: CoreAncestorsScoreSortKey) -> Self {
246        Self {
247            fee: value.fee.into(),
248            weight: value.weight.into(),
249            ancestors_fee: value.ancestors_fee.into(),
250            ancestors_weight: value.ancestors_weight.into(),
251        }
252    }
253}
254
255/// A Tx details info in tx-pool.
256#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, JsonSchema)]
257pub struct PoolTxDetailInfo {
258    /// The time added into tx-pool
259    pub timestamp: Uint64,
260    /// The detailed status in tx-pool, `pending`, `gap`, `proposed`
261    pub entry_status: String,
262    /// The rank in pending, starting from 0
263    pub rank_in_pending: Uint64,
264    /// The pending(`pending` and `gap`) count
265    pub pending_count: Uint64,
266    /// The proposed count
267    pub proposed_count: Uint64,
268    /// The descendants count of tx
269    pub descendants_count: Uint64,
270    /// The ancestors count of tx
271    pub ancestors_count: Uint64,
272    /// The score key details, useful to debug
273    pub score_sortkey: AncestorsScoreSortKey,
274}
275
276impl From<CorePoolTxDetailInfo> for PoolTxDetailInfo {
277    fn from(info: CorePoolTxDetailInfo) -> Self {
278        Self {
279            timestamp: info.timestamp.into(),
280            entry_status: info.entry_status,
281            rank_in_pending: (info.rank_in_pending as u64).into(),
282            pending_count: (info.pending_count as u64).into(),
283            proposed_count: (info.proposed_count as u64).into(),
284            descendants_count: (info.descendants_count as u64).into(),
285            ancestors_count: (info.ancestors_count as u64).into(),
286            score_sortkey: info.score_sortkey.into(),
287        }
288    }
289}
290
291/// TX reject message, `PoolTransactionReject` is a JSON object with following fields.
292///    * `type`:  the Reject type with following enum values
293///    * `description`: `string` - Detailed description about why the transaction is rejected.
294#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
295#[serde(tag = "type", content = "description")]
296pub enum PoolTransactionReject {
297    /// Transaction fee lower than config
298    LowFeeRate(String),
299
300    /// Transaction exceeded maximum ancestors count limit
301    ExceededMaximumAncestorsCount(String),
302
303    /// Transaction exceeded maximum size limit
304    ExceededTransactionSizeLimit(String),
305
306    /// Transaction are replaced because the pool is full
307    Full(String),
308
309    /// Transaction already exists in transaction_pool
310    Duplicated(String),
311
312    /// Malformed transaction
313    Malformed(String),
314
315    /// Declared wrong cycles
316    DeclaredWrongCycles(String),
317
318    /// Resolve failed
319    Resolve(String),
320
321    /// Verification failed
322    Verification(String),
323
324    /// Transaction expired
325    Expiry(String),
326
327    /// RBF rejected
328    RBFRejected(String),
329
330    /// Invalidated rejected
331    Invalidated(String),
332}
333
334impl From<Reject> for PoolTransactionReject {
335    fn from(reject: Reject) -> Self {
336        match reject {
337            Reject::LowFeeRate(..) => Self::LowFeeRate(format!("{reject}")),
338            Reject::ExceededMaximumAncestorsCount => {
339                Self::ExceededMaximumAncestorsCount(format!("{reject}"))
340            }
341            Reject::ExceededTransactionSizeLimit(..) => {
342                Self::ExceededTransactionSizeLimit(format!("{reject}"))
343            }
344            Reject::Full(..) => Self::Full(format!("{reject}")),
345            Reject::Duplicated(_) => Self::Duplicated(format!("{reject}")),
346            Reject::Malformed(_, _) => Self::Malformed(format!("{reject}")),
347            Reject::DeclaredWrongCycles(..) => Self::DeclaredWrongCycles(format!("{reject}")),
348            Reject::Resolve(_) => Self::Resolve(format!("{reject}")),
349            Reject::Verification(_) => Self::Verification(format!("{reject}")),
350            Reject::Expiry(_) => Self::Expiry(format!("{reject}")),
351            Reject::RBFRejected(_) => Self::RBFRejected(format!("{reject}")),
352            Reject::Invalidated(_) => Self::Invalidated(format!("{reject}")),
353        }
354    }
355}
356
357/// Transaction's verify result by test_tx_pool_accept
358#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug, JsonSchema)]
359pub struct EntryCompleted {
360    /// Cached tx cycles
361    pub cycles: Cycle,
362    /// Cached tx fee
363    pub fee: Capacity,
364}
365
366impl From<ckb_types::core::EntryCompleted> for EntryCompleted {
367    fn from(value: ckb_types::core::EntryCompleted) -> Self {
368        Self {
369            cycles: value.cycles.into(),
370            fee: value.fee.into(),
371        }
372    }
373}