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}