Skip to content

Commit

Permalink
Fix up previous code
Browse files Browse the repository at this point in the history
  • Loading branch information
pgherveou committed Feb 27, 2024
1 parent 81d447a commit d956914
Show file tree
Hide file tree
Showing 8 changed files with 323 additions and 5 deletions.
37 changes: 37 additions & 0 deletions substrate/frame/contracts/fixtures/contracts/xcm_query.rs
Original file line number Diff line number Diff line change
@@ -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);
}
38 changes: 38 additions & 0 deletions substrate/frame/contracts/fixtures/contracts/xcm_take_response.rs
Original file line number Diff line number Diff line change
@@ -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);
}
107 changes: 105 additions & 2 deletions substrate/frame/contracts/mock-network/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,29 @@
// 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::{
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_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<parachain::Runtime>;
type QueryId = <pallet_xcm::Pallet<parachain::Runtime> as QueryHandler>::QueryId;

macro_rules! assert_return_code {
( $x:expr , $y:expr $(,)? ) => {{
Expand Down Expand Up @@ -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<parachain::Runtime> = 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 = <ParachainPalletXcm as QueryHandler>::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::<BlockNumberFor<parachain::Runtime>>::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));
})
}
84 changes: 82 additions & 2 deletions substrate/frame/contracts/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2075,7 +2075,7 @@ pub mod env {
) -> Result<ReturnErrorCode, TrapReason> {
let str_len = str_len.min(DebugBufferVec::<E::T>::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);
Expand Down Expand Up @@ -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());
};
Expand All @@ -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<ReturnErrorCode, TrapReason> {
use frame_system::pallet_prelude::BlockNumberFor;
use xcm::VersionedLocation;
use xcm_builder::{QueryController, QueryControllerWeightInfo};

let timeout: BlockNumberFor<E::T> = ctx.read_sandbox_memory_as(memory, timeout_ptr)?;
let match_querier: VersionedLocation =
ctx.read_sandbox_memory_as(memory, match_querier_ptr)?;

let weight = <<E::T as Config>::Xcm as QueryController<_, _>>::WeightInfo::query();
ctx.charge_gas(RuntimeCosts::CallRuntime(weight))?;
let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into();

match <<E::T as Config>::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<ReturnErrorCode, TrapReason> {
use xcm_builder::{QueryController, QueryControllerWeightInfo, QueryHandler};

let query_id: <<E::T as Config>::Xcm as QueryHandler>::QueryId =
ctx.read_sandbox_memory_as(memory, query_id_ptr)?;

let weight = <<E::T as Config>::Xcm as QueryController<_, _>>::WeightInfo::take_response();
ctx.charge_gas(RuntimeCosts::CallRuntime(weight))?;

let response = <<E::T as Config>::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]
Expand Down
32 changes: 31 additions & 1 deletion substrate/frame/contracts/uapi/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<T>`.
/// - `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;
}
8 changes: 8 additions & 0 deletions substrate/frame/contracts/uapi/src/host/riscv32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!()
}
}
Loading

0 comments on commit d956914

Please sign in to comment.