diff --git a/Cargo.lock b/Cargo.lock
index 76e1954280..c3ac071e90 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1501,12 +1501,15 @@ dependencies = [
"ckb-traits",
"ckb-types",
"ckb-vm",
+ "daggy",
"faster-hex",
+ "molecule",
"proptest",
"rand 0.8.5",
"serde",
"tempfile",
"tiny-keccak",
+ "tokio",
]
[[package]]
@@ -1700,6 +1703,7 @@ dependencies = [
"ckb-metrics",
"ckb-network",
"ckb-reward-calculator",
+ "ckb-script",
"ckb-snapshot",
"ckb-stop-handler",
"ckb-store",
@@ -1711,6 +1715,7 @@ dependencies = [
"hyper",
"lru",
"multi_index_map",
+ "num_cpus",
"rand 0.8.5",
"rustc-hash",
"sentry",
@@ -1775,6 +1780,7 @@ dependencies = [
"ckb-verification-traits",
"derive_more",
"lru",
+ "tokio",
]
[[package]]
@@ -1813,9 +1819,9 @@ dependencies = [
[[package]]
name = "ckb-vm"
-version = "0.24.9"
+version = "0.24.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2c3d68dc7f891e5555c7ebc054722b28ab005e51c5076f54c20d36002dc8e83"
+checksum = "ddff96029d3298cb630e95f29d4b9a93384e938a0b75758684aa8794b53bdd1a"
dependencies = [
"byteorder",
"bytes",
@@ -1831,9 +1837,9 @@ dependencies = [
[[package]]
name = "ckb-vm-definitions"
-version = "0.24.9"
+version = "0.24.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2fdf9c8ee14409b2208d23b9ad88828242d7881153ddc04872b66d2e018a52f"
+checksum = "c280bf1d589d23ab0358f58601c2187fc6be86a131644583ef72ea96a0a13ddd"
dependencies = [
"paste",
]
@@ -2089,11 +2095,10 @@ dependencies = [
[[package]]
name = "crossbeam"
-version = "0.8.2"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
+checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
dependencies = [
- "cfg-if",
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
@@ -2222,6 +2227,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "daggy"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91a9304e55e9d601a39ae4deaba85406d5c0980e106f65afcf0460e9af1e7602"
+dependencies = [
+ "petgraph",
+]
+
[[package]]
name = "darling"
version = "0.20.8"
@@ -3580,13 +3594,12 @@ dependencies = [
[[package]]
name = "minstant"
-version = "0.1.4"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8dfc09c8abbe145769b6d51fd03f84fdd459906cbd6ac54e438708f016b40bd"
+checksum = "1fb9b5c752f145ac5046bccc3c4f62892e3c950c1d1eab80c5949cd68a2078db"
dependencies = [
"ctor",
- "libc",
- "wasi 0.7.0",
+ "web-time",
]
[[package]]
@@ -6196,12 +6209,6 @@ dependencies = [
"try-lock",
]
-[[package]]
-name = "wasi"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
-
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
@@ -6296,6 +6303,16 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "webpki"
version = "0.22.4"
diff --git a/Makefile b/Makefile
index 79a84eedcb..91ef1c2a5d 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ MOLC := moleculec
MOLC_VERSION := 0.7.5
VERBOSE := $(if ${CI},--verbose,)
CLIPPY_OPTS := -D warnings -D clippy::clone_on_ref_ptr -D clippy::redundant_clone -D clippy::enum_glob_use -D clippy::fallible_impl_from \
- -A clippy::mutable_key_type -A clippy::upper_case_acronyms
+ -A clippy::mutable_key_type -A clippy::upper_case_acronyms -A clippy::needless_return
CKB_TEST_ARGS := -c 4 ${CKB_TEST_ARGS}
CKB_FEATURES ?= deadlock_detection,with_sentry
ALL_FEATURES := deadlock_detection,with_sentry,with_dns_seeding,profiling,march-native
diff --git a/chain/src/tests/load_code_with_snapshot.rs b/chain/src/tests/load_code_with_snapshot.rs
index 42db84283d..9a51e6e82b 100644
--- a/chain/src/tests/load_code_with_snapshot.rs
+++ b/chain/src/tests/load_code_with_snapshot.rs
@@ -113,7 +113,7 @@ fn test_load_code() {
let tx_status = tx_pool.get_tx_status(tx.hash());
assert_eq!(
tx_status.unwrap().unwrap(),
- (TxStatus::Pending, Some(11174))
+ (TxStatus::Pending, Some(11325))
);
}
diff --git a/rpc/README.md b/rpc/README.md
index 939792d82f..e85719ae50 100644
--- a/rpc/README.md
+++ b/rpc/README.md
@@ -85,6 +85,7 @@ The crate `ckb-rpc`'s minimum supported rustc version is 1.71.1.
* [Method `notify_transaction`](#integration_test-notify_transaction)
* [Method `generate_block_with_template`](#integration_test-generate_block_with_template)
* [Method `calculate_dao_field`](#integration_test-calculate_dao_field)
+ * [Method `send_test_transaction`](#integration_test-send_test_transaction)
* [Module Miner](#module-miner) [👉 OpenRPC spec](http://playground.open-rpc.org/?uiSchema[appBar][ui:title]=CKB-Miner&uiSchema[appBar][ui:splitView]=false&uiSchema[appBar][ui:examplesDropdown]=false&uiSchema[appBar][ui:logoUrl]=https://raw.githubusercontent.com/nervosnetwork/ckb-rpc-resources/develop/ckb-logo.jpg&schemaUrl=https://raw.githubusercontent.com/nervosnetwork/ckb-rpc-resources/develop/json/miner_rpc_doc.json)
* [Method `get_block_template`](#miner-get_block_template)
@@ -3549,6 +3550,95 @@ Response
}
```
+
+#### Method `send_test_transaction`
+* `send_test_transaction(tx, outputs_validator)`
+ * `tx`: [`Transaction`](#type-transaction)
+ * `outputs_validator`: [`OutputsValidator`](#type-outputsvalidator) `|` `null`
+* result: [`H256`](#type-h256)
+
+Submits a new test local transaction into the transaction pool, only for testing.
+If the transaction is already in the pool, rebroadcast it to peers.
+
+###### Params
+
+* `transaction` - The transaction.
+* `outputs_validator` - Validates the transaction outputs before entering the tx-pool. (**Optional**, default is "passthrough").
+
+###### Errors
+
+* [`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".
+* [`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`.
+* [`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`.
+* [`PoolIsFull (-1106)`](../enum.RPCError.html#variant.PoolIsFull) - Pool is full.
+* [`PoolRejectedDuplicatedTransaction (-1107)`](../enum.RPCError.html#variant.PoolRejectedDuplicatedTransaction) - The transaction is already in the pool.
+* [`TransactionFailedToResolve (-301)`](../enum.RPCError.html#variant.TransactionFailedToResolve) - Failed to resolve the referenced cells and headers used in the transaction, as inputs or dependencies.
+* [`TransactionFailedToVerify (-302)`](../enum.RPCError.html#variant.TransactionFailedToVerify) - Failed to verify the transaction.
+
+###### Examples
+
+Request
+
+```json
+{
+ "id": 42,
+ "jsonrpc": "2.0",
+ "method": "send_test_transaction",
+ "params": [
+ {
+ "cell_deps": [
+ {
+ "dep_type": "code",
+ "out_point": {
+ "index": "0x0",
+ "tx_hash": "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
+ }
+ }
+ ],
+ "header_deps": [
+ "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed"
+ ],
+ "inputs": [
+ {
+ "previous_output": {
+ "index": "0x0",
+ "tx_hash": "0x365698b50ca0da75dca2c87f9e7b563811d3b5813736b8cc62cc3b106faceb17"
+ },
+ "since": "0x0"
+ }
+ ],
+ "outputs": [
+ {
+ "capacity": "0x2540be400",
+ "lock": {
+ "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
+ "hash_type": "data",
+ "args": "0x"
+ },
+ "type": null
+ }
+ ],
+ "outputs_data": [
+ "0x"
+ ],
+ "version": "0x0",
+ "witnesses": []
+ },
+ "passthrough"
+ ]
+}
+```
+
+Response
+
+```json
+{
+ "id": 42,
+ "jsonrpc": "2.0",
+ "result": "0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3"
+}
+```
+
### Module `Miner`
- [👉 OpenRPC spec](http://playground.open-rpc.org/?uiSchema[appBar][ui:title]=CKB-Miner&uiSchema[appBar][ui:splitView]=false&uiSchema[appBar][ui:examplesDropdown]=false&uiSchema[appBar][ui:logoUrl]=https://raw.githubusercontent.com/nervosnetwork/ckb-rpc-resources/develop/ckb-logo.jpg&schemaUrl=https://raw.githubusercontent.com/nervosnetwork/ckb-rpc-resources/develop/json/miner_rpc_doc.json)
@@ -4619,7 +4709,8 @@ Response
"tip_number": "0x400",
"total_tx_cycles": "0x219",
"total_tx_size": "0x112",
- "tx_size_limit": "0x7d000"
+ "tx_size_limit": "0x7d000",
+ "verify_queue_size": "0x0"
}
}
```
@@ -7160,6 +7251,8 @@ Transaction pool information.
Transactions with a large size close to the block size limit may not be packaged, because the block header and cellbase are occupied, so the tx-pool is limited to accepting transaction up to tx_size_limit.
+* `verify_queue_size`: [`Uint64`](#type-uint64) - verify_queue size
+
### Type `TxStatus`
Transaction status and the block hash if it is committed.
diff --git a/rpc/src/module/pool.rs b/rpc/src/module/pool.rs
index aab1071e0d..9fe30b357e 100644
--- a/rpc/src/module/pool.rs
+++ b/rpc/src/module/pool.rs
@@ -287,7 +287,8 @@ pub trait PoolRpc {
/// "tip_number": "0x400",
/// "total_tx_cycles": "0x219",
/// "total_tx_size": "0x112",
- /// "tx_size_limit": "0x7d000"
+ /// "tx_size_limit": "0x7d000",
+ /// "verify_queue_size": "0x0"
/// }
/// }
/// ```
diff --git a/rpc/src/module/test.rs b/rpc/src/module/test.rs
index 72f2afbbc2..1974b39404 100644
--- a/rpc/src/module/test.rs
+++ b/rpc/src/module/test.rs
@@ -2,7 +2,9 @@ use crate::error::RPCError;
use async_trait::async_trait;
use ckb_chain::ChainController;
use ckb_dao::DaoCalculator;
-use ckb_jsonrpc_types::{Block, BlockTemplate, Byte32, EpochNumberWithFraction, Transaction};
+use ckb_jsonrpc_types::{
+ Block, BlockTemplate, Byte32, EpochNumberWithFraction, OutputsValidator, Transaction,
+};
use ckb_logger::error;
use ckb_network::{NetworkController, SupportProtocols};
use ckb_shared::{shared::Shared, Snapshot};
@@ -25,6 +27,8 @@ use jsonrpc_utils::rpc;
use std::collections::HashSet;
use std::sync::Arc;
+use super::pool::WellKnownScriptsOnlyValidator;
+
/// RPC for Integration Test.
#[rpc(openrpc)]
#[async_trait]
@@ -498,6 +502,95 @@ pub trait IntegrationTestRpc {
/// ```
#[rpc(name = "calculate_dao_field")]
fn calculate_dao_field(&self, block_template: BlockTemplate) -> Result;
+
+ /// Submits a new test local transaction into the transaction pool, only for testing.
+ /// If the transaction is already in the pool, rebroadcast it to peers.
+ ///
+ /// ## Params
+ ///
+ /// * `transaction` - The transaction.
+ /// * `outputs_validator` - Validates the transaction outputs before entering the tx-pool. (**Optional**, default is "passthrough").
+ ///
+ /// ## Errors
+ ///
+ /// * [`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".
+ /// * [`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`.
+ /// * [`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`.
+ /// * [`PoolIsFull (-1106)`](../enum.RPCError.html#variant.PoolIsFull) - Pool is full.
+ /// * [`PoolRejectedDuplicatedTransaction (-1107)`](../enum.RPCError.html#variant.PoolRejectedDuplicatedTransaction) - The transaction is already in the pool.
+ /// * [`TransactionFailedToResolve (-301)`](../enum.RPCError.html#variant.TransactionFailedToResolve) - Failed to resolve the referenced cells and headers used in the transaction, as inputs or dependencies.
+ /// * [`TransactionFailedToVerify (-302)`](../enum.RPCError.html#variant.TransactionFailedToVerify) - Failed to verify the transaction.
+ ///
+ /// ## Examples
+ ///
+ /// Request
+ ///
+ /// ```json
+ /// {
+ /// "id": 42,
+ /// "jsonrpc": "2.0",
+ /// "method": "send_test_transaction",
+ /// "params": [
+ /// {
+ /// "cell_deps": [
+ /// {
+ /// "dep_type": "code",
+ /// "out_point": {
+ /// "index": "0x0",
+ /// "tx_hash": "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
+ /// }
+ /// }
+ /// ],
+ /// "header_deps": [
+ /// "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed"
+ /// ],
+ /// "inputs": [
+ /// {
+ /// "previous_output": {
+ /// "index": "0x0",
+ /// "tx_hash": "0x365698b50ca0da75dca2c87f9e7b563811d3b5813736b8cc62cc3b106faceb17"
+ /// },
+ /// "since": "0x0"
+ /// }
+ /// ],
+ /// "outputs": [
+ /// {
+ /// "capacity": "0x2540be400",
+ /// "lock": {
+ /// "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
+ /// "hash_type": "data",
+ /// "args": "0x"
+ /// },
+ /// "type": null
+ /// }
+ /// ],
+ /// "outputs_data": [
+ /// "0x"
+ /// ],
+ /// "version": "0x0",
+ /// "witnesses": []
+ /// },
+ /// "passthrough"
+ /// ]
+ /// }
+ /// ```
+ ///
+ /// Response
+ ///
+ /// ```json
+ /// {
+ /// "id": 42,
+ /// "jsonrpc": "2.0",
+ /// "result": "0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3"
+ /// }
+ /// ```
+ ///
+ #[rpc(name = "send_test_transaction")]
+ fn send_test_transaction(
+ &self,
+ tx: Transaction,
+ outputs_validator: Option,
+ ) -> Result;
}
#[derive(Clone)]
@@ -505,6 +598,8 @@ pub(crate) struct IntegrationTestRpcImpl {
pub network_controller: NetworkController,
pub shared: Shared,
pub chain: ChainController,
+ pub well_known_lock_scripts: Vec,
+ pub well_known_type_scripts: Vec,
}
#[async_trait]
@@ -666,6 +761,49 @@ impl IntegrationTestRpc for IntegrationTestRpcImpl {
.into(),
)
}
+
+ fn send_test_transaction(
+ &self,
+ tx: Transaction,
+ outputs_validator: Option,
+ ) -> Result {
+ let tx: packed::Transaction = tx.into();
+ let tx: core::TransactionView = tx.into_view();
+
+ if let Err(e) = match outputs_validator {
+ None | Some(OutputsValidator::Passthrough) => Ok(()),
+ Some(OutputsValidator::WellKnownScriptsOnly) => WellKnownScriptsOnlyValidator::new(
+ self.shared.consensus(),
+ &self.well_known_lock_scripts,
+ &self.well_known_type_scripts,
+ )
+ .validate(&tx),
+ } {
+ return Err(RPCError::custom_with_data(
+ RPCError::PoolRejectedTransactionByOutputsValidator,
+ format!(
+ "The transaction is rejected by OutputsValidator set in params[1]: {}. \
+ Please check the related information in https://github.com/nervosnetwork/ckb/wiki/Transaction-%C2%BB-Default-Outputs-Validator",
+ outputs_validator.unwrap_or(OutputsValidator::WellKnownScriptsOnly).json_display()
+ ),
+ e,
+ ));
+ }
+
+ let tx_pool = self.shared.tx_pool_controller();
+ let submit_tx = tx_pool.submit_local_test_tx(tx.clone());
+
+ if let Err(e) = submit_tx {
+ error!("Send submit_tx request error {}", e);
+ return Err(RPCError::ckb_internal_error(e));
+ }
+
+ let tx_hash = tx.hash();
+ match submit_tx.unwrap() {
+ Ok(_) => Ok(tx_hash.unpack()),
+ Err(reject) => Err(RPCError::from_submit_transaction_reject(&reject)),
+ }
+ }
}
impl IntegrationTestRpcImpl {
diff --git a/rpc/src/service_builder.rs b/rpc/src/service_builder.rs
index 22d49069ad..c71dea35fe 100644
--- a/rpc/src/service_builder.rs
+++ b/rpc/src/service_builder.rs
@@ -144,6 +144,8 @@ impl<'a> ServiceBuilder<'a> {
shared: Shared,
network_controller: NetworkController,
chain: ChainController,
+ well_known_lock_scripts: Vec