Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions docs/transaction-priority.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## Transaction Priority

### Overview
In Subtensor, transaction priority is determined by custom transaction extensions, which alter or override the default Substrate SDK behavior. Extensions affecting transaction priority are:

- **`ChargeTransactionPaymentWrapper`** (wraps `ChargeTransactionPayment`)
- **`DrandPriority`**

Substrate SDK combines priorities from all transaction extensions using addition.

---

### 1. `ChargeTransactionPaymentWrapper`
In the Substrate SDK, `ChargeTransactionPayment` normally calculates transaction priority based on:
- **Tip** — an extra fee paid by the sender.
- **Weight** — computational complexity of the transaction.
- **Dispatch class** — category of the transaction (`Normal`, `Operational`, `Mandatory`).

However, in Subtensor, `ChargeTransactionPaymentWrapper` **overrides** this logic.
It replaces the dynamic calculation with a **flat priority scale** based only on the dispatch class.

#### Current priority values:
| Dispatch Class | Priority Value | Notes |
|---------------------|-------------------|--------------------------------------------------------------|
| `Normal` | `1` | Standard transactions |
| `Mandatory` | `1` | Rarely used, same as `Normal` |
| `Operational` | `10_000_000_000` | Reserved for critical system extrinsics (e.g.: `sudo` calls) |


---

### 2. `DrandPriority`

Special pallet_drand priority: 10_000 for `write_pulse` extrinsic.

---
39 changes: 23 additions & 16 deletions node/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use crate::client::FullClient;

use node_subtensor_runtime as runtime;
use node_subtensor_runtime::check_nonce;
use node_subtensor_runtime::pallet_subtensor;
use node_subtensor_runtime::{check_nonce, transaction_payment_wrapper};
use runtime::{BalancesCall, SystemCall};
use sc_cli::Result;
use sc_client_api::BlockBackend;
Expand Down Expand Up @@ -123,21 +123,27 @@ pub fn create_benchmark_extrinsic(
.checked_next_power_of_two()
.map(|c| c / 2)
.unwrap_or(2) as u64;
let extra: runtime::TransactionExtensions = (
frame_system::CheckNonZeroSender::<runtime::Runtime>::new(),
frame_system::CheckSpecVersion::<runtime::Runtime>::new(),
frame_system::CheckTxVersion::<runtime::Runtime>::new(),
frame_system::CheckGenesis::<runtime::Runtime>::new(),
frame_system::CheckEra::<runtime::Runtime>::from(sp_runtime::generic::Era::mortal(
period,
best_block.saturated_into(),
)),
check_nonce::CheckNonce::<runtime::Runtime>::from(nonce),
frame_system::CheckWeight::<runtime::Runtime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<runtime::Runtime>::from(0),
pallet_subtensor::SubtensorTransactionExtension::<runtime::Runtime>::new(),
frame_metadata_hash_extension::CheckMetadataHash::<runtime::Runtime>::new(true),
);
let extra: runtime::TransactionExtensions =
(
frame_system::CheckNonZeroSender::<runtime::Runtime>::new(),
frame_system::CheckSpecVersion::<runtime::Runtime>::new(),
frame_system::CheckTxVersion::<runtime::Runtime>::new(),
frame_system::CheckGenesis::<runtime::Runtime>::new(),
frame_system::CheckEra::<runtime::Runtime>::from(sp_runtime::generic::Era::mortal(
period,
best_block.saturated_into(),
)),
check_nonce::CheckNonce::<runtime::Runtime>::from(nonce),
frame_system::CheckWeight::<runtime::Runtime>::new(),
transaction_payment_wrapper::ChargeTransactionPaymentWrapper::new(
pallet_transaction_payment::ChargeTransactionPayment::<runtime::Runtime>::from(0),
),
pallet_subtensor::transaction_extension::SubtensorTransactionExtension::<
runtime::Runtime,
>::new(),
pallet_drand::drand_priority::DrandPriority::<runtime::Runtime>::new(),
frame_metadata_hash_extension::CheckMetadataHash::<runtime::Runtime>::new(true),
);

let raw_payload = runtime::SignedPayload::from_raw(
call.clone(),
Expand All @@ -152,6 +158,7 @@ pub fn create_benchmark_extrinsic(
(),
(),
(),
(),
None,
),
);
Expand Down
2 changes: 1 addition & 1 deletion pallets/commitments/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ pub mod pallet {
Weight::from_parts(33_480_000, 0)
.saturating_add(T::DbWeight::get().reads(5_u64))
.saturating_add(T::DbWeight::get().writes(4_u64)),
DispatchClass::Operational,
DispatchClass::Normal,
Pays::No
))]
pub fn set_commitment(
Expand Down
2 changes: 2 additions & 0 deletions pallets/drand/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ frame-system.workspace = true
sp-core.workspace = true
sp-io.workspace = true
sp-runtime.workspace = true
sp-std.workspace = true
# arkworks dependencies
sp-ark-bls12-381.workspace = true
ark-bls12-381 = { workspace = true, features = ["curve"] }
Expand Down Expand Up @@ -55,6 +56,7 @@ std = [
"frame-system/std",
"scale-info/std",
"sp-core/std",
"sp-std/std",
"sp-io/std",
"sp-keystore/std",
"sp-keyring/std",
Expand Down
89 changes: 89 additions & 0 deletions pallets/drand/src/drand_priority.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use crate::{Call, Config};
use codec::{Decode, DecodeWithMemTracking, Encode};
use frame_support::dispatch::{DispatchInfo, PostDispatchInfo};
use frame_support::pallet_prelude::Weight;
use frame_support::traits::IsSubType;
use scale_info::TypeInfo;
use sp_runtime::traits::{
DispatchInfoOf, DispatchOriginOf, Dispatchable, Implication, TransactionExtension,
ValidateResult,
};
use sp_runtime::transaction_validity::{
TransactionPriority, TransactionSource, TransactionValidityError, ValidTransaction,
};
use sp_std::marker::PhantomData;
use subtensor_macros::freeze_struct;

pub type RuntimeCallFor<T> = <T as frame_system::Config>::RuntimeCall;

#[freeze_struct("d0d094192bd6390e")]
#[derive(Default, Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
pub struct DrandPriority<T: Config + Send + Sync + TypeInfo>(pub PhantomData<T>);

impl<T: Config + Send + Sync + TypeInfo> sp_std::fmt::Debug for DrandPriority<T> {
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
write!(f, "DrandPriority")
}
}

impl<T: Config + Send + Sync + TypeInfo> DrandPriority<T> {
pub fn new() -> Self {
Self(PhantomData)
}

fn get_drand_priority() -> TransactionPriority {
10_000u64
}
}

impl<T: Config + Send + Sync + TypeInfo> TransactionExtension<RuntimeCallFor<T>>
for DrandPriority<T>
where
<T as frame_system::Config>::RuntimeCall:
Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
<T as frame_system::Config>::RuntimeCall: IsSubType<Call<T>>,
{
const IDENTIFIER: &'static str = "DrandPriority";
type Implicit = ();
type Val = ();
type Pre = ();

fn weight(&self, _call: &RuntimeCallFor<T>) -> Weight {
// TODO: benchmark transaction extension
Weight::zero()
}

fn validate(
&self,
origin: DispatchOriginOf<RuntimeCallFor<T>>,
call: &RuntimeCallFor<T>,
_info: &DispatchInfoOf<RuntimeCallFor<T>>,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl Implication,
_source: TransactionSource,
) -> ValidateResult<Self::Val, RuntimeCallFor<T>> {
match call.is_sub_type() {
Some(Call::write_pulse { .. }) => {
let validity = ValidTransaction {
priority: Self::get_drand_priority(),
..Default::default()
};

Ok((validity, (), origin))
}
_ => Ok((Default::default(), (), origin)),
}
}

fn prepare(
self,
_val: Self::Val,
_origin: &DispatchOriginOf<RuntimeCallFor<T>>,
_call: &RuntimeCallFor<T>,
_info: &DispatchInfoOf<RuntimeCallFor<T>>,
_len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
Ok(())
}
}
9 changes: 5 additions & 4 deletions pallets/drand/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ use sp_runtime::{
};

pub mod bls12_381;
pub mod drand_priority;
pub mod migrations;
pub mod types;
pub mod utils;
Expand Down Expand Up @@ -404,9 +405,9 @@ pub mod pallet {
/// * `origin`: the root user
/// * `config`: the beacon configuration
#[pallet::call_index(1)]
#[pallet::weight(Weight::from_parts(8_766_000, 0)
#[pallet::weight((Weight::from_parts(8_766_000, 0)
.saturating_add(T::DbWeight::get().reads(0_u64))
.saturating_add(T::DbWeight::get().writes(2_u64)))]
.saturating_add(T::DbWeight::get().writes(2_u64)), DispatchClass::Operational))]
pub fn set_beacon_config(
origin: OriginFor<T>,
config_payload: BeaconConfigurationPayload<T::Public, BlockNumberFor<T>>,
Expand All @@ -425,9 +426,9 @@ pub mod pallet {

/// allows the root user to set the oldest stored round
#[pallet::call_index(2)]
#[pallet::weight(Weight::from_parts(5_370_000, 0)
#[pallet::weight((Weight::from_parts(5_370_000, 0)
.saturating_add(T::DbWeight::get().reads(0_u64))
.saturating_add(T::DbWeight::get().writes(1_u64)))]
.saturating_add(T::DbWeight::get().writes(1_u64)), DispatchClass::Operational))]
pub fn set_oldest_stored_round(origin: OriginFor<T>, oldest_round: u64) -> DispatchResult {
ensure_root(origin)?;
OldestStoredRound::<T>::put(oldest_round);
Expand Down
2 changes: 1 addition & 1 deletion pallets/registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ pub mod pallet {
#[pallet::call_index(0)]
#[pallet::weight((
T::WeightInfo::set_identity(),
DispatchClass::Operational
DispatchClass::Normal
))]
pub fn set_identity(
origin: OriginFor<T>,
Expand Down
Loading