Skip to content
lib.rs 53.1 KiB
Newer Older
				Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Normal),
	#[test]
	fn pallet_parameter_may_be_updated_by_root() {
		run_test(|| {
			get_ready_for_events();

			let parameter = TestMessagesParameter::TokenConversionRate(10.into());
Hernando Castano's avatar
Hernando Castano committed
			assert_ok!(Pallet::<TestRuntime>::update_pallet_parameter(
				Origin::root(),
				parameter.clone(),
			));

			assert_eq!(TokenConversionRate::get(), 10.into());
			assert_eq!(
				System::<TestRuntime>::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
					event: TestEvent::Messages(RawEvent::ParameterUpdated(parameter)),
					topics: vec![],
				}],
			);
		});
	}

	#[test]
	fn pallet_parameter_may_be_updated_by_owner() {
		run_test(|| {
Hernando Castano's avatar
Hernando Castano committed
			PalletOwner::<TestRuntime>::put(2);
			let parameter = TestMessagesParameter::TokenConversionRate(10.into());
Hernando Castano's avatar
Hernando Castano committed
			assert_ok!(Pallet::<TestRuntime>::update_pallet_parameter(
				Origin::signed(2),
				parameter.clone(),
			));

			assert_eq!(TokenConversionRate::get(), 10.into());
			assert_eq!(
				System::<TestRuntime>::events(),
				vec![EventRecord {
					phase: Phase::Initialization,
					event: TestEvent::Messages(RawEvent::ParameterUpdated(parameter)),
					topics: vec![],
				}],
			);
		});
	}

	#[test]
	fn pallet_parameter_cant_be_updated_by_arbitrary_submitter() {
		run_test(|| {
			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::update_pallet_parameter(
					TestMessagesParameter::TokenConversionRate(10.into()),
Hernando Castano's avatar
Hernando Castano committed
			PalletOwner::<TestRuntime>::put(2);
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::update_pallet_parameter(
					TestMessagesParameter::TokenConversionRate(10.into()),
				),
				DispatchError::BadOrigin,
			);
		});
	}

	#[test]
	fn fixed_u128_works_as_i_think() {
		// this test is here just to be sure that conversion rate may be represented with FixedU128
		run_test(|| {
			use sp_runtime::{FixedPointNumber, FixedU128};

			// 1:1 conversion that we use by default for testnets
			let rialto_token = 1u64;
			let rialto_token_in_millau_tokens = TokenConversionRate::get().saturating_mul_int(rialto_token);
			assert_eq!(rialto_token_in_millau_tokens, 1);

			// let's say conversion rate is 1:1.7
			let conversion_rate = FixedU128::saturating_from_rational(170, 100);
			let rialto_tokens = 100u64;
			let rialto_tokens_in_millau_tokens = conversion_rate.saturating_mul_int(rialto_tokens);
			assert_eq!(rialto_tokens_in_millau_tokens, 170);

			// let's say conversion rate is 1:0.25
			let conversion_rate = FixedU128::saturating_from_rational(25, 100);
			let rialto_tokens = 100u64;
			let rialto_tokens_in_millau_tokens = conversion_rate.saturating_mul_int(rialto_tokens);
			assert_eq!(rialto_tokens_in_millau_tokens, 25);
		});
	}

	#[test]
	fn pallet_rejects_transactions_if_halted() {
		run_test(|| {
			// send message first to be able to check that delivery_proof fails later
			send_regular_message();

			PalletOperatingMode::<DefaultInstance>::put(OperatingMode::Halted);
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::send_message(
					Origin::signed(1),
					TEST_LANE_ID,
					REGULAR_PAYLOAD,
					REGULAR_PAYLOAD.1,
				),
				Error::<TestRuntime, DefaultInstance>::Halted,
			);

			assert_noop!(
				Pallet::<TestRuntime>::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 1,),
				Error::<TestRuntime, DefaultInstance>::Halted,
			);

Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::receive_messages_proof(
					Origin::signed(1),
					TEST_RELAYER_A,
					Ok(vec![message(2, REGULAR_PAYLOAD)]).into(),
					REGULAR_PAYLOAD.1,
				),
				Error::<TestRuntime, DefaultInstance>::Halted,
			);

			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::receive_messages_delivery_proof(
					TestMessagesDeliveryProof(Ok((
						TEST_LANE_ID,
						InboundLaneData {
							last_confirmed_nonce: 1,
				),
				Error::<TestRuntime, DefaultInstance>::Halted,
			);
		});
	}

	#[test]
	fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() {
		run_test(|| {
			// send message first to be able to check that delivery_proof fails later
			send_regular_message();

			PalletOperatingMode::<DefaultInstance>::put(OperatingMode::RejectingOutboundMessages);

			assert_noop!(
				Pallet::<TestRuntime>::send_message(
					Origin::signed(1),
					TEST_LANE_ID,
					REGULAR_PAYLOAD,
					REGULAR_PAYLOAD.1,
				),
				Error::<TestRuntime, DefaultInstance>::Halted,
			);

			assert_ok!(Pallet::<TestRuntime>::increase_message_fee(
				Origin::signed(1),
				TEST_LANE_ID,
				1,
				1,
			));

			assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
				Origin::signed(1),
				TEST_RELAYER_A,
				Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
				1,
				REGULAR_PAYLOAD.1,
			),);

			assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
				Origin::signed(1),
				TestMessagesDeliveryProof(Ok((
					TEST_LANE_ID,
					InboundLaneData {
						last_confirmed_nonce: 1,
						..Default::default()
					},
				))),
				Default::default(),
			));
		});
	}

	#[test]
	fn send_message_works() {
		run_test(|| {
			send_regular_message();
		});
	}

	#[test]
	fn chain_verifier_rejects_invalid_message_in_send_message() {
		run_test(|| {
			// messages with this payload are rejected by target chain verifier
			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::send_message(
					Origin::signed(1),
					TEST_LANE_ID,
					PAYLOAD_REJECTED_BY_TARGET_CHAIN,
					PAYLOAD_REJECTED_BY_TARGET_CHAIN.1
				),
				Error::<TestRuntime, DefaultInstance>::MessageRejectedByChainVerifier,
			);
		});
	}

	#[test]
	fn lane_verifier_rejects_invalid_message_in_send_message() {
		run_test(|| {
			// messages with zero fee are rejected by lane verifier
			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 0),
				Error::<TestRuntime, DefaultInstance>::MessageRejectedByLaneVerifier,
			);
		});
	}

	#[test]
	fn message_send_fails_if_submitter_cant_pay_message_fee() {
		run_test(|| {
			TestMessageDeliveryAndDispatchPayment::reject_payments();
			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::send_message(
					Origin::signed(1),
					TEST_LANE_ID,
					REGULAR_PAYLOAD,
					REGULAR_PAYLOAD.1
				),
				Error::<TestRuntime, DefaultInstance>::FailedToWithdrawMessageFee,
			);
		});
	}

	#[test]
	fn receive_messages_proof_works() {
		run_test(|| {
Hernando Castano's avatar
Hernando Castano committed
			assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
				TEST_RELAYER_A,
				Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
			assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 1);
		});
	}

	#[test]
	fn receive_messages_proof_updates_confirmed_message_nonce() {
		run_test(|| {
			// say we have received 10 messages && last confirmed message is 8
			InboundLanes::<TestRuntime, DefaultInstance>::insert(
				TEST_LANE_ID,
				InboundLaneData {
					last_confirmed_nonce: 8,
					relayers: vec![(9, 9, TEST_RELAYER_A), (10, 10, TEST_RELAYER_B)]
						.into_iter()
						.collect(),
				},
			);
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::inbound_unrewarded_relayers_state(TEST_LANE_ID),
				UnrewardedRelayersState {
					unrewarded_relayer_entries: 2,
					messages_in_oldest_entry: 1,

			// message proof includes outbound lane state with latest confirmed message updated to 9
			let mut message_proof: TestMessagesProof = Ok(vec![message(11, REGULAR_PAYLOAD)]).into();
			message_proof.result.as_mut().unwrap()[0].1.lane_state = Some(OutboundLaneData {
				latest_received_nonce: 9,
				..Default::default()
			});

Hernando Castano's avatar
Hernando Castano committed
			assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
				Origin::signed(1),
				TEST_RELAYER_A,
				message_proof,
				InboundLanes::<TestRuntime>::get(TEST_LANE_ID),
				InboundLaneData {
					last_confirmed_nonce: 9,
					relayers: vec![(10, 10, TEST_RELAYER_B), (11, 11, TEST_RELAYER_A)]
						.into_iter()
						.collect(),
				},
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::inbound_unrewarded_relayers_state(TEST_LANE_ID),
				UnrewardedRelayersState {
					unrewarded_relayer_entries: 2,
					messages_in_oldest_entry: 1,
		});
	}

	#[test]
	fn receive_messages_proof_rejects_invalid_dispatch_weight() {
		run_test(|| {
			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::receive_messages_proof(
					TEST_RELAYER_A,
					Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
					REGULAR_PAYLOAD.1 - 1,
				),
				Error::<TestRuntime, DefaultInstance>::InvalidMessagesDispatchWeight,
			);
		});
	}

	#[test]
	fn receive_messages_proof_rejects_invalid_proof() {
		run_test(|| {
			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime, DefaultInstance>::receive_messages_proof(
					Origin::signed(1),
					TEST_RELAYER_A,
					Err(()).into(),
				Error::<TestRuntime, DefaultInstance>::InvalidMessagesProof,
			);
		});
	}

	#[test]
	fn receive_messages_proof_rejects_proof_with_too_many_messages() {
		run_test(|| {
			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime, DefaultInstance>::receive_messages_proof(
					Origin::signed(1),
					TEST_RELAYER_A,
					Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
					u32::MAX,
					0,
				),
				Error::<TestRuntime, DefaultInstance>::TooManyMessagesInTheProof,
			);
		});
	}

	#[test]
	fn receive_messages_delivery_proof_works() {
		run_test(|| {
			send_regular_message();
			receive_messages_delivery_proof();

			assert_eq!(
				OutboundLanes::<DefaultInstance>::get(&TEST_LANE_ID).latest_received_nonce,
				1,
			);
		});
	}

	#[test]
	fn receive_messages_delivery_proof_rewards_relayers() {
		run_test(|| {
Hernando Castano's avatar
Hernando Castano committed
			assert_ok!(Pallet::<TestRuntime>::send_message(
				Origin::signed(1),
				TEST_LANE_ID,
				REGULAR_PAYLOAD,
				1000,
			));
Hernando Castano's avatar
Hernando Castano committed
			assert_ok!(Pallet::<TestRuntime>::send_message(
				Origin::signed(1),
				TEST_LANE_ID,
				REGULAR_PAYLOAD,
				2000,
			));

			// this reports delivery of message 1 => reward is paid to TEST_RELAYER_A
Hernando Castano's avatar
Hernando Castano committed
			assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
				TestMessagesDeliveryProof(Ok((
					TEST_LANE_ID,
					InboundLaneData {
						relayers: vec![(1, 1, TEST_RELAYER_A)].into_iter().collect(),
						..Default::default()
					}
				UnrewardedRelayersState {
					unrewarded_relayer_entries: 1,
					total_messages: 1,
					..Default::default()
				},
			));
			assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(
				TEST_RELAYER_A,
				1000
			));
			assert!(!TestMessageDeliveryAndDispatchPayment::is_reward_paid(
				TEST_RELAYER_B,
				2000
			));

			// this reports delivery of both message 1 and message 2 => reward is paid only to TEST_RELAYER_B
Hernando Castano's avatar
Hernando Castano committed
			assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
				TestMessagesDeliveryProof(Ok((
					TEST_LANE_ID,
					InboundLaneData {
						relayers: vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B)]
							.into_iter()
							.collect(),
						..Default::default()
					}
				UnrewardedRelayersState {
					unrewarded_relayer_entries: 2,
					total_messages: 2,
					..Default::default()
				},
			));
			assert!(!TestMessageDeliveryAndDispatchPayment::is_reward_paid(
				TEST_RELAYER_A,
				1000
			));
			assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(
				TEST_RELAYER_B,
				2000
			));
		});
	}

	#[test]
	fn receive_messages_delivery_proof_rejects_invalid_proof() {
		run_test(|| {
			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::receive_messages_delivery_proof(
					Origin::signed(1),
					TestMessagesDeliveryProof(Err(())),
					Default::default(),
				),
				Error::<TestRuntime, DefaultInstance>::InvalidMessagesDeliveryProof,
			);
		});
	}
	#[test]
	fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_invalid() {
		run_test(|| {
			// when number of relayers entires is invalid
			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::receive_messages_delivery_proof(
					TestMessagesDeliveryProof(Ok((
						TEST_LANE_ID,
						InboundLaneData {
							relayers: vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B)]
								.into_iter()
								.collect(),
							..Default::default()
						}
					UnrewardedRelayersState {
						unrewarded_relayer_entries: 1,
						total_messages: 2,
						..Default::default()
					},
				),
				Error::<TestRuntime, DefaultInstance>::InvalidUnrewardedRelayersState,
			);

			// when number of messages is invalid
			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime>::receive_messages_delivery_proof(
					TestMessagesDeliveryProof(Ok((
						TEST_LANE_ID,
						InboundLaneData {
							relayers: vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B)]
								.into_iter()
								.collect(),
							..Default::default()
						}
					UnrewardedRelayersState {
						unrewarded_relayer_entries: 2,
						total_messages: 1,
						..Default::default()
					},
				),
				Error::<TestRuntime, DefaultInstance>::InvalidUnrewardedRelayersState,
			);
		});
	}

	#[test]
	fn receive_messages_accepts_single_message_with_invalid_payload() {
		run_test(|| {
			let mut invalid_message = message(1, REGULAR_PAYLOAD);
			invalid_message.data.payload = Vec::new();

Hernando Castano's avatar
Hernando Castano committed
			assert_ok!(Pallet::<TestRuntime, DefaultInstance>::receive_messages_proof(
				Origin::signed(1),
				TEST_RELAYER_A,
				Ok(vec![invalid_message]).into(),
				0, // weight may be zero in this case (all messages are improperly encoded)
			),);

			assert_eq!(
				InboundLanes::<TestRuntime>::get(&TEST_LANE_ID).last_delivered_nonce(),
				1,
			);
		});
	}

	#[test]
	fn receive_messages_accepts_batch_with_message_with_invalid_payload() {
		run_test(|| {
			let mut invalid_message = message(2, REGULAR_PAYLOAD);
			invalid_message.data.payload = Vec::new();

Hernando Castano's avatar
Hernando Castano committed
			assert_ok!(Pallet::<TestRuntime, DefaultInstance>::receive_messages_proof(
				Origin::signed(1),
				Ok(vec![
					message(1, REGULAR_PAYLOAD),
					invalid_message,
					message(3, REGULAR_PAYLOAD),
				REGULAR_PAYLOAD.1 + REGULAR_PAYLOAD.1,
			),);

			assert_eq!(
				InboundLanes::<TestRuntime>::get(&TEST_LANE_ID).last_delivered_nonce(),
				3,
			);

	#[test]
	fn storage_message_key_computed_properly() {
		// If this test fails, then something has been changed in module storage that is breaking all
		// previously crafted messages proofs.
		let storage_key = storage_keys::message_key::<TestRuntime, DefaultInstance>(&*b"test", 42).0;
			storage_key,
			hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(),
			"Unexpected storage key: {}",
			hex::encode(&storage_key),
		);
	}

	#[test]
	fn outbound_lane_data_key_computed_properly() {
		// If this test fails, then something has been changed in module storage that is breaking all
		// previously crafted outbound lane state proofs.
		let storage_key = storage_keys::outbound_lane_data_key::<DefaultInstance>(&*b"test").0;
			storage_key,
			hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(),
			"Unexpected storage key: {}",
			hex::encode(&storage_key),
		);
	}

	#[test]
	fn inbound_lane_data_key_computed_properly() {
		// If this test fails, then something has been changed in module storage that is breaking all
		// previously crafted inbound lane state proofs.
		let storage_key = storage_keys::inbound_lane_data_key::<TestRuntime, DefaultInstance>(&*b"test").0;
			storage_key,
			hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(),
			"Unexpected storage key: {}",
			hex::encode(&storage_key),

	#[test]
	fn actual_dispatch_weight_does_not_overlow() {
		run_test(|| {
			let message1 = message(1, TestPayload(0, Weight::MAX / 2));
			let message2 = message(2, TestPayload(0, Weight::MAX / 2));
			let message3 = message(2, TestPayload(0, Weight::MAX / 2));

			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime, DefaultInstance>::receive_messages_proof(
					Origin::signed(1),
					TEST_RELAYER_A,
					// this may cause overflow if source chain storage is invalid
					Ok(vec![message1, message2, message3]).into(),
					3,
					100,
				),
				Error::<TestRuntime, DefaultInstance>::InvalidMessagesDispatchWeight,
			);
		});
	}

	#[test]
	fn increase_message_fee_fails_if_message_is_already_delivered() {
		run_test(|| {
			send_regular_message();
			receive_messages_delivery_proof();

			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime, DefaultInstance>::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 100,),
				Error::<TestRuntime, DefaultInstance>::MessageIsAlreadyDelivered,
			);
		});
	}

	#[test]
	fn increase_message_fee_fails_if_message_is_not_yet_sent() {
		run_test(|| {
			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime, DefaultInstance>::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 100,),
				Error::<TestRuntime, DefaultInstance>::MessageIsNotYetSent,
			);
		});
	}

	#[test]
	fn increase_message_fee_fails_if_submitter_cant_pay_additional_fee() {
		run_test(|| {
			send_regular_message();

			TestMessageDeliveryAndDispatchPayment::reject_payments();

			assert_noop!(
Hernando Castano's avatar
Hernando Castano committed
				Pallet::<TestRuntime, DefaultInstance>::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 100,),
				Error::<TestRuntime, DefaultInstance>::FailedToWithdrawMessageFee,
			);
		});
	}

	#[test]
	fn increase_message_fee_succeeds() {
		run_test(|| {
			send_regular_message();

Hernando Castano's avatar
Hernando Castano committed
			assert_ok!(Pallet::<TestRuntime, DefaultInstance>::increase_message_fee(
				Origin::signed(1),
				TEST_LANE_ID,
				1,
				100,
			),);
			assert!(TestMessageDeliveryAndDispatchPayment::is_fee_paid(1, 100));
		});
	}