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}