Solana FeeStructure(综合费用) 计算手续费和优先级费用

2024-11-29 11:49:00

计算交易打包检查

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,
        }
    }
}

综合费用因素计算

  1. 签名数量
    每个签名固定费率
  2. 写锁数量
    每个可写账户的固定利率
  3. 数据字节成本
    所有交易指令数据的长度总和的每字节固定费率
  4. 账户规模
    账户规模无法预先得知,但可以占到交易在网络上产生的相当一部分负载。付款人将预先支付最大账户规模(1000 万)的费用,并在实际账户规模得知后退还差额。
  5. 计算预算
    每笔交易将获得默认的交易范围计算预算 20 万个单位,并可选择通过计算预算指令请求更大的预算,最高可达 100 万个单位。此预算用于限制处理交易所需的时间。费用的计算预算部分将根据默认或请求的金额预先收取。处理后,将知道实际消耗的单位数,并将向付款人退还差额,因此付款人只需支付他们使用的费用。内置程序将具有固定成本,而 SBF 程序的成本将在运行时衡量。
  6. 预编译程序
    预编译程序正在执行计算密集型操作。预编译程序所产生的工作可以根据指令的数据数组进行预测。因此,将根据指令数据的解析为每个预编译程序分配成本。由于预编译程序是在银行之外处理的,因此它们的计算成本不会反映在计算预算中,也不会用于事务调度决策。

计算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,
        )
    }
当前页面是本站的「Baidu MIP」版。发表评论请点击:完整版 »