Skip to content

Commit

Permalink
Merge #3669
Browse files Browse the repository at this point in the history
3669: feat: add rpc estimate_cycles r=zhangsoledad a=zhangsoledad

<!--
Thank you for contributing to nervosnetwork/ckb!

If you haven't already, please read [CONTRIBUTING](https://github.com/nervosnetwork/ckb/blob/develop/CONTRIBUTING.md) document.

If you're unsure about anything, just ask; somebody should be along to answer within a day or two.

PR Title Format:
1. module [, module2, module3]: what's changed
2. *: what's changed
-->

### What problem does this PR solve?

An new RPC to run the transaction and return cycles.

### What is changed and how it works?

`estimate_cycles` is essentially a renaming of the previous `dry_run_transaction`, which was then moved from Experiment to Chain module, with the original `dry_run_transaction` retained and marked as deprecated.

### Related changes

- PR to update `owner/repo`:
- Need to cherry-pick to the release branch

### Check List <!--REMOVE the items that are not applicable-->

Tests <!-- At least one of them must be included. -->

- Unit test
- Integration test


### Release note <!-- Choose from None, Title Only and Note. Bugfixes or new features need a release note. -->

```release-note
Title Only: Include only the PR title in the release note.
```



Co-authored-by: zhangsoledad <[email protected]>
  • Loading branch information
bors[bot] and zhangsoledad authored Oct 28, 2022
2 parents 499d64d + 91f202b commit 838e2ab
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 101 deletions.
117 changes: 104 additions & 13 deletions rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand All @@ -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.

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
158 changes: 154 additions & 4 deletions rpc/src/module/chain.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -1270,6 +1276,85 @@ pub trait ChainRpc {
/// ```
#[rpc(name = "get_block_median_time")]
fn get_block_median_time(&self, block_hash: H256) -> Result<Option<Timestamp>>;

/// `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<EstimateCycles>;
}

pub(crate) struct ChainRpcImpl {
Expand Down Expand Up @@ -1737,6 +1822,11 @@ impl ChainRpc for ChainRpcImpl {
);
Ok(Some(median_time.into()))
}

fn estimate_cycles(&self, tx: Transaction) -> Result<EstimateCycles> {
let tx: packed::Transaction = tx.into();
CyclesEstimator::new(&self.shared).run(tx)
}
}

impl ChainRpcImpl {
Expand Down Expand Up @@ -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<EstimateCycles> {
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,
)),
}
}
}
Loading

0 comments on commit 838e2ab

Please sign in to comment.