lib.rs 30 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.

//! Runtime module which takes care of dispatching messages received over the bridge.
//!
//! The messages are interpreted directly as runtime `Call`. We attempt to decode
//! them and then dispatch as usual. To prevent compatibility issues, the Calls have
//! to include a `spec_version`. This will be checked before dispatch. In the case of
Denis_P's avatar
Denis_P committed
22
//! a successful dispatch an event is emitted.
23
24
25

#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
26
27
// Generated by `decl_event!`
#![allow(clippy::unused_unit)]
28

29
use bp_message_dispatch::{CallOrigin, MessageDispatch, MessagePayload, SpecVersion, Weight};
30
31
32
33
34
use bp_runtime::{
	derive_account_id,
	messages::{DispatchFeePayment, MessageDispatchResult},
	ChainId, SourceAccount,
};
35
36
37
38
39
use codec::{Decode, Encode};
use frame_support::{
	decl_event, decl_module, decl_storage,
	dispatch::{Dispatchable, Parameter},
	ensure,
40
	traits::{Contains, Get},
41
42
43
44
45
46
47
48
49
50
51
52
53
54
	weights::{extract_actual_weight, GetDispatchInfo},
};
use frame_system::RawOrigin;
use sp_runtime::{
	traits::{BadOrigin, Convert, IdentifyAccount, MaybeDisplay, MaybeSerializeDeserialize, Member, Verify},
	DispatchResult,
};
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};

/// The module configuration trait.
pub trait Config<I = DefaultInstance>: frame_system::Config {
	/// The overarching event type.
	type Event: From<Event<Self, I>> + Into<<Self as frame_system::Config>::Event>;
	/// Id of the message. Whenever message is passed to the dispatch module, it emits
Denis_P's avatar
Denis_P committed
55
	/// event with this id + dispatch result. Could be e.g. (`LaneId`, `MessageNonce`) if
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
	/// it comes from the messages module.
	type MessageId: Parameter;
	/// Type of account ID on source chain.
	type SourceChainAccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + Ord + Default;
	/// Type of account public key on target chain.
	type TargetChainAccountPublic: Parameter + IdentifyAccount<AccountId = Self::AccountId>;
	/// Type of signature that may prove that the message has been signed by
	/// owner of `TargetChainAccountPublic`.
	type TargetChainSignature: Parameter + Verify<Signer = Self::TargetChainAccountPublic>;
	/// The overarching dispatch call type.
	type Call: Parameter
		+ GetDispatchInfo
		+ Dispatchable<
			Origin = <Self as frame_system::Config>::Origin,
			PostInfo = frame_support::dispatch::PostDispatchInfo,
		>;
	/// Pre-dispatch filter for incoming calls.
	///
	/// The pallet will filter all incoming calls right before they're dispatched. If this filter
	/// rejects the call, special event (`Event::MessageCallRejected`) is emitted.
76
	type CallFilter: Contains<<Self as Config<I>>::Call>;
77
78
79
	/// The type that is used to wrap the `Self::Call` when it is moved over bridge.
	///
	/// The idea behind this is to avoid `Call` conversion/decoding until we'll be sure
Denis_P's avatar
Denis_P committed
80
	/// that all other stuff (like `spec_version`) is OK. If we would try to decode
81
82
83
	/// `Call` which has been encoded using previous `spec_version`, then we might end
	/// up with decoding error, instead of `MessageVersionSpecMismatch`.
	type EncodedCall: Decode + Encode + Into<Result<<Self as Config<I>>::Call, ()>>;
Denis_P's avatar
Denis_P committed
84
	/// A type which can be turned into an `AccountId` from a 256-bit hash.
85
	///
Denis_P's avatar
Denis_P committed
86
	/// Used when deriving target chain `AccountId`s from source chain `AccountId`s.
87
88
89
90
91
92
93
94
95
	type AccountIdConverter: sp_runtime::traits::Convert<sp_core::hash::H256, Self::AccountId>;
}

decl_storage! {
	trait Store for Pallet<T: Config<I>, I: Instance = DefaultInstance> as Dispatch {}
}

decl_event!(
	pub enum Event<T, I = DefaultInstance> where
96
97
		<T as Config<I>>::MessageId,
		AccountId = <T as frame_system::Config>::AccountId,
98
99
	{
		/// Message has been rejected before reaching dispatch.
100
		MessageRejected(ChainId, MessageId),
101
102
		/// Message has been rejected by dispatcher because of spec version mismatch.
		/// Last two arguments are: expected and passed spec version.
103
		MessageVersionSpecMismatch(ChainId, MessageId, SpecVersion, SpecVersion),
104
105
		/// Message has been rejected by dispatcher because of weight mismatch.
		/// Last two arguments are: expected and passed call weight.
106
		MessageWeightMismatch(ChainId, MessageId, Weight, Weight),
107
		/// Message signature mismatch.
108
		MessageSignatureMismatch(ChainId, MessageId),
109
		/// We have failed to decode Call from the message.
110
		MessageCallDecodeFailed(ChainId, MessageId),
111
		/// The call from the message has been rejected by the call filter.
112
113
114
115
116
		MessageCallRejected(ChainId, MessageId),
		/// The origin account has failed to pay fee for dispatching the message.
		MessageDispatchPaymentFailed(ChainId, MessageId, AccountId, Weight),
		/// Message has been dispatched with given result.
		MessageDispatched(ChainId, MessageId, DispatchResult),
117
118
119
120
121
122
123
124
125
126
127
128
129
		/// Phantom member, never used. Needed to handle multiple pallet instances.
		_Dummy(PhantomData<I>),
	}
);

decl_module! {
	/// Call Dispatch FRAME Pallet.
	pub struct Module<T: Config<I>, I: Instance = DefaultInstance> for enum Call where origin: T::Origin {
		/// Deposit one of this module's events by using the default implementation.
		fn deposit_event() = default;
	}
}

130
impl<T: Config<I>, I: Instance> MessageDispatch<T::AccountId, T::MessageId> for Pallet<T, I> {
131
132
133
134
135
136
137
	type Message =
		MessagePayload<T::SourceChainAccountId, T::TargetChainAccountPublic, T::TargetChainSignature, T::EncodedCall>;

	fn dispatch_weight(message: &Self::Message) -> Weight {
		message.weight
	}

138
139
140
141
142
143
144
	fn dispatch<P: FnOnce(&T::AccountId, Weight) -> Result<(), ()>>(
		source_chain: ChainId,
		target_chain: ChainId,
		id: T::MessageId,
		message: Result<Self::Message, ()>,
		pay_dispatch_fee: P,
	) -> MessageDispatchResult {
145
146
147
148
		// emit special even if message has been rejected by external component
		let message = match message {
			Ok(message) => message,
			Err(_) => {
149
150
151
152
153
154
155
156
157
158
159
160
				log::trace!(
					target: "runtime::bridge-dispatch",
					"Message {:?}/{:?}: rejected before actual dispatch",
					source_chain,
					id,
				);
				Self::deposit_event(RawEvent::MessageRejected(source_chain, id));
				return MessageDispatchResult {
					dispatch_result: false,
					unspent_weight: 0,
					dispatch_fee_paid_during_dispatch: false,
				};
161
162
163
164
165
			}
		};

		// verify spec version
		// (we want it to be the same, because otherwise we may decode Call improperly)
166
167
168
169
170
		let mut dispatch_result = MessageDispatchResult {
			dispatch_result: false,
			unspent_weight: message.weight,
			dispatch_fee_paid_during_dispatch: false,
		};
171
172
173
174
		let expected_version = <T as frame_system::Config>::Version::get().spec_version;
		if message.spec_version != expected_version {
			log::trace!(
				"Message {:?}/{:?}: spec_version mismatch. Expected {:?}, got {:?}",
175
				source_chain,
176
177
178
179
180
				id,
				expected_version,
				message.spec_version,
			);
			Self::deposit_event(RawEvent::MessageVersionSpecMismatch(
181
				source_chain,
182
183
184
185
				id,
				expected_version,
				message.spec_version,
			));
186
			return dispatch_result;
187
188
189
190
191
192
		}

		// now that we have spec version checked, let's decode the call
		let call = match message.call.into() {
			Ok(call) => call,
			Err(_) => {
193
194
195
196
197
198
199
200
				log::trace!(
					target: "runtime::bridge-dispatch",
					"Failed to decode Call from message {:?}/{:?}",
					source_chain,
					id,
				);
				Self::deposit_event(RawEvent::MessageCallDecodeFailed(source_chain, id));
				return dispatch_result;
201
202
203
204
205
206
			}
		};

		// prepare dispatch origin
		let origin_account = match message.origin {
			CallOrigin::SourceRoot => {
207
				let hex_id = derive_account_id::<T::SourceChainAccountId>(source_chain, SourceAccount::Root);
208
209
210
211
212
				let target_id = T::AccountIdConverter::convert(hex_id);
				log::trace!(target: "runtime::bridge-dispatch", "Root Account: {:?}", &target_id);
				target_id
			}
			CallOrigin::TargetAccount(source_account_id, target_public, target_signature) => {
213
214
215
216
217
218
219
				let digest = account_ownership_digest(
					&call,
					source_account_id,
					message.spec_version,
					source_chain,
					target_chain,
				);
220
221
222
223
224
225

				let target_account = target_public.into_account();
				if !target_signature.verify(&digest[..], &target_account) {
					log::trace!(
						target: "runtime::bridge-dispatch",
						"Message {:?}/{:?}: origin proof is invalid. Expected account: {:?} from signature: {:?}",
226
						source_chain,
227
228
229
230
						id,
						target_account,
						target_signature,
					);
231
232
					Self::deposit_event(RawEvent::MessageSignatureMismatch(source_chain, id));
					return dispatch_result;
233
234
235
236
237
238
				}

				log::trace!(target: "runtime::bridge-dispatch", "Target Account: {:?}", &target_account);
				target_account
			}
			CallOrigin::SourceAccount(source_account_id) => {
239
				let hex_id = derive_account_id(source_chain, SourceAccount::Account(source_account_id));
240
241
242
243
244
245
246
				let target_id = T::AccountIdConverter::convert(hex_id);
				log::trace!(target: "runtime::bridge-dispatch", "Source Account: {:?}", &target_id);
				target_id
			}
		};

		// filter the call
247
		if !T::CallFilter::contains(&call) {
248
249
250
			log::trace!(
				target: "runtime::bridge-dispatch",
				"Message {:?}/{:?}: the call ({:?}) is rejected by filter",
251
				source_chain,
252
253
254
				id,
				call,
			);
255
256
			Self::deposit_event(RawEvent::MessageCallRejected(source_chain, id));
			return dispatch_result;
257
258
259
260
261
262
263
264
265
266
267
		}

		// verify weight
		// (we want passed weight to be at least equal to pre-dispatch weight of the call
		// because otherwise Calls may be dispatched at lower price)
		let dispatch_info = call.get_dispatch_info();
		let expected_weight = dispatch_info.weight;
		if message.weight < expected_weight {
			log::trace!(
				target: "runtime::bridge-dispatch",
				"Message {:?}/{:?}: passed weight is too low. Expected at least {:?}, got {:?}",
268
				source_chain,
269
270
271
272
273
				id,
				expected_weight,
				message.weight,
			);
			Self::deposit_event(RawEvent::MessageWeightMismatch(
274
				source_chain,
275
276
277
278
				id,
				expected_weight,
				message.weight,
			));
279
			return dispatch_result;
280
281
		}

282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
		// pay dispatch fee right before dispatch
		let pay_dispatch_fee_at_target_chain = message.dispatch_fee_payment == DispatchFeePayment::AtTargetChain;
		if pay_dispatch_fee_at_target_chain && pay_dispatch_fee(&origin_account, message.weight).is_err() {
			log::trace!(
				target: "runtime::bridge-dispatch",
				"Failed to pay dispatch fee for dispatching message {:?}/{:?} with weight {}",
				source_chain,
				id,
				message.weight,
			);
			Self::deposit_event(RawEvent::MessageDispatchPaymentFailed(
				source_chain,
				id,
				origin_account,
				message.weight,
			));
			return dispatch_result;
		}
		dispatch_result.dispatch_fee_paid_during_dispatch = pay_dispatch_fee_at_target_chain;

302
303
		// finally dispatch message
		let origin = RawOrigin::Signed(origin_account).into();
304

305
		log::trace!(target: "runtime::bridge-dispatch", "Message being dispatched is: {:.4096?}", &call);
306
307
308
309
		let result = call.dispatch(origin);
		let actual_call_weight = extract_actual_weight(&result, &dispatch_info);
		dispatch_result.dispatch_result = result.is_ok();
		dispatch_result.unspent_weight = message.weight.saturating_sub(actual_call_weight);
310
311
312

		log::trace!(
			target: "runtime::bridge-dispatch",
313
314
			"Message {:?}/{:?} has been dispatched. Weight: {} of {}. Result: {:?}. Call dispatch result: {:?}",
			source_chain,
315
			id,
316
			dispatch_result.unspent_weight,
317
318
			message.weight,
			dispatch_result,
319
			result,
320
321
322
		);

		Self::deposit_event(RawEvent::MessageDispatched(
323
			source_chain,
324
			id,
325
			result.map(drop).map_err(|e| e.error),
326
		));
327
328

		dispatch_result
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
	}
}

/// Check if the message is allowed to be dispatched on the target chain given the sender's origin
/// on the source chain.
///
/// For example, if a message is sent from a "regular" account on the source chain it will not be
/// allowed to be dispatched as Root on the target chain. This is a useful check to do on the source
/// chain _before_ sending a message whose dispatch will be rejected on the target chain.
pub fn verify_message_origin<SourceChainAccountId, TargetChainAccountPublic, TargetChainSignature, Call>(
	sender_origin: &RawOrigin<SourceChainAccountId>,
	message: &MessagePayload<SourceChainAccountId, TargetChainAccountPublic, TargetChainSignature, Call>,
) -> Result<Option<SourceChainAccountId>, BadOrigin>
where
	SourceChainAccountId: PartialEq + Clone,
{
	match message.origin {
		CallOrigin::SourceRoot => {
			ensure!(sender_origin == &RawOrigin::Root, BadOrigin);
			Ok(None)
		}
		CallOrigin::TargetAccount(ref source_account_id, _, _) => {
			ensure!(
				sender_origin == &RawOrigin::Signed(source_account_id.clone()),
				BadOrigin
			);
			Ok(Some(source_account_id.clone()))
		}
		CallOrigin::SourceAccount(ref source_account_id) => {
			ensure!(
359
				sender_origin == &RawOrigin::Signed(source_account_id.clone()) || sender_origin == &RawOrigin::Root,
360
361
362
363
364
365
366
367
368
369
370
371
				BadOrigin
			);
			Ok(Some(source_account_id.clone()))
		}
	}
}

/// Target account ownership digest from the source chain.
///
/// The byte vector returned by this function will be signed with a target chain account
/// private key. This way, the owner of `source_account_id` on the source chain proves that
/// the target chain account private key is also under his control.
372
pub fn account_ownership_digest<Call, AccountId, SpecVersion>(
373
374
375
	call: &Call,
	source_account_id: AccountId,
	target_spec_version: SpecVersion,
376
377
	source_chain_id: ChainId,
	target_chain_id: ChainId,
378
379
380
381
382
383
384
385
386
387
) -> Vec<u8>
where
	Call: Encode,
	AccountId: Encode,
	SpecVersion: Encode,
{
	let mut proof = Vec::new();
	call.encode_to(&mut proof);
	source_account_id.encode_to(&mut proof);
	target_spec_version.encode_to(&mut proof);
388
389
	source_chain_id.encode_to(&mut proof);
	target_chain_id.encode_to(&mut proof);
390
391
392
393
394
395
396
397
398
399

	proof
}

#[cfg(test)]
mod tests {
	// From construct_runtime macro
	#![allow(clippy::from_over_into)]

	use super::*;
Shawn Tabrizi's avatar
Shawn Tabrizi committed
400
	use frame_support::{dispatch::GetDispatchInfo, parameter_types, weights::Weight};
401
402
403
404
405
406
407
408
409
410
411
	use frame_system::{EventRecord, Phase};
	use sp_core::H256;
	use sp_runtime::{
		testing::Header,
		traits::{BlakeTwo256, IdentityLookup},
		Perbill,
	};

	type AccountId = u64;
	type MessageId = [u8; 4];

412
413
414
	const SOURCE_CHAIN_ID: ChainId = *b"srce";
	const TARGET_CHAIN_ID: ChainId = *b"trgt";

415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
	#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq)]
	pub struct TestAccountPublic(AccountId);

	impl IdentifyAccount for TestAccountPublic {
		type AccountId = AccountId;

		fn into_account(self) -> AccountId {
			self.0
		}
	}

	#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq)]
	pub struct TestSignature(AccountId);

	impl Verify for TestSignature {
		type Signer = TestAccountPublic;

		fn verify<L: sp_runtime::traits::Lazy<[u8]>>(&self, _msg: L, signer: &AccountId) -> bool {
			self.0 == *signer
		}
	}

	pub struct AccountIdConverter;

	impl sp_runtime::traits::Convert<H256, AccountId> for AccountIdConverter {
		fn convert(hash: H256) -> AccountId {
			hash.to_low_u64_ne()
		}
	}

	type Block = frame_system::mocking::MockBlock<TestRuntime>;
	type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<TestRuntime>;

	use crate as call_dispatch;

	frame_support::construct_runtime! {
		pub enum TestRuntime where
			Block = Block,
			NodeBlock = Block,
			UncheckedExtrinsic = UncheckedExtrinsic,
		{
			System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
			Dispatch: call_dispatch::{Pallet, Call, Event<T>},
		}
	}

	parameter_types! {
		pub const BlockHashCount: u64 = 250;
		pub const MaximumBlockWeight: Weight = 1024;
		pub const MaximumBlockLength: u32 = 2 * 1024;
		pub const AvailableBlockRatio: Perbill = Perbill::one();
	}

	impl frame_system::Config for TestRuntime {
		type Origin = Origin;
		type Index = u64;
		type Call = Call;
		type BlockNumber = u64;
		type Hash = H256;
		type Hashing = BlakeTwo256;
		type AccountId = AccountId;
		type Lookup = IdentityLookup<Self::AccountId>;
		type Header = Header;
		type Event = Event;
		type BlockHashCount = BlockHashCount;
		type Version = ();
		type PalletInfo = PalletInfo;
		type AccountData = ();
		type OnNewAccount = ();
		type OnKilledAccount = ();
485
		type BaseCallFilter = frame_support::traits::Everything;
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
		type SystemWeightInfo = ();
		type BlockWeights = ();
		type BlockLength = ();
		type DbWeight = ();
		type SS58Prefix = ();
		type OnSetCode = ();
	}

	impl Config for TestRuntime {
		type Event = Event;
		type MessageId = MessageId;
		type SourceChainAccountId = AccountId;
		type TargetChainAccountPublic = TestAccountPublic;
		type TargetChainSignature = TestSignature;
		type Call = Call;
		type CallFilter = TestCallFilter;
		type EncodedCall = EncodedCall;
		type AccountIdConverter = AccountIdConverter;
	}

	#[derive(Decode, Encode)]
	pub struct EncodedCall(Vec<u8>);

	impl From<EncodedCall> for Result<Call, ()> {
		fn from(call: EncodedCall) -> Result<Call, ()> {
			Call::decode(&mut &call.0[..]).map_err(drop)
		}
	}

	pub struct TestCallFilter;

517
518
	impl Contains<Call> for TestCallFilter {
		fn contains(call: &Call) -> bool {
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
			!matches!(*call, Call::System(frame_system::Call::fill_block(_)))
		}
	}

	const TEST_SPEC_VERSION: SpecVersion = 0;
	const TEST_WEIGHT: Weight = 1_000_000_000;

	fn new_test_ext() -> sp_io::TestExternalities {
		let t = frame_system::GenesisConfig::default()
			.build_storage::<TestRuntime>()
			.unwrap();
		sp_io::TestExternalities::new(t)
	}

	fn prepare_message(
		origin: CallOrigin<AccountId, TestAccountPublic, TestSignature>,
		call: Call,
536
	) -> <Pallet<TestRuntime> as MessageDispatch<AccountId, <TestRuntime as Config>::MessageId>>::Message {
537
538
539
540
		MessagePayload {
			spec_version: TEST_SPEC_VERSION,
			weight: TEST_WEIGHT,
			origin,
541
			dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
542
543
544
545
546
547
			call: EncodedCall(call.encode()),
		}
	}

	fn prepare_root_message(
		call: Call,
548
	) -> <Pallet<TestRuntime> as MessageDispatch<AccountId, <TestRuntime as Config>::MessageId>>::Message {
549
550
551
552
553
		prepare_message(CallOrigin::SourceRoot, call)
	}

	fn prepare_target_message(
		call: Call,
554
	) -> <Pallet<TestRuntime> as MessageDispatch<AccountId, <TestRuntime as Config>::MessageId>>::Message {
555
556
557
558
559
560
		let origin = CallOrigin::TargetAccount(1, TestAccountPublic(1), TestSignature(1));
		prepare_message(origin, call)
	}

	fn prepare_source_message(
		call: Call,
561
	) -> <Pallet<TestRuntime> as MessageDispatch<AccountId, <TestRuntime as Config>::MessageId>>::Message {
562
563
564
565
566
567
568
569
570
571
572
573
		let origin = CallOrigin::SourceAccount(1);
		prepare_message(origin, call)
	}

	#[test]
	fn should_fail_on_spec_version_mismatch() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];

			const BAD_SPEC_VERSION: SpecVersion = 99;
			let mut message =
				prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));
574
			let weight = message.weight;
575
576
577
			message.spec_version = BAD_SPEC_VERSION;

			System::set_block_number(1);
578
579
580
			let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
			assert_eq!(result.unspent_weight, weight);
			assert!(!result.dispatch_result);
581
582
583
584
585

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
Keith Yeung's avatar
Keith Yeung committed
586
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageVersionSpecMismatch(
587
						SOURCE_CHAIN_ID,
588
589
590
591
592
593
594
595
596
597
598
599
600
601
						id,
						TEST_SPEC_VERSION,
						BAD_SPEC_VERSION
					)),
					topics: vec![],
				}],
			);
		});
	}

	#[test]
	fn should_fail_on_weight_mismatch() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];
thiolliere's avatar
thiolliere committed
602
603
604
			let call = Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3]));
			let call_weight = call.get_dispatch_info().weight;
			let mut message = prepare_root_message(call);
605
			message.weight = 7;
Shawn Tabrizi's avatar
Shawn Tabrizi committed
606
607
608
609
			assert!(
				call_weight != 7,
				"needed for test to actually trigger a weight mismatch"
			);
610
611

			System::set_block_number(1);
612
613
614
			let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
			assert_eq!(result.unspent_weight, 7);
			assert!(!result.dispatch_result);
615
616
617
618
619

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
Keith Yeung's avatar
Keith Yeung committed
620
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageWeightMismatch(
621
622
						SOURCE_CHAIN_ID,
						id,
thiolliere's avatar
thiolliere committed
623
						call_weight,
624
						7,
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
					)),
					topics: vec![],
				}],
			);
		});
	}

	#[test]
	fn should_fail_on_signature_mismatch() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];

			let call_origin = CallOrigin::TargetAccount(1, TestAccountPublic(1), TestSignature(99));
			let message = prepare_message(
				call_origin,
				Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])),
			);
642
			let weight = message.weight;
643
644

			System::set_block_number(1);
645
646
647
			let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
			assert_eq!(result.unspent_weight, weight);
			assert!(!result.dispatch_result);
648
649
650
651
652

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
Keith Yeung's avatar
Keith Yeung committed
653
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageSignatureMismatch(
654
655
						SOURCE_CHAIN_ID,
						id
656
657
658
659
660
661
662
663
664
665
666
667
668
					)),
					topics: vec![],
				}],
			);
		});
	}

	#[test]
	fn should_emit_event_for_rejected_messages() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];

			System::set_block_number(1);
669
			Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Err(()), |_, _| unreachable!());
670
671
672
673
674

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
675
676
677
678
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageRejected(
						SOURCE_CHAIN_ID,
						id
					)),
679
680
681
682
683
684
685
686
687
688
689
690
691
					topics: vec![],
				}],
			);
		});
	}

	#[test]
	fn should_fail_on_call_decode() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];

			let mut message =
				prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));
692
			let weight = message.weight;
693
694
695
			message.call.0 = vec![];

			System::set_block_number(1);
696
697
698
			let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
			assert_eq!(result.unspent_weight, weight);
			assert!(!result.dispatch_result);
699
700
701
702
703

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
Keith Yeung's avatar
Keith Yeung committed
704
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageCallDecodeFailed(
705
706
						SOURCE_CHAIN_ID,
						id
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
					)),
					topics: vec![],
				}],
			);
		});
	}

	#[test]
	fn should_emit_event_for_rejected_calls() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];

			let call = Call::System(<frame_system::Call<TestRuntime>>::fill_block(Perbill::from_percent(75)));
			let weight = call.get_dispatch_info().weight;
			let mut message = prepare_root_message(call);
			message.weight = weight;

			System::set_block_number(1);
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
			let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
			assert_eq!(result.unspent_weight, weight);
			assert!(!result.dispatch_result);

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageCallRejected(
						SOURCE_CHAIN_ID,
						id
					)),
					topics: vec![],
				}],
			);
		});
	}

	#[test]
	fn should_emit_event_for_unpaid_calls() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];

			let mut message =
				prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));
			let weight = message.weight;
			message.dispatch_fee_payment = DispatchFeePayment::AtTargetChain;

			System::set_block_number(1);
			let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| Err(()));
			assert_eq!(result.unspent_weight, weight);
			assert!(!result.dispatch_result);

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageDispatchPaymentFailed(
						SOURCE_CHAIN_ID,
						id,
						AccountIdConverter::convert(derive_account_id::<AccountId>(
							SOURCE_CHAIN_ID,
							SourceAccount::Root
						)),
						TEST_WEIGHT,
					)),
					topics: vec![],
				}],
			);
		});
	}

	#[test]
	fn should_dispatch_calls_paid_at_target_chain() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];

			let mut message =
				prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));
			message.dispatch_fee_payment = DispatchFeePayment::AtTargetChain;

			System::set_block_number(1);
			let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| Ok(()));
			assert!(result.dispatch_fee_paid_during_dispatch);
			assert!(result.dispatch_result);
790
791
792
793
794

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
795
796
797
798
799
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageDispatched(
						SOURCE_CHAIN_ID,
						id,
						Ok(())
					)),
800
801
802
803
804
805
					topics: vec![],
				}],
			);
		});
	}

806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
	#[test]
	fn should_return_dispatch_failed_flag_if_dispatch_happened_but_failed() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];

			let call = Call::System(<frame_system::Call<TestRuntime>>::set_heap_pages(1));
			let message = prepare_target_message(call);

			System::set_block_number(1);
			let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
			assert!(!result.dispatch_fee_paid_during_dispatch);
			assert!(!result.dispatch_result);

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageDispatched(
						SOURCE_CHAIN_ID,
						id,
						Err(sp_runtime::DispatchError::BadOrigin)
					)),
					topics: vec![],
				}],
			);
		})
	}

834
835
836
837
838
839
840
	#[test]
	fn should_dispatch_bridge_message_from_root_origin() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];
			let message = prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));

			System::set_block_number(1);
841
842
843
			let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
			assert!(!result.dispatch_fee_paid_during_dispatch);
			assert!(result.dispatch_result);
844
845
846
847
848

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
Keith Yeung's avatar
Keith Yeung committed
849
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageDispatched(
850
						SOURCE_CHAIN_ID,
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
						id,
						Ok(())
					)),
					topics: vec![],
				}],
			);
		});
	}

	#[test]
	fn should_dispatch_bridge_message_from_target_origin() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];

			let call = Call::System(<frame_system::Call<TestRuntime>>::remark(vec![]));
			let message = prepare_target_message(call);

			System::set_block_number(1);
869
870
871
			let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
			assert!(!result.dispatch_fee_paid_during_dispatch);
			assert!(result.dispatch_result);
872
873
874
875
876

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
Keith Yeung's avatar
Keith Yeung committed
877
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageDispatched(
878
						SOURCE_CHAIN_ID,
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
						id,
						Ok(())
					)),
					topics: vec![],
				}],
			);
		})
	}

	#[test]
	fn should_dispatch_bridge_message_from_source_origin() {
		new_test_ext().execute_with(|| {
			let id = [0; 4];

			let call = Call::System(<frame_system::Call<TestRuntime>>::remark(vec![]));
			let message = prepare_source_message(call);

			System::set_block_number(1);
897
898
899
			let result = Dispatch::dispatch(SOURCE_CHAIN_ID, TARGET_CHAIN_ID, id, Ok(message), |_, _| unreachable!());
			assert!(!result.dispatch_fee_paid_during_dispatch);
			assert!(result.dispatch_result);
900
901
902
903
904

			assert_eq!(
				System::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
Keith Yeung's avatar
Keith Yeung committed
905
					event: Event::Dispatch(call_dispatch::Event::<TestRuntime>::MessageDispatched(
906
						SOURCE_CHAIN_ID,
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
						id,
						Ok(())
					)),
					topics: vec![],
				}],
			);
		})
	}

	#[test]
	fn origin_is_checked_when_verifying_sending_message_using_source_root_account() {
		let call = Call::System(<frame_system::Call<TestRuntime>>::remark(vec![]));
		let message = prepare_root_message(call);

		// When message is sent by Root, CallOrigin::SourceRoot is allowed
		assert!(matches!(verify_message_origin(&RawOrigin::Root, &message), Ok(None)));

		// when message is sent by some real account, CallOrigin::SourceRoot is not allowed
		assert!(matches!(
			verify_message_origin(&RawOrigin::Signed(1), &message),
			Err(BadOrigin)
		));
	}

	#[test]
	fn origin_is_checked_when_verifying_sending_message_using_target_account() {
		let call = Call::System(<frame_system::Call<TestRuntime>>::remark(vec![]));
		let message = prepare_target_message(call);

		// When message is sent by Root, CallOrigin::TargetAccount is not allowed
		assert!(matches!(
			verify_message_origin(&RawOrigin::Root, &message),
			Err(BadOrigin)
		));

		// When message is sent by some other account, it is rejected
		assert!(matches!(
			verify_message_origin(&RawOrigin::Signed(2), &message),
			Err(BadOrigin)
		));

		// When message is sent by a real account, it is allowed to have origin
		// CallOrigin::TargetAccount
		assert!(matches!(
			verify_message_origin(&RawOrigin::Signed(1), &message),
			Ok(Some(1))
		));
	}

	#[test]
	fn origin_is_checked_when_verifying_sending_message_using_source_account() {
		let call = Call::System(<frame_system::Call<TestRuntime>>::remark(vec![]));
		let message = prepare_source_message(call);

		// Sending a message from the expected origin account works
		assert!(matches!(
			verify_message_origin(&RawOrigin::Signed(1), &message),
			Ok(Some(1))
		));

		// If we send a message from a different account, it is rejected
		assert!(matches!(
			verify_message_origin(&RawOrigin::Signed(2), &message),
			Err(BadOrigin)
		));

973
974
		// The Root account is allowed to assume any expected origin account
		assert!(matches!(verify_message_origin(&RawOrigin::Root, &message), Ok(Some(1))));
975
976
	}
}