From 1a1024d2fa7db99969eb509c81e7df0624cfa42a Mon Sep 17 00:00:00 2001 From: German Date: Thu, 26 Oct 2023 00:32:05 +0100 Subject: [PATCH] Compare `Environment` types against the node (#1377) * check env types * add changelog * resolve merge conflict * refactor API * attempt to add tests * test type resolution * check for invalid composite types * test the env match between contract and node * remove comment * test mismatch * remove json file * move module to extrinsics * update the metadata to only refer to contracts * adjust segment search * use anyhow error * adjust segment comparison * print warning if Env is not present in node metadata * prettify code a bit * use let-else and proper warning * add proper verbosity flag * elborate warning msg * vanila eprintln * saturating sub for slice bound --- CHANGELOG.md | 1 + Cargo.lock | 61 +-- crates/extrinsics/Cargo.toml | 1 + crates/extrinsics/src/call.rs | 6 +- crates/extrinsics/src/env_check.rs | 385 ++++++++++++++++++ crates/extrinsics/src/instantiate.rs | 6 +- crates/extrinsics/src/lib.rs | 12 + ...contracts_runtime.scale => metadata.scale} | Bin crates/extrinsics/src/runtime_api/mod.rs | 2 +- crates/extrinsics/src/upload.rs | 15 +- crates/transcode/src/lib.rs | 4 + 11 files changed, 459 insertions(+), 34 deletions(-) create mode 100644 crates/extrinsics/src/env_check.rs rename crates/extrinsics/src/runtime_api/{contracts_runtime.scale => metadata.scale} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a26fc755..b9175cd7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Compare `Environment` types against the node - [#1377](https://github.com/paritytech/cargo-contract/pull/1377) - Adds workflow for publishing docker images for the verifiable builds - [#1267](https://github.com/paritytech/cargo-contract/pull/1267) - Detect `INK_STATIC_BUFFER_SIZE` env var - [#1310](https://github.com/paritytech/cargo-contract/pull/1310) - Add `verify` command - [#1306](https://github.com/paritytech/cargo-contract/pull/1306) diff --git a/Cargo.lock b/Cargo.lock index 07a13285e..6e843b0a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,7 +351,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -892,7 +892,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -1041,6 +1041,7 @@ dependencies = [ "contract-transcode", "futures", "hex", + "ink_metadata", "pallet-contracts-primitives", "parity-scale-codec", "predicates", @@ -1308,7 +1309,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -1348,7 +1349,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -1365,7 +1366,7 @@ checksum = "2fa16a70dd58129e4dfffdff535fb1bce66673f7bbeec4a5a1765a504e1ccd84" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -1413,7 +1414,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -1435,7 +1436,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -1822,7 +1823,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -2297,7 +2298,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -2356,7 +2357,7 @@ dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -2371,7 +2372,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", "synstructure", ] @@ -2729,7 +2730,7 @@ checksum = "bc28438cad73dcc90ff3466fc329a9252b1b8ba668eb0d5668ba97088cf4eef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -3195,7 +3196,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -3508,7 +3509,7 @@ checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -4120,7 +4121,7 @@ checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -4153,7 +4154,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -4595,7 +4596,7 @@ checksum = "f12dae7cf6c1e825d13ffd4ce16bd9309db7c539929d0302b4443ed451a9f4e5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -4724,7 +4725,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -4928,7 +4929,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -5010,7 +5011,7 @@ dependencies = [ "quote", "scale-info", "subxt-metadata", - "syn 2.0.29", + "syn 2.0.38", "thiserror", "tokio", ] @@ -5041,7 +5042,7 @@ dependencies = [ "darling 0.20.3", "proc-macro-error", "subxt-codegen", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -5092,9 +5093,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -5109,7 +5110,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", "unicode-xid", ] @@ -5200,7 +5201,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -5309,7 +5310,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -5449,7 +5450,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -5766,7 +5767,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", "wasm-bindgen-shared", ] @@ -5788,7 +5789,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6270,7 +6271,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] diff --git a/crates/extrinsics/Cargo.toml b/crates/extrinsics/Cargo.toml index d2cee9c14..528c53a19 100644 --- a/crates/extrinsics/Cargo.toml +++ b/crates/extrinsics/Cargo.toml @@ -38,6 +38,7 @@ scale-info = "2.10.0" subxt = "0.32.1" subxt-signer = { version = "0.32.1", features = ["subxt", "sr25519"] } hex = "0.4.3" +ink_metadata = "5.0.0-alpha" [dev-dependencies] assert_cmd = "2.0.12" diff --git a/crates/extrinsics/src/call.rs b/crates/extrinsics/src/call.rs index 16596fd35..a670ec1dc 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -31,7 +31,10 @@ use super::{ Missing, TokenMetadata, }; -use crate::extrinsic_opts::ExtrinsicOpts; +use crate::{ + check_env_types, + extrinsic_opts::ExtrinsicOpts, +}; use anyhow::{ anyhow, @@ -186,6 +189,7 @@ impl CallCommandBuilder { let rpc = RpcClient::from_url(&url).await?; let client = OnlineClient::from_rpc_client(rpc.clone()).await?; let rpc = LegacyRpcMethods::new(rpc); + check_env_types(&client, &transcoder)?; let token_metadata = TokenMetadata::query(&rpc).await?; diff --git a/crates/extrinsics/src/env_check.rs b/crates/extrinsics/src/env_check.rs new file mode 100644 index 000000000..f84ce4645 --- /dev/null +++ b/crates/extrinsics/src/env_check.rs @@ -0,0 +1,385 @@ +use ink_metadata::InkProject; +use scale_info::{ + form::PortableForm, + Field, + PortableRegistry, + TypeDef, +}; + +use anyhow::{ + Context, + Result, +}; + +fn get_node_env_fields( + registry: &PortableRegistry, +) -> Result>>> { + let Some(env_type) = registry.types.iter().find(|t| { + let len = t.ty.path.segments.len(); + let bound = len.saturating_sub(2); + t.ty.path.segments[bound..] == ["pallet_contracts", "Environment"] + }) else { + // if we can't find the type, then we use the old contract version. + eprintln!( + "The targeted version of `pallet-contracts` does not contain the `Environment` type. \ + Therefore the check for compatible types cannot be performed, and your types may not match those of the target node" + ); + return Ok(None) + }; + + if let TypeDef::Composite(composite) = &env_type.ty.type_def { + Ok(Some(composite.fields.clone())) + } else { + anyhow::bail!("`Environment` type definition is in the wrong format"); + } +} + +pub(crate) fn resolve_type_definition( + registry: &PortableRegistry, + id: u32, +) -> Result> { + let tt = registry + .resolve(id) + .context("Type is not present in registry")?; + if tt.type_params.is_empty() { + if let TypeDef::Composite(comp) = &tt.type_def { + if comp.fields.len() > 1 || comp.fields.is_empty() { + anyhow::bail!("Composite field has incorrect composite type format") + } + + let tt_id = comp + .fields + .get(0) + .context("Incorrect format of a field")? + .ty + .id; + return resolve_type_definition(registry, tt_id) + } + Ok(tt.type_def.clone()) + } else { + let param_id = tt + .type_params + .get(0) + .context("type param is not present")? + .ty + .context("concrete type is not present")? + .id; + resolve_type_definition(registry, param_id) + } +} + +/// Compares the environment type of the targeted chain against the current contract. +/// +/// It is achieved by iterating over the type specifications of `Environment` trait +/// in the node's metadata anf comparing finding the corresponding type +/// in the contract's `Environment` trait. +pub fn compare_node_env_with_contract( + node_registry: &PortableRegistry, + contract_metadata: &InkProject, +) -> Result<()> { + let Some(env_fields) = get_node_env_fields(node_registry)? else { + return Ok(()) + }; + for field in env_fields { + let field_name = field.name.context("Field does not have a name")?; + if &field_name == "hasher" { + continue + } + let field_def = resolve_type_definition(node_registry, field.ty.id)?; + let checked = + compare_type(&field_name, field_def, contract_metadata, node_registry)?; + if !checked { + anyhow::bail!("Failed to validate the field: {}", field_name); + } + } + Ok(()) +} + +/// Compares the contract's environment type with a provided type definition. +fn compare_type( + type_name: &str, + type_def: TypeDef, + contract_metadata: &InkProject, + node_registry: &PortableRegistry, +) -> Result { + let contract_registry = contract_metadata.registry(); + let tt_id = match type_name { + "account_id" => contract_metadata.spec().environment().account_id().ty().id, + "balance" => contract_metadata.spec().environment().balance().ty().id, + "hash" => contract_metadata.spec().environment().hash().ty().id, + "timestamp" => contract_metadata.spec().environment().timestamp().ty().id, + "block_number" => { + contract_metadata + .spec() + .environment() + .block_number() + .ty() + .id + } + _ => anyhow::bail!("Trying to resolve unknown environment type"), + }; + let tt_def = resolve_type_definition(contract_registry, tt_id)?; + if let TypeDef::Array(node_arr) = &type_def { + let node_arr_type = + resolve_type_definition(node_registry, node_arr.type_param.id)?; + if let TypeDef::Array(contract_arr) = &tt_def { + if node_arr.len != contract_arr.len { + anyhow::bail!("Mismatch in array lengths"); + } + let contract_arr_type = + resolve_type_definition(contract_registry, contract_arr.type_param.id)?; + return Ok(contract_arr_type == node_arr_type) + } + } + Ok(type_def == tt_def) +} + +#[cfg(test)] +mod tests { + use ink_metadata::{ + layout::{ + Layout, + LayoutKey, + LeafLayout, + }, + ConstructorSpec, + ContractSpec, + EnvironmentSpec, + InkProject, + MessageParamSpec, + MessageSpec, + ReturnTypeSpec, + TypeSpec, + }; + use scale::{ + Decode, + Encode, + }; + use scale_info::{ + form::PortableForm, + MetaType, + PortableRegistry, + Registry, + TypeDef, + TypeInfo, + }; + use std::marker::PhantomData; + + use crate::{ + compare_node_env_with_contract, + env_check::resolve_type_definition, + }; + + #[derive(Encode, Decode, TypeInfo, serde::Serialize, serde::Deserialize)] + pub struct AccountId([u8; 32]); + + #[derive(Encode, Decode, TypeInfo, serde::Serialize, serde::Deserialize)] + pub struct Balance(u128); + + #[derive(Encode, Decode, TypeInfo, serde::Serialize, serde::Deserialize)] + pub struct Hash([u8; 32]); + + #[derive(Encode, Decode, TypeInfo, serde::Serialize, serde::Deserialize)] + pub struct Hasher; + + #[derive(Encode, Decode, TypeInfo, serde::Serialize, serde::Deserialize)] + pub struct Timestamp(u64); + + #[derive(Encode, Decode, TypeInfo, serde::Serialize, serde::Deserialize)] + pub struct BlockNumber(u32); + + #[derive(Encode, Decode, TypeInfo, serde::Serialize, serde::Deserialize)] + pub struct SomeStruct { + one: u32, + two: u64, + } + + #[derive(Encode, Decode, TypeInfo, serde::Serialize, serde::Deserialize)] + pub struct CompositeBlockNumber(SomeStruct); + + #[derive(Encode, Decode, TypeInfo, serde::Serialize, serde::Deserialize)] + pub struct EnvironmentType(PhantomData); + + #[derive(Encode, Decode, TypeInfo, serde::Serialize, serde::Deserialize)] + #[scale_info(replace_segment("tests", "pallet_contracts"))] + pub struct Environment { + account_id: EnvironmentType, + balance: EnvironmentType, + hash: EnvironmentType, + hasher: EnvironmentType, + timestamp: EnvironmentType, + block_number: EnvironmentType, + } + + #[derive(Encode, Decode, TypeInfo, serde::Serialize, serde::Deserialize)] + #[scale_info(replace_segment("tests", "pallet_contracts"))] + #[scale_info(replace_segment("InvalidEnvironment", "Environment"))] + pub struct InvalidEnvironment { + account_id: EnvironmentType, + balance: EnvironmentType, + hash: EnvironmentType, + hasher: EnvironmentType, + timestamp: EnvironmentType, + block_number: EnvironmentType, + } + + #[test] + fn resolve_works() { + let mut registry = Registry::new(); + registry.register_type(&MetaType::new::()); + let u64_typedef = + TypeDef::::Primitive(scale_info::TypeDefPrimitive::U64); + + let portable: PortableRegistry = registry.into(); + let resolved_type = resolve_type_definition(&portable, 12); + assert!(resolved_type.is_ok()); + let resolved_type = resolved_type.unwrap(); + + assert_eq!(u64_typedef, resolved_type); + } + + #[test] + #[should_panic(expected = "Type is not present in registry")] + fn resolve_unknown_type_fails() { + let mut registry = Registry::new(); + registry.register_type(&MetaType::new::()); + + let portable: PortableRegistry = registry.into(); + let _ = resolve_type_definition(&portable, 18).unwrap(); + } + + #[test] + #[should_panic(expected = "Composite field has incorrect composite type format")] + fn composite_type_fails_to_resolve() { + let mut registry = Registry::new(); + registry.register_type(&MetaType::new::()); + + let portable: PortableRegistry = registry.into(); + let _ = resolve_type_definition(&portable, 15).unwrap(); + } + + fn generate_contract_ink_project() -> InkProject + where + A: TypeInfo + 'static, + BA: TypeInfo + 'static, + BN: TypeInfo + 'static, + H: TypeInfo + 'static, + T: TypeInfo + 'static, + { + // let _ = generate_metadata(); + let leaf = LeafLayout::from_key::(LayoutKey::new(0_u8)); + let layout = Layout::Leaf(leaf); + + #[derive(scale_info::TypeInfo)] + pub enum NoChainExtension {} + + type ChainExtension = NoChainExtension; + const MAX_EVENT_TOPICS: usize = 4; + const BUFFER_SIZE: usize = 1 << 14; + + // given + let contract: ContractSpec = ContractSpec::new() + .constructors(vec![ConstructorSpec::from_label("new") + .selector([94u8, 189u8, 136u8, 214u8]) + .payable(true) + .args(vec![MessageParamSpec::new("init_value") + .of_type(TypeSpec::with_name_segs::( + vec!["i32"].into_iter().map(AsRef::as_ref), + )) + .done()]) + .returns(ReturnTypeSpec::new(None)) + .docs(Vec::new()) + .done()]) + .messages(vec![MessageSpec::from_label("get") + .selector([37u8, 68u8, 74u8, 254u8]) + .mutates(false) + .payable(false) + .args(Vec::new()) + .returns(ReturnTypeSpec::new(TypeSpec::with_name_segs::( + vec!["i32"].into_iter().map(AsRef::as_ref), + ))) + .done()]) + .events(Vec::new()) + .environment( + EnvironmentSpec::new() + .account_id(TypeSpec::with_name_segs::( + ::core::iter::Iterator::map( + ::core::iter::IntoIterator::into_iter(["AccountId"]), + ::core::convert::AsRef::as_ref, + ), + )) + .balance(TypeSpec::with_name_segs::( + ::core::iter::Iterator::map( + ::core::iter::IntoIterator::into_iter(["Balance"]), + ::core::convert::AsRef::as_ref, + ), + )) + .hash(TypeSpec::with_name_segs::( + ::core::iter::Iterator::map( + ::core::iter::IntoIterator::into_iter(["Hash"]), + ::core::convert::AsRef::as_ref, + ), + )) + .timestamp(TypeSpec::with_name_segs::( + ::core::iter::Iterator::map( + ::core::iter::IntoIterator::into_iter(["Timestamp"]), + ::core::convert::AsRef::as_ref, + ), + )) + .block_number(TypeSpec::with_name_segs::( + ::core::iter::Iterator::map( + ::core::iter::IntoIterator::into_iter(["BlockNumber"]), + ::core::convert::AsRef::as_ref, + ), + )) + .chain_extension(TypeSpec::with_name_segs::( + ::core::iter::Iterator::map( + ::core::iter::IntoIterator::into_iter(["ChainExtension"]), + ::core::convert::AsRef::as_ref, + ), + )) + .max_event_topics(MAX_EVENT_TOPICS) + .static_buffer_size(BUFFER_SIZE) + .done(), + ) + .done(); + + InkProject::new(layout, contract) + } + + #[test] + fn contract_and_node_match() { + let mut registry = Registry::new(); + registry.register_type(&MetaType::new::()); + + let portable: PortableRegistry = registry.into(); + + let ink_project = generate_contract_ink_project::< + AccountId, + Balance, + BlockNumber, + Hash, + Timestamp, + >(); + + let valid = compare_node_env_with_contract(&portable, &ink_project); + assert!(valid.is_ok(), "{}", valid.err().unwrap()) + } + + #[test] + fn contract_and_node_mismatch() { + let mut registry = Registry::new(); + registry.register_type(&MetaType::new::()); + + let portable: PortableRegistry = registry.into(); + + let ink_project = + generate_contract_ink_project::(); + + let result = compare_node_env_with_contract(&portable, &ink_project); + assert_eq!( + result.err().unwrap().to_string(), + "Failed to validate the field: timestamp" + ) + } +} diff --git a/crates/extrinsics/src/instantiate.rs b/crates/extrinsics/src/instantiate.rs index 94ba2b50c..05e1d48c2 100644 --- a/crates/extrinsics/src/instantiate.rs +++ b/crates/extrinsics/src/instantiate.rs @@ -31,7 +31,10 @@ use super::{ StorageDeposit, TokenMetadata, }; -use crate::extrinsic_opts::ExtrinsicOpts; +use crate::{ + check_env_types, + extrinsic_opts::ExtrinsicOpts, +}; use anyhow::{ anyhow, Context, @@ -180,6 +183,7 @@ impl InstantiateCommandBuilder { let rpc_cli = RpcClient::from_url(&url).await?; let client = OnlineClient::from_rpc_client(rpc_cli.clone()).await?; + check_env_types(&client, &transcoder)?; let rpc = LegacyRpcMethods::new(rpc_cli); let token_metadata = TokenMetadata::query(&rpc).await?; diff --git a/crates/extrinsics/src/lib.rs b/crates/extrinsics/src/lib.rs index 544c3a215..a777df673 100644 --- a/crates/extrinsics/src/lib.rs +++ b/crates/extrinsics/src/lib.rs @@ -16,6 +16,7 @@ mod balance; mod call; +mod env_check; mod error; mod events; mod extrinsic_opts; @@ -29,6 +30,7 @@ mod upload; mod integration_tests; use colored::Colorize; +use env_check::compare_node_env_with_contract; use subxt::utils::AccountId32; use anyhow::{ @@ -370,6 +372,16 @@ pub async fn fetch_contract_info( } } +fn check_env_types( + client: &OnlineClient, + transcoder: &ContractMessageTranscoder, +) -> Result<()> +where + T: Config, +{ + compare_node_env_with_contract(client.metadata().types(), transcoder.metadata()) +} + #[derive(serde::Serialize)] pub struct ContractInfo { trie_id: String, diff --git a/crates/extrinsics/src/runtime_api/contracts_runtime.scale b/crates/extrinsics/src/runtime_api/metadata.scale similarity index 100% rename from crates/extrinsics/src/runtime_api/contracts_runtime.scale rename to crates/extrinsics/src/runtime_api/metadata.scale diff --git a/crates/extrinsics/src/runtime_api/mod.rs b/crates/extrinsics/src/runtime_api/mod.rs index 5c94b7317..870403ebf 100644 --- a/crates/extrinsics/src/runtime_api/mod.rs +++ b/crates/extrinsics/src/runtime_api/mod.rs @@ -17,7 +17,7 @@ #![allow(clippy::too_many_arguments)] #[subxt::subxt( - runtime_metadata_path = "src/runtime_api/contracts_runtime.scale", + runtime_metadata_path = "src/runtime_api/metadata.scale", substitute_type( path = "sp_weights::weight_v2::Weight", with = "::subxt::utils::Static<::sp_weights::Weight>" diff --git a/crates/extrinsics/src/upload.rs b/crates/extrinsics/src/upload.rs index a332895f4..ca7d7442b 100644 --- a/crates/extrinsics/src/upload.rs +++ b/crates/extrinsics/src/upload.rs @@ -33,8 +33,12 @@ use super::{ TokenMetadata, WasmCode, }; -use crate::extrinsic_opts::ExtrinsicOpts; +use crate::{ + check_env_types, + extrinsic_opts::ExtrinsicOpts, +}; use anyhow::Result; +use contract_transcode::ContractMessageTranscoder; use core::marker::PhantomData; use pallet_contracts_primitives::CodeUploadResult; use scale::Encode; @@ -99,6 +103,7 @@ impl UploadCommandBuilder { /// execution. pub async fn done(self) -> Result { let artifacts = self.opts.extrinsic_opts.contract_artifacts()?; + let transcoder = artifacts.contract_transcoder()?; let signer = self.opts.extrinsic_opts.signer()?; let artifacts_path = artifacts.artifact_path().to_path_buf(); @@ -112,6 +117,7 @@ impl UploadCommandBuilder { let url = self.opts.extrinsic_opts.url(); let rpc_cli = RpcClient::from_url(&url).await?; let client = OnlineClient::from_rpc_client(rpc_cli.clone()).await?; + check_env_types(&client, &transcoder)?; let rpc = LegacyRpcMethods::new(rpc_cli); let token_metadata = TokenMetadata::query(&rpc).await?; @@ -123,6 +129,7 @@ impl UploadCommandBuilder { code, signer, token_metadata, + transcoder, }) } } @@ -134,6 +141,7 @@ pub struct UploadExec { code: WasmCode, signer: Keypair, token_metadata: TokenMetadata, + transcoder: ContractMessageTranscoder, } impl UploadExec { @@ -211,6 +219,11 @@ impl UploadExec { pub fn token_metadata(&self) -> &TokenMetadata { &self.token_metadata } + + /// Returns the contract message transcoder. + pub fn transcoder(&self) -> &ContractMessageTranscoder { + &self.transcoder + } } /// A struct that encodes RPC parameters required for a call to upload a new code. diff --git a/crates/transcode/src/lib.rs b/crates/transcode/src/lib.rs index 8fb318113..1eb68b7d2 100644 --- a/crates/transcode/src/lib.rs +++ b/crates/transcode/src/lib.rs @@ -266,6 +266,10 @@ impl ContractMessageTranscoder { .decode(self.metadata.registry(), type_id, input) } + pub fn metadata(&self) -> &InkProject { + &self.metadata + } + fn constructors(&self) -> impl Iterator> { self.metadata.spec().constructors().iter() }