Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add raw RPC calls #1458

Merged
merged 16 commits into from
Jan 31, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Add a user-friendly view of contract storage data in the form of a table - [#1414](https://github.com/paritytech/cargo-contract/pull/1414)
- Add `rpc` command - [#1458](https://github.com/paritytech/cargo-contract/pull/1458)

## [4.0.0-rc.1]

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ Verify a metadata file or a contract bundle containing metadata against the sche

Fetch and display the storage of a contract on chain.

##### `cargo contract rpc`

Invoke an RPC call to the node. See [rpc](docs/rpc.md).


## Publishing

Expand Down
2 changes: 2 additions & 0 deletions crates/cargo-contract/src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod encode;
pub mod info;
pub mod instantiate;
pub mod remove;
pub mod rpc;
pub mod schema;
pub mod storage;
pub mod upload;
Expand All @@ -39,6 +40,7 @@ pub(crate) use self::{
},
instantiate::InstantiateCommand,
remove::RemoveCommand,
rpc::RpcCommand,
schema::{
GenerateSchemaCommand,
VerifySchemaCommand,
Expand Down
74 changes: 74 additions & 0 deletions crates/cargo-contract/src/cmd/rpc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of cargo-contract.
//
// cargo-contract is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// cargo-contract is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with cargo-contract. If not, see <http://www.gnu.org/licenses/>.

use contract_build::name_value_println;
use contract_extrinsics::{
ErrorVariant,
RawParams,
RpcRequest,
};
use subxt::ext::scale_value;

use super::MAX_KEY_COL_WIDTH;

#[derive(Debug, clap::Args)]
#[clap(name = "rpc", about = "Make a raw RPC call")]
pub struct RpcCommand {
/// The name of the method to call.
method: String,
/// The arguments of the method to call.
#[clap(num_args = 0..)]
params: Vec<String>,
/// Websockets url of a substrate node.
#[clap(
name = "url",
long,
value_parser,
default_value = "ws://localhost:9944"
)]
url: url::Url,
/// Export the call output in JSON format.
#[clap(long)]
output_json: bool,
}

impl RpcCommand {
pub async fn run(&self) -> Result<(), ErrorVariant> {
let request = RpcRequest::new(&self.url).await?;
let params = RawParams::new(&self.params)?;

let result = request.raw_call(&self.method, params).await;

match (result, self.output_json) {
(Err(err), false) => Err(anyhow::anyhow!("Method call failed: {}", err))?,
(Err(err), true) => {
Err(anyhow::anyhow!(serde_json::to_string_pretty(
&ErrorVariant::from(err)
)?))?
}
(Ok(res), false) => {
let output: scale_value::Value = serde_json::from_str(res.get())?;
name_value_println!("Result", output, MAX_KEY_COL_WIDTH);
Ok(())
}
(Ok(res), true) => {
let json: serde_json::Value = serde_json::from_str(res.get())?;
println!("{}", serde_json::to_string_pretty(&json)?);
Ok(())
}
}
}
}
7 changes: 7 additions & 0 deletions crates/cargo-contract/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use self::cmd::{
InfoCommand,
InstantiateCommand,
RemoveCommand,
RpcCommand,
StorageCommand,
UploadCommand,
VerifyCommand,
Expand Down Expand Up @@ -154,6 +155,9 @@ enum Command {
/// Verify schema from the current metadata specification.
#[clap(name = "verify-schema")]
VerifySchema(VerifySchemaCommand),
/// Make a raw RPC call.
#[clap(name = "rpc")]
Rpc(RpcCommand),
}

fn main() {
Expand Down Expand Up @@ -260,6 +264,9 @@ fn exec(cmd: Command) -> Result<()> {
}
Ok(())
}
Command::Rpc(rpc) => {
runtime.block_on(async { rpc.run().await.map_err(format_err) })
}
}
}

Expand Down
45 changes: 45 additions & 0 deletions crates/extrinsics/src/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,51 @@ async fn api_build_upload_remove() {
let _ = node_process;
}

/// Sanity test the RPC API
#[tokio::test]
async fn api_rpc_call() {
init_tracing_subscriber();

let tmp_dir = tempfile::Builder::new()
.prefix("cargo-contract.cli.test.")
.tempdir()
.expect("temporary directory creation failed");

let node_process = ContractsNodeProcess::spawn(CONTRACTS_NODE)
.await
.expect("Error spawning contracts node");

cargo_contract(tmp_dir.path())
.arg("rpc")
.arg("author_insertKey")
.arg("\"sr25\"")
.arg("\"//ALICE\"")
.arg("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")
.assert()
.success();

let output = cargo_contract(tmp_dir.path())
.arg("rpc")
.arg("author_hasKey")
.arg("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")
.arg("\"sr25\"")
.arg("--output-json")
.output()
.expect("failed to execute process");

let stdout = str::from_utf8(&output.stdout).unwrap();
let stderr = str::from_utf8(&output.stderr).unwrap();
assert!(
output.status.success(),
"rpc method execution failed: {stderr}"
);

assert_eq!(stdout.trim_end(), "true", "{stdout:?}");

// prevent the node_process from being dropped and killed
let _ = node_process;
}

/// Sanity test the whole lifecycle of:
/// new -> build -> upload -> instantiate -> storage
///
Expand Down
6 changes: 6 additions & 0 deletions crates/extrinsics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mod extrinsic_calls;
mod extrinsic_opts;
mod instantiate;
mod remove;
mod rpc;
mod upload;

#[cfg(test)]
Expand Down Expand Up @@ -111,6 +112,11 @@ pub use upload::{
UploadResult,
};

pub use rpc::{
RawParams,
RpcRequest,
};

pub type Client = OnlineClient<DefaultConfig>;
pub type Balance = u128;
pub type CodeHash = <DefaultConfig as Config>::Hash;
Expand Down
Loading
Loading