计算交易打包检查
core/src/banking_stage/transaction_scheduler/scheduler_controller.rs

fn buffer_packets(&mut self, packets: Vec<ImmutableDeserializedPacket>) { ... for ((transaction, fee_budget_limits), _) in transactions .into_iter() .zip(fee_budget_limits_vec) .zip(check_results) .filter(|(_, check_result)| check_result.0.is_ok()) { ... let transaction_id = self.transaction_id_generator.next(); let (priority, cost) = Self::calculate_priority_and_cost(&transaction, &fee_budget_limits, &bank);// 计算优先级和手续费费用 let transaction_ttl = SanitizedTransactionTTL { transaction, max_age_slot: last_slot_in_epoch, }; if self.container.insert_new_transaction( transaction_id, transaction_ttl, priority, cost, ) {
计算交易所需的Fee
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()), bank.feature_set .is_active(&remove_rounding_in_fee_calculation::id()), ); // 我们在这里需要一个乘数,以避免过度向下舍入。 // 对于许多交易来说,成本将高于原始 Lamport 的费用。 // 为了计算优先级,我们将费用乘以一个大数,以便成本只是一小部分。 // 分母中使用 1 的偏移量来明确避免除以零。 const MULTIPLIER: u64 = 1_000_000; ( fee.saturating_mul(MULTIPLIER) .saturating_div(cost.saturating_add(1)), // 优先级费用 cost, // 交易fee ) }
从返回值作用,cost等于交易的Fee, 而calculate_fee计算的fee用于计算交易的优先级
计算Cost
cost-model/src/cost_model.rs
pub fn calculate_cost( transaction: &SanitizedTransaction, feature_set: &FeatureSet, ) -> TransactionCost { if transaction.is_simple_vote_transaction() { // 判断是否为简单的验证者投票交易 TransactionCost::SimpleVote { writable_accounts: Self::get_writable_accounts(transaction), } } else { // 常规交易 let mut tx_cost = UsageCostDetails::new_with_default_capacity(); Self::get_signature_cost(&mut tx_cost, transaction); Self::get_write_lock_cost(&mut tx_cost, transaction, feature_set); Self::get_transaction_cost(&mut tx_cost, transaction, feature_set); tx_cost.account_data_size = Self::calculate_account_data_size(transaction); debug!("transaction {:?} has cost {:?}", transaction, tx_cost); TransactionCost::Transaction(tx_cost) } }
impl Default for UsageCostDetails { fn default() -> Self { Self { writable_accounts: Vec::with_capacity(MAX_WRITABLE_ACCOUNTS), signature_cost: 0u64, write_lock_cost: 0u64, data_bytes_cost: 0u64, programs_execution_cost: 0u64, loaded_accounts_data_size_cost: 0u64, account_data_size: 0u64, num_transaction_signatures: 0u64, num_secp256k1_instruction_signatures: 0u64, num_ed25519_instruction_signatures: 0u64, } } }
综合费用因素计算
签名数量
每个签名固定费率写锁数量
每个可写账户的固定利率数据字节成本
所有交易指令数据的长度总和的每字节固定费率账户规模
账户规模无法预先得知,但可以占到交易在网络上产生的相当一部分负载。付款人将预先支付最大账户规模(1000 万)的费用,并在实际账户规模得知后退还差额。计算预算
每笔交易将获得默认的交易范围计算预算 20 万个单位,并可选择通过计算预算指令请求更大的预算,最高可达 100 万个单位。此预算用于限制处理交易所需的时间。费用的计算预算部分将根据默认或请求的金额预先收取。处理后,将知道实际消耗的单位数,并将向付款人退还差额,因此付款人只需支付他们使用的费用。内置程序将具有固定成本,而 SBF 程序的成本将在运行时衡量。预编译程序
预编译程序正在执行计算密集型操作。预编译程序所产生的工作可以根据指令的数据数组进行预测。因此,将根据指令数据的解析为每个预编译程序分配成本。由于预编译程序是在银行之外处理的,因此它们的计算成本不会反映在计算预算中,也不会用于事务调度决策。
计算Fee
sdk/src/fee.rs
#[cfg(not(target_os = "solana"))] pub fn calculate_fee( &self, message: &SanitizedMessage, lamports_per_signature: u64, budget_limits: &FeeBudgetLimits, include_loaded_account_data_size_in_fee: bool, remove_rounding_in_fee_calculation: bool, ) -> u64 { // Fee based on compute units and signatures let congestion_multiplier = if lamports_per_signature == 0 { 0 // test only } else { 1 // multiplier that has no effect }; self.calculate_fee_details( message, budget_limits, include_loaded_account_data_size_in_fee, ) .total_fee(remove_rounding_in_fee_calculation) .saturating_mul(congestion_multiplier) }
#[cfg(not(target_os = "solana"))] pub fn calculate_fee_details( &self, message: &SanitizedMessage, budget_limits: &FeeBudgetLimits, include_loaded_account_data_size_in_fee: bool, ) -> FeeDetails { let signature_fee = message .num_signatures() .saturating_mul(self.lamports_per_signature); let write_lock_fee = message .num_write_locks() .saturating_mul(self.lamports_per_write_lock); // `compute_fee` covers costs for both requested_compute_units and // requested_loaded_account_data_size let loaded_accounts_data_size_cost = if include_loaded_account_data_size_in_fee { FeeStructure::calculate_memory_usage_cost( budget_limits.loaded_accounts_data_size_limit, budget_limits.heap_cost, ) } else { 0_u64 }; let total_compute_units = loaded_accounts_data_size_cost.saturating_add(budget_limits.compute_unit_limit); let compute_fee = self .compute_fee_bins .iter() .find(|bin| total_compute_units <= bin.limit) .map(|bin| bin.fee) .unwrap_or_else(|| { self.compute_fee_bins .last() .map(|bin| bin.fee) .unwrap_or_default() }); FeeDetails { transaction_fee: signature_fee .saturating_add(write_lock_fee) .saturating_add(compute_fee), prioritization_fee: budget_limits.prioritization_fee, } }
交易打包余额检查
交易执行检查时,一样会计算交易的fee,确保支付地址余额充足
core/src/banking_stage/unprocessed_transaction_storage.rs
fn consume_scan_should_process_packet( bank: &Bank, banking_stage_stats: &BankingStageStats, packet: &ImmutableDeserializedPacket, payload: &mut ConsumeScannerPayload, ) -> ProcessingDecision { ... // 仅当我们确实可以采取锁定时才检查费用支付者 // 我们不会在此立即丢弃检查锁定失败, // 因为优先级保护要求我们始终采取锁定 // 除非在丢弃交易的情况下(即“从不”)。 if payload.account_locks.check_locks(message) && Consumer::check_fee_payer_unlocked(bank, message, &mut payload.error_counters) .is_err() { payload .message_hash_to_transaction .remove(packet.message_hash()); return ProcessingDecision::Never; }
core/src/banking_stage/consumer.rs
pub fn check_fee_payer_unlocked( bank: &Bank, message: &SanitizedMessage, error_counters: &mut TransactionErrorMetrics, ) -> Result<(), TransactionError> { let fee_payer = message.fee_payer(); let budget_limits = process_compute_budget_instructions(message.program_instructions_iter())?.into(); let fee = bank.fee_structure.calculate_fee( // 同上,计算交易的fee message, bank.get_lamports_per_signature(), &budget_limits, bank.feature_set.is_active( &feature_set::include_loaded_accounts_data_size_in_fee_calculation::id(), ), bank.feature_set .is_active(&feature_set::remove_rounding_in_fee_calculation::id()), ); let (mut fee_payer_account, _slot) = bank .rc .accounts .accounts_db .load_with_fixed_root(&bank.ancestors, fee_payer) .ok_or(TransactionError::AccountNotFound)?; validate_fee_payer( fee_payer, &mut fee_payer_account, 0, error_counters, bank.rent_collector(), fee, ) }