coretime_westend_runtime/
coretime.rs1use crate::{xcm_config::LocationToAccountId, *};
18use codec::{Decode, Encode};
19use cumulus_pallet_parachain_system::RelaychainDataProvider;
20use cumulus_primitives_core::relay_chain;
21use frame_support::{
22 parameter_types,
23 traits::{
24 fungible::{Balanced, Credit, Inspect},
25 tokens::{Fortitude, Preservation},
26 DefensiveResult, OnUnbalanced,
27 },
28};
29use frame_system::Pallet as System;
30use pallet_broker::{
31 CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600, RCBlockNumberOf, TaskId, Timeslice,
32};
33use parachains_common::{AccountId, Balance};
34use sp_runtime::traits::{AccountIdConversion, MaybeConvert};
35use westend_runtime_constants::system_parachain::coretime;
36use xcm::latest::prelude::*;
37use xcm_executor::traits::{ConvertLocation, TransactAsset};
38
39pub struct BurnCoretimeRevenue;
40impl OnUnbalanced<Credit<AccountId, Balances>> for BurnCoretimeRevenue {
41 fn on_nonzero_unbalanced(amount: Credit<AccountId, Balances>) {
42 let acc = RevenueAccumulationAccount::get();
43 if !System::<Runtime>::account_exists(&acc) {
44 System::<Runtime>::inc_providers(&acc);
45 }
46 Balances::resolve(&acc, amount).defensive_ok();
47 }
48}
49
50type AssetTransactor = <xcm_config::XcmConfig as xcm_executor::Config>::AssetTransactor;
51
52fn burn_at_relay(stash: &AccountId, value: Balance) -> Result<(), XcmError> {
53 let dest = Location::parent();
54 let stash_location =
55 Junction::AccountId32 { network: None, id: stash.clone().into() }.into_location();
56 let asset = Asset { id: AssetId(Location::parent()), fun: Fungible(value) };
57 let dummy_xcm_context = XcmContext { origin: None, message_id: [0; 32], topic: None };
58
59 let withdrawn = AssetTransactor::withdraw_asset(&asset, &stash_location, None)?;
60
61 AssetTransactor::can_check_out(&dest, &asset, &dummy_xcm_context)?;
62
63 let parent_assets = Into::<Assets>::into(withdrawn)
64 .reanchored(&dest, &Here.into())
65 .defensive_map_err(|_| XcmError::ReanchorFailed)?;
66
67 PolkadotXcm::send_xcm(
68 Here,
69 Location::parent(),
70 Xcm(vec![
71 Instruction::UnpaidExecution {
72 weight_limit: WeightLimit::Unlimited,
73 check_origin: None,
74 },
75 ReceiveTeleportedAsset(parent_assets.clone()),
76 BurnAsset(parent_assets),
77 ]),
78 )?;
79
80 AssetTransactor::check_out(&dest, &asset, &dummy_xcm_context);
81
82 Ok(())
83}
84
85#[derive(Encode, Decode)]
89enum RelayRuntimePallets {
90 #[codec(index = 66)]
91 Coretime(CoretimeProviderCalls),
92}
93
94#[derive(Encode, Decode)]
96enum CoretimeProviderCalls {
97 #[codec(index = 1)]
98 RequestCoreCount(CoreIndex),
99 #[codec(index = 2)]
100 RequestRevenueInfoAt(relay_chain::BlockNumber),
101 #[codec(index = 3)]
102 CreditAccount(AccountId, Balance),
103 #[codec(index = 4)]
104 AssignCore(
105 CoreIndex,
106 relay_chain::BlockNumber,
107 Vec<(CoreAssignment, PartsOf57600)>,
108 Option<relay_chain::BlockNumber>,
109 ),
110}
111
112parameter_types! {
113 pub const BrokerPalletId: PalletId = PalletId(*b"py/broke");
114 pub const MinimumCreditPurchase: Balance = UNITS / 10;
115 pub RevenueAccumulationAccount: AccountId = BrokerPalletId::get().into_sub_account_truncating(b"burnstash");
116 pub const MinimumEndPrice: Balance = UNITS;
117}
118
119pub struct CoretimeAllocator;
123impl CoretimeInterface for CoretimeAllocator {
124 type AccountId = AccountId;
125 type Balance = Balance;
126 type RelayChainBlockNumberProvider = RelaychainDataProvider<Runtime>;
127
128 fn request_core_count(count: CoreIndex) {
129 use crate::coretime::CoretimeProviderCalls::RequestCoreCount;
130 let request_core_count_call = RelayRuntimePallets::Coretime(RequestCoreCount(count));
131
132 let call_weight = Weight::from_parts(190_000_000, 1700);
137
138 let message = Xcm(vec![
139 Instruction::UnpaidExecution {
140 weight_limit: WeightLimit::Unlimited,
141 check_origin: None,
142 },
143 Instruction::Transact {
144 origin_kind: OriginKind::Native,
145 call: request_core_count_call.encode().into(),
146 fallback_max_weight: Some(call_weight),
147 },
148 ]);
149
150 match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
151 Ok(_) => log::debug!(
152 target: "runtime::coretime",
153 "Request to update schedulable cores sent successfully."
154 ),
155 Err(e) => log::error!(
156 target: "runtime::coretime",
157 "Failed to send request to update schedulable cores: {:?}",
158 e
159 ),
160 }
161 }
162
163 fn request_revenue_info_at(when: RCBlockNumberOf<Self>) {
164 use crate::coretime::CoretimeProviderCalls::RequestRevenueInfoAt;
165 let request_revenue_info_at_call =
166 RelayRuntimePallets::Coretime(RequestRevenueInfoAt(when));
167
168 let message = Xcm(vec![
169 Instruction::UnpaidExecution {
170 weight_limit: WeightLimit::Unlimited,
171 check_origin: None,
172 },
173 Instruction::Transact {
174 origin_kind: OriginKind::Native,
175 call: request_revenue_info_at_call.encode().into(),
176 fallback_max_weight: Some(Weight::from_parts(1_000_000_000, 200_000)),
177 },
178 ]);
179
180 match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
181 Ok(_) => log::debug!(
182 target: "runtime::coretime",
183 "Request for revenue information sent successfully."
184 ),
185 Err(e) => log::error!(
186 target: "runtime::coretime",
187 "Request for revenue information failed to send: {:?}",
188 e
189 ),
190 }
191 }
192
193 fn credit_account(who: Self::AccountId, amount: Self::Balance) {
194 use crate::coretime::CoretimeProviderCalls::CreditAccount;
195 let credit_account_call = RelayRuntimePallets::Coretime(CreditAccount(who, amount));
196
197 let message = Xcm(vec![
198 Instruction::UnpaidExecution {
199 weight_limit: WeightLimit::Unlimited,
200 check_origin: None,
201 },
202 Instruction::Transact {
203 origin_kind: OriginKind::Native,
204 call: credit_account_call.encode().into(),
205 fallback_max_weight: Some(Weight::from_parts(1_000_000_000, 200_000)),
206 },
207 ]);
208
209 match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
210 Ok(_) => log::debug!(
211 target: "runtime::coretime",
212 "Instruction to credit account sent successfully."
213 ),
214 Err(e) => log::error!(
215 target: "runtime::coretime",
216 "Instruction to credit account failed to send: {:?}",
217 e
218 ),
219 }
220 }
221
222 fn assign_core(
223 core: CoreIndex,
224 begin: RCBlockNumberOf<Self>,
225 assignment: Vec<(CoreAssignment, PartsOf57600)>,
226 end_hint: Option<RCBlockNumberOf<Self>>,
227 ) {
228 use crate::coretime::CoretimeProviderCalls::AssignCore;
229
230 let call_weight = Weight::from_parts(980_000_000, 3800);
235
236 let assignment = if assignment.len() > 28 {
243 let mut total_parts = 0u16;
244 let mut assignment_truncated = vec![(CoreAssignment::Idle, 0)];
248 assignment_truncated.extend(
250 assignment
251 .into_iter()
252 .filter(|(a, _)| *a != CoreAssignment::Idle)
253 .take(27)
254 .inspect(|(_, parts)| total_parts += *parts)
255 .collect::<Vec<_>>(),
256 );
257
258 assignment_truncated[0].1 = 57_600u16.saturating_sub(total_parts);
260 assignment_truncated
261 } else {
262 assignment
263 };
264
265 let assign_core_call =
266 RelayRuntimePallets::Coretime(AssignCore(core, begin, assignment, end_hint));
267
268 let message = Xcm(vec![
269 Instruction::UnpaidExecution {
270 weight_limit: WeightLimit::Unlimited,
271 check_origin: None,
272 },
273 Instruction::Transact {
274 origin_kind: OriginKind::Native,
275 call: assign_core_call.encode().into(),
276 fallback_max_weight: Some(call_weight),
277 },
278 ]);
279
280 match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
281 Ok(_) => log::debug!(
282 target: "runtime::coretime",
283 "Core assignment sent successfully."
284 ),
285 Err(e) => log::error!(
286 target: "runtime::coretime",
287 "Core assignment failed to send: {:?}",
288 e
289 ),
290 }
291 }
292
293 fn on_new_timeslice(_timeslice: Timeslice) {
294 let stash = RevenueAccumulationAccount::get();
295 let value =
296 Balances::reducible_balance(&stash, Preservation::Expendable, Fortitude::Polite);
297
298 if value > 0 {
299 log::debug!(target: "runtime::coretime", "Going to burn {value} stashed tokens at RC");
300 match burn_at_relay(&stash, value) {
301 Ok(()) => {
302 log::debug!(target: "runtime::coretime", "Succesfully burnt {value} tokens");
303 },
304 Err(err) => {
305 log::error!(target: "runtime::coretime", "burn_at_relay failed: {err:?}");
306 },
307 }
308 }
309 }
310}
311
312pub struct SovereignAccountOf;
313impl MaybeConvert<TaskId, AccountId> for SovereignAccountOf {
314 fn maybe_convert(id: TaskId) -> Option<AccountId> {
315 let location = Location::new(1, [Parachain(id)]);
317 LocationToAccountId::convert_location(&location)
318 }
319}
320
321impl pallet_broker::Config for Runtime {
322 type RuntimeEvent = RuntimeEvent;
323 type Currency = Balances;
324 type OnRevenue = BurnCoretimeRevenue;
325 type TimeslicePeriod = ConstU32<{ coretime::TIMESLICE_PERIOD }>;
326 type MaxLeasedCores = ConstU32<10>;
328 type MaxReservedCores = ConstU32<10>;
329 type Coretime = CoretimeAllocator;
330 type ConvertBalance = sp_runtime::traits::Identity;
331 type WeightInfo = weights::pallet_broker::WeightInfo<Runtime>;
332 type PalletId = BrokerPalletId;
333 type AdminOrigin = EnsureRoot<AccountId>;
334 type SovereignAccountOf = SovereignAccountOf;
335 type MaxAutoRenewals = ConstU32<20>;
336 type PriceAdapter = pallet_broker::MinimumPrice<Balance, MinimumEndPrice>;
337 type MinimumCreditPurchase = MinimumCreditPurchase;
338}