区块链中文技术社区

Solana genesis参数target_lamports_per_signature如何强制设置

背景

Solana默认初始化手续费价格是在创建genesis时通过--target-lamports-per-signature参数传入的,但是后期可能需要强制调整,需要分析下代码,看下如果修改

数据写入和读取

先分析下现有逻辑参数数据的整体写入和读取逻辑

写入

genesis/src/main.rs

...
.arg( // 通过参数传入
    Arg::with_name("target_lamports_per_signature")
    .long("target-lamports-per-signature")
    .value_name("LAMPORTS")
    .takes_value(true)
    .default_value(default_target_lamports_per_signature)
    .help(
    "The cost in lamports that the cluster will charge for signature \
    verification when the cluster is operating at target-signatures-per-slot",
    ),
)
...
 let mut fee_rate_governor = FeeRateGovernor::new(
        value_t_or_exit!(matches, "target_lamports_per_signature", u64), // 获取参数值
        value_t_or_exit!(matches, "target_signatures_per_slot", u64),
    );
...
let mut genesis_config = GenesisConfig {
        native_instruction_processors: vec![],
        ticks_per_slot,
        poh_config,
        fee_rate_governor, // 组装genesis数据
        rent,
        epoch_schedule,
        cluster_type,
        ..GenesisConfig::default()
    };
...
create_new_ledger( // 创建存储
        &ledger_path,
        &genesis_config,
        max_genesis_archive_unpacked_size,
        LedgerColumnOptions::default(),
    )?;
pub fn create_new_ledger(
    ledger_path: &Path,
    genesis_config: &GenesisConfig,
    max_genesis_archive_unpacked_size: u64,
    column_options: LedgerColumnOptions,
) -> Result<Hash> {
    Blockstore::destroy(ledger_path)?;
    genesis_config.write(ledger_path)?; // 写入 genesis
    ...
    // 用链接回 genesis_config 的刻度填充插槽 0,以引导分类账。
     let blockstore_dir = column_options.shred_storage_type.blockstore_directory();
    let blockstore = Blockstore::open_with_options(
        ledger_path,
        BlockstoreOptions {
            access_type: AccessType::Primary,
            recovery_mode: None,
            enforce_ulimit_nofile: false,
            column_options: column_options.clone(),
        },
    )?;
    ...
    blockstore.insert_shreds(shreds, None, false)?;
    blockstore.set_roots(std::iter::once(&0))?;
    // Explicitly close the blockstore before we create the archived genesis file
    drop(blockstore);

Solana geneis 初始化步骤和 geth init 基本一致,写入genesis文件,以及初始化第一个slot

读取

sdk/program/src/fee_calculator.rs

pub fn new_derived(
        base_fee_rate_governor: &FeeRateGovernor,
        latest_signatures_per_slot: u64,
    ) -> Self {
        let mut me = base_fee_rate_governor.clone();

        if me.target_signatures_per_slot > 0 {
            // lamports_per_signature can range from 50% to 1000% of
            // target_lamports_per_signature
            me.min_lamports_per_signature = std::cmp::max(1, me.target_lamports_per_signature / 2); // 主要看下target_lamports_per_signature的初始化读取位置
            me.max_lamports_per_signature = me.target_lamports_per_signature * 10;
impl FeeRateGovernor {
    pub fn new(target_lamports_per_signature: u64, target_signatures_per_slot: u64) -> Self {
        let base_fee_rate_governor = Self {
            target_lamports_per_signature,
            lamports_per_signature: target_lamports_per_signature,

runtime/src/bank.rs

n _new_from_parent(
        parent: Arc<Bank>,
        collector_id: &Pubkey,
        slot: Slot,
        reward_calc_tracer: Option<impl RewardCalcTracer>,
        new_bank_options: NewBankOptions,
    ) -> Self {
    ...
     let (fee_rate_governor, fee_components_time_us) = measure_us!(
            FeeRateGovernor::new_derived(&parent.fee_rate_governor, parent.signature_count())
        );

从parent.fee_rate_governor获取

ledger/src/blockstore_processor.rs

fn process_next_slots(
    bank: &Arc<Bank>,
    meta: &SlotMeta,
    blockstore: &Blockstore,
    leader_schedule_cache: &LeaderScheduleCache,
    pending_slots: &mut Vec<(SlotMeta, Bank, Hash)>,
    halt_at_slot: Option<Slot>,
) -> result::Result<(), BlockstoreProcessorError> {
    if meta.next_slots.is_empty() {
        return Ok(());
    }
    ...
    if next_meta.is_full() {
            let next_bank = Bank::new_from_parent( // 读取数据
                bank.clone(),
                &leader_schedule_cache
                    .slot_leader_at(*next_slot, Some(bank))
                    .unwrap(),
                *next_slot,
            );
            trace!(
                "New bank for slot {}, parent slot is {}",
                next_slot,
                bank.slot(),
            );
            pending_slots.push((next_meta, next_bank, bank.last_blockhash()));
        }

综合分析

下一个FeeRateGovernor都是从前一个Slot读出的,依次迭代更新,现有逻辑无法后期设置,且模块和代码耦合较深

强制更改的方案

  1. 截获原有迭代更新的逻辑:FeeRateGovernor是附加模块,并且逻辑耦合较深,并且可能引起Slot校验等问题,不适合
  2. 固定设置:对于作为L2的场景,节点权限可控,并且版本稳定后,价格更新频率较低,简单有效

固定设置-代码定制

原则:最小改动,最小风险,优先使用

pub const MIN_TARGET_LAMPORTS_PER_SIGNATURE: u64 = 0;
pub const MAX_TARGET_LAMPORTS_PER_SIGNATURE: u64 = 0;

代码修改后

pub fn new_derived(
        base_fee_rate_governor: &FeeRateGovernor,
        latest_signatures_per_slot: u64,
    ) -> Self {
        let mut me = base_fee_rate_governor.clone();

        if me.target_signatures_per_slot > 0 && DEFAULT_TARGET_SIGNATURES_PER_SLOT > 0{
            // lamports_per_signature can range from 50% to 1000% of
            // target_lamports_per_signature
            // Support mandatory settings
            if MIN_TARGET_LAMPORTS_PER_SIGNATURE > 0 {
                me.min_lamports_per_signature = MIN_TARGET_LAMPORTS_PER_SIGNATURE;
            } else {
                me.min_lamports_per_signature = std::cmp::max(1, me.target_lamports_per_signature / 2);
            }

            if MAX_TARGET_LAMPORTS_PER_SIGNATURE > 0 {
                me.max_lamports_per_signature = MAX_TARGET_LAMPORTS_PER_SIGNATURE;
            } else {
                me.max_lamports_per_signature = me.target_lamports_per_signature * 10;
            }
            // Prevent a single setting from causing high data non-compliance
            if me.min_lamports_per_signature > me.max_lamports_per_signature {
                me.min_lamports_per_signature = me.max_lamports_per_signature;
            }

            // What the cluster should charge at `latest_signatures_per_slot`
            let desired_lamports_per_signature =
                me.max_lamports_per_signature
                    .min(me.min_lamports_per_signature.max(
                        me.target_lamports_per_signature
                            * std::cmp::min(latest_signatures_per_slot, std::u32::MAX as u64)
                            / me.target_signatures_per_slot,
                    ));

            trace!(
                "desired_lamports_per_signature: {}",
                desired_lamports_per_signature
            );

            let gap = desired_lamports_per_signature as i64
                - base_fee_rate_governor.lamports_per_signature as i64;

            if gap == 0 {
                me.lamports_per_signature = desired_lamports_per_signature;
            } else {
                // Adjust fee by 5% of target_lamports_per_signature to produce a smooth
                // increase/decrease in fees over time.
                let gap_adjust =
                    std::cmp::max(1, me.target_lamports_per_signature / 20) as i64 * gap.signum();

                trace!(
                    "lamports_per_signature gap is {}, adjusting by {}",
                    gap,
                    gap_adjust
                );

                me.lamports_per_signature =
                    me.max_lamports_per_signature
                        .min(me.min_lamports_per_signature.max(
                            (base_fee_rate_governor.lamports_per_signature as i64 + gap_adjust)
                                as u64,
                        ));
            }
        } else {
            me.lamports_per_signature = base_fee_rate_governor.target_lamports_per_signature;

            if MIN_TARGET_LAMPORTS_PER_SIGNATURE > 0 && me.lamports_per_signature < MIN_TARGET_LAMPORTS_PER_SIGNATURE {
                me.lamports_per_signature = MIN_TARGET_LAMPORTS_PER_SIGNATURE
            } else if MAX_TARGET_LAMPORTS_PER_SIGNATURE > 0 && me.lamports_per_signature > MAX_TARGET_LAMPORTS_PER_SIGNATURE {
                me.lamports_per_signature = MAX_TARGET_LAMPORTS_PER_SIGNATURE
            }

            me.min_lamports_per_signature = me.target_lamports_per_signature;
            me.max_lamports_per_signature = me.target_lamports_per_signature;
        }
        debug!(
            "new_derived(): lamports_per_signature: {}",
            me.lamports_per_signature
        );
        me
    }

Diff

 sdk/program/src/fee_calculator.rs | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/sdk/program/src/fee_calculator.rs b/sdk/program/src/fee_calculator.rs
index c4dcb572cf..fc4415f2ab 100644
--- a/sdk/program/src/fee_calculator.rs
+++ b/sdk/program/src/fee_calculator.rs
@@ -75,6 +75,9 @@ pub struct FeeRateGovernor {
 pub const DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE: u64 = 10_000;
 pub const DEFAULT_TARGET_SIGNATURES_PER_SLOT: u64 = 50 * DEFAULT_MS_PER_SLOT;
 
+pub const MIN_TARGET_LAMPORTS_PER_SIGNATURE: u64 = 0;
+pub const MAX_TARGET_LAMPORTS_PER_SIGNATURE: u64 = 0;
+
 // Percentage of tx fees to burn
 pub const DEFAULT_BURN_PERCENT: u8 = 50;
 
@@ -109,11 +112,25 @@ impl FeeRateGovernor {
     ) -> Self {
         let mut me = base_fee_rate_governor.clone();
 
-        if me.target_signatures_per_slot > 0 {
+        if me.target_signatures_per_slot > 0 && DEFAULT_TARGET_SIGNATURES_PER_SLOT > 0{
             // lamports_per_signature can range from 50% to 1000% of
             // target_lamports_per_signature
-            me.min_lamports_per_signature = std::cmp::max(1, me.target_lamports_per_signature / 2);
-            me.max_lamports_per_signature = me.target_lamports_per_signature * 10;
+            // Support mandatory settings
+            if MIN_TARGET_LAMPORTS_PER_SIGNATURE > 0 {
+                me.min_lamports_per_signature = MIN_TARGET_LAMPORTS_PER_SIGNATURE;
+            } else {
+                me.min_lamports_per_signature = std::cmp::max(1, me.target_lamports_per_signature / 2);
+            }
+
+            if MAX_TARGET_LAMPORTS_PER_SIGNATURE > 0 {
+                me.max_lamports_per_signature = MAX_TARGET_LAMPORTS_PER_SIGNATURE;
+            } else {
+                me.max_lamports_per_signature = me.target_lamports_per_signature * 10;
+            }
+            // Prevent a single setting from causing high data non-compliance
+            if me.min_lamports_per_signature > me.max_lamports_per_signature {
+                me.min_lamports_per_signature = me.max_lamports_per_signature;
+            }
 
             // What the cluster should charge at `latest_signatures_per_slot`
             let desired_lamports_per_signature =
@@ -155,6 +172,13 @@ impl FeeRateGovernor {
             }
         } else {
             me.lamports_per_signature = base_fee_rate_governor.target_lamports_per_signature;
+
+            if MIN_TARGET_LAMPORTS_PER_SIGNATURE > 0 && me.lamports_per_signature < MIN_TARGET_LAMPORTS_PER_SIGNATURE {
+                me.lamports_per_signature = MIN_TARGET_LAMPORTS_PER_SIGNATURE
+            } else if MAX_TARGET_LAMPORTS_PER_SIGNATURE > 0 && me.lamports_per_signature > MAX_TARGET_LAMPORTS_PER_SIGNATURE {
+                me.lamports_per_signature = MAX_TARGET_LAMPORTS_PER_SIGNATURE
+            }
+
             me.min_lamports_per_signature = me.target_lamports_per_signature;
             me.max_lamports_per_signature = me.target_lamports_per_signature;
         }

控制介绍

假设目前已经基于genesis使用前期参数初始化,并启动测试网

  1. 通过控制DEFAULT_TARGET_SIGNATURES_PER_SLOT是否为0,强制切换是否动态调节lamports_per_signature
  2. 通过设置MIN_TARGET_LAMPORTS_PER_SIGNATURE和MAX_TARGET_LAMPORTS_PER_SIGNATURE,强制设置lamports_per_signature的最小和最大允许值范围

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »