Unverified Commit 2961428c authored by Andrew Jones's avatar Andrew Jones Committed by GitHub
Browse files

Fix `--dry-run` error deserialization and report error details (#534)

* Update metadata, derive Deserialize for runtime error types

* Add error details to dry run instantiate

* Factor out common method for reporting dry-run error

* Report module error details on upload dry-run

* Report module error details for call dry-run

* Move printing result back to callers

* Update subxt

* Fix for new subxt and satisfy clippy

* Update to `subxt` master

* Use latest subxt release
parent 0215f1b3
Pipeline #192291 failed with stages
in 10 minutes and 37 seconds
......@@ -554,12 +554,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chameleon"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bd83544cd11113170ce1eee45383928f3f720bc8b305af18c2a3da3547e1ae"
[[package]]
name = "chrono"
version = "0.4.19"
......@@ -782,9 +776,9 @@ dependencies = [
[[package]]
name = "darling"
version = "0.13.1"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4"
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
dependencies = [
"darling_core",
"darling_macro",
......@@ -792,9 +786,9 @@ dependencies = [
[[package]]
name = "darling_core"
version = "0.13.1"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324"
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
dependencies = [
"fnv",
"ident_case",
......@@ -806,9 +800,9 @@ dependencies = [
[[package]]
name = "darling_macro"
version = "0.13.1"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b"
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
dependencies = [
"darling_core",
"quote",
......@@ -1674,12 +1668,12 @@ dependencies = [
[[package]]
name = "jsonrpsee"
version = "0.9.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d0b8cc1959f8c05256ace093b2317482da9127f1d9227564f47e7e6bf9bda8"
checksum = "91dc760c341fa81173f9a434931aaf32baad5552b0230cc6c93e8fb7eaad4c19"
dependencies = [
"jsonrpsee-client-transport 0.9.0",
"jsonrpsee-core 0.9.0",
"jsonrpsee-client-transport 0.10.1",
"jsonrpsee-core 0.10.1",
]
[[package]]
......@@ -1695,21 +1689,21 @@ dependencies = [
[[package]]
name = "jsonrpsee-client-transport"
version = "0.9.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa370c2c717d798c3c0a315ae3f0a707a388c6963c11f9da7dbbe1d3f7392f5f"
checksum = "765f7a36d5087f74e3b3b47805c2188fef8eb54afcb587b078d9f8ebfe9c7220"
dependencies = [
"futures",
"http",
"jsonrpsee-core 0.9.0",
"jsonrpsee-types 0.9.0",
"jsonrpsee-core 0.10.1",
"jsonrpsee-types 0.10.1",
"pin-project",
"rustls-native-certs",
"soketto",
"thiserror",
"tokio",
"tokio-rustls",
"tokio-util 0.6.9",
"tokio-util",
"tracing",
"webpki-roots",
]
......@@ -1730,16 +1724,16 @@ dependencies = [
"thiserror",
"tokio",
"tokio-rustls",
"tokio-util 0.7.1",
"tokio-util",
"tracing",
"webpki-roots",
]
[[package]]
name = "jsonrpsee-core"
version = "0.9.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22abc3274b265dcefe2e26c4beecf9fda4fffa48cf94930443a6c73678f020d5"
checksum = "82ef77ecd20c2254d54f5da8c0738eacca61e6b6511268a8f2753e3148c6c706"
dependencies = [
"anyhow",
"arrayvec 0.7.2",
......@@ -1748,7 +1742,7 @@ dependencies = [
"futures-channel",
"futures-util",
"hyper",
"jsonrpsee-types 0.9.0",
"jsonrpsee-types 0.10.1",
"rustc-hash",
"serde",
"serde_json",
......@@ -1782,9 +1776,9 @@ dependencies = [
[[package]]
name = "jsonrpsee-types"
version = "0.9.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f4c45d2e2aa1db4c7d7d7dbaabc10a5b5258d99cd9d42fbfd5260b76f80c324"
checksum = "38b6aa52f322cbf20c762407629b8300f39bcc0cf0619840d9252a2f65fd2dd9"
dependencies = [
"anyhow",
"beef",
......@@ -2182,7 +2176,7 @@ version = "3.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c45ed1f39709f5a89338fab50e59816b2e8815f5bb58276e7ddf9afd495f73f8"
dependencies = [
"proc-macro-crate 1.1.3",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
......@@ -2391,15 +2385,6 @@ dependencies = [
"uint",
]
[[package]]
name = "proc-macro-crate"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
dependencies = [
"toml",
]
[[package]]
name = "proc-macro-crate"
version = "1.1.3"
......@@ -2436,18 +2421,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.36"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.15"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
......@@ -2725,7 +2710,7 @@ version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4260c630e8a8a33429d1688eff2f163f24c65a4e1b1578ef6b565061336e4b6f"
dependencies = [
"proc-macro-crate 1.1.3",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
......@@ -3221,7 +3206,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ecb916b9664ed9f90abef0ff5a3e61454c1efea5861b2997e03f39b59b955f"
dependencies = [
"Inflector",
"proc-macro-crate 1.1.3",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
......@@ -3375,68 +3360,71 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "subxt"
version = "0.20.0"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc99c89f8e581c6e8b3e6923e66512041d908180efee7cb026b2ccd5f0334218"
checksum = "4ab902b8d1bf5ecdc16c78e1f7fdef77665d5ce77004b2be1f6ac4b4f679d987"
dependencies = [
"async-trait",
"bitvec",
"chameleon",
"derivative",
"frame-metadata",
"futures",
"hex",
"jsonrpsee 0.9.0",
"jsonrpsee 0.10.1",
"log",
"parity-scale-codec",
"parking_lot",
"scale-info",
"serde",
"serde_json",
"sp-core",
"sp-runtime",
"subxt-macro",
"subxt-metadata",
"thiserror",
]
[[package]]
name = "subxt-codegen"
version = "0.20.0"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74283176932f38c579311f65f4138ff2817a3bd1040250055fbd1f3857027cbb"
checksum = "2ac3c4e3565338616f009bc40419f45fa7d9472a5717fa8cce129777c709d1a1"
dependencies = [
"async-trait",
"darling",
"frame-metadata",
"heck",
"parity-scale-codec",
"proc-macro-crate 0.1.5",
"proc-macro-error",
"proc-macro2",
"quote",
"scale-info",
"subxt-metadata",
"syn",
]
[[package]]
name = "subxt-macro"
version = "0.20.0"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bb844cf84cdae36fc337810569cc39e3e022c17cc2ed11031c66d0e09b73726"
checksum = "078edfe8f06cb00848e2e64e923fe809f345042c3d7ec13edcd7f0e617656a9b"
dependencies = [
"async-trait",
"darling",
"frame-metadata",
"heck",
"parity-scale-codec",
"proc-macro-crate 0.1.5",
"proc-macro-error",
"proc-macro2",
"quote",
"scale-info",
"subxt-codegen",
"syn",
]
[[package]]
name = "subxt-metadata"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819a10a1a13bd9645419f59ac9d7cc8deb51052566b9d0c2157354ea44513d5"
dependencies = [
"frame-metadata",
"parity-scale-codec",
"scale-info",
"sp-core",
]
[[package]]
name = "syn"
version = "1.0.92"
......@@ -3610,21 +3598,6 @@ dependencies = [
"webpki",
]
[[package]]
name = "tokio-util"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
dependencies = [
"bytes",
"futures-core",
"futures-io",
"futures-sink",
"log",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.1"
......
......@@ -55,7 +55,7 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive"
sp-core = "6.0.0"
sp-runtime = "6.0.0"
pallet-contracts-primitives = "6.0.0"
subxt = "0.20.0"
subxt = "0.21.0"
futures = "0.3.19"
hex = "0.4.3"
jsonrpsee = { version = "0.11.0", features = ["ws-client"] }
......
......@@ -17,6 +17,7 @@
use super::{
display_contract_exec_result,
display_events,
dry_run_error_details,
load_metadata,
parse_balance,
wait_for_success_and_handle_error,
......@@ -25,6 +26,7 @@ use super::{
ExtrinsicOpts,
PairSigner,
RuntimeApi,
RuntimeDispatchError,
EXEC_RESULT_MAX_KEY_COL_WIDTH,
};
use crate::name_value_println;
......@@ -34,9 +36,16 @@ use jsonrpsee::{
rpc_params,
ws_client::WsClientBuilder,
};
use pallet_contracts_primitives::{
ContractResult,
ExecReturnValue,
};
use serde::Serialize;
use sp_core::Bytes;
use std::fmt::Debug;
use std::{
fmt::Debug,
result,
};
use subxt::{
rpc::NumberOrHex,
ClientBuilder,
......@@ -44,7 +53,8 @@ use subxt::{
DefaultConfig,
};
type ContractExecResult = pallet_contracts_primitives::ContractExecResult<Balance>;
type ContractExecResult =
ContractResult<result::Result<ExecReturnValue, RuntimeDispatchError>, Balance>;
#[derive(Debug, clap::Args)]
#[clap(name = "call", about = "Call a contract")]
......@@ -79,16 +89,24 @@ impl CallCommand {
let signer = super::pair_signer(self.extrinsic_opts.signer()?);
async_std::task::block_on(async {
let url = self.extrinsic_opts.url.to_string();
let api = ClientBuilder::new()
.set_url(&url)
.build()
.await?
.to_runtime_api::<RuntimeApi>();
if self.extrinsic_opts.dry_run {
self.call_rpc(call_data, &signer, &transcoder).await
self.call_rpc(&api, call_data, &signer, &transcoder).await
} else {
self.call(call_data, &signer, &transcoder).await
self.call(&api, call_data, &signer, &transcoder).await
}
})
}
async fn call_rpc(
&self,
api: &RuntimeApi,
data: Vec<u8>,
signer: &PairSigner,
transcoder: &ContractMessageTranscoder<'_>,
......@@ -131,12 +149,9 @@ impl CallCommand {
EXEC_RESULT_MAX_KEY_COL_WIDTH
);
}
Err(err) => {
name_value_println!(
"Result",
format!("Error: {:?}", err),
EXEC_RESULT_MAX_KEY_COL_WIDTH
);
Err(ref err) => {
let err = dry_run_error_details(api, err).await?;
name_value_println!("Result", err, EXEC_RESULT_MAX_KEY_COL_WIDTH);
}
}
display_contract_exec_result(&result)?;
......@@ -145,17 +160,11 @@ impl CallCommand {
async fn call(
&self,
api: &RuntimeApi,
data: Vec<u8>,
signer: &PairSigner,
transcoder: &ContractMessageTranscoder<'_>,
) -> Result<()> {
let url = self.extrinsic_opts.url.to_string();
let api = ClientBuilder::new()
.set_url(&url)
.build()
.await?
.to_runtime_api::<RuntimeApi>();
log::debug!("calling contract {:?}", self.contract);
let tx_progress = api
.tx()
......@@ -166,7 +175,7 @@ impl CallCommand {
self.gas_limit,
self.extrinsic_opts.storage_deposit_limit,
data,
)
)?
.sign_and_submit_then_watch_default(signer)
.await?;
......
......@@ -17,6 +17,7 @@
use super::{
display_contract_exec_result,
display_events,
dry_run_error_details,
parse_balance,
runtime_api::api,
wait_for_success_and_handle_error,
......@@ -27,6 +28,7 @@ use super::{
ExtrinsicOpts,
PairSigner,
RuntimeApi,
RuntimeDispatchError,
EXEC_RESULT_MAX_KEY_COL_WIDTH,
};
use crate::{
......@@ -44,6 +46,10 @@ use jsonrpsee::{
rpc_params,
ws_client::WsClientBuilder,
};
use pallet_contracts_primitives::{
ContractResult,
InstantiateReturnValue,
};
use serde::Serialize;
use sp_core::{
crypto::Ss58Codec,
......@@ -55,6 +61,7 @@ use std::{
Path,
PathBuf,
},
result,
};
use subxt::{
rpc::NumberOrHex,
......@@ -63,8 +70,10 @@ use subxt::{
DefaultConfig,
};
type ContractInstantiateResult =
pallet_contracts_primitives::ContractInstantiateResult<ContractAccount, Balance>;
type ContractInstantiateResult = ContractResult<
result::Result<InstantiateReturnValue<ContractAccount>, RuntimeDispatchError>,
Balance,
>;
#[derive(Debug, clap::Args)]
pub struct InstantiateCommand {
......@@ -228,12 +237,10 @@ impl<'a> Exec<'a> {
EXEC_RESULT_MAX_KEY_COL_WIDTH
);
}
Err(err) => {
name_value_println!(
"Result",
format!("Error: {:?}", err),
EXEC_RESULT_MAX_KEY_COL_WIDTH
);
Err(ref err) => {
let err =
dry_run_error_details(&self.subxt_api().await?, err).await?;
name_value_println!("Result", err, EXEC_RESULT_MAX_KEY_COL_WIDTH);
}
}
display_contract_exec_result(&result)?;
......@@ -270,7 +277,7 @@ impl<'a> Exec<'a> {
code.to_vec(),
self.args.data.clone(),
self.args.salt.0.clone(),
)
)?
.sign_and_submit_then_watch_default(&self.signer)
.await?;
......@@ -302,7 +309,7 @@ impl<'a> Exec<'a> {
code_hash,
self.args.data.clone(),
self.args.salt.0.clone(),
)
)?
.sign_and_submit_then_watch_default(&self.signer)
.await?;
......@@ -340,6 +347,7 @@ impl<'a> Exec<'a> {
.request("contracts_instantiate", params)
.await
.context("contracts_instantiate RPC error")?;
Ok(result)
}
}
......
......@@ -51,6 +51,7 @@ use sp_core::{
use subxt::{
Config,
DefaultConfig,
HasModuleError as _,
};
pub use self::transcode::ContractMessageTranscoder;
......@@ -229,3 +230,25 @@ where
.await
.map_err(Into::into)
}
/// Extract and display error details for an RPC `--dry-run` result.
async fn dry_run_error_details(
api: &RuntimeApi,
error: &RuntimeDispatchError,
) -> Result<String> {
let error = if let Some(error_data) = error.module_error_data() {
let details = api
.client
.metadata()
.error(error_data.pallet_index, error_data.error_index())?;
format!(
"ModuleError: {}::{}: {:?}",
details.pallet(),
details.error(),
details.description()
)
} else {
format!("{:?}", error)
};
Ok(error)
}
......@@ -15,6 +15,17 @@
// along with cargo-contract. If not, see <http://www.gnu.org/licenses/>.
#[subxt::subxt(
runtime_metadata_path = "src/cmd/extrinsics/runtime_api/contracts_runtime.scale"
runtime_metadata_path = "src/cmd/extrinsics/runtime_api/contracts_runtime.scale",
derive_for_type(type = "sp_runtime::DispatchError", derive = "::serde::Deserialize"),
derive_for_type(type = "sp_runtime::ModuleError", derive = "::serde::Deserialize"),
derive_for_type(type = "sp_runtime::TokenError", derive = "::serde::Deserialize"),
derive_for_type(
type = "sp_runtime::ArithmeticError",
derive = "::serde::Deserialize"
),
derive_for_type(
type = "sp_runtime::TransactionalError",
derive = "::serde::Deserialize"
)
)]
pub mod api {}
......@@ -16,6 +16,7 @@
use super::{
display_events,
dry_run_error_details,
runtime_api::api,
wait_for_success_and_handle_error,
Balance,
......@@ -24,6 +25,7 @@ use super::{
ExtrinsicOpts,
PairSigner,
RuntimeApi,
RuntimeDispatchError,
};
use crate::name_value_println;
use anyhow::{
......@@ -40,6 +42,7 @@ use sp_core::Bytes;
use std::{
fmt::Debug,
path::PathBuf,
result,
};
use subxt::{
rpc::NumberOrHex,
......@@ -48,7 +51,7 @@ use subxt::{
DefaultConfig,
};
type CodeUploadResult = pallet_contracts_primitives::CodeUploadResult<CodeHash, Balance>;
type CodeUploadResult = result::Result<CodeUploadReturnValue, RuntimeDispatchError>;
type CodeUploadReturnValue =
pallet_contracts_primitives::CodeUploadReturnValue<CodeHash, Balance>;
......@@ -79,15 +82,32 @@ impl UploadCommand {
.context(format!("Failed to read from {}", wasm_path.display()))?;
async_std::task::block_on(async {
if self.extrinsic_opts.dry_run {
let result = self.upload_code_rpc(code, &signer).await?;
name_value_println!("Code hash", format!("{:?}", result.code_hash));
name_value_println!("Deposit", format!("{:?}", result.deposit));
let url = self.extrinsic_opts.url.to_string();
let api = ClientBuilder::new()
.set_url(&url)
.build()
.await?
.to_runtime_api::<RuntimeApi>();
if self.extrinsic_opts.dry_run {
match self.upload_code_rpc(code, &signer).await? {
Ok(result) => {
name_value_println!("Result", String::from("Success!"));
name_value_println!(
"Code hash",
format!("{:?}", result.code_hash)
);
name_value_println!("Deposit", format!("{:?}", result.deposit));
}
Err(err) => {
let err = dry_run_error_details(&api, &err).await?;
name_value_println!("Result", err);
}
}
Ok(())
} else {
let code_stored = self.upload_code(code, &signer, &transcoder).await?;
let code_stored =
self.upload_code(&api, code, &signer, &transcoder).await?;
name_value_println!("Code hash", format!("{:?}", code_stored.code_hash));
......@@ -100,7 +120,7 @@ impl UploadCommand {
&self,
code: Vec<u8>,
signer: &PairSigner,