// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork use crate::{ mock::{AggregateMessageOrigin::*, *}, *, }; use alloy_core::primitives::FixedBytes; use codec::Encode; use hex_literal::hex; use pezframe_support::{ assert_err, assert_noop, assert_ok, traits::{Hooks, ProcessMessage, ProcessMessageError, QueueFootprintQuery}, weights::WeightMeter, BoundedVec, }; use pezsnowbridge_core::{digest_item::SnowbridgeDigestItem, ChannelId, ParaId}; use pezsnowbridge_outbound_queue_primitives::{ v2::{abi::OutboundMessageWrapper, Command, Initializer, SendMessage}, SendError, }; use pezsp_core::{hexdisplay::HexDisplay, H256}; #[test] fn submit_messages_and_commit() { new_tester().execute_with(|| { for para_id in 1000..1004 { let message = mock_message(para_id); let ticket = OutboundQueue::validate(&message).unwrap(); assert_ok!(OutboundQueue::deliver(ticket)); } ServiceWeight::set(Some(Weight::MAX)); run_to_end_of_next_block(); assert_eq!(Nonce::::get(), 4); let digest = System::digest(); let digest_items = digest.logs(); assert!(digest_items.len() == 1 && digest_items[0].as_other().is_some()); assert_eq!(Messages::::decode_len(), Some(4)); }); } #[test] fn submit_message_fail_too_large() { new_tester().execute_with(|| { let message = mock_invalid_governance_message::(); assert_err!(OutboundQueue::validate(&message), SendError::MessageTooLarge); }); } #[test] fn commit_exits_early_if_no_processed_messages() { new_tester().execute_with(|| { // on_finalize should do nothing, nor should it panic OutboundQueue::on_finalize(System::block_number()); let digest = System::digest(); let digest_items = digest.logs(); assert_eq!(digest_items.len(), 0); }); } #[test] fn process_message_yields_on_max_messages_per_block() { new_tester().execute_with(|| { for _ in 0..::MaxMessagesPerBlock::get() { MessageLeaves::::append(H256::zero()) } let _channel_id: ChannelId = ParaId::from(1000).into(); let origin = AggregateMessageOrigin::SnowbridgeV2(H256::zero()); let message = Message { origin: Default::default(), id: Default::default(), fee: 0, commands: BoundedVec::try_from(vec![Command::Upgrade { impl_address: Default::default(), impl_code_hash: Default::default(), initializer: Initializer { params: (0..512).map(|_| 1u8).collect::>(), maximum_required_gas: 0, }, }]) .unwrap(), }; let mut meter = WeightMeter::new(); assert_err!( OutboundQueue::process_message( message.encode().as_slice(), origin, &mut meter, &mut [0u8; 32] ), ProcessMessageError::Yield ); let events = System::events(); let last_event = events.last().expect("Expected at least one event").event.clone(); match last_event { mock::RuntimeEvent::OutboundQueue(Event::MessagePostponed { payload: _, reason: ProcessMessageError::Yield, }) => {}, _ => { panic!("Expected Event::MessagePostponed(Yield) but got {:?}", last_event); }, } }) } #[test] fn process_message_fails_on_max_nonce_reached() { new_tester().execute_with(|| { let sibling_id = 1000; let _channel_id: ChannelId = ParaId::from(sibling_id).into(); let origin = AggregateMessageOrigin::SnowbridgeV2(H256::zero()); let message: Message = mock_message(sibling_id); let mut meter = WeightMeter::with_limit(Weight::MAX); Nonce::::set(u64::MAX); let result = OutboundQueue::process_message( message.encode().as_slice(), origin, &mut meter, &mut [0u8; 32], ); assert_err!(result, ProcessMessageError::Unsupported); let events = System::events(); let last_event = events.last().expect("Expected at least one event").event.clone(); match last_event { mock::RuntimeEvent::OutboundQueue(Event::MessageRejected { id: None, payload: _, error: ProcessMessageError::Unsupported, }) => {}, _ => { panic!("Expected Event::MessageRejected(Unsupported) but got {:?}", last_event); }, } }) } #[test] fn process_message_fails_on_overweight_message() { new_tester().execute_with(|| { let sibling_id = 1000; let _channel_id: ChannelId = ParaId::from(sibling_id).into(); let origin = AggregateMessageOrigin::SnowbridgeV2(H256::zero()); let message: Message = mock_message(sibling_id); let mut meter = WeightMeter::with_limit(Weight::from_parts(1, 1)); assert_err!( OutboundQueue::process_message( message.encode().as_slice(), origin, &mut meter, &mut [0u8; 32] ), ProcessMessageError::Overweight(::WeightInfo::do_process_message()) ); let events = System::events(); let last_event = events.last().expect("Expected at least one event").event.clone(); match last_event { mock::RuntimeEvent::OutboundQueue(Event::MessagePostponed { payload: _, reason: ProcessMessageError::Overweight(_), }) => {}, _ => { panic!("Expected Event::MessagePostponed(Overweight(_)) but got {:?}", last_event); }, } }) } #[test] fn governance_message_not_processed_in_same_block_when_queue_congested_with_low_priority_messages() { let sibling_id: u32 = 1000; new_tester().execute_with(|| { // submit a lot of low priority messages from asset_hub which will need multiple blocks to // execute(20 messages for each block so 40 required at least 2 blocks) let max_messages = 40; for _ in 0..max_messages { // submit low priority message let message = mock_message(sibling_id); let ticket = OutboundQueue::validate(&message).unwrap(); OutboundQueue::deliver(ticket).unwrap(); } let footprint = MessageQueue::footprint(SnowbridgeV2(H256::from_low_u64_be(sibling_id as u64))); assert_eq!(footprint.storage.count, (max_messages) as u64); let message = mock_governance_message::(); let ticket = OutboundQueue::validate(&message).unwrap(); OutboundQueue::deliver(ticket).unwrap(); // move to next block ServiceWeight::set(Some(Weight::MAX)); run_to_end_of_next_block(); // first process 20 messages from sibling channel let footprint = MessageQueue::footprint(SnowbridgeV2(H256::from_low_u64_be(sibling_id as u64))); assert_eq!(footprint.storage.count, 40 - 20); // and governance message does not have the chance to execute in same block let footprint = MessageQueue::footprint(SnowbridgeV2(bridge_hub_root_origin())); assert_eq!(footprint.storage.count, 1); // move to next block ServiceWeight::set(Some(Weight::MAX)); run_to_end_of_next_block(); // now governance message get executed in this block let footprint = MessageQueue::footprint(SnowbridgeV2(bridge_hub_root_origin())); assert_eq!(footprint.storage.count, 0); // and this time process 19 messages from sibling channel so we have 1 message left let footprint = MessageQueue::footprint(SnowbridgeV2(H256::from_low_u64_be(sibling_id as u64))); assert_eq!(footprint.storage.count, 1); // move to the next block, the last 1 message from sibling channel get executed ServiceWeight::set(Some(Weight::MAX)); run_to_end_of_next_block(); let footprint = MessageQueue::footprint(SnowbridgeV2(H256::from_low_u64_be(sibling_id as u64))); assert_eq!(footprint.storage.count, 0); }); } #[test] fn encode_digest_item_with_correct_index() { new_tester().execute_with(|| { let digest_item: DigestItem = SnowbridgeDigestItem::Snowbridge(H256::default()).into(); let enum_prefix = match digest_item { DigestItem::Other(data) => data[0], _ => u8::MAX, }; assert_eq!(enum_prefix, 0); }); } #[test] fn encode_digest_item() { new_tester().execute_with(|| { let digest_item: DigestItem = SnowbridgeDigestItem::Snowbridge([5u8; 32].into()).into(); let digest_item_raw = digest_item.encode(); assert_eq!(digest_item_raw[0], 0); // DigestItem::Other assert_eq!(digest_item_raw[2], 0); // SnowbridgeDigestItem::Snowbridge assert_eq!( digest_item_raw, [ 0, 132, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 ] ); }); } fn encode_mock_message(message: Message) -> Vec { let commands: Vec = message .commands .into_iter() .map(|command| CommandWrapper { kind: command.index(), gas: ::GasMeter::maximum_dispatch_gas_used_at_most(&command), payload: Bytes::from(command.abi_encode()), }) .collect(); // print the abi-encoded message and decode with solidity test let committed_message = OutboundMessageWrapper { origin: FixedBytes::from(message.origin.as_fixed_bytes()), nonce: 1, topic: FixedBytes::from(message.id.as_fixed_bytes()), commands, }; let message_abi_encoded = committed_message.abi_encode(); message_abi_encoded } #[test] fn encode_unlock_message() { let message: Message = mock_message(1000); let message_abi_encoded = encode_mock_message(message); println!("{}", HexDisplay::from(&message_abi_encoded)); assert_eq!(hex!("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000b1185ede04202fe62d38f5db72f71e38ff3e830500000000000000000000000000000000000000000000000000000000000f4240").to_vec(), message_abi_encoded) } #[test] fn encode_register_pna() { let message: Message = mock_register_token_message(1000); let message_abi_encoded = encode_mock_message(message); println!("{}", HexDisplay::from(&message_abi_encoded)); assert_eq!(hex!("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000124f80000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").to_vec(), message_abi_encoded) } #[test] fn test_add_tip_cumulative() { new_tester().execute_with(|| { let nonce = 1; let initial_fee = 1000; let additional_fee = 500; let current_block = System::block_number(); let order = PendingOrder { nonce, fee: initial_fee, block_number: current_block }; PendingOrders::::insert(nonce, order); assert_ok!(OutboundQueue::add_tip(nonce, additional_fee)); let order_after = PendingOrders::::get(nonce).unwrap(); assert_eq!(order_after.fee, initial_fee + additional_fee); }); } #[test] fn test_add_tip_fails_no_pending_order() { new_tester().execute_with(|| { let nonce = 42; let amount = 1000; assert_noop!(OutboundQueue::add_tip(nonce, amount), AddTipError::UnknownMessage); }); } #[test] fn test_add_tip_fails_amount_zero() { new_tester().execute_with(|| { let nonce = 1; let initial_fee = 1000; let zero_amount = 0; let current_block = System::block_number(); let order = PendingOrder { nonce, fee: initial_fee, block_number: current_block }; PendingOrders::::insert(nonce, order); assert_noop!(OutboundQueue::add_tip(nonce, zero_amount), AddTipError::AmountZero); // Verify the original fee is unchanged let order_after = PendingOrders::::get(nonce).unwrap(); assert_eq!(order_after.fee, initial_fee); }); }