Skip to content
lib.rs 74.7 KiB
Newer Older
hacpy's avatar
hacpy committed
					100,
				),
				Error::<TestRuntime, ()>::FailedToWithdrawMessageFee,
			);
		});
	}

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

			assert_ok!(Pallet::<TestRuntime, ()>::increase_message_fee(
				Origin::signed(1),
				TEST_LANE_ID,
				1,
				100,
			),);
			assert!(TestMessageDeliveryAndDispatchPayment::is_fee_paid(1, 100));
		});
	}

	#[test]
	fn weight_refund_from_receive_messages_proof_works() {
		run_test(|| {
			fn submit_with_unspent_weight(
				nonce: MessageNonce,
				unspent_weight: Weight,
				is_prepaid: bool,
			) -> (Weight, Weight) {
				let mut payload = REGULAR_PAYLOAD;
				payload.dispatch_result.unspent_weight = unspent_weight;
				payload.dispatch_result.dispatch_fee_paid_during_dispatch = !is_prepaid;
				let proof = Ok(vec![message(nonce, payload)]).into();
				let messages_count = 1;
hacpy's avatar
hacpy committed
				let pre_dispatch_weight =
					<TestRuntime as Config>::WeightInfo::receive_messages_proof_weight(
						&proof,
						messages_count,
						REGULAR_PAYLOAD.declared_weight,
					);
				let post_dispatch_weight = Pallet::<TestRuntime>::receive_messages_proof(
					Origin::signed(1),
					TEST_RELAYER_A,
					proof,
					messages_count,
					REGULAR_PAYLOAD.declared_weight,
				)
				.expect("delivery has failed")
				.actual_weight
				.expect("receive_messages_proof always returns Some");

				(pre_dispatch_weight, post_dispatch_weight)
			}

			// when dispatch is returning `unspent_weight < declared_weight`
			let (pre, post) = submit_with_unspent_weight(1, 1, false);
			assert_eq!(post, pre - 1);

			// when dispatch is returning `unspent_weight = declared_weight`
			let (pre, post) = submit_with_unspent_weight(2, REGULAR_PAYLOAD.declared_weight, false);
			assert_eq!(post, pre - REGULAR_PAYLOAD.declared_weight);

			// when dispatch is returning `unspent_weight > declared_weight`
hacpy's avatar
hacpy committed
			let (pre, post) =
				submit_with_unspent_weight(3, REGULAR_PAYLOAD.declared_weight + 1, false);
			assert_eq!(post, pre - REGULAR_PAYLOAD.declared_weight);

			// when there's no unspent weight
			let (pre, post) = submit_with_unspent_weight(4, 0, false);
			assert_eq!(post, pre);

			// when dispatch is returning `unspent_weight < declared_weight` AND message is prepaid
			let (pre, post) = submit_with_unspent_weight(5, 1, true);
			assert_eq!(
				post,
				pre - 1 - <TestRuntime as Config>::WeightInfo::pay_inbound_dispatch_fee_overhead()
			);
		});
	}

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

			// messages 1+2 are confirmed in 1 tx, message 3 in a separate tx
			// dispatch of message 2 has failed
			let mut delivered_messages_1_and_2 = DeliveredMessages::new(1, true);
			delivered_messages_1_and_2.note_dispatched_message(false);
			let messages_1_and_2_proof = Ok((
				TEST_LANE_ID,
				InboundLaneData {
					last_confirmed_nonce: 0,
					relayers: vec![UnrewardedRelayer {
						relayer: 0,
						messages: delivered_messages_1_and_2.clone(),
					}]
					.into_iter()
					.collect(),
				},
			));
			let delivered_message_3 = DeliveredMessages::new(3, true);
			let messages_3_proof = Ok((
				TEST_LANE_ID,
				InboundLaneData {
					last_confirmed_nonce: 0,
					relayers: vec![UnrewardedRelayer {
						relayer: 0,
						messages: delivered_message_3.clone(),
					}]
					.into_iter()
					.collect(),
				},
			));

			// first tx with messages 1+2
			assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
				Origin::signed(1),
				TestMessagesDeliveryProof(messages_1_and_2_proof),
				UnrewardedRelayersState {
					unrewarded_relayer_entries: 1,
					total_messages: 2,
					..Default::default()
				},
			));
			// second tx with message 3
			assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
				Origin::signed(1),
				TestMessagesDeliveryProof(messages_3_proof),
				UnrewardedRelayersState {
					unrewarded_relayer_entries: 1,
					total_messages: 1,
					..Default::default()
				},
			));

			// ensure that both callbacks have been called twice: for 1+2, then for 3
			TestOnDeliveryConfirmed1::ensure_called(&TEST_LANE_ID, &delivered_messages_1_and_2);
			TestOnDeliveryConfirmed1::ensure_called(&TEST_LANE_ID, &delivered_message_3);
			TestOnDeliveryConfirmed2::ensure_called(&TEST_LANE_ID, &delivered_messages_1_and_2);
			TestOnDeliveryConfirmed2::ensure_called(&TEST_LANE_ID, &delivered_message_3);
		});
	}

	fn confirm_3_messages_delivery() -> (Weight, Weight) {
		send_regular_message();
		send_regular_message();
		send_regular_message();

		let proof = TestMessagesDeliveryProof(Ok((
			TEST_LANE_ID,
			InboundLaneData {
				last_confirmed_nonce: 0,
				relayers: vec![unrewarded_relayer(1, 3, TEST_RELAYER_A)].into_iter().collect(),
			},
		)));
		let relayers_state = UnrewardedRelayersState {
			unrewarded_relayer_entries: 1,
			total_messages: 3,
hacpy's avatar
hacpy committed
		let pre_dispatch_weight =
			<TestRuntime as Config>::WeightInfo::receive_messages_delivery_proof_weight(
				&proof,
				&relayers_state,
				crate::mock::DbWeight::get(),
			);
		let post_dispatch_weight = Pallet::<TestRuntime>::receive_messages_delivery_proof(
			Origin::signed(1),
			proof,
			relayers_state,
		)
		.expect("confirmation has failed")
		.actual_weight
		.expect("receive_messages_delivery_proof always returns Some");
		(pre_dispatch_weight, post_dispatch_weight)
	}

	#[test]
	fn receive_messages_delivery_proof_refunds_zero_weight() {
		run_test(|| {
			let (pre_dispatch_weight, post_dispatch_weight) = confirm_3_messages_delivery();
			assert_eq!(pre_dispatch_weight, post_dispatch_weight);
		});
	}

	#[test]
	fn receive_messages_delivery_proof_refunds_non_zero_weight() {
		run_test(|| {
hacpy's avatar
hacpy committed
			TestOnDeliveryConfirmed1::set_consumed_weight_per_message(
				crate::mock::DbWeight::get().writes(1),
			);

			let (pre_dispatch_weight, post_dispatch_weight) = confirm_3_messages_delivery();
			assert_eq!(
				pre_dispatch_weight.saturating_sub(post_dispatch_weight),
				crate::mock::DbWeight::get().reads(1) * 3
			);
		});
	}

	#[test]
	#[should_panic]
	#[cfg(debug_assertions)]
	fn receive_messages_panics_in_debug_mode_if_callback_is_wrong() {
		run_test(|| {
hacpy's avatar
hacpy committed
			TestOnDeliveryConfirmed1::set_consumed_weight_per_message(
				crate::mock::DbWeight::get().reads_writes(2, 2),
			);
hacpy's avatar
hacpy committed
	fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messages_than_expected(
	) {
		run_test(|| {
			// send message first to be able to check that delivery_proof fails later
			send_regular_message();

			// 1) InboundLaneData declares that the `last_confirmed_nonce` is 1;
			// 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()`
			//    returns `last_confirmed_nonce`;
			// 3) it means that we're going to confirm delivery of messages 1..=1;
			// 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and
			//    numer of actually confirmed messages is `1`.
			assert_noop!(
				Pallet::<TestRuntime>::receive_messages_delivery_proof(
					Origin::signed(1),
					TestMessagesDeliveryProof(Ok((
						TEST_LANE_ID,
hacpy's avatar
hacpy committed
						InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() },
					UnrewardedRelayersState { last_delivered_nonce: 1, ..Default::default() },
				Error::<TestRuntime, ()>::TryingToConfirmMoreMessagesThanExpected,
	#[test]
	fn increase_message_fee_weight_depends_on_message_size() {
		run_test(|| {
			let mut small_payload = message_payload(0, 100);
			let mut large_payload = message_payload(1, 100);
			small_payload.extra = vec![1; 100];
			large_payload.extra = vec![2; 16_384];

			assert_ok!(Pallet::<TestRuntime>::send_message(
				Origin::signed(1),
				TEST_LANE_ID,
				small_payload,
				100,
			));
			assert_ok!(Pallet::<TestRuntime>::send_message(
				Origin::signed(1),
				TEST_LANE_ID,
				large_payload,
				100,
			));

hacpy's avatar
hacpy committed
			let small_weight =
				Pallet::<TestRuntime>::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 1)
					.expect("increase_message_fee has failed")
					.actual_weight
					.expect("increase_message_fee always returns Some");
hacpy's avatar
hacpy committed
			let large_weight =
				Pallet::<TestRuntime>::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 2, 1)
					.expect("increase_message_fee has failed")
					.actual_weight
					.expect("increase_message_fee always returns Some");

			assert!(
				large_weight > small_weight,
				"Actual post-dispatch weigth for larger message {} must be larger than {} for small message",
				large_weight,
				small_weight,
			);
		});
	}

	#[test]
	fn weight_is_refunded_for_messages_that_are_not_pruned() {
		run_test(|| {
			// send first MAX messages - no messages are pruned
			let max_messages_to_prune = crate::mock::MaxMessagesToPruneAtOnce::get();
			let when_zero_messages_are_pruned = send_regular_message();
			let mut delivered_messages = DeliveredMessages::new(1, true);
			for _ in 1..max_messages_to_prune {
				assert_eq!(send_regular_message(), when_zero_messages_are_pruned);
				delivered_messages.note_dispatched_message(true);
			}

			// confirm delivery of all sent messages
			assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
				Origin::signed(1),
				TestMessagesDeliveryProof(Ok((
					TEST_LANE_ID,
					InboundLaneData {
						last_confirmed_nonce: 1,
						relayers: vec![UnrewardedRelayer {
							relayer: 0,
							messages: delivered_messages,
						}]
						.into_iter()
						.collect(),
					},
				))),
				UnrewardedRelayersState {
					unrewarded_relayer_entries: 1,
					total_messages: max_messages_to_prune,
					last_delivered_nonce: max_messages_to_prune,
					..Default::default()
				},
			));

			// when next message is sent, MAX messages are pruned
			let weight_when_max_messages_are_pruned = send_regular_message();
			assert_eq!(
				weight_when_max_messages_are_pruned,
hacpy's avatar
hacpy committed
				when_zero_messages_are_pruned +
					crate::mock::DbWeight::get().writes(max_messages_to_prune),

	#[test]
	fn message_accepted_callbacks_are_called() {
		run_test(|| {
			send_regular_message();
			TestOnMessageAccepted::ensure_called(&TEST_LANE_ID, &1);
		});
	}

	#[test]
	#[should_panic]
	#[cfg(debug_assertions)]
	fn message_accepted_panics_in_debug_mode_if_callback_is_wrong() {
		run_test(|| {
hacpy's avatar
hacpy committed
			TestOnMessageAccepted::set_consumed_weight_per_message(
				crate::mock::DbWeight::get().reads_writes(2, 2),
			);
			send_regular_message();
		});
	}

	#[test]
	fn message_accepted_refunds_non_zero_weight() {
		run_test(|| {
hacpy's avatar
hacpy committed
			TestOnMessageAccepted::set_consumed_weight_per_message(
				crate::mock::DbWeight::get().writes(1),
			);
			let actual_callback_weight = send_regular_message();
			let pre_dispatch_weight = <TestRuntime as Config>::WeightInfo::send_message_weight(
				&REGULAR_PAYLOAD,
				crate::mock::DbWeight::get(),
			);
hacpy's avatar
hacpy committed
			let prune_weight = crate::mock::DbWeight::get()
				.writes(<TestRuntime as Config>::MaxMessagesToPruneAtOnce::get());

			assert_eq!(
				pre_dispatch_weight.saturating_sub(actual_callback_weight),
				crate::mock::DbWeight::get().reads(1).saturating_add(prune_weight)
			);
		});
	}

	#[test]
	fn storage_keys_computed_properly() {
		assert_eq!(
			PalletOperatingMode::<TestRuntime>::storage_value_final_key().to_vec(),
			bp_messages::storage_keys::operating_mode_key("Messages").0,
		);

		assert_eq!(
			OutboundMessages::<TestRuntime>::storage_map_final_key(MessageKey {
				lane_id: TEST_LANE_ID,
				nonce: 42
			}),
			bp_messages::storage_keys::message_key("Messages", &TEST_LANE_ID, 42).0,
		);

		assert_eq!(
			OutboundLanes::<TestRuntime>::storage_map_final_key(TEST_LANE_ID),
			bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0,
		);

		assert_eq!(
			InboundLanes::<TestRuntime>::storage_map_final_key(TEST_LANE_ID),
			bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0,
		);
	}

	#[test]
	fn inbound_message_details_works() {
		run_test(|| {
			assert_eq!(
				Pallet::<TestRuntime>::inbound_message_data(
					TEST_LANE_ID,
					REGULAR_PAYLOAD.encode(),
					OutboundMessageDetails {
						nonce: 0,
						dispatch_weight: 0,
						size: 0,
						delivery_and_dispatch_fee: 0,
						dispatch_fee_payment:
							bp_runtime::messages::DispatchFeePayment::AtTargetChain,
					},
				),
				InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight },
			);
		});
	}