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
+138 -44
View File
@@ -14,11 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use crate::mock::*;
use crate::{mock::*, QueryStatus};
use frame_support::{assert_noop, assert_ok, traits::Currency};
use polkadot_parachain::primitives::{AccountIdConversion, Id as ParaId};
use std::convert::TryInto;
use xcm::{v1::prelude::*, VersionedXcm};
use xcm::{latest::prelude::*, VersionedXcm};
use xcm_executor::XcmExecutor;
const ALICE: AccountId = AccountId::new([0u8; 32]);
const BOB: AccountId = AccountId::new([1u8; 32]);
@@ -26,6 +27,111 @@ const PARA_ID: u32 = 2000;
const INITIAL_BALANCE: u128 = 100;
const SEND_AMOUNT: u128 = 10;
#[test]
fn report_outcome_notify_works() {
let balances =
vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)];
let sender = AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into();
let mut message = Xcm(vec![TransferAsset {
assets: (Here, SEND_AMOUNT).into(),
beneficiary: sender.clone(),
}]);
let call = pallet_test_notifier::Call::notification_received(0, Default::default());
let notify = Call::TestNotifier(call);
new_test_ext_with_balances(balances).execute_with(|| {
XcmPallet::report_outcome_notify(&mut message, Parachain(PARA_ID).into(), notify, 100);
assert_eq!(
message,
Xcm(vec![
SetAppendix(Xcm(vec![ReportError {
query_id: 0,
dest: Parent.into(),
max_response_weight: 1_000_000
},])),
TransferAsset { assets: (Here, SEND_AMOUNT).into(), beneficiary: sender.clone() },
])
);
let status = QueryStatus::Pending {
responder: MultiLocation::from(Parachain(PARA_ID)).into(),
maybe_notify: Some((4, 2)),
timeout: 100,
};
assert_eq!(crate::Queries::<Test>::iter().collect::<Vec<_>>(), vec![(0, status)]);
let r = XcmExecutor::<XcmConfig>::execute_xcm(
Parachain(PARA_ID).into(),
Xcm(vec![QueryResponse {
query_id: 0,
response: Response::ExecutionResult(Ok(())),
max_weight: 1_000_000,
}]),
1_000_000_000,
);
assert_eq!(r, Outcome::Complete(1_000));
assert_eq!(
last_events(2),
vec![
Event::TestNotifier(pallet_test_notifier::Event::ResponseReceived(
Parachain(PARA_ID).into(),
0,
Response::ExecutionResult(Ok(())),
)),
Event::XcmPallet(crate::Event::Notified(0, 4, 2)),
]
);
assert_eq!(crate::Queries::<Test>::iter().collect::<Vec<_>>(), vec![]);
});
}
#[test]
fn report_outcome_works() {
let balances =
vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)];
let sender = AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into();
let mut message = Xcm(vec![TransferAsset {
assets: (Here, SEND_AMOUNT).into(),
beneficiary: sender.clone(),
}]);
new_test_ext_with_balances(balances).execute_with(|| {
XcmPallet::report_outcome(&mut message, Parachain(PARA_ID).into(), 100);
assert_eq!(
message,
Xcm(vec![
SetAppendix(Xcm(vec![ReportError {
query_id: 0,
dest: Parent.into(),
max_response_weight: 0
},])),
TransferAsset { assets: (Here, SEND_AMOUNT).into(), beneficiary: sender.clone() },
])
);
let status = QueryStatus::Pending {
responder: MultiLocation::from(Parachain(PARA_ID)).into(),
maybe_notify: None,
timeout: 100,
};
assert_eq!(crate::Queries::<Test>::iter().collect::<Vec<_>>(), vec![(0, status)]);
let r = XcmExecutor::<XcmConfig>::execute_xcm(
Parachain(PARA_ID).into(),
Xcm(vec![QueryResponse {
query_id: 0,
response: Response::ExecutionResult(Ok(())),
max_weight: 0,
}]),
1_000_000_000,
);
assert_eq!(r, Outcome::Complete(1_000));
assert_eq!(
last_event(),
Event::XcmPallet(crate::Event::ResponseReady(0, Response::ExecutionResult(Ok(())),))
);
let response = Some((Response::ExecutionResult(Ok(())), 1));
assert_eq!(XcmPallet::take_response(0), response);
});
}
/// Test sending an `XCM` message (`XCM::ReserveAssetDeposit`)
///
/// Asserts that the expected message is sent and the event is emitted
@@ -34,30 +140,26 @@ fn send_works() {
let balances =
vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)];
new_test_ext_with_balances(balances).execute_with(|| {
let weight = 2 * BaseXcmWeight::get();
let sender: MultiLocation =
AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into();
let message = Xcm::ReserveAssetDeposited {
assets: (Parent, SEND_AMOUNT).into(),
effects: vec![
buy_execution((Parent, SEND_AMOUNT), weight),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() },
],
};
assert_ok!(XcmPallet::send(
Origin::signed(ALICE),
Box::new(RelayLocation::get().into()),
Box::new(VersionedXcm::from(message.clone()))
));
let message = Xcm(vec![
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
ClearOrigin,
buy_execution((Parent, SEND_AMOUNT)),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() },
]);
let versioned_dest = Box::new(RelayLocation::get().into());
let versioned_message = Box::new(VersionedXcm::from(message.clone()));
assert_ok!(XcmPallet::send(Origin::signed(ALICE), versioned_dest, versioned_message));
assert_eq!(
sent_xcm(),
vec![(
Here.into(),
RelayedFrom {
who: sender.clone().try_into().unwrap(),
message: Box::new(message.clone()),
}
)]
Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap()))
.into_iter()
.chain(message.0.clone().into_iter())
.collect())
)],
);
assert_eq!(
last_event(),
@@ -75,16 +177,13 @@ fn send_fails_when_xcm_router_blocks() {
let balances =
vec![(ALICE, INITIAL_BALANCE), (ParaId::from(PARA_ID).into_account(), INITIAL_BALANCE)];
new_test_ext_with_balances(balances).execute_with(|| {
let weight = 2 * BaseXcmWeight::get();
let sender: MultiLocation =
Junction::AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into();
let message = Xcm::ReserveAssetDeposited {
assets: (Parent, SEND_AMOUNT).into(),
effects: vec![
buy_execution((Parent, SEND_AMOUNT), weight),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() },
],
};
let message = Xcm(vec![
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
buy_execution((Parent, SEND_AMOUNT)),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() },
]);
assert_noop!(
XcmPallet::send(
Origin::signed(ALICE),
@@ -113,7 +212,6 @@ fn teleport_assets_works() {
Box::new(AccountId32 { network: Any, id: BOB.into() }.into().into()),
Box::new((Here, SEND_AMOUNT).into()),
0,
weight,
));
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
assert_eq!(
@@ -142,7 +240,6 @@ fn reserve_transfer_assets_works() {
Box::new(dest.clone().into()),
Box::new((Here, SEND_AMOUNT).into()),
0,
weight
));
// Alice spent amount
assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE - SEND_AMOUNT);
@@ -153,13 +250,12 @@ fn reserve_transfer_assets_works() {
sent_xcm(),
vec![(
Parachain(PARA_ID).into(),
Xcm::ReserveAssetDeposited {
assets: (Parent, SEND_AMOUNT).into(),
effects: vec![
buy_execution((Parent, SEND_AMOUNT), weight),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest },
]
}
Xcm(vec![
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
ClearOrigin,
buy_execution((Parent, SEND_AMOUNT)),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest },
]),
)]
);
assert_eq!(
@@ -184,13 +280,11 @@ fn execute_withdraw_to_deposit_works() {
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
assert_ok!(XcmPallet::execute(
Origin::signed(ALICE),
Box::new(VersionedXcm::from(Xcm::WithdrawAsset {
assets: (Here, SEND_AMOUNT).into(),
effects: vec![
buy_execution((Here, SEND_AMOUNT), weight),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest }
],
})),
Box::new(VersionedXcm::from(Xcm(vec![
WithdrawAsset((Here, SEND_AMOUNT).into()),
buy_execution((Here, SEND_AMOUNT)),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest },
]))),
weight
));
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);