BCSkill (Block chain skill )
区块链中文技术社区

只讨论区块链底层技术
遵守一切相关法律政策!

实时 solana TPS 跟踪仪表盘

Solana 项目演示了Solana 区块链的实时每秒交易量 (TPS)仪表板。该项目利用了以下技术:

Rust Backend :从Solana 区块链获取实时数据并将其存储在InfluxDB中以进行时间序列数据管理。
InfluxDB:用于存储 Solana 交易数据的高性能时间序列数据库。
Streamlit:一个基于 Python 的框架,用于构建TPS和其他关键指标的交互式可视化。
该仪表板提供了对 Solana 区块链性能的实时洞察,帮助用户跟踪和分析交易吞吐量和其他相关指标。它是集成Rust、Python和InfluxDB以构建高效实时数据管道和可视化工具的一个示例

github: https://github.com/amil13/solana_project

Solana 设置手续费价格

背景

当使用Solana部署私链或者侧链时,需要自定义链的手续费价格

配置

先说如何配置
拿multinode-demo/setup.sh举例
在文件尾部添加 --target-lamports-per-signature

...
default_arg --target-lamports-per-signature 10

$solana_genesis "${args[@]}"

链代码分析

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 { // 当配置target_signatures_per_slot时
            // 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;

            // 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 { // 当没有设置target_signatures_per_slot时,链采用固定价格
            me.lamports_per_signature = base_fee_rate_governor.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
    }

分析汇总

  1. 当没有设置target_signatures_per_slot时,链采用固定价格
  2. 当设置target_signatures_per_slot时,最小价格为target-lamports-per-signature设置的50%,最高为 target-lamports-per-signature的10倍
    1. 当latest_signatures_per_slot <= target_signatures_per_slot/2时,返回target_lamports_per_signature的50%
    2. 当latest_signatures_per_slot >= target_signatures_per_slot/2 && latest_signatures_per_slot >= target_signatures_per_slot, 返回target_lamports_per_signature的50%->100%
    3. 当latest_signatures_per_slot >= target_signatures_per_slot && latest_signatures_per_slot <= target_signatures_per_slot * 10, 返回target_lamports_per_signature 1-10倍
    4. latest_signatures_per_slot >= target_signatures_per_slot * 10, 返回target_lamports_per_signature 10倍

数据测试demo

fn main() {
    let latest_signatures_per_slot = 1;
    let target_signatures_per_slot = 10;
    let target_lamports_per_signature = 100;
    let min_lamports_per_signature = std::cmp::max(1, target_lamports_per_signature / 2);
    let max_lamports_per_signature = target_lamports_per_signature * 10;
    let desired_lamports_per_signature =
                max_lamports_per_signature
                    .min(min_lamports_per_signature.max(
                        target_lamports_per_signature
                            * std::cmp::min(latest_signatures_per_slot, std::u32::MAX as u64)
                            / target_signatures_per_slot,
                    ));
    println!("{}", desired_lamports_per_signature);
}

Solana 手续费燃烧设置

背景

Solana 默认配置手续费会燃烧50%,有些时候不需要燃烧

配置

先直接说如何配置
拿multinode-demo/setup.sh 举例,初始化genesis时,传入 --fee-burn-percentage 0
例如尾部加一行

...
default_arg --fee-burn-percentage 0

$solana_genesis "${args[@]}"

代码分析

genesis创建时,会读取传入参数

genesis/src/main.rs

...
.arg(
    Arg::with_name("fee_burn_percentage")
    .long("fee-burn-percentage")
    .value_name("NUMBER")
    .takes_value(true)
    .default_value(default_fee_burn_percentage)
    .help("percentage of collected fee to burn")
    .validator(is_valid_percentage),
)
...

手续费派发逻辑

runtime/src/bank/fee_distribution.rs

pub(super) fn distribute_transaction_fees(&self) {
        let collector_fees = self.collector_fees.load(Relaxed);
        if collector_fees != 0 {
            let (deposit, mut burn) = self.fee_rate_governor.burn(collector_fees); // 燃烧一定比例
            if deposit > 0 {
                let validate_fee_collector = self.validate_fee_collector_account();
                match self.deposit_fees(
                    &self.collector_id,
                    deposit,
                    DepositFeeOptions {
                        check_account_owner: validate_fee_collector,
                        check_rent_paying: validate_fee_collector,
                    },
                ) {
                    Ok(post_balance) => {
                        self.rewards.write().unwrap().push((
                            self.collector_id,
                            RewardInfo {
                                reward_type: RewardType::Fee,
                                lamports: deposit as i64,
                                post_balance,
                                commission: None,
                            },
                        ));
                    }
                    Err(err) => {
                        debug!(
                            "Burned {} lamport tx fee instead of sending to {} due to {}",
                            deposit, self.collector_id, err
                        );
                        datapoint_warn!(
                            "bank-burned_fee",
                            ("slot", self.slot(), i64),
                            ("num_lamports", deposit, i64),
                            ("error", err.to_string(), String),
                        );
                        burn += deposit;
                    }
                }
            }
            self.capitalization.fetch_sub(burn, Relaxed);
        }
    }

sdk/program/src/fee_calculator.rs

pub fn burn(&self, fees: u64) -> (u64, u64) {
    let burned = fees * u64::from(self.burn_percent) / 100;
    (fees - burned, burned)
}