From 91f202bc0b8eb08b2be7bd7a079a43de37daf93d Mon Sep 17 00:00:00 2001 From: zhangsoledad <787953403@qq.com> Date: Fri, 28 Oct 2022 12:11:17 +0800 Subject: [PATCH] feat: add rpc estimate_cycles --- rpc/README.md | 117 +++++++++++++++++--- rpc/src/module/chain.rs | 158 ++++++++++++++++++++++++++- rpc/src/module/experiment.rs | 85 ++------------ rpc/src/module/mod.rs | 2 +- test/src/rpc.rs | 10 +- util/jsonrpc-types/src/experiment.rs | 4 +- util/jsonrpc-types/src/lib.rs | 2 +- 7 files changed, 277 insertions(+), 101 deletions(-) diff --git a/rpc/README.md b/rpc/README.md index 96c8bd197c..0aa1daf220 100644 --- a/rpc/README.md +++ b/rpc/README.md @@ -51,6 +51,7 @@ The crate `ckb-rpc`'s minimum supported rustc version is 1.61.0. * [Method `get_fork_block`](#method-get_fork_block) * [Method `get_consensus`](#method-get_consensus) * [Method `get_block_median_time`](#method-get_block_median_time) + * [Method `estimate_cycles`](#method-estimate_cycles) * [Module Experiment](#module-experiment) * [Method `dry_run_transaction`](#method-dry_run_transaction) * [Method `calculate_dao_maximum_withdraw`](#method-calculate_dao_maximum_withdraw) @@ -124,11 +125,11 @@ The crate `ckb-rpc`'s minimum supported rustc version is 1.61.0. * [Type `DeploymentPos`](#type-deploymentpos) * [Type `DeploymentState`](#type-deploymentstate) * [Type `DeploymentsInfo`](#type-deploymentsinfo) - * [Type `DryRunResult`](#type-dryrunresult) * [Type `Either`](#type-either) * [Type `EpochNumber`](#type-epochnumber) * [Type `EpochNumberWithFraction`](#type-epochnumberwithfraction) * [Type `EpochView`](#type-epochview) + * [Type `EstimateCycles`](#type-estimatecycles) * [Type `H256`](#type-h256) * [Type `HardForkFeature`](#type-hardforkfeature) * [Type `Header`](#type-header) @@ -1574,6 +1575,92 @@ Response ``` +#### Method `estimate_cycles` +* `estimate_cycles(tx)` + * `tx`: [`Transaction`](#type-transaction) +* result: [`EstimateCycles`](#type-estimatecycles) + +`estimate_cycles` run a transaction and return the execution consumed cycles. + +This method will not check the transaction validity, but only run the lock script and type script and then return the execution cycles. + +It is used to estimate how many cycles the scripts consume. + +###### Errors + +* [`TransactionFailedToResolve (-301)`](#error-transactionfailedtoresolve) - Failed to resolve the referenced cells and headers used in the transaction, as inputs or dependencies. + +* [`TransactionFailedToVerify (-302)`](#error-transactionfailedtoverify) - There is a script returns with an error. + +###### Examples + +Request + + +``` +{ + "id": 42, + "jsonrpc": "2.0", + "method": "estimate_cycles", + "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": [] + } + ] +} +``` + + +Response + + +``` +{ + "id": 42, + "jsonrpc": "2.0", + "result": { + "cycles": "0x219" + } +} +``` + + ### Module Experiment RPC Module Experiment for experimenting methods. @@ -1585,7 +1672,11 @@ The methods here may be removed or changed in future releases without prior noti #### Method `dry_run_transaction` * `dry_run_transaction(tx)` * `tx`: [`Transaction`](#type-transaction) -* result: [`DryRunResult`](#type-dryrunresult) +* result: [`EstimateCycles`](#type-estimatecycles) + +👎 Deprecated since 0.105.1: +Please use the RPC method [`estimate_cycles`](#method-estimate_cycles) instead + Dry run a transaction and return the execution cycles. @@ -5385,17 +5476,6 @@ Chain information. * `deployments`: `{ [ key:` [`DeploymentPos`](#type-deploymentpos) `]: ` [`DeploymentInfo`](#type-deploymentinfo) `}` - deployments info -### Type `DryRunResult` - -Response result of the RPC method `dry_run_transaction`. - -#### Fields - -`DryRunResult` is a JSON object with the following fields. - -* `cycles`: [`Cycle`](#type-cycle) - The count of cycles that the VM has consumed to verify this transaction. - - ### Type `Either` The enum `Either` with variants `Left` and `Right` is a general purpose sum type with two cases. @@ -5468,6 +5548,17 @@ CKB adjusts difficulty based on epochs. * `compact_target`: [`Uint32`](#type-uint32) - The difficulty target for any block in this epoch. +### Type `EstimateCycles` + +Response result of the RPC method `estimate_cycles`. + +#### Fields + +`EstimateCycles` is a JSON object with the following fields. + +* `cycles`: [`Cycle`](#type-cycle) - The count of cycles that the VM has consumed to verify this transaction. + + ### Type `H256` The 32-byte fixed-length binary data. diff --git a/rpc/src/module/chain.rs b/rpc/src/module/chain.rs index 1954c7cd7d..2aa110bb68 100644 --- a/rpc/src/module/chain.rs +++ b/rpc/src/module/chain.rs @@ -1,22 +1,28 @@ use crate::error::RPCError; use ckb_jsonrpc_types::{ BlockEconomicState, BlockNumber, BlockView, CellWithStatus, Consensus, EpochNumber, EpochView, - HeaderView, JsonBytes, MerkleProof as JsonMerkleProof, OutPoint, ResponseFormat, - ResponseFormatInnerType, Timestamp, TransactionProof, TransactionWithStatusResponse, Uint32, + EstimateCycles, HeaderView, JsonBytes, MerkleProof as JsonMerkleProof, OutPoint, + ResponseFormat, ResponseFormatInnerType, Timestamp, Transaction, TransactionProof, + TransactionWithStatusResponse, Uint32, }; use ckb_logger::error; use ckb_reward_calculator::RewardCalculator; -use ckb_shared::shared::Shared; +use ckb_shared::{shared::Shared, Snapshot}; use ckb_store::ChainStore; use ckb_traits::HeaderProvider; use ckb_types::core::tx_pool::TransactionWithStatus; use ckb_types::{ - core::{self, cell::CellProvider}, + core::{ + self, + cell::{resolve_transaction, CellProvider, CellStatus, HeaderChecker}, + error::OutPointError, + }, packed, prelude::*, utilities::{merkle_root, MerkleProof, CBMT}, H256, }; +use ckb_verification::ScriptVerifier; use jsonrpc_core::Result; use jsonrpc_derive::rpc; use std::collections::HashSet; @@ -1270,6 +1276,85 @@ pub trait ChainRpc { /// ``` #[rpc(name = "get_block_median_time")] fn get_block_median_time(&self, block_hash: H256) -> Result>; + + /// `estimate_cycles` run a transaction and return the execution consumed cycles. + /// + /// This method will not check the transaction validity, but only run the lock script + /// and type script and then return the execution cycles. + /// + /// It is used to estimate how many cycles the scripts consume. + /// + /// ## Errors + /// + /// * [`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) - There is a script returns with an error. + /// + /// ## Examples + /// + /// Request + /// + /// ```json + /// { + /// "id": 42, + /// "jsonrpc": "2.0", + /// "method": "estimate_cycles", + /// "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": [] + /// } + /// ] + /// } + /// ``` + /// + /// Response + /// + /// ```json + /// { + /// "id": 42, + /// "jsonrpc": "2.0", + /// "result": { + /// "cycles": "0x219" + /// } + /// } + /// ``` + #[rpc(name = "estimate_cycles")] + fn estimate_cycles(&self, tx: Transaction) -> Result; } pub(crate) struct ChainRpcImpl { @@ -1737,6 +1822,11 @@ impl ChainRpc for ChainRpcImpl { ); Ok(Some(median_time.into())) } + + fn estimate_cycles(&self, tx: Transaction) -> Result { + let tx: packed::Transaction = tx.into(); + CyclesEstimator::new(&self.shared).run(tx) + } } impl ChainRpcImpl { @@ -1794,3 +1884,63 @@ impl ChainRpcImpl { Ok(Some(transaction_with_status)) } } + +// CyclesEstimator run given transaction, and return the result, including execution cycles. +pub(crate) struct CyclesEstimator<'a> { + shared: &'a Shared, +} + +impl<'a> CellProvider for CyclesEstimator<'a> { + fn cell(&self, out_point: &packed::OutPoint, eager_load: bool) -> CellStatus { + let snapshot = self.shared.snapshot(); + snapshot + .get_cell(out_point) + .map(|mut cell_meta| { + if eager_load { + if let Some((data, data_hash)) = snapshot.get_cell_data(out_point) { + cell_meta.mem_cell_data = Some(data); + cell_meta.mem_cell_data_hash = Some(data_hash); + } + } + CellStatus::live_cell(cell_meta) + }) // treat as live cell, regardless of live or dead + .unwrap_or(CellStatus::Unknown) + } +} + +impl<'a> HeaderChecker for CyclesEstimator<'a> { + fn check_valid(&self, block_hash: &packed::Byte32) -> std::result::Result<(), OutPointError> { + self.shared.snapshot().check_valid(block_hash) + } +} + +impl<'a> CyclesEstimator<'a> { + pub(crate) fn new(shared: &'a Shared) -> Self { + Self { shared } + } + + pub(crate) fn run(&self, tx: packed::Transaction) -> Result { + let snapshot: &Snapshot = &self.shared.snapshot(); + let consensus = snapshot.consensus(); + match resolve_transaction(tx.into_view(), &mut HashSet::new(), self, self) { + Ok(resolved) => { + let max_cycles = consensus.max_block_cycles; + match ScriptVerifier::new(&resolved, &snapshot.as_data_provider()) + .verify(max_cycles) + { + Ok(cycles) => Ok(EstimateCycles { + cycles: cycles.into(), + }), + Err(err) => Err(RPCError::custom_with_error( + RPCError::TransactionFailedToVerify, + err, + )), + } + } + Err(err) => Err(RPCError::custom_with_error( + RPCError::TransactionFailedToResolve, + err, + )), + } + } +} diff --git a/rpc/src/module/experiment.rs b/rpc/src/module/experiment.rs index 6bbc11a2bc..103c0d4ed1 100644 --- a/rpc/src/module/experiment.rs +++ b/rpc/src/module/experiment.rs @@ -1,23 +1,14 @@ use crate::error::RPCError; +use crate::module::chain::CyclesEstimator; use ckb_dao::DaoCalculator; use ckb_jsonrpc_types::{ - Capacity, DaoWithdrawingCalculationKind, DryRunResult, OutPoint, Transaction, + Capacity, DaoWithdrawingCalculationKind, EstimateCycles, OutPoint, Transaction, }; use ckb_shared::{shared::Shared, Snapshot}; use ckb_store::ChainStore; -use ckb_types::{ - core::{ - self, - cell::{resolve_transaction, CellProvider, CellStatus, HeaderChecker}, - error::OutPointError, - }, - packed, - prelude::*, -}; -use ckb_verification::ScriptVerifier; +use ckb_types::{core, packed, prelude::*}; use jsonrpc_core::Result; use jsonrpc_derive::rpc; -use std::collections::HashSet; /// RPC Module Experiment for experimenting methods. /// @@ -102,8 +93,12 @@ pub trait ExperimentRpc { /// } /// } /// ``` + #[deprecated( + since = "0.105.1", + note = "Please use the RPC method [`estimate_cycles`](#tymethod.estimate_cycles) instead" + )] #[rpc(name = "dry_run_transaction")] - fn dry_run_transaction(&self, tx: Transaction) -> Result; + fn dry_run_transaction(&self, tx: Transaction) -> Result; /// Calculates the maximum withdrawal one can get, given a referenced DAO cell, and /// a withdrawing block hash. @@ -172,9 +167,9 @@ pub(crate) struct ExperimentRpcImpl { } impl ExperimentRpc for ExperimentRpcImpl { - fn dry_run_transaction(&self, tx: Transaction) -> Result { + fn dry_run_transaction(&self, tx: Transaction) -> Result { let tx: packed::Transaction = tx.into(); - DryRunner::new(&self.shared).run(tx) + CyclesEstimator::new(&self.shared).run(tx) } fn calculate_dao_maximum_withdraw( @@ -243,63 +238,3 @@ impl ExperimentRpc for ExperimentRpcImpl { } } } - -// DryRunner dry run given transaction, and return the result, including execution cycles. -pub(crate) struct DryRunner<'a> { - shared: &'a Shared, -} - -impl<'a> CellProvider for DryRunner<'a> { - fn cell(&self, out_point: &packed::OutPoint, eager_load: bool) -> CellStatus { - let snapshot = self.shared.snapshot(); - snapshot - .get_cell(out_point) - .map(|mut cell_meta| { - if eager_load { - if let Some((data, data_hash)) = snapshot.get_cell_data(out_point) { - cell_meta.mem_cell_data = Some(data); - cell_meta.mem_cell_data_hash = Some(data_hash); - } - } - CellStatus::live_cell(cell_meta) - }) // treat as live cell, regardless of live or dead - .unwrap_or(CellStatus::Unknown) - } -} - -impl<'a> HeaderChecker for DryRunner<'a> { - fn check_valid(&self, block_hash: &packed::Byte32) -> std::result::Result<(), OutPointError> { - self.shared.snapshot().check_valid(block_hash) - } -} - -impl<'a> DryRunner<'a> { - pub(crate) fn new(shared: &'a Shared) -> Self { - Self { shared } - } - - pub(crate) fn run(&self, tx: packed::Transaction) -> Result { - let snapshot: &Snapshot = &self.shared.snapshot(); - let consensus = snapshot.consensus(); - match resolve_transaction(tx.into_view(), &mut HashSet::new(), self, self) { - Ok(resolved) => { - let max_cycles = consensus.max_block_cycles; - match ScriptVerifier::new(&resolved, &snapshot.as_data_provider()) - .verify(max_cycles) - { - Ok(cycles) => Ok(DryRunResult { - cycles: cycles.into(), - }), - Err(err) => Err(RPCError::custom_with_error( - RPCError::TransactionFailedToVerify, - err, - )), - } - } - Err(err) => Err(RPCError::custom_with_error( - RPCError::TransactionFailedToResolve, - err, - )), - } - } -} diff --git a/rpc/src/module/mod.rs b/rpc/src/module/mod.rs index 591ad9b630..e55ac1bde0 100644 --- a/rpc/src/module/mod.rs +++ b/rpc/src/module/mod.rs @@ -111,7 +111,7 @@ #![allow(deprecated)] mod alert; -mod chain; +pub(crate) mod chain; mod debug; mod experiment; mod indexer; diff --git a/test/src/rpc.rs b/test/src/rpc.rs index 9433bcbd51..f886538194 100644 --- a/test/src/rpc.rs +++ b/test/src/rpc.rs @@ -6,7 +6,7 @@ mod error; use ckb_error::AnyError; use ckb_jsonrpc_types::{ Alert, BannedAddr, Block, BlockEconomicState, BlockNumber, BlockTemplate, BlockView, Capacity, - CellWithStatus, ChainInfo, DryRunResult, EpochNumber, EpochView, HeaderView, JsonBytes, + CellWithStatus, ChainInfo, EpochNumber, EpochView, EstimateCycles, HeaderView, JsonBytes, LocalNode, OutPoint, RawTxPool, RemoteNode, Timestamp, Transaction, TransactionProof, TransactionWithStatusResponse, TxPoolInfo, Uint32, Uint64, Version, }; @@ -215,10 +215,10 @@ impl RpcClient { .expect("rpc call get_raw_tx_pool") } - pub fn dry_run_transaction(&self, tx: Transaction) -> DryRunResult { + pub fn estimate_cycles(&self, tx: Transaction) -> EstimateCycles { self.inner - .dry_run_transaction(tx) - .expect("rpc call dry_run_transaction") + .estimate_cycles(tx) + .expect("rpc call estimate_cycles") } pub fn send_alert(&self, alert: Alert) { @@ -336,7 +336,7 @@ jsonrpc!(pub struct Inner { pub fn submit_block(&self, _work_id: String, _data: Block) -> H256; pub fn get_blockchain_info(&self) -> ChainInfo; pub fn get_block_median_time(&self, block_hash: H256) -> Option; - pub fn dry_run_transaction(&self, _tx: Transaction) -> DryRunResult; + pub fn estimate_cycles(&self, _tx: Transaction) -> EstimateCycles; pub fn send_transaction(&self, tx: Transaction, outputs_validator: Option) -> H256; pub fn remove_transaction(&self, tx_hash: H256) -> bool; pub fn tx_pool_info(&self) -> TxPoolInfo; diff --git a/util/jsonrpc-types/src/experiment.rs b/util/jsonrpc-types/src/experiment.rs index 303766d329..23e3dc5d25 100644 --- a/util/jsonrpc-types/src/experiment.rs +++ b/util/jsonrpc-types/src/experiment.rs @@ -2,9 +2,9 @@ use crate::{Cycle, OutPoint}; use ckb_types::H256; use serde::{Deserialize, Serialize}; -/// Response result of the RPC method `dry_run_transaction`. +/// Response result of the RPC method `estimate_cycles`. #[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)] -pub struct DryRunResult { +pub struct EstimateCycles { /// The count of cycles that the VM has consumed to verify this transaction. pub cycles: Cycle, } diff --git a/util/jsonrpc-types/src/lib.rs b/util/jsonrpc-types/src/lib.rs index a4b9117edf..ca55172108 100644 --- a/util/jsonrpc-types/src/lib.rs +++ b/util/jsonrpc-types/src/lib.rs @@ -33,7 +33,7 @@ pub use self::blockchain::{ pub use self::bytes::JsonBytes; pub use self::cell::{CellData, CellInfo, CellWithStatus}; pub use self::debug::{ExtraLoggerConfig, MainLoggerConfig}; -pub use self::experiment::{DaoWithdrawingCalculationKind, DryRunResult}; +pub use self::experiment::{DaoWithdrawingCalculationKind, EstimateCycles}; pub use self::fee_rate::FeeRateDef; pub use self::fixed_bytes::Byte32; pub use self::info::{ChainInfo, DeploymentInfo, DeploymentPos, DeploymentState, DeploymentsInfo};