Skip to main content

ckb_rpc/module/
pool.rs

1use crate::error::RPCError;
2use async_trait::async_trait;
3use ckb_chain_spec::consensus::Consensus;
4use ckb_constant::hardfork::{mainnet, testnet};
5use ckb_jsonrpc_types::{
6    EntryCompleted, OutputsValidator, PoolTxDetailInfo, RawTxPool, Script, Transaction, TxPoolInfo,
7};
8use ckb_logger::error;
9use ckb_shared::shared::Shared;
10use ckb_types::core::TransactionView;
11use ckb_types::{H256, core, packed, prelude::*};
12use ckb_verification::{Since, SinceMetric};
13use jsonrpc_core::Result;
14use jsonrpc_utils::rpc;
15use std::sync::Arc;
16
17/// RPC Module Pool for transaction memory pool.
18#[rpc(openrpc)]
19#[async_trait]
20pub trait PoolRpc {
21    /// Submits a new transaction into the transaction pool. If the transaction is already in the
22    /// pool, rebroadcast it to peers.
23    ///
24    /// Please note that `send_transaction` is an asynchronous process.
25    /// The return of `send_transaction` does NOT indicate that the transaction have been fully verified.
26    /// If you want to track the status of the transaction, please use the `get_transaction`rpc.
27    ///
28    /// ## Params
29    ///
30    /// * `transaction` - The transaction.
31    /// * `outputs_validator` - Validates the transaction outputs before entering the tx-pool. (**Optional**, default is "passthrough").
32    ///
33    /// ## Errors
34    ///
35    /// * [`PoolRejectedTransactionByOutputsValidator (-1102)`](../enum.RPCError.html#variant.PoolRejectedTransactionByOutputsValidator) - The transaction is rejected by the validator specified by `outputs_validator`. If you really want to send transactions with advanced scripts, please set `outputs_validator` to "passthrough".
36    /// * [`PoolRejectedTransactionByMinFeeRate (-1104)`](../enum.RPCError.html#variant.PoolRejectedTransactionByMinFeeRate) - The transaction fee rate must be greater than or equal to the config option `tx_pool.min_fee_rate`.
37    /// * [`PoolRejectedTransactionByMaxAncestorsCountLimit (-1105)`](../enum.RPCError.html#variant.PoolRejectedTransactionByMaxAncestorsCountLimit) - The ancestors count must be greater than or equal to the config option `tx_pool.max_ancestors_count`.
38    /// * [`PoolIsFull (-1106)`](../enum.RPCError.html#variant.PoolIsFull) - Pool is full.
39    /// * [`PoolRejectedDuplicatedTransaction (-1107)`](../enum.RPCError.html#variant.PoolRejectedDuplicatedTransaction) - The transaction is already in the pool.
40    /// * [`TransactionFailedToResolve (-301)`](../enum.RPCError.html#variant.TransactionFailedToResolve) - Failed to resolve the referenced cells and headers used in the transaction, as inputs or dependencies.
41    /// * [`TransactionFailedToVerify (-302)`](../enum.RPCError.html#variant.TransactionFailedToVerify) - Failed to verify the transaction.
42    ///
43    /// ## Examples
44    ///
45    /// Request
46    ///
47    /// ```json
48    /// {
49    ///   "id": 42,
50    ///   "jsonrpc": "2.0",
51    ///   "method": "send_transaction",
52    ///   "params": [
53    ///     {
54    ///       "cell_deps": [
55    ///         {
56    ///           "dep_type": "code",
57    ///           "out_point": {
58    ///             "index": "0x0",
59    ///             "tx_hash": "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
60    ///           }
61    ///         }
62    ///       ],
63    ///       "header_deps": [
64    ///         "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed"
65    ///       ],
66    ///       "inputs": [
67    ///         {
68    ///           "previous_output": {
69    ///             "index": "0x0",
70    ///             "tx_hash": "0x365698b50ca0da75dca2c87f9e7b563811d3b5813736b8cc62cc3b106faceb17"
71    ///           },
72    ///           "since": "0x0"
73    ///         }
74    ///       ],
75    ///       "outputs": [
76    ///         {
77    ///           "capacity": "0x2540be400",
78    ///           "lock": {
79    ///             "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
80    ///             "hash_type": "data",
81    ///             "args": "0x"
82    ///           },
83    ///           "type": null
84    ///         }
85    ///       ],
86    ///       "outputs_data": [
87    ///         "0x"
88    ///       ],
89    ///       "version": "0x0",
90    ///       "witnesses": []
91    ///     },
92    ///     "passthrough"
93    ///   ]
94    /// }
95    /// ```
96    ///
97    /// Response
98    ///
99    /// ```json
100    /// {
101    ///   "id": 42,
102    ///   "jsonrpc": "2.0",
103    ///   "result": "0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3"
104    /// }
105    /// ```
106    #[rpc(name = "send_transaction")]
107    fn send_transaction(
108        &self,
109        tx: Transaction,
110        outputs_validator: Option<OutputsValidator>,
111    ) -> Result<H256>;
112
113    /// Test if a transaction can be accepted by the transaction pool without inserting it into the pool or rebroadcasting it to peers.
114    /// The parameters and errors of this method are the same as `send_transaction`.
115    ///
116    /// ## Params
117    ///
118    /// * `transaction` - The transaction.
119    /// * `outputs_validator` - Validates the transaction outputs before entering the tx-pool. (**Optional**, default is "passthrough").
120    ///
121    /// ## Errors
122    ///
123    /// * [`PoolRejectedTransactionByOutputsValidator (-1102)`](../enum.RPCError.html#variant.PoolRejectedTransactionByOutputsValidator) - The transaction is rejected by the validator specified by `outputs_validator`. If you really want to send transactions with advanced scripts, please set `outputs_validator` to "passthrough".
124    /// * [`PoolRejectedTransactionByMinFeeRate (-1104)`](../enum.RPCError.html#variant.PoolRejectedTransactionByMinFeeRate) - The transaction fee rate must be greater than or equal to the config option `tx_pool.min_fee_rate`.
125    /// * [`PoolRejectedTransactionByMaxAncestorsCountLimit (-1105)`](../enum.RPCError.html#variant.PoolRejectedTransactionByMaxAncestorsCountLimit) - The ancestors count must be greater than or equal to the config option `tx_pool.max_ancestors_count`.
126    /// * [`PoolIsFull (-1106)`](../enum.RPCError.html#variant.PoolIsFull) - Pool is full.
127    /// * [`PoolRejectedDuplicatedTransaction (-1107)`](../enum.RPCError.html#variant.PoolRejectedDuplicatedTransaction) - The transaction is already in the pool.
128    /// * [`TransactionFailedToResolve (-301)`](../enum.RPCError.html#variant.TransactionFailedToResolve) - Failed to resolve the referenced cells and headers used in the transaction, as inputs or dependencies.
129    /// * [`TransactionFailedToVerify (-302)`](../enum.RPCError.html#variant.TransactionFailedToVerify) - Failed to verify the transaction.
130    ///
131    /// ## Examples
132    ///
133    /// Request
134    ///
135    /// ```json
136    /// {
137    ///   "id": 42,
138    ///   "jsonrpc": "2.0",
139    ///   "method": "test_tx_pool_accept",
140    ///   "params": [
141    ///     {
142    ///       "cell_deps": [
143    ///         {
144    ///           "dep_type": "code",
145    ///           "out_point": {
146    ///             "index": "0x0",
147    ///             "tx_hash": "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
148    ///           }
149    ///         }
150    ///       ],
151    ///       "header_deps": [
152    ///         "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed"
153    ///       ],
154    ///       "inputs": [
155    ///         {
156    ///           "previous_output": {
157    ///             "index": "0x0",
158    ///             "tx_hash": "0x075fe030c1f4725713c5aacf41c2f59b29b284008fdb786e5efd8a058be51d0c"
159    ///           },
160    ///           "since": "0x0"
161    ///         }
162    ///       ],
163    ///       "outputs": [
164    ///         {
165    ///           "capacity": "0x2431ac129",
166    ///           "lock": {
167    ///             "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
168    ///             "hash_type": "data",
169    ///             "args": "0x"
170    ///           },
171    ///           "type": null
172    ///         }
173    ///       ],
174    ///       "outputs_data": [
175    ///         "0x"
176    ///       ],
177    ///       "version": "0x0",
178    ///       "witnesses": []
179    ///     },
180    ///     "passthrough"
181    ///   ]
182    /// }
183    /// ```
184    ///
185    /// Response
186    ///
187    /// ```json
188    /// {
189    ///   "id": 42,
190    ///   "jsonrpc": "2.0",
191    ///   "result": {
192    ///     "cycles": "0x219",
193    ///     "fee": "0x2a66f36e90"
194    ///   }
195    /// }
196    /// ```
197    ///
198    ///
199    /// The response looks like below if the transaction pool check fails
200    ///
201    /// ```text
202    /// {
203    ///   "id": 42,
204    ///   "jsonrpc": "2.0",
205    ///   "result": null,
206    ///   "error": {
207    ///     "code": -1107,
208    ///     "data": "Duplicated(Byte32(0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3))",
209    ///     "message": "PoolRejectedDuplicatedTransaction: Transaction(Byte32(0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3)) already exists in transaction_pool"
210    ///   }
211    /// }
212    /// ```
213    #[rpc(name = "test_tx_pool_accept")]
214    fn test_tx_pool_accept(
215        &self,
216        tx: Transaction,
217        outputs_validator: Option<OutputsValidator>,
218    ) -> Result<EntryCompleted>;
219
220    /// Removes a transaction and all transactions which depends on it from tx pool if it exists.
221    ///
222    /// ## Params
223    ///
224    /// * `tx_hash` - Hash of a transaction.
225    ///
226    /// ## Returns
227    ///
228    /// If the transaction exists, return true; otherwise, return false.
229    ///
230    /// ## Examples
231    ///
232    /// Request
233    ///
234    /// ```json
235    /// {
236    ///   "id": 42,
237    ///   "jsonrpc": "2.0",
238    ///   "method": "remove_transaction",
239    ///   "params": [
240    ///     "0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3"
241    ///   ]
242    /// }
243    /// ```
244    ///
245    /// Response
246    ///
247    /// ```json
248    /// {
249    ///   "id": 42,
250    ///   "jsonrpc": "2.0",
251    ///   "result": true
252    /// }
253    /// ```
254    #[rpc(name = "remove_transaction")]
255    fn remove_transaction(&self, tx_hash: H256) -> Result<bool>;
256
257    /// Returns the transaction pool information.
258    ///
259    /// ## Examples
260    ///
261    /// Request
262    ///
263    /// ```json
264    /// {
265    ///   "id": 42,
266    ///   "jsonrpc": "2.0",
267    ///   "method": "tx_pool_info",
268    ///   "params": []
269    /// }
270    /// ```
271    ///
272    /// Response
273    ///
274    /// ```json
275    /// {
276    ///   "id": 42,
277    ///   "jsonrpc": "2.0",
278    ///   "result": {
279    ///     "last_txs_updated_at": "0x0",
280    ///     "min_fee_rate": "0x3e8",
281    ///     "min_rbf_rate": "0x5dc",
282    ///     "max_tx_pool_size": "0xaba9500",
283    ///     "orphan": "0x0",
284    ///     "pending": "0x1",
285    ///     "proposed": "0x0",
286    ///     "tip_hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
287    ///     "tip_number": "0x400",
288    ///     "total_tx_cycles": "0x219",
289    ///     "total_tx_size": "0x112",
290    ///     "tx_size_limit": "0x7d000",
291    ///     "verify_queue_size": "0x0"
292    ///   }
293    /// }
294    /// ```
295    #[rpc(name = "tx_pool_info")]
296    fn tx_pool_info(&self) -> Result<TxPoolInfo>;
297
298    /// Removes all transactions from the transaction pool.
299    ///
300    /// ## Examples
301    ///
302    /// Request
303    ///
304    /// ```json
305    /// {
306    ///   "id": 42,
307    ///   "jsonrpc": "2.0",
308    ///   "method": "clear_tx_pool",
309    ///   "params": []
310    /// }
311    /// ```
312    ///
313    /// Response
314    ///
315    /// ```json
316    /// {
317    ///   "id": 42,
318    ///   "jsonrpc": "2.0",
319    ///   "result": null
320    /// }
321    /// ```
322    #[rpc(name = "clear_tx_pool")]
323    fn clear_tx_pool(&self) -> Result<()>;
324
325    /// Removes all transactions from the verification queue.
326    ///
327    /// ## Examples
328    ///
329    /// Request
330    ///
331    /// ```json
332    /// {
333    ///   "id": 42,
334    ///   "jsonrpc": "2.0",
335    ///   "method": "clear_tx_verify_queue",
336    ///   "params": []
337    /// }
338    /// ```
339    ///
340    /// Response
341    ///
342    /// ```json
343    /// {
344    ///   "id": 42,
345    ///   "jsonrpc": "2.0",
346    ///   "result": null
347    /// }
348    /// ```
349    #[rpc(name = "clear_tx_verify_queue")]
350    fn clear_tx_verify_queue(&self) -> Result<()>;
351
352    /// Returns all transaction ids in tx pool as a json array of string transaction ids.
353    /// ## Params
354    ///
355    /// * `verbose` - True for a json object, false for array of transaction ids, default=false
356    ///
357    /// ## Examples
358    ///
359    /// Request
360    ///
361    /// ```json
362    /// {
363    ///   "id": 42,
364    ///   "jsonrpc": "2.0",
365    ///   "method": "get_raw_tx_pool",
366    ///   "params": [true]
367    /// }
368    /// ```
369    ///
370    /// Response
371    ///
372    /// ```json
373    /// {
374    ///   "id": 42,
375    ///   "jsonrpc": "2.0",
376    ///   "result":
377    ///    {
378    ///        "pending": {
379    ///            "0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3": {
380    ///                "cycles": "0x219",
381    ///                "size": "0x112",
382    ///                "fee": "0x16923f7dcf",
383    ///                "ancestors_size": "0x112",
384    ///                "ancestors_cycles": "0x219",
385    ///                "ancestors_count": "0x1",
386    ///                "timestamp": "0x17c983e6e44"
387    ///            }
388    ///        },
389    ///        "conflicted": [],
390    ///        "proposed": {}
391    ///    }
392    /// }
393    /// ```
394    #[rpc(name = "get_raw_tx_pool")]
395    fn get_raw_tx_pool(&self, verbose: Option<bool>) -> Result<RawTxPool>;
396
397    /// Query and returns the details of a transaction in the pool, only for trouble shooting
398    /// ## Params
399    ///
400    /// * `tx_hash` - Hash of a transaction
401    ///
402    /// ## Examples
403    ///
404    /// Request
405    ///
406    /// ```json
407    /// {
408    ///   "id": 42,
409    ///   "jsonrpc": "2.0",
410    ///   "method": "get_pool_tx_detail_info",
411    ///   "params": [
412    ///     "0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3"
413    ///   ]
414    /// }
415    /// ```
416    ///
417    /// Response
418    ///
419    /// ```json
420    /// {
421    ///    "jsonrpc": "2.0",
422    ///    "result": {
423    ///        "ancestors_count": "0x0",
424    ///        "descendants_count": "0x0",
425    ///        "entry_status": "pending",
426    ///        "pending_count": "0x1",
427    ///        "proposed_count": "0x0",
428    ///        "rank_in_pending": "0x1",
429    ///        "score_sortkey": {
430    ///            "ancestors_fee": "0x16923f7dcf",
431    ///            "ancestors_weight": "0x112",
432    ///            "fee": "0x16923f7dcf",
433    ///            "weight": "0x112"
434    ///        },
435    ///        "timestamp": "0x18aa1baa54c"
436    ///    },
437    ///    "id": 42
438    /// }
439    /// ```
440    #[rpc(name = "get_pool_tx_detail_info")]
441    fn get_pool_tx_detail_info(&self, tx_hash: H256) -> Result<PoolTxDetailInfo>;
442
443    /// Returns whether tx-pool service is started, ready for request.
444    ///
445    /// ## Examples
446    ///
447    /// Request
448    ///
449    /// ```json
450    /// {
451    ///   "id": 42,
452    ///   "jsonrpc": "2.0",
453    ///   "method": "tx_pool_ready",
454    ///   "params": []
455    /// }
456    /// ```
457    ///
458    /// Response
459    ///
460    /// ```json
461    /// {
462    ///   "id": 42,
463    ///   "jsonrpc": "2.0",
464    ///   "result": true
465    /// }
466    /// ```
467    #[rpc(name = "tx_pool_ready")]
468    fn tx_pool_ready(&self) -> Result<bool>;
469}
470
471#[derive(Clone)]
472pub(crate) struct PoolRpcImpl {
473    shared: Shared,
474    well_known_lock_scripts: Vec<packed::Script>,
475    well_known_type_scripts: Vec<packed::Script>,
476}
477
478impl PoolRpcImpl {
479    pub fn new(
480        shared: Shared,
481        mut extra_well_known_lock_scripts: Vec<packed::Script>,
482        mut extra_well_known_type_scripts: Vec<packed::Script>,
483    ) -> PoolRpcImpl {
484        let mut well_known_lock_scripts =
485            build_well_known_lock_scripts(shared.consensus().id.as_str());
486        let mut well_known_type_scripts =
487            build_well_known_type_scripts(shared.consensus().id.as_str());
488
489        well_known_lock_scripts.append(&mut extra_well_known_lock_scripts);
490        well_known_type_scripts.append(&mut extra_well_known_type_scripts);
491
492        PoolRpcImpl {
493            shared,
494            well_known_lock_scripts,
495            well_known_type_scripts,
496        }
497    }
498
499    fn check_output_validator(
500        &self,
501        outputs_validator: Option<OutputsValidator>,
502        tx: &TransactionView,
503    ) -> Result<()> {
504        if let Err(e) = match outputs_validator {
505            None | Some(OutputsValidator::Passthrough) => Ok(()),
506            Some(OutputsValidator::WellKnownScriptsOnly) => WellKnownScriptsOnlyValidator::new(
507                self.shared.consensus(),
508                &self.well_known_lock_scripts,
509                &self.well_known_type_scripts,
510            )
511            .validate(tx),
512        } {
513            return Err(RPCError::custom_with_data(
514                RPCError::PoolRejectedTransactionByOutputsValidator,
515                format!(
516                    "The transaction is rejected by OutputsValidator set in params[1]: {}. \
517                    Please check the related information in https://github.com/nervosnetwork/ckb/wiki/Transaction-%C2%BB-Default-Outputs-Validator",
518                    outputs_validator
519                        .unwrap_or(OutputsValidator::WellKnownScriptsOnly)
520                        .json_display()
521                ),
522                e,
523            ));
524        }
525        Ok(())
526    }
527}
528
529/// Build well known lock scripts
530/// https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0026-anyone-can-pay/0026-anyone-can-pay.md
531/// https://talk.nervos.org/t/sudt-cheque-deposit-design-and-implementation/5209
532/// 1. anyone_can_pay
533/// 2. cheque
534fn build_well_known_lock_scripts(chain_spec_name: &str) -> Vec<packed::Script> {
535    serde_json::from_str::<Vec<Script>>(
536    match chain_spec_name {
537        mainnet::CHAIN_SPEC_NAME => {
538            r#"
539            [
540                {
541                    "code_hash": "0xd369597ff47f29fbc0d47d2e3775370d1250b85140c670e4718af712983a2354",
542                    "hash_type": "type",
543                    "args": "0x"
544                },
545                {
546                    "code_hash": "0xe4d4ecc6e5f9a059bf2f7a82cca292083aebc0c421566a52484fe2ec51a9fb0c",
547                    "hash_type": "type",
548                    "args": "0x"
549                }
550            ]
551            "#
552        }
553        testnet::CHAIN_SPEC_NAME => {
554            r#"
555            [
556                {
557                    "code_hash": "0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356",
558                    "hash_type": "type",
559                    "args": "0x"
560                },
561                {
562                    "code_hash": "0x60d5f39efce409c587cb9ea359cefdead650ca128f0bd9cb3855348f98c70d5b",
563                    "hash_type": "type",
564                    "args": "0x"
565                }
566            ]
567            "#
568        }
569        _ => "[]"
570    }).expect("checked json str").into_iter().map(Into::into).collect()
571}
572
573/// Build well known type scripts
574/// https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0025-simple-udt/0025-simple-udt.md
575/// 1. Simple UDT
576fn build_well_known_type_scripts(chain_spec_name: &str) -> Vec<packed::Script> {
577    serde_json::from_str::<Vec<Script>>(
578    match chain_spec_name {
579        mainnet::CHAIN_SPEC_NAME => {
580            r#"
581            [
582                {
583                    "code_hash": "0x5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5",
584                    "hash_type": "type",
585                    "args": "0x"
586                }
587            ]
588            "#
589        }
590        testnet::CHAIN_SPEC_NAME => {
591            r#"
592            [
593                {
594                    "code_hash": "0xc5e5dcf215925f7ef4dfaf5f4b4f105bc321c02776d6e7d52a1db3fcd9d011a4",
595                    "hash_type": "type",
596                    "args": "0x"
597                }
598            ]
599            "#
600        }
601        _ => "[]"
602    }).expect("checked json str").into_iter().map(Into::into).collect()
603}
604
605#[async_trait]
606impl PoolRpc for PoolRpcImpl {
607    fn tx_pool_ready(&self) -> Result<bool> {
608        let tx_pool = self.shared.tx_pool_controller();
609        Ok(tx_pool.service_started())
610    }
611
612    fn send_transaction(
613        &self,
614        tx: Transaction,
615        outputs_validator: Option<OutputsValidator>,
616    ) -> Result<H256> {
617        let tx: packed::Transaction = tx.into();
618        let tx: core::TransactionView = tx.into_view();
619
620        self.check_output_validator(outputs_validator, &tx)?;
621
622        let tx_pool = self.shared.tx_pool_controller();
623        let submit_tx = tx_pool.submit_local_tx(tx.clone());
624
625        if let Err(e) = submit_tx {
626            error!("Send submit_tx request error {}", e);
627            return Err(RPCError::ckb_internal_error(e));
628        }
629
630        let tx_hash = tx.hash();
631        match submit_tx.unwrap() {
632            Ok(_) => Ok(tx_hash.into()),
633            Err(reject) => Err(RPCError::from_submit_transaction_reject(&reject)),
634        }
635    }
636
637    fn test_tx_pool_accept(
638        &self,
639        tx: Transaction,
640        outputs_validator: Option<OutputsValidator>,
641    ) -> Result<EntryCompleted> {
642        let tx: packed::Transaction = tx.into();
643        let tx: core::TransactionView = tx.into_view();
644
645        self.check_output_validator(outputs_validator, &tx)?;
646
647        let tx_pool = self.shared.tx_pool_controller();
648
649        let test_accept_tx_reslt = tx_pool.test_accept_tx(tx).map_err(|e| {
650            error!("Send test_tx_pool_accept_tx request error {}", e);
651            RPCError::ckb_internal_error(e)
652        })?;
653
654        test_accept_tx_reslt
655            .map(|test_accept_result| test_accept_result.into())
656            .map_err(|reject| {
657                error!("Send test_tx_pool_accept_tx request error {}", reject);
658                RPCError::from_submit_transaction_reject(&reject)
659            })
660    }
661
662    fn remove_transaction(&self, tx_hash: H256) -> Result<bool> {
663        let tx_pool = self.shared.tx_pool_controller();
664
665        tx_pool.remove_local_tx(tx_hash.into()).map_err(|e| {
666            error!("Send remove_tx request error {}", e);
667            RPCError::ckb_internal_error(e)
668        })
669    }
670
671    fn tx_pool_info(&self) -> Result<TxPoolInfo> {
672        let tx_pool = self.shared.tx_pool_controller();
673        let get_tx_pool_info = tx_pool.get_tx_pool_info();
674        if let Err(e) = get_tx_pool_info {
675            error!("Send get_tx_pool_info request error {}", e);
676            return Err(RPCError::ckb_internal_error(e));
677        };
678
679        let tx_pool_info = get_tx_pool_info.unwrap();
680
681        Ok(tx_pool_info.into())
682    }
683
684    fn clear_tx_pool(&self) -> Result<()> {
685        let snapshot = Arc::clone(&self.shared.snapshot());
686        let tx_pool = self.shared.tx_pool_controller();
687        tx_pool
688            .clear_pool(snapshot)
689            .map_err(|err| RPCError::custom(RPCError::Invalid, err.to_string()))?;
690
691        Ok(())
692    }
693
694    fn clear_tx_verify_queue(&self) -> Result<()> {
695        let tx_pool = self.shared.tx_pool_controller();
696        tx_pool
697            .clear_verify_queue()
698            .map_err(|err| RPCError::custom(RPCError::Invalid, err.to_string()))?;
699
700        Ok(())
701    }
702
703    fn get_raw_tx_pool(&self, verbose: Option<bool>) -> Result<RawTxPool> {
704        let tx_pool = self.shared.tx_pool_controller();
705
706        let raw = if verbose.unwrap_or(false) {
707            let info = tx_pool
708                .get_all_entry_info()
709                .map_err(|err| RPCError::custom(RPCError::CKBInternalError, err.to_string()))?;
710            RawTxPool::Verbose(info.into())
711        } else {
712            let ids = tx_pool
713                .get_all_ids()
714                .map_err(|err| RPCError::custom(RPCError::CKBInternalError, err.to_string()))?;
715            RawTxPool::Ids(ids.into())
716        };
717        Ok(raw)
718    }
719
720    fn get_pool_tx_detail_info(&self, tx_hash: H256) -> Result<PoolTxDetailInfo> {
721        let tx_pool = self.shared.tx_pool_controller();
722        let tx_detail = tx_pool
723            .get_tx_detail(tx_hash.into())
724            .map_err(|err| RPCError::custom(RPCError::CKBInternalError, err.to_string()))?;
725        Ok(tx_detail.into())
726    }
727}
728
729pub(crate) struct WellKnownScriptsOnlyValidator<'a> {
730    consensus: &'a Consensus,
731    well_known_lock_scripts: &'a [packed::Script],
732    well_known_type_scripts: &'a [packed::Script],
733}
734
735#[derive(Debug)]
736enum DefaultOutputsValidatorError {
737    HashType,
738    CodeHash,
739    ArgsLen,
740    ArgsSince,
741    NotWellKnownLockScript,
742    NotWellKnownTypeScript,
743}
744
745impl<'a> WellKnownScriptsOnlyValidator<'a> {
746    pub fn new(
747        consensus: &'a Consensus,
748        well_known_lock_scripts: &'a [packed::Script],
749        well_known_type_scripts: &'a [packed::Script],
750    ) -> Self {
751        Self {
752            consensus,
753            well_known_lock_scripts,
754            well_known_type_scripts,
755        }
756    }
757
758    pub fn validate(&self, tx: &core::TransactionView) -> std::result::Result<(), String> {
759        tx.outputs()
760            .into_iter()
761            .enumerate()
762            .try_for_each(|(index, output)| {
763                self.validate_lock_script(&output)
764                    .and(self.validate_type_script(&output))
765                    .map_err(|err| format!("output index: {index}, error: {err:?}"))
766            })
767    }
768
769    fn validate_lock_script(
770        &self,
771        output: &packed::CellOutput,
772    ) -> std::result::Result<(), DefaultOutputsValidatorError> {
773        self.validate_secp256k1_blake160_sighash_all(output)
774            .or_else(|_| self.validate_secp256k1_blake160_multisig_all(output))
775            .or_else(|_| self.validate_well_known_lock_scripts(output))
776    }
777
778    fn validate_type_script(
779        &self,
780        output: &packed::CellOutput,
781    ) -> std::result::Result<(), DefaultOutputsValidatorError> {
782        self.validate_dao(output)
783            .or_else(|_| self.validate_well_known_type_scripts(output))
784    }
785
786    fn validate_secp256k1_blake160_sighash_all(
787        &self,
788        output: &packed::CellOutput,
789    ) -> std::result::Result<(), DefaultOutputsValidatorError> {
790        let script = output.lock();
791        if !script.is_hash_type_type() {
792            Err(DefaultOutputsValidatorError::HashType)
793        } else if script.code_hash()
794            != self
795                .consensus
796                .secp256k1_blake160_sighash_all_type_hash()
797                .expect("No secp256k1_blake160_sighash_all system cell")
798        {
799            Err(DefaultOutputsValidatorError::CodeHash)
800        } else if script.args().len() != BLAKE160_LEN {
801            Err(DefaultOutputsValidatorError::ArgsLen)
802        } else {
803            Ok(())
804        }
805    }
806
807    fn validate_secp256k1_blake160_multisig_all(
808        &self,
809        output: &packed::CellOutput,
810    ) -> std::result::Result<(), DefaultOutputsValidatorError> {
811        let script = output.lock();
812        if !script.is_hash_type_type() {
813            Err(DefaultOutputsValidatorError::HashType)
814        } else if script.code_hash()
815            != self
816                .consensus
817                .secp256k1_blake160_multisig_all_type_hash()
818                .expect("No secp256k1_blake160_multisig_all system cell")
819        {
820            Err(DefaultOutputsValidatorError::CodeHash)
821        } else if script.args().len() != BLAKE160_LEN {
822            if script.args().len() == BLAKE160_LEN + SINCE_LEN {
823                if extract_since_from_secp256k1_blake160_multisig_all_args(&script).flags_is_valid()
824                {
825                    Ok(())
826                } else {
827                    Err(DefaultOutputsValidatorError::ArgsSince)
828                }
829            } else {
830                Err(DefaultOutputsValidatorError::ArgsLen)
831            }
832        } else {
833            Ok(())
834        }
835    }
836
837    fn validate_well_known_lock_scripts(
838        &self,
839        output: &packed::CellOutput,
840    ) -> std::result::Result<(), DefaultOutputsValidatorError> {
841        let script = output.lock();
842        if self
843            .well_known_lock_scripts
844            .iter()
845            .any(|well_known_script| is_well_known_script(&script, well_known_script))
846        {
847            Ok(())
848        } else {
849            Err(DefaultOutputsValidatorError::NotWellKnownLockScript)
850        }
851    }
852
853    fn validate_dao(
854        &self,
855        output: &packed::CellOutput,
856    ) -> std::result::Result<(), DefaultOutputsValidatorError> {
857        match output.type_().to_opt() {
858            Some(script) => {
859                if !script.is_hash_type_type() {
860                    Err(DefaultOutputsValidatorError::HashType)
861                } else if script.code_hash() != self.consensus.dao_type_hash() {
862                    Err(DefaultOutputsValidatorError::CodeHash)
863                } else if output.lock().args().len() == BLAKE160_LEN + SINCE_LEN {
864                    // https://github.com/nervosnetwork/ckb/wiki/Common-Gotchas#nervos-dao
865                    let since =
866                        extract_since_from_secp256k1_blake160_multisig_all_args(&output.lock());
867                    match since.extract_metric() {
868                        Some(SinceMetric::EpochNumberWithFraction(_)) if since.is_absolute() => {
869                            Ok(())
870                        }
871                        _ => Err(DefaultOutputsValidatorError::ArgsSince),
872                    }
873                } else {
874                    Ok(())
875                }
876            }
877            None => Ok(()),
878        }
879    }
880
881    fn validate_well_known_type_scripts(
882        &self,
883        output: &packed::CellOutput,
884    ) -> std::result::Result<(), DefaultOutputsValidatorError> {
885        if let Some(script) = output.type_().to_opt() {
886            if self
887                .well_known_type_scripts
888                .iter()
889                .any(|well_known_script| is_well_known_script(&script, well_known_script))
890            {
891                Ok(())
892            } else {
893                Err(DefaultOutputsValidatorError::NotWellKnownTypeScript)
894            }
895        } else {
896            Ok(())
897        }
898    }
899}
900
901const BLAKE160_LEN: usize = 20;
902const SINCE_LEN: usize = 8;
903
904fn extract_since_from_secp256k1_blake160_multisig_all_args(script: &packed::Script) -> Since {
905    Since(u64::from_le_bytes(
906        (&script.args().raw_data()[BLAKE160_LEN..])
907            .try_into()
908            .expect("checked len"),
909    ))
910}
911
912fn is_well_known_script(script: &packed::Script, well_known_script: &packed::Script) -> bool {
913    script.hash_type() == well_known_script.hash_type()
914        && script.code_hash() == well_known_script.code_hash()
915        && script
916            .args()
917            .as_slice()
918            .starts_with(well_known_script.args().as_slice())
919}