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

Solana Hello World(安装和故障排除)

这是 Solana 的 Hello World 教程。我们将引导您完成安装 Solana 的步骤并解决可能出现的问题。
如果您遇到问题,请查看本文末尾的故障排除部分。

安装步骤

安装 Rust

如果您已经安装了 Rust,请跳过此步骤。

# install rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

安装 Yarn

您需要它来运行单元测试。如果您已经安装了 yarn,请跳过此步骤。

# install yarn -- assumes node js is installed
corepack enable # corepack comes with node js

安装 Solana cli

我们强烈建议使用stable版本,而不是latest。 Solana 安装不再支持符号通道名称(edge、beta、stable),因此我们必须指定版本。

# install solana
sh -c "$(curl -sSfL https://release.solana.com/v1.16.25/install)"

安装锚点

Anchor 是 Solana 开发的框架。它在很多方面与 hardhat 非常相似。

# install anchor
cargo install --git https://github.com/coral-xyz/anchor avm --locked --force

avm install latest
avm use latest

测试安装

初始化并构建一个锚点程序(用于 hello world)

Mac 用户:我们建议调用您的程序day_1而不是day1因为 Anchor 有时似乎会在 Mac 机器上默默插入下划线。

anchor init day1 # use day_1 if you have a mac
cd day1
anchor build

根据您的机器和互联网连接,此步骤可能需要一段时间。这也是您可能遇到安装问题的地方,因此如有必要,请参阅故障排除部分。

配置 Solana 在本地主机上运行

# shell 1
solana config set --url localhost

运行测试验证器节点

在新的 shell 中(而不是在 Anchor 项目中)运行以下命令。但不要关闭运行的 shell anchor build。这会在您的机器上运行本地(测试)Solana 节点实例:

# shell 2

solana-test-validator

确保 program_id 与 Anchor 键同步

返回 Anchor 项目的 shell 并运行以下命令:

# shell 1

anchor keys sync

运行测试

在 Anchor 项目中运行此命令

# shell 1

anchor test --skip-local-validator

上面的命令运行我们程序的测试。如果您尚未创建测试钱包,Anchor 将为您提供如何创建钱包的说明。我们在此不提供这些说明,因为它取决于您的操作系统和文件结构。您可能还需要通过在终端中运行来为自己空投一些本地 Sol 。您可以通过在命令行中solana airdrop 100 {YOUR_WALLET_ADDRESS}运行来获取您的钱包地址。solana address
预期输出如下:

你好世界

现在让我们让程序输出“Hello, world!”。将以下标有 的行添加NEW LINE HERE到programs/day_1/src/lib.rs。

use anchor_lang::prelude::*;

declare_id!("...");

#[program]
pub mod day_1 {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        msg!("Hello, world!"); // **** NEW LINE HERE ****
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize {}

再次运行测试之前,请终止本地验证器进程并使用以下命令重新启动它:

solana-test-validator --reset

再次运行测试

anchor test --skip-local-validator

通过运行查找日志文件

ls .anchor/program-logs/

打开该文件即可看到记录的“Hello world”

实时 Solana 日志

或者,您可以通过打开第三个 shell 并运行以下命令来查看日志:

# shell 3

solana logs

现在再次运行测试,您应该在运行的终端中看到相同的消息solana logs。

问答

为什么 declared_id! 和 msg! 后面有感叹号?

在 Rust 中,感叹号表示这些是宏。我们将在后面的教程中重新讨论宏。

我需要一个初始化函数吗?

不,这是由 Anchor 框架自动生成的。您可以随意命名。

在此上下文中,initialize 这个名称没有什么特殊之处,因此我们可以将其更改为任何我们喜欢的名称。这与其他一些关键字和语言不同,例如 main 在某些语言中是一个特殊名称,或者在 Solidity 中,constructor 是一个特殊名称。
练习:尝试将initialize中的programs/day_1/src/lib.rs和initialize中的重命名为 ,tests/day_1.ts然后initialize2再次运行测试。请参见下面橙色圆圈中标记的更改。

为什么我们要使用 –skip-local-validator 来运行测试?

当测试针对某个节点运行时,我们将能够查询该节点的状态变化。如果您无法让节点运行,则可以anchor test在不使用--skip-local-validator标志的情况下运行。但是,这将使您的开发和测试变得更加困难,因此我们建议让本地验证器正常工作。

故障排除

Solana 是一款快速发展的软件,您可能会遇到安装问题。我们在以下部分记录了您最有可能遇到的问题。
我们的教程系列使用以下版本编写: Anchor = 版本 0.29.0 Solana = 版本 1.16.25 * Rustc = 1.77.0-nightly
您可以通过运行以下命令来更改 Anchor 版本:

avm install 0.29.0
avm use 0.29.0

错误:solana-program v1.18.0无法构建包

error: package `solana-program v1.18.0` cannot be built because it requires rustc 1.72.0 or newer, while the currently active rustc version is 1.68.0-dev
Either upgrade to rustc 1.72.0 or newer, or use
cargo update -p solana-program@1.18.0 --precise ver

检查您正在使用的 Solana 版本solana --version。然后将该版本插入到ver上面的内容中。示例解决方案如下所示:

错误[E0658]:使用不稳定库功能“build_hasher_simple_hash_one”

如果出现以下错误:

error[E0658]: use of unstable library feature 'build_hasher_simple_hash_one'
--> src/random_state.rs:463:5
|
463 | / fn hash_one<T: Hash>(&self, x: T) -> u64 {
464 | | RandomState::hash_one(self, x)
465 | | }
| |_____^
|
= note: see issue #86161 https://github.com/rust-lang/rust/issues/86161 for more information
= help: add #![feature(build_hasher_simple_hash_one)] to the crate attributes to enable

运行以下命令:cargo update -p ahash@0.8.7 --precise 0.8.6。

来源:https ://solana.stackexchange.com/questions/8800/cant-build-hello-world

错误:部署程序失败:错误处理指令 1:自定义程序错误:0x1

Error: Deploying program failed: Error processing Instruction 1: custom program error: 0x1
There was a problem deploying: Output { status: ExitStatus(unix_wait_status(256)), stdout: "", stderr: "" }.

如果出现此错误,则表示您的密钥未同步。运行anchor keys sync。

错误:无法发送交易:交易模拟失败:尝试加载不存在的程序

您的密钥未同步。运行anchor keys sync。

错误:您配置的 rpc 端口:8899 已被使用

当验证器在后台运行时,您anchor test没有运行--skip-local-validator。请关闭验证器并运行,anchor test或在验证器运行时运行anchor test --skip-local-validator。跳过本地验证器意味着跳过它为项目创建的临时验证器,而不是在后台运行的验证器。

错误:帐户 J7t…zjK 资金不足,无法消费

运行以下命令将 100 SOL 空投到您的开发地址:

solana airdrop 100 J7t...zjK

错误:RPC 请求错误:集群版本查询失败

Error: RPC request error: cluster version query failed: error sending request for url (http://localhost:8899/): error trying to connect: tcp connect error: Connection refused (os error 61)
There was a problem deploying: Output { status: ExitStatus(unix_wait_status(256)), stdout: "", stderr: "" }.

这意味着solana-test-validator不在后台运行。solana-test-validator在另一个 shell 中运行。

线程“main”因“调用Option::unwrap()某个None值”而惊慌失措

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', /Users/username/.cargo/git/checkouts/anchor-50c4b9c8b5e0501f/347c225/lang/syn/src/idl/file.rs:214:73
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

你可能还没跑anchor build。

我在使用 Mac,出现错误:无法启动验证器:无法在测试分类账中创建分类账:块存储错误

按照此Stack Exchange 线程中的说明进行操作。

我的 Mac 上有 node.js,但是没有 corepack

运行以下命令:

brew install corepack
brew link --overwrite corepack

来源:https ://stackoverflow.com/questions/70082424/command-not-found-corepack-when-installing-yarn-on-node-v17-0-1

错误:不是目录:

BPF SDK: /Users/rareskills/.local/share/solana/install/releases/stable-43daa37937907c10099e30af10a5a0b43e2dd2fe/solana-release/bin/sdk/bpf
cargo-build-bpf child: rustup toolchain list -v
cargo-build-bpf child: rustup toolchain link bpf /Users/rareskills/.local/share/solana/install/releases/stable-43daa37937907c10099e30af10a5a0b43e2dd2fe/solana-release/bin/sdk/bpf/dependencies/bpf-tools/rust
error: not a directory:

清除缓存:运行rm -rf ~/.cache/solana/*。

错误:target/idl/day_1.json 不存在。您运行了吗anchor build?

创建一个新项目并将其命名为 day_1 而不是 day1。Anchor 似乎会在某些机器上默默插入下划线。

原文:https://www.rareskills.io/post/hello-world-solana