From d9569141c8a7cd6ccd5f1f3b70581f8fcf14f2e3 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 27 Feb 2024 23:03:20 +0100 Subject: [PATCH 1/4] Fix up previous code --- .../contracts/fixtures/contracts/xcm_query.rs | 37 ++++++ .../fixtures/contracts/xcm_take_response.rs | 38 +++++++ .../frame/contracts/mock-network/src/tests.rs | 107 +++++++++++++++++- substrate/frame/contracts/src/wasm/runtime.rs | 84 +++++++++++++- substrate/frame/contracts/uapi/src/host.rs | 32 +++++- .../frame/contracts/uapi/src/host/riscv32.rs | 8 ++ .../frame/contracts/uapi/src/host/wasm32.rs | 20 ++++ substrate/frame/contracts/uapi/src/lib.rs | 2 + 8 files changed, 323 insertions(+), 5 deletions(-) create mode 100644 substrate/frame/contracts/fixtures/contracts/xcm_query.rs create mode 100644 substrate/frame/contracts/fixtures/contracts/xcm_take_response.rs diff --git a/substrate/frame/contracts/fixtures/contracts/xcm_query.rs b/substrate/frame/contracts/fixtures/contracts/xcm_query.rs new file mode 100644 index 000000000000..2d24e9f8af71 --- /dev/null +++ b/substrate/frame/contracts/fixtures/contracts/xcm_query.rs @@ -0,0 +1,37 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(512, timeout: [u8; 8], match_querier: [u8],); + let mut query_id = [0u8; 8]; + + #[allow(deprecated)] + api::xcm_query(timeout, match_querier, &mut query_id).unwrap(); + api::return_value(uapi::ReturnFlags::empty(), &query_id); +} diff --git a/substrate/frame/contracts/fixtures/contracts/xcm_take_response.rs b/substrate/frame/contracts/fixtures/contracts/xcm_take_response.rs new file mode 100644 index 000000000000..4b0740b9e36d --- /dev/null +++ b/substrate/frame/contracts/fixtures/contracts/xcm_take_response.rs @@ -0,0 +1,38 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(query_id: [u8; 8],); + let mut response_status = [0u8; 100]; + + #[allow(deprecated)] + api::xcm_take_response(query_id, &mut response_status).unwrap(); + + api::return_value(uapi::ReturnFlags::empty(), &response_status); +} diff --git a/substrate/frame/contracts/mock-network/src/tests.rs b/substrate/frame/contracts/mock-network/src/tests.rs index d22221fe8ee0..3c345e3e74f6 100644 --- a/substrate/frame/contracts/mock-network/src/tests.rs +++ b/substrate/frame/contracts/mock-network/src/tests.rs @@ -16,10 +16,11 @@ // limitations under the License. use crate::{ - parachain::{self, Runtime}, + parachain::{self, Runtime, RuntimeOrigin}, parachain_account_sovereign_account_id, primitives::{AccountId, CENTS}, - relay_chain, MockNet, ParaA, ParachainBalances, Relay, ALICE, BOB, INITIAL_BALANCE, + relay_chain, MockNet, ParaA, ParachainBalances, ParachainPalletXcm, Relay, ALICE, BOB, + INITIAL_BALANCE, }; use codec::{Decode, Encode}; use frame_support::{ @@ -27,14 +28,17 @@ use frame_support::{ pallet_prelude::Weight, traits::{fungibles::Mutate, Currency}, }; +use frame_system::pallet_prelude::BlockNumberFor; use pallet_balances::{BalanceLock, Reasons}; use pallet_contracts::{Code, CollectEvents, DebugInfo, Determinism}; use pallet_contracts_fixtures::compile_module; use pallet_contracts_uapi::ReturnErrorCode; use xcm::{v4::prelude::*, VersionedLocation, VersionedXcm}; +use xcm_executor::traits::{QueryHandler, QueryResponseStatus}; use xcm_simulator::TestExt; type ParachainContracts = pallet_contracts::Pallet; +type QueryId = as QueryHandler>::QueryId; macro_rules! assert_return_code { ( $x:expr , $y:expr $(,)? ) => {{ @@ -276,3 +280,102 @@ fn test_xcm_send() { ); }); } + +#[test] +fn test_xcm_query() { + MockNet::reset(); + let contract_addr = instantiate_test_contract("xcm_query"); + + ParaA::execute_with(|| { + let match_querier = Location::from(AccountId32 { network: None, id: ALICE.into() }); + let match_querier = VersionedLocation::V4(match_querier); + let timeout: BlockNumberFor = 1u32.into(); + + // Invoke the contract to create an XCM query. + let exec = ParachainContracts::bare_call( + ALICE, + contract_addr.clone(), + 0, + Weight::MAX, + None, + (timeout, match_querier).encode(), + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + Determinism::Enforced, + ); + + let mut data = &exec.result.unwrap().data[..]; + let query_id = QueryId::decode(&mut data).expect("Failed to decode message"); + + // Verify that the query exists and is pending. + let response = ::take_response(query_id); + let expected_response = QueryResponseStatus::Pending { timeout }; + assert_eq!(response, expected_response); + }); +} + +#[test] +fn test_xcm_take_response() { + MockNet::reset(); + let contract_addr = instantiate_test_contract("xcm_take_response"); + + ParaA::execute_with(|| { + let querier: Location = (Parent, AccountId32 { network: None, id: ALICE.into() }).into(); + let responder = Location::from(AccountId32 { + network: Some(NetworkId::ByGenesis([0u8; 32])), + id: ALICE.into(), + }); + + // Register a new query. + let query_id = ParachainPalletXcm::new_query(responder, 1u32.into(), querier.clone()); + + // Helper closure to call the contract to take the response. + let call = |query_id: QueryId| { + let exec = ParachainContracts::bare_call( + ALICE, + contract_addr.clone(), + 0, + Weight::MAX, + None, + query_id.encode(), + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + Determinism::Enforced, + ); + + QueryResponseStatus::>::decode( + &mut &exec.result.unwrap().data[..], + ) + .expect("Failed to decode message") + }; + + // Query is not yet answered. + assert_eq!(QueryResponseStatus::Pending { timeout: 1u32.into() }, call(query_id)); + + // Execute the XCM program that answers the query. + let message = Xcm(vec![QueryResponse { + query_id, + response: Response::ExecutionResult(None), + max_weight: Weight::zero(), + querier: Some(querier), + }]); + ParachainPalletXcm::execute( + RuntimeOrigin::signed(ALICE), + Box::new(VersionedXcm::V4(message)), + Weight::from_parts(1_000_000_000, 1_000_000_000), + ) + .unwrap(); + + // First call returns the response. + assert_eq!( + QueryResponseStatus::Ready { + response: Response::ExecutionResult(None), + at: 1u32.into() + }, + call(query_id) + ); + + // Second call returns `NotFound`. (Query was already answered) + assert_eq!(QueryResponseStatus::NotFound, call(query_id)); + }) +} diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index f440c818166d..73a22cf184f0 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -2075,7 +2075,7 @@ pub mod env { ) -> Result { let str_len = str_len.min(DebugBufferVec::::bound() as u32); ctx.charge_gas(RuntimeCosts::DebugMessage(str_len))?; - if ctx.ext.append_debug_buffer("") { + if ctx.ext.debug_buffer_enabled() { let data = ctx.read_sandbox_memory(memory, str_ptr, str_len)?; if let Some(msg) = core::str::from_utf8(&data).ok() { ctx.ext.append_debug_buffer(msg); @@ -2171,7 +2171,7 @@ pub mod env { Ok(ReturnErrorCode::Success) }, Err(e) => { - if ctx.ext.append_debug_buffer("") { + if ctx.ext.debug_buffer_enabled() { ctx.ext.append_debug_buffer("seal0::xcm_send failed with: "); ctx.ext.append_debug_buffer(e.into()); }; @@ -2180,6 +2180,86 @@ pub mod env { } } + /// Create a new query, using the contract's address as the responder. + /// + /// # Parameters + /// + /// - `timeout_ptr`: the pointer into the linear memory where the timeout is placed. + /// - `match_querier_ptr`: the pointer into the linear memory where the match_querier is placed. + /// - `output_ptr`: the pointer into the linear memory where the + /// [`xcm_builder::QueryHandler::QueryId`] is placed. + /// + /// # Return Value + /// + /// Returns `ReturnCode::Success` when the query was successfully created. When the query + /// creation fails, `ReturnCode::XcmQueryFailed` is returned. + #[unstable] + fn xcm_query( + ctx: _, + memory: _, + timeout_ptr: u32, + match_querier_ptr: u32, + output_ptr: u32, + ) -> Result { + use frame_system::pallet_prelude::BlockNumberFor; + use xcm::VersionedLocation; + use xcm_builder::{QueryController, QueryControllerWeightInfo}; + + let timeout: BlockNumberFor = ctx.read_sandbox_memory_as(memory, timeout_ptr)?; + let match_querier: VersionedLocation = + ctx.read_sandbox_memory_as(memory, match_querier_ptr)?; + + let weight = <::Xcm as QueryController<_, _>>::WeightInfo::query(); + ctx.charge_gas(RuntimeCosts::CallRuntime(weight))?; + let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into(); + + match <::Xcm>::query(origin, timeout, match_querier) { + Ok(query_id) => { + ctx.write_sandbox_memory(memory, output_ptr, &query_id.encode())?; + Ok(ReturnErrorCode::Success) + }, + Err(e) => { + if ctx.ext.debug_buffer_enabled() { + ctx.ext.append_debug_buffer("call failed with: "); + ctx.ext.append_debug_buffer(e.into()); + }; + Ok(ReturnErrorCode::XcmQueryFailed) + }, + } + } + + /// Take an XCM response for the specified query. + /// + /// # Parameters + /// + /// - `query_id_ptr`: the pointer into the linear memory where the + /// [`xcm_builder::QueryHandler::QueryId`] is placed. + /// - `output_ptr`: the pointer into the linear memory where the response + /// [`xcm_builder::QueryResponseStatus`] is placed. + /// + /// # Return Value + /// + /// Returns `ReturnCode::Success` when successful. + #[unstable] + fn xcm_take_response( + ctx: _, + memory: _, + query_id_ptr: u32, + output_ptr: u32, + ) -> Result { + use xcm_builder::{QueryController, QueryControllerWeightInfo, QueryHandler}; + + let query_id: <::Xcm as QueryHandler>::QueryId = + ctx.read_sandbox_memory_as(memory, query_id_ptr)?; + + let weight = <::Xcm as QueryController<_, _>>::WeightInfo::take_response(); + ctx.charge_gas(RuntimeCosts::CallRuntime(weight))?; + + let response = <::Xcm>::take_response(query_id).encode(); + ctx.write_sandbox_memory(memory, output_ptr, &response)?; + Ok(ReturnErrorCode::Success) + } + /// Recovers the ECDSA public key from the given message hash and signature. /// See [`pallet_contracts_uapi::HostFn::ecdsa_recover`]. #[prefixed_alias] diff --git a/substrate/frame/contracts/uapi/src/host.rs b/substrate/frame/contracts/uapi/src/host.rs index c25be4479cef..0af56d55633f 100644 --- a/substrate/frame/contracts/uapi/src/host.rs +++ b/substrate/frame/contracts/uapi/src/host.rs @@ -790,7 +790,7 @@ pub trait HostFn { /// /// # Parameters /// - /// - `dest`: The XCM destination, should be decodable as [VersionedMultiLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedMultiLocation.html), + /// - `dest`: The XCM destination, should be decodable as [VersionedLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedLocation.html), /// traps otherwise. /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), /// traps otherwise. @@ -803,4 +803,34 @@ pub trait HostFn { note = "Unstable function. Behaviour can change without further notice. Use only for testing." )] fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result; + + /// Create a new query, using the contract's address as the responder. + /// + /// # Parameters + /// + /// - `timeout_ptr`: The query timeout, should be decodable as a `BlockNumberFor`. + /// - `match_querier`: The match_querier should be decodable as [VersionedLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedLocation.html), + /// - `output`: A reference to the output data buffer to write the + /// [`xcm_builder::QueryHandler::QueryId`]. + /// + /// # Return Value + /// + /// Returns `ReturnCode::Success` when the query was successfully created. When the query + /// creation fails, `ReturnCode::XcmQueryFailed` is returned. + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] + fn xcm_query(timeout: &[u8], match_querier: &[u8], output: &mut [u8]) -> Result; + + /// Take an XCM response for the specified query. + /// + /// # Parameters + /// + /// - `query_id`: The [`xcm_builder::QueryHandler::QueryId`] + /// - `output`: A reference to the output data buffer to write the + /// [`xcm_builder::QueryResponseStatus`]. + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] + fn xcm_take_response(query_id: &[u8], output: &mut [u8]) -> Result; } diff --git a/substrate/frame/contracts/uapi/src/host/riscv32.rs b/substrate/frame/contracts/uapi/src/host/riscv32.rs index dbd5abc42409..d2191aa136b3 100644 --- a/substrate/frame/contracts/uapi/src/host/riscv32.rs +++ b/substrate/frame/contracts/uapi/src/host/riscv32.rs @@ -304,4 +304,12 @@ impl HostFn for HostFnImpl { fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result { todo!() } + + fn xcm_query(timeout: &[u8], match_querier: &[u8], output: &mut &mut [u8]) -> Result { + todo!() + } + + fn xcm_take_response(query_id: &[u8], output: &mut [u8]) { + todo!() + } } diff --git a/substrate/frame/contracts/uapi/src/host/wasm32.rs b/substrate/frame/contracts/uapi/src/host/wasm32.rs index 9651aa73d6f9..1a36a1092c06 100644 --- a/substrate/frame/contracts/uapi/src/host/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/host/wasm32.rs @@ -168,6 +168,14 @@ mod sys { msg_len: u32, output_ptr: *mut u8, ) -> ReturnCode; + + pub fn xcm_query( + timeout_ptr: *const u8, + match_querier: *const u8, + output_ptr: *mut u8, + ) -> ReturnCode; + + pub fn xcm_take_response(query_id_ptr: *const u8, output_ptr: *mut u8) -> ReturnCode; } pub mod v1 { @@ -830,4 +838,16 @@ impl HostFn for HostFnImpl { }; ret_code.into() } + + fn xcm_query(timeout: &[u8], match_querier: &[u8], output: &mut [u8]) -> Result { + let ret_code = unsafe { + sys::xcm_query(timeout.as_ptr(), match_querier.as_ptr(), output.as_mut_ptr()) + }; + ret_code.into() + } + + fn xcm_take_response(query_id: &[u8], output: &mut [u8]) -> Result { + let ret_code = unsafe { sys::xcm_take_response(query_id.as_ptr(), output.as_mut_ptr()) }; + ret_code.into() + } } diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 83877c6efd40..c1d69524d888 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -103,6 +103,8 @@ define_error_codes! { XcmExecutionFailed = 13, /// The `xcm_send` call failed. XcmSendFailed = 14, + /// The `xcm_query` call failed. + XcmQueryFailed = 15, } /// The raw return code returned by the host side. From d665ccc93b760f2ba54de583e81b23e7756d1972 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 27 Feb 2024 23:05:32 +0100 Subject: [PATCH 2/4] fix fmt --- substrate/frame/contracts/fixtures/contracts/xcm_query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/contracts/xcm_query.rs b/substrate/frame/contracts/fixtures/contracts/xcm_query.rs index 2d24e9f8af71..eec5d0e87634 100644 --- a/substrate/frame/contracts/fixtures/contracts/xcm_query.rs +++ b/substrate/frame/contracts/fixtures/contracts/xcm_query.rs @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(512, timeout: [u8; 8], match_querier: [u8],); + input!(512, timeout: [u8; 8], match_querier: [u8],); let mut query_id = [0u8; 8]; #[allow(deprecated)] From c1fba21a711ffe54e890af99b273ae1184e74b8d Mon Sep 17 00:00:00 2001 From: pgherveou Date: Wed, 28 Feb 2024 10:35:43 +0100 Subject: [PATCH 3/4] Add TODO --- .../fixtures/contracts/xcm_take_response.rs | 1 - substrate/frame/contracts/src/wasm/runtime.rs | 25 ++++++++----------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/substrate/frame/contracts/fixtures/contracts/xcm_take_response.rs b/substrate/frame/contracts/fixtures/contracts/xcm_take_response.rs index 4b0740b9e36d..847dd1e4c572 100644 --- a/substrate/frame/contracts/fixtures/contracts/xcm_take_response.rs +++ b/substrate/frame/contracts/fixtures/contracts/xcm_take_response.rs @@ -33,6 +33,5 @@ pub extern "C" fn call() { #[allow(deprecated)] api::xcm_take_response(query_id, &mut response_status).unwrap(); - api::return_value(uapi::ReturnFlags::empty(), &response_status); } diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index 73a22cf184f0..e4af8043e8cf 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -2104,7 +2104,7 @@ pub mod env { } /// Execute an XCM program locally, using the contract's address as the origin. - /// See [`pallet_contracts_uapi::HostFn::execute_xcm`]. + /// See [`pallet_contracts_uapi::HostFn::xcm_execute`]. #[unstable] fn xcm_execute( ctx: _, @@ -2143,7 +2143,7 @@ pub mod env { } /// Send an XCM program from the contract to the specified destination. - /// See [`pallet_contracts_uapi::HostFn::send_xcm`]. + /// See [`pallet_contracts_uapi::HostFn::xcm_send`]. #[unstable] fn xcm_send( ctx: _, @@ -2181,18 +2181,7 @@ pub mod env { } /// Create a new query, using the contract's address as the responder. - /// - /// # Parameters - /// - /// - `timeout_ptr`: the pointer into the linear memory where the timeout is placed. - /// - `match_querier_ptr`: the pointer into the linear memory where the match_querier is placed. - /// - `output_ptr`: the pointer into the linear memory where the - /// [`xcm_builder::QueryHandler::QueryId`] is placed. - /// - /// # Return Value - /// - /// Returns `ReturnCode::Success` when the query was successfully created. When the query - /// creation fails, `ReturnCode::XcmQueryFailed` is returned. + /// See [`pallet_contracts_uapi::HostFn::xcm_query`]. #[unstable] fn xcm_query( ctx: _, @@ -2213,6 +2202,10 @@ pub mod env { ctx.charge_gas(RuntimeCosts::CallRuntime(weight))?; let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into(); + // TODO: + // - Take deposit for query. + // - Keep a map of account_id to query_id. + match <::Xcm>::query(origin, timeout, match_querier) { Ok(query_id) => { ctx.write_sandbox_memory(memory, output_ptr, &query_id.encode())?; @@ -2249,6 +2242,10 @@ pub mod env { ) -> Result { use xcm_builder::{QueryController, QueryControllerWeightInfo, QueryHandler}; + // TODO: + // - Ensure query_id belong to contract + // - Release deposit for query. + let query_id: <::Xcm as QueryHandler>::QueryId = ctx.read_sandbox_memory_as(memory, query_id_ptr)?; From 26e121fb63bc4c813fe2e5294c8047f8f0a63170 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Fri, 1 Mar 2024 15:58:43 +0100 Subject: [PATCH 4/4] wip --- .../xcm-executor/src/traits/on_response.rs | 2 +- .../contracts/fixtures/contracts/xcm_query.rs | 15 +- .../frame/contracts/mock-network/src/lib.rs | 202 ++- .../contracts/mock-network/src/parachain.rs | 513 +++--- .../frame/contracts/mock-network/src/tests.rs | 724 ++++---- substrate/frame/contracts/uapi/src/host.rs | 1591 +++++++++-------- 6 files changed, 1631 insertions(+), 1416 deletions(-) diff --git a/polkadot/xcm/xcm-executor/src/traits/on_response.rs b/polkadot/xcm/xcm-executor/src/traits/on_response.rs index 952bd2d0040a..53c43573f1d1 100644 --- a/polkadot/xcm/xcm-executor/src/traits/on_response.rs +++ b/polkadot/xcm/xcm-executor/src/traits/on_response.rs @@ -98,7 +98,7 @@ impl VersionChangeNotifier for () { } /// The possible state of an XCM query response. -#[derive(Debug, PartialEq, Eq, Encode, Decode)] +#[derive(Debug, PartialEq, Eq, Encode, Decode, TypeInfo)] pub enum QueryResponseStatus { /// The response has arrived, and includes the inner Response and the block number it arrived /// at. diff --git a/substrate/frame/contracts/fixtures/contracts/xcm_query.rs b/substrate/frame/contracts/fixtures/contracts/xcm_query.rs index eec5d0e87634..8ce664433ea9 100644 --- a/substrate/frame/contracts/fixtures/contracts/xcm_query.rs +++ b/substrate/frame/contracts/fixtures/contracts/xcm_query.rs @@ -19,7 +19,10 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{ + HostFn, + HostFnImpl as api, +}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -28,10 +31,10 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(512, timeout: [u8; 8], match_querier: [u8],); - let mut query_id = [0u8; 8]; + input!(512, timeout: [u8; 4], match_querier: [u8],); + let mut query_id = [0u8; 8]; - #[allow(deprecated)] - api::xcm_query(timeout, match_querier, &mut query_id).unwrap(); - api::return_value(uapi::ReturnFlags::empty(), &query_id); + #[allow(deprecated)] + api::xcm_query(timeout, match_querier, &mut query_id).unwrap(); + api::return_value(uapi::ReturnFlags::empty(), &query_id); } diff --git a/substrate/frame/contracts/mock-network/src/lib.rs b/substrate/frame/contracts/mock-network/src/lib.rs index 8a17a3f2fa78..dfed77589d3c 100644 --- a/substrate/frame/contracts/mock-network/src/lib.rs +++ b/substrate/frame/contracts/mock-network/src/lib.rs @@ -22,12 +22,20 @@ pub mod relay_chain; #[cfg(test)] mod tests; -use crate::primitives::{AccountId, UNITS}; +use crate::primitives::{ + AccountId, + UNITS, +}; use sp_runtime::BuildStorage; use xcm::latest::prelude::*; +pub use xcm_executor; use xcm_executor::traits::ConvertLocation; pub use xcm_simulator::TestExt; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; +use xcm_simulator::{ + decl_test_network, + decl_test_parachain, + decl_test_relay_chain, +}; // Accounts pub const ADMIN: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]); @@ -38,114 +46,132 @@ pub const BOB: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([2u8; 32]) pub const INITIAL_BALANCE: u128 = 1_000_000_000 * UNITS; decl_test_parachain! { - pub struct ParaA { - Runtime = parachain::Runtime, - XcmpMessageHandler = parachain::MsgQueue, - DmpMessageHandler = parachain::MsgQueue, - new_ext = para_ext(1), - } + pub struct ParaA { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(1), + } } decl_test_relay_chain! { - pub struct Relay { - Runtime = relay_chain::Runtime, - RuntimeCall = relay_chain::RuntimeCall, - RuntimeEvent = relay_chain::RuntimeEvent, - XcmConfig = relay_chain::XcmConfig, - MessageQueue = relay_chain::MessageQueue, - System = relay_chain::System, - new_ext = relay_ext(), - } + pub struct Relay { + Runtime = relay_chain::Runtime, + RuntimeCall = relay_chain::RuntimeCall, + RuntimeEvent = relay_chain::RuntimeEvent, + XcmConfig = relay_chain::XcmConfig, + MessageQueue = relay_chain::MessageQueue, + System = relay_chain::System, + new_ext = relay_ext(), + } } decl_test_network! { - pub struct MockNet { - relay_chain = Relay, - parachains = vec![ - (1, ParaA), - ], - } + pub struct MockNet { + relay_chain = Relay, + parachains = vec![ + (1, ParaA), + ], + } } pub fn relay_sovereign_account_id() -> AccountId { - let location: Location = (Parent,).into(); - parachain::SovereignAccountOf::convert_location(&location).unwrap() + let location: Location = (Parent,).into(); + parachain::SovereignAccountOf::convert_location(&location).unwrap() } pub fn parachain_sovereign_account_id(para: u32) -> AccountId { - let location: Location = (Parachain(para),).into(); - relay_chain::SovereignAccountOf::convert_location(&location).unwrap() + let location: Location = (Parachain(para),).into(); + relay_chain::SovereignAccountOf::convert_location(&location).unwrap() } pub fn parachain_account_sovereign_account_id( - para: u32, - who: sp_runtime::AccountId32, + para: u32, + who: sp_runtime::AccountId32, ) -> AccountId { - let location: Location = ( - Parachain(para), - AccountId32 { network: Some(relay_chain::RelayNetwork::get()), id: who.into() }, - ) - .into(); - relay_chain::SovereignAccountOf::convert_location(&location).unwrap() + let location: Location = ( + Parachain(para), + AccountId32 { + network: Some(relay_chain::RelayNetwork::get()), + id: who.into(), + }, + ) + .into(); + relay_chain::SovereignAccountOf::convert_location(&location).unwrap() } pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { - use parachain::{MsgQueue, Runtime, System}; - - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![ - (ALICE, INITIAL_BALANCE), - (relay_sovereign_account_id(), INITIAL_BALANCE), - (BOB, INITIAL_BALANCE), - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - pallet_assets::GenesisConfig:: { - assets: vec![ - (0u128, ADMIN, false, 1u128), // Create derivative asset for relay's native token - ], - metadata: Default::default(), - accounts: vec![ - (0u128, ALICE, INITIAL_BALANCE), - (0u128, relay_sovereign_account_id(), INITIAL_BALANCE), - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| { - sp_tracing::try_init_simple(); - System::set_block_number(1); - MsgQueue::set_para_id(para_id.into()); - }); - ext + use parachain::{ + MsgQueue, + Runtime, + System, + }; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (relay_sovereign_account_id(), INITIAL_BALANCE), + (BOB, INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_assets::GenesisConfig:: { + assets: vec![ + (0u128, ADMIN, false, 1u128), /* Create derivative asset for relay's + * native token */ + ], + metadata: Default::default(), + accounts: vec![ + (0u128, ALICE, INITIAL_BALANCE), + (0u128, relay_sovereign_account_id(), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + sp_tracing::try_init_simple(); + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext } pub fn relay_ext() -> sp_io::TestExternalities { - use relay_chain::{Runtime, System}; - - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![ - (ALICE, INITIAL_BALANCE), - (parachain_sovereign_account_id(1), INITIAL_BALANCE), - (parachain_account_sovereign_account_id(1, ALICE), INITIAL_BALANCE), - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| { - System::set_block_number(1); - }); - ext + use relay_chain::{ + Runtime, + System, + }; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (parachain_sovereign_account_id(1), INITIAL_BALANCE), + ( + parachain_account_sovereign_account_id(1, ALICE), + INITIAL_BALANCE, + ), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext } pub type ParachainPalletXcm = pallet_xcm::Pallet; diff --git a/substrate/frame/contracts/mock-network/src/parachain.rs b/substrate/frame/contracts/mock-network/src/parachain.rs index 7a60a66b3145..cde36186a2e1 100644 --- a/substrate/frame/contracts/mock-network/src/parachain.rs +++ b/substrate/frame/contracts/mock-network/src/parachain.rs @@ -18,202 +18,255 @@ mod contracts_config; use crate::{ - mocks::msg_queue::pallet as mock_msg_queue, - primitives::{AccountId, AssetIdForAssets, Balance}, + mocks::msg_queue::pallet as mock_msg_queue, + primitives::{ + AccountId, + AssetIdForAssets, + Balance, + }, }; use core::marker::PhantomData; use frame_support::{ - construct_runtime, derive_impl, parameter_types, - traits::{AsEnsureOriginWithArg, Contains, ContainsPair, Everything, EverythingBut, Nothing}, - weights::{ - constants::{WEIGHT_PROOF_SIZE_PER_MB, WEIGHT_REF_TIME_PER_SECOND}, - Weight, - }, + construct_runtime, + derive_impl, + parameter_types, + traits::{ + AsEnsureOriginWithArg, + Contains, + ContainsPair, + Everything, + EverythingBut, + Nothing, + }, + weights::{ + constants::{ + WEIGHT_PROOF_SIZE_PER_MB, + WEIGHT_REF_TIME_PER_SECOND, + }, + Weight, + }, +}; +use frame_system::{ + EnsureRoot, + EnsureSigned, }; -use frame_system::{EnsureRoot, EnsureSigned}; use pallet_xcm::XcmPassthrough; -use sp_core::{ConstU32, ConstU64, H256}; -use sp_runtime::traits::{Get, IdentityLookup, MaybeEquivalence}; +use sp_core::{ + ConstU32, + ConstU64, + H256, +}; +use sp_runtime::traits::{ + Get, + IdentityLookup, + MaybeEquivalence, +}; use sp_std::prelude::*; use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, - ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, - FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, IsConcrete, NativeAsset, - NoChecking, ParentAsSuperuser, ParentIsPreset, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, WithComputedOrigin, + AccountId32Aliases, + AllowExplicitUnpaidExecutionFrom, + AllowTopLevelPaidExecutionFrom, + ConvertedConcreteId, + EnsureXcmOrigin, + FixedRateOfFungible, + FixedWeightBounds, + FrameTransactionalProcessor, + FungibleAdapter, + FungiblesAdapter, + IsConcrete, + NativeAsset, + NoChecking, + ParentAsSuperuser, + ParentIsPreset, + SignedAccountId32AsNative, + SignedToAccountId32, + SovereignSignedViaLocation, + WithComputedOrigin, +}; +use xcm_executor::{ + traits::JustTry, + Config, + XcmExecutor, }; -use xcm_executor::{traits::JustTry, Config, XcmExecutor}; -pub type SovereignAccountOf = - (AccountId32Aliases, ParentIsPreset); +pub type SovereignAccountOf = ( + AccountId32Aliases, + ParentIsPreset, +); parameter_types! { - pub const BlockHashCount: u64 = 250; + pub const BlockHashCount: u32 = 250; } #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Block = Block; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type BlockWeights = (); - type BlockLength = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type DbWeight = (); - type BaseCallFilter = Everything; - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; } parameter_types! { - pub ExistentialDeposit: Balance = 1; - pub const MaxLocks: u32 = 50; - pub const MaxReserves: u32 = 50; + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; } impl pallet_balances::Config for Runtime { - type AccountStore = System; - type Balance = Balance; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<0>; - type MaxLocks = MaxLocks; - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeEvent = RuntimeEvent; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type WeightInfo = (); + type AccountStore = System; + type Balance = Balance; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeEvent = RuntimeEvent; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type WeightInfo = (); } parameter_types! { - pub const AssetDeposit: u128 = 1_000_000; - pub const MetadataDepositBase: u128 = 1_000_000; - pub const MetadataDepositPerByte: u128 = 100_000; - pub const AssetAccountDeposit: u128 = 1_000_000; - pub const ApprovalDeposit: u128 = 1_000_000; - pub const AssetsStringLimit: u32 = 50; - pub const RemoveItemsLimit: u32 = 50; + pub const AssetDeposit: u128 = 1_000_000; + pub const MetadataDepositBase: u128 = 1_000_000; + pub const MetadataDepositPerByte: u128 = 100_000; + pub const AssetAccountDeposit: u128 = 1_000_000; + pub const ApprovalDeposit: u128 = 1_000_000; + pub const AssetsStringLimit: u32 = 50; + pub const RemoveItemsLimit: u32 = 50; } impl pallet_assets::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Balance = Balance; - type AssetId = AssetIdForAssets; - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = EnsureRoot; - type AssetDeposit = AssetDeposit; - type MetadataDepositBase = MetadataDepositBase; - type MetadataDepositPerByte = MetadataDepositPerByte; - type AssetAccountDeposit = AssetAccountDeposit; - type ApprovalDeposit = ApprovalDeposit; - type StringLimit = AssetsStringLimit; - type Freezer = (); - type Extra = (); - type WeightInfo = (); - type RemoveItemsLimit = RemoveItemsLimit; - type AssetIdParameter = AssetIdForAssets; - type CallbackHandle = (); - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetIdForAssets; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type AssetAccountDeposit = AssetAccountDeposit; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = (); + type RemoveItemsLimit = RemoveItemsLimit; + type AssetIdParameter = AssetIdForAssets; + type CallbackHandle = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { - pub const ReservedXcmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); - pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); + pub const ReservedXcmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); + pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); } parameter_types! { - pub const KsmLocation: Location = Location::parent(); - pub const TokenLocation: Location = Here.into_location(); - pub const RelayNetwork: NetworkId = ByGenesis([0; 32]); - pub UniversalLocation: InteriorLocation = Parachain(MsgQueue::parachain_id().into()).into(); + pub const KsmLocation: Location = Location::parent(); + pub const TokenLocation: Location = Here.into_location(); + pub const RelayNetwork: NetworkId = ByGenesis([0; 32]); + pub UniversalLocation: InteriorLocation = Parachain(MsgQueue::parachain_id().into()).into(); } pub type XcmOriginToCallOrigin = ( - SovereignSignedViaLocation, - ParentAsSuperuser, - SignedAccountId32AsNative, - XcmPassthrough, + SovereignSignedViaLocation, + ParentAsSuperuser, + SignedAccountId32AsNative, + XcmPassthrough, ); parameter_types! { - pub const XcmInstructionWeight: Weight = Weight::from_parts(1_000, 1_000); - pub TokensPerSecondPerMegabyte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1_000_000_000_000, 1024 * 1024); - pub const MaxInstructions: u32 = 100; - pub const MaxAssetsIntoHolding: u32 = 64; - pub ForeignPrefix: Location = (Parent,).into(); - pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - pub TrustedLockPairs: (Location, AssetFilter) = - (Parent.into(), Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible })); + pub const XcmInstructionWeight: Weight = Weight::from_parts(1_000, 1_000); + pub TokensPerSecondPerMegabyte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1_000_000_000_000, 1024 * 1024); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; + pub ForeignPrefix: Location = (Parent,).into(); + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub TrustedLockPairs: (Location, AssetFilter) = + (Parent.into(), Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible })); } pub fn estimate_message_fee(number_of_instructions: u64) -> u128 { - let weight = estimate_weight(number_of_instructions); + let weight = estimate_weight(number_of_instructions); - estimate_fee_for_weight(weight) + estimate_fee_for_weight(weight) } pub fn estimate_weight(number_of_instructions: u64) -> Weight { - XcmInstructionWeight::get().saturating_mul(number_of_instructions) + XcmInstructionWeight::get().saturating_mul(number_of_instructions) } pub fn estimate_fee_for_weight(weight: Weight) -> u128 { - let (_, units_per_second, units_per_mb) = TokensPerSecondPerMegabyte::get(); + let (_, units_per_second, units_per_mb) = TokensPerSecondPerMegabyte::get(); - units_per_second * (weight.ref_time() as u128) / (WEIGHT_REF_TIME_PER_SECOND as u128) + - units_per_mb * (weight.proof_size() as u128) / (WEIGHT_PROOF_SIZE_PER_MB as u128) + units_per_second * (weight.ref_time() as u128) / (WEIGHT_REF_TIME_PER_SECOND as u128) + + units_per_mb * (weight.proof_size() as u128) + / (WEIGHT_PROOF_SIZE_PER_MB as u128) } -pub type LocalBalancesTransactor = - FungibleAdapter, SovereignAccountOf, AccountId, ()>; +pub type LocalBalancesTransactor = FungibleAdapter< + Balances, + IsConcrete, + SovereignAccountOf, + AccountId, + (), +>; pub struct FromLocationToAsset(PhantomData<(Location, AssetId)>); impl MaybeEquivalence - for FromLocationToAsset + for FromLocationToAsset { - fn convert(value: &Location) -> Option { - match value.unpack() { - (1, []) => Some(0 as AssetIdForAssets), - (1, [Parachain(para_id)]) => Some(*para_id as AssetIdForAssets), - _ => None, - } - } - - fn convert_back(_id: &AssetIdForAssets) -> Option { - None - } + fn convert(value: &Location) -> Option { + match value.unpack() { + (1, []) => Some(0 as AssetIdForAssets), + (1, [Parachain(para_id)]) => Some(*para_id as AssetIdForAssets), + _ => None, + } + } + + fn convert_back(_id: &AssetIdForAssets) -> Option { + None + } } pub type ForeignAssetsTransactor = FungiblesAdapter< - Assets, - ConvertedConcreteId< - AssetIdForAssets, - Balance, - FromLocationToAsset, - JustTry, - >, - SovereignAccountOf, - AccountId, - NoChecking, - CheckingAccount, + Assets, + ConvertedConcreteId< + AssetIdForAssets, + Balance, + FromLocationToAsset, + JustTry, + >, + SovereignAccountOf, + AccountId, + NoChecking, + CheckingAccount, >; /// Means for transacting assets on this chain @@ -221,128 +274,136 @@ pub type AssetTransactors = (LocalBalancesTransactor, ForeignAssetsTransactor); pub struct ParentRelay; impl Contains for ParentRelay { - fn contains(location: &Location) -> bool { - location.contains_parents_only(1) - } + fn contains(location: &Location) -> bool { + location.contains_parents_only(1) + } } pub struct ThisParachain; impl Contains for ThisParachain { - fn contains(location: &Location) -> bool { - matches!(location.unpack(), (0, [Junction::AccountId32 { .. }])) - } + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (0, [Junction::AccountId32 { .. }])) + } } pub type XcmRouter = crate::ParachainXcmRouter; pub type Barrier = ( - xcm_builder::AllowUnpaidExecutionFrom, - WithComputedOrigin< - (AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom), - UniversalLocation, - ConstU32<1>, - >, + xcm_builder::AllowUnpaidExecutionFrom, + WithComputedOrigin< + ( + AllowExplicitUnpaidExecutionFrom, + AllowTopLevelPaidExecutionFrom, + ), + UniversalLocation, + ConstU32<1>, + >, ); parameter_types! { - pub NftCollectionOne: AssetFilter - = Wild(AllOf { fun: WildNonFungible, id: AssetId((Parent, GeneralIndex(1)).into()) }); - pub NftCollectionOneForRelay: (AssetFilter, Location) - = (NftCollectionOne::get(), Parent.into()); - pub RelayNativeAsset: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId((Parent, Here).into()) }); - pub RelayNativeAssetForRelay: (AssetFilter, Location) = (RelayNativeAsset::get(), Parent.into()); + pub NftCollectionOne: AssetFilter + = Wild(AllOf { fun: WildNonFungible, id: AssetId((Parent, GeneralIndex(1)).into()) }); + pub NftCollectionOneForRelay: (AssetFilter, Location) + = (NftCollectionOne::get(), Parent.into()); + pub RelayNativeAsset: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId((Parent, Here).into()) }); + pub RelayNativeAssetForRelay: (AssetFilter, Location) = (RelayNativeAsset::get(), Parent.into()); } -pub type TrustedTeleporters = - (xcm_builder::Case, xcm_builder::Case); +pub type TrustedTeleporters = ( + xcm_builder::Case, + xcm_builder::Case, +); pub type TrustedReserves = EverythingBut>; pub struct XcmConfig; impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = AssetTransactors; - type OriginConverter = XcmOriginToCallOrigin; - type IsReserve = (NativeAsset, TrustedReserves); - type IsTeleporter = TrustedTeleporters; - type UniversalLocation = UniversalLocation; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; - type Trader = FixedRateOfFungible; - type ResponseHandler = PolkadotXcm; - type AssetTrap = PolkadotXcm; - type AssetLocker = PolkadotXcm; - type AssetExchanger = (); - type AssetClaims = PolkadotXcm; - type SubscriptionService = PolkadotXcm; - type PalletInstancesInfo = AllPalletsWithSystem; - type FeeManager = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type MessageExporter = (); - type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = FrameTransactionalProcessor; + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToCallOrigin; + type IsReserve = (NativeAsset, TrustedReserves); + type IsTeleporter = TrustedTeleporters; + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetLocker = PolkadotXcm; + type AssetExchanger = (); + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = AllPalletsWithSystem; + type FeeManager = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } impl mock_msg_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; } -pub type LocalOriginToLocation = SignedToAccountId32; +pub type LocalOriginToLocation = + SignedToAccountId32; pub struct TrustedLockerCase(PhantomData); -impl> ContainsPair for TrustedLockerCase { - fn contains(origin: &Location, asset: &Asset) -> bool { - let (o, a) = T::get(); - a.matches(asset) && &o == origin - } +impl> ContainsPair + for TrustedLockerCase +{ + fn contains(origin: &Location, asset: &Asset) -> bool { + let (o, a) = T::get(); + a.matches(asset) && &o == origin + } } impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Everything; - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = Nothing; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - type UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = IsConcrete; - type TrustedLockers = TrustedLockerCase; - type SovereignAccountOf = SovereignAccountOf; - type MaxLockers = ConstU32<8>; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type WeightInfo = pallet_xcm::TestWeightInfo; - type AdminOrigin = EnsureRoot; + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type TrustedLockers = TrustedLockerCase; + type SovereignAccountOf = SovereignAccountOf; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type WeightInfo = pallet_xcm::TestWeightInfo; + type AdminOrigin = EnsureRoot; } -type Block = frame_system::mocking::MockBlock; +type Block = frame_system::mocking::MockBlockU32; impl pallet_timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = ConstU64<1>; - type WeightInfo = (); + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<1>; + type WeightInfo = (); } construct_runtime!( - pub enum Runtime - { - System: frame_system, - Balances: pallet_balances, - Timestamp: pallet_timestamp, - MsgQueue: mock_msg_queue, - PolkadotXcm: pallet_xcm, - Contracts: pallet_contracts, - Assets: pallet_assets, - } + pub enum Runtime + { + System: frame_system, + Balances: pallet_balances, + Timestamp: pallet_timestamp, + MsgQueue: mock_msg_queue, + PolkadotXcm: pallet_xcm, + Contracts: pallet_contracts, + Assets: pallet_assets, + } ); diff --git a/substrate/frame/contracts/mock-network/src/tests.rs b/substrate/frame/contracts/mock-network/src/tests.rs index 3c345e3e74f6..4de25b08d9b9 100644 --- a/substrate/frame/contracts/mock-network/src/tests.rs +++ b/substrate/frame/contracts/mock-network/src/tests.rs @@ -16,366 +16,454 @@ // limitations under the License. use crate::{ - parachain::{self, Runtime, RuntimeOrigin}, - parachain_account_sovereign_account_id, - primitives::{AccountId, CENTS}, - relay_chain, MockNet, ParaA, ParachainBalances, ParachainPalletXcm, Relay, ALICE, BOB, - INITIAL_BALANCE, + parachain::{ + self, + Runtime, + RuntimeOrigin, + }, + parachain_account_sovereign_account_id, + primitives::{ + AccountId, + CENTS, + }, + relay_chain, + MockNet, + ParaA, + ParachainBalances, + ParachainPalletXcm, + Relay, + ALICE, + BOB, + INITIAL_BALANCE, +}; +use codec::{ + Decode, + Encode, }; -use codec::{Decode, Encode}; use frame_support::{ - assert_err, - pallet_prelude::Weight, - traits::{fungibles::Mutate, Currency}, + assert_err, + pallet_prelude::Weight, + traits::{ + fungibles::Mutate, + Currency, + }, }; use frame_system::pallet_prelude::BlockNumberFor; -use pallet_balances::{BalanceLock, Reasons}; -use pallet_contracts::{Code, CollectEvents, DebugInfo, Determinism}; +use pallet_balances::{ + BalanceLock, + Reasons, +}; +use pallet_contracts::{ + Code, + CollectEvents, + DebugInfo, + Determinism, +}; use pallet_contracts_fixtures::compile_module; use pallet_contracts_uapi::ReturnErrorCode; -use xcm::{v4::prelude::*, VersionedLocation, VersionedXcm}; -use xcm_executor::traits::{QueryHandler, QueryResponseStatus}; +use xcm::{ + v4::prelude::*, + VersionedLocation, + VersionedXcm, +}; +use xcm_executor::traits::{ + QueryHandler, + QueryResponseStatus, +}; use xcm_simulator::TestExt; type ParachainContracts = pallet_contracts::Pallet; type QueryId = as QueryHandler>::QueryId; macro_rules! assert_return_code { - ( $x:expr , $y:expr $(,)? ) => {{ - assert_eq!(u32::from_le_bytes($x.data[..].try_into().unwrap()), $y as u32); - }}; + ( $x:expr , $y:expr $(,)? ) => {{ + assert_eq!( + u32::from_le_bytes($x.data[..].try_into().unwrap()), + $y as u32 + ); + }}; } /// Instantiate the tests contract, and fund it with some balance and assets. fn instantiate_test_contract(name: &str) -> AccountId { - let (wasm, _) = compile_module::(name).unwrap(); - - // Instantiate contract. - let contract_addr = ParaA::execute_with(|| { - ParachainContracts::bare_instantiate( - ALICE, - 0, - Weight::MAX, - None, - Code::Upload(wasm), - vec![], - vec![], - DebugInfo::UnsafeDebug, - CollectEvents::Skip, - ) - .result - .unwrap() - .account_id - }); - - // Funds contract account with some balance and assets. - ParaA::execute_with(|| { - parachain::Balances::make_free_balance_be(&contract_addr, INITIAL_BALANCE); - parachain::Assets::mint_into(0u32.into(), &contract_addr, INITIAL_BALANCE).unwrap(); - }); - Relay::execute_with(|| { - let sovereign_account = parachain_account_sovereign_account_id(1u32, contract_addr.clone()); - relay_chain::Balances::make_free_balance_be(&sovereign_account, INITIAL_BALANCE); - }); - - contract_addr + let (wasm, _) = compile_module::(name).unwrap(); + + // Instantiate contract. + let contract_addr = ParaA::execute_with(|| { + ParachainContracts::bare_instantiate( + ALICE, + 0, + Weight::MAX, + None, + Code::Upload(wasm), + vec![], + vec![], + DebugInfo::UnsafeDebug, + CollectEvents::Skip, + ) + .result + .unwrap() + .account_id + }); + + // Funds contract account with some balance and assets. + ParaA::execute_with(|| { + parachain::Balances::make_free_balance_be(&contract_addr, INITIAL_BALANCE); + parachain::Assets::mint_into(0u32.into(), &contract_addr, INITIAL_BALANCE) + .unwrap(); + }); + Relay::execute_with(|| { + let sovereign_account = + parachain_account_sovereign_account_id(1u32, contract_addr.clone()); + relay_chain::Balances::make_free_balance_be(&sovereign_account, INITIAL_BALANCE); + }); + + contract_addr } #[test] fn test_xcm_execute() { - MockNet::reset(); - - let contract_addr = instantiate_test_contract("xcm_execute"); - - // Execute XCM instructions through the contract. - ParaA::execute_with(|| { - let amount: u128 = 10 * CENTS; - - // The XCM used to transfer funds to Bob. - let message: Xcm<()> = Xcm(vec![ - WithdrawAsset(vec![(Here, amount).into()].into()), - DepositAsset { - assets: All.into(), - beneficiary: AccountId32 { network: None, id: BOB.clone().into() }.into(), - }, - ]); - - let result = ParachainContracts::bare_call( - ALICE, - contract_addr.clone(), - 0, - Weight::MAX, - None, - VersionedXcm::V4(message).encode(), - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - Determinism::Enforced, - ); - - assert_eq!(result.gas_consumed, result.gas_required); - assert_return_code!(&result.result.unwrap(), ReturnErrorCode::Success); - - // Check if the funds are subtracted from the account of Alice and added to the account of - // Bob. - let initial = INITIAL_BALANCE; - assert_eq!(ParachainBalances::free_balance(BOB), initial + amount); - assert_eq!(ParachainBalances::free_balance(&contract_addr), initial - amount); - }); + MockNet::reset(); + + let contract_addr = instantiate_test_contract("xcm_execute"); + + // Execute XCM instructions through the contract. + ParaA::execute_with(|| { + let amount: u128 = 10 * CENTS; + + // The XCM used to transfer funds to Bob. + let message: Xcm<()> = Xcm(vec![ + WithdrawAsset(vec![(Here, amount).into()].into()), + DepositAsset { + assets: All.into(), + beneficiary: AccountId32 { + network: None, + id: BOB.clone().into(), + } + .into(), + }, + ]); + + let result = ParachainContracts::bare_call( + ALICE, + contract_addr.clone(), + 0, + Weight::MAX, + None, + VersionedXcm::V4(message).encode(), + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + Determinism::Enforced, + ); + + assert_eq!(result.gas_consumed, result.gas_required); + assert_return_code!(&result.result.unwrap(), ReturnErrorCode::Success); + + // Check if the funds are subtracted from the account of Alice and added to the + // account of Bob. + let initial = INITIAL_BALANCE; + assert_eq!(ParachainBalances::free_balance(BOB), initial + amount); + assert_eq!( + ParachainBalances::free_balance(&contract_addr), + initial - amount + ); + }); } #[test] fn test_xcm_execute_incomplete() { - MockNet::reset(); - - let contract_addr = instantiate_test_contract("xcm_execute"); - let amount = 10 * CENTS; - - // Execute XCM instructions through the contract. - ParaA::execute_with(|| { - // The XCM used to transfer funds to Bob. - let message: Xcm<()> = Xcm(vec![ - WithdrawAsset(vec![(Here, amount).into()].into()), - // This will fail as the contract does not have enough balance to complete both - // withdrawals. - WithdrawAsset(vec![(Here, INITIAL_BALANCE).into()].into()), - DepositAsset { - assets: All.into(), - beneficiary: AccountId32 { network: None, id: BOB.clone().into() }.into(), - }, - ]); - - let result = ParachainContracts::bare_call( - ALICE, - contract_addr.clone(), - 0, - Weight::MAX, - None, - VersionedXcm::V4(message).encode(), - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - Determinism::Enforced, - ); - - assert_eq!(result.gas_consumed, result.gas_required); - assert_return_code!(&result.result.unwrap(), ReturnErrorCode::XcmExecutionFailed); - - assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE); - assert_eq!(ParachainBalances::free_balance(&contract_addr), INITIAL_BALANCE - amount); - }); + MockNet::reset(); + + let contract_addr = instantiate_test_contract("xcm_execute"); + let amount = 10 * CENTS; + + // Execute XCM instructions through the contract. + ParaA::execute_with(|| { + // The XCM used to transfer funds to Bob. + let message: Xcm<()> = Xcm(vec![ + WithdrawAsset(vec![(Here, amount).into()].into()), + // This will fail as the contract does not have enough balance to complete + // both withdrawals. + WithdrawAsset(vec![(Here, INITIAL_BALANCE).into()].into()), + DepositAsset { + assets: All.into(), + beneficiary: AccountId32 { + network: None, + id: BOB.clone().into(), + } + .into(), + }, + ]); + + let result = ParachainContracts::bare_call( + ALICE, + contract_addr.clone(), + 0, + Weight::MAX, + None, + VersionedXcm::V4(message).encode(), + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + Determinism::Enforced, + ); + + assert_eq!(result.gas_consumed, result.gas_required); + assert_return_code!(&result.result.unwrap(), ReturnErrorCode::XcmExecutionFailed); + + assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE); + assert_eq!( + ParachainBalances::free_balance(&contract_addr), + INITIAL_BALANCE - amount + ); + }); } #[test] fn test_xcm_execute_filtered_call() { - MockNet::reset(); - - let contract_addr = instantiate_test_contract("xcm_execute"); - - ParaA::execute_with(|| { - // `remark` should be rejected, as it is not allowed by our CallFilter. - let call = parachain::RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - let message: Xcm = Xcm(vec![Transact { - origin_kind: OriginKind::Native, - require_weight_at_most: Weight::MAX, - call: call.encode().into(), - }]); - - let result = ParachainContracts::bare_call( - ALICE, - contract_addr.clone(), - 0, - Weight::MAX, - None, - VersionedXcm::V4(message).encode(), - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - Determinism::Enforced, - ); - - assert_err!(result.result, frame_system::Error::::CallFiltered); - }); + MockNet::reset(); + + let contract_addr = instantiate_test_contract("xcm_execute"); + + ParaA::execute_with(|| { + // `remark` should be rejected, as it is not allowed by our CallFilter. + let call = + parachain::RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let message: Xcm = Xcm(vec![Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: Weight::MAX, + call: call.encode().into(), + }]); + + let result = ParachainContracts::bare_call( + ALICE, + contract_addr.clone(), + 0, + Weight::MAX, + None, + VersionedXcm::V4(message).encode(), + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + Determinism::Enforced, + ); + + assert_err!( + result.result, + frame_system::Error::::CallFiltered + ); + }); } #[test] fn test_xcm_execute_reentrant_call() { - MockNet::reset(); - - let contract_addr = instantiate_test_contract("xcm_execute"); - - ParaA::execute_with(|| { - let transact_call = parachain::RuntimeCall::Contracts(pallet_contracts::Call::call { - dest: contract_addr.clone(), - gas_limit: 1_000_000.into(), - storage_deposit_limit: None, - data: vec![], - value: 0u128, - }); - - // The XCM used to transfer funds to Bob. - let message: Xcm = Xcm(vec![ - Transact { - origin_kind: OriginKind::Native, - require_weight_at_most: 1_000_000_000.into(), - call: transact_call.encode().into(), - }, - ExpectTransactStatus(MaybeErrorCode::Success), - ]); - - let result = ParachainContracts::bare_call( - ALICE, - contract_addr.clone(), - 0, - Weight::MAX, - None, - VersionedXcm::V4(message).encode(), - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - Determinism::Enforced, - ); - - assert_return_code!(&result.result.unwrap(), ReturnErrorCode::XcmExecutionFailed); - - // Funds should not change hands as the XCM transact failed. - assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE); - }); + MockNet::reset(); + + let contract_addr = instantiate_test_contract("xcm_execute"); + + ParaA::execute_with(|| { + let transact_call = + parachain::RuntimeCall::Contracts(pallet_contracts::Call::call { + dest: contract_addr.clone(), + gas_limit: 1_000_000.into(), + storage_deposit_limit: None, + data: vec![], + value: 0u128, + }); + + // The XCM used to transfer funds to Bob. + let message: Xcm = Xcm(vec![ + Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: 1_000_000_000.into(), + call: transact_call.encode().into(), + }, + ExpectTransactStatus(MaybeErrorCode::Success), + ]); + + let result = ParachainContracts::bare_call( + ALICE, + contract_addr.clone(), + 0, + Weight::MAX, + None, + VersionedXcm::V4(message).encode(), + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + Determinism::Enforced, + ); + + assert_return_code!(&result.result.unwrap(), ReturnErrorCode::XcmExecutionFailed); + + // Funds should not change hands as the XCM transact failed. + assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE); + }); } #[test] fn test_xcm_send() { - MockNet::reset(); - let contract_addr = instantiate_test_contract("xcm_send"); - let fee = parachain::estimate_message_fee(4); // Accounts for the `DescendOrigin` instruction added by `send_xcm` - - // Send XCM instructions through the contract, to lock some funds on the relay chain. - ParaA::execute_with(|| { - let dest = Location::from(Parent); - let dest = VersionedLocation::V4(dest); - - let message: Xcm<()> = Xcm(vec![ - WithdrawAsset((Here, fee).into()), - BuyExecution { fees: (Here, fee).into(), weight_limit: WeightLimit::Unlimited }, - LockAsset { asset: (Here, 5 * CENTS).into(), unlocker: (Parachain(1)).into() }, - ]); - let message = VersionedXcm::V4(message); - let exec = ParachainContracts::bare_call( - ALICE, - contract_addr.clone(), - 0, - Weight::MAX, - None, - (dest, message).encode(), - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - Determinism::Enforced, - ); - - let mut data = &exec.result.unwrap().data[..]; - XcmHash::decode(&mut data).expect("Failed to decode xcm_send message_id"); - }); - - Relay::execute_with(|| { - // Check if the funds are locked on the relay chain. - assert_eq!( - relay_chain::Balances::locks(¶chain_account_sovereign_account_id(1, contract_addr)), - vec![BalanceLock { id: *b"py/xcmlk", amount: 5 * CENTS, reasons: Reasons::All }] - ); - }); + MockNet::reset(); + let contract_addr = instantiate_test_contract("xcm_send"); + let fee = parachain::estimate_message_fee(4); // Accounts for the `DescendOrigin` instruction added by `send_xcm` + + // Send XCM instructions through the contract, to lock some funds on the relay chain. + ParaA::execute_with(|| { + let dest = Location::from(Parent); + let dest = VersionedLocation::V4(dest); + + let message: Xcm<()> = Xcm(vec![ + WithdrawAsset((Here, fee).into()), + BuyExecution { + fees: (Here, fee).into(), + weight_limit: WeightLimit::Unlimited, + }, + LockAsset { + asset: (Here, 5 * CENTS).into(), + unlocker: (Parachain(1)).into(), + }, + ]); + let message = VersionedXcm::V4(message); + let exec = ParachainContracts::bare_call( + ALICE, + contract_addr.clone(), + 0, + Weight::MAX, + None, + (dest, message).encode(), + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + Determinism::Enforced, + ); + + let mut data = &exec.result.unwrap().data[..]; + XcmHash::decode(&mut data).expect("Failed to decode xcm_send message_id"); + }); + + Relay::execute_with(|| { + // Check if the funds are locked on the relay chain. + assert_eq!( + relay_chain::Balances::locks(¶chain_account_sovereign_account_id( + 1, + contract_addr + )), + vec![BalanceLock { + id: *b"py/xcmlk", + amount: 5 * CENTS, + reasons: Reasons::All + }] + ); + }); } #[test] fn test_xcm_query() { - MockNet::reset(); - let contract_addr = instantiate_test_contract("xcm_query"); - - ParaA::execute_with(|| { - let match_querier = Location::from(AccountId32 { network: None, id: ALICE.into() }); - let match_querier = VersionedLocation::V4(match_querier); - let timeout: BlockNumberFor = 1u32.into(); - - // Invoke the contract to create an XCM query. - let exec = ParachainContracts::bare_call( - ALICE, - contract_addr.clone(), - 0, - Weight::MAX, - None, - (timeout, match_querier).encode(), - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - Determinism::Enforced, - ); - - let mut data = &exec.result.unwrap().data[..]; - let query_id = QueryId::decode(&mut data).expect("Failed to decode message"); - - // Verify that the query exists and is pending. - let response = ::take_response(query_id); - let expected_response = QueryResponseStatus::Pending { timeout }; - assert_eq!(response, expected_response); - }); + MockNet::reset(); + let contract_addr = instantiate_test_contract("xcm_query"); + + ParaA::execute_with(|| { + let match_querier = Location::from(AccountId32 { + network: None, + id: ALICE.into(), + }); + let match_querier = VersionedLocation::V4(match_querier); + let timeout: BlockNumberFor = 1u32.into(); + + // Invoke the contract to create an XCM query. + let exec = ParachainContracts::bare_call( + ALICE, + contract_addr.clone(), + 0, + Weight::MAX, + None, + (timeout, match_querier).encode(), + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + Determinism::Enforced, + ); + + let mut data = &exec.result.unwrap().data[..]; + let query_id = QueryId::decode(&mut data).expect("Failed to decode message"); + + // Verify that the query exists and is pending. + let response = ::take_response(query_id); + let expected_response = QueryResponseStatus::Pending { timeout }; + assert_eq!(response, expected_response); + }); } #[test] fn test_xcm_take_response() { - MockNet::reset(); - let contract_addr = instantiate_test_contract("xcm_take_response"); - - ParaA::execute_with(|| { - let querier: Location = (Parent, AccountId32 { network: None, id: ALICE.into() }).into(); - let responder = Location::from(AccountId32 { - network: Some(NetworkId::ByGenesis([0u8; 32])), - id: ALICE.into(), - }); - - // Register a new query. - let query_id = ParachainPalletXcm::new_query(responder, 1u32.into(), querier.clone()); - - // Helper closure to call the contract to take the response. - let call = |query_id: QueryId| { - let exec = ParachainContracts::bare_call( - ALICE, - contract_addr.clone(), - 0, - Weight::MAX, - None, - query_id.encode(), - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - Determinism::Enforced, - ); - - QueryResponseStatus::>::decode( - &mut &exec.result.unwrap().data[..], - ) - .expect("Failed to decode message") - }; - - // Query is not yet answered. - assert_eq!(QueryResponseStatus::Pending { timeout: 1u32.into() }, call(query_id)); - - // Execute the XCM program that answers the query. - let message = Xcm(vec![QueryResponse { - query_id, - response: Response::ExecutionResult(None), - max_weight: Weight::zero(), - querier: Some(querier), - }]); - ParachainPalletXcm::execute( - RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::V4(message)), - Weight::from_parts(1_000_000_000, 1_000_000_000), - ) - .unwrap(); - - // First call returns the response. - assert_eq!( - QueryResponseStatus::Ready { - response: Response::ExecutionResult(None), - at: 1u32.into() - }, - call(query_id) - ); - - // Second call returns `NotFound`. (Query was already answered) - assert_eq!(QueryResponseStatus::NotFound, call(query_id)); - }) + MockNet::reset(); + let contract_addr = instantiate_test_contract("xcm_take_response"); + + ParaA::execute_with(|| { + let querier: Location = ( + Parent, + AccountId32 { + network: None, + id: ALICE.into(), + }, + ) + .into(); + let responder = Location::from(AccountId32 { + network: Some(NetworkId::ByGenesis([0u8; 32])), + id: ALICE.into(), + }); + + // Register a new query. + let query_id = + ParachainPalletXcm::new_query(responder, 1u32.into(), querier.clone()); + + // Helper closure to call the contract to take the response. + let call = |query_id: QueryId| { + let exec = ParachainContracts::bare_call( + ALICE, + contract_addr.clone(), + 0, + Weight::MAX, + None, + query_id.encode(), + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + Determinism::Enforced, + ); + + QueryResponseStatus::>::decode( + &mut &exec.result.unwrap().data[..], + ) + .expect("Failed to decode message") + }; + + // Query is not yet answered. + assert_eq!( + QueryResponseStatus::Pending { + timeout: 1u32.into() + }, + call(query_id) + ); + + // Execute the XCM program that answers the query. + let message = Xcm(vec![QueryResponse { + query_id, + response: Response::ExecutionResult(None), + max_weight: Weight::zero(), + querier: Some(querier), + }]); + ParachainPalletXcm::execute( + RuntimeOrigin::signed(ALICE), + Box::new(VersionedXcm::V4(message)), + Weight::from_parts(1_000_000_000, 1_000_000_000), + ) + .unwrap(); + + // First call returns the response. + assert_eq!( + QueryResponseStatus::Ready { + response: Response::ExecutionResult(None), + at: 1u32.into() + }, + call(query_id) + ); + + // Second call returns `NotFound`. (Query was already answered) + assert_eq!(QueryResponseStatus::NotFound, call(query_id)); + }) } diff --git a/substrate/frame/contracts/uapi/src/host.rs b/substrate/frame/contracts/uapi/src/host.rs index 0af56d55633f..1b3d9a152810 100644 --- a/substrate/frame/contracts/uapi/src/host.rs +++ b/substrate/frame/contracts/uapi/src/host.rs @@ -11,7 +11,11 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use crate::{CallFlags, Result, ReturnFlags}; +use crate::{ + CallFlags, + Result, + ReturnFlags, +}; use paste::paste; #[cfg(target_arch = "wasm32")] @@ -40,27 +44,27 @@ macro_rules! hash_fn { #[cfg(target_arch = "wasm32")] #[inline(always)] fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { - debug_assert!(new_len <= output.len()); - let tmp = core::mem::take(output); - *output = &mut tmp[..new_len]; + debug_assert!(new_len <= output.len()); + let tmp = core::mem::take(output); + *output = &mut tmp[..new_len]; } #[cfg(target_arch = "wasm32")] #[inline(always)] fn ptr_len_or_sentinel(data: &mut Option<&mut &mut [u8]>) -> (*mut u8, u32) { - match data { - Some(ref mut data) => (data.as_mut_ptr(), data.len() as _), - None => (crate::SENTINEL as _, 0), - } + match data { + Some(ref mut data) => (data.as_mut_ptr(), data.len() as _), + None => (crate::SENTINEL as _, 0), + } } #[cfg(target_arch = "wasm32")] #[inline(always)] fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { - match data { - Some(ref data) => data.as_ptr(), - None => crate::SENTINEL as _, - } + match data { + Some(ref data) => data.as_ptr(), + None => crate::SENTINEL as _, + } } /// Implements [`HostFn`] for each supported target architecture. @@ -68,769 +72,802 @@ pub enum HostFnImpl {} /// Defines all the host apis implemented by both wasm and RISC-V vms. pub trait HostFn { - /// Returns the number of times specified contract exists on the call stack. Delegated calls are - /// not counted as separate calls. - /// - /// # Parameters - /// - /// - `account`: The contract address. Should be decodable as an `T::AccountId`. Traps - /// otherwise. - /// - /// # Return - /// - /// Returns the number of times specified contract exists on the call stack. - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] - fn account_reentrance_count(account: &[u8]) -> u32; - - /// Stores the address of the current contract into the supplied buffer. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the address. - fn address(output: &mut &mut [u8]); - - /// Lock a new delegate dependency to the contract. - /// - /// Traps if the maximum number of delegate_dependencies is reached or if - /// the delegate dependency already exists. - /// - /// # Parameters - /// - /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps - /// otherwise. - fn lock_delegate_dependency(code_hash: &[u8]); - - /// Stores the *free* balance of the current account into the supplied buffer. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the balance. - fn balance(output: &mut &mut [u8]); - - /// Stores the current block number of the current contract into the supplied buffer. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the block number. - fn block_number(output: &mut &mut [u8]); - - /// Make a call to another contract. - /// - /// This is equivalent to calling the newer version of this function with - /// `flags` set to [`CallFlags::ALLOW_REENTRY`]. See the newer version for documentation. - #[deprecated(note = "Deprecated, use newer version instead")] - fn call( - callee: &[u8], - gas: u64, - value: &[u8], - input_data: &[u8], - output: Option<&mut &mut [u8]>, - ) -> Result; - - /// Make a call to another contract. - /// - /// Equivalent to the newer [`Self::call_v2`] version but works with - /// *ref_time* Weight only - #[deprecated(note = "Deprecated, use newer version instead")] - fn call_v1( - flags: CallFlags, - callee: &[u8], - gas: u64, - value: &[u8], - input_data: &[u8], - output: Option<&mut &mut [u8]>, - ) -> Result; - - /// Call (possibly transferring some amount of funds) into the specified account. - /// - /// # Parameters - /// - /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. - /// - `callee`: The address of the callee. Should be decodable as an `T::AccountId`. Traps - /// otherwise. - /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. - /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. - /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a - /// `Option`. Traps otherwise. Passing `None` means setting no specific limit for - /// the call, which implies storage usage up to the limit of the parent call. - /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. - /// Traps otherwise. - /// - `input`: The input data buffer used to call the contract. - /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` - /// is provided then the output buffer is not copied. - /// - /// # Errors - /// - /// An error means that the call wasn't successful output buffer is returned unless - /// stated otherwise. - /// - /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. - /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] - /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] - /// - [NotCallable][`crate::ReturnErrorCode::NotCallable] - fn call_v2( - flags: CallFlags, - callee: &[u8], - ref_time_limit: u64, - proof_time_limit: u64, - deposit: Option<&[u8]>, - value: &[u8], - input_data: &[u8], - output: Option<&mut &mut [u8]>, - ) -> Result; - - /// Call into the chain extension provided by the chain if any. - /// - /// Handling of the input values is up to the specific chain extension and so is the - /// return value. The extension can decide to use the inputs as primitive inputs or as - /// in/out arguments by interpreting them as pointers. Any caller of this function - /// must therefore coordinate with the chain that it targets. - /// - /// # Note - /// - /// If no chain extension exists the contract will trap with the `NoChainExtension` - /// module error. - /// - /// # Parameters - /// - /// - `func_id`: The function id of the chain extension. - /// - `input`: The input data buffer. - /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` - /// is provided then the output buffer is not copied. - /// - /// # Return - /// - /// The chain extension returned value, if executed successfully. - fn call_chain_extension(func_id: u32, input: &[u8], output: Option<&mut &mut [u8]>) -> u32; - - /// Call some dispatchable of the runtime. - /// - /// # Parameters - /// - /// - `call`: The call data. - /// - /// # Return - /// - /// Returns `Error::Success` when the dispatchable was successfully executed and - /// returned `Ok`. When the dispatchable was executed but returned an error - /// `Error::CallRuntimeFailed` is returned. The full error is not - /// provided because it is not guaranteed to be stable. - /// - /// # Comparison with `ChainExtension` - /// - /// Just as a chain extension this API allows the runtime to extend the functionality - /// of contracts. While making use of this function is generally easier it cannot be - /// used in all cases. Consider writing a chain extension if you need to do perform - /// one of the following tasks: - /// - /// - Return data. - /// - Provide functionality **exclusively** to contracts. - /// - Provide custom weights. - /// - Avoid the need to keep the `Call` data structure stable. - fn call_runtime(call: &[u8]) -> Result; - - /// Stores the address of the caller into the supplied buffer. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// - /// If this is a top-level call (i.e. initiated by an extrinsic) the origin address of the - /// extrinsic will be returned. Otherwise, if this call is initiated by another contract then - /// the address of the contract will be returned. - /// - /// If there is no address associated with the caller (e.g. because the caller is root) then - /// it traps with `BadOrigin`. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the caller address. - fn caller(output: &mut &mut [u8]); - - /// Checks whether the caller of the current contract is the origin of the whole call stack. - /// - /// Prefer this over [`is_contract()`][`Self::is_contract`] when checking whether your contract - /// is being called by a contract or a plain account. The reason is that it performs better - /// since it does not need to do any storage lookups. - /// - /// # Return - /// - /// A return value of `true` indicates that this contract is being called by a plain account - /// and `false` indicates that the caller is another contract. - fn caller_is_origin() -> bool; - - /// Checks whether the caller of the current contract is root. - /// - /// Note that only the origin of the call stack can be root. Hence this function returning - /// `true` implies that the contract is being called by the origin. - /// - /// A return value of `true` indicates that this contract is being called by a root origin, - /// and `false` indicates that the caller is a signed origin. - fn caller_is_root() -> u32; - - /// Clear the value at the given key in the contract storage. - /// - /// Equivalent to the newer [`Self::clear_storage_v1`] version with - /// the exception of the return type. Still a valid thing to call when not interested in the - /// return value. - fn clear_storage(key: &[u8]); - - /// Clear the value at the given key in the contract storage. - /// - /// # Parameters - /// - /// - `key`: The storage key. - /// - /// # Return - /// - /// Returns the size of the pre-existing value at the specified key if any. - fn clear_storage_v1(key: &[u8]) -> Option; - - /// Retrieve the code hash for a specified contract address. - /// - /// # Parameters - /// - /// - `account_id`: The address of the contract.Should be decodable as an `T::AccountId`. Traps - /// otherwise. - /// - `output`: A reference to the output data buffer to write the code hash. - /// - /// - /// # Errors - /// - /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result; - - /// Checks whether there is a value stored under the given key. - /// - /// This version is to be used with a fixed sized storage key. For runtimes supporting - /// transparent hashing, please use the newer version of this function. - fn contains_storage(key: &[u8]) -> Option; - - /// Checks whether there is a value stored under the given key. - /// - /// The key length must not exceed the maximum defined by the contracts module parameter. - /// - /// # Parameters - /// - `key`: The storage key. - /// - /// # Return - /// - /// Returns the size of the pre-existing value at the specified key if any. - fn contains_storage_v1(key: &[u8]) -> Option; - - /// Emit a custom debug message. - /// - /// No newlines are added to the supplied message. - /// Specifying invalid UTF-8 just drops the message with no trap. - /// - /// This is a no-op if debug message recording is disabled which is always the case - /// when the code is executing on-chain. The message is interpreted as UTF-8 and - /// appended to the debug buffer which is then supplied to the calling RPC client. - /// - /// # Note - /// - /// Even though no action is taken when debug message recording is disabled there is still - /// a non trivial overhead (and weight cost) associated with calling this function. Contract - /// languages should remove calls to this function (either at runtime or compile time) when - /// not being executed as an RPC. For example, they could allow users to disable logging - /// through compile time flags (cargo features) for on-chain deployment. Additionally, the - /// return value of this function can be cached in order to prevent further calls at runtime. - fn debug_message(str: &[u8]) -> Result; - - /// Execute code in the context (storage, caller, value) of the current contract. - /// - /// Reentrancy protection is always disabled since the callee is allowed - /// to modify the callers storage. This makes going through a reentrancy attack - /// unnecessary for the callee when it wants to exploit the caller. - /// - /// # Parameters - /// - /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. - /// - `code_hash`: The hash of the code to be executed. - /// - `input`: The input data buffer used to call the contract. - /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` - /// is provided then the output buffer is not copied. - /// - /// # Errors - /// - /// An error means that the call wasn't successful and no output buffer is returned unless - /// stated otherwise. - /// - /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. - /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] - /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - fn delegate_call( - flags: CallFlags, - code_hash: &[u8], - input_data: &[u8], - output: Option<&mut &mut [u8]>, - ) -> Result; - - /// Deposit a contract event with the data buffer and optional list of topics. There is a limit - /// on the maximum number of topics specified by `event_topics`. - /// - /// There should not be any duplicates in `topics`. - /// - /// # Parameters - /// - /// - `topics`: The topics list encoded as `Vec`. It can't contain duplicates. - fn deposit_event(topics: &[u8], data: &[u8]); - - /// Recovers the ECDSA public key from the given message hash and signature. - /// - /// Writes the public key into the given output buffer. - /// Assumes the secp256k1 curve. - /// - /// # Parameters - /// - /// - `signature`: The signature bytes. - /// - `message_hash`: The message hash bytes. - /// - `output`: A reference to the output data buffer to write the public key. - /// - /// # Errors - /// - /// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed] - fn ecdsa_recover( - signature: &[u8; 65], - message_hash: &[u8; 32], - output: &mut [u8; 33], - ) -> Result; - - /// Calculates Ethereum address from the ECDSA compressed public key and stores - /// it into the supplied buffer. - /// - /// # Parameters - /// - /// - `pubkey`: The public key bytes. - /// - `output`: A reference to the output data buffer to write the address. - /// - /// # Errors - /// - /// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed] - fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result; - - /// Stores the weight left into the supplied buffer. - /// - /// Equivalent to the newer [`Self::gas_left_v1`] version but - /// works with *ref_time* Weight only. - fn gas_left(out: &mut &mut [u8]); - - /// Stores the amount of weight left into the supplied buffer. - /// The data is encoded as Weight. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the weight left. - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] - fn gas_left_v1(output: &mut &mut [u8]); - - /// Retrieve the value under the given key from storage. - /// - /// This version is to be used with a fixed sized storage key. For runtimes supporting - /// transparent hashing, please use the newer version of this function. - fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result; - - /// Retrieve the value under the given key from storage. - /// - /// The key length must not exceed the maximum defined by the contracts module parameter. - /// - /// # Parameters - /// - `key`: The storage key. - /// - `output`: A reference to the output data buffer to write the storage entry. - /// - /// # Errors - /// - /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] - fn get_storage_v1(key: &[u8], output: &mut &mut [u8]) -> Result; - - hash_fn!(sha2_256, 32); - hash_fn!(keccak_256, 32); - hash_fn!(blake2_256, 32); - hash_fn!(blake2_128, 16); - - /// Stores the input passed by the caller into the supplied buffer. - /// - /// # Note - /// - /// This function traps if: - /// - the input is larger than the available space. - /// - the input was previously forwarded by a [`call()`][`Self::call()`]. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the input data. - fn input(output: &mut &mut [u8]); - - /// Instantiate a contract with the specified code hash. - /// - /// Equivalent to the newer [`Self::instantiate_v2`] version but works - /// with *ref_time* Weight only. - #[deprecated(note = "Deprecated, use newer version instead")] - fn instantiate_v1( - code_hash: &[u8], - gas: u64, - value: &[u8], - input: &[u8], - address: Option<&mut &mut [u8]>, - output: Option<&mut &mut [u8]>, - salt: &[u8], - ) -> Result; - - /// Instantiate a contract with the specified code hash. - /// - /// This function creates an account and executes the constructor defined in the code specified - /// by the code hash. - /// - /// # Parameters - /// - /// - `code_hash`: The hash of the code to be instantiated. - /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. - /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. - /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a - /// `Option`. Traps otherwise. Passing `None` means setting no specific limit for - /// the call, which implies storage usage up to the limit of the parent call. - /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. - /// Traps otherwise. - /// - `input`: The input data buffer. - /// - `address`: A reference to the address buffer to write the address of the contract. If - /// `None` is provided then the output buffer is not copied. - /// - `output`: A reference to the return value buffer to write the constructor output buffer. - /// If `None` is provided then the output buffer is not copied. - /// - `salt`: The salt bytes to use for this instantiation. - /// - /// # Errors - /// - /// Please consult the [ReturnErrorCode][`crate::ReturnErrorCode`] enum declaration for more - /// information on those errors. Here we only note things specific to this function. - /// - /// An error means that the account wasn't created and no address or output buffer - /// is returned unless stated otherwise. - /// - /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. - /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] - /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] - /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - fn instantiate_v2( - code_hash: &[u8], - ref_time_limit: u64, - proof_size_limit: u64, - deposit: Option<&[u8]>, - value: &[u8], - input: &[u8], - address: Option<&mut &mut [u8]>, - output: Option<&mut &mut [u8]>, - salt: &[u8], - ) -> Result; - - /// Returns a nonce that is unique per contract instantiation. - /// - /// The nonce is incremented for each successful contract instantiation. This is a - /// sensible default salt for contract instantiations. - fn instantiation_nonce() -> u64; - - /// Checks whether a specified address belongs to a contract. - /// - /// # Parameters - /// - /// - `account_id`: The address to check. Should be decodable as an `T::AccountId`. Traps - /// otherwise. - /// - /// # Return - /// - /// Returns `true` if the address belongs to a contract. - fn is_contract(account_id: &[u8]) -> bool; - - /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. - /// The data is encoded as `T::Balance`. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the minimum balance. - fn minimum_balance(output: &mut &mut [u8]); - - /// Retrieve the code hash of the currently executing contract. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the code hash. - fn own_code_hash(output: &mut [u8]); - - /// Load the latest block timestamp into the supplied buffer - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the timestamp. - fn now(output: &mut &mut [u8]); - - /// Returns the number of times the currently executing contract exists on the call stack in - /// addition to the calling instance. - /// - /// # Return - /// - /// Returns `0` when there is no reentrancy. - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] - fn reentrance_count() -> u32; - - /// Removes the delegate dependency from the contract. - /// - /// Traps if the delegate dependency does not exist. - /// - /// # Parameters - /// - /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps - /// otherwise. - fn unlock_delegate_dependency(code_hash: &[u8]); - - /// Cease contract execution and save a data buffer as a result of the execution. - /// - /// This function never returns as it stops execution of the caller. - /// This is the only way to return a data buffer to the caller. Returning from - /// execution without calling this function is equivalent to calling: - /// ```nocompile - /// return_value(ReturnFlags::empty(), &[]) - /// ``` - /// - /// Using an unnamed non empty `ReturnFlags` triggers a trap. - /// - /// # Parameters - /// - /// - `flags`: Flag used to signal special return conditions to the supervisor. See - /// [`ReturnFlags`] for a documentation of the supported flags. - /// - `return_value`: The return value buffer. - fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !; - - /// Replace the contract code at the specified address with new code. - /// - /// # Note - /// - /// There are a couple of important considerations which must be taken into account when - /// using this API: - /// - /// 1. The storage at the code address will remain untouched. This means that contract - /// developers must ensure that the storage layout of the new code is compatible with that of - /// the old code. - /// - /// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another - /// way, when using this API you lose the guarantee that an address always identifies a specific - /// code hash. - /// - /// 3. If a contract calls into itself after changing its code the new call would use - /// the new code. However, if the original caller panics after returning from the sub call it - /// would revert the changes made by [`set_code_hash()`][`Self::set_code_hash`] and the next - /// caller would use the old code. - /// - /// # Parameters - /// - /// - `code_hash`: The hash of the new code. Should be decodable as an `T::Hash`. Traps - /// otherwise. - /// - /// # Errors - /// - /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - fn set_code_hash(code_hash: &[u8]) -> Result; - - /// Set the value at the given key in the contract storage. - /// - /// Equivalent to [`Self::set_storage_v1`] version with the - /// exception of the return type. Still a valid thing to call for fixed sized storage key, when - /// not interested in the return value. - fn set_storage(key: &[u8], value: &[u8]); - - /// Set the value at the given key in the contract storage. - /// - /// This version is to be used with a fixed sized storage key. For runtimes supporting - /// transparent hashing, please use the newer version of this function. - fn set_storage_v1(key: &[u8], value: &[u8]) -> Option; - - /// Set the value at the given key in the contract storage. - /// - /// The key and value lengths must not exceed the maximums defined by the contracts module - /// parameters. - /// - /// # Parameters - /// - /// - `key`: The storage key. - /// - `encoded_value`: The storage value. - /// - /// # Return - /// - /// Returns the size of the pre-existing value at the specified key if any. - fn set_storage_v2(key: &[u8], value: &[u8]) -> Option; - - /// Verify a sr25519 signature - /// - /// # Parameters - /// - /// - `signature`: The signature bytes. - /// - `message`: The message bytes. - /// - /// # Errors - /// - /// - [Sr25519VerifyFailed][`crate::ReturnErrorCode::Sr25519VerifyFailed] - fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result; - - /// Retrieve and remove the value under the given key from storage. - /// - /// # Parameters - /// - `key`: The storage key. - /// - `output`: A reference to the output data buffer to write the storage entry. - /// - /// # Errors - /// - /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] - fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result; - - /// Transfer some amount of funds into the specified account. - /// - /// # Parameters - /// - /// - `account_id`: The address of the account to transfer funds to. Should be decodable as an - /// `T::AccountId`. Traps otherwise. - /// - `value`: The value to transfer. Should be decodable as a `T::Balance`. Traps otherwise. - /// - /// # Errors - /// - /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] - fn transfer(account_id: &[u8], value: &[u8]) -> Result; - - /// Remove the calling account and transfer remaining balance. - /// - /// This is equivalent to calling the newer version of this function - #[deprecated(note = "Deprecated, use newer version instead")] - fn terminate(beneficiary: &[u8]) -> !; - - /// Remove the calling account and transfer remaining **free** balance. - /// - /// This function never returns. Either the termination was successful and the - /// execution of the destroyed contract is halted. Or it failed during the termination - /// which is considered fatal and results in a trap + rollback. - /// - /// # Parameters - /// - /// - `beneficiary`: The address of the beneficiary account, Should be decodable as an - /// `T::AccountId`. - /// - /// # Traps - /// - /// - The contract is live i.e is already on the call stack. - /// - Failed to send the balance to the beneficiary. - /// - The deletion queue is full. - fn terminate_v1(beneficiary: &[u8]) -> !; - - /// Stores the value transferred along with this call/instantiate into the supplied buffer. - /// The data is encoded as `T::Balance`. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the transferred value. - fn value_transferred(output: &mut &mut [u8]); - - /// Stores the price for the specified amount of gas into the supplied buffer. - /// - /// Equivalent to the newer [`Self::weight_to_fee_v1`] version but - /// works with *ref_time* Weight only. It is recommended to switch to the latest version, once - /// it's stabilized. - fn weight_to_fee(gas: u64, output: &mut &mut [u8]); - - /// Stores the price for the specified amount of gas into the supplied buffer. - /// The data is encoded as `T::Balance`. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// - /// # Parameters - /// - /// - `ref_time_limit`: The *ref_time* Weight limit to query the price for. - /// - `proof_size_limit`: The *proof_size* Weight limit to query the price for. - /// - `output`: A reference to the output data buffer to write the price. - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] - fn weight_to_fee_v1(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]); - - /// Execute an XCM program locally, using the contract's address as the origin. - /// This is equivalent to dispatching `pallet_xcm::execute` through call_runtime, except that - /// the function is called directly instead of being dispatched. - /// - /// # Parameters - /// - /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), - /// traps otherwise. - /// - `output`: A reference to the output data buffer to write the [Outcome](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v3/enum.Outcome.html) - /// - /// # Return - /// - /// Returns `Error::Success` when the XCM execution attempt is successful. When the XCM - /// execution fails, `ReturnCode::XcmExecutionFailed` is returned - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] - fn xcm_execute(msg: &[u8]) -> Result; - - /// Send an XCM program from the contract to the specified destination. - /// This is equivalent to dispatching `pallet_xcm::send` through `call_runtime`, except that - /// the function is called directly instead of being dispatched. - /// - /// # Parameters - /// - /// - `dest`: The XCM destination, should be decodable as [VersionedLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedLocation.html), - /// traps otherwise. - /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), - /// traps otherwise. - /// - /// # Return - /// - /// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM - /// execution fails, `ReturnErrorCode::XcmSendFailed` is returned. - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] - fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result; - - /// Create a new query, using the contract's address as the responder. - /// - /// # Parameters - /// - /// - `timeout_ptr`: The query timeout, should be decodable as a `BlockNumberFor`. - /// - `match_querier`: The match_querier should be decodable as [VersionedLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedLocation.html), - /// - `output`: A reference to the output data buffer to write the - /// [`xcm_builder::QueryHandler::QueryId`]. - /// - /// # Return Value - /// - /// Returns `ReturnCode::Success` when the query was successfully created. When the query - /// creation fails, `ReturnCode::XcmQueryFailed` is returned. - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] - fn xcm_query(timeout: &[u8], match_querier: &[u8], output: &mut [u8]) -> Result; - - /// Take an XCM response for the specified query. - /// - /// # Parameters - /// - /// - `query_id`: The [`xcm_builder::QueryHandler::QueryId`] - /// - `output`: A reference to the output data buffer to write the - /// [`xcm_builder::QueryResponseStatus`]. - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] - fn xcm_take_response(query_id: &[u8], output: &mut [u8]) -> Result; + /// Returns the number of times specified contract exists on the call stack. Delegated + /// calls are not counted as separate calls. + /// + /// # Parameters + /// + /// - `account`: The contract address. Should be decodable as an `T::AccountId`. Traps + /// otherwise. + /// + /// # Return + /// + /// Returns the number of times specified contract exists on the call stack. + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] + fn account_reentrance_count(account: &[u8]) -> u32; + + /// Stores the address of the current contract into the supplied buffer. + /// + /// If the available space in `output` is less than the size of the value a trap is + /// triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the address. + fn address(output: &mut &mut [u8]); + + /// Lock a new delegate dependency to the contract. + /// + /// Traps if the maximum number of delegate_dependencies is reached or if + /// the delegate dependency already exists. + /// + /// # Parameters + /// + /// - `code_hash`: The code hash of the dependency. Should be decodable as an + /// `T::Hash`. Traps otherwise. + fn lock_delegate_dependency(code_hash: &[u8]); + + /// Stores the *free* balance of the current account into the supplied buffer. + /// + /// If the available space in `output` is less than the size of the value a trap is + /// triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the balance. + fn balance(output: &mut &mut [u8]); + + /// Stores the current block number of the current contract into the supplied buffer. + /// + /// If the available space in `output` is less than the size of the value a trap is + /// triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the block number. + fn block_number(output: &mut &mut [u8]); + + /// Make a call to another contract. + /// + /// This is equivalent to calling the newer version of this function with + /// `flags` set to [`CallFlags::ALLOW_REENTRY`]. See the newer version for + /// documentation. + #[deprecated(note = "Deprecated, use newer version instead")] + fn call( + callee: &[u8], + gas: u64, + value: &[u8], + input_data: &[u8], + output: Option<&mut &mut [u8]>, + ) -> Result; + + /// Make a call to another contract. + /// + /// Equivalent to the newer [`Self::call_v2`] version but works with + /// *ref_time* Weight only + #[deprecated(note = "Deprecated, use newer version instead")] + fn call_v1( + flags: CallFlags, + callee: &[u8], + gas: u64, + value: &[u8], + input_data: &[u8], + output: Option<&mut &mut [u8]>, + ) -> Result; + + /// Call (possibly transferring some amount of funds) into the specified account. + /// + /// # Parameters + /// + /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. + /// - `callee`: The address of the callee. Should be decodable as an `T::AccountId`. + /// Traps otherwise. + /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. + /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. + /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a + /// `Option`. Traps otherwise. Passing `None` means setting no specific + /// limit for the call, which implies storage usage up to the limit of the parent + /// call. + /// - `value`: The value to transfer into the contract. Should be decodable as a + /// `T::Balance`. Traps otherwise. + /// - `input`: The input data buffer used to call the contract. + /// - `output`: A reference to the output data buffer to write the call output buffer. + /// If `None` is provided then the output buffer is not copied. + /// + /// # Errors + /// + /// An error means that the call wasn't successful output buffer is returned unless + /// stated otherwise. + /// + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is + /// returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] + /// - [NotCallable][`crate::ReturnErrorCode::NotCallable] + fn call_v2( + flags: CallFlags, + callee: &[u8], + ref_time_limit: u64, + proof_time_limit: u64, + deposit: Option<&[u8]>, + value: &[u8], + input_data: &[u8], + output: Option<&mut &mut [u8]>, + ) -> Result; + + /// Call into the chain extension provided by the chain if any. + /// + /// Handling of the input values is up to the specific chain extension and so is the + /// return value. The extension can decide to use the inputs as primitive inputs or as + /// in/out arguments by interpreting them as pointers. Any caller of this function + /// must therefore coordinate with the chain that it targets. + /// + /// # Note + /// + /// If no chain extension exists the contract will trap with the `NoChainExtension` + /// module error. + /// + /// # Parameters + /// + /// - `func_id`: The function id of the chain extension. + /// - `input`: The input data buffer. + /// - `output`: A reference to the output data buffer to write the call output buffer. + /// If `None` is provided then the output buffer is not copied. + /// + /// # Return + /// + /// The chain extension returned value, if executed successfully. + fn call_chain_extension( + func_id: u32, + input: &[u8], + output: Option<&mut &mut [u8]>, + ) -> u32; + + /// Call some dispatchable of the runtime. + /// + /// # Parameters + /// + /// - `call`: The call data. + /// + /// # Return + /// + /// Returns `Error::Success` when the dispatchable was successfully executed and + /// returned `Ok`. When the dispatchable was executed but returned an error + /// `Error::CallRuntimeFailed` is returned. The full error is not + /// provided because it is not guaranteed to be stable. + /// + /// # Comparison with `ChainExtension` + /// + /// Just as a chain extension this API allows the runtime to extend the functionality + /// of contracts. While making use of this function is generally easier it cannot be + /// used in all cases. Consider writing a chain extension if you need to do perform + /// one of the following tasks: + /// + /// - Return data. + /// - Provide functionality **exclusively** to contracts. + /// - Provide custom weights. + /// - Avoid the need to keep the `Call` data structure stable. + fn call_runtime(call: &[u8]) -> Result; + + /// Stores the address of the caller into the supplied buffer. + /// + /// If the available space in `output` is less than the size of the value a trap is + /// triggered. + /// + /// If this is a top-level call (i.e. initiated by an extrinsic) the origin address of + /// the extrinsic will be returned. Otherwise, if this call is initiated by + /// another contract then the address of the contract will be returned. + /// + /// If there is no address associated with the caller (e.g. because the caller is + /// root) then it traps with `BadOrigin`. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the caller address. + fn caller(output: &mut &mut [u8]); + + /// Checks whether the caller of the current contract is the origin of the whole call + /// stack. + /// + /// Prefer this over [`is_contract()`][`Self::is_contract`] when checking whether your + /// contract is being called by a contract or a plain account. The reason is that + /// it performs better since it does not need to do any storage lookups. + /// + /// # Return + /// + /// A return value of `true` indicates that this contract is being called by a plain + /// account and `false` indicates that the caller is another contract. + fn caller_is_origin() -> bool; + + /// Checks whether the caller of the current contract is root. + /// + /// Note that only the origin of the call stack can be root. Hence this function + /// returning `true` implies that the contract is being called by the origin. + /// + /// A return value of `true` indicates that this contract is being called by a root + /// origin, and `false` indicates that the caller is a signed origin. + fn caller_is_root() -> u32; + + /// Clear the value at the given key in the contract storage. + /// + /// Equivalent to the newer [`Self::clear_storage_v1`] version with + /// the exception of the return type. Still a valid thing to call when not interested + /// in the return value. + fn clear_storage(key: &[u8]); + + /// Clear the value at the given key in the contract storage. + /// + /// # Parameters + /// + /// - `key`: The storage key. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn clear_storage_v1(key: &[u8]) -> Option; + + /// Retrieve the code hash for a specified contract address. + /// + /// # Parameters + /// + /// - `account_id`: The address of the contract.Should be decodable as an + /// `T::AccountId`. Traps otherwise. + /// - `output`: A reference to the output data buffer to write the code hash. + /// + /// + /// # Errors + /// + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] + fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result; + + /// Checks whether there is a value stored under the given key. + /// + /// This version is to be used with a fixed sized storage key. For runtimes supporting + /// transparent hashing, please use the newer version of this function. + fn contains_storage(key: &[u8]) -> Option; + + /// Checks whether there is a value stored under the given key. + /// + /// The key length must not exceed the maximum defined by the contracts module + /// parameter. + /// + /// # Parameters + /// - `key`: The storage key. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn contains_storage_v1(key: &[u8]) -> Option; + + /// Emit a custom debug message. + /// + /// No newlines are added to the supplied message. + /// Specifying invalid UTF-8 just drops the message with no trap. + /// + /// This is a no-op if debug message recording is disabled which is always the case + /// when the code is executing on-chain. The message is interpreted as UTF-8 and + /// appended to the debug buffer which is then supplied to the calling RPC client. + /// + /// # Note + /// + /// Even though no action is taken when debug message recording is disabled there is + /// still a non trivial overhead (and weight cost) associated with calling this + /// function. Contract languages should remove calls to this function (either at + /// runtime or compile time) when not being executed as an RPC. For example, they + /// could allow users to disable logging through compile time flags (cargo + /// features) for on-chain deployment. Additionally, the return value of this + /// function can be cached in order to prevent further calls at runtime. + fn debug_message(str: &[u8]) -> Result; + + /// Execute code in the context (storage, caller, value) of the current contract. + /// + /// Reentrancy protection is always disabled since the callee is allowed + /// to modify the callers storage. This makes going through a reentrancy attack + /// unnecessary for the callee when it wants to exploit the caller. + /// + /// # Parameters + /// + /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. + /// - `code_hash`: The hash of the code to be executed. + /// - `input`: The input data buffer used to call the contract. + /// - `output`: A reference to the output data buffer to write the call output buffer. + /// If `None` is provided then the output buffer is not copied. + /// + /// # Errors + /// + /// An error means that the call wasn't successful and no output buffer is returned + /// unless stated otherwise. + /// + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is + /// returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] + fn delegate_call( + flags: CallFlags, + code_hash: &[u8], + input_data: &[u8], + output: Option<&mut &mut [u8]>, + ) -> Result; + + /// Deposit a contract event with the data buffer and optional list of topics. There + /// is a limit on the maximum number of topics specified by `event_topics`. + /// + /// There should not be any duplicates in `topics`. + /// + /// # Parameters + /// + /// - `topics`: The topics list encoded as `Vec`. It can't contain + /// duplicates. + fn deposit_event(topics: &[u8], data: &[u8]); + + /// Recovers the ECDSA public key from the given message hash and signature. + /// + /// Writes the public key into the given output buffer. + /// Assumes the secp256k1 curve. + /// + /// # Parameters + /// + /// - `signature`: The signature bytes. + /// - `message_hash`: The message hash bytes. + /// - `output`: A reference to the output data buffer to write the public key. + /// + /// # Errors + /// + /// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed] + fn ecdsa_recover( + signature: &[u8; 65], + message_hash: &[u8; 32], + output: &mut [u8; 33], + ) -> Result; + + /// Calculates Ethereum address from the ECDSA compressed public key and stores + /// it into the supplied buffer. + /// + /// # Parameters + /// + /// - `pubkey`: The public key bytes. + /// - `output`: A reference to the output data buffer to write the address. + /// + /// # Errors + /// + /// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed] + fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result; + + /// Stores the weight left into the supplied buffer. + /// + /// Equivalent to the newer [`Self::gas_left_v1`] version but + /// works with *ref_time* Weight only. + fn gas_left(out: &mut &mut [u8]); + + /// Stores the amount of weight left into the supplied buffer. + /// The data is encoded as Weight. + /// + /// If the available space in `output` is less than the size of the value a trap is + /// triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the weight left. + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] + fn gas_left_v1(output: &mut &mut [u8]); + + /// Retrieve the value under the given key from storage. + /// + /// This version is to be used with a fixed sized storage key. For runtimes supporting + /// transparent hashing, please use the newer version of this function. + fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result; + + /// Retrieve the value under the given key from storage. + /// + /// The key length must not exceed the maximum defined by the contracts module + /// parameter. + /// + /// # Parameters + /// - `key`: The storage key. + /// - `output`: A reference to the output data buffer to write the storage entry. + /// + /// # Errors + /// + /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] + fn get_storage_v1(key: &[u8], output: &mut &mut [u8]) -> Result; + + hash_fn!(sha2_256, 32); + hash_fn!(keccak_256, 32); + hash_fn!(blake2_256, 32); + hash_fn!(blake2_128, 16); + + /// Stores the input passed by the caller into the supplied buffer. + /// + /// # Note + /// + /// This function traps if: + /// - the input is larger than the available space. + /// - the input was previously forwarded by a [`call()`][`Self::call()`]. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the input data. + fn input(output: &mut &mut [u8]); + + /// Instantiate a contract with the specified code hash. + /// + /// Equivalent to the newer [`Self::instantiate_v2`] version but works + /// with *ref_time* Weight only. + #[deprecated(note = "Deprecated, use newer version instead")] + fn instantiate_v1( + code_hash: &[u8], + gas: u64, + value: &[u8], + input: &[u8], + address: Option<&mut &mut [u8]>, + output: Option<&mut &mut [u8]>, + salt: &[u8], + ) -> Result; + + /// Instantiate a contract with the specified code hash. + /// + /// This function creates an account and executes the constructor defined in the code + /// specified by the code hash. + /// + /// # Parameters + /// + /// - `code_hash`: The hash of the code to be instantiated. + /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. + /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. + /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a + /// `Option`. Traps otherwise. Passing `None` means setting no specific + /// limit for the call, which implies storage usage up to the limit of the parent + /// call. + /// - `value`: The value to transfer into the contract. Should be decodable as a + /// `T::Balance`. Traps otherwise. + /// - `input`: The input data buffer. + /// - `address`: A reference to the address buffer to write the address of the + /// contract. If `None` is provided then the output buffer is not copied. + /// - `output`: A reference to the return value buffer to write the constructor output + /// buffer. If `None` is provided then the output buffer is not copied. + /// - `salt`: The salt bytes to use for this instantiation. + /// + /// # Errors + /// + /// Please consult the [ReturnErrorCode][`crate::ReturnErrorCode`] enum declaration + /// for more information on those errors. Here we only note things specific to + /// this function. + /// + /// An error means that the account wasn't created and no address or output buffer + /// is returned unless stated otherwise. + /// + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is + /// returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] + fn instantiate_v2( + code_hash: &[u8], + ref_time_limit: u64, + proof_size_limit: u64, + deposit: Option<&[u8]>, + value: &[u8], + input: &[u8], + address: Option<&mut &mut [u8]>, + output: Option<&mut &mut [u8]>, + salt: &[u8], + ) -> Result; + + /// Returns a nonce that is unique per contract instantiation. + /// + /// The nonce is incremented for each successful contract instantiation. This is a + /// sensible default salt for contract instantiations. + fn instantiation_nonce() -> u64; + + /// Checks whether a specified address belongs to a contract. + /// + /// # Parameters + /// + /// - `account_id`: The address to check. Should be decodable as an `T::AccountId`. + /// Traps otherwise. + /// + /// # Return + /// + /// Returns `true` if the address belongs to a contract. + fn is_contract(account_id: &[u8]) -> bool; + + /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. + /// The data is encoded as `T::Balance`. + /// + /// If the available space in `output` is less than the size of the value a trap is + /// triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the minimum balance. + fn minimum_balance(output: &mut &mut [u8]); + + /// Retrieve the code hash of the currently executing contract. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the code hash. + fn own_code_hash(output: &mut [u8]); + + /// Load the latest block timestamp into the supplied buffer + /// + /// If the available space in `output` is less than the size of the value a trap is + /// triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the timestamp. + fn now(output: &mut &mut [u8]); + + /// Returns the number of times the currently executing contract exists on the call + /// stack in addition to the calling instance. + /// + /// # Return + /// + /// Returns `0` when there is no reentrancy. + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] + fn reentrance_count() -> u32; + + /// Removes the delegate dependency from the contract. + /// + /// Traps if the delegate dependency does not exist. + /// + /// # Parameters + /// + /// - `code_hash`: The code hash of the dependency. Should be decodable as an + /// `T::Hash`. Traps otherwise. + fn unlock_delegate_dependency(code_hash: &[u8]); + + /// Cease contract execution and save a data buffer as a result of the execution. + /// + /// This function never returns as it stops execution of the caller. + /// This is the only way to return a data buffer to the caller. Returning from + /// execution without calling this function is equivalent to calling: + /// ```nocompile + /// return_value(ReturnFlags::empty(), &[]) + /// ``` + /// + /// Using an unnamed non empty `ReturnFlags` triggers a trap. + /// + /// # Parameters + /// + /// - `flags`: Flag used to signal special return conditions to the supervisor. See + /// [`ReturnFlags`] for a documentation of the supported flags. + /// - `return_value`: The return value buffer. + fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !; + + /// Replace the contract code at the specified address with new code. + /// + /// # Note + /// + /// There are a couple of important considerations which must be taken into account + /// when using this API: + /// + /// 1. The storage at the code address will remain untouched. This means that contract + /// developers must ensure that the storage layout of the new code is compatible with + /// that of the old code. + /// + /// 2. Contracts using this API can't be assumed as having deterministic addresses. + /// Said another + /// way, when using this API you lose the guarantee that an address always identifies + /// a specific code hash. + /// + /// 3. If a contract calls into itself after changing its code the new call would use + /// the new code. However, if the original caller panics after returning from the sub + /// call it would revert the changes made by + /// [`set_code_hash()`][`Self::set_code_hash`] and the next caller would use the + /// old code. + /// + /// # Parameters + /// + /// - `code_hash`: The hash of the new code. Should be decodable as an `T::Hash`. + /// Traps otherwise. + /// + /// # Errors + /// + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] + fn set_code_hash(code_hash: &[u8]) -> Result; + + /// Set the value at the given key in the contract storage. + /// + /// Equivalent to [`Self::set_storage_v1`] version with the + /// exception of the return type. Still a valid thing to call for fixed sized storage + /// key, when not interested in the return value. + fn set_storage(key: &[u8], value: &[u8]); + + /// Set the value at the given key in the contract storage. + /// + /// This version is to be used with a fixed sized storage key. For runtimes supporting + /// transparent hashing, please use the newer version of this function. + fn set_storage_v1(key: &[u8], value: &[u8]) -> Option; + + /// Set the value at the given key in the contract storage. + /// + /// The key and value lengths must not exceed the maximums defined by the contracts + /// module parameters. + /// + /// # Parameters + /// + /// - `key`: The storage key. + /// - `encoded_value`: The storage value. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn set_storage_v2(key: &[u8], value: &[u8]) -> Option; + + /// Verify a sr25519 signature + /// + /// # Parameters + /// + /// - `signature`: The signature bytes. + /// - `message`: The message bytes. + /// + /// # Errors + /// + /// - [Sr25519VerifyFailed][`crate::ReturnErrorCode::Sr25519VerifyFailed] + fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) + -> Result; + + /// Retrieve and remove the value under the given key from storage. + /// + /// # Parameters + /// - `key`: The storage key. + /// - `output`: A reference to the output data buffer to write the storage entry. + /// + /// # Errors + /// + /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] + fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result; + + /// Transfer some amount of funds into the specified account. + /// + /// # Parameters + /// + /// - `account_id`: The address of the account to transfer funds to. Should be + /// decodable as an `T::AccountId`. Traps otherwise. + /// - `value`: The value to transfer. Should be decodable as a `T::Balance`. Traps + /// otherwise. + /// + /// # Errors + /// + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] + fn transfer(account_id: &[u8], value: &[u8]) -> Result; + + /// Remove the calling account and transfer remaining balance. + /// + /// This is equivalent to calling the newer version of this function + #[deprecated(note = "Deprecated, use newer version instead")] + fn terminate(beneficiary: &[u8]) -> !; + + /// Remove the calling account and transfer remaining **free** balance. + /// + /// This function never returns. Either the termination was successful and the + /// execution of the destroyed contract is halted. Or it failed during the termination + /// which is considered fatal and results in a trap + rollback. + /// + /// # Parameters + /// + /// - `beneficiary`: The address of the beneficiary account, Should be decodable as an + /// `T::AccountId`. + /// + /// # Traps + /// + /// - The contract is live i.e is already on the call stack. + /// - Failed to send the balance to the beneficiary. + /// - The deletion queue is full. + fn terminate_v1(beneficiary: &[u8]) -> !; + + /// Stores the value transferred along with this call/instantiate into the supplied + /// buffer. The data is encoded as `T::Balance`. + /// + /// If the available space in `output` is less than the size of the value a trap is + /// triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the transferred value. + fn value_transferred(output: &mut &mut [u8]); + + /// Stores the price for the specified amount of gas into the supplied buffer. + /// + /// Equivalent to the newer [`Self::weight_to_fee_v1`] version but + /// works with *ref_time* Weight only. It is recommended to switch to the latest + /// version, once it's stabilized. + fn weight_to_fee(gas: u64, output: &mut &mut [u8]); + + /// Stores the price for the specified amount of gas into the supplied buffer. + /// The data is encoded as `T::Balance`. + /// + /// If the available space in `output` is less than the size of the value a trap is + /// triggered. + /// + /// # Parameters + /// + /// - `ref_time_limit`: The *ref_time* Weight limit to query the price for. + /// - `proof_size_limit`: The *proof_size* Weight limit to query the price for. + /// - `output`: A reference to the output data buffer to write the price. + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] + fn weight_to_fee_v1( + ref_time_limit: u64, + proof_size_limit: u64, + output: &mut &mut [u8], + ); + + /// Execute an XCM program locally, using the contract's address as the origin. + /// This is equivalent to dispatching `pallet_xcm::execute` through call_runtime, + /// except that the function is called directly instead of being dispatched. + /// + /// # Parameters + /// + /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), + /// traps otherwise. + /// - `output`: A reference to the output data buffer to write the [Outcome](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v3/enum.Outcome.html) + /// + /// # Return + /// + /// Returns `Error::Success` when the XCM execution attempt is successful. When the + /// XCM execution fails, `ReturnCode::XcmExecutionFailed` is returned + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] + fn xcm_execute(msg: &[u8]) -> Result; + + /// Send an XCM program from the contract to the specified destination. + /// This is equivalent to dispatching `pallet_xcm::send` through `call_runtime`, + /// except that the function is called directly instead of being dispatched. + /// + /// # Parameters + /// + /// - `dest`: The XCM destination, should be decodable as [VersionedLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedLocation.html), + /// traps otherwise. + /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), + /// traps otherwise. + /// + /// # Return + /// + /// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM + /// execution fails, `ReturnErrorCode::XcmSendFailed` is returned. + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] + fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result; + + /// Create a new query, using the contract's address as the responder. + /// + /// # Parameters + /// + /// - `timeout_ptr`: The query timeout, should be decodable as a `BlockNumberFor`. + /// - `match_querier`: The match_querier should be decodable as [VersionedLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedLocation.html), + /// - `output`: A reference to the output data buffer to write the + /// [`xcm_builder::QueryHandler::QueryId`]. + /// + /// # Return Value + /// + /// Returns `ReturnCode::Success` when the query was successfully created. When the + /// query creation fails, `ReturnCode::XcmQueryFailed` is returned. + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] + fn xcm_query(timeout: &[u8], match_querier: &[u8], output: &mut [u8]) -> Result; + + /// Take an XCM response for the specified query. + /// + /// # Parameters + /// + /// - `query_id`: The [`xcm_builder::QueryHandler::QueryId`] + /// - `output`: A reference to the output data buffer to write the + /// [`xcm_builder::QueryResponseStatus`]. + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] + fn xcm_take_response(query_id: &[u8], output: &mut [u8]) -> Result; }