Scheduler - prioritization fees/cost (#34888)
This commit is contained in:
parent
864f29e938
commit
1517d22ecc
|
@ -191,7 +191,7 @@ impl PrioGraphScheduler {
|
||||||
saturating_add_assign!(num_scheduled, 1);
|
saturating_add_assign!(num_scheduled, 1);
|
||||||
|
|
||||||
let sanitized_transaction_ttl = transaction_state.transition_to_pending();
|
let sanitized_transaction_ttl = transaction_state.transition_to_pending();
|
||||||
let cost = transaction_state.transaction_cost().sum();
|
let cost = transaction_state.cost();
|
||||||
|
|
||||||
let SanitizedTransactionTTL {
|
let SanitizedTransactionTTL {
|
||||||
transaction,
|
transaction,
|
||||||
|
@ -490,12 +490,9 @@ mod tests {
|
||||||
crate::banking_stage::consumer::TARGET_NUM_TRANSACTIONS_PER_BATCH,
|
crate::banking_stage::consumer::TARGET_NUM_TRANSACTIONS_PER_BATCH,
|
||||||
crossbeam_channel::{unbounded, Receiver},
|
crossbeam_channel::{unbounded, Receiver},
|
||||||
itertools::Itertools,
|
itertools::Itertools,
|
||||||
solana_cost_model::cost_model::CostModel,
|
|
||||||
solana_runtime::compute_budget_details::ComputeBudgetDetails,
|
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
compute_budget::ComputeBudgetInstruction, feature_set::FeatureSet, hash::Hash,
|
compute_budget::ComputeBudgetInstruction, hash::Hash, message::Message, pubkey::Pubkey,
|
||||||
message::Message, pubkey::Pubkey, signature::Keypair, signer::Signer,
|
signature::Keypair, signer::Signer, system_instruction, transaction::Transaction,
|
||||||
system_instruction, transaction::Transaction,
|
|
||||||
},
|
},
|
||||||
std::borrow::Borrow,
|
std::borrow::Borrow,
|
||||||
};
|
};
|
||||||
|
@ -572,19 +569,16 @@ mod tests {
|
||||||
lamports,
|
lamports,
|
||||||
compute_unit_price,
|
compute_unit_price,
|
||||||
);
|
);
|
||||||
let transaction_cost = CostModel::calculate_cost(&transaction, &FeatureSet::default());
|
|
||||||
let transaction_ttl = SanitizedTransactionTTL {
|
let transaction_ttl = SanitizedTransactionTTL {
|
||||||
transaction,
|
transaction,
|
||||||
max_age_slot: Slot::MAX,
|
max_age_slot: Slot::MAX,
|
||||||
};
|
};
|
||||||
|
const TEST_TRANSACTION_COST: u64 = 5000;
|
||||||
container.insert_new_transaction(
|
container.insert_new_transaction(
|
||||||
id,
|
id,
|
||||||
transaction_ttl,
|
transaction_ttl,
|
||||||
ComputeBudgetDetails {
|
compute_unit_price,
|
||||||
compute_unit_price,
|
TEST_TRANSACTION_COST,
|
||||||
compute_unit_limit: 1,
|
|
||||||
},
|
|
||||||
transaction_cost,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,12 @@ use {
|
||||||
itertools::MinMaxResult,
|
itertools::MinMaxResult,
|
||||||
solana_cost_model::cost_model::CostModel,
|
solana_cost_model::cost_model::CostModel,
|
||||||
solana_measure::measure_us,
|
solana_measure::measure_us,
|
||||||
|
solana_program_runtime::compute_budget_processor::process_compute_budget_instructions,
|
||||||
solana_runtime::{bank::Bank, bank_forks::BankForks},
|
solana_runtime::{bank::Bank, bank_forks::BankForks},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
clock::MAX_PROCESSING_AGE, saturating_add_assign, timing::AtomicInterval,
|
clock::MAX_PROCESSING_AGE,
|
||||||
transaction::SanitizedTransaction,
|
feature_set::include_loaded_accounts_data_size_in_fee_calculation, fee::FeeBudgetLimits,
|
||||||
|
saturating_add_assign, timing::AtomicInterval, transaction::SanitizedTransaction,
|
||||||
},
|
},
|
||||||
solana_svm::transaction_error_metrics::TransactionErrorMetrics,
|
solana_svm::transaction_error_metrics::TransactionErrorMetrics,
|
||||||
std::{
|
std::{
|
||||||
|
@ -100,7 +102,7 @@ impl SchedulerController {
|
||||||
// Reset intervals when appropriate, regardless of report.
|
// Reset intervals when appropriate, regardless of report.
|
||||||
let should_report = self.count_metrics.has_data();
|
let should_report = self.count_metrics.has_data();
|
||||||
self.count_metrics
|
self.count_metrics
|
||||||
.update_prioritization_stats(self.container.get_min_max_prioritization_fees());
|
.update_priority_stats(self.container.get_min_max_priority());
|
||||||
self.count_metrics.maybe_report_and_reset(should_report);
|
self.count_metrics.maybe_report_and_reset(should_report);
|
||||||
self.timing_metrics.maybe_report_and_reset(should_report);
|
self.timing_metrics.maybe_report_and_reset(should_report);
|
||||||
self.worker_metrics
|
self.worker_metrics
|
||||||
|
@ -311,21 +313,24 @@ impl SchedulerController {
|
||||||
let mut error_counts = TransactionErrorMetrics::default();
|
let mut error_counts = TransactionErrorMetrics::default();
|
||||||
for chunk in packets.chunks(CHUNK_SIZE) {
|
for chunk in packets.chunks(CHUNK_SIZE) {
|
||||||
let mut post_sanitization_count: usize = 0;
|
let mut post_sanitization_count: usize = 0;
|
||||||
let (transactions, compute_budget_details): (Vec<_>, Vec<_>) = chunk
|
let (transactions, fee_budget_limits_vec): (Vec<_>, Vec<_>) = chunk
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|packet| {
|
.filter_map(|packet| {
|
||||||
packet
|
packet.build_sanitized_transaction(feature_set, vote_only, bank.as_ref())
|
||||||
.build_sanitized_transaction(feature_set, vote_only, bank.as_ref())
|
|
||||||
.map(|tx| (tx, packet.compute_budget_details()))
|
|
||||||
})
|
})
|
||||||
.inspect(|_| saturating_add_assign!(post_sanitization_count, 1))
|
.inspect(|_| saturating_add_assign!(post_sanitization_count, 1))
|
||||||
.filter(|(tx, _)| {
|
.filter(|tx| {
|
||||||
SanitizedTransaction::validate_account_locks(
|
SanitizedTransaction::validate_account_locks(
|
||||||
tx.message(),
|
tx.message(),
|
||||||
transaction_account_lock_limit,
|
transaction_account_lock_limit,
|
||||||
)
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
})
|
})
|
||||||
|
.filter_map(|tx| {
|
||||||
|
process_compute_budget_instructions(tx.message().program_instructions_iter())
|
||||||
|
.map(|compute_budget| (tx, compute_budget.into()))
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
.unzip();
|
.unzip();
|
||||||
|
|
||||||
let check_results = bank.check_transactions(
|
let check_results = bank.check_transactions(
|
||||||
|
@ -337,16 +342,17 @@ impl SchedulerController {
|
||||||
let post_lock_validation_count = transactions.len();
|
let post_lock_validation_count = transactions.len();
|
||||||
|
|
||||||
let mut post_transaction_check_count: usize = 0;
|
let mut post_transaction_check_count: usize = 0;
|
||||||
for ((transaction, compute_budget_details), _) in transactions
|
for ((transaction, fee_budget_limits), _) in transactions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(compute_budget_details)
|
.zip(fee_budget_limits_vec)
|
||||||
.zip(check_results)
|
.zip(check_results)
|
||||||
.filter(|(_, check_result)| check_result.0.is_ok())
|
.filter(|(_, check_result)| check_result.0.is_ok())
|
||||||
{
|
{
|
||||||
saturating_add_assign!(post_transaction_check_count, 1);
|
saturating_add_assign!(post_transaction_check_count, 1);
|
||||||
let transaction_id = self.transaction_id_generator.next();
|
let transaction_id = self.transaction_id_generator.next();
|
||||||
|
|
||||||
let transaction_cost = CostModel::calculate_cost(&transaction, &bank.feature_set);
|
let (priority, cost) =
|
||||||
|
Self::calculate_priority_and_cost(&transaction, &fee_budget_limits, &bank);
|
||||||
let transaction_ttl = SanitizedTransactionTTL {
|
let transaction_ttl = SanitizedTransactionTTL {
|
||||||
transaction,
|
transaction,
|
||||||
max_age_slot: last_slot_in_epoch,
|
max_age_slot: last_slot_in_epoch,
|
||||||
|
@ -355,8 +361,8 @@ impl SchedulerController {
|
||||||
if self.container.insert_new_transaction(
|
if self.container.insert_new_transaction(
|
||||||
transaction_id,
|
transaction_id,
|
||||||
transaction_ttl,
|
transaction_ttl,
|
||||||
compute_budget_details,
|
priority,
|
||||||
transaction_cost,
|
cost,
|
||||||
) {
|
) {
|
||||||
saturating_add_assign!(self.count_metrics.num_dropped_on_capacity, 1);
|
saturating_add_assign!(self.count_metrics.num_dropped_on_capacity, 1);
|
||||||
}
|
}
|
||||||
|
@ -384,6 +390,51 @@ impl SchedulerController {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate priority and cost for a transaction:
|
||||||
|
///
|
||||||
|
/// Cost is calculated through the `CostModel`,
|
||||||
|
/// and priority is calculated through a formula here that attempts to sell
|
||||||
|
/// blockspace to the highest bidder.
|
||||||
|
///
|
||||||
|
/// The priority is calculated as:
|
||||||
|
/// P = R / (1 + C)
|
||||||
|
/// where P is the priority, R is the reward,
|
||||||
|
/// and C is the cost towards block-limits.
|
||||||
|
///
|
||||||
|
/// Current minimum costs are on the order of several hundred,
|
||||||
|
/// so the denominator is effectively C, and the +1 is simply
|
||||||
|
/// to avoid any division by zero due to a bug - these costs
|
||||||
|
/// are calculated by the cost-model and are not direct
|
||||||
|
/// from user input. They should never be zero.
|
||||||
|
/// Any difference in the prioritization is negligible for
|
||||||
|
/// the current transaction costs.
|
||||||
|
fn calculate_priority_and_cost(
|
||||||
|
transaction: &SanitizedTransaction,
|
||||||
|
fee_budget_limits: &FeeBudgetLimits,
|
||||||
|
bank: &Bank,
|
||||||
|
) -> (u64, u64) {
|
||||||
|
let cost = CostModel::calculate_cost(transaction, &bank.feature_set).sum();
|
||||||
|
let fee = bank.fee_structure.calculate_fee(
|
||||||
|
transaction.message(),
|
||||||
|
5_000, // this just needs to be non-zero
|
||||||
|
fee_budget_limits,
|
||||||
|
bank.feature_set
|
||||||
|
.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// We need a multiplier here to avoid rounding down too aggressively.
|
||||||
|
// For many transactions, the cost will be greater than the fees in terms of raw lamports.
|
||||||
|
// For the purposes of calculating prioritization, we multiply the fees by a large number so that
|
||||||
|
// the cost is a small fraction.
|
||||||
|
// An offset of 1 is used in the denominator to explicitly avoid division by zero.
|
||||||
|
const MULTIPLIER: u64 = 1_000_000;
|
||||||
|
(
|
||||||
|
fee.saturating_mul(MULTIPLIER)
|
||||||
|
.saturating_div(cost.saturating_add(1)),
|
||||||
|
cost,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -475,16 +526,8 @@ impl SchedulerCountMetrics {
|
||||||
i64
|
i64
|
||||||
),
|
),
|
||||||
("num_dropped_on_capacity", self.num_dropped_on_capacity, i64),
|
("num_dropped_on_capacity", self.num_dropped_on_capacity, i64),
|
||||||
(
|
("min_priority", self.get_min_priority(), i64),
|
||||||
"min_prioritization_fees",
|
("max_priority", self.get_max_priority(), i64)
|
||||||
self.get_min_prioritization_fees(),
|
|
||||||
i64
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"max_prioritization_fees",
|
|
||||||
self.get_max_prioritization_fees(),
|
|
||||||
i64
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,8 +567,8 @@ impl SchedulerCountMetrics {
|
||||||
self.max_prioritization_fees = 0;
|
self.max_prioritization_fees = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_prioritization_stats(&mut self, min_max_fees: MinMaxResult<u64>) {
|
pub fn update_priority_stats(&mut self, min_max_fees: MinMaxResult<u64>) {
|
||||||
// update min/max priotization fees
|
// update min/max priority
|
||||||
match min_max_fees {
|
match min_max_fees {
|
||||||
itertools::MinMaxResult::NoElements => {
|
itertools::MinMaxResult::NoElements => {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
@ -541,7 +584,7 @@ impl SchedulerCountMetrics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_min_prioritization_fees(&self) -> u64 {
|
pub fn get_min_priority(&self) -> u64 {
|
||||||
// to avoid getting u64::max recorded by metrics / in case of edge cases
|
// to avoid getting u64::max recorded by metrics / in case of edge cases
|
||||||
if self.min_prioritization_fees != u64::MAX {
|
if self.min_prioritization_fees != u64::MAX {
|
||||||
self.min_prioritization_fees
|
self.min_prioritization_fees
|
||||||
|
@ -550,7 +593,7 @@ impl SchedulerCountMetrics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_max_prioritization_fees(&self) -> u64 {
|
pub fn get_max_priority(&self) -> u64 {
|
||||||
self.max_prioritization_fees
|
self.max_prioritization_fees
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -728,7 +771,7 @@ mod tests {
|
||||||
from_keypair: &Keypair,
|
from_keypair: &Keypair,
|
||||||
to_pubkey: &Pubkey,
|
to_pubkey: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
priority: u64,
|
compute_unit_price: u64,
|
||||||
recent_blockhash: Hash,
|
recent_blockhash: Hash,
|
||||||
) -> Transaction {
|
) -> Transaction {
|
||||||
// Fund the sending key, so that the transaction does not get filtered by the fee-payer check.
|
// Fund the sending key, so that the transaction does not get filtered by the fee-payer check.
|
||||||
|
@ -743,7 +786,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let transfer = system_instruction::transfer(&from_keypair.pubkey(), to_pubkey, lamports);
|
let transfer = system_instruction::transfer(&from_keypair.pubkey(), to_pubkey, lamports);
|
||||||
let prioritization = ComputeBudgetInstruction::set_compute_unit_price(priority);
|
let prioritization = ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price);
|
||||||
let message = Message::new(&[transfer, prioritization], Some(&from_keypair.pubkey()));
|
let message = Message::new(&[transfer, prioritization], Some(&from_keypair.pubkey()));
|
||||||
Transaction::new(&vec![from_keypair], message, recent_blockhash)
|
Transaction::new(&vec![from_keypair], message, recent_blockhash)
|
||||||
}
|
}
|
||||||
|
@ -999,7 +1042,7 @@ mod tests {
|
||||||
&Keypair::new(),
|
&Keypair::new(),
|
||||||
&Pubkey::new_unique(),
|
&Pubkey::new_unique(),
|
||||||
1,
|
1,
|
||||||
i,
|
i * 10,
|
||||||
bank.last_blockhash(),
|
bank.last_blockhash(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
use {
|
use solana_sdk::{clock::Slot, transaction::SanitizedTransaction};
|
||||||
solana_cost_model::transaction_cost::TransactionCost,
|
|
||||||
solana_runtime::compute_budget_details::ComputeBudgetDetails,
|
|
||||||
solana_sdk::{slot_history::Slot, transaction::SanitizedTransaction},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Simple wrapper type to tie a sanitized transaction to max age slot.
|
/// Simple wrapper type to tie a sanitized transaction to max age slot.
|
||||||
pub(crate) struct SanitizedTransactionTTL {
|
pub(crate) struct SanitizedTransactionTTL {
|
||||||
|
@ -34,77 +30,38 @@ pub(crate) enum TransactionState {
|
||||||
/// The transaction is available for scheduling.
|
/// The transaction is available for scheduling.
|
||||||
Unprocessed {
|
Unprocessed {
|
||||||
transaction_ttl: SanitizedTransactionTTL,
|
transaction_ttl: SanitizedTransactionTTL,
|
||||||
compute_budget_details: ComputeBudgetDetails,
|
priority: u64,
|
||||||
transaction_cost: TransactionCost,
|
cost: u64,
|
||||||
forwarded: bool,
|
|
||||||
},
|
},
|
||||||
/// The transaction is currently scheduled or being processed.
|
/// The transaction is currently scheduled or being processed.
|
||||||
Pending {
|
Pending { priority: u64, cost: u64 },
|
||||||
compute_budget_details: ComputeBudgetDetails,
|
|
||||||
transaction_cost: TransactionCost,
|
|
||||||
forwarded: bool,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionState {
|
impl TransactionState {
|
||||||
/// Creates a new `TransactionState` in the `Unprocessed` state.
|
/// Creates a new `TransactionState` in the `Unprocessed` state.
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(transaction_ttl: SanitizedTransactionTTL, priority: u64, cost: u64) -> Self {
|
||||||
transaction_ttl: SanitizedTransactionTTL,
|
|
||||||
compute_budget_details: ComputeBudgetDetails,
|
|
||||||
transaction_cost: TransactionCost,
|
|
||||||
) -> Self {
|
|
||||||
Self::Unprocessed {
|
Self::Unprocessed {
|
||||||
transaction_ttl,
|
transaction_ttl,
|
||||||
compute_budget_details,
|
priority,
|
||||||
transaction_cost,
|
cost,
|
||||||
forwarded: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the compute budget details of the transaction.
|
/// Return the priority of the transaction.
|
||||||
pub(crate) fn compute_budget_details(&self) -> &ComputeBudgetDetails {
|
/// This is *not* the same as the `compute_unit_price` of the transaction.
|
||||||
|
/// The priority is used to order transactions for processing.
|
||||||
|
pub(crate) fn priority(&self) -> u64 {
|
||||||
match self {
|
match self {
|
||||||
Self::Unprocessed {
|
Self::Unprocessed { priority, .. } => *priority,
|
||||||
compute_budget_details,
|
Self::Pending { priority, .. } => *priority,
|
||||||
..
|
|
||||||
} => compute_budget_details,
|
|
||||||
Self::Pending {
|
|
||||||
compute_budget_details,
|
|
||||||
..
|
|
||||||
} => compute_budget_details,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the transaction cost of the transaction.
|
/// Return the cost of the transaction.
|
||||||
pub(crate) fn transaction_cost(&self) -> &TransactionCost {
|
pub(crate) fn cost(&self) -> u64 {
|
||||||
match self {
|
match self {
|
||||||
Self::Unprocessed {
|
Self::Unprocessed { cost, .. } => *cost,
|
||||||
transaction_cost, ..
|
Self::Pending { cost, .. } => *cost,
|
||||||
} => transaction_cost,
|
|
||||||
Self::Pending {
|
|
||||||
transaction_cost, ..
|
|
||||||
} => transaction_cost,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the compute unit price of the transaction.
|
|
||||||
pub(crate) fn compute_unit_price(&self) -> u64 {
|
|
||||||
self.compute_budget_details().compute_unit_price
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether or not the transaction has already been forwarded.
|
|
||||||
pub(crate) fn forwarded(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Unprocessed { forwarded, .. } => *forwarded,
|
|
||||||
Self::Pending { forwarded, .. } => *forwarded,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the transaction as forwarded.
|
|
||||||
pub(crate) fn set_forwarded(&mut self) {
|
|
||||||
match self {
|
|
||||||
Self::Unprocessed { forwarded, .. } => *forwarded = true,
|
|
||||||
Self::Pending { forwarded, .. } => *forwarded = true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,15 +76,10 @@ impl TransactionState {
|
||||||
match self.take() {
|
match self.take() {
|
||||||
TransactionState::Unprocessed {
|
TransactionState::Unprocessed {
|
||||||
transaction_ttl,
|
transaction_ttl,
|
||||||
compute_budget_details,
|
priority,
|
||||||
transaction_cost,
|
cost,
|
||||||
forwarded,
|
|
||||||
} => {
|
} => {
|
||||||
*self = TransactionState::Pending {
|
*self = TransactionState::Pending { priority, cost };
|
||||||
compute_budget_details,
|
|
||||||
transaction_cost,
|
|
||||||
forwarded,
|
|
||||||
};
|
|
||||||
transaction_ttl
|
transaction_ttl
|
||||||
}
|
}
|
||||||
TransactionState::Pending { .. } => {
|
TransactionState::Pending { .. } => {
|
||||||
|
@ -145,16 +97,11 @@ impl TransactionState {
|
||||||
pub(crate) fn transition_to_unprocessed(&mut self, transaction_ttl: SanitizedTransactionTTL) {
|
pub(crate) fn transition_to_unprocessed(&mut self, transaction_ttl: SanitizedTransactionTTL) {
|
||||||
match self.take() {
|
match self.take() {
|
||||||
TransactionState::Unprocessed { .. } => panic!("already unprocessed"),
|
TransactionState::Unprocessed { .. } => panic!("already unprocessed"),
|
||||||
TransactionState::Pending {
|
TransactionState::Pending { priority, cost } => {
|
||||||
compute_budget_details,
|
|
||||||
transaction_cost,
|
|
||||||
forwarded,
|
|
||||||
} => {
|
|
||||||
*self = Self::Unprocessed {
|
*self = Self::Unprocessed {
|
||||||
transaction_ttl,
|
transaction_ttl,
|
||||||
compute_budget_details,
|
priority,
|
||||||
transaction_cost,
|
cost,
|
||||||
forwarded,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,14 +126,8 @@ impl TransactionState {
|
||||||
core::mem::replace(
|
core::mem::replace(
|
||||||
self,
|
self,
|
||||||
Self::Pending {
|
Self::Pending {
|
||||||
compute_budget_details: ComputeBudgetDetails {
|
priority: 0,
|
||||||
compute_unit_price: 0,
|
cost: 0,
|
||||||
compute_unit_limit: 0,
|
|
||||||
},
|
|
||||||
transaction_cost: TransactionCost::SimpleVote {
|
|
||||||
writable_accounts: vec![],
|
|
||||||
},
|
|
||||||
forwarded: false,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -196,7 +137,6 @@ impl TransactionState {
|
||||||
mod tests {
|
mod tests {
|
||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
solana_cost_model::transaction_cost::UsageCostDetails,
|
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
compute_budget::ComputeBudgetInstruction, hash::Hash, message::Message,
|
compute_budget::ComputeBudgetInstruction, hash::Hash, message::Message,
|
||||||
signature::Keypair, signer::Signer, system_instruction, transaction::Transaction,
|
signature::Keypair, signer::Signer, system_instruction, transaction::Transaction,
|
||||||
|
@ -215,24 +155,13 @@ mod tests {
|
||||||
];
|
];
|
||||||
let message = Message::new(&ixs, Some(&from_keypair.pubkey()));
|
let message = Message::new(&ixs, Some(&from_keypair.pubkey()));
|
||||||
let tx = Transaction::new(&[&from_keypair], message, Hash::default());
|
let tx = Transaction::new(&[&from_keypair], message, Hash::default());
|
||||||
let transaction_cost = TransactionCost::Transaction(UsageCostDetails {
|
|
||||||
signature_cost: 5000,
|
|
||||||
..UsageCostDetails::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let transaction_ttl = SanitizedTransactionTTL {
|
let transaction_ttl = SanitizedTransactionTTL {
|
||||||
transaction: SanitizedTransaction::from_transaction_for_tests(tx),
|
transaction: SanitizedTransaction::from_transaction_for_tests(tx),
|
||||||
max_age_slot: Slot::MAX,
|
max_age_slot: Slot::MAX,
|
||||||
};
|
};
|
||||||
|
const TEST_TRANSACTION_COST: u64 = 5000;
|
||||||
TransactionState::new(
|
TransactionState::new(transaction_ttl, compute_unit_price, TEST_TRANSACTION_COST)
|
||||||
transaction_ttl,
|
|
||||||
ComputeBudgetDetails {
|
|
||||||
compute_unit_price,
|
|
||||||
compute_unit_limit: 0,
|
|
||||||
},
|
|
||||||
transaction_cost,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -294,16 +223,16 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_compute_unit_price() {
|
fn test_priority() {
|
||||||
let compute_unit_price = 15;
|
let priority = 15;
|
||||||
let mut transaction_state = create_transaction_state(compute_unit_price);
|
let mut transaction_state = create_transaction_state(priority);
|
||||||
assert_eq!(transaction_state.compute_unit_price(), compute_unit_price);
|
assert_eq!(transaction_state.priority(), priority);
|
||||||
|
|
||||||
// ensure compute unit price is not lost through state transitions
|
// ensure compute unit price is not lost through state transitions
|
||||||
let transaction_ttl = transaction_state.transition_to_pending();
|
let transaction_ttl = transaction_state.transition_to_pending();
|
||||||
assert_eq!(transaction_state.compute_unit_price(), compute_unit_price);
|
assert_eq!(transaction_state.priority(), priority);
|
||||||
transaction_state.transition_to_unprocessed(transaction_ttl);
|
transaction_state.transition_to_unprocessed(transaction_ttl);
|
||||||
assert_eq!(transaction_state.compute_unit_price(), compute_unit_price);
|
assert_eq!(transaction_state.priority(), priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -6,8 +6,6 @@ use {
|
||||||
crate::banking_stage::scheduler_messages::TransactionId,
|
crate::banking_stage::scheduler_messages::TransactionId,
|
||||||
itertools::MinMaxResult,
|
itertools::MinMaxResult,
|
||||||
min_max_heap::MinMaxHeap,
|
min_max_heap::MinMaxHeap,
|
||||||
solana_cost_model::transaction_cost::TransactionCost,
|
|
||||||
solana_runtime::compute_budget_details::ComputeBudgetDetails,
|
|
||||||
std::collections::HashMap,
|
std::collections::HashMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -99,14 +97,13 @@ impl TransactionStateContainer {
|
||||||
&mut self,
|
&mut self,
|
||||||
transaction_id: TransactionId,
|
transaction_id: TransactionId,
|
||||||
transaction_ttl: SanitizedTransactionTTL,
|
transaction_ttl: SanitizedTransactionTTL,
|
||||||
compute_budget_details: ComputeBudgetDetails,
|
priority: u64,
|
||||||
transaction_cost: TransactionCost,
|
cost: u64,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let priority_id =
|
let priority_id = TransactionPriorityId::new(priority, transaction_id);
|
||||||
TransactionPriorityId::new(compute_budget_details.compute_unit_price, transaction_id);
|
|
||||||
self.id_to_transaction_state.insert(
|
self.id_to_transaction_state.insert(
|
||||||
transaction_id,
|
transaction_id,
|
||||||
TransactionState::new(transaction_ttl, compute_budget_details, transaction_cost),
|
TransactionState::new(transaction_ttl, priority, cost),
|
||||||
);
|
);
|
||||||
self.push_id_into_queue(priority_id)
|
self.push_id_into_queue(priority_id)
|
||||||
}
|
}
|
||||||
|
@ -121,8 +118,7 @@ impl TransactionStateContainer {
|
||||||
let transaction_state = self
|
let transaction_state = self
|
||||||
.get_mut_transaction_state(&transaction_id)
|
.get_mut_transaction_state(&transaction_id)
|
||||||
.expect("transaction must exist");
|
.expect("transaction must exist");
|
||||||
let priority_id =
|
let priority_id = TransactionPriorityId::new(transaction_state.priority(), transaction_id);
|
||||||
TransactionPriorityId::new(transaction_state.compute_unit_price(), transaction_id);
|
|
||||||
transaction_state.transition_to_unprocessed(transaction_ttl);
|
transaction_state.transition_to_unprocessed(transaction_ttl);
|
||||||
self.push_id_into_queue(priority_id);
|
self.push_id_into_queue(priority_id);
|
||||||
}
|
}
|
||||||
|
@ -148,7 +144,7 @@ impl TransactionStateContainer {
|
||||||
.expect("transaction must exist");
|
.expect("transaction must exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_min_max_prioritization_fees(&self) -> MinMaxResult<u64> {
|
pub(crate) fn get_min_max_priority(&self) -> MinMaxResult<u64> {
|
||||||
match self.priority_queue.peek_min() {
|
match self.priority_queue.peek_min() {
|
||||||
Some(min) => match self.priority_queue.peek_max() {
|
Some(min) => match self.priority_queue.peek_max() {
|
||||||
Some(max) => MinMaxResult::MinMax(min.priority, max.priority),
|
Some(max) => MinMaxResult::MinMax(min.priority, max.priority),
|
||||||
|
@ -163,10 +159,8 @@ impl TransactionStateContainer {
|
||||||
mod tests {
|
mod tests {
|
||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
solana_cost_model::cost_model::CostModel,
|
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
compute_budget::ComputeBudgetInstruction,
|
compute_budget::ComputeBudgetInstruction,
|
||||||
feature_set::FeatureSet,
|
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::Message,
|
message::Message,
|
||||||
signature::Keypair,
|
signature::Keypair,
|
||||||
|
@ -177,13 +171,8 @@ mod tests {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn test_transaction(
|
/// Returns (transaction_ttl, priority, cost)
|
||||||
priority: u64,
|
fn test_transaction(priority: u64) -> (SanitizedTransactionTTL, u64, u64) {
|
||||||
) -> (
|
|
||||||
SanitizedTransactionTTL,
|
|
||||||
ComputeBudgetDetails,
|
|
||||||
TransactionCost,
|
|
||||||
) {
|
|
||||||
let from_keypair = Keypair::new();
|
let from_keypair = Keypair::new();
|
||||||
let ixs = vec![
|
let ixs = vec![
|
||||||
system_instruction::transfer(
|
system_instruction::transfer(
|
||||||
|
@ -199,31 +188,23 @@ mod tests {
|
||||||
message,
|
message,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
));
|
));
|
||||||
let transaction_cost = CostModel::calculate_cost(&tx, &FeatureSet::default());
|
|
||||||
let transaction_ttl = SanitizedTransactionTTL {
|
let transaction_ttl = SanitizedTransactionTTL {
|
||||||
transaction: tx,
|
transaction: tx,
|
||||||
max_age_slot: Slot::MAX,
|
max_age_slot: Slot::MAX,
|
||||||
};
|
};
|
||||||
(
|
const TEST_TRANSACTION_COST: u64 = 5000;
|
||||||
transaction_ttl,
|
(transaction_ttl, priority, TEST_TRANSACTION_COST)
|
||||||
ComputeBudgetDetails {
|
|
||||||
compute_unit_price: priority,
|
|
||||||
compute_unit_limit: 0,
|
|
||||||
},
|
|
||||||
transaction_cost,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_to_container(container: &mut TransactionStateContainer, num: usize) {
|
fn push_to_container(container: &mut TransactionStateContainer, num: usize) {
|
||||||
for id in 0..num as u64 {
|
for id in 0..num as u64 {
|
||||||
let priority = id;
|
let priority = id;
|
||||||
let (transaction_ttl, compute_budget_details, transaction_cost) =
|
let (transaction_ttl, priority, cost) = test_transaction(priority);
|
||||||
test_transaction(priority);
|
|
||||||
container.insert_new_transaction(
|
container.insert_new_transaction(
|
||||||
TransactionId::new(id),
|
TransactionId::new(id),
|
||||||
transaction_ttl,
|
transaction_ttl,
|
||||||
compute_budget_details,
|
priority,
|
||||||
transaction_cost,
|
cost,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,7 +229,7 @@ mod tests {
|
||||||
container
|
container
|
||||||
.id_to_transaction_state
|
.id_to_transaction_state
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ts| ts.1.compute_unit_price())
|
.map(|ts| ts.1.priority())
|
||||||
.next()
|
.next()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
4
|
4
|
||||||
|
|
Loading…
Reference in New Issue