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