ckb-rpc 0.107.0

CKB RPC server.
Documentation
use crate::error::RPCError;
use crate::util::FeeRateCollector;
use ckb_jsonrpc_types::{
    BlockEconomicState, BlockNumber, BlockResponse, BlockView, CellWithStatus, Consensus,
    EpochNumber, EpochView, EstimateCycles, FeeRateStatics, HeaderView, JsonBytes, OutPoint,
    ResponseFormat, ResponseFormatInnerType, Timestamp, Transaction, TransactionAndWitnessProof,
    TransactionProof, TransactionWithStatusResponse, Uint32, Uint64,
};
use ckb_logger::error;
use ckb_reward_calculator::RewardCalculator;
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::{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;

/// RPC Module Chain for methods related to the canonical chain.
///
/// This module queries information about the canonical chain.
///
/// ## Canonical Chain
///
/// A canonical chain is the one with the most accumulated work. The accumulated work is
/// the sum of difficulties of all the blocks in the chain.
///
/// ## Chain Reorganization
///
/// Chain Reorganization happens when CKB found a chain that has accumulated more work than the
/// canonical chain. The reorganization reverts the blocks in the current canonical chain if needed,
/// and switch the canonical chain to that better chain.
///
/// ## Live Cell
///
/// A cell is live if
///
/// * it is found as an output in any transaction in the [canonical chain](#canonical-chain),
/// and
/// * it is not found as an input in any transaction in the canonical chain.
#[rpc(server)]
pub trait ChainRpc {
    /// Returns the information about a block by hash.
    ///
    /// ## Params
    ///
    /// * `block_hash` - the block hash.
    /// * `verbosity` - result format which allows 0 and 2. (**Optional**, the default is 2.)
    /// * `with_cycles` - whether the return cycles of block transactions. (**Optional**, default false.)
    ///
    /// ## Returns
    ///
    /// The RPC returns a block or null. When the RPC returns a block, the block hash must equal to
    /// the parameter `block_hash`.
    ///
    /// If the block is in the [canonical chain](#canonical-chain), the RPC must return the block
    /// information. Otherwise, the behavior is undefined. The RPC may return blocks found in local
    /// storage or simply returns null for all blocks that are not in the canonical chain. And
    /// because of [chain reorganization](#chain-reorganization), for the same `block_hash`, the
    /// RPC may sometimes return null and sometimes return the block.
    ///
    /// When `verbosity` is 2, it returns a JSON object as the `result`. See `BlockView` for the
    /// schema.
    ///
    /// When `verbosity` is 0, it returns a 0x-prefixed hex string as the `result`. The string
    /// encodes the block serialized by molecule using schema `table Block`.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_block",
    ///   "params": [
    ///      "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40"
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "header": {
    ///       "compact_target": "0x1e083126",
    ///       "dao": "0xb5a3e047474401001bc476b9ee573000c0c387962a38000000febffacf030000",
    ///       "epoch": "0x7080018000001",
    ///       "extra_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///       "hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
    ///       "nonce": "0x0",
    ///       "number": "0x400",
    ///       "parent_hash": "0xae003585fa15309b30b31aed3dcf385e9472c3c3e93746a6c4540629a6a1ed2d",
    ///       "proposals_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///       "timestamp": "0x5cd2b117",
    ///       "transactions_root": "0xc47d5b78b3c4c4c853e2a32810818940d0ee403423bea9ec7b8e566d9595206c",
    ///       "version": "0x0"
    ///     },
    ///     "proposals": [],
    ///     "transactions": [
    ///       {
    ///         "cell_deps": [],
    ///         "hash": "0x365698b50ca0da75dca2c87f9e7b563811d3b5813736b8cc62cc3b106faceb17",
    ///         "header_deps": [],
    ///         "inputs": [
    ///           {
    ///             "previous_output": {
    ///               "index": "0xffffffff",
    ///               "tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000"
    ///             },
    ///             "since": "0x400"
    ///           }
    ///         ],
    ///         "outputs": [
    ///           {
    ///             "capacity": "0x18e64b61cf",
    ///             "lock": {
    ///               "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
    ///               "hash_type": "data",
    ///               "args": "0x"
    ///             },
    ///             "type": null
    ///           }
    ///         ],
    ///         "outputs_data": [
    ///           "0x"
    ///         ],
    ///         "version": "0x0",
    ///         "witnesses": [
    ///           "0x450000000c000000410000003500000010000000300000003100000028e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5000000000000000000"
    ///         ]
    ///       }
    ///     ],
    ///     "uncles": []
    ///   }
    /// }
    /// ```
    ///
    /// The response looks like below when `verbosity` is 0.
    ///
    /// ```text
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": "0x..."
    /// }
    /// ```
    ///
    /// When specifying with_cycles, the response object will be different like below:
    ///
    /// ```text
    /// {
    ///     "id": 42,
    ///     "jsonrpc": "2.0",
    ///     "result": {
    ///         "block": <Object> or "0x...",
    ///         "cycles": []
    ///     }
    /// }
    /// ```
    #[rpc(name = "get_block")]
    fn get_block(
        &self,
        block_hash: H256,
        verbosity: Option<Uint32>,
        with_cycles: Option<bool>,
    ) -> Result<Option<BlockResponse>>;

    /// Returns the block in the [canonical chain](#canonical-chain) with the specific block number.
    ///
    /// ## Params
    ///
    /// * `block_number` - the block number.
    /// * `verbosity` - result format which allows 0 and 2. (**Optional**, the default is 2.)
    /// * `with_cycles` - whether the return cycles of block transactions. (**Optional**, default false.)
    ///
    /// ## Returns
    ///
    /// The RPC returns the block when `block_number` is less than or equal to the tip block
    /// number returned by [`get_tip_block_number`](#tymethod.get_tip_block_number) and returns
    /// null otherwise.
    ///
    /// Because of [chain reorganization](#chain-reorganization), the PRC may return null or even
    /// different blocks in different invocations with the same `block_number`.
    ///
    /// When `verbosity` is 2, it returns a JSON object as the `result`. See `BlockView` for the
    /// schema.
    ///
    /// When `verbosity` is 0, it returns a 0x-prefixed hex string as the `result`. The string
    /// encodes the block serialized by molecule using schema `table Block`.
    ///
    /// ## Errors
    ///
    /// * [`ChainIndexIsInconsistent (-201)`](../enum.RPCError.html#variant.ChainIndexIsInconsistent) - The index is inconsistent. It says a block hash is in the main chain, but cannot read it from the database.
    /// * [`DatabaseIsCorrupt (-202)`](../enum.RPCError.html#variant.DatabaseIsCorrupt) - The data read from database is dirty. Please report it as a bug.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_block_by_number",
    ///   "params": [
    ///     "0x400"
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "header": {
    ///       "compact_target": "0x1e083126",
    ///       "dao": "0xb5a3e047474401001bc476b9ee573000c0c387962a38000000febffacf030000",
    ///       "epoch": "0x7080018000001",
    ///       "extra_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///       "hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
    ///       "nonce": "0x0",
    ///       "number": "0x400",
    ///       "parent_hash": "0xae003585fa15309b30b31aed3dcf385e9472c3c3e93746a6c4540629a6a1ed2d",
    ///       "proposals_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///       "timestamp": "0x5cd2b117",
    ///       "transactions_root": "0xc47d5b78b3c4c4c853e2a32810818940d0ee403423bea9ec7b8e566d9595206c",
    ///       "version": "0x0"
    ///     },
    ///     "proposals": [],
    ///     "transactions": [
    ///       {
    ///         "cell_deps": [],
    ///         "hash": "0x365698b50ca0da75dca2c87f9e7b563811d3b5813736b8cc62cc3b106faceb17",
    ///         "header_deps": [],
    ///         "inputs": [
    ///           {
    ///             "previous_output": {
    ///               "index": "0xffffffff",
    ///               "tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000"
    ///             },
    ///             "since": "0x400"
    ///           }
    ///         ],
    ///         "outputs": [
    ///           {
    ///             "capacity": "0x18e64b61cf",
    ///             "lock": {
    ///               "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
    ///               "hash_type": "data",
    ///               "args": "0x"
    ///             },
    ///             "type": null
    ///           }
    ///         ],
    ///         "outputs_data": [
    ///           "0x"
    ///         ],
    ///         "version": "0x0",
    ///         "witnesses": [
    ///           "0x450000000c000000410000003500000010000000300000003100000028e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5000000000000000000"
    ///         ]
    ///       }
    ///     ],
    ///     "uncles": []
    ///   }
    /// }
    /// ```
    ///
    /// The response looks like below when `verbosity` is 0.
    ///
    /// ```text
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": "0x..."
    /// }
    /// ```
    ///
    /// When specifying with_cycles, the response object will be different like below:
    ///
    /// ```text
    /// {
    ///     "id": 42,
    ///     "jsonrpc": "2.0",
    ///     "result": {
    ///         "block": <Object> or "0x...",
    ///         "cycles": []
    ///     }
    /// }
    /// ```
    #[rpc(name = "get_block_by_number")]
    fn get_block_by_number(
        &self,
        block_number: BlockNumber,
        verbosity: Option<Uint32>,
        with_cycles: Option<bool>,
    ) -> Result<Option<BlockResponse>>;

    /// Returns the information about a block header by hash.
    ///
    /// ## Params
    ///
    /// * `block_hash` - the block hash.
    /// * `verbosity` - result format which allows 0 and 1. (**Optional**, the default is 1.)
    ///
    /// ## Returns
    ///
    /// The RPC returns a header or null. When the RPC returns a header, the block hash must equal to
    /// the parameter `block_hash`.
    ///
    /// If the block is in the [canonical chain](#canonical-chain), the RPC must return the header
    /// information. Otherwise, the behavior is undefined. The RPC may return blocks found in local
    /// storage or simply returns null for all blocks that are not in the canonical chain. And
    /// because of [chain reorganization](#chain-reorganization), for the same `block_hash`, the
    /// RPC may sometimes return null and sometimes return the block header.
    ///
    /// When `verbosity` is 1, it returns a JSON object as the `result`. See `HeaderView` for the
    /// schema.
    ///
    /// When `verbosity` is 0, it returns a 0x-prefixed hex string as the `result`. The string
    /// encodes the block header serialized by molecule using schema `table Header`.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_header",
    ///   "params": [
    ///     "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40"
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "compact_target": "0x1e083126",
    ///     "dao": "0xb5a3e047474401001bc476b9ee573000c0c387962a38000000febffacf030000",
    ///     "epoch": "0x7080018000001",
    ///     "extra_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///     "hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
    ///     "nonce": "0x0",
    ///     "number": "0x400",
    ///     "parent_hash": "0xae003585fa15309b30b31aed3dcf385e9472c3c3e93746a6c4540629a6a1ed2d",
    ///     "proposals_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///     "timestamp": "0x5cd2b117",
    ///     "transactions_root": "0xc47d5b78b3c4c4c853e2a32810818940d0ee403423bea9ec7b8e566d9595206c",
    ///     "version": "0x0"
    ///   }
    /// }
    /// ```
    ///
    /// The response looks like below when `verbosity` is 0.
    ///
    /// ```text
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": "0x..."
    /// }
    /// ```
    #[rpc(name = "get_header")]
    fn get_header(
        &self,
        block_hash: H256,
        verbosity: Option<Uint32>,
    ) -> Result<Option<ResponseFormat<HeaderView>>>;

    /// Returns the block header in the [canonical chain](#canonical-chain) with the specific block
    /// number.
    ///
    /// ## Params
    ///
    /// * `block_number` - Number of a block
    /// * `verbosity` - result format which allows 0 and 1. (**Optional**, the default is 1.)
    ///
    /// ## Returns
    ///
    /// The RPC returns the block header when `block_number` is less than or equal to the tip block
    /// number returned by [`get_tip_block_number`](#tymethod.get_tip_block_number) and returns
    /// null otherwise.
    ///
    /// Because of [chain reorganization](#chain-reorganization), the PRC may return null or even
    /// different block headers in different invocations with the same `block_number`.
    ///
    /// When `verbosity` is 1, it returns a JSON object as the `result`. See `HeaderView` for the
    /// schema.
    ///
    /// When `verbosity` is 0, it returns a 0x-prefixed hex string as the `result`. The string
    /// encodes the block header serialized by molecule using schema `table Header`.
    ///
    /// ## Errors
    ///
    /// * [`ChainIndexIsInconsistent (-201)`](../enum.RPCError.html#variant.ChainIndexIsInconsistent) - The index is inconsistent. It says a block hash is in the main chain, but cannot read it from the database.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_header_by_number",
    ///   "params": [
    ///     "0x400"
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "compact_target": "0x1e083126",
    ///     "dao": "0xb5a3e047474401001bc476b9ee573000c0c387962a38000000febffacf030000",
    ///     "epoch": "0x7080018000001",
    ///     "extra_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///     "hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
    ///     "nonce": "0x0",
    ///     "number": "0x400",
    ///     "parent_hash": "0xae003585fa15309b30b31aed3dcf385e9472c3c3e93746a6c4540629a6a1ed2d",
    ///     "proposals_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///     "timestamp": "0x5cd2b117",
    ///     "transactions_root": "0xc47d5b78b3c4c4c853e2a32810818940d0ee403423bea9ec7b8e566d9595206c",
    ///     "version": "0x0"
    ///   }
    /// }
    /// ```
    ///
    /// The response looks like below when `verbosity` is 0.
    ///
    /// ```text
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": "0x..."
    /// }
    /// ```
    #[rpc(name = "get_header_by_number")]
    fn get_header_by_number(
        &self,
        block_number: BlockNumber,
        verbosity: Option<Uint32>,
    ) -> Result<Option<ResponseFormat<HeaderView>>>;

    /// Returns the block filter by block hash.
    ///
    /// ## Params
    ///
    /// * `block_hash` - the block hash.
    ///
    /// ## Returns
    ///
    /// The block filter data
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_block_filter",
    ///   "params": [
    ///     "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40"
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": null
    /// }
    /// ```
    ///
    /// The response looks like below when the block have block filter.
    ///
    /// ```text
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": "0x..."
    /// }
    /// ```
    #[rpc(name = "get_block_filter")]
    fn get_block_filter(&self, block_hash: H256) -> Result<Option<JsonBytes>>;

    /// Returns the information about a transaction requested by transaction hash.
    ///
    /// ## Returns
    ///
    /// This RPC returns `null` if the transaction is not committed in the
    /// [canonical chain](#canonical-chain) nor the transaction memory pool.
    ///
    /// If the transaction is in the chain, the block hash is also returned.
    ///
    /// ## Params
    ///
    /// * `tx_hash` - Hash of a transaction
    /// * `verbosity` - result format which allows 0, 1 and 2. (**Optional**, the defaults to 2.)
    ///
    /// ## Returns
    ///
    /// When verbosity=0, it's response value is as same as verbosity=2, but it
    /// return a 0x-prefixed hex encoded molecule packed::Transaction on `transaction` field
    ///
    /// When verbosity is 1: The RPC does not return the transaction content and the field transaction must be null.
    ///
    /// When verbosity is 2: if tx_status.status is pending, proposed, or committed,
    /// the RPC returns the transaction content as field transaction, otherwise the field is null.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_transaction",
    ///   "params": [
    ///     "0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3"
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "transaction": {
    ///       "cell_deps": [
    ///         {
    ///           "dep_type": "code",
    ///           "out_point": {
    ///             "index": "0x0",
    ///             "tx_hash": "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
    ///           }
    ///         }
    ///       ],
    ///       "hash": "0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3",
    ///       "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": []
    ///     },
    ///     "cycles": "0x219",
    ///     "tx_status": {
    ///       "block_hash": null,
    ///       "status": "pending",
    ///       "reason": null
    ///     }
    ///   }
    /// }
    /// ```
    ///
    ///
    /// The response looks like below when `verbosity` is 0.
    ///
    /// ```text
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "transaction": "0x.....",
    ///     "cycles": "0x219",
    ///     "tx_status": {
    ///       "block_hash": null,
    ///       "status": "pending",
    ///       "reason": null
    ///     }
    ///   }
    /// }
    /// ```
    ///
    #[rpc(name = "get_transaction")]
    fn get_transaction(
        &self,
        tx_hash: H256,
        verbosity: Option<Uint32>,
    ) -> Result<TransactionWithStatusResponse>;

    /// Returns the hash of a block in the [canonical chain](#canonical-chain) with the specified
    /// `block_number`.
    ///
    /// ## Params
    ///
    /// * `block_number` - Block number
    ///
    /// ## Returns
    ///
    /// The RPC returns the block hash when `block_number` is less than or equal to the tip block
    /// number returned by [`get_tip_block_number`](#tymethod.get_tip_block_number) and returns
    /// null otherwise.
    ///
    /// Because of [chain reorganization](#chain-reorganization), the PRC may return null or even
    /// different block hashes in different invocations with the same `block_number`.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_block_hash",
    ///   "params": [
    ///     "0x400"
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40"
    /// }
    /// ```
    #[rpc(name = "get_block_hash")]
    fn get_block_hash(&self, block_number: BlockNumber) -> Result<Option<H256>>;

    /// Returns the header with the highest block number in the [canonical chain](#canonical-chain).
    ///
    /// Because of [chain reorganization](#chain-reorganization), the block number returned can be
    /// less than previous invocations and different invocations may return different block headers
    /// with the same block number.
    ///
    /// ## Params
    ///
    /// * `verbosity` - result format which allows 0 and 1. (**Optional**, the default is 1.)
    ///
    /// ## Returns
    ///
    /// When `verbosity` is 1, the RPC returns a JSON object as the `result`. See HeaderView for the
    /// schema.
    ///
    /// When `verbosity` is 0, it returns a 0x-prefixed hex string as the `result`. The string
    /// encodes the header serialized by molecule using schema `table Header`.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_tip_header",
    ///   "params": []
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "compact_target": "0x1e083126",
    ///     "dao": "0xb5a3e047474401001bc476b9ee573000c0c387962a38000000febffacf030000",
    ///     "epoch": "0x7080018000001",
    ///     "extra_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///     "hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
    ///     "nonce": "0x0",
    ///     "number": "0x400",
    ///     "parent_hash": "0xae003585fa15309b30b31aed3dcf385e9472c3c3e93746a6c4540629a6a1ed2d",
    ///     "proposals_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///     "timestamp": "0x5cd2b117",
    ///     "transactions_root": "0xc47d5b78b3c4c4c853e2a32810818940d0ee403423bea9ec7b8e566d9595206c",
    ///     "version": "0x0"
    ///   },
    ///   "id": 42
    /// }
    /// ```
    ///
    /// The response looks like below when `verbosity` is 0.
    ///
    /// ```text
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": "0x..."
    /// }
    /// ```
    #[rpc(name = "get_tip_header")]
    fn get_tip_header(&self, verbosity: Option<Uint32>) -> Result<ResponseFormat<HeaderView>>;

    /// Returns the status of a cell. The RPC returns extra information if it is a [live cell](#live-cell).
    ///
    /// ## Returns
    ///
    /// This RPC tells whether a cell is live or not.
    ///
    /// If the cell is live, the RPC will return details about the cell. Otherwise, the field `cell` is
    /// null in the result.
    ///
    /// If the cell is live and `with_data` is set to `false`, the field `cell.data` is null in the
    /// result.
    ///
    /// ## Params
    ///
    /// * `out_point` - Reference to the cell by transaction hash and output index.
    /// * `with_data` - Whether the RPC should return cell data. Cell data can be huge, if the client
    /// does not need the data, it should set this to `false` to save bandwidth.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_live_cell",
    ///   "params": [
    ///     {
    ///       "index": "0x0",
    ///       "tx_hash": "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
    ///     },
    ///     true
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "cell": {
    ///       "data": {
    ///         "content": "0x7f454c460201010000000000000000000200f3000100000078000100000000004000000000000000980000000000000005000000400038000100400003000200010000000500000000000000000000000000010000000000000001000000000082000000000000008200000000000000001000000000000001459308d00573000000002e7368737472746162002e74657874000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000010000000600000000000000780001000000000078000000000000000a0000000000000000000000000000000200000000000000000000000000000001000000030000000000000000000000000000000000000082000000000000001100000000000000000000000000000001000000000000000000000000000000",
    ///         "hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5"
    ///       },
    ///       "output": {
    ///         "capacity": "0x802665800",
    ///         "lock": {
    ///           "code_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///           "hash_type": "data",
    ///           "args": "0x"
    ///         },
    ///         "type": null
    ///       }
    ///     },
    ///     "status": "live"
    ///   }
    /// }
    /// ```
    #[rpc(name = "get_live_cell")]
    fn get_live_cell(&self, out_point: OutPoint, with_data: bool) -> Result<CellWithStatus>;

    /// Returns the highest block number in the [canonical chain](#canonical-chain).
    ///
    /// Because of [chain reorganization](#chain-reorganization), the returned block number may be
    /// less than a value returned in the previous invocation.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_tip_block_number",
    ///   "params": []
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": "0x400"
    /// }
    /// ```
    #[rpc(name = "get_tip_block_number")]
    fn get_tip_block_number(&self) -> Result<BlockNumber>;

    /// Returns the epoch with the highest number in the [canonical chain](#canonical-chain).
    ///
    /// Pay attention that like blocks with the specific block number may change because of [chain
    /// reorganization](#chain-reorganization), This RPC may return different epochs which have
    /// the same epoch number.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_current_epoch",
    ///   "params": []
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "compact_target": "0x1e083126",
    ///     "length": "0x708",
    ///     "number": "0x1",
    ///     "start_number": "0x3e8"
    ///   }
    /// }
    /// ```
    #[rpc(name = "get_current_epoch")]
    fn get_current_epoch(&self) -> Result<EpochView>;

    /// Returns the epoch in the [canonical chain](#canonical-chain) with the specific epoch number.
    ///
    /// ## Params
    ///
    /// * `epoch_number` - Epoch number
    ///
    /// ## Returns
    ///
    /// The RPC returns the epoch when `epoch_number` is less than or equal to the current epoch number
    /// returned by [`get_current_epoch`](#tymethod.get_current_epoch) and returns null otherwise.
    ///
    /// Because of [chain reorganization](#chain-reorganization), for the same `epoch_number`, this
    /// RPC may return null or different epochs in different invocations.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_epoch_by_number",
    ///   "params": [
    ///     "0x0"
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "compact_target": "0x20010000",
    ///     "length": "0x3e8",
    ///     "number": "0x0",
    ///     "start_number": "0x0"
    ///   }
    /// }
    /// ```
    #[rpc(name = "get_epoch_by_number")]
    fn get_epoch_by_number(&self, epoch_number: EpochNumber) -> Result<Option<EpochView>>;

    /// Returns increased issuance, miner reward, and the total transaction fee of a block.
    ///
    /// This RPC returns null if the block is not in the [canonical chain](#canonical-chain).
    ///
    /// CKB delays CKB creation for miners. The output cells in the cellbase of block N are for the
    /// miner creating block `N - 1 - ProposalWindow.farthest`.
    ///
    /// In mainnet, `ProposalWindow.farthest` is 10, so the outputs in block 100 are rewards for
    /// miner creating block 89.
    ///
    /// Because of the delay, this RPC returns null if the block rewards are not finalized yet. For
    /// example, the economic state for block 89 is only available when the number returned by
    /// [`get_tip_block_number`](#tymethod.get_tip_block_number) is greater than or equal to 100.
    ///
    /// ## Params
    ///
    /// * `block_hash` - Specifies the block hash which rewards should be analyzed.
    ///
    /// ## Returns
    ///
    /// If the block with the hash `block_hash` is in the [canonical chain](#canonical-chain) and
    /// its rewards have been finalized, return the block rewards analysis for this block. A special
    /// case is that the return value for genesis block is null.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_block_economic_state",
    ///   "params": [
    ///     "0x02530b25ad0ff677acc365cb73de3e8cc09c7ddd58272e879252e199d08df83b"
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "finalized_at": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
    ///     "issuance": {
    ///       "primary": "0x18ce922bca",
    ///       "secondary": "0x7f02ec655"
    ///     },
    ///     "miner_reward": {
    ///       "committed": "0x0",
    ///       "primary": "0x18ce922bca",
    ///       "proposal": "0x0",
    ///       "secondary": "0x17b93605"
    ///     },
    ///     "txs_fee": "0x0"
    ///   }
    /// }
    /// ```
    #[rpc(name = "get_block_economic_state")]
    fn get_block_economic_state(&self, block_hash: H256) -> Result<Option<BlockEconomicState>>;

    /// Returns a Merkle proof that transactions are included in a block.
    ///
    /// ## Params
    ///
    /// * `tx_hashes` - Transaction hashes, all transactions must be in the same block
    /// * `block_hash` - An optional parameter, if specified, looks for transactions in the block with this hash
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_transaction_proof",
    ///   "params": [
    ///     [ "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3" ]
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "block_hash": "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed",
    ///     "proof": {
    ///       "indices": [ "0x0" ],
    ///       "lemmas": []
    ///     },
    ///     "witnesses_root": "0x2bb631f4a251ec39d943cc238fc1e39c7f0e99776e8a1e7be28a03c70c4f4853"
    ///   }
    /// }
    /// ```
    #[rpc(name = "get_transaction_proof")]
    fn get_transaction_proof(
        &self,
        tx_hashes: Vec<H256>,
        block_hash: Option<H256>,
    ) -> Result<TransactionProof>;

    /// Verifies that a proof points to transactions in a block, returning the transaction hashes it commits to.
    ///
    /// ## Parameters
    ///
    /// * `transaction_proof` - proof generated by [`get_transaction_proof`](#tymethod.get_transaction_proof).
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "verify_transaction_proof",
    ///   "params": [
    ///     {
    ///       "block_hash": "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed",
    ///       "proof": {
    ///         "indices": [ "0x0" ],
    ///         "lemmas": []
    ///       },
    ///       "witnesses_root": "0x2bb631f4a251ec39d943cc238fc1e39c7f0e99776e8a1e7be28a03c70c4f4853"
    ///     }
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": [
    ///     "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
    ///   ]
    /// }
    /// ```
    #[rpc(name = "verify_transaction_proof")]
    fn verify_transaction_proof(&self, tx_proof: TransactionProof) -> Result<Vec<H256>>;

    /// Returns a Merkle proof of transactions' witness included in a block.
    ///
    /// ## Params
    ///
    /// * `tx_hashes` - Transaction hashes, all transactions must be in the same block
    /// * `block_hash` - An optional parameter, if specified, looks for transactions in the block with this hash
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_transaction_and_witness_proof",
    ///   "params": [
    ///     [ "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3" ]
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///     "jsonrpc": "2.0",
    ///     "result": {
    ///         "block_hash": "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed",
    ///         "transactions_proof": {
    ///             "indices": [ "0x0" ],
    ///             "lemmas": []
    ///         },
    ///         "witnesses_proof": {
    ///             "indices": [
    ///                 "0x0"
    ///             ],
    ///             "lemmas": []
    ///         }
    ///     },
    ///     "id": 42
    /// }
    /// ```
    #[rpc(name = "get_transaction_and_witness_proof")]
    fn get_transaction_and_witness_proof(
        &self,
        tx_hashes: Vec<H256>,
        block_hash: Option<H256>,
    ) -> Result<TransactionAndWitnessProof>;

    /// Verifies that a proof points to transactions in a block, returning the transaction hashes it commits to.
    ///
    /// ## Parameters
    ///
    /// * `tx_proof` - proof generated by [`get_transaction_and_witness_proof`](#tymethod.get_transaction_and_witness_proof).
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "verify_transaction_and_witness_proof",
    ///   "params": [
    ///     {
    ///       "block_hash": "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed",
    ///         "transactions_proof": {
    ///             "indices": [ "0x0" ],
    ///             "lemmas": []
    ///         },
    ///         "witnesses_proof": {
    ///             "indices": [
    ///                 "0x0"
    ///             ],
    ///             "lemmas": []
    ///         }
    ///     }
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": [
    ///     "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
    ///   ]
    /// }
    /// ```
    #[rpc(name = "verify_transaction_and_witness_proof")]
    fn verify_transaction_and_witness_proof(
        &self,
        tx_proof: TransactionAndWitnessProof,
    ) -> Result<Vec<H256>>;

    /// Returns the information about a fork block by hash.
    ///
    /// ## Params
    ///
    /// * `block_hash` - the fork block hash.
    /// * `verbosity` - result format which allows 0 and 2. (**Optional**, the default is 2.)
    ///
    /// ## Returns
    ///
    /// The RPC returns a fork block or null. When the RPC returns a block, the block hash must equal to
    /// the parameter `block_hash`.
    ///
    /// Please note that due to the technical nature of the peer to peer sync, the RPC may return null or a fork block
    /// result on different nodes with same `block_hash` even they are fully synced to the [canonical chain](#canonical-chain).
    /// And because of [chain reorganization](#chain-reorganization), for the same `block_hash`, the
    /// RPC may sometimes return null and sometimes return the fork block.
    ///
    /// When `verbosity` is 2, it returns a JSON object as the `result`. See `BlockView` for the
    /// schema.
    ///
    /// When `verbosity` is 0, it returns a 0x-prefixed hex string as the `result`. The string
    /// encodes the block serialized by molecule using schema `table Block`.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_fork_block",
    ///   "params": [
    ///     "0xdca341a42890536551f99357612cef7148ed471e3b6419d0844a4e400be6ee94"
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "header": {
    ///       "compact_target": "0x1e083126",
    ///       "dao": "0xb5a3e047474401001bc476b9ee573000c0c387962a38000000febffacf030000",
    ///       "epoch": "0x7080018000001",
    ///       "extra_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///       "hash": "0xdca341a42890536551f99357612cef7148ed471e3b6419d0844a4e400be6ee94",
    ///       "nonce": "0x0",
    ///       "number": "0x400",
    ///       "parent_hash": "0xae003585fa15309b30b31aed3dcf385e9472c3c3e93746a6c4540629a6a1ed2d",
    ///       "proposals_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    ///       "timestamp": "0x5cd2b118",
    ///       "transactions_root": "0xc47d5b78b3c4c4c853e2a32810818940d0ee403423bea9ec7b8e566d9595206c",
    ///       "version": "0x0"
    ///     },
    ///     "proposals": [],
    ///     "transactions": [
    ///       {
    ///         "cell_deps": [],
    ///         "hash": "0x365698b50ca0da75dca2c87f9e7b563811d3b5813736b8cc62cc3b106faceb17",
    ///         "header_deps": [],
    ///         "inputs": [
    ///           {
    ///             "previous_output": {
    ///               "index": "0xffffffff",
    ///               "tx_hash": "0x0000000000000000000000000000000000000000000000000000000000000000"
    ///             },
    ///             "since": "0x400"
    ///           }
    ///         ],
    ///         "outputs": [
    ///           {
    ///             "capacity": "0x18e64b61cf",
    ///             "lock": {
    ///               "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
    ///               "hash_type": "data",
    ///               "args": "0x"
    ///             },
    ///             "type": null
    ///           }
    ///         ],
    ///         "outputs_data": [
    ///           "0x"
    ///         ],
    ///         "version": "0x0",
    ///         "witnesses": [
    ///           "0x450000000c000000410000003500000010000000300000003100000028e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5000000000000000000"
    ///         ]
    ///       }
    ///     ],
    ///     "uncles": []
    ///   }
    /// }
    /// ```
    ///
    /// The response looks like below when `verbosity` is 0.
    ///
    /// ```text
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": "0x..."
    /// }
    /// ```
    #[rpc(name = "get_fork_block")]
    fn get_fork_block(
        &self,
        block_hash: H256,
        verbosity: Option<Uint32>,
    ) -> Result<Option<ResponseFormat<BlockView>>>;

    /// Return various consensus parameters.
    ///
    /// ## Returns
    ///
    /// If any hardfork feature has `epoch=null`, it means the feature will never be activated.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_consensus",
    ///   "params": []
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///         "block_version": "0x0",
    ///         "cellbase_maturity": "0x10000000000",
    ///         "dao_type_hash": null,
    ///         "epoch_duration_target": "0x3840",
    ///         "genesis_hash": "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed",
    ///         "hardfork_features": [
    ///             { "rfc": "0028", "epoch_number": "0x1526" },
    ///             { "rfc": "0029", "epoch_number": "0x0" },
    ///             { "rfc": "0030", "epoch_number": "0x0" },
    ///             { "rfc": "0031", "epoch_number": "0x0" },
    ///             { "rfc": "0032", "epoch_number": "0x0" },
    ///             { "rfc": "0036", "epoch_number": "0x0" },
    ///             { "rfc": "0038", "epoch_number": "0x0" }
    ///         ],
    ///         "id": "main",
    ///         "initial_primary_epoch_reward": "0x71afd498d000",
    ///         "max_block_bytes": "0x91c08",
    ///         "max_block_cycles": "0xd09dc300",
    ///         "max_block_proposals_limit": "0x5dc",
    ///         "max_uncles_num": "0x2",
    ///         "median_time_block_count": "0x25",
    ///         "orphan_rate_target": {
    ///             "denom": "0x28",
    ///             "numer": "0x1"
    ///         },
    ///         "permanent_difficulty_in_dummy": false,
    ///         "primary_epoch_reward_halving_interval": "0x2238",
    ///         "proposer_reward_ratio": {
    ///             "denom": "0xa",
    ///             "numer": "0x4"
    ///         },
    ///         "secondary_epoch_reward": "0x37d0c8e28542",
    ///         "secp256k1_blake160_multisig_all_type_hash": null,
    ///         "secp256k1_blake160_sighash_all_type_hash": null,
    ///         "tx_proposal_window": {
    ///             "closest": "0x2",
    ///             "farthest": "0xa"
    ///         },
    ///         "tx_version": "0x0",
    ///         "type_id_code_hash": "0x00000000000000000000000000000000000000000000000000545950455f4944"
    ///     }
    /// }
    /// ```
    #[rpc(name = "get_consensus")]
    fn get_consensus(&self) -> Result<Consensus>;

    /// Returns the past median time by block hash.
    ///
    /// ## Params
    ///
    /// * `block_hash` - A median time is calculated for a consecutive block sequence. `block_hash` indicates the highest block of the sequence.
    ///
    /// ## Returns
    ///
    /// When the given block hash is not on the current canonical chain, this RPC returns null;
    /// otherwise returns the median time of the consecutive 37 blocks where the given block_hash has the highest height.
    ///
    /// Note that the given block is included in the median time. The included block number range is `[MAX(block - 36, 0), block]`.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_block_median_time",
    ///   "params": [
    ///     "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40"
    ///   ]
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": "0x5cd2b105"
    /// }
    /// ```
    #[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>;

    /// Returns the fee_rate statistics of confirmed blocks on the chain
    ///
    /// ## Params
    ///
    /// * `target` - Specify the number (1 - 101) of confirmed blocks to be counted.
    ///  If the number is even, automatically add one. If not specified, defaults to 21
    ///
    /// ## Returns
    ///
    /// If the query finds the corresponding historical data,
    /// the corresponding statistics are returned,
    /// containing the mean and median, in shannons per kilo-weight.
    /// If not, it returns null.
    ///
    /// ## Examples
    ///
    /// Request
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "method": "get_fee_rate_statics",
    ///   "params": []
    /// }
    /// ```
    ///
    /// Response
    ///
    /// ```json
    /// {
    ///   "id": 42,
    ///   "jsonrpc": "2.0",
    ///   "result": {
    ///     "mean": "0xe79d",
    ///     "median": "0x14a8"
    ///    }
    /// }
    /// ```
    #[rpc(name = "get_fee_rate_statics")]
    fn get_fee_rate_statics(&self, target: Option<Uint64>) -> Result<Option<FeeRateStatics>>;
}

pub(crate) struct ChainRpcImpl {
    pub shared: Shared,
}

const DEFAULT_BLOCK_VERBOSITY_LEVEL: u32 = 2;
const DEFAULT_HEADER_VERBOSITY_LEVEL: u32 = 1;
const DEFAULT_GET_TRANSACTION_VERBOSITY_LEVEL: u32 = 2;

impl ChainRpc for ChainRpcImpl {
    fn get_block(
        &self,
        block_hash: H256,
        verbosity: Option<Uint32>,
        with_cycles: Option<bool>,
    ) -> Result<Option<BlockResponse>> {
        let snapshot = self.shared.snapshot();
        let block_hash = block_hash.pack();

        self.get_block_by_hash(&snapshot, &block_hash, verbosity, with_cycles)
    }

    fn get_block_by_number(
        &self,
        block_number: BlockNumber,
        verbosity: Option<Uint32>,
        with_cycles: Option<bool>,
    ) -> Result<Option<BlockResponse>> {
        let snapshot = self.shared.snapshot();
        let block_hash = match snapshot.get_block_hash(block_number.into()) {
            Some(block_hash) => block_hash,
            None => return Ok(None),
        };

        let ret = self.get_block_by_hash(&snapshot, &block_hash, verbosity, with_cycles);
        if ret == Ok(None) {
            let message = format!(
                "Chain Index says block #{} is {:#x}, but that block is not in the database",
                block_number, block_hash
            );
            error!("{}", message);
            return Err(RPCError::custom(
                RPCError::ChainIndexIsInconsistent,
                message,
            ));
        }
        ret
    }

    fn get_header(
        &self,
        block_hash: H256,
        verbosity: Option<Uint32>,
    ) -> Result<Option<ResponseFormat<HeaderView>>> {
        let snapshot = self.shared.snapshot();
        let block_hash = block_hash.pack();
        if !snapshot.is_main_chain(&block_hash) {
            return Ok(None);
        }

        let verbosity = verbosity
            .map(|v| v.value())
            .unwrap_or(DEFAULT_HEADER_VERBOSITY_LEVEL);
        if verbosity == 1 {
            Ok(snapshot
                .get_block_header(&block_hash)
                .map(|header| ResponseFormat::json(header.into())))
        } else if verbosity == 0 {
            Ok(snapshot
                .get_packed_block_header(&block_hash)
                .map(|packed| ResponseFormat::hex(packed.as_bytes())))
        } else {
            Err(RPCError::invalid_params("invalid verbosity level"))
        }
    }

    fn get_header_by_number(
        &self,
        block_number: BlockNumber,
        verbosity: Option<Uint32>,
    ) -> Result<Option<ResponseFormat<HeaderView>>> {
        let snapshot = self.shared.snapshot();
        let block_hash = match snapshot.get_block_hash(block_number.into()) {
            Some(block_hash) => block_hash,
            None => return Ok(None),
        };

        let verbosity = verbosity
            .map(|v| v.value())
            .unwrap_or(DEFAULT_HEADER_VERBOSITY_LEVEL);
        let result = if verbosity == 1 {
            snapshot
                .get_block_header(&block_hash)
                .map(|header| Some(ResponseFormat::json(header.into())))
        } else if verbosity == 0 {
            snapshot
                .get_packed_block_header(&block_hash)
                .map(|header| Some(ResponseFormat::hex(header.as_bytes())))
        } else {
            return Err(RPCError::invalid_params("invalid verbosity level"));
        };

        result.ok_or_else(|| {
            let message = format!(
                "Chain Index says block #{} is {:#x}, but that block is not in the database",
                block_number, block_hash
            );
            error!("{}", message);
            RPCError::custom(RPCError::ChainIndexIsInconsistent, message)
        })
    }

    fn get_block_filter(&self, block_hash: H256) -> Result<Option<JsonBytes>> {
        let store = self.shared.store();
        let block_hash = block_hash.pack();
        if !store.is_main_chain(&block_hash) {
            return Ok(None);
        }
        Ok(store.get_block_filter(&block_hash).map(Into::into))
    }

    fn get_transaction(
        &self,
        tx_hash: H256,
        verbosity: Option<Uint32>,
    ) -> Result<TransactionWithStatusResponse> {
        let tx_hash = tx_hash.pack();
        let verbosity = verbosity
            .map(|v| v.value())
            .unwrap_or(DEFAULT_GET_TRANSACTION_VERBOSITY_LEVEL);

        if verbosity == 0 {
            // when verbosity=0, it's response value is as same as verbosity=2, but it
            // return a 0x-prefixed hex encoded molecule packed::Transaction` on `transaction` field
            self.get_transaction_verbosity2(tx_hash)
                .map(|tws| TransactionWithStatusResponse::from(tws, ResponseFormatInnerType::Hex))
        } else if verbosity == 1 {
            // The RPC does not return the transaction content and the field transaction must be null.
            self.get_transaction_verbosity1(tx_hash)
                .map(|tws| TransactionWithStatusResponse::from(tws, ResponseFormatInnerType::Json))
        } else if verbosity == 2 {
            // if tx_status.status is pending, proposed, or committed,
            // the RPC returns the transaction content as field transaction,
            // otherwise the field is null.
            self.get_transaction_verbosity2(tx_hash)
                .map(|tws| TransactionWithStatusResponse::from(tws, ResponseFormatInnerType::Json))
        } else {
            Err(RPCError::invalid_params("invalid verbosity level"))
        }
    }

    fn get_block_hash(&self, block_number: BlockNumber) -> Result<Option<H256>> {
        Ok(self
            .shared
            .snapshot()
            .get_block_hash(block_number.into())
            .map(|h| h.unpack()))
    }

    fn get_tip_header(&self, verbosity: Option<Uint32>) -> Result<ResponseFormat<HeaderView>> {
        let verbosity = verbosity
            .map(|v| v.value())
            .unwrap_or(DEFAULT_HEADER_VERBOSITY_LEVEL);
        if verbosity == 1 {
            Ok(ResponseFormat::json(
                self.shared.snapshot().tip_header().clone().into(),
            ))
        } else if verbosity == 0 {
            Ok(ResponseFormat::hex(
                self.shared.snapshot().tip_header().data().as_bytes(),
            ))
        } else {
            Err(RPCError::invalid_params("invalid verbosity level"))
        }
    }

    fn get_current_epoch(&self) -> Result<EpochView> {
        Ok(EpochView::from_ext(
            self.shared.snapshot().epoch_ext().pack(),
        ))
    }

    fn get_epoch_by_number(&self, epoch_number: EpochNumber) -> Result<Option<EpochView>> {
        let snapshot = self.shared.snapshot();
        Ok(snapshot
            .get_epoch_index(epoch_number.into())
            .and_then(|hash| {
                snapshot
                    .get_epoch_ext(&hash)
                    .map(|ext| EpochView::from_ext(ext.pack()))
            }))
    }

    fn get_live_cell(&self, out_point: OutPoint, with_data: bool) -> Result<CellWithStatus> {
        let cell_status = self
            .shared
            .snapshot()
            .as_ref()
            .cell(&out_point.into(), with_data);
        Ok(cell_status.into())
    }

    fn get_tip_block_number(&self) -> Result<BlockNumber> {
        Ok(self.shared.snapshot().tip_header().number().into())
    }

    fn get_block_economic_state(&self, block_hash: H256) -> Result<Option<BlockEconomicState>> {
        let snapshot = self.shared.snapshot();

        let block_number = if let Some(block_number) = snapshot.get_block_number(&block_hash.pack())
        {
            block_number
        } else {
            return Ok(None);
        };

        let delay_length = snapshot.consensus().finalization_delay_length();
        let finalized_at_number = block_number + delay_length;
        if block_number == 0 || snapshot.tip_number() < finalized_at_number {
            return Ok(None);
        }

        let block_hash = block_hash.pack();
        let finalized_at = if let Some(block_hash) = snapshot.get_block_hash(finalized_at_number) {
            block_hash
        } else {
            return Ok(None);
        };

        let issuance = if let Some(issuance) = snapshot
            .get_block_epoch_index(&block_hash)
            .and_then(|index| snapshot.get_epoch_ext(&index))
            .and_then(|epoch_ext| {
                let primary = epoch_ext.block_reward(block_number).ok()?;
                let secondary = epoch_ext
                    .secondary_block_issuance(
                        block_number,
                        snapshot.consensus().secondary_epoch_reward(),
                    )
                    .ok()?;
                Some(core::BlockIssuance { primary, secondary })
            }) {
            issuance
        } else {
            return Ok(None);
        };

        let txs_fee = if let Some(txs_fee) =
            snapshot.get_block_ext(&block_hash).and_then(|block_ext| {
                block_ext
                    .txs_fees
                    .iter()
                    .try_fold(core::Capacity::zero(), |acc, tx_fee| acc.safe_add(*tx_fee))
                    .ok()
            }) {
            txs_fee
        } else {
            return Ok(None);
        };

        Ok(snapshot.get_block_header(&block_hash).and_then(|header| {
            RewardCalculator::new(snapshot.consensus(), snapshot.as_ref())
                .block_reward_for_target(&header)
                .ok()
                .map(|(_, block_reward)| core::BlockEconomicState {
                    issuance,
                    miner_reward: block_reward.into(),
                    txs_fee,
                    finalized_at,
                })
                .map(Into::into)
        }))
    }

    fn get_transaction_proof(
        &self,
        tx_hashes: Vec<H256>,
        block_hash: Option<H256>,
    ) -> Result<TransactionProof> {
        let (block, leaf_indices) = self.get_tx_indices(tx_hashes, block_hash)?;
        Ok(TransactionProof {
            block_hash: block.hash().unpack(),
            witnesses_root: block.calc_witnesses_root().unpack(),
            proof: CBMT::build_merkle_proof(
                &block
                    .transactions()
                    .iter()
                    .map(|tx| tx.hash())
                    .collect::<Vec<_>>(),
                &leaf_indices,
            )
            .expect("build proof with verified inputs should be OK")
            .into(),
        })
    }

    fn verify_transaction_proof(&self, tx_proof: TransactionProof) -> Result<Vec<H256>> {
        let snapshot = self.shared.snapshot();

        snapshot
            .get_block(&tx_proof.block_hash.pack())
            .ok_or_else(|| {
                RPCError::invalid_params(format!("Cannot find block {:#x}", tx_proof.block_hash))
            })
            .and_then(|block| {
                let witnesses_root = tx_proof.witnesses_root.pack();
                let merkle_proof = MerkleProof::new(
                    tx_proof
                        .proof
                        .indices
                        .into_iter()
                        .map(|index| index.value())
                        .collect(),
                    tx_proof
                        .proof
                        .lemmas
                        .into_iter()
                        .map(|lemma| lemma.pack())
                        .collect(),
                );

                CBMT::retrieve_leaves(block.tx_hashes(), &merkle_proof)
                    .and_then(|tx_hashes| {
                        merkle_proof
                            .root(&tx_hashes)
                            .and_then(|raw_transactions_root| {
                                if block.transactions_root()
                                    == merkle_root(&[raw_transactions_root, witnesses_root])
                                {
                                    Some(tx_hashes.iter().map(|hash| hash.unpack()).collect())
                                } else {
                                    None
                                }
                            })
                    })
                    .ok_or_else(|| RPCError::invalid_params("Invalid transaction proof"))
            })
    }

    fn get_transaction_and_witness_proof(
        &self,
        tx_hashes: Vec<H256>,
        block_hash: Option<H256>,
    ) -> Result<TransactionAndWitnessProof> {
        let (block, leaf_indices) = self.get_tx_indices(tx_hashes, block_hash)?;
        Ok(TransactionAndWitnessProof {
            block_hash: block.hash().unpack(),
            transactions_proof: CBMT::build_merkle_proof(
                &block
                    .transactions()
                    .iter()
                    .map(|tx| tx.hash())
                    .collect::<Vec<_>>(),
                &leaf_indices,
            )
            .expect("build proof with verified inputs should be OK")
            .into(),
            witnesses_proof: CBMT::build_merkle_proof(block.tx_witness_hashes(), &leaf_indices)
                .expect("build proof with verified inputs should be OK")
                .into(),
        })
    }

    fn verify_transaction_and_witness_proof(
        &self,
        tx_proof: TransactionAndWitnessProof,
    ) -> Result<Vec<H256>> {
        let snapshot = self.shared.snapshot();
        snapshot
            .get_block(&tx_proof.block_hash.pack())
            .ok_or_else(|| {
                RPCError::invalid_params(format!("Cannot find block {:#x}", tx_proof.block_hash))
            })
            .and_then(|block| {
                let transactions_merkle_proof = MerkleProof::new(
                    tx_proof
                        .transactions_proof
                        .indices
                        .into_iter()
                        .map(|index| index.value())
                        .collect(),
                    tx_proof
                        .transactions_proof
                        .lemmas
                        .into_iter()
                        .map(|lemma| lemma.pack())
                        .collect(),
                );
                let witnesses_merkle_proof = MerkleProof::new(
                    tx_proof
                        .witnesses_proof
                        .indices
                        .into_iter()
                        .map(|index| index.value())
                        .collect(),
                    tx_proof
                        .witnesses_proof
                        .lemmas
                        .into_iter()
                        .map(|lemma| lemma.pack())
                        .collect(),
                );

                CBMT::retrieve_leaves(block.tx_witness_hashes(), &witnesses_merkle_proof)
                    .and_then(|witnesses_hashes| witnesses_merkle_proof.root(&witnesses_hashes))
                    .and_then(|witnesses_proof_root| {
                        CBMT::retrieve_leaves(block.tx_hashes(), &transactions_merkle_proof)
                            .and_then(|tx_hashes| {
                                transactions_merkle_proof.root(&tx_hashes).and_then(
                                    |raw_transactions_root| {
                                        if block.transactions_root()
                                            == merkle_root(&[
                                                raw_transactions_root,
                                                witnesses_proof_root,
                                            ])
                                        {
                                            Some(
                                                tx_hashes
                                                    .iter()
                                                    .map(|hash| hash.unpack())
                                                    .collect(),
                                            )
                                        } else {
                                            None
                                        }
                                    },
                                )
                            })
                    })
                    .ok_or_else(|| {
                        RPCError::invalid_params("Invalid transaction_and_witness proof")
                    })
            })
    }

    fn get_fork_block(
        &self,
        block_hash: H256,
        verbosity: Option<Uint32>,
    ) -> Result<Option<ResponseFormat<BlockView>>> {
        let snapshot = self.shared.snapshot();
        let block_hash = block_hash.pack();
        if snapshot.is_main_chain(&block_hash) {
            return Ok(None);
        }

        let verbosity = verbosity
            .map(|v| v.value())
            .unwrap_or(DEFAULT_BLOCK_VERBOSITY_LEVEL);
        // TODO: verbosity level == 1, output block only contains tx_hash in JSON format
        if verbosity == 2 {
            Ok(snapshot
                .get_block(&block_hash)
                .map(|block| ResponseFormat::json(block.into())))
        } else if verbosity == 0 {
            Ok(snapshot
                .get_packed_block(&block_hash)
                .map(|packed| ResponseFormat::hex(packed.as_bytes())))
        } else {
            Err(RPCError::invalid_params("invalid verbosity level"))
        }
    }

    fn get_consensus(&self) -> Result<Consensus> {
        let consensus = self.shared.consensus().clone();
        Ok(consensus.into())
    }

    fn get_block_median_time(&self, block_hash: H256) -> Result<Option<Timestamp>> {
        let block_hash = block_hash.pack();
        let snapshot = self.shared.snapshot();
        if !snapshot.is_main_chain(&block_hash) {
            return Ok(None);
        }

        let median_time = snapshot.block_median_time(
            &block_hash,
            self.shared.consensus().median_time_block_count(),
        );
        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)
    }

    fn get_fee_rate_statics(&self, target: Option<Uint64>) -> Result<Option<FeeRateStatics>> {
        Ok(FeeRateCollector::new(self.shared.snapshot().as_ref())
            .statistics(target.map(Into::into)))
    }
}

impl ChainRpcImpl {
    fn get_transaction_verbosity1(&self, tx_hash: packed::Byte32) -> Result<TransactionWithStatus> {
        let snapshot = self.shared.snapshot();
        if let Some(tx_info) = snapshot.get_transaction_info(&tx_hash) {
            let cycles = if tx_info.is_cellbase() {
                None
            } else {
                snapshot
                    .get_block_ext(&tx_info.block_hash)
                    .and_then(|block_ext| {
                        block_ext
                            .cycles
                            .and_then(|v| v.get(tx_info.index.saturating_sub(1)).copied())
                    })
            };

            return Ok(TransactionWithStatus::with_committed(
                None,
                tx_info.block_hash.unpack(),
                cycles,
            ));
        }

        let tx_pool = self.shared.tx_pool_controller();
        let tx_status = tx_pool.get_tx_status(tx_hash);
        if let Err(e) = tx_status {
            error!("send get_tx_status request error {}", e);
            return Err(RPCError::ckb_internal_error(e));
        };
        let tx_status = tx_status.unwrap();

        if let Err(e) = tx_status {
            error!("get_tx_status from db error {}", e);
            return Err(RPCError::ckb_internal_error(e));
        };
        let (tx_status, cycles) = tx_status.unwrap();
        Ok(TransactionWithStatus::omit_transaction(tx_status, cycles))
    }

    fn get_transaction_verbosity2(&self, tx_hash: packed::Byte32) -> Result<TransactionWithStatus> {
        let snapshot = self.shared.snapshot();
        if let Some((tx, tx_info)) = snapshot.get_transaction_with_info(&tx_hash) {
            let cycles = if tx_info.is_cellbase() {
                None
            } else {
                snapshot
                    .get_block_ext(&tx_info.block_hash)
                    .and_then(|block_ext| {
                        block_ext
                            .cycles
                            .and_then(|v| v.get(tx_info.index.saturating_sub(1)).copied())
                    })
            };

            return Ok(TransactionWithStatus::with_committed(
                Some(tx),
                tx_info.block_hash.unpack(),
                cycles,
            ));
        }

        let tx_pool = self.shared.tx_pool_controller();
        let transaction_with_status = tx_pool.get_transaction_with_status(tx_hash);
        if let Err(e) = transaction_with_status {
            error!("send get_transaction_with_status request error {}", e);
            return Err(RPCError::ckb_internal_error(e));
        };
        let transaction_with_status = transaction_with_status.unwrap();

        if let Err(e) = transaction_with_status {
            error!("get transaction_with_status from db error {}", e);
            return Err(RPCError::ckb_internal_error(e));
        };
        let transaction_with_status = transaction_with_status.unwrap();
        Ok(transaction_with_status)
    }
    fn get_block_by_hash(
        &self,
        snapshot: &Snapshot,
        block_hash: &packed::Byte32,
        verbosity: Option<Uint32>,
        with_cycles: Option<bool>,
    ) -> Result<Option<BlockResponse>> {
        if !snapshot.is_main_chain(block_hash) {
            return Ok(None);
        }

        let verbosity = verbosity
            .map(|v| v.value())
            .unwrap_or(DEFAULT_BLOCK_VERBOSITY_LEVEL);

        // default false
        let with_cycles = with_cycles.unwrap_or(false);

        // TODO: verbosity level == 1, output block only contains tx_hash in JSON format
        let block_view = if verbosity == 2 {
            snapshot
                .get_block(block_hash)
                .map(|block| ResponseFormat::json(block.into()))
        } else if verbosity == 0 {
            snapshot
                .get_packed_block(block_hash)
                .map(|packed| ResponseFormat::hex(packed.as_bytes()))
        } else {
            return Err(RPCError::invalid_params("invalid verbosity level"));
        };

        Ok(block_view.map(|block| {
            if with_cycles {
                let cycles = snapshot
                    .get_block_ext(block_hash)
                    .and_then(|ext| ext.cycles);

                BlockResponse::with_cycles(
                    block,
                    cycles.map(|c| c.into_iter().map(Into::into).collect()),
                )
            } else {
                BlockResponse::regular(block)
            }
        }))
    }

    fn get_tx_indices(
        &self,
        tx_hashes: Vec<H256>,
        block_hash: Option<H256>,
    ) -> Result<(core::BlockView, Vec<u32>)> {
        if tx_hashes.is_empty() {
            return Err(RPCError::invalid_params("Empty transaction hashes"));
        }
        let snapshot = self.shared.snapshot();

        let mut retrieved_block_hash = None;
        let mut tx_indices = HashSet::new();
        for tx_hash in tx_hashes {
            match snapshot.get_transaction_info(&tx_hash.pack()) {
                Some(tx_info) => {
                    if retrieved_block_hash.is_none() {
                        retrieved_block_hash = Some(tx_info.block_hash);
                    } else if Some(tx_info.block_hash) != retrieved_block_hash {
                        return Err(RPCError::invalid_params(
                            "Not all transactions found in retrieved block",
                        ));
                    }

                    if !tx_indices.insert(tx_info.index as u32) {
                        return Err(RPCError::invalid_params(format!(
                            "Duplicated tx_hash {:#x}",
                            tx_hash
                        )));
                    }
                }
                None => {
                    return Err(RPCError::invalid_params(format!(
                        "Transaction {:#x} not yet in block",
                        tx_hash
                    )));
                }
            }
        }

        let retrieved_block_hash = retrieved_block_hash.expect("checked len");
        if let Some(specified_block_hash) = block_hash {
            if !retrieved_block_hash.eq(&specified_block_hash.pack()) {
                return Err(RPCError::invalid_params(
                    "Not all transactions found in specified block",
                ));
            }
        }

        snapshot
            .get_block(&retrieved_block_hash)
            .ok_or_else(|| {
                let message = format!(
                    "Chain TransactionInfo says block {:#x} existing, but that block is not in the database",
                    retrieved_block_hash
                );
                error!("{}", message);
                RPCError::custom(RPCError::ChainIndexIsInconsistent, message)
            })
            .map(|block| {
                (block, tx_indices.into_iter().collect::<Vec<_>>())
            })
    }
}

// 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,
            )),
        }
    }
}