use super::*;
use crate::{
mock::{
deregister_teyrchain, new_test_ext, register_teyrchain, register_teyrchain_with_balance,
Dmp, Hrmp, MockGenesisConfig, Paras, ParasShared, RuntimeEvent as MockEvent, RuntimeOrigin,
System, Test, TestUsesOnlyStoredVersionWrapper,
},
shared,
};
use pezframe_support::{assert_noop, assert_ok};
use pezkuwi_primitives::{BlockNumber, InboundDownwardMessage};
use pezsp_runtime::traits::BadOrigin;
use std::collections::BTreeMap;
pub(crate) fn run_to_block(to: BlockNumber, new_session: Option<Vec<BlockNumber>>) {
let config = configuration::ActiveConfig::<Test>::get();
while System::block_number() < to {
let b = System::block_number();
Hrmp::initializer_finalize();
Paras::initializer_finalize(b);
ParasShared::initializer_finalize();
if new_session.as_ref().map_or(false, |v| v.contains(&(b + 1))) {
let notification = crate::initializer::SessionChangeNotification {
prev_config: config.clone(),
new_config: config.clone(),
session_index: shared::CurrentSessionIndex::<Test>::get() + 1,
..Default::default()
};
ParasShared::initializer_on_new_session(
notification.session_index,
notification.random_seed,
¬ification.new_config,
notification.validators.clone(),
);
let outgoing_paras = Paras::initializer_on_new_session(¬ification);
Hrmp::initializer_on_new_session(¬ification, &outgoing_paras);
}
System::on_finalize(b);
System::on_initialize(b + 1);
System::set_block_number(b + 1);
ParasShared::initializer_initialize(b + 1);
Paras::initializer_initialize(b + 1);
Hrmp::initializer_initialize(b + 1);
}
}
#[derive(Debug)]
pub(super) struct GenesisConfigBuilder {
hrmp_channel_max_capacity: u32,
hrmp_channel_max_message_size: u32,
hrmp_max_paras_outbound_channels: u32,
hrmp_max_paras_inbound_channels: u32,
hrmp_max_message_num_per_candidate: u32,
hrmp_channel_max_total_size: u32,
hrmp_sender_deposit: Balance,
hrmp_recipient_deposit: Balance,
}
impl Default for GenesisConfigBuilder {
fn default() -> Self {
Self {
hrmp_channel_max_capacity: 2,
hrmp_channel_max_message_size: 8,
hrmp_max_paras_outbound_channels: 2,
hrmp_max_paras_inbound_channels: 2,
hrmp_max_message_num_per_candidate: 2,
hrmp_channel_max_total_size: 16,
hrmp_sender_deposit: 100,
hrmp_recipient_deposit: 100,
}
}
}
impl GenesisConfigBuilder {
pub(super) fn build(self) -> crate::mock::MockGenesisConfig {
let mut genesis = default_genesis_config();
let config = &mut genesis.configuration.config;
config.hrmp_channel_max_capacity = self.hrmp_channel_max_capacity;
config.hrmp_channel_max_message_size = self.hrmp_channel_max_message_size;
config.hrmp_max_teyrchain_outbound_channels = self.hrmp_max_paras_outbound_channels;
config.hrmp_max_teyrchain_inbound_channels = self.hrmp_max_paras_inbound_channels;
config.hrmp_max_message_num_per_candidate = self.hrmp_max_message_num_per_candidate;
config.hrmp_channel_max_total_size = self.hrmp_channel_max_total_size;
config.hrmp_sender_deposit = self.hrmp_sender_deposit;
config.hrmp_recipient_deposit = self.hrmp_recipient_deposit;
genesis
}
}
fn default_genesis_config() -> MockGenesisConfig {
MockGenesisConfig {
configuration: crate::configuration::GenesisConfig {
config: crate::configuration::HostConfiguration {
max_downward_message_size: 1024,
..Default::default()
},
},
..Default::default()
}
}
fn channel_exists(sender: ParaId, recipient: ParaId) -> bool {
HrmpChannels::<Test>::get(&HrmpChannelId { sender, recipient }).is_some()
}
#[test]
fn empty_state_consistent_state() {
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn open_channel_works() {
let para_a = 2001.into();
let para_a_origin: crate::Origin = 2001.into();
let para_b = 2003.into();
let para_b_origin: crate::Origin = 2003.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
Hrmp::hrmp_init_open_channel(para_a_origin.into(), para_b, 2, 8).unwrap();
Hrmp::assert_storage_consistency_exhaustive();
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::OpenChannelRequested {
sender: para_a,
recipient: para_b,
proposed_max_capacity: 2,
proposed_max_message_size: 8
})));
Hrmp::hrmp_accept_open_channel(para_b_origin.into(), para_a).unwrap();
Hrmp::assert_storage_consistency_exhaustive();
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::OpenChannelAccepted { sender: para_a, recipient: para_b })));
run_to_block(6, None);
assert!(!channel_exists(para_a, para_b));
Hrmp::assert_storage_consistency_exhaustive();
run_to_block(8, Some(vec![8]));
assert!(channel_exists(para_a, para_b));
});
}
#[test]
fn force_open_channel_works() {
let para_a = 1.into();
let para_b = 2003.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
let para_a_free_balance =
<Test as Config>::Currency::free_balance(¶_a.into_account_truncating());
let para_b_free_balance =
<Test as Config>::Currency::free_balance(¶_b.into_account_truncating());
run_to_block(5, Some(vec![4, 5]));
Hrmp::force_open_hrmp_channel(RuntimeOrigin::root(), para_a, para_b, 2, 8).unwrap();
Hrmp::force_open_hrmp_channel(RuntimeOrigin::root(), para_b, para_a, 2, 8).unwrap();
Hrmp::assert_storage_consistency_exhaustive();
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::HrmpChannelForceOpened {
sender: para_a,
recipient: para_b,
proposed_max_capacity: 2,
proposed_max_message_size: 8
})));
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::HrmpChannelForceOpened {
sender: para_b,
recipient: para_a,
proposed_max_capacity: 2,
proposed_max_message_size: 8
})));
run_to_block(6, None);
assert!(!channel_exists(para_a, para_b));
assert!(!channel_exists(para_b, para_a));
Hrmp::assert_storage_consistency_exhaustive();
run_to_block(8, Some(vec![8]));
assert!(channel_exists(para_a, para_b));
assert!(channel_exists(para_b, para_a));
assert_eq!(
<Test as Config>::Currency::free_balance(¶_a.into_account_truncating()),
para_a_free_balance
);
assert_eq!(
<Test as Config>::Currency::free_balance(¶_b.into_account_truncating()),
para_b_free_balance
);
});
}
#[test]
fn force_open_channel_without_free_balance_works() {
let para_a = 1.into();
let para_b = 2003.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain_with_balance(para_a, 0);
register_teyrchain_with_balance(para_b, 0);
run_to_block(5, Some(vec![4, 5]));
Hrmp::force_open_hrmp_channel(RuntimeOrigin::root(), para_a, para_b, 2, 8).unwrap();
Hrmp::force_open_hrmp_channel(RuntimeOrigin::root(), para_b, para_a, 2, 8).unwrap();
Hrmp::assert_storage_consistency_exhaustive();
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::HrmpChannelForceOpened {
sender: para_a,
recipient: para_b,
proposed_max_capacity: 2,
proposed_max_message_size: 8
})));
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::HrmpChannelForceOpened {
sender: para_b,
recipient: para_a,
proposed_max_capacity: 2,
proposed_max_message_size: 8
})));
run_to_block(6, None);
assert!(!channel_exists(para_a, para_b));
assert!(!channel_exists(para_b, para_a));
Hrmp::assert_storage_consistency_exhaustive();
run_to_block(8, Some(vec![8]));
assert!(channel_exists(para_a, para_b));
assert!(channel_exists(para_b, para_a));
});
}
#[test]
fn force_open_channel_works_with_existing_request() {
let para_a = 2001.into();
let para_a_origin: crate::Origin = 2001.into();
let para_b = 2003.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(3, Some(vec![2, 3]));
Hrmp::hrmp_init_open_channel(para_a_origin.into(), para_b, 2, 8).unwrap();
Hrmp::assert_storage_consistency_exhaustive();
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::OpenChannelRequested {
sender: para_a,
recipient: para_b,
proposed_max_capacity: 2,
proposed_max_message_size: 8
})));
run_to_block(5, Some(vec![4, 5]));
assert!(HrmpOpenChannelRequests::<Test>::get(&HrmpChannelId {
sender: para_a,
recipient: para_b
})
.is_some());
assert!(!channel_exists(para_a, para_b));
Hrmp::force_open_hrmp_channel(RuntimeOrigin::root(), para_a, para_b, 2, 8).unwrap();
Hrmp::assert_storage_consistency_exhaustive();
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::HrmpChannelForceOpened {
sender: para_a,
recipient: para_b,
proposed_max_capacity: 2,
proposed_max_message_size: 8
})));
run_to_block(6, None);
assert!(!channel_exists(para_a, para_b));
Hrmp::assert_storage_consistency_exhaustive();
run_to_block(8, Some(vec![8]));
assert!(channel_exists(para_a, para_b));
});
}
#[test]
fn open_system_channel_works() {
let para_a = 1.into();
let para_b = 3.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
Hrmp::establish_system_channel(RuntimeOrigin::signed(1), para_a, para_b).unwrap();
Hrmp::assert_storage_consistency_exhaustive();
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::HrmpSystemChannelOpened {
sender: para_a,
recipient: para_b,
proposed_max_capacity: 2,
proposed_max_message_size: 8
})));
run_to_block(6, None);
assert!(!channel_exists(para_a, para_b));
Hrmp::assert_storage_consistency_exhaustive();
run_to_block(8, Some(vec![8]));
assert!(channel_exists(para_a, para_b));
});
}
#[test]
fn open_system_channel_does_not_work_for_non_system_chains() {
let para_a = 2001.into();
let para_b = 2003.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
assert_noop!(
Hrmp::establish_system_channel(RuntimeOrigin::signed(1), para_a, para_b),
Error::<Test>::ChannelCreationNotAuthorized
);
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn open_system_channel_does_not_work_with_one_non_system_chain() {
let para_a = 1.into();
let para_b = 2003.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
assert_noop!(
Hrmp::establish_system_channel(RuntimeOrigin::signed(1), para_a, para_b),
Error::<Test>::ChannelCreationNotAuthorized
);
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn poke_deposits_works() {
let para_a = 1.into();
let para_b = 2001.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain_with_balance(para_a, 200);
register_teyrchain_with_balance(para_b, 200);
let config = configuration::ActiveConfig::<Test>::get();
let channel_id = HrmpChannelId { sender: para_a, recipient: para_b };
HrmpChannels::<Test>::insert(
&channel_id,
HrmpChannel {
sender_deposit: config.hrmp_sender_deposit,
recipient_deposit: config.hrmp_recipient_deposit,
max_capacity: config.hrmp_channel_max_capacity,
max_total_size: config.hrmp_channel_max_total_size,
max_message_size: config.hrmp_channel_max_message_size,
msg_count: 0,
total_size: 0,
mqc_head: None,
},
);
assert_ok!(<Test as Config>::Currency::reserve(
¶_a.into_account_truncating(),
config.hrmp_sender_deposit
));
assert_ok!(<Test as Config>::Currency::reserve(
¶_b.into_account_truncating(),
config.hrmp_recipient_deposit
));
assert_ok!(Hrmp::poke_channel_deposits(RuntimeOrigin::signed(1), para_a, para_b));
assert_eq!(
<Test as Config>::Currency::reserved_balance(¶_a.into_account_truncating()),
0
);
assert_eq!(
<Test as Config>::Currency::reserved_balance(¶_b.into_account_truncating()),
0
);
});
}
#[test]
fn close_channel_works() {
let para_a = 2005.into();
let para_b = 2002.into();
let para_b_origin: crate::Origin = 2002.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
run_to_block(6, Some(vec![6]));
assert!(channel_exists(para_a, para_b));
let channel_id = HrmpChannelId { sender: para_a, recipient: para_b };
Hrmp::hrmp_close_channel(para_b_origin.into(), channel_id.clone()).unwrap();
assert!(channel_exists(para_a, para_b));
Hrmp::assert_storage_consistency_exhaustive();
run_to_block(8, Some(vec![8]));
assert!(!channel_exists(para_a, para_b));
Hrmp::assert_storage_consistency_exhaustive();
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::ChannelClosed {
by_teyrchain: para_b,
channel_id: channel_id.clone()
})));
});
}
#[test]
fn send_recv_messages() {
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_channel_max_message_size = 20;
genesis.hrmp_channel_max_total_size = 20;
new_test_ext(genesis.build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 20).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
run_to_block(6, Some(vec![6]));
assert!(channel_exists(para_a, para_b));
let msgs: HorizontalMessages =
vec![OutboundHrmpMessage { recipient: para_b, data: b"this is an emergency".to_vec() }]
.try_into()
.unwrap();
let config = configuration::ActiveConfig::<Test>::get();
assert!(Hrmp::check_outbound_hrmp(&config, para_a, &msgs).is_ok());
let _ = Hrmp::queue_outbound_hrmp(para_a, msgs);
Hrmp::assert_storage_consistency_exhaustive();
run_to_block(7, None);
assert!(Hrmp::check_hrmp_watermark(para_b, 7, 6).is_ok());
let _ = Hrmp::prune_hrmp(para_b, 6);
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn hrmp_mqc_head_fixture() {
let para_a = 2000.into();
let para_b = 2024.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_channel_max_message_size = 20;
genesis.hrmp_channel_max_total_size = 20;
new_test_ext(genesis.build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(2, Some(vec![1, 2]));
Hrmp::init_open_channel(para_a, para_b, 2, 20).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
run_to_block(3, Some(vec![3]));
let _ = Hrmp::queue_outbound_hrmp(
para_a,
vec![OutboundHrmpMessage { recipient: para_b, data: vec![1, 2, 3] }]
.try_into()
.unwrap(),
);
run_to_block(4, None);
let _ = Hrmp::queue_outbound_hrmp(
para_a,
vec![OutboundHrmpMessage { recipient: para_b, data: vec![4, 5, 6] }]
.try_into()
.unwrap(),
);
assert_eq!(
Hrmp::hrmp_mqc_heads(para_b),
vec![(
para_a,
hex_literal::hex![
"a964fd3b4f3d3ce92a0e25e576b87590d92bb5cb7031909c7f29050e1f04a375"
]
.into()
),],
);
});
}
#[test]
fn accept_incoming_request_and_offboard() {
let para_a = 2032.into();
let para_b = 2064.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
deregister_teyrchain(para_a);
run_to_block(7, Some(vec![6, 7]));
assert!(!Paras::is_valid_para(para_a));
assert!(!channel_exists(para_a, para_b));
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn check_sent_messages() {
let para_a = 2032.into();
let para_b = 2064.into();
let para_c = 2097.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
register_teyrchain(para_c);
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
Hrmp::init_open_channel(para_c, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_c).unwrap();
run_to_block(6, Some(vec![6]));
assert!(Paras::is_valid_para(para_a));
let msgs: HorizontalMessages =
vec![OutboundHrmpMessage { recipient: para_b, data: b"knock".to_vec() }]
.try_into()
.unwrap();
let config = configuration::ActiveConfig::<Test>::get();
assert!(Hrmp::check_outbound_hrmp(&config, para_a, &msgs).is_ok());
let _ = Hrmp::queue_outbound_hrmp(para_a, msgs.clone());
let mqc_heads = Hrmp::hrmp_mqc_heads(para_b);
let contents = Hrmp::inbound_hrmp_channels_contents(para_b);
assert_eq!(
contents,
vec![
(para_a, vec![InboundHrmpMessage { sent_at: 6, data: b"knock".to_vec() }]),
(para_c, vec![])
]
.into_iter()
.collect::<BTreeMap::<_, _>>(),
);
assert_eq!(
mqc_heads,
vec![
(
para_a,
hex_literal::hex!(
"3bba6404e59c91f51deb2ae78f1273ebe75896850713e13f8c0eba4b0996c483"
)
.into()
),
(para_c, Default::default())
],
);
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn verify_externally_accessible() {
use pezkuwi_primitives::{well_known_keys, AbridgedHrmpChannel};
let para_a = 2020.into();
let para_b = 2021.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
run_to_block(8, Some(vec![8]));
assert!(channel_exists(para_a, para_b));
let raw_hrmp_channel =
pezsp_io::storage::get(&well_known_keys::hrmp_channels(HrmpChannelId {
sender: para_a,
recipient: para_b,
}))
.expect("the channel exists and we must be able to get it through well known keys");
let abridged_hrmp_channel = AbridgedHrmpChannel::decode(&mut &raw_hrmp_channel[..])
.expect("HrmpChannel should be decodable as AbridgedHrmpChannel");
assert_eq!(
abridged_hrmp_channel,
AbridgedHrmpChannel {
max_capacity: 2,
max_total_size: 16,
max_message_size: 8,
msg_count: 0,
total_size: 0,
mqc_head: None,
},
);
let raw_ingress_index =
pezsp_io::storage::get(&well_known_keys::hrmp_ingress_channel_index(para_b))
.expect("the ingress index must be present for para_b");
let ingress_index = <Vec<ParaId>>::decode(&mut &raw_ingress_index[..])
.expect("ingress index should be decodable as a list of para ids");
assert_eq!(ingress_index, vec![para_a]);
let raw_egress_index =
pezsp_io::storage::get(&well_known_keys::hrmp_egress_channel_index(para_a))
.expect("the egress index must be present for para_a");
let egress_index = <Vec<ParaId>>::decode(&mut &raw_egress_index[..])
.expect("egress index should be decodable as a list of para ids");
assert_eq!(egress_index, vec![para_b]);
});
}
#[test]
fn charging_deposits() {
let para_a = 2032.into();
let para_b = 2064.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain_with_balance(para_a, 0);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
assert_noop!(
Hrmp::init_open_channel(para_a, para_b, 2, 8),
pezpallet_balances::Error::<Test, _>::InsufficientBalance
);
});
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain_with_balance(para_b, 0);
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
assert_noop!(
Hrmp::accept_open_channel(para_b, para_a),
pezpallet_balances::Error::<Test, _>::InsufficientBalance
);
});
}
#[test]
fn refund_deposit_on_normal_closure() {
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_sender_deposit = 20;
genesis.hrmp_recipient_deposit = 15;
new_test_ext(genesis.build()).execute_with(|| {
register_teyrchain_with_balance(para_a, 100);
register_teyrchain_with_balance(para_b, 110);
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
assert_eq!(<Test as Config>::Currency::free_balance(¶_a.into_account_truncating()), 80);
assert_eq!(<Test as Config>::Currency::free_balance(¶_b.into_account_truncating()), 95);
run_to_block(8, Some(vec![8]));
Hrmp::close_channel(para_b, HrmpChannelId { sender: para_a, recipient: para_b }).unwrap();
run_to_block(10, Some(vec![10]));
assert_eq!(
<Test as Config>::Currency::free_balance(¶_a.into_account_truncating()),
100
);
assert_eq!(
<Test as Config>::Currency::free_balance(¶_b.into_account_truncating()),
110
);
});
}
#[test]
fn refund_deposit_on_offboarding() {
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_sender_deposit = 20;
genesis.hrmp_recipient_deposit = 15;
new_test_ext(genesis.build()).execute_with(|| {
register_teyrchain_with_balance(para_a, 100);
register_teyrchain_with_balance(para_b, 110);
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
assert_eq!(<Test as Config>::Currency::free_balance(¶_a.into_account_truncating()), 80);
assert_eq!(<Test as Config>::Currency::free_balance(¶_b.into_account_truncating()), 95);
run_to_block(8, Some(vec![8]));
assert!(channel_exists(para_a, para_b));
deregister_teyrchain(para_a);
run_to_block(10, Some(vec![9, 10]));
assert!(!Paras::is_valid_para(para_a));
assert!(!channel_exists(para_a, para_b));
Hrmp::assert_storage_consistency_exhaustive();
assert_eq!(
<Test as Config>::Currency::free_balance(¶_a.into_account_truncating()),
100
);
assert_eq!(
<Test as Config>::Currency::free_balance(¶_b.into_account_truncating()),
110
);
});
}
#[test]
fn no_dangling_open_requests() {
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_sender_deposit = 20;
genesis.hrmp_recipient_deposit = 15;
new_test_ext(genesis.build()).execute_with(|| {
register_teyrchain_with_balance(para_a, 100);
register_teyrchain_with_balance(para_b, 110);
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
assert_eq!(<Test as Config>::Currency::free_balance(¶_a.into_account_truncating()), 80);
deregister_teyrchain(para_a);
run_to_block(9, Some(vec![9]));
Hrmp::accept_open_channel(para_b, para_a).unwrap();
assert_eq!(<Test as Config>::Currency::free_balance(¶_b.into_account_truncating()), 95);
assert!(!channel_exists(para_a, para_b));
run_to_block(10, Some(vec![10]));
assert_eq!(
<Test as Config>::Currency::free_balance(¶_b.into_account_truncating()),
110
);
assert!(!channel_exists(para_a, para_b));
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn cancel_pending_open_channel_request() {
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_sender_deposit = 20;
genesis.hrmp_recipient_deposit = 15;
new_test_ext(genesis.build()).execute_with(|| {
register_teyrchain_with_balance(para_a, 100);
register_teyrchain_with_balance(para_b, 110);
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 8).unwrap();
assert_eq!(<Test as Config>::Currency::free_balance(¶_a.into_account_truncating()), 80);
Hrmp::cancel_open_request(para_a, HrmpChannelId { sender: para_a, recipient: para_b })
.unwrap();
assert_eq!(
<Test as Config>::Currency::free_balance(¶_a.into_account_truncating()),
100
);
run_to_block(10, Some(vec![10]));
assert!(!channel_exists(para_a, para_b));
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn watermark_can_remain_the_same() {
let para_a = 2032.into();
let para_b = 2064.into();
let para_c = 3000.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_channel_max_message_size = 20;
genesis.hrmp_channel_max_total_size = 20;
new_test_ext(genesis.build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
register_teyrchain(para_c);
run_to_block(3, Some(vec![2, 3]));
Hrmp::init_open_channel(para_a, para_b, 2, 20).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
Hrmp::init_open_channel(para_c, para_b, 2, 20).unwrap();
Hrmp::accept_open_channel(para_b, para_c).unwrap();
assert!(Hrmp::check_hrmp_watermark(para_b, 3, 3).is_ok());
let _ = Hrmp::prune_hrmp(para_b, 3);
run_to_block(6, Some(vec![6]));
assert!(channel_exists(para_a, para_b));
let msgs: HorizontalMessages =
vec![OutboundHrmpMessage { recipient: para_b, data: b"HRMP message from A".to_vec() }]
.try_into()
.unwrap();
let config = configuration::ActiveConfig::<Test>::get();
assert!(Hrmp::check_outbound_hrmp(&config, para_a, &msgs).is_ok());
let _ = Hrmp::queue_outbound_hrmp(para_a, msgs);
Hrmp::assert_storage_consistency_exhaustive();
assert!(channel_exists(para_c, para_b));
let msgs: HorizontalMessages =
vec![OutboundHrmpMessage { recipient: para_b, data: b"HRMP message from C".to_vec() }]
.try_into()
.unwrap();
let config = configuration::ActiveConfig::<Test>::get();
assert!(Hrmp::check_outbound_hrmp(&config, para_c, &msgs).is_ok());
let _ = Hrmp::queue_outbound_hrmp(para_c, msgs);
Hrmp::assert_storage_consistency_exhaustive();
assert!(matches!(
Hrmp::check_hrmp_watermark(para_b, 6, 2),
Err(HrmpWatermarkAcceptanceErr::AdvancementRule {
new_watermark: 2,
last_watermark: 3
})
));
assert!(matches!(
Hrmp::check_hrmp_watermark(para_b, 6, 5),
Err(HrmpWatermarkAcceptanceErr::LandsOnBlockWithNoMessages { new_watermark: 5 })
));
run_to_block(7, None);
assert!(Hrmp::check_hrmp_watermark(para_b, 7, 3).is_ok());
let _ = Hrmp::prune_hrmp(para_b, 5);
Hrmp::assert_storage_consistency_exhaustive();
run_to_block(8, None);
assert!(Hrmp::check_hrmp_watermark(para_b, 7, 6).is_ok());
let _ = Hrmp::prune_hrmp(para_b, 6);
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn watermark_maxed_out_at_relay_parent() {
let para_a = 2032.into();
let para_b = 2064.into();
let mut genesis = GenesisConfigBuilder::default();
genesis.hrmp_channel_max_message_size = 20;
genesis.hrmp_channel_max_total_size = 20;
new_test_ext(genesis.build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
Hrmp::init_open_channel(para_a, para_b, 2, 20).unwrap();
Hrmp::accept_open_channel(para_b, para_a).unwrap();
run_to_block(6, Some(vec![6]));
assert!(channel_exists(para_a, para_b));
let msgs: HorizontalMessages =
vec![OutboundHrmpMessage { recipient: para_b, data: b"this is an emergency".to_vec() }]
.try_into()
.unwrap();
let config = configuration::ActiveConfig::<Test>::get();
assert!(Hrmp::check_outbound_hrmp(&config, para_a, &msgs).is_ok());
let _ = Hrmp::queue_outbound_hrmp(para_a, msgs);
Hrmp::assert_storage_consistency_exhaustive();
assert!(matches!(
Hrmp::check_hrmp_watermark(para_b, 6, 7),
Err(HrmpWatermarkAcceptanceErr::AheadRelayParent {
new_watermark: 7,
relay_chain_parent_number: 6,
})
));
run_to_block(8, None);
assert!(Hrmp::check_hrmp_watermark(para_b, 7, 7).is_ok());
let _ = Hrmp::prune_hrmp(para_b, 7);
Hrmp::assert_storage_consistency_exhaustive();
run_to_block(9, None);
assert!(Hrmp::check_hrmp_watermark(para_b, 7, 7).is_ok());
let _ = Hrmp::prune_hrmp(para_b, 7);
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn establish_channel_with_system_works() {
let para_a = 2000.into();
let para_a_origin: crate::Origin = 2000.into();
let para_b = 3.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
Hrmp::establish_channel_with_system(para_a_origin.into(), para_b).unwrap();
Hrmp::assert_storage_consistency_exhaustive();
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::HrmpSystemChannelOpened {
sender: para_a,
recipient: para_b,
proposed_max_capacity: 1,
proposed_max_message_size: 4
})));
assert!(System::events().iter().any(|record| record.event
== MockEvent::Hrmp(Event::HrmpSystemChannelOpened {
sender: para_b,
recipient: para_a,
proposed_max_capacity: 1,
proposed_max_message_size: 4
})));
run_to_block(6, None);
assert!(!channel_exists(para_a, para_b));
assert!(!channel_exists(para_b, para_a));
Hrmp::assert_storage_consistency_exhaustive();
run_to_block(8, Some(vec![8]));
assert!(channel_exists(para_a, para_b));
assert!(channel_exists(para_b, para_a));
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn establish_channel_with_system_with_invalid_args() {
let para_a = 2001.into();
let para_a_origin: crate::Origin = 2000.into();
let para_b = 2003.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
assert_noop!(
Hrmp::establish_channel_with_system(RuntimeOrigin::signed(1), para_b),
BadOrigin
);
assert_noop!(
Hrmp::establish_channel_with_system(para_a_origin.into(), para_b),
Error::<Test>::ChannelCreationNotAuthorized
);
Hrmp::assert_storage_consistency_exhaustive();
});
}
#[test]
fn hrmp_notifications_works() {
use xcm::{
opaque::{
latest::{prelude::*, Xcm},
VersionedXcm,
},
IntoVersion,
};
let para_a = 2001.into();
let para_a_origin: crate::Origin = 2001.into();
let para_b = 2003.into();
let para_b_origin: crate::Origin = 2003.into();
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
register_teyrchain(para_a);
register_teyrchain(para_b);
run_to_block(5, Some(vec![4, 5]));
TestUsesOnlyStoredVersionWrapper::set_version(
Location::new(0, [Junction::Teyrchain(para_a.into())]),
None,
);
let previous_version = XCM_VERSION - 1;
TestUsesOnlyStoredVersionWrapper::set_version(
Location::new(0, [Junction::Teyrchain(para_b.into())]),
Some(previous_version),
);
let assert_notification_for = |sent_at, para_id, expected| {
assert_eq!(
Dmp::dmq_contents(para_id),
vec![InboundDownwardMessage { sent_at, msg: expected }]
);
};
assert_ok!(Hrmp::hrmp_init_open_channel(para_a_origin.clone().into(), para_b, 2, 8));
assert_ok!(Hrmp::hrmp_init_open_channel(para_b_origin.clone().into(), para_a, 2, 8));
Hrmp::assert_storage_consistency_exhaustive();
assert_notification_for(
5,
para_b,
VersionedXcm::from(Xcm(vec![HrmpNewChannelOpenRequest {
sender: u32::from(para_a),
max_capacity: 2,
max_message_size: 8,
}]))
.into_version(previous_version)
.expect("compatible")
.encode(),
);
assert_notification_for(
5,
para_a,
VersionedXcm::from(Xcm(vec![HrmpNewChannelOpenRequest {
sender: u32::from(para_b),
max_capacity: 2,
max_message_size: 8,
}]))
.encode(),
);
let _ = Dmp::prune_dmq(para_a, 1000);
let _ = Dmp::prune_dmq(para_b, 1000);
assert_ok!(Hrmp::hrmp_accept_open_channel(para_a_origin.clone().into(), para_b));
assert_ok!(Hrmp::hrmp_accept_open_channel(para_b_origin.clone().into(), para_a));
Hrmp::assert_storage_consistency_exhaustive();
assert_notification_for(
5,
para_b,
VersionedXcm::from(Xcm(vec![HrmpChannelAccepted { recipient: u32::from(para_a) }]))
.into_version(previous_version)
.expect("compatible")
.encode(),
);
assert_notification_for(
5,
para_a,
VersionedXcm::from(Xcm(vec![HrmpChannelAccepted { recipient: u32::from(para_b) }]))
.encode(),
);
let _ = Dmp::prune_dmq(para_a, 1000);
let _ = Dmp::prune_dmq(para_b, 1000);
run_to_block(6, Some(vec![6]));
assert!(channel_exists(para_a, para_b));
assert_ok!(Hrmp::hrmp_close_channel(
para_a_origin.into(),
HrmpChannelId { sender: para_a, recipient: para_b }
));
assert_ok!(Hrmp::hrmp_close_channel(
para_b_origin.into(),
HrmpChannelId { sender: para_b, recipient: para_a }
));
Hrmp::assert_storage_consistency_exhaustive();
assert_notification_for(
6,
para_b,
VersionedXcm::from(Xcm(vec![HrmpChannelClosing {
initiator: u32::from(para_a),
sender: u32::from(para_a),
recipient: u32::from(para_b),
}]))
.into_version(previous_version)
.expect("compatible")
.encode(),
);
assert_notification_for(
6,
para_a,
VersionedXcm::from(Xcm(vec![HrmpChannelClosing {
initiator: u32::from(para_b),
sender: u32::from(para_b),
recipient: u32::from(para_a),
}]))
.encode(),
);
});
}