Skip to main content

ckb_rpc/module/
test.rs

1use crate::error::RPCError;
2use async_trait::async_trait;
3use ckb_chain::ChainController;
4use ckb_dao::DaoCalculator;
5use ckb_jsonrpc_types::{
6    Block, BlockTemplate, Byte32, EpochNumberWithFraction, OutputsValidator, Transaction,
7};
8use ckb_logger::error;
9use ckb_network::{NetworkController, SupportProtocols};
10use ckb_shared::{Snapshot, shared::Shared};
11use ckb_store::ChainStore;
12use ckb_types::{
13    H256,
14    core::{
15        self, BlockView,
16        cell::{
17            OverlayCellProvider, ResolvedTransaction, TransactionsProvider, resolve_transaction,
18        },
19    },
20    packed,
21    prelude::*,
22};
23use ckb_verification_traits::Switch;
24use jsonrpc_core::Result;
25use jsonrpc_utils::rpc;
26use std::collections::HashSet;
27use std::sync::Arc;
28
29use super::pool::WellKnownScriptsOnlyValidator;
30
31/// RPC for Integration Test.
32#[rpc(openrpc)]
33#[async_trait]
34pub trait IntegrationTestRpc {
35    /// process block without any block verification.
36    ///
37    /// ## Params
38    ///
39    /// * `data` - block data(in binary).
40    ///
41    /// * `broadcast` - true to enable broadcast(relay) the block to other peers.
42    ///
43    /// ## Examples
44    ///
45    /// Request
46    ///
47    /// ```json
48    /// {
49    ///   "id": 42,
50    ///   "jsonrpc": "2.0",
51    ///   "method": "process_block_without_verify",
52    ///   "params": [
53    ///    {
54    /// 	"header": {
55    /// 		"compact_target": "0x1e083126",
56    /// 		"dao": "0xb5a3e047474401001bc476b9ee573000c0c387962a38000000febffacf030000",
57    /// 		"epoch": "0x7080018000001",
58    /// 		"extra_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
59    /// 		"nonce": "0x0",
60    /// 		"number": "0x400",
61    /// 		"parent_hash": "0xae003585fa15309b30b31aed3dcf385e9472c3c3e93746a6c4540629a6a1ed2d",
62    /// 		"proposals_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
63    /// 		"timestamp": "0x5cd2b117",
64    /// 		"transactions_root": "0xc47d5b78b3c4c4c853e2a32810818940d0ee403423bea9ec7b8e566d9595206c",
65    /// 		"version": "0x0"
66    /// 	},
67    /// 	"proposals": [],
68    /// 	"transactions": [{
69    /// 		"cell_deps": [],
70    /// 		"header_deps": [],
71    /// 		"inputs": [{
72    /// 			"previous_output": {
73    /// 				"index": "0xffffffff",
74    /// 				"tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000"
75    /// 			},
76    /// 			"since": "0x400"
77    /// 		}],
78    /// 		"outputs": [{
79    /// 			"capacity": "0x18e64b61cf",
80    /// 			"lock": {
81    /// 				"code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
82    /// 				"hash_type": "data",
83    /// 				"args": "0x"
84    /// 			},
85    /// 			"type": null
86    /// 		}],
87    /// 		"outputs_data": [
88    /// 			"0x"
89    /// 		],
90    /// 		"version": "0x0",
91    /// 		"witnesses": [
92    /// 			"0x450000000c000000410000003500000010000000300000003100000028e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5000000000000000000"
93    /// 		]
94    /// 	}],
95    /// 	"uncles": []
96    ///     },
97    ///     true
98    ///   ]
99    /// }
100    /// ```
101    ///
102    /// Response
103    ///
104    /// ```json
105    /// {
106    ///   "id": 42,
107    ///   "jsonrpc": "2.0",
108    ///   "result": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40"
109    /// }
110    /// ```
111    #[rpc(name = "process_block_without_verify")]
112    fn process_block_without_verify(&self, data: Block, broadcast: bool) -> Result<Option<H256>>;
113
114    /// Truncate chain to specified tip hash, can only truncate less then 50000 blocks each time.
115    ///
116    /// ## Params
117    ///
118    /// * `target_tip_hash` - specified header hash
119    ///
120    /// ## Examples
121    ///
122    /// Request
123    ///
124    /// ```json
125    /// {
126    ///   "id": 42,
127    ///   "jsonrpc": "2.0",
128    ///   "method": "truncate",
129    ///   "params": [
130    ///     "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40"
131    ///   ]
132    /// }
133    /// ```
134    ///
135    /// Response
136    ///
137    /// ```json
138    /// {
139    ///   "id": 42,
140    ///   "jsonrpc": "2.0",
141    ///   "result": null
142    /// }
143    /// ```
144    #[rpc(name = "truncate")]
145    fn truncate(&self, target_tip_hash: H256) -> Result<()>;
146
147    /// Generate block(with verification) and broadcast the block.
148    ///
149    /// Note that if called concurrently, it may return the hash of the same block.
150    ///
151    /// ## Examples
152    ///
153    /// Request
154    ///
155    /// ```json
156    /// {
157    ///   "id": 42,
158    ///   "jsonrpc": "2.0",
159    ///   "method": "generate_block",
160    ///   "params": []
161    /// }
162    /// ```
163    ///
164    /// Response
165    ///
166    /// ```json
167    /// {
168    ///   "id": 42,
169    ///   "jsonrpc": "2.0",
170    ///   "result": "0x60dd3fa0e81db3ee3ad41cf4ab956eae7e89eb71cd935101c26c4d0652db3029"
171    /// }
172    /// ```
173    #[rpc(name = "generate_block")]
174    fn generate_block(&self) -> Result<H256>;
175
176    /// Generate epochs during development, can be useful for scenarios
177    /// like testing DAO-related functionalities.
178    ///
179    /// Returns the updated epoch number after generating the specified number of epochs.
180    ///
181    /// ## Params
182    ///
183    /// * `num_epochs` - The number of epochs to generate.
184    ///
185    /// ## Examples
186    ///
187    /// Request
188    ///
189    /// Generating 2 epochs:
190    ///
191    /// ```json
192    /// {
193    ///   "id": 42,
194    ///   "jsonrpc": "2.0",
195    ///   "method": "generate_epochs",
196    ///   "params": ["0x2"]
197    /// }
198    /// ```
199    ///
200    /// The input parameter "0x2" will be normalized to "0x10000000002"(the correct
201    /// [`EpochNumberWithFraction`](#type-epochnumberwithfraction) type) within the method.
202    /// Therefore, if you want to generate epochs as integers, you can simply pass an integer
203    /// as long as it does not exceed 16777215 (24 bits).
204    ///
205    /// Generating 1/2 epoch:
206    ///
207    /// ```text
208    /// {
209    ///   "id": 42,
210    ///   "jsonrpc": "2.0",
211    ///   "method": "generate_epochs",
212    ///   "params": ["0x20001000000"]
213    /// }
214    /// ```
215    ///
216    /// Response
217    ///
218    /// ```json
219    /// {
220    ///   "id": 42,
221    ///   "jsonrpc": "2.0",
222    ///   "result": "0xa0001000003"
223    /// }
224    /// ```
225    #[rpc(name = "generate_epochs")]
226    fn generate_epochs(
227        &self,
228        num_epochs: EpochNumberWithFraction,
229    ) -> Result<EpochNumberWithFraction>;
230
231    /// Add transaction to tx-pool.
232    ///
233    /// ## Params
234    ///
235    /// * `transaction` - specified transaction to add
236    ///
237    /// ## Examples
238    ///
239    /// Request
240    ///
241    /// ```json
242    /// {
243    /// 	"id": 42,
244    /// 	"jsonrpc": "2.0",
245    /// 	"method": "notify_transaction",
246    /// 	"params":
247    ///     [
248    ///          {
249    /// 			"cell_deps": [{
250    /// 				"dep_type": "code",
251    /// 				"out_point": {
252    /// 					"index": "0x0",
253    /// 					"tx_hash": "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
254    /// 				}
255    /// 			}],
256    /// 			"header_deps": [
257    /// 				"0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed"
258    /// 			],
259    /// 			"inputs": [{
260    /// 				"previous_output": {
261    /// 					"index": "0x0",
262    /// 					"tx_hash": "0x365698b50ca0da75dca2c87f9e7b563811d3b5813736b8cc62cc3b106faceb17"
263    /// 				},
264    /// 				"since": "0x0"
265    /// 			}],
266    /// 			"outputs": [{
267    /// 				"capacity": "0x2540be400",
268    /// 				"lock": {
269    /// 					"code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
270    /// 					"hash_type": "data",
271    /// 					"args": "0x"
272    /// 				},
273    /// 				"type": null
274    /// 			}],
275    /// 			"outputs_data": [
276    /// 				"0x"
277    /// 			],
278    /// 			"version": "0x0",
279    /// 			"witnesses": []
280    /// 		}
281    /// 	]
282    /// }
283    /// ```
284    ///
285    /// Response
286    ///
287    /// ```json
288    /// {
289    ///   "id": 42,
290    ///   "jsonrpc": "2.0",
291    ///   "result": "0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3"
292    /// }
293    /// ```
294    #[rpc(name = "notify_transaction")]
295    fn notify_transaction(&self, transaction: Transaction) -> Result<H256>;
296
297    /// Generate block with block template, attach calculated dao field to build new block,
298    ///
299    /// then process block and broadcast the block.
300    ///
301    /// ## Params
302    ///
303    /// * `block_template` - specified transaction to add
304    ///
305    /// ## Examples
306    ///
307    /// Request
308    ///
309    /// ```json
310    /// {
311    ///   "id": 42,
312    ///   "jsonrpc": "2.0",
313    ///   "method": "generate_block_with_template",
314    ///   "params": [
315    ///    {
316    ///     "bytes_limit": "0x91c08",
317    ///     "cellbase": {
318    ///       "cycles": null,
319    ///       "data": {
320    ///         "cell_deps": [],
321    ///         "header_deps": [],
322    ///         "inputs": [
323    ///           {
324    ///             "previous_output": {
325    ///               "index": "0xffffffff",
326    ///               "tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000"
327    ///             },
328    ///             "since": "0x401"
329    ///           }
330    ///         ],
331    ///        "outputs": [
332    ///          {
333    ///            "capacity": "0x18e64efc04",
334    ///             "lock": {
335    ///               "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
336    ///               "hash_type": "data",
337    ///               "args": "0x"
338    ///             },
339    ///             "type": null
340    ///           }
341    ///         ],
342    ///         "outputs_data": [
343    ///           "0x"
344    ///         ],
345    ///         "version": "0x0",
346    ///         "witnesses": [
347    ///           "0x650000000c00000055000000490000001000000030000000310000001892ea40d82b53c678ff88312450bbb17e164d7a3e0a90941aa58839f56f8df20114000000b2e61ff569acf041b3c2c17724e2379c581eeac30c00000054455354206d657373616765"
348    ///         ]
349    ///       },
350    ///       "hash": "0xbaf7e4db2fd002f19a597ca1a31dfe8cfe26ed8cebc91f52b75b16a7a5ec8bab"
351    ///     },
352    ///     "compact_target": "0x1e083126",
353    ///     "current_time": "0x174c45e17a3",
354    ///     "cycles_limit": "0xd09dc300",
355    ///     "dao": "0xd495a106684401001e47c0ae1d5930009449d26e32380000000721efd0030000",
356    ///     "epoch": "0x7080019000001",
357    ///     "extension": "0xb0a0079f3778c0ba0d89d88b389c602cc18b8a0355d16c0713f8bfcee64b5f84",
358    ///     "number": "0x401",
359    ///     "parent_hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
360    ///     "proposals": ["0xa0ef4eb5f4ceeb08a4c8"],
361    ///     "transactions": [],
362    ///     "uncles": [
363    ///       {
364    ///         "hash": "0xdca341a42890536551f99357612cef7148ed471e3b6419d0844a4e400be6ee94",
365    ///         "header": {
366    ///           "compact_target": "0x1e083126",
367    ///           "dao": "0xb5a3e047474401001bc476b9ee573000c0c387962a38000000febffacf030000",
368    ///           "epoch": "0x7080018000001",
369    ///           "extra_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
370    ///           "nonce": "0x0",
371    ///           "number": "0x400",
372    ///           "parent_hash": "0xae003585fa15309b30b31aed3dcf385e9472c3c3e93746a6c4540629a6a1ed2d",
373    ///           "proposals_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
374    ///           "timestamp": "0x5cd2b118",
375    ///           "transactions_root": "0xc47d5b78b3c4c4c853e2a32810818940d0ee403423bea9ec7b8e566d9595206c",
376    ///           "version":"0x0"
377    ///         },
378    ///         "proposals": [],
379    ///         "required": false
380    ///       }
381    ///     ],
382    ///     "uncles_count_limit": "0x2",
383    ///     "version": "0x0",
384    ///     "work_id": "0x0"
385    ///    }
386    ///  ]
387    /// }
388    /// ```
389    ///
390    /// Response
391    ///
392    /// ```json
393    /// {
394    ///   "id": 42,
395    ///   "jsonrpc": "2.0",
396    ///   "result": "0x899541646ae412a99fdbefc081e1a782605a7815998a096af16e51d4df352c75"
397    /// }
398    /// ```
399    #[rpc(name = "generate_block_with_template")]
400    fn generate_block_with_template(&self, block_template: BlockTemplate) -> Result<H256>;
401
402    /// Return calculated dao field according to specified block template.
403    ///
404    /// ## Params
405    ///
406    /// * `block_template` - specified block template
407    ///
408    /// ## Examples
409    ///
410    /// Request
411    ///
412    /// ```json
413    /// {
414    ///   "id": 42,
415    ///   "jsonrpc": "2.0",
416    ///   "method": "calculate_dao_field",
417    ///   "params": [
418    ///    {
419    ///     "bytes_limit": "0x91c08",
420    ///     "cellbase": {
421    ///       "cycles": null,
422    ///       "data": {
423    ///         "cell_deps": [],
424    ///         "header_deps": [],
425    ///         "inputs": [
426    ///           {
427    ///             "previous_output": {
428    ///               "index": "0xffffffff",
429    ///               "tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000"
430    ///             },
431    ///             "since": "0x401"
432    ///           }
433    ///         ],
434    ///        "outputs": [
435    ///          {
436    ///            "capacity": "0x18e64efc04",
437    ///             "lock": {
438    ///               "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
439    ///               "hash_type": "data",
440    ///               "args": "0x"
441    ///             },
442    ///             "type": null
443    ///           }
444    ///         ],
445    ///         "outputs_data": [
446    ///           "0x"
447    ///         ],
448    ///         "version": "0x0",
449    ///         "witnesses": [
450    ///           "0x650000000c00000055000000490000001000000030000000310000001892ea40d82b53c678ff88312450bbb17e164d7a3e0a90941aa58839f56f8df20114000000b2e61ff569acf041b3c2c17724e2379c581eeac30c00000054455354206d657373616765"
451    ///         ]
452    ///       },
453    ///       "hash": "0xbaf7e4db2fd002f19a597ca1a31dfe8cfe26ed8cebc91f52b75b16a7a5ec8bab"
454    ///     },
455    ///     "compact_target": "0x1e083126",
456    ///     "current_time": "0x174c45e17a3",
457    ///     "cycles_limit": "0xd09dc300",
458    ///     "dao": "0xd495a106684401001e47c0ae1d5930009449d26e32380000000721efd0030000",
459    ///     "epoch": "0x7080019000001",
460    ///     "extension": "0xb0a0079f3778c0ba0d89d88b389c602cc18b8a0355d16c0713f8bfcee64b5f84",
461    ///     "number": "0x401",
462    ///     "parent_hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
463    ///     "proposals": ["0xa0ef4eb5f4ceeb08a4c8"],
464    ///     "transactions": [],
465    ///     "uncles": [
466    ///       {
467    ///         "hash": "0xdca341a42890536551f99357612cef7148ed471e3b6419d0844a4e400be6ee94",
468    ///         "header": {
469    ///           "compact_target": "0x1e083126",
470    ///           "dao": "0xb5a3e047474401001bc476b9ee573000c0c387962a38000000febffacf030000",
471    ///           "epoch": "0x7080018000001",
472    ///           "extra_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
473    ///           "nonce": "0x0",
474    ///           "number": "0x400",
475    ///           "parent_hash": "0xae003585fa15309b30b31aed3dcf385e9472c3c3e93746a6c4540629a6a1ed2d",
476    ///           "proposals_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
477    ///           "timestamp": "0x5cd2b118",
478    ///           "transactions_root": "0xc47d5b78b3c4c4c853e2a32810818940d0ee403423bea9ec7b8e566d9595206c",
479    ///           "version":"0x0"
480    ///         },
481    ///         "proposals": [],
482    ///         "required": false
483    ///       }
484    ///     ],
485    ///     "uncles_count_limit": "0x2",
486    ///     "version": "0x0",
487    ///     "work_id": "0x0"
488    ///    }
489    ///   ]
490    /// }
491    /// ```
492    ///
493    /// Response
494    ///
495    /// ```json
496    /// {
497    ///   "id": 42,
498    ///   "jsonrpc": "2.0",
499    ///   "result": "0xd495a106684401001e47c0ae1d5930009449d26e32380000000721efd0030000"
500    /// }
501    /// ```
502    #[rpc(name = "calculate_dao_field")]
503    fn calculate_dao_field(&self, block_template: BlockTemplate) -> Result<Byte32>;
504
505    /// Submits a new test local transaction into the transaction pool, only for testing.
506    /// If the transaction is already in the pool, rebroadcast it to peers.
507    ///
508    /// ## Params
509    ///
510    /// * `transaction` - The transaction.
511    /// * `outputs_validator` - Validates the transaction outputs before entering the tx-pool. (**Optional**, default is "passthrough").
512    ///
513    /// ## Errors
514    ///
515    /// * [`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".
516    /// * [`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`.
517    /// * [`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`.
518    /// * [`PoolIsFull (-1106)`](../enum.RPCError.html#variant.PoolIsFull) - Pool is full.
519    /// * [`PoolRejectedDuplicatedTransaction (-1107)`](../enum.RPCError.html#variant.PoolRejectedDuplicatedTransaction) - The transaction is already in the pool.
520    /// * [`TransactionFailedToResolve (-301)`](../enum.RPCError.html#variant.TransactionFailedToResolve) - Failed to resolve the referenced cells and headers used in the transaction, as inputs or dependencies.
521    /// * [`TransactionFailedToVerify (-302)`](../enum.RPCError.html#variant.TransactionFailedToVerify) - Failed to verify the transaction.
522    ///
523    /// ## Examples
524    ///
525    /// Request
526    ///
527    /// ```json
528    /// {
529    ///   "id": 42,
530    ///   "jsonrpc": "2.0",
531    ///   "method": "send_test_transaction",
532    ///   "params": [
533    ///     {
534    ///       "cell_deps": [
535    ///         {
536    ///           "dep_type": "code",
537    ///           "out_point": {
538    ///             "index": "0x0",
539    ///             "tx_hash": "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
540    ///           }
541    ///         }
542    ///       ],
543    ///       "header_deps": [
544    ///         "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed"
545    ///       ],
546    ///       "inputs": [
547    ///         {
548    ///           "previous_output": {
549    ///             "index": "0x0",
550    ///             "tx_hash": "0x365698b50ca0da75dca2c87f9e7b563811d3b5813736b8cc62cc3b106faceb17"
551    ///           },
552    ///           "since": "0x0"
553    ///         }
554    ///       ],
555    ///       "outputs": [
556    ///         {
557    ///           "capacity": "0x2540be400",
558    ///           "lock": {
559    ///             "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
560    ///             "hash_type": "data",
561    ///             "args": "0x"
562    ///           },
563    ///           "type": null
564    ///         }
565    ///       ],
566    ///       "outputs_data": [
567    ///         "0x"
568    ///       ],
569    ///       "version": "0x0",
570    ///       "witnesses": []
571    ///     },
572    ///     "passthrough"
573    ///   ]
574    /// }
575    /// ```
576    ///
577    /// Response
578    ///
579    /// ```json
580    /// {
581    ///   "id": 42,
582    ///   "jsonrpc": "2.0",
583    ///   "result": "0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3"
584    /// }
585    /// ```
586    ///
587    #[rpc(name = "send_test_transaction")]
588    fn send_test_transaction(
589        &self,
590        tx: Transaction,
591        outputs_validator: Option<OutputsValidator>,
592    ) -> Result<H256>;
593}
594
595#[derive(Clone)]
596pub(crate) struct IntegrationTestRpcImpl {
597    pub network_controller: NetworkController,
598    pub shared: Shared,
599    pub chain: ChainController,
600    pub well_known_lock_scripts: Vec<packed::Script>,
601    pub well_known_type_scripts: Vec<packed::Script>,
602}
603
604#[async_trait]
605impl IntegrationTestRpc for IntegrationTestRpcImpl {
606    fn process_block_without_verify(&self, data: Block, broadcast: bool) -> Result<Option<H256>> {
607        let block: packed::Block = data.into();
608        let block: Arc<BlockView> = Arc::new(block.into_view());
609        let ret = self
610            .chain
611            .blocking_process_block_with_switch(Arc::clone(&block), Switch::DISABLE_ALL);
612        if broadcast {
613            let content = packed::CompactBlock::build_from_block(&block, &HashSet::new());
614            let message = packed::RelayMessage::new_builder().set(content).build();
615            self.network_controller.quick_broadcast_with_handle(
616                SupportProtocols::RelayV3.protocol_id(),
617                message.as_bytes(),
618                self.shared.async_handle(),
619            );
620        }
621        if ret.is_ok() {
622            Ok(Some(block.hash().into()))
623        } else {
624            error!("process_block_without_verify error: {:?}", ret);
625            Ok(None)
626        }
627    }
628
629    fn truncate(&self, target_tip_hash: H256) -> Result<()> {
630        let header = {
631            let snapshot = self.shared.snapshot();
632            let header = snapshot
633                .get_block_header(&target_tip_hash.into())
634                .ok_or_else(|| {
635                    RPCError::custom(RPCError::Invalid, "block not found".to_string())
636                })?;
637            if !snapshot.is_main_chain(&header.hash()) {
638                return Err(RPCError::custom(
639                    RPCError::Invalid,
640                    "block not on main chain".to_string(),
641                ));
642            }
643            header
644        };
645
646        // Truncate the chain and database
647        self.chain
648            .truncate(header.hash())
649            .map_err(|err| RPCError::custom(RPCError::Invalid, err.to_string()))?;
650
651        // Clear the tx_pool
652        let new_snapshot = Arc::clone(&self.shared.snapshot());
653        let tx_pool = self.shared.tx_pool_controller();
654        tx_pool
655            .clear_pool(new_snapshot)
656            .map_err(|err| RPCError::custom(RPCError::Invalid, err.to_string()))?;
657
658        Ok(())
659    }
660
661    fn generate_block(&self) -> Result<H256> {
662        let tx_pool = self.shared.tx_pool_controller();
663        let block_template = tx_pool
664            .get_block_template(None, None, None)
665            .map_err(|err| RPCError::custom(RPCError::Invalid, err.to_string()))?
666            .map_err(|err| RPCError::custom(RPCError::CKBInternalError, err.to_string()))?;
667
668        self.process_and_announce_block(block_template.into())
669    }
670
671    fn generate_epochs(
672        &self,
673        num_epochs: EpochNumberWithFraction,
674    ) -> Result<EpochNumberWithFraction> {
675        let tip_block_number = self.shared.snapshot().tip_header().number();
676        let mut current_epoch = self
677            .shared
678            .snapshot()
679            .epoch_ext()
680            .number_with_fraction(tip_block_number);
681        let target_epoch = current_epoch.to_rational()
682            + core::EpochNumberWithFraction::from_full_value(num_epochs.into()).to_rational();
683
684        let tx_pool = self.shared.tx_pool_controller();
685        while current_epoch.to_rational() < target_epoch {
686            let block_template = tx_pool
687                .get_block_template(None, None, None)
688                .map_err(|err| RPCError::custom(RPCError::Invalid, err.to_string()))?
689                .map_err(|err| RPCError::custom(RPCError::CKBInternalError, err.to_string()))?;
690            current_epoch =
691                core::EpochNumberWithFraction::from_full_value(block_template.epoch.into());
692            self.process_and_announce_block(block_template.into())?;
693        }
694
695        Ok(current_epoch.full_value().into())
696    }
697
698    fn notify_transaction(&self, tx: Transaction) -> Result<H256> {
699        let tx: packed::Transaction = tx.into();
700        let tx: core::TransactionView = tx.into_view();
701        let tx_pool = self.shared.tx_pool_controller();
702        let tx_hash = tx.hash();
703        if let Err(e) = tx_pool.notify_txs(vec![tx]) {
704            error!("Send notify_txs request error {}", e);
705            return Err(RPCError::ckb_internal_error(e));
706        }
707        Ok(tx_hash.into())
708    }
709
710    fn generate_block_with_template(&self, block_template: BlockTemplate) -> Result<H256> {
711        let dao_field = self.calculate_dao_field(block_template.clone())?;
712
713        let mut update_dao_template = block_template;
714        update_dao_template.dao = dao_field;
715        let block = update_dao_template.into();
716        self.process_and_announce_block(block)
717    }
718
719    fn calculate_dao_field(&self, block_template: BlockTemplate) -> Result<Byte32> {
720        let snapshot: &Snapshot = &self.shared.snapshot();
721        let consensus = snapshot.consensus();
722        let parent_header = snapshot
723            .get_block_header(&(&block_template.parent_hash).into())
724            .expect("parent header should be stored");
725        let mut seen_inputs = HashSet::new();
726
727        let txs: Vec<_> = packed::Block::from(block_template)
728            .transactions()
729            .into_iter()
730            .map(|tx| tx.into_view())
731            .collect();
732
733        let transactions_provider = TransactionsProvider::new(txs.as_slice().iter());
734        let overlay_cell_provider = OverlayCellProvider::new(&transactions_provider, snapshot);
735        let rtxs = txs
736            .iter()
737            .map(|tx| {
738                resolve_transaction(
739                    tx.clone(),
740                    &mut seen_inputs,
741                    &overlay_cell_provider,
742                    snapshot,
743                )
744                .map_err(|err| {
745                    error!(
746                        "Resolve transactions error when generating block \
747                         with block template, error: {:?}",
748                        err
749                    );
750                    RPCError::invalid_params(err.to_string())
751                })
752            })
753            .collect::<Result<Vec<ResolvedTransaction>>>()?;
754
755        Ok(
756            DaoCalculator::new(consensus, &snapshot.borrow_as_data_loader())
757                .dao_field(rtxs.iter(), &parent_header)
758                .expect("dao calculation should be OK")
759                .into(),
760        )
761    }
762
763    fn send_test_transaction(
764        &self,
765        tx: Transaction,
766        outputs_validator: Option<OutputsValidator>,
767    ) -> Result<H256> {
768        let tx: packed::Transaction = tx.into();
769        let tx: core::TransactionView = tx.into_view();
770
771        if let Err(e) = match outputs_validator {
772            None | Some(OutputsValidator::Passthrough) => Ok(()),
773            Some(OutputsValidator::WellKnownScriptsOnly) => WellKnownScriptsOnlyValidator::new(
774                self.shared.consensus(),
775                &self.well_known_lock_scripts,
776                &self.well_known_type_scripts,
777            )
778            .validate(&tx),
779        } {
780            return Err(RPCError::custom_with_data(
781                RPCError::PoolRejectedTransactionByOutputsValidator,
782                format!(
783                    "The transaction is rejected by OutputsValidator set in params[1]: {}. \
784                    Please check the related information in https://github.com/nervosnetwork/ckb/wiki/Transaction-%C2%BB-Default-Outputs-Validator",
785                    outputs_validator
786                        .unwrap_or(OutputsValidator::WellKnownScriptsOnly)
787                        .json_display()
788                ),
789                e,
790            ));
791        }
792
793        let tx_pool = self.shared.tx_pool_controller();
794        let submit_tx = tx_pool.submit_local_test_tx(tx.clone());
795
796        if let Err(e) = submit_tx {
797            error!("Send submit_tx request error {}", e);
798            return Err(RPCError::ckb_internal_error(e));
799        }
800
801        let tx_hash = tx.hash();
802        match submit_tx.unwrap() {
803            Ok(_) => Ok(tx_hash.into()),
804            Err(reject) => Err(RPCError::from_submit_transaction_reject(&reject)),
805        }
806    }
807}
808
809impl IntegrationTestRpcImpl {
810    fn process_and_announce_block(&self, block: packed::Block) -> Result<H256> {
811        let block_view = Arc::new(block.into_view());
812        let content = packed::CompactBlock::build_from_block(&block_view, &HashSet::new());
813        let message = packed::RelayMessage::new_builder().set(content).build();
814
815        // insert block to chain
816        self.chain
817            .blocking_process_block(Arc::clone(&block_view))
818            .map_err(|err| RPCError::custom(RPCError::CKBInternalError, err.to_string()))?;
819
820        // announce new block
821        self.network_controller.quick_broadcast_with_handle(
822            SupportProtocols::RelayV3.protocol_id(),
823            message.as_bytes(),
824            self.shared.async_handle(),
825        );
826
827        Ok(block_view.header().hash().into())
828    }
829}