Skip to content

Commit

Permalink
Dry run api (starcoinorg#2648)
Browse files Browse the repository at this point in the history
* [rpc] dry run api require sender's public_key resolve starcoinorg#2480

* [rpc] Add contract.dry_run_raw api and

* [cmd] Add dev call-api method for call any json rpc api.
  • Loading branch information
jolestar authored and naughtyvenom committed Jul 19, 2021
1 parent 59e7b81 commit 8dbde12
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 39 deletions.
55 changes: 55 additions & 0 deletions cmd/starcoin/src/dev/call_api_cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) The Starcoin Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::cli_state::CliState;
use crate::StarcoinOpt;
use anyhow::Result;
use scmd::{CommandAction, ExecContext};
use serde_json::Value;
use starcoin_rpc_client::Params;
use structopt::StructOpt;

/// Call rpc api command
/// Some examples:
/// ``` shell
/// dev call-api node.info
/// dev call-api chain.get_block_by_number [0]
/// ```
#[derive(Debug, StructOpt)]
#[structopt(name = "call-api")]
pub struct CallApiOpt {
#[structopt(name = "rpc-api-name")]
/// api name to call, example: node.info
rpc_api_name: String,

#[structopt(name = "api-params")]
/// api params, should be a json array string
params: Option<String>,
}

pub struct CallApiCommand;

impl CommandAction for CallApiCommand {
type State = CliState;
type GlobalOpt = StarcoinOpt;
type Opt = CallApiOpt;
type ReturnItem = Value;

fn run(
&self,
ctx: &ExecContext<Self::State, Self::GlobalOpt, Self::Opt>,
) -> Result<Self::ReturnItem> {
let opt = ctx.opt();

let params = match opt.params.as_ref() {
Some(param) => serde_json::from_str(param.as_str())?,
None => Params::None,
};

let result = ctx
.state()
.client()
.call_raw_api(opt.rpc_api_name.as_str(), params)?;
Ok(result)
}
}
1 change: 1 addition & 0 deletions cmd/starcoin/src/dev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub use upgrade_module_proposal_cmd::*;
pub use upgrade_module_queue_cmd::*;
pub use upgrade_vm_config_proposal_cmd::*;

pub(crate) mod call_api_cmd;
mod call_contract_cmd;
mod compile_cmd;
mod deploy_cmd;
Expand Down
1 change: 1 addition & 0 deletions cmd/starcoin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ pub fn add_command(
.subcommand(dev::UpgradeVMConfigProposalCommand)
.subcommand(dev::PackageCmd)
.subcommand(dev::CallContractCommand)
.subcommand(dev::call_api_cmd::CallApiCommand)
.subcommand(
Command::with_name("subscribe")
.with_about("Subscribe the chain events")
Expand Down
1 change: 0 additions & 1 deletion node/src/rpc_service_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ impl ServiceFactory<RpcService> for RpcServiceFactory {
account_service,
txpool_service,
chain_state_service,
chain_service,
dev_playground,
)
};
Expand Down
9 changes: 9 additions & 0 deletions rpc/api/src/contract_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::types::{
use crate::FutureResult;
use starcoin_vm_types::account_address::AccountAddress;
use starcoin_vm_types::language_storage::{ModuleId, StructTag};
use starcoin_vm_types::transaction::authenticator::AccountPublicKey;

#[rpc]
pub trait ContractApi {
Expand All @@ -29,4 +30,12 @@ pub trait ContractApi {

#[rpc(name = "contract.dry_run")]
fn dry_run(&self, txn: DryRunTransactionRequest) -> FutureResult<TransactionOutputView>;

/// Dry run RawUserTransaction, the raw_txn parameter is RawUserTransaction's hex
#[rpc(name = "contract.dry_run_raw")]
fn dry_run_raw(
&self,
raw_txn: String,
sender_public_key: StrView<AccountPublicKey>,
) -> FutureResult<TransactionOutputView>;
}
4 changes: 2 additions & 2 deletions rpc/api/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,12 @@ impl From<RawUserTransaction> for TransactionRequest {
}
}

#[derive(Default, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct DryRunTransactionRequest {
#[serde(flatten)]
pub transaction: TransactionRequest,
/// Sender's public key
pub sender_public_key: Option<StrView<AccountPublicKey>>,
pub sender_public_key: StrView<AccountPublicKey>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down
3 changes: 1 addition & 2 deletions rpc/server/src/module/account_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,11 @@ where
node_config,
}
}
fn txn_request_filler(&self) -> TransactionRequestFiller<Account, Pool, State, Chain> {
fn txn_request_filler(&self) -> TransactionRequestFiller<Account, Pool, State> {
TransactionRequestFiller {
account: Some(self.account.clone()),
pool: self.pool.clone(),
chain_state: self.chain_state.clone(),
chain: self.chain.clone(),
node_config: self.node_config.clone(),
}
}
Expand Down
58 changes: 31 additions & 27 deletions rpc/server/src/module/contract_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::module::map_err;
use futures::future::TryFutureExt;
use futures::FutureExt;
use starcoin_account_api::AccountAsyncService;
use starcoin_chain_service::ChainAsyncService;
use starcoin_config::NodeConfig;
use starcoin_dev::playground::PlaygroudService;
use starcoin_rpc_api::contract_api::ContractApi;
Expand All @@ -19,60 +18,56 @@ use starcoin_state_api::ChainStateAsyncService;
use starcoin_txpool_api::TxPoolSyncService;
use starcoin_types::account_address::AccountAddress;
use starcoin_types::language_storage::{ModuleId, StructTag};
use starcoin_types::transaction::DryRunTransaction;
use starcoin_types::transaction::{DryRunTransaction, RawUserTransaction};
use starcoin_vm_types::access_path::AccessPath;
use starcoin_vm_types::transaction::authenticator::AccountPublicKey;
use std::str::FromStr;
use std::sync::Arc;

pub struct ContractRpcImpl<Account, Pool, State, Chain> {
pub struct ContractRpcImpl<Account, Pool, State> {
pub(crate) account: Option<Account>,
pub(crate) pool: Pool,
pub(crate) chain_state: State,
pub(crate) chain: Chain,
pub(crate) node_config: Arc<NodeConfig>,
playground: PlaygroudService,
}

impl<Account, Pool, State, Chain> ContractRpcImpl<Account, Pool, State, Chain>
impl<Account, Pool, State> ContractRpcImpl<Account, Pool, State>
where
Account: AccountAsyncService + 'static,
Pool: TxPoolSyncService + 'static,
State: ChainStateAsyncService + 'static,
Chain: ChainAsyncService + 'static,
{
pub fn new(
node_config: Arc<NodeConfig>,
account: Option<Account>,
pool: Pool,
chain_state: State,
chain: Chain,
playground: PlaygroudService,
) -> Self {
Self {
account,
pool,
chain_state,
chain,
node_config,
playground,
}
}
fn txn_request_filler(&self) -> TransactionRequestFiller<Account, Pool, State, Chain> {
fn txn_request_filler(&self) -> TransactionRequestFiller<Account, Pool, State> {
TransactionRequestFiller {
account: self.account.clone(),
pool: self.pool.clone(),
chain_state: self.chain_state.clone(),
chain: self.chain.clone(),
node_config: self.node_config.clone(),
}
}
}

impl<Account, Pool, State, Chain> ContractApi for ContractRpcImpl<Account, Pool, State, Chain>
impl<Account, Pool, State> ContractApi for ContractRpcImpl<Account, Pool, State>
where
Account: AccountAsyncService + 'static,
Pool: TxPoolSyncService + 'static,
State: ChainStateAsyncService + 'static,
Chain: ChainAsyncService + 'static,
{
fn get_code(&self, module_id: StrView<ModuleId>) -> FutureResult<Option<StrView<Vec<u8>>>> {
let service = self.chain_state.clone();
Expand Down Expand Up @@ -135,7 +130,6 @@ where
let service = self.chain_state.clone();
let txn_builder = self.txn_request_filler();
let playground = self.playground.clone();
let account_service = self.account.clone();
let f = async move {
let state_root = service.state_root().await?;
let DryRunTransactionRequest {
Expand All @@ -144,25 +138,35 @@ where
} = txn;

let txn = txn_builder.fill_transaction(transaction).await?;
let sender_public_key = match sender_public_key {
None => match account_service {
Some(account) => account
.get_account(txn.sender())
.await?
.map(|a| a.public_key)
.ok_or_else(|| {
anyhow::anyhow!("cannot fill public key of txn sender {}", txn.sender())
})?,
None => anyhow::bail!("account api is disabled"),
},
Some(p) => p.0,
};

let output = playground.dry_run(
state_root,
DryRunTransaction {
raw_txn: txn,
public_key: sender_public_key,
public_key: sender_public_key.0,
},
)?;
Ok(output.1.into())
}
.map_err(map_err);
Box::pin(f.boxed())
}

fn dry_run_raw(
&self,
raw_txn: String,
sender_public_key: StrView<AccountPublicKey>,
) -> FutureResult<TransactionOutputView> {
let service = self.chain_state.clone();
let playground = self.playground.clone();
let f = async move {
let state_root = service.state_root().await?;
let raw_txn = RawUserTransaction::from_str(raw_txn.as_str())?;
let output = playground.dry_run(
state_root,
DryRunTransaction {
raw_txn,
public_key: sender_public_key.0,
},
)?;
Ok(output.1.into())
Expand Down
12 changes: 5 additions & 7 deletions rpc/server/src/module/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use starcoin_account_api::AccountAsyncService;
use starcoin_chain_service::ChainAsyncService;
use starcoin_config::NodeConfig;
use starcoin_rpc_api::types::TransactionRequest;
use starcoin_state_api::ChainStateAsyncService;
Expand All @@ -9,20 +8,18 @@ use starcoin_types::transaction::{Module, Package, RawUserTransaction, Transacti
use std::sync::Arc;

#[derive(Clone)]
pub(crate) struct TransactionRequestFiller<Account, Pool, State, Chain> {
pub(crate) struct TransactionRequestFiller<Account, Pool, State> {
pub(crate) account: Option<Account>,
pub(crate) pool: Pool,
pub(crate) chain_state: State,
pub(crate) chain: Chain,
pub(crate) node_config: Arc<NodeConfig>,
}

impl<Account, Pool, State, Chain> TransactionRequestFiller<Account, Pool, State, Chain>
impl<Account, Pool, State> TransactionRequestFiller<Account, Pool, State>
where
Account: AccountAsyncService + 'static,
Pool: TxPoolSyncService + 'static,
State: ChainStateAsyncService + 'static,
Chain: ChainAsyncService + 'static,
{
pub(crate) async fn fill_transaction(
&self,
Expand Down Expand Up @@ -51,7 +48,7 @@ where
let sender = match txn_request.sender {
Some(s) => s,
None => match self.account.as_ref() {
None => anyhow::bail!("sender "),
None => anyhow::bail!("please set txn request's sender"),
Some(account_service) => {
account_service
.get_default_account()
Expand All @@ -61,6 +58,7 @@ where
}
},
};

let next_seq_number = match txn_request
.sequence_number
.or_else(|| self.pool.next_sequence_number(sender))
Expand All @@ -82,7 +80,7 @@ where
.expiration_timestamp_secs
.unwrap_or_else(|| self.node_config.net().time_service().now_secs() + 60 * 60 * 12); // default to 0.5d

let chain_id = self.chain.main_status().await?.head().chain_id();
let chain_id = self.node_config.net().chain_id();
if let Some(cid) = txn_request.chain_id {
if cid != chain_id.id() {
anyhow::bail!(
Expand Down
34 changes: 34 additions & 0 deletions vm/types/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub use script::{
TypeArgumentABI,
};
use starcoin_crypto::hash::SPARSE_MERKLE_PLACEHOLDER_HASH;
use std::str::FromStr;
pub use transaction_argument::{
parse_transaction_argument, parse_transaction_arguments, TransactionArgument,
};
Expand Down Expand Up @@ -276,6 +277,14 @@ impl RawUserTransaction {
.len()
}

pub fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self> {
Self::from_bytes(hex::decode(hex)?)
}

pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self> {
bcs_ext::from_bytes(bytes.as_ref())
}

pub fn mock() -> Self {
Self::mock_by_sender(AccountAddress::random())
}
Expand Down Expand Up @@ -305,6 +314,31 @@ impl RawUserTransaction {
}
}

impl FromStr for RawUserTransaction {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.strip_prefix("0x").unwrap_or(s);
Self::from_hex(s)
}
}

impl TryFrom<&[u8]> for RawUserTransaction {
type Error = anyhow::Error;

fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
Self::from_bytes(bytes)
}
}

impl TryFrom<Vec<u8>> for RawUserTransaction {
type Error = anyhow::Error;

fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
Self::from_bytes(bytes)
}
}

impl Sample for RawUserTransaction {
fn sample() -> Self {
Self::new_module(
Expand Down

0 comments on commit 8dbde12

Please sign in to comment.