use crate::{xcm_config::LocationToAccountId, *};
use codec::{Decode, Encode};
use cumulus_pallet_parachain_system::RelaychainDataProvider;
use cumulus_primitives_core::relay_chain;
use frame_support::{
parameter_types,
traits::{
fungible::{Balanced, Credit, Inspect},
tokens::{Fortitude, Preservation},
DefensiveResult, OnUnbalanced,
},
};
use frame_system::Pallet as System;
use pallet_broker::{
CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600, RCBlockNumberOf, TaskId, Timeslice,
};
use parachains_common::{AccountId, Balance};
use sp_runtime::traits::{AccountIdConversion, MaybeConvert};
use westend_runtime_constants::system_parachain::coretime;
use xcm::latest::prelude::*;
use xcm_executor::traits::{ConvertLocation, TransactAsset};
pub struct BurnCoretimeRevenue;
impl OnUnbalanced<Credit<AccountId, Balances>> for BurnCoretimeRevenue {
fn on_nonzero_unbalanced(amount: Credit<AccountId, Balances>) {
let acc = RevenueAccumulationAccount::get();
if !System::<Runtime>::account_exists(&acc) {
System::<Runtime>::inc_providers(&acc);
}
Balances::resolve(&acc, amount).defensive_ok();
}
}
type AssetTransactor = <xcm_config::XcmConfig as xcm_executor::Config>::AssetTransactor;
fn burn_at_relay(stash: &AccountId, value: Balance) -> Result<(), XcmError> {
let dest = Location::parent();
let stash_location =
Junction::AccountId32 { network: None, id: stash.clone().into() }.into_location();
let asset = Asset { id: AssetId(Location::parent()), fun: Fungible(value) };
let dummy_xcm_context = XcmContext { origin: None, message_id: [0; 32], topic: None };
let withdrawn = AssetTransactor::withdraw_asset(&asset, &stash_location, None)?;
AssetTransactor::can_check_out(&dest, &asset, &dummy_xcm_context)?;
let parent_assets = Into::<Assets>::into(withdrawn)
.reanchored(&dest, &Here.into())
.defensive_map_err(|_| XcmError::ReanchorFailed)?;
PolkadotXcm::send_xcm(
Here,
Location::parent(),
Xcm(vec![
Instruction::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: None,
},
ReceiveTeleportedAsset(parent_assets.clone()),
BurnAsset(parent_assets),
]),
)?;
AssetTransactor::check_out(&dest, &asset, &dummy_xcm_context);
Ok(())
}
#[derive(Encode, Decode)]
enum RelayRuntimePallets {
#[codec(index = 66)]
Coretime(CoretimeProviderCalls),
}
#[derive(Encode, Decode)]
enum CoretimeProviderCalls {
#[codec(index = 1)]
RequestCoreCount(CoreIndex),
#[codec(index = 2)]
RequestRevenueInfoAt(relay_chain::BlockNumber),
#[codec(index = 3)]
CreditAccount(AccountId, Balance),
#[codec(index = 4)]
AssignCore(
CoreIndex,
relay_chain::BlockNumber,
Vec<(CoreAssignment, PartsOf57600)>,
Option<relay_chain::BlockNumber>,
),
}
parameter_types! {
pub const BrokerPalletId: PalletId = PalletId(*b"py/broke");
pub RevenueAccumulationAccount: AccountId = BrokerPalletId::get().into_sub_account_truncating(b"burnstash");
}
pub struct CoretimeAllocator;
impl CoretimeInterface for CoretimeAllocator {
type AccountId = AccountId;
type Balance = Balance;
type RelayChainBlockNumberProvider = RelaychainDataProvider<Runtime>;
fn request_core_count(count: CoreIndex) {
use crate::coretime::CoretimeProviderCalls::RequestCoreCount;
let request_core_count_call = RelayRuntimePallets::Coretime(RequestCoreCount(count));
let call_weight = Weight::from_parts(190_000_000, 1700);
let message = Xcm(vec![
Instruction::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: None,
},
Instruction::Transact {
origin_kind: OriginKind::Native,
call: request_core_count_call.encode().into(),
fallback_max_weight: Some(call_weight),
},
]);
match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
Ok(_) => log::debug!(
target: "runtime::coretime",
"Request to update schedulable cores sent successfully."
),
Err(e) => log::error!(
target: "runtime::coretime",
"Failed to send request to update schedulable cores: {:?}",
e
),
}
}
fn request_revenue_info_at(when: RCBlockNumberOf<Self>) {
use crate::coretime::CoretimeProviderCalls::RequestRevenueInfoAt;
let request_revenue_info_at_call =
RelayRuntimePallets::Coretime(RequestRevenueInfoAt(when));
let message = Xcm(vec![
Instruction::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: None,
},
Instruction::Transact {
origin_kind: OriginKind::Native,
call: request_revenue_info_at_call.encode().into(),
fallback_max_weight: Some(Weight::from_parts(1_000_000_000, 200_000)),
},
]);
match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
Ok(_) => log::debug!(
target: "runtime::coretime",
"Request for revenue information sent successfully."
),
Err(e) => log::error!(
target: "runtime::coretime",
"Request for revenue information failed to send: {:?}",
e
),
}
}
fn credit_account(who: Self::AccountId, amount: Self::Balance) {
use crate::coretime::CoretimeProviderCalls::CreditAccount;
let credit_account_call = RelayRuntimePallets::Coretime(CreditAccount(who, amount));
let message = Xcm(vec![
Instruction::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: None,
},
Instruction::Transact {
origin_kind: OriginKind::Native,
call: credit_account_call.encode().into(),
fallback_max_weight: Some(Weight::from_parts(1_000_000_000, 200_000)),
},
]);
match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
Ok(_) => log::debug!(
target: "runtime::coretime",
"Instruction to credit account sent successfully."
),
Err(e) => log::error!(
target: "runtime::coretime",
"Instruction to credit account failed to send: {:?}",
e
),
}
}
fn assign_core(
core: CoreIndex,
begin: RCBlockNumberOf<Self>,
assignment: Vec<(CoreAssignment, PartsOf57600)>,
end_hint: Option<RCBlockNumberOf<Self>>,
) {
use crate::coretime::CoretimeProviderCalls::AssignCore;
let call_weight = Weight::from_parts(980_000_000, 3800);
let assignment = if assignment.len() > 28 {
let mut total_parts = 0u16;
let mut assignment_truncated = vec![(CoreAssignment::Idle, 0)];
assignment_truncated.extend(
assignment
.into_iter()
.filter(|(a, _)| *a != CoreAssignment::Idle)
.take(27)
.inspect(|(_, parts)| total_parts += *parts)
.collect::<Vec<_>>(),
);
assignment_truncated[0].1 = 57_600u16.saturating_sub(total_parts);
assignment_truncated
} else {
assignment
};
let assign_core_call =
RelayRuntimePallets::Coretime(AssignCore(core, begin, assignment, end_hint));
let message = Xcm(vec![
Instruction::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: None,
},
Instruction::Transact {
origin_kind: OriginKind::Native,
call: assign_core_call.encode().into(),
fallback_max_weight: Some(call_weight),
},
]);
match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) {
Ok(_) => log::debug!(
target: "runtime::coretime",
"Core assignment sent successfully."
),
Err(e) => log::error!(
target: "runtime::coretime",
"Core assignment failed to send: {:?}",
e
),
}
}
fn on_new_timeslice(_timeslice: Timeslice) {
let stash = RevenueAccumulationAccount::get();
let value =
Balances::reducible_balance(&stash, Preservation::Expendable, Fortitude::Polite);
if value > 0 {
log::debug!(target: "runtime::coretime", "Going to burn {value} stashed tokens at RC");
match burn_at_relay(&stash, value) {
Ok(()) => {
log::debug!(target: "runtime::coretime", "Succesfully burnt {value} tokens");
},
Err(err) => {
log::error!(target: "runtime::coretime", "burn_at_relay failed: {err:?}");
},
}
}
}
}
pub struct SovereignAccountOf;
impl MaybeConvert<TaskId, AccountId> for SovereignAccountOf {
fn maybe_convert(id: TaskId) -> Option<AccountId> {
let location = Location::new(1, [Parachain(id)]);
LocationToAccountId::convert_location(&location)
}
}
impl pallet_broker::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type OnRevenue = BurnCoretimeRevenue;
type TimeslicePeriod = ConstU32<{ coretime::TIMESLICE_PERIOD }>;
type MaxLeasedCores = ConstU32<10>;
type MaxReservedCores = ConstU32<10>;
type Coretime = CoretimeAllocator;
type ConvertBalance = sp_runtime::traits::Identity;
type WeightInfo = weights::pallet_broker::WeightInfo<Runtime>;
type PalletId = BrokerPalletId;
type AdminOrigin = EnsureRoot<AccountId>;
type SovereignAccountOf = SovereignAccountOf;
type MaxAutoRenewals = ConstU32<20>;
type PriceAdapter = pallet_broker::CenterTargetPrice<Balance>;
}