Newer
Older
/// 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: InboundLaneData<T::InboundRelayer> =
InboundLanes::<T, I>::get(&self.lane_id).into();
*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, StoredInboundLaneData::<T, I>(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 })
.map(Into::into)
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, MAX_OUTBOUND_PAYLOAD_SIZE,
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 bp_test_utils::generate_owned_bridge_module_tests;
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 inbound_lane_data = InboundLanes::<TestRuntime, ()>::get(&lane).0;
Svyatoslav Nikolsky
committed
let last_delivered_nonce = inbound_lane_data.last_delivered_nonce();
let relayers = inbound_lane_data.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),
Svyatoslav Nikolsky
committed
last_delivered_nonce,
}
}
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 {
lane_id: TEST_LANE_ID,
nonce: 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,
Svyatoslav Nikolsky
committed
last_delivered_nonce: 1,
Svyatoslav Nikolsky
committed
..Default::default()
},
));
assert_eq!(
System::<TestRuntime>::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::Messages(Event::MessagesDelivered {
lane_id: TEST_LANE_ID,
messages: DeliveredMessages::new(1, true),
}),
topics: vec![],
}],
);
}
#[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(MessagesOperatingMode::Basic(
BasicOperatingMode::Halted,
));
assert_noop!(
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.declared_weight,
Error::<TestRuntime, ()>::NotOperatingNormally,
assert_noop!(
Pallet::<TestRuntime>::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 1,),
Error::<TestRuntime, ()>::BridgeModule(bp_runtime::OwnedBridgeModuleError::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, ()>::BridgeModule(bp_runtime::OwnedBridgeModuleError::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,
Svyatoslav Nikolsky
committed
last_delivered_nonce: 1,
},
Error::<TestRuntime, ()>::BridgeModule(bp_runtime::OwnedBridgeModuleError::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(
MessagesOperatingMode::RejectingOutboundMessages,
);
assert_noop!(
Pallet::<TestRuntime>::send_message(
Origin::signed(1),
TEST_LANE_ID,
REGULAR_PAYLOAD,
REGULAR_PAYLOAD.declared_weight,
Error::<TestRuntime, ()>::NotOperatingNormally,
);
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,
Svyatoslav Nikolsky
committed
last_delivered_nonce: 1,
},
));
});
}
#[test]
fn send_message_works() {
run_test(|| {
send_regular_message();
});
}
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
#[test]
fn send_message_rejects_too_large_message() {
run_test(|| {
let mut message_payload = message_payload(1, 0);
// the payload isn't simply extra, so it'll definitely overflow
// `MAX_OUTBOUND_PAYLOAD_SIZE` if we add `MAX_OUTBOUND_PAYLOAD_SIZE` bytes to extra
message_payload
.extra
.extend_from_slice(&[0u8; MAX_OUTBOUND_PAYLOAD_SIZE as usize]);
assert_noop!(
Pallet::<TestRuntime>::send_message(
Origin::signed(1),
TEST_LANE_ID,
message_payload,
0,
),
Error::<TestRuntime, ()>::MessageIsTooLarge,
);
})
}
#[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).0.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,
Svyatoslav Nikolsky
committed
last_delivered_nonce: 10,
},
);
// 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).0,
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,
Svyatoslav Nikolsky
committed
last_delivered_nonce: 11,
},
);
});
}
#[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,
Svyatoslav Nikolsky
committed
last_delivered_nonce: 1,
Svyatoslav Nikolsky
committed
..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,
Svyatoslav Nikolsky
committed
last_delivered_nonce: 2,
Svyatoslav Nikolsky
committed
..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,
Svyatoslav Nikolsky
committed
last_delivered_nonce: 2,
Svyatoslav Nikolsky
committed
..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,
Svyatoslav Nikolsky
committed
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
last_delivered_nonce: 2,
..Default::default()
},
),
Error::<TestRuntime, ()>::InvalidUnrewardedRelayersState,
);
// when last delivered nonce is invalid
assert_noop!(
Pallet::<TestRuntime>::receive_messages_delivery_proof(
Origin::signed(1),
TestMessagesDeliveryProof(Ok((
TEST_LANE_ID,
InboundLaneData {
relayers: vec![
unrewarded_relayer(1, 1, TEST_RELAYER_A),
unrewarded_relayer(2, 2, TEST_RELAYER_B)
]
.into_iter()
.collect(),
..Default::default()
}
))),
UnrewardedRelayersState {
unrewarded_relayer_entries: 2,
total_messages: 2,
last_delivered_nonce: 8,
Svyatoslav Nikolsky
committed
..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,
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);