背景
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读出的,依次迭代更新,现有逻辑无法后期设置,且模块和代码耦合较深
强制更改的方案
截获原有迭代更新的逻辑:FeeRateGovernor是附加模块,并且逻辑耦合较深,并且可能引起Slot校验等问题,不适合
固定设置:对于作为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使用前期参数初始化,并启动测试网
通过控制DEFAULT_TARGET_SIGNATURES_PER_SLOT是否为0,强制切换是否动态调节lamports_per_signature
通过设置MIN_TARGET_LAMPORTS_PER_SIGNATURE和MAX_TARGET_LAMPORTS_PER_SIGNATURE,强制设置lamports_per_signature的最小和最大允许值范围