referrerpolicy=no-referrer-when-downgrade

coretime_westend_runtime/
coretime.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17use crate::{xcm_config::LocationToAccountId, *};
18use codec::{Decode, Encode};
19use cumulus_pallet_parachain_system::RelaychainDataProvider;
20use cumulus_primitives_core::relay_chain;
21use frame_support::parameter_types;
22use pallet_broker::{
23	CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600, RCBlockNumberOf, TaskId,
24};
25use parachains_common::{AccountId, Balance};
26use sp_runtime::traits::MaybeConvert;
27use westend_runtime_constants::system_parachain::coretime;
28use xcm::latest::prelude::*;
29use xcm_executor::traits::ConvertLocation;
30
31/// A type containing the encoding of the coretime pallet in the Relay chain runtime. Used to
32/// construct any remote calls. The codec index must correspond to the index of `Coretime` in the
33/// `construct_runtime` of the Relay chain.
34#[derive(Encode, Decode)]
35enum RelayRuntimePallets {
36	#[codec(index = 66)]
37	Coretime(CoretimeProviderCalls),
38}
39
40/// Call encoding for the calls needed from the relay coretime pallet.
41#[derive(Encode, Decode)]
42enum CoretimeProviderCalls {
43	#[codec(index = 1)]
44	RequestCoreCount(CoreIndex),
45	#[codec(index = 2)]
46	RequestRevenueInfoAt(relay_chain::BlockNumber),
47	#[codec(index = 3)]
48	CreditAccount(AccountId, Balance),
49	#[codec(index = 4)]
50	AssignCore(
51		CoreIndex,
52		relay_chain::BlockNumber,
53		Vec<(CoreAssignment, PartsOf57600)>,
54		Option<relay_chain::BlockNumber>,
55	),
56}
57
58parameter_types! {
59	pub const BrokerPalletId: PalletId = PalletId(*b"py/broke");
60	pub const MinimumCreditPurchase: Balance = UNITS / 10;
61	pub const MinimumEndPrice: Balance = UNITS;
62}
63
64/// Type that implements the `CoretimeInterface` for the allocation of Coretime. Meant to operate
65/// from the parachain context. That is, the parachain provides a market (broker) for the sale of
66/// coretime, but assumes a `CoretimeProvider` (i.e. a Relay Chain) to actually provide cores.
67pub struct CoretimeAllocator;
68impl CoretimeInterface for CoretimeAllocator {
69	type AccountId = AccountId;
70	type Balance = Balance;
71	type RelayChainBlockNumberProvider = RelaychainDataProvider<Runtime>;
72
73	fn request_core_count(count: CoreIndex) {
74		use crate::coretime::CoretimeProviderCalls::RequestCoreCount;
75		let request_core_count_call = RelayRuntimePallets::Coretime(RequestCoreCount(count));
76
77		// Weight for `request_core_count` from westend benchmarks:
78		// `ref_time` = 7889000 + (3 * 25000000) + (1 * 100000000) = 182889000
79		// `proof_size` = 1636
80		// Add 5% to each component and round to 2 significant figures.
81		let call_weight = Weight::from_parts(190_000_000, 1700);
82
83		let message = Xcm(vec![
84			Instruction::UnpaidExecution {
85				weight_limit: WeightLimit::Unlimited,
86				check_origin: None,
87			},
88			Instruction::Transact {
89				origin_kind: OriginKind::Native,
90				call: request_core_count_call.encode().into(),
91				fallback_max_weight: Some(call_weight),
92			},
93		]);
94
95		match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
96			Ok(_) => tracing::debug!(
97				target: "runtime::coretime",
98				"Request to update schedulable cores sent successfully."
99			),
100			Err(e) => tracing::error!(
101				target: "runtime::coretime", error=?e,
102				"Failed to send request to update schedulable cores"
103			),
104		}
105	}
106
107	fn request_revenue_info_at(when: RCBlockNumberOf<Self>) {
108		use crate::coretime::CoretimeProviderCalls::RequestRevenueInfoAt;
109		let request_revenue_info_at_call =
110			RelayRuntimePallets::Coretime(RequestRevenueInfoAt(when));
111
112		let message = Xcm(vec![
113			Instruction::UnpaidExecution {
114				weight_limit: WeightLimit::Unlimited,
115				check_origin: None,
116			},
117			Instruction::Transact {
118				origin_kind: OriginKind::Native,
119				call: request_revenue_info_at_call.encode().into(),
120				fallback_max_weight: Some(Weight::from_parts(1_000_000_000, 200_000)),
121			},
122		]);
123
124		match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
125			Ok(_) => tracing::debug!(
126				target: "runtime::coretime",
127				"Request for revenue information sent successfully."
128			),
129			Err(e) => tracing::error!(
130				target: "runtime::coretime", error=?e,
131				"Request for revenue information failed to send"
132			),
133		}
134	}
135
136	fn credit_account(who: Self::AccountId, amount: Self::Balance) {
137		use crate::coretime::CoretimeProviderCalls::CreditAccount;
138		let credit_account_call = RelayRuntimePallets::Coretime(CreditAccount(who, amount));
139
140		let message = Xcm(vec![
141			Instruction::UnpaidExecution {
142				weight_limit: WeightLimit::Unlimited,
143				check_origin: None,
144			},
145			Instruction::Transact {
146				origin_kind: OriginKind::Native,
147				call: credit_account_call.encode().into(),
148				fallback_max_weight: Some(Weight::from_parts(1_000_000_000, 200_000)),
149			},
150		]);
151
152		match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
153			Ok(_) => tracing::debug!(
154				target: "runtime::coretime",
155				"Instruction to credit account sent successfully."
156			),
157			Err(e) => tracing::error!(
158				target: "runtime::coretime", error=?e,
159				"Instruction to credit account failed to send"
160			),
161		}
162	}
163
164	fn assign_core(
165		core: CoreIndex,
166		begin: RCBlockNumberOf<Self>,
167		assignment: Vec<(CoreAssignment, PartsOf57600)>,
168		end_hint: Option<RCBlockNumberOf<Self>>,
169	) {
170		use crate::coretime::CoretimeProviderCalls::AssignCore;
171
172		// Weight for `assign_core` from westend benchmarks:
173		// `ref_time` = 10177115 + (1 * 25000000) + (2 * 100000000) + (57600 * 13932) = 937660315
174		// `proof_size` = 3612
175		// Add 5% to each component and round to 2 significant figures.
176		let call_weight = Weight::from_parts(980_000_000, 3800);
177
178		// The relay chain currently only allows `assign_core` to be called with a complete mask
179		// and only ever with increasing `begin`. The assignments must be truncated to avoid
180		// dropping that core's assignment completely.
181
182		// This shadowing of `assignment` is temporary and can be removed when the relay can accept
183		// multiple messages to assign a single core.
184		let assignment = if assignment.len() > 28 {
185			let mut total_parts = 0u16;
186			// Account for missing parts with a new `Idle` assignment at the start as
187			// `assign_core` on the relay assumes this is sorted. We'll add the rest of the
188			// assignments and sum the parts in one pass, so this is just initialized to 0.
189			let mut assignment_truncated = vec![(CoreAssignment::Idle, 0)];
190			// Truncate to first 27 non-idle assignments.
191			assignment_truncated.extend(
192				assignment
193					.into_iter()
194					.filter(|(a, _)| *a != CoreAssignment::Idle)
195					.take(27)
196					.inspect(|(_, parts)| total_parts += *parts)
197					.collect::<Vec<_>>(),
198			);
199
200			// Set the parts of the `Idle` assignment we injected at the start of the vec above.
201			assignment_truncated[0].1 = 57_600u16.saturating_sub(total_parts);
202			assignment_truncated
203		} else {
204			assignment
205		};
206
207		let assign_core_call =
208			RelayRuntimePallets::Coretime(AssignCore(core, begin, assignment, end_hint));
209
210		let message = Xcm(vec![
211			Instruction::UnpaidExecution {
212				weight_limit: WeightLimit::Unlimited,
213				check_origin: None,
214			},
215			Instruction::Transact {
216				origin_kind: OriginKind::Native,
217				call: assign_core_call.encode().into(),
218				fallback_max_weight: Some(call_weight),
219			},
220		]);
221
222		match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
223			Ok(_) => tracing::debug!(
224				target: "runtime::coretime",
225				"Core assignment sent successfully."
226			),
227			Err(e) => tracing::error!(
228				target: "runtime::coretime", error=?e,
229				"Core assignment failed to send"
230			),
231		}
232	}
233}
234
235pub struct SovereignAccountOf;
236impl MaybeConvert<TaskId, AccountId> for SovereignAccountOf {
237	fn maybe_convert(id: TaskId) -> Option<AccountId> {
238		// Currently all tasks are parachains.
239		let location = Location::new(1, [Parachain(id)]);
240		LocationToAccountId::convert_location(&location)
241	}
242}
243
244impl pallet_broker::Config for Runtime {
245	type RuntimeEvent = RuntimeEvent;
246	type Currency = Balances;
247	type OnRevenue = AccumulateForward;
248	type TimeslicePeriod = ConstU32<{ coretime::TIMESLICE_PERIOD }>;
249	type MaxLeasedCores = ConstU32<50>;
250	type MaxReservedCores = ConstU32<50>;
251	type Coretime = CoretimeAllocator;
252	type ConvertBalance = sp_runtime::traits::Identity;
253	type WeightInfo = weights::pallet_broker::WeightInfo<Runtime>;
254	type PalletId = BrokerPalletId;
255	type AdminOrigin = EnsureRoot<AccountId>;
256	type SovereignAccountOf = SovereignAccountOf;
257	type MaxAutoRenewals = ConstU32<50>;
258	type PriceAdapter = pallet_broker::MinimumPrice<Balance, MinimumEndPrice>;
259	type MinimumCreditPurchase = MinimumCreditPurchase;
260}