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读出的,依次迭代更新,现有逻辑无法后期设置,且模块和代码耦合较深
强制更改的方案
- 截获原有迭代更新的逻辑: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的最小和最大允许值范围