Newer
Older
/// Creates new inbound lane object, backed by runtime storage.
fn inbound_lane<T: Config<I>, I: 'static>(
lane_id: LaneId,
) -> InboundLane<RuntimeInboundLaneStorage<T, I>> {
InboundLane::new(inbound_lane_storage::<T, I>(lane_id))
}
/// Creates new runtime inbound lane storage.
fn inbound_lane_storage<T: Config<I>, I: 'static>(
lane_id: LaneId,
) -> RuntimeInboundLaneStorage<T, I> {
RuntimeInboundLaneStorage {
cached_data: RefCell::new(None),
_phantom: Default::default(),
}
/// Creates new outbound lane object, backed by runtime storage.
fn outbound_lane<T: Config<I>, I: 'static>(
lane_id: LaneId,
) -> OutboundLane<RuntimeOutboundLaneStorage<T, I>> {
OutboundLane::new(RuntimeOutboundLaneStorage { lane_id, _phantom: Default::default() })
}
/// Runtime inbound lane storage.
struct RuntimeInboundLaneStorage<T: Config<I>, I: 'static = ()> {
cached_data: RefCell<Option<InboundLaneData<T::InboundRelayer>>>,
_phantom: PhantomData<I>,
impl<T: Config<I>, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage<T, I> {
type MessageFee = T::InboundMessageFee;
type Relayer = T::InboundRelayer;
fn id(&self) -> LaneId {
self.lane_id
}
fn max_unrewarded_relayer_entries(&self) -> MessageNonce {
T::MaxUnrewardedRelayerEntriesAtInboundLane::get()
}
fn max_unconfirmed_messages(&self) -> MessageNonce {
T::MaxUnconfirmedMessagesAtInboundLane::get()
}
fn data(&self) -> InboundLaneData<T::InboundRelayer> {
match self.cached_data.clone().into_inner() {
Some(data) => data,
None => {
let data = InboundLanes::<T, I>::get(&self.lane_id);
*self.cached_data.try_borrow_mut().expect(
"we're in the single-threaded environment;\
we have no recursive borrows; qed",
) = Some(data.clone());
data
fn set_data(&mut self, data: InboundLaneData<T::InboundRelayer>) {
*self.cached_data.try_borrow_mut().expect(
"we're in the single-threaded environment;\
we have no recursive borrows; qed",
) = Some(data.clone());
InboundLanes::<T, I>::insert(&self.lane_id, data)
}
}
/// Runtime outbound lane storage.
struct RuntimeOutboundLaneStorage<T, I = ()> {
lane_id: LaneId,
_phantom: PhantomData<(T, I)>,
}
impl<T: Config<I>, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage<T, I> {
type MessageFee = T::OutboundMessageFee;
fn id(&self) -> LaneId {
self.lane_id
}
fn data(&self) -> OutboundLaneData {
OutboundLanes::<T, I>::get(&self.lane_id)
}
fn set_data(&mut self, data: OutboundLaneData) {
OutboundLanes::<T, I>::insert(&self.lane_id, data)
}
#[cfg(test)]
fn message(&self, nonce: &MessageNonce) -> Option<MessageData<T::OutboundMessageFee>> {
OutboundMessages::<T, I>::get(MessageKey { lane_id: self.lane_id, nonce: *nonce })
fn save_message(
&mut self,
nonce: MessageNonce,
mesage_data: MessageData<T::OutboundMessageFee>,
) {
OutboundMessages::<T, I>::insert(MessageKey { lane_id: self.lane_id, nonce }, mesage_data);
}
fn remove_message(&mut self, nonce: &MessageNonce) {
OutboundMessages::<T, I>::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce });
/// Verify messages proof and return proved messages with decoded payload.
fn verify_and_decode_messages_proof<Chain: SourceHeaderChain<Fee>, Fee, DispatchPayload: Decode>(
proof: Chain::MessagesProof,
) -> Result<ProvedMessages<DispatchMessage<DispatchPayload, Fee>>, Chain::Error> {
// `receive_messages_proof` weight formula and `MaxUnconfirmedMessagesAtInboundLane` check
// guarantees that the `message_count` is sane and Vec<Message> may be allocated.
// (tx with too many messages will either be rejected from the pool, or will fail earlier)
Chain::verify_messages_proof(proof, messages_count).map(|messages_by_lane| {
messages_by_lane
.into_iter()
.map(|(lane, lane_data)| {
(
lane,
ProvedLaneMessages {
lane_state: lane_data.lane_state,
messages: lane_data.messages.into_iter().map(Into::into).collect(),
},
)
})
.collect()
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{
Svyatoslav Nikolsky
committed
message, message_payload, run_test, unrewarded_relayer, Event as TestEvent, Origin,
TestMessageDeliveryAndDispatchPayment, TestMessagesDeliveryProof, TestMessagesParameter,
TestMessagesProof, TestOnDeliveryConfirmed1, TestOnDeliveryConfirmed2,
TestOnMessageAccepted, TestRuntime, TokenConversionRate, PAYLOAD_REJECTED_BY_TARGET_CHAIN,
REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B,
Svyatoslav Nikolsky
committed
use bp_messages::{UnrewardedRelayer, UnrewardedRelayersState};
use frame_support::{
assert_noop, assert_ok,
storage::generator::{StorageMap, StorageValue},
weights::Weight,
};
use frame_system::{EventRecord, Pallet as System, Phase};
use sp_runtime::DispatchError;
fn get_ready_for_events() {
System::<TestRuntime>::set_block_number(1);
System::<TestRuntime>::reset_events();
}
fn inbound_unrewarded_relayers_state(
lane: bp_messages::LaneId,
) -> bp_messages::UnrewardedRelayersState {
let relayers = InboundLanes::<TestRuntime, ()>::get(&lane).relayers;
bp_messages::UnrewardedRelayersState {
unrewarded_relayer_entries: relayers.len() as _,
messages_in_oldest_entry: relayers
.front()
.map(|entry| 1 + entry.messages.end - entry.messages.begin)
.unwrap_or(0),
total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX),
}
}
fn send_regular_message() -> Weight {
get_ready_for_events();
let message_nonce =
outbound_lane::<TestRuntime, ()>(TEST_LANE_ID).data().latest_generated_nonce + 1;
let weight = Pallet::<TestRuntime>::send_message(
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.declared_weight,
)
.expect("send_message has failed")
.actual_weight
.expect("send_message always returns Some");
// check event with assigned nonce
assert_eq!(
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Messages(Event::MessageAccepted(TEST_LANE_ID, message_nonce)),
topics: vec![],
}],
);
// check that fee has been withdrawn from submitter
assert!(TestMessageDeliveryAndDispatchPayment::is_fee_paid(
1,
REGULAR_PAYLOAD.declared_weight
));
}
fn receive_messages_delivery_proof() {
System::<TestRuntime>::set_block_number(1);
System::<TestRuntime>::reset_events();
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
Origin::signed(1),
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
last_confirmed_nonce: 1,
Svyatoslav Nikolsky
committed
relayers: vec![UnrewardedRelayer {
relayer: 0,
messages: DeliveredMessages::new(1, true),
}]
.into_iter()
.collect(),
Svyatoslav Nikolsky
committed
},
Svyatoslav Nikolsky
committed
UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 1,
..Default::default()
},
));
assert_eq!(
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Messages(Event::MessagesDelivered(
TEST_LANE_ID,
DeliveredMessages::new(1, true),
)),
topics: vec![],
}],
);
}
#[test]
fn pallet_owner_may_change_owner() {
run_test(|| {
assert_ok!(Pallet::<TestRuntime>::set_owner(Origin::root(), Some(1)));
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(2), OperatingMode::Halted),
DispatchError::BadOrigin,
);
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::root(),
OperatingMode::Halted
));
assert_ok!(Pallet::<TestRuntime>::set_owner(Origin::signed(1), None));
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Normal),
DispatchError::BadOrigin,
);
assert_noop!(
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(2), OperatingMode::Normal),
DispatchError::BadOrigin,
);
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::root(),
OperatingMode::Normal
));
});
}
#[test]
fn pallet_may_be_halted_by_root() {
run_test(|| {
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::root(),
OperatingMode::Halted
));
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::root(),
OperatingMode::Normal
));
});
}
#[test]
fn pallet_may_be_halted_by_owner() {
run_test(|| {
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::signed(2),
OperatingMode::Halted
));
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::signed(2),
OperatingMode::Normal
));
assert_noop!(
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Halted),
DispatchError::BadOrigin,
);
assert_noop!(
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Normal),
DispatchError::BadOrigin,
);
assert_ok!(Pallet::<TestRuntime>::set_operating_mode(
Origin::signed(2),
OperatingMode::Halted
));
Pallet::<TestRuntime>::set_operating_mode(Origin::signed(1), OperatingMode::Normal),
DispatchError::BadOrigin,
);
});
}
#[test]
fn pallet_parameter_may_be_updated_by_root() {
run_test(|| {
get_ready_for_events();
let parameter = TestMessagesParameter::TokenConversionRate(10.into());
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(Event::ParameterUpdated(parameter)),
topics: vec![],
}],
);
});
}
#[test]
fn pallet_parameter_may_be_updated_by_owner() {
run_test(|| {
get_ready_for_events();
let parameter = TestMessagesParameter::TokenConversionRate(10.into());
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(Event::ParameterUpdated(parameter)),
topics: vec![],
}],
);
});
}
#[test]
fn pallet_parameter_cant_be_updated_by_arbitrary_submitter() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime>::update_pallet_parameter(
Origin::signed(2),
TestMessagesParameter::TokenConversionRate(10.into()),
),
DispatchError::BadOrigin,
);
assert_noop!(
Pallet::<TestRuntime>::update_pallet_parameter(
Origin::signed(1),
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::<TestRuntime, ()>::put(OperatingMode::Halted);
assert_noop!(
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.declared_weight,
Error::<TestRuntime, ()>::Halted,
assert_noop!(
Pallet::<TestRuntime>::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 1,),
Error::<TestRuntime, ()>::Halted,
Pallet::<TestRuntime>::receive_messages_proof(
Origin::signed(1),
TEST_RELAYER_A,
Ok(vec![message(2, REGULAR_PAYLOAD)]).into(),
REGULAR_PAYLOAD.declared_weight,
Error::<TestRuntime, ()>::Halted,
);
assert_noop!(
Pallet::<TestRuntime>::receive_messages_delivery_proof(
Origin::signed(1),
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
last_confirmed_nonce: 1,
relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)]
.into_iter()
.collect(),
Svyatoslav Nikolsky
committed
},
UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
messages_in_oldest_entry: 1,
total_messages: 1,
},
Error::<TestRuntime, ()>::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::<TestRuntime, ()>::put(OperatingMode::RejectingOutboundMessages);
assert_noop!(
Pallet::<TestRuntime>::send_message(
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.declared_weight,
Error::<TestRuntime, ()>::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.declared_weight,
),);
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
Origin::signed(1),
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
last_confirmed_nonce: 1,
relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)]
.into_iter()
.collect(),
UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
messages_in_oldest_entry: 1,
total_messages: 1,
},
));
});
}
#[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!(
Origin::signed(1),
TEST_LANE_ID,
PAYLOAD_REJECTED_BY_TARGET_CHAIN,
PAYLOAD_REJECTED_BY_TARGET_CHAIN.declared_weight
Error::<TestRuntime, ()>::MessageRejectedByChainVerifier,
);
});
}
#[test]
fn lane_verifier_rejects_invalid_message_in_send_message() {
run_test(|| {
// messages with zero fee are rejected by lane verifier
assert_noop!(
Pallet::<TestRuntime>::send_message(
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
0
),
Error::<TestRuntime, ()>::MessageRejectedByLaneVerifier,
);
});
}
#[test]
fn message_send_fails_if_submitter_cant_pay_message_fee() {
run_test(|| {
TestMessageDeliveryAndDispatchPayment::reject_payments();
assert_noop!(
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.declared_weight
Error::<TestRuntime, ()>::FailedToWithdrawMessageFee,
);
});
}
#[test]
fn receive_messages_proof_works() {
run_test(|| {
assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
Origin::signed(1),
TEST_RELAYER_A,
Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
REGULAR_PAYLOAD.declared_weight,
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, ()>::insert(
TEST_LANE_ID,
InboundLaneData {
last_confirmed_nonce: 8,
Svyatoslav Nikolsky
committed
relayers: vec![
unrewarded_relayer(9, 9, TEST_RELAYER_A),
unrewarded_relayer(10, 10, TEST_RELAYER_B),
]
.into_iter()
.collect(),
assert_eq!(
inbound_unrewarded_relayers_state(TEST_LANE_ID),
UnrewardedRelayersState {
unrewarded_relayer_entries: 2,
messages_in_oldest_entry: 1,
Svyatoslav Nikolsky
committed
total_messages: 2,
},
);
// 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() });
assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
Origin::signed(1),
TEST_RELAYER_A,
message_proof,
REGULAR_PAYLOAD.declared_weight,
));
assert_eq!(
InboundLanes::<TestRuntime>::get(TEST_LANE_ID),
InboundLaneData {
last_confirmed_nonce: 9,
Svyatoslav Nikolsky
committed
relayers: vec![
unrewarded_relayer(10, 10, TEST_RELAYER_B),
unrewarded_relayer(11, 11, TEST_RELAYER_A)
]
.into_iter()
.collect(),
assert_eq!(
inbound_unrewarded_relayers_state(TEST_LANE_ID),
UnrewardedRelayersState {
unrewarded_relayer_entries: 2,
messages_in_oldest_entry: 1,
Svyatoslav Nikolsky
committed
total_messages: 2,
},
);
});
}
#[test]
fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enough() {
run_test(|| {
assert_ok!(Pallet::<TestRuntime>::receive_messages_proof(
Origin::signed(1),
TEST_RELAYER_A,
Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
1,
REGULAR_PAYLOAD.declared_weight - 1,
));
assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 0);
});
}
#[test]
fn receive_messages_proof_rejects_invalid_proof() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime, ()>::receive_messages_proof(
Origin::signed(1),
TEST_RELAYER_A,
Err(()).into(),
Error::<TestRuntime, ()>::InvalidMessagesProof,
);
});
}
#[test]
fn receive_messages_proof_rejects_proof_with_too_many_messages() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime, ()>::receive_messages_proof(
Origin::signed(1),
TEST_RELAYER_A,
Ok(vec![message(1, REGULAR_PAYLOAD)]).into(),
u32::MAX,
0,
),
Error::<TestRuntime, ()>::TooManyMessagesInTheProof,
#[test]
fn receive_messages_delivery_proof_works() {
run_test(|| {
send_regular_message();
receive_messages_delivery_proof();
assert_eq!(
OutboundLanes::<TestRuntime, ()>::get(&TEST_LANE_ID).latest_received_nonce,
1,
);
});
}
#[test]
fn receive_messages_delivery_proof_rewards_relayers() {
run_test(|| {
assert_ok!(Pallet::<TestRuntime>::send_message(
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
1000,
));
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
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
Origin::signed(1),
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)]
.into_iter()
.collect(),
..Default::default()
}
Svyatoslav Nikolsky
committed
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
assert_ok!(Pallet::<TestRuntime>::receive_messages_delivery_proof(
Origin::signed(1),
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
Svyatoslav Nikolsky
committed
relayers: vec![
unrewarded_relayer(1, 1, TEST_RELAYER_A),
unrewarded_relayer(2, 2, TEST_RELAYER_B)
]
.into_iter()
.collect(),
..Default::default()
}
Svyatoslav Nikolsky
committed
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!(
Pallet::<TestRuntime>::receive_messages_delivery_proof(
Origin::signed(1),
TestMessagesDeliveryProof(Err(())),
Default::default(),
),
Error::<TestRuntime, ()>::InvalidMessagesDeliveryProof,
);
});
}
Svyatoslav Nikolsky
committed
#[test]
fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_invalid() {
run_test(|| {
// when number of relayers entries is invalid
Svyatoslav Nikolsky
committed
assert_noop!(
Pallet::<TestRuntime>::receive_messages_delivery_proof(
Svyatoslav Nikolsky
committed
Origin::signed(1),
TestMessagesDeliveryProof(Ok((
Svyatoslav Nikolsky
committed
TEST_LANE_ID,
InboundLaneData {
Svyatoslav Nikolsky
committed
relayers: vec![
unrewarded_relayer(1, 1, TEST_RELAYER_A),
unrewarded_relayer(2, 2, TEST_RELAYER_B)
]
.into_iter()
.collect(),
Svyatoslav Nikolsky
committed
..Default::default()
}
Svyatoslav Nikolsky
committed
UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 2,
..Default::default()
},
),
Error::<TestRuntime, ()>::InvalidUnrewardedRelayersState,
Svyatoslav Nikolsky
committed
);
// when number of messages is invalid
assert_noop!(
Pallet::<TestRuntime>::receive_messages_delivery_proof(
Svyatoslav Nikolsky
committed
Origin::signed(1),
TestMessagesDeliveryProof(Ok((
Svyatoslav Nikolsky
committed
TEST_LANE_ID,
InboundLaneData {
Svyatoslav Nikolsky
committed
relayers: vec![
unrewarded_relayer(1, 1, TEST_RELAYER_A),
unrewarded_relayer(2, 2, TEST_RELAYER_B)
]
.into_iter()
.collect(),
Svyatoslav Nikolsky
committed
..Default::default()
}
Svyatoslav Nikolsky
committed
UnrewardedRelayersState {
unrewarded_relayer_entries: 2,
total_messages: 1,
..Default::default()
},
),
Error::<TestRuntime, ()>::InvalidUnrewardedRelayersState,
Svyatoslav Nikolsky
committed
);
});
}
#[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();
assert_ok!(Pallet::<TestRuntime, ()>::receive_messages_proof(
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();
assert_ok!(Pallet::<TestRuntime, ()>::receive_messages_proof(
Ok(
vec![message(1, REGULAR_PAYLOAD), invalid_message, message(3, REGULAR_PAYLOAD),]
)
REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight,
assert_eq!(InboundLanes::<TestRuntime>::get(&TEST_LANE_ID).last_delivered_nonce(), 3,);
#[test]
fn actual_dispatch_weight_does_not_overlow() {
run_test(|| {
let message1 = message(1, message_payload(0, Weight::MAX / 2));
let message2 = message(2, message_payload(0, Weight::MAX / 2));
let message3 = message(3, message_payload(0, Weight::MAX / 2));
assert_ok!(Pallet::<TestRuntime, ()>::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,
Weight::MAX,
));
assert_eq!(InboundLanes::<TestRuntime>::get(TEST_LANE_ID).last_delivered_nonce(), 2);
#[test]
fn increase_message_fee_fails_if_message_is_already_delivered() {
run_test(|| {
send_regular_message();
receive_messages_delivery_proof();
assert_noop!(
Pallet::<TestRuntime, ()>::increase_message_fee(
Origin::signed(1),
TEST_LANE_ID,
1,
100,
),
Error::<TestRuntime, ()>::MessageIsAlreadyDelivered,
);
});
}
#[test]
fn increase_message_fee_fails_if_message_is_not_yet_sent() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime, ()>::increase_message_fee(
Origin::signed(1),
TEST_LANE_ID,
1,
100,
),
Error::<TestRuntime, ()>::MessageIsNotYetSent,
);
});
}
#[test]
fn increase_message_fee_fails_if_submitter_cant_pay_additional_fee() {
run_test(|| {
send_regular_message();
TestMessageDeliveryAndDispatchPayment::reject_payments();
assert_noop!(
Pallet::<TestRuntime, ()>::increase_message_fee(
Origin::signed(1),
TEST_LANE_ID,
1,
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;
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,