XCM v2: Scripting, Query responses, Exception handling and Error reporting (#3629)

* Intoduce XCM v2

Also some minor fix for v0/v1

* Minor version cleanup

* Minor version cleanup

* Introduce SendError for XcmSend trait to avoid cycles with having Outcome in Xcm

* comment

* Corrent type

* Docs

* Fix build

* Fixes

* Introduce the basic impl

* Docs

* Add function

* Basic implementation

* Weighed responses and on_report

* Make XCM more script-like

* Remove BuyExecution::orders

* Fixes

* Fixes

* Fixes

* Formatting

* Initial draft and make pallet-xcm build

* fix XCM tests

* Formatting

* Fixes

* Formatting

* spelling

* Fixes

* Fixes

* spelling

* tests for translation

* extra fields to XCM pallet

* Formatting

* Fixes

* spelling

* first integration test

* Another integration test

* Formatting

* fix tests

* all tests

* Fixes

* Fixes

* Formatting

* Fixes

* Fixes

* Formatting

* Bump

* Remove unneeded structuring

* add instruction

* Fixes

* spelling

* Fixes

* Fixes

* Formatting

* Fixes

* Fixes

* Formatting

* Introduce and use VersionedResponse

* Introduce versioning to dispatchables' params

* Fixes

* Formatting

* Rest of merge

* more work

* Formatting

* Basic logic

* Fixes

* Fixes

* Add test

* Fixes

* Formatting

* Fixes

* Fixes

* Fixes

* Nits

* Simplify

* Spelling

* Formatting

* Return weight of unexecuted instructions in case of error as surplus

* Formatting

* Fixes

* Test for instruction count limiting

* Formatting

* Docs
This commit is contained in:
Gavin Wood
2021-08-26 12:41:16 +02:00
committed by GitHub
parent 4193f05fa3
commit 8b80b283d4
37 changed files with 3070 additions and 966 deletions
@@ -21,46 +21,35 @@ use polkadot_test_client::{
BlockBuilderExt, ClientBlockImportExt, DefaultTestClientBuilderExt, ExecutionStrategy,
InitPolkadotBlockBuilder, TestClientBuilder, TestClientBuilderExt,
};
use polkadot_test_runtime::pallet_test_notifier;
use polkadot_test_service::construct_extrinsic;
use sp_runtime::{generic::BlockId, traits::Block};
use sp_state_machine::InspectState;
use xcm::{latest::prelude::*, VersionedXcm};
use xcm_executor::MAX_RECURSION_LIMIT;
// This is the inflection point where the test should either fail or pass.
const MAX_RECURSION_CHECK: u32 = MAX_RECURSION_LIMIT / 2;
use xcm::{latest::prelude::*, VersionedResponse, VersionedXcm};
#[test]
fn execute_within_recursion_limit() {
fn basic_buy_fees_message_executes() {
sp_tracing::try_init_simple();
let mut client = TestClientBuilder::new()
.set_execution_strategy(ExecutionStrategy::AlwaysWasm)
.build();
let mut msg = WithdrawAsset { assets: (Parent, 100).into(), effects: vec![] };
for _ in 0..MAX_RECURSION_CHECK {
msg = WithdrawAsset {
assets: (Parent, 100).into(),
effects: vec![Order::BuyExecution {
fees: (Parent, 1).into(),
weight: 0,
debt: 0,
halt_on_error: true,
// nest `msg` into itself on each iteration.
instructions: vec![msg],
}],
};
}
let msg = Xcm(vec![
WithdrawAsset((Parent, 100).into()),
BuyExecution { fees: (Parent, 1).into(), weight_limit: Unlimited },
DepositAsset { assets: Wild(All), max_assets: 1, beneficiary: Parent.into() },
]);
let mut block_builder = client.init_polkadot_block_builder();
let execute = construct_extrinsic(
&client,
polkadot_test_runtime::Call::Xcm(pallet_xcm::Call::execute(
Box::new(VersionedXcm::from(msg.clone())),
Box::new(VersionedXcm::from(msg)),
1_000_000_000,
)),
sp_keyring::Sr25519Keyring::Alice,
0,
);
block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
@@ -79,42 +68,65 @@ fn execute_within_recursion_limit() {
r.event,
polkadot_test_runtime::Event::Xcm(pallet_xcm::Event::Attempted(Outcome::Complete(
_
)),),
))),
)));
});
}
#[test]
fn exceed_recursion_limit() {
fn query_response_fires() {
use pallet_test_notifier::Event::*;
use pallet_xcm::QueryStatus;
use polkadot_test_runtime::Event::TestNotifier;
sp_tracing::try_init_simple();
let mut client = TestClientBuilder::new()
.set_execution_strategy(ExecutionStrategy::AlwaysWasm)
.build();
let mut msg = WithdrawAsset { assets: (Parent, 100).into(), effects: vec![] };
for _ in 0..(MAX_RECURSION_CHECK + 1) {
msg = WithdrawAsset {
assets: (Parent, 100).into(),
effects: vec![Order::BuyExecution {
fees: (Parent, 1).into(),
weight: 0,
debt: 0,
halt_on_error: true,
// nest `msg` into itself on each iteration.
instructions: vec![msg],
}],
};
}
let mut block_builder = client.init_polkadot_block_builder();
let execute = construct_extrinsic(
&client,
polkadot_test_runtime::Call::Xcm(pallet_xcm::Call::execute(
Box::new(VersionedXcm::from(msg.clone())),
1_000_000_000,
)),
polkadot_test_runtime::Call::TestNotifier(pallet_test_notifier::Call::prepare_new_query()),
sp_keyring::Sr25519Keyring::Alice,
0,
);
block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
let block = block_builder.build().expect("Finalizes the block").block;
let block_hash = block.hash();
futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block))
.expect("imports the block");
let mut query_id = None;
client
.state_at(&BlockId::Hash(block_hash))
.expect("state should exist")
.inspect_state(|| {
for r in polkadot_test_runtime::System::events().iter() {
match r.event {
TestNotifier(QueryPrepared(q)) => query_id = Some(q),
_ => (),
}
}
});
let query_id = query_id.unwrap();
let mut block_builder = client.init_polkadot_block_builder();
let response = Response::ExecutionResult(Ok(()));
let max_weight = 1_000_000;
let msg = Xcm(vec![QueryResponse { query_id, response, max_weight }]);
let msg = Box::new(VersionedXcm::from(msg));
let execute = construct_extrinsic(
&client,
polkadot_test_runtime::Call::Xcm(pallet_xcm::Call::execute(msg, 1_000_000_000)),
sp_keyring::Sr25519Keyring::Alice,
1,
);
block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
@@ -131,9 +143,99 @@ fn exceed_recursion_limit() {
.inspect_state(|| {
assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
r.event,
polkadot_test_runtime::Event::Xcm(pallet_xcm::Event::Attempted(
Outcome::Incomplete(_, XcmError::RecursionLimitReached),
)),
polkadot_test_runtime::Event::Xcm(pallet_xcm::Event::ResponseReady(
q,
Response::ExecutionResult(Ok(())),
)) if q == query_id,
)));
assert_eq!(
polkadot_test_runtime::Xcm::query(query_id),
Some(QueryStatus::Ready {
response: VersionedResponse::V2(Response::ExecutionResult(Ok(()))),
at: 2u32.into()
}),
)
});
}
#[test]
fn query_response_elicits_handler() {
use pallet_test_notifier::Event::*;
use polkadot_test_runtime::Event::TestNotifier;
sp_tracing::try_init_simple();
let mut client = TestClientBuilder::new()
.set_execution_strategy(ExecutionStrategy::AlwaysWasm)
.build();
let mut block_builder = client.init_polkadot_block_builder();
let execute = construct_extrinsic(
&client,
polkadot_test_runtime::Call::TestNotifier(
pallet_test_notifier::Call::prepare_new_notify_query(),
),
sp_keyring::Sr25519Keyring::Alice,
0,
);
block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
let block = block_builder.build().expect("Finalizes the block").block;
let block_hash = block.hash();
futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block))
.expect("imports the block");
let mut query_id = None;
client
.state_at(&BlockId::Hash(block_hash))
.expect("state should exist")
.inspect_state(|| {
for r in polkadot_test_runtime::System::events().iter() {
match r.event {
TestNotifier(NotifyQueryPrepared(q)) => query_id = Some(q),
_ => (),
}
}
});
let query_id = query_id.unwrap();
let mut block_builder = client.init_polkadot_block_builder();
let response = Response::ExecutionResult(Ok(()));
let max_weight = 1_000_000;
let msg = Xcm(vec![QueryResponse { query_id, response, max_weight }]);
let execute = construct_extrinsic(
&client,
polkadot_test_runtime::Call::Xcm(pallet_xcm::Call::execute(
Box::new(VersionedXcm::from(msg)),
1_000_000_000,
)),
sp_keyring::Sr25519Keyring::Alice,
1,
);
block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
let block = block_builder.build().expect("Finalizes the block").block;
let block_hash = block.hash();
futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block))
.expect("imports the block");
client
.state_at(&BlockId::Hash(block_hash))
.expect("state should exist")
.inspect_state(|| {
assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
r.event,
TestNotifier(ResponseReceived(
MultiLocation { parents: 0, interior: X1(Junction::AccountId32 { .. }) },
q,
Response::ExecutionResult(Ok(())),
)) if q == query_id,
)));
});
}