1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.

use super::{Pallet as Ump, *};
use frame_system::RawOrigin;
use xcm::prelude::*;

fn assert_last_event_type<T: Config>(generic_event: <T as Config>::Event) {
	let events = frame_system::Pallet::<T>::events();
	let system_event: <T as frame_system::Config>::Event = generic_event.into();
	// compare to the last event record
	let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
	assert_eq!(sp_std::mem::discriminant(event), sp_std::mem::discriminant(&system_event));
}

fn queue_upward_msg<T: Config>(
	host_conf: &HostConfiguration<T::BlockNumber>,
	para: ParaId,
	msg: UpwardMessage,
) {
	let len = msg.len() as u32;
	let msgs = vec![msg];
	Ump::<T>::check_upward_messages(host_conf, para, &msgs).unwrap();
	let _ = Ump::<T>::receive_upward_messages(para, msgs);
	assert_last_event_type::<T>(Event::UpwardMessagesReceived(para, 1, len).into());
}

// Create a message with at least `size` bytes encoded length
fn create_message_min_size<T: Config>(size: u32) -> Vec<u8> {
	// Create a message with an empty remark call to determine the encoding overhead
	let msg_size_empty_transact = VersionedXcm::<T>::from(Xcm::<T>(vec![Transact {
		origin_type: OriginKind::SovereignAccount,
		require_weight_at_most: Weight::MAX.ref_time(),
		call: frame_system::Call::<T>::remark_with_event { remark: vec![] }.encode().into(),
	}]))
	.encode()
	.len();

	// Create a message with a remark call of just the size required to make the whole encoded message the requested size
	let size = size.saturating_sub(msg_size_empty_transact as u32) as usize;
	let mut remark = Vec::new();
	remark.resize(size, 0u8);
	let msg = VersionedXcm::<T>::from(Xcm::<T>(vec![Transact {
		origin_type: OriginKind::SovereignAccount,
		require_weight_at_most: Weight::MAX.ref_time(),
		call: frame_system::Call::<T>::remark_with_event { remark }.encode().into(),
	}]))
	.encode();

	assert!(msg.len() >= size);
	msg
}

fn create_message_overweight<T: Config>() -> Vec<u8> {
	let max_block_weight = T::BlockWeights::get().max_block;
	// We use a `set_code` Call because it
	let call = frame_system::Call::<T>::set_code { code: vec![] };
	VersionedXcm::<T>::from(Xcm::<T>(vec![Transact {
		origin_type: OriginKind::Superuser,
		require_weight_at_most: max_block_weight.ref_time(),
		call: call.encode().into(),
	}]))
	.encode()
}

frame_benchmarking::benchmarks! {
	// NOTE: We are overestimating slightly here.
	// The benchmark is timing this whole function with different message sizes and a NOOP extrinsic to
	// measure the size-dependent weight. But as we use the weight function **in** the benchmarked function we
	// are taking call and control-flow overhead into account twice.
	process_upward_message {
		let s in 0..MAX_UPWARD_MESSAGE_SIZE_BOUND;
		let para = ParaId::from(1978);
		let data = create_message_min_size::<T>(s);
	}: {
		assert!(T::UmpSink::process_upward_message(para, &data[..], Weight::MAX).is_ok());
	}

	clean_ump_after_outgoing {
		// max number of queued messages.
		let count = configuration::ActiveConfig::<T>::get().max_upward_queue_count;
		let host_conf = configuration::ActiveConfig::<T>::get();
		let msg = create_message_min_size::<T>(0);
		// Start with the block number 1. This is needed because should an event be
		// emitted during the genesis block they will be implicitly wiped.
		frame_system::Pallet::<T>::set_block_number(1u32.into());
		// fill the queue, each message has it's own para-id.
		for id in 0..count {
			queue_upward_msg::<T>(&host_conf, ParaId::from(id), msg.clone());
		}
	}: {
		Ump::<T>::clean_ump_after_outgoing(&ParaId::from(0));
	}

	service_overweight {
		let host_conf = configuration::ActiveConfig::<T>::get();
		let weight = host_conf.ump_max_individual_weight + host_conf.ump_max_individual_weight + Weight::from_ref_time(1000000);
		let para = ParaId::from(1978);
		// The message's weight does not really matter here, as we add service_overweight's
		// max_weight parameter to the extrinsic's weight in the weight calculation.
		// The size of the message influences decoding time, so we create a min-sized message here
		// and take the decoding weight into account by adding it to the extrinsic execution weight
		// in the process_upward_message function.
		let msg = create_message_overweight::<T>();

		// This just makes sure that 0 is not a valid index and we can use it later on.
		let _ = Ump::<T>::service_overweight(RawOrigin::Root.into(), 0, Weight::from_ref_time(1000));
		// Start with the block number 1. This is needed because should an event be
		// emitted during the genesis block they will be implicitly wiped.
		frame_system::Pallet::<T>::set_block_number(1u32.into());
		queue_upward_msg::<T>(&host_conf, para, msg.clone());
		Ump::<T>::process_pending_upward_messages();
		assert_last_event_type::<T>(
			Event::OverweightEnqueued(para, upward_message_id(&msg), 0, Weight::zero()).into()
			);
	}: _(RawOrigin::Root, 0, Weight::MAX)
	verify {
		assert_last_event_type::<T>(Event::OverweightServiced(0, Weight::zero()).into());
	}
}

frame_benchmarking::impl_benchmark_test_suite!(
	Ump,
	crate::mock::new_test_ext(crate::ump::tests::GenesisConfigBuilder::default().build()),
	crate::mock::Test
);