referrerpolicy=no-referrer-when-downgrade

snowbridge_pallet_outbound_queue_v2/
benchmarking.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
3use super::*;
4
5use crate::fixture::make_submit_delivery_receipt_message;
6use bridge_hub_common::AggregateMessageOrigin;
7use codec::Encode;
8use frame_benchmarking::v2::*;
9use frame_support::{traits::Hooks, BoundedVec};
10use frame_system::RawOrigin;
11use snowbridge_outbound_queue_primitives::v2::{Command, Initializer, Message};
12use sp_core::{H160, H256};
13
14#[allow(unused_imports)]
15use crate::Pallet as OutboundQueue;
16
17#[benchmarks(
18	where
19		<T as Config>::MaxMessagePayloadSize: Get<u32>,
20		<T as frame_system::Config>::AccountId: From<[u8; 32]>,
21)]
22mod benchmarks {
23	use super::*;
24	use frame_support::assert_ok;
25
26	/// Build `Upgrade` message with `MaxMessagePayloadSize`, in the worst-case.
27	fn build_message<T: Config>() -> (Message, OutboundMessage) {
28		let commands = vec![Command::Upgrade {
29			impl_address: H160::zero(),
30			impl_code_hash: H256::zero(),
31			initializer: Initializer {
32				params: core::iter::repeat_with(|| 1_u8)
33					.take(<T as Config>::MaxMessagePayloadSize::get() as usize)
34					.collect(),
35				maximum_required_gas: 200_000,
36			},
37		}];
38		let message = Message {
39			origin: Default::default(),
40			id: H256::default(),
41			fee: 0,
42			commands: BoundedVec::try_from(commands.clone()).unwrap(),
43		};
44		let wrapped_commands: Vec<OutboundCommandWrapper> = commands
45			.into_iter()
46			.map(|command| OutboundCommandWrapper {
47				kind: command.index(),
48				gas: T::GasMeter::maximum_dispatch_gas_used_at_most(&command),
49				payload: command.abi_encode(),
50			})
51			.collect();
52		let outbound_message = OutboundMessage {
53			origin: Default::default(),
54			nonce: 1,
55			topic: H256::default(),
56			commands: wrapped_commands.clone().try_into().unwrap(),
57		};
58		(message, outbound_message)
59	}
60
61	/// Initialize `MaxMessagesPerBlock` messages need to be committed, in the worst-case.
62	fn initialize_worst_case<T: Config>() {
63		for _ in 0..T::MaxMessagesPerBlock::get() {
64			initialize_with_one_message::<T>();
65		}
66	}
67
68	/// Initialize with a single message
69	fn initialize_with_one_message<T: Config>() {
70		let (message, outbound_message) = build_message::<T>();
71		let leaf = <T as Config>::Hashing::hash(&message.encode());
72		MessageLeaves::<T>::append(leaf);
73		Messages::<T>::append(outbound_message);
74	}
75
76	/// Benchmark for processing a message.
77	#[benchmark]
78	fn do_process_message() -> Result<(), BenchmarkError> {
79		let (enqueued_message, _) = build_message::<T>();
80		let origin = AggregateMessageOrigin::SnowbridgeV2([1; 32].into());
81		let message = enqueued_message.encode();
82
83		#[block]
84		{
85			let _ = OutboundQueue::<T>::do_process_message(origin, &message).unwrap();
86		}
87
88		assert_eq!(MessageLeaves::<T>::decode_len().unwrap(), 1);
89
90		Ok(())
91	}
92
93	/// Benchmark for producing final messages commitment, in the worst-case
94	#[benchmark]
95	fn commit() -> Result<(), BenchmarkError> {
96		initialize_worst_case::<T>();
97
98		#[block]
99		{
100			OutboundQueue::<T>::commit();
101		}
102
103		Ok(())
104	}
105
106	/// Benchmark for producing commitment for a single message, used to estimate the delivery
107	/// cost. The assumption is that cost of commit a single message is even higher than the average
108	/// cost of commit all messages.
109	#[benchmark]
110	fn commit_single() -> Result<(), BenchmarkError> {
111		initialize_with_one_message::<T>();
112
113		#[block]
114		{
115			OutboundQueue::<T>::commit();
116		}
117
118		Ok(())
119	}
120
121	/// Benchmark for `on_initialize` in the worst-case
122	#[benchmark]
123	fn on_initialize() -> Result<(), BenchmarkError> {
124		initialize_worst_case::<T>();
125		#[block]
126		{
127			OutboundQueue::<T>::on_initialize(1_u32.into());
128		}
129		Ok(())
130	}
131
132	/// Benchmark the entire process flow in the worst-case. This can be used to determine
133	/// appropriate values for the configuration parameters `MaxMessagesPerBlock` and
134	/// `MaxMessagePayloadSize`
135	#[benchmark]
136	fn process() -> Result<(), BenchmarkError> {
137		initialize_worst_case::<T>();
138		let origin = AggregateMessageOrigin::SnowbridgeV2([1; 32].into());
139		let (enqueued_message, _) = build_message::<T>();
140		let message = enqueued_message.encode();
141
142		#[block]
143		{
144			OutboundQueue::<T>::on_initialize(1_u32.into());
145			for _ in 0..T::MaxMessagesPerBlock::get() {
146				OutboundQueue::<T>::do_process_message(origin, &message).unwrap();
147			}
148			OutboundQueue::<T>::commit();
149		}
150
151		Ok(())
152	}
153
154	#[benchmark]
155	fn submit_delivery_receipt() -> Result<(), BenchmarkError> {
156		let caller: T::AccountId = whitelisted_caller();
157
158		let message = make_submit_delivery_receipt_message();
159
160		T::Helper::initialize_storage(message.finalized_header, message.block_roots_root);
161
162		let receipt = DeliveryReceipt::try_from(&message.event.event_log).unwrap();
163
164		let order = PendingOrder {
165			nonce: receipt.nonce,
166			fee: 0,
167			block_number: frame_system::Pallet::<T>::current_block_number(),
168		};
169		<PendingOrders<T>>::insert(receipt.nonce, order);
170
171		#[block]
172		{
173			assert_ok!(OutboundQueue::<T>::submit_delivery_receipt(
174				RawOrigin::Signed(caller.clone()).into(),
175				Box::new(message.event),
176			));
177		}
178
179		Ok(())
180	}
181
182	impl_benchmark_test_suite!(OutboundQueue, crate::mock::new_tester(), crate::mock::Test,);
183}