您正在查看: 2024年12月

推出 Solana 签名 SDK:让 Solana 用户能够访问 EVM dApp

Solana Signature SDK 是一个以开发人员为中心的工具包,旨在将 Solana 钱包(例如 Phantom、Backpack 和 Solflare)集成到通过 Neon EVM 部署在 Solana 上的与以太坊兼容的 dApp 中。

关键要点

  • 统一的用户体验:使用与 Solana 兼容的钱包以及与以太坊兼容的 dApp 进行交易签名。
  • 技术创新:通过链上签名验证支持以太坊交易的 Solana 原生 ed25519 签名。
  • 简化开发:通过 SDK 快速集成钱包和交易管理,几分钟内即可完成设置和功能。
  • 生命周期透明度:从钱包到 Neon EVM 上执行的交易流程的详细概述。
  • 高级功能:支持 Solana 程序调用、ERC20-SPL 资产管理和交易调度。

随着 Solana 签名钱包 SDK 的发布,我们朝着实现 Solana 原生集成和简化 Solana 环境中与以太坊兼容 dApp 的用户交互迈出了重要的变革一步,正如我们的白皮书中所强调的那样。

Neon EVM 和钱包集成简介

Solana 签名钱包 SDK 工具包使开发人员能够构建 dApp,使 Solana 用户能够与部署在 Neon EVM 上的智能合约进行交互。与以太坊兼容的 dApp 历来依赖类似以太坊的签名 (secp256k1) 进行交易身份验证。这种依赖关系为 Solana 用户带来了障碍,要求他们使用 MetaMask 等其他工具并进行复杂的流动性转移。Solana 签名钱包 SDK 与 Neon Proxy 一起解决了这个问题。通过匹配 Solana 的 ed25519 签名方案和以太坊的交易要求,此 SDK 简化了钱包集成和跨生态系统的资产可访问性。

从本质上讲,此 SDK 简化了交易管理,如下所示:

  1. 在 Solana 钱包内创建 EVM 交易。
  2. 通过 Neon EVM 的链上内存池打包并提交交易
  3. 通过 Neon Proxy 执行和状态管理
  4. Neon 和 Solana 的状态更新。

通过抽象交易转换的复杂性并增强两个网络之间的兼容性,它允许开发人员专注于构建和与去中心化应用程序(dApps)交互,而不必担心底层基础设施障碍。

让我们深入了解细节、技术概述、代币管理流程、优势、局限性和未来前景。

Solana 签名钱包 SDK 详细概述

Neon EVM Solana 签名钱包 SDK 是一种模块化且灵活的解决方案,旨在使开发人员和用户受益。

好处:

对于用户

  • 简化的体验:使用现有的 Solana 钱包与与以太坊兼容的 dApp 进行交互。
  • 降低复杂性:无需额外的钱包或复杂的流动性转移。

对于开发人员

  • 快速集成:通过全面的 SDK 工具简化钱包连接和交易工作流程。
  • 生态系统兼容性:构建适合 Solana 和以太坊用户的 dApp,无需额外的开发开销。

Solana 签名验证:功能

Neon EVM Solana 签名钱包 SDK 的一个突出功能是它能够使用 Solana 的 ed25519 签名系统验证 Solana 交易。这一增强功能对于确保通过 Solana 钱包(例如 Phantom、Solflare 和 Backpack)发起的交易在 Neon EVM 网络上准确、安全地处理至关重要。

验证过程在 Neon Proxy 中实时进行,它会在 Neon EVM 上执行任何交易之前检查 Solana 签名的真实性。通过原生验证 Solana 的 ed25519 签名,Neon Proxy 无需手动验证签名或使用中间层。这意味着交易可以安全地处理,延迟最少,确保每笔交易都经过身份验证并正确记录。

这种实时验证机制对于 Solana 钱包用户直接与部署在 Neon EVM 上的与以太坊兼容的 dApp 交互至关重要,而无需单独的身份验证方法或 MetaMask 等附加工具。

对于 SDK,为确保流畅的用户体验,两个关键关注领域包括:

  • 通过 Neon Proxy 实现的技术功能;
  • 使用 ERC20ForSPL 管理进行资产管理,用于以太坊和 Solana 网络之间的代币转移。

继续阅读,了解 Neon Proxy 如何支持这一点,以及 ERC20ForSPL 合约如何管理 Solana 和以太坊兼容环境之间的代币余额和转移。

Neon Proxy 变更:增强基础设施功能

Neon Proxy 对于促进交互至关重要。它将类似以太坊的交易打包到 Solana 交易中,消除了实现转换逻辑的负担。代理架构的最新更新引入了新功能,优化了交易处理,并确保与现有系统的兼容性,同时整合了 Solana 签名基础设施。

Solana 签名 SDK 的关键代理增强功能

  • Solana 签名验证: Neon Proxy 现在可以验证 Solana 的 ed25519 签名,使 Solana 钱包(Phantom、Backpack、Solflare 等)能够直接向 Neon EVM 签名并提交交易。这消除了中间层,简化了流程并确保了安全性。
  • 链上内存池集成:借助链上内存池支持,Solana 钱包交易可以在 Neon EVM 生态系统中透明高效地存储、验证和执行。这一改进增强了可扩展性,并消除了对交易调度链下操作的依赖。

为了实现链上内存池,Neon 引入了NeonTxs 树的概念。这将单个大交易拆分为更小、可管理的单元(NeonTxs),同时保持整体逻辑。

工作原理

  1. 交易树:
    Neon 合约创建了一个交易“树”,其中较小的单元(分支)可以独立执行。
    每个交易都是原子的,这意味着只有交易成功完成,其变化才会生效。

  2. 并行执行:
    独立交易可以并行运行,从而加快进程。

  3. 错误处理:
    如果树中的一个交易失败,它不会破坏整个过程。Neon 合约的逻辑可以决定如何处理失败。

  4. 最终聚合:
    树中的所有交易完成后,它们的结果将被组合(聚合)成最终交易。

使用 Solana 签名 SDK 的代理工作流

  • 交易创建:
    Solana 用户通过使用 Solana Signature SDK 通过他们的原生钱包(Phantom、Backpack、Solflare)对交易进行签名来发起交易。
    交易通过SDK打包成Neon兼容的格式并发送给Neon Proxy。

  • 链上调度和映射:
    Neon Proxy 将用户的 Solana 钱包地址映射到与以太坊兼容的地址,确保与 Neon EVM 上基于以太坊的智能合约正确集成。
    该交易在 Neon EVM 内存池中链上调度,等待执行。

  • 交易执行:
    Neon Proxy 扫描内存池以查找预定的交易,并使用 Solana 的 ed25519 签名来验证它们。
    该交易在 Neon EVM 上执行,触发智能合约交互并更新 Solana 和 Neon 网络上的状态。

  • 执行后清理:
    Neon Proxy 通过清理临时资源来确保高效的系统维护,从而优化未来的交易。

虽然 Neon Proxy 可以作为 Solana 钱包和 Neon EVM 生态系统之间交互的推动者,但要充分发挥市场潜力,需要消除资产管理碎片化。跨网络的统一资产管理是推动采用和更集成的用户体验的关键。

ERC20ForSPL 更新:统一代币管理

SDK 的主要功能是其完善的 ERC20ForSPL 合约逻辑,解决了以太坊和 Solana 之间的互操作性挑战。合约通过使 ERC-20 代币能够在 Solana 环境中运行来连接两个网络。重点是完善 ERC20ForSPL 合约逻辑,以统一余额、保持向后兼容性并为开发人员引入强大的工具。具体方法如下:

统一余额管理

  • PDA 和 ATA 集成:Neon EVM 目前使用程序派生地址 (PDA) 来管理余额。随着关联代币账户 (ATA) 使用的实施,合约将整合这两个关键 Solana 组件,以统一的方式管理余额。
  • 转账优先级:在转账时,合约优先使用PDA余额,如果PDA余额不够,则使用ATA余额。
  • 消除碎片化:这种统一使得代币管理变得更简单、更少碎片化,为用户提供统一的代币管理体验。

这里的传输逻辑很简单:

  • 收件人地址验证:在发送代币之前,合约会检查收件人的地址是否与以太坊(EVM)兼容,并确保他们的 ATA(Solana 版本的钱包)已初始化。
  • 有条件传输:如果 ATA 已准备就绪,令牌将发送到那里。如果没有,则默认将令牌发送到 PDA。
  • 综合余额视图:balanceOf方法现在提供PDA和ATA余额的统一视图,确保准确反映用户的总持有量。

向后兼容性和安全性

虽然 ERC20ForSPL 合约已经更新,但保持向后兼容性(确保现有实现继续不中断地运行)同时添加新功能以增强功能仍然至关重要。
为此,合约通过保留状态变量、方法签名和事件参数来保持与现有实现的兼容性。它在 0xFf000000000000000000000000000000000000000000007 下引入了新的预编译方法,增强了开发人员的能力。isSolanaUser 确定 EVM 地址是否对应于 Solana 帐户,而 solanaAddress 检索与 EVM 地址关联的 Solana 公钥。

构建器预览:使用 SDK 创建的 Neon 交易的生命周期

步骤 1:在前端创建交易

  1. 使用 ethers.js、web3.js 或任何其他工具创建 Neon EVM 交易。
  2. 使用 Native Wallet SDK 将交易打包成 Solana 交易,通过 Neon Proxy 获取必要的元数据。
  3. (可选):添加 Solana 资产审批说明,以便 Neon EVM 智能合约直接访问。

步骤 2:Solana 钱包签署并提交交易

  1. dApp 前端将打包的交易发送到用户的 Solana 钱包。
  2. 用户通过其钱包 UI 审查并签署交易。
  3. 钱包将签名的交易提交给 Solana 网络,并将其安排在 Neon 的链上内存池中。

步骤 3:Neon Proxy 执行预定交易

  1. Neon Proxy 扫描链上内存池并识别准备执行的交易。
  2. 在 Neon EVM 上执行交易,从而导致 Neon 和 Solana 上的状态更新。
  3. 执行后从内存池中删除交易。

开始使用 SDK

访问存储库:深入研究GitHub上的 Neon Solana Signature SDK 。
开发人员文档:探索全面的文档
社区开发者支持:加入我们的Discord。查看 Discord 开发频道以获取实时帮助,并寻求我们仅限邀请的 Telegram Builder 聊天访问权限。

原文:https://neonevm.org/blog/unveiling-solana-signature-sdk-enabling-solana-users-to-access-evm-dapps

Solana 部署bigtable持久化数据节点

安装 gcloud CLI

sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates gnupg curl
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
sudo apt-get update && sudo apt-get install google-cloud-cli

启用IAM API

https://cloud.google.com/iam/docs/keys-create-delete?hl=zh-cn&cloudshell=true
注意:选择对应的项目,然后启用API

创建实例

实例ID和实例名称都设置为solana-ledger

创建密钥

新tab打开服务账号

点击创建服务账号

输入服务信息


点击创建并继续

选择Bigtable User


点击继续
最后一步可选,直接点击完成

创建密钥

刚新建完,是没有密钥的

点击该行最右侧的操作,管理密钥

添加键->创建新密钥

选择JSON格式

直接会弹出保存对话框,选择自己本地路径进行保存
例如名称为:bcskill-testnet-rpc-storage-93984f743890.json

创建数据表

blocks,entries,tx,tx-by-addr
创建完,挨个添加列族,列族名称都为x,基于版本的政策: 版本上限5

初始化

gcloud init

等待一段时间

Network diagnostic detects and fixes local network connection issues.
Checking network connection...done.                                                                                                                                                            
Reachability Check passed.
Network diagnostic passed (1/1 checks passed).

You must sign in to continue. Would you like to sign in (Y/n)?  
Go to the following link in your browser, and complete the sign-in prompts:

    https://accounts.google.com/o/oauth2/auth?response_type=c......

然后将上面的链接,复制到浏览器中,通过对应的谷歌账号进行登录,授权后,将返回的Key,输入到终端里
然后选择对应的实例

You are signed in as: [c..@gmail.com].

Pick cloud project to use: 
 [1] august-outlet-389809
 [2] bpay-2204b
 [3] ethereal-fort-389810
 [4] g-gcp-202408-01
 [5] infinite-alcove-389904
 [6] sturdy-web-389809
 [7] Enter a project ID
 [8] Create a new project
Please enter numeric choice or text value (must exactly match list item):  4
gcloud config configurations list
NAME     IS_ACTIVE  ACCOUNT            PROJECT          COMPUTE_DEFAULT_ZONE  COMPUTE_DEFAULT_REGION
default  True       c...@gmail.com  g-gcp-202408-01
mkdir ./config
mkdir -p ./data/accounts
mkdir ./logs
./solana-keygen new -o ./config/identity.json
export GOOGLE_APPLICATION_CREDENTIALS=./config/bcskill-testnet-rpc-storage-93984f743890.json
./agave-validator \
        --ledger ./data \
        --identity ./config/identity.json \
        --entrypoint 172.18.39.93:8001 \
        --dynamic-port-range 9000-9020 \
        --only-known-rpc \
        --known-validator Fc9UXBuQyMYBow5fTwgrYQf4k1Pwqx8o53yYBuvFQmnc \
        --allow-private-addr \
        --enable-rpc-transaction-history \
        --rpc-port 8896 \
        --private-rpc \
        --log ./logs/agave-validator.logs \
        --no-voting \
        --wal-recovery-mode skip_any_corrupted_record \
        --limit-ledger-size \
        --enable-bigtable-ledger-upload \
        --enable-cpi-and-log-storage \
        --accounts ./data/accounts \
        --no-genesis-fetch \
        --no-snapshot-fetch

查看上传数据

tail -f logs/agave-validator.logs | grep -a 'bigtable'
2024-12-16T06:31:21.663829260Z INFO  solana_ledger::bigtable_upload] Preparing the next 3 blocks for upload
[2024-12-16T06:31:21.683000789Z INFO  solana_metrics::metrics] datapoint: storage-bigtable-upload-block slot=1924269i transactions=1i entries=65i bytes=3050i
[2024-12-16T06:31:21.683031739Z INFO  solana_metrics::metrics] datapoint: storage-bigtable-upload-block slot=1924270i transactions=1i entries=65i bytes=3050i
[2024-12-16T06:31:21.683641626Z INFO  solana_metrics::metrics] datapoint: storage-bigtable-upload-block slot=1924271i transactions=1i entries=65i bytes=3049i
[2024-12-16T06:31:21.683647257Z INFO  solana_ledger::bigtable_upload] Upload took 19ms for 3 blocks
[2024-12-16T06:31:21.683659731Z INFO  solana_ledger::bigtable_upload] entire upload took 29ms
[2024-12-16T06:31:21.683872194Z INFO  solana_ledger::bigtable_upload] blockstore upload took 2.205029ms for 3 blocks (1360.53 blocks/s) errors: 0
[2024-12-16T06:31:22.683968514Z INFO  solana_ledger::bigtable_upload] Loading ledger slots from 1924272 to 1924274
[2024-12-16T06:31:22.684083929Z INFO  solana_ledger::bigtable_upload] Found 3 slots in the range (1924272, 1924274)
[2024-12-16T06:31:22.684093327Z INFO  solana_ledger::bigtable_upload] Loading list of bigtable blocks between slots 1924272 and 1924274...
[2024-12-16T06:31:22.684136962Z INFO  solana_metrics::metrics] datapoint: bigtable_blocks read_rows=1i
[2024-12-16T06:31:22.689038044Z INFO  solana_ledger::bigtable_upload] 3 blocks to be uploaded to the bucket in the range (1924272, 1924274)

常见排查

查看日志错误

tail -f logs/agave-validator.logs | grep -a 'ERROR'

https://github.com/solana-labs/solana-bigtable
https://docs.anza.xyz/implemented-proposals/rpc-transaction-history/

Solana - 全量同步节点数据

背景

当Solana 新增外部数据节点,开始同步后,前期的区块数据缺失

测试配置

./agave-validator --ledger "$TESTNET_DIR"/rpc-explorer --identity "$TESTNET_DIR"/rpc-explorer/identity.json --only-known-rpc --full-rpc-api --no-voting --log "$TESTNET_DIR"/rpc-explorer/rpc-val.log --log-messages-bytes-limit 104857600 --known-validator Hk42DsKjJxHufjRnzXMWD32... --allow-private-addr --enable-rpc-bigtable-ledger-storage --enable-rpc-transaction-history --rpc-port 8888 --entrypoint 172.18.39.93:8001

测试节点信息

服务类型 机器ip 目录 RPC端口
Validator 172.18.39.93 /data/agave/config/bootstrap-validator 8899
ExplorerRpc 172.18.34.76 /data/testnet-rpc/rpc-explorer 8888
PublicRpc1 172.18.34.76 /data/testnet-rpc/rpc-1 8890
PublicRpc2 172.18.34.76 /data/testnet-rpc/rpc-2 8892
PublicRpc3 172.18.34.76 /data/testnet-rpc/rpc-3 8894

测试查询

curl http://172.18.34.76:8894/ \
  -X POST \
  -H "Content-Type: application/json" \
  --data '{"jsonrpc": "2.0","id":1,"method":"getBlock","params":[1000, {"encoding": "jsonParsed"}]}'

返回

{"jsonrpc":"2.0","error":{"code":-32001,"message":"Block 1000 cleaned up, does not exist on node. First available block: 214204"},"id":1}

查询214203,与上面结果相同

查询214204

curl http://172.18.34.76:8894/ \
  -X POST \
  -H "Content-Type: application/json" \
  --data '{"jsonrpc": "2.0","id":1,"method":"getBlock","params":[214204, {"encoding": "jsonParsed"}]}'

正常返回数据

{"jsonrpc":"2.0","result":{"blockHeight":214202,"blockTime":1733728218,"blockhash":"99HZhuvyp2MN61eo2PzCmEuhNavaThR6ZU1aX6qeNqXf","parentSlot":214203,"previousBlockhash":"GcwivEHrdh7EDc72LayQqaEkYHDgPuRaEmbVEXSS7JdX",...}

分析

当前配置下,新起的节点只保存最新的区块,并没有从起始位置同步,与以太坊节点逻辑不同

跟进代码

先看下错误信息触发的逻辑
rpc/src/rpc.rs

pub async fn get_block(
        &self,
        slot: Slot,
        config: Option<RpcEncodingConfigWrapper<RpcBlockConfig>>,
    ) -> Result<Option<UiConfirmedBlock>> {
        if self.config.enable_rpc_transaction_history {
fn check_slot_cleaned_up<T>(
        &self,
        result: &std::result::Result<T, BlockstoreError>,
        slot: Slot,
    ) -> Result<()> {
        let first_available_block = self
            .blockstore
            .get_first_available_block()
            .unwrap_or_default();
        let err: Error = RpcCustomError::BlockCleanedUp {
            slot,
            first_available_block,
        }
        .into();
        if let Err(BlockstoreError::SlotCleanedUp) = result {
            return Err(err);
        }
        if slot < first_available_block {
            return Err(err);
        }
        Ok(())
    }

当开启enable_rpc_transaction_history时,查询get_block/get_block_time,当前查询的slot 小于get_first_available_block时,则代表已被清理

/// Blockstore 账本中第一个完整的区块
    pub fn get_first_available_block(&self) -> Result<Slot> {
        let mut root_iterator = self.rooted_slot_iterator(self.lowest_slot_with_genesis())?;
        let first_root = root_iterator.next().unwrap_or_default();
        // 如果第一个根是 slot 0,则它是创世。创世总是完整的,因此将其作为第一个可用返回是正确的。
        if first_root == 0 {
            return Ok(first_root);
        }
        // 否则,root-index 0 处的块永远无法完成,因为它缺少其父块哈希。必须根据前一个
        // 块的条目计算父块哈希。因此,第一个可用的完整块是root-index 1 处的块。
        Ok(root_iterator.next().unwrap_or_default())
    }

从代码分析,当前新起的节点应该是从cluster获取了快照,快速同步了state数据,为了精简本地存储,区块数据并没有同步

节点配置

参数 解释 备注
identity 验证者身份密钥对
authorized_voter_keypairs 包含额外的授权投票者密钥对。可以多次指定。[默认:--identity 密钥对]
vote_account 验证者投票账户公钥。如果未指定,投票将被禁用。账户的授权投票者必须是 --identity 密钥对 或由 --authorized-voter 参数设置
init_complete_file 如果验证器初始化完成后该文件尚不存在,则创建该文件
ledger_path 使用 DIR 作为账本位置
entrypoint 在此gossip入口点与集群会合
no_snapshot_fetch 不要尝试从集群中获取快照,如果存在,则从本地快照开始
no_genesis_fetch 不要从集群中获取创世数据
no_voting 启动验证者但不进行投票
check_vote_account 启动时检查投票账户状态是否正常。RPC_URL 处的 JSON RPC 端点必须公开 --full-rpc-api
restricted_repair_only_mode 不要发布 Gossip、TPU、TVU 或 Repair Service 端口。这样做会导致节点以有限的容量运行,从而减少其对集群其余部分的暴露。当启用此标志时,--no-voting 标志是隐式的
dev_halt_at_slot 当验证器到达指定时隙时,停止验证器 可用于特殊场景测试
rpc_port 在此端口上启用 JSON RPC,并在下一个端口上启用 RPC websocket 如果当前RPC为8545,默认WS为8546
full_rpc_api 公开用于查询链状态和交易历史的 RPC 方法 对于Public RPC必须的
private_rpc 不要发布 RPC 端口供他人使用 对于core RPC应开启
no_port_check 启动时不执行 TCP/UDP 可达端口检查
enable_rpc_transaction_history 通过 JSON RPC 启用历史交易信息,包括 getConfirmedBlock API。这将导致磁盘使用率和 IOPS 增加 导致磁盘占用的主要原因
enable_rpc_bigtable_ledger_storage 从 BigTable 实例获取历史交易信息作为本地账本数据的后备 BigTable
enable_bigtable_ledger_upload 将新确认的块上传到 BigTable 实例 BigTable
enable_extended_tx_metadata_storage 存储的历史交易信息中包含 CPI 内部指令、日志和返回数据 类似以太坊debug?
rpc_max_multiple_accounts 覆盖 getMultipleAccounts JSON RPC 方法接受的默认最大账户数 默认100
health_check_slot_distance 如果验证器的最新重放乐观确认槽位于集群最新乐观确认槽的指定槽数之内,则报告此验证器为健康
rpc_faucet_addr 使用此水龙头地址启用 JSON RPC“requestAirdrop”API。 关闭
account_paths 逗号分隔的持久帐户位置。可以多次指定。[默认值:LEDGER/accounts]
account_shrink_path 指向账户收缩路径的路径,可以保存压缩的账户集
accounts_hash_cache_path 使用 PATH 作为账户哈希缓存位置 [默认值:LEDGER/accounts_hash_cache]
snapshots 使用 DIR 作为快照的基本位置。将创建一个名为“snapshots”的子目录。[默认值:--ledger 值]
use_snapshot_archives_at_startup 启动时,应何时提取快照存档,而不是使用磁盘上已有的存档。指定“always”将始终通过提取快照存档来启动,并忽略磁盘上已有的任何快照相关状态。请注意,从快照存档启动将产生与提取存档和重建本地状态相关的运行时成本。指定“never”将永远不会从快照存档启动,并且只会使用磁盘上已有的快照相关状态。如果磁盘上没有状态,启动将失败。请注意,这将使用可用的最新状态,该状态可能比最新的快照存档更新。指定“when-newest”将使用磁盘上已有的快照相关状态,除非有比它更新的快照存档。如果在节点停止时下载了新的快照存档,则可能会发生这种情况。
full_snapshot_archive_path 使用 DIR 作为完整快照存档位置 [默认值:--snapshots 值]
incremental_snapshot_archive_path 使用 DIR 作为增量快照存档位置 [默认值:--snapshots 值]
tower 使用 DIR 作为文件塔存储位置 [默认值:--ledger 值]
tower_storage 塔的存放位置
etcd_endpoint 要连接的 etcd gRPC 端点
etcd_domain_name 用于验证 etcd 服务器的 TLS 证书的域名
etcd_cacert_file 使用此 CA 包验证 etcd 端点的 TLS 证书
etcd_key_file 建立与 etcd 端点的连接时使用的 TLS 密钥文件
etcd_cert_file 建立与 etcd 端点的连接时使用的 TLS 证书
gossip_port 验证者的 Gossip 端口号
gossip_host 验证器在 gossip 中宣传的 Gossip DNS 名称或 IP 地址 [默认值:ask --entrypoint,当未提供 --entrypoint 时为 127.0.0.1]
public_tpu_addr 指定要在 gossip 中宣传的 TPU 地址 [默认:当未提供 --entrypoint 时询问 --entrypoint 或 localhost]
public_tpu_forwards_addr 指定 TPU 转发地址以在 gossip 中宣传 [默认:当未提供 --entrypoint 时询问 --entrypoint 或 localhost]
public_rpc_addr 验证器在 gossip 中公开宣传的 RPC 地址。对于在负载均衡器或代理后面运行的验证器很有用 [默认:使用 -rpc-bind-address / --rpc-port]
dynamic_port_range 用于动态分配端口的范围
maximum_local_snapshot_age 如果本地快照距离其他验证者可下载的最高快照的槽位少于这么多,则重复使用本地快照
no_incremental_snapshots 禁用增量快照
snapshot_interval_slots 生成快照之间的时隙数。如果启用了增量快照,则此设置增量快照间隔。如果禁用了增量快照,则此设置完整快照间隔。将此设置为 0 可禁用所有快照。
full_snapshot_interval_slots 生成完整快照之间的间隔数。必须是增量快照间隔的倍数。仅在启用增量快照时使用。
maximum_full_snapshots_to_retain 清除旧快照时要保留的完整快照存档的最大数量。 排除下本地增长是否与快照有关?
maximum_incremental_snapshots_to_retain 清除旧快照时要保留的增量快照存档的最大数量。
snapshot_packager_niceness_adj 将此值添加到快照打包器线程的优先级。负值增加优先级,正值降低优先级。
minimal_snapshot_download_speed 快照下载的最小速度(以字节/秒为单位)。如果初始下载速度低于此阈值,系统将针对不同的 rpc 节点重试下载。
maximum_snapshot_download_abort 遇到快照下载缓慢时中止并重试的最大次数。
contact_debug_interval 打印gossip联系人调试之间的毫秒数。
no_poh_speed_test 跳过 PoH 速度检查。
no_os_network_limits_test 跳过对操作系统网络限制的检查。
no_os_memory_stats_reporting 禁用操作系统内存统计信息的报告。
no_os_network_stats_reporting 禁用操作系统网络统计信息的报告。
no_os_cpu_stats_reporting 禁用操作系统 CPU 统计信息的报告。
no_os_disk_stats_reporting 禁用操作系统磁盘统计信息的报告。
snapshot_version 输出快照版本
limit_ledger_size 将此数量的碎片保留在根槽中。
rocksdb_shred_compaction 控制 RocksDB 如何压缩碎片。警告:在选项之间切换时,您将丢失 Blockstore 数据。可能的值是:'level':使用 RocksDB 的默认(级别)压缩存储碎片。'fifo':在 RocksDB 的 FIFO 压缩下存储碎片。此选项在 Blockstore 的磁盘写入字节方面更有效。
rocksdb_fifo_shred_storage_size 碎片存储大小(以字节为单位)。建议值至少为分类帐存储大小的 50%。如果未指定此参数,我们将根据 --limit-ledger-size 分配适当的值。如果没有提供 --limit-ledger-size,则表示分类帐大小没有限制,因此 rocksdb_fifo_shred_storage_size 也将不受限制。
rocksdb_ledger_compression 用于压缩交易状态数据的压缩算法。启用压缩可以节省约 10% 的账本大小。 "none", "lz4", "snappy", "zlib"
rocksdb_perf_sample_interval 控制收集 RocksDB 读/写性能样本的频率。Perf 样本以 1/ROCKS_PERF_SAMPLE_INTERVAL 采样率收集。
skip_startup_ledger_verification 在验证器启动时跳过分类账验证。
cuda 使用CUDA TODO
require_tower 如果未找到已保存的塔状态则拒绝启动
expected_genesis_hash 要求创世块有这个哈希值
expected_bank_hash 当等待绝对多数x时,要求x处的银行拥有此哈希值
expected_shred_version 要求碎片版本为此值
logfile 将日志重定向到指定文件,‘-’ 表示标准错误。向验证器进程发送 SIGUSR1 信号将导致其重新打开日志文件
wait_for_supermajority 处理完账本后,下一个 slot 是 SLOT,等到 gossip 上出现绝大多数的权益后,再开始 PoH
no_wait_for_vote_to_start_leader 如果验证器启动时没有账本,它将等待直到看到投票进入根槽后才开始生成区块。这可以防止双重签名。关闭可避免双重签名区块的风险。
hard_forks 在此位置添加硬分叉
known_validators 此验证器必须将快照哈希发布到 gossip 中才能接受。可以多次指定。如果未指定,则将接受任何快照哈希
debug_key 当处理引用给定密钥的交易时进行记录。
only_known_rpc 仅使用已知验证者的 RPC 服务 public<->core<->validator
repair_validators 请求修复的验证器列表。如果指定,则不会向此集合之外的验证器请求修复 [默认:所有验证器]
repair_whitelist 优先处理修复请求的验证器列表。如果指定,列表中验证器的修复请求将优先于其他验证器的请求。[默认:所有验证器]
gossip_validators 与之进行 gossip 的验证器列表。如果指定,gossip 将不会从此集合之外的验证器推送/拉取信息。[默认值:所有验证器]
tpu_coalesce_ms TPU 接收器中等待数据包合并的毫秒数。
tpu_use_quic 使用 QUIC 发送交易。
tpu_disable_quic 不要使用 QUIC 发送交易。
tpu_enable_udp 启用 UDP 来接收/发送交易。
tpu_connection_pool_size 控制每个远程地址的 TPU 连接池大小
tpu_max_connections_per_ipaddr_per_minute 控制每分钟每个 IpAddr 的客户端连接速率。
staked_nodes_overrides 提供 yaml 文件的路径,其中包含针对特定身份的质押的自定义覆盖。覆盖此验证器认为对网络中其他对等方有效的质押金额。质押金额用于计算对等方和投票数据包发送方阶段允许的 QUIC 流数量。文件格式:`staked_map_id: {pubkey: SOL 质押金额}
bind_address 绑定验证器端口的 IP 地址
rpc_bind_address 绑定 RPC 端口的 IP 地址 [默认值:如果存在 --private-rpc,则为 127.0.0.1,否则使用 --bind-address]
rpc_threads 用于服务 RPC 请求的线程数 默认:cpu核心数
rpc_niceness_adj 将此值添加到 RPC 线程的优先级。负值增加优先级,正值降低优先级。
rpc_bigtable_timeout BigTable 支持的 RPC 请求超时前的秒数
rpc_bigtable_instance_name 要上传到的 Bigtable 实例的名称
rpc_bigtable_app_profile_id 请求中使用的 Bigtable 应用程序配置文件 ID
rpc_bigtable_max_message_size Bigtable Grpc 客户端使用的最大编码和解码消息大小
rpc_pubsub_worker_threads PubSub 工作线程
rpc_pubsub_enable_block_subscription 启用不稳定的 RPC PubSub blockSubscribe 订阅
rpc_pubsub_enable_vote_subscription 启用不稳定的 RPC PubSub voteSubscribe 订阅
rpc_pubsub_max_active_subscriptions RPC PubSub 在所有连接中接受的最大有效订阅数。
rpc_pubsub_queue_capacity_items RPC PubSub 在所有连接中存储的最大通知数量。
rpc_pubsub_queue_capacity_bytes RPC PubSub 在所有连接中存储的通知的最大总大小。
rpc_pubsub_notification_threads RPC PubSub 用于生成通知的最大线程数。0 将禁用 RPC PubSub 通知
rpc_send_transaction_retry_ms 通过 rpc 服务发送的事务重试的速率
rpc_send_transaction_batch_ms 通过 rpc 服务批量发送交易的速率。
rpc_send_transaction_leader_forward_count 通过 RPC 服务转发交易的未来领导者的数量。
rpc_send_transaction_default_max_retries 当请求未指定时,交易广播重试的最大次数,否则重试直至到期。
rpc_send_transaction_service_max_retries 事务广播重试的最大次数,无论请求的值是多少。
rpc_send_transaction_batch_size 要批量发送的交易的大小。 默认1
rpc_send_transaction_retry_pool_max_size 事务重试池的最大大小。
rpc_send_transaction_tpu_peer 向其他节点广播交易,以替代当前领导者
rpc_send_transaction_also_leader 使用 --rpc-send-transaction-tpu-peer HOST:PORT,也发送给当前领导者
rpc_scan_and_fix_roots 在启动时验证块存储根并修复任何差距
rpc_max_request_body_size rpc服务接受的最大请求体大小
geyser_plugin_config 指定 Geyser 插件的配置文件。
snapshot_archive_format 要使用的快照存档格式。 可能影响存储空间,支持"zstd", "lz4"
max_genesis_archive_unpacked_size 下载的 Genesis 档案的最大未压缩文件总大小
wal_recovery_mode 恢复分类帐数据库预写日志的模式。
poh_pinned_cpu_core 实验:指定 PoH 固定到哪个 CPU 核心
poh_hashes_per_batch 在 PoH 服务中指定每批哈希值
process_ledger_before_services 在启动网络服务之前,全面处理本地账本
account_indexes 启用帐户索引,按所选帐户字段进行索引
account_index_exclude_key 当启用账户索引时,从索引中排除此键。
account_index_include_key 启用账户索引后,仅将特定键包含在索引中。这会覆盖 --account-index-exclude-key。
accounts_db_verify_refcounts 调试选项用于在清理之前扫描所有附加向量并验证帐户索引引用计数
accounts_db_test_skip_rewrites 调试选项可跳过免租账户的重写,但仍将其添加到银行增量哈希计算中
no_skip_initial_accounts_db_clean 验证快照银行时不要跳过初始账户清理
accounts_db_squash_storages_method 使用此方法将多个帐户存储文件压缩在一起
accounts_db_access_storages_method 使用此方法访问帐户存储
accounts_db_ancient_append_vecs 早于 (slots_per_epoch - SLOT-OFFSET) 的 AppendVecs 被挤压在一起。
accounts_db_cache_limit_mb 帐户数据的写入缓存可以达到多大。如果超出此值,则会更积极地刷新缓存。
accounts_db_read_cache_limit_mb 帐户数据的读取缓存可以达到多大,以兆字节为单位。如果给定单个值,则它将是缓存的最大大小。如果给定一对值,则它们将是缓存的低水位线和高水位线。当缓存超过高水位线时,条目将被逐出,直到大小达到低水位线。
accounts_index_scan_results_limit_mb 帐户索引扫描的累积结果可以达到多大。如果超出此值,则扫描中止。
accounts_index_memory_limit_mb 帐户索引可以占用多少内存。如果超出此限制,一些帐户索引条目将存储在磁盘上。 协调内存与磁盘的占比
accounts_index_bins 将账户指数划分为的箱数
partitioned_epoch_rewards_compare_calculation 进行正常的 epoch 奖励分配,但也使用分区奖励代码路径计算奖励,并比较最终的投票和质押账户
partitioned_epoch_rewards_force_enable_single_slot 强制进行分区奖励分配,但将所有奖励分配在 epoch 的第一个 slot 中。这应该与正常奖励分配达成共识。
accounts_index_path 持久帐户索引位置。可以多次指定。[默认值:LEDGER/accounts_index]
accounts_db_test_hash_calculation 使用 AccountsHashVerifier 中的存储启用哈希计算测试。这需要计算成本。
accounts_shrink_optimize_total_space 当设置为 true 时,系统将缩减最稀疏的账户,当整体缩减率高于指定的账户缩减率时,缩减将停止,并跳过所有其他不太稀疏的账户。
accounts_shrink_ratio 指定要缩减的帐户的缩减比率。缩减比率定义为活动字节数与使用的总字节数之比。如果帐户的缩减比率小于此比率,则该帐户将成为缩减的候选对象。该值必须介于 0 和 1.0 之间(含)。
allow_private_addr 允许联系私有 IP 地址
log_messages_bytes_limit 截断前写入程序日志的最大字节数
banking_trace_dir_byte_limit 明确启用银行跟踪,默认情况下启用该跟踪并为模拟领导者块写入跟踪文件,在账本中保留默认或指定的总字节数。此标志可用于覆盖其字节限制。
disable_banking_trace 禁用银行追踪
delay_leader_block_for_pending_fork 在重放从当前分叉下降且比下一个领导者时隙更低的区块时,延迟领导者区块的创建。如果我们不在这里延迟,我们的新领导者区块将位于与我们正在重放的区块不同的分叉上,并且集群很有可能会确认该区块的分叉而不是我们的领导者区块的分叉,因为它是在我们开始创建我们的区块之前创建的。
block_verification_method 切换用于验证账本条目的交易调度方法
block_production_method 用于生成分类帐条目的切换交易调度方法
unified_scheduler_handler_threads 更改统一调度器专用于每个块的事务执行线程数,否则按 cpu_cores/4 计算
wen_restart 指定后,验证器将进入 Wen Restart 模式,该模式将暂停正常活动。在此模式下,验证器将传播其最后一次投票,以就安全重启槽达成共识并修复所选分叉上的所有块。安全槽将是最新乐观确认槽的后代,以确保我们不会回滚任何乐观确认槽。此模式下的进度将保存在提供的文件位置。如果达成共识,验证器将自动退出,然后执行 wait_for_supermajority 逻辑,以便集群恢复执行。进度文件将保留以供将来调试。集群恢复正常运行后,可以调整验证器参数以删除 --wen_restart 并将 expected_shred_version 更新为共识中商定的新 shred_version。如果 wen_restart 失败,请参考进度文件(proto3 格式)进行进一步调试。

总结

默认情况下,当启动 RPC 节点时,它将根据通过 Solana 网络接收的区块构建其本地分类账。此本地分类账在节点启动时下载的帐户快照开始。如果未--no-snapshot-fetch在solana-validator命令行中添加任何内容,验证器通常会在启动时从网络中提取快照。这会在停止 RPC 节点的点和下载帐户快照的点之间在分类账中留下空缺间隙。为避免这种情况,请始终--no-snapshot-fetch在第一次启动节点后指定。请记住,每次提取快照时都会在本地分类账中创建一个空缺间隙。

本地账本的大小由参数决定--limit-ledger-size,以碎片为单位。碎片是固定的数据单位。碎片和块之间的转换并不固定,因为块的大小可以变化。因此,很难说节点将存储多少以时间或块数衡量的历史记录。必须根据需要对其进行调整。一个好的起点可以是 2.5 亿到 3.5 亿个碎片,这应该大约涵盖一个时代,也就是大约 3 天。

RPC 节点将存储的确切数据量还取决于参数--enable-cpi-and-log-storage和--enable-rpc-transaction-history。这些对于节点保留和提供完整的块和交易数据是必需的。

节点只能提供已存储在其本地分类账中的数据。这意味着历史记录将始终从启动节点的点开始(实际上:启动节点的快照槽)。如果网络当前处于槽 N,并且在槽 M 提取快照,那么节点将开始重建其在槽 M 和槽 N 之间的历史记录。这就是在期间发生的事情catchup,节点正在处理(重放)在 M 和 N 之间发生的所有事情,直到它赶上网络并可以处理所有当前传入的数据。

节点(理论上)可以存储高速存储所能容纳的尽可能多的历史记录(例如,如果不指定--limit-ledger-size或为其赋予巨大的值)。但是,这不会缩减到创世纪。要获取所有历史记录,可以使用内置的 Google BigTable 支持。既可以设置节点以将数据上传到 Google BigTable 实例,在那里它可以永久用于历史查询。还可以配置节点以支持对 BigTable 实例的查询。在这种情况下,对于节点在其本地分类帐中没有的任何查询,它将向 Google BigTable 发出请求,如果它在 Google BigTable 中找到它,它可以从那里提取数据。

Solana 区块链每秒能够创建许多交易。由于链上的交易量很大,RPC 节点将整个区块链存储在机器上是不切实际的。相反,RPC 操作员使用标志--limit-ledger-size来指定在 RPC 节点上存储多少个块。如果 RPC 节点的用户需要历史区块链数据,那么 RPC 服务器将必须通过 Solana bigtable 实例访问较旧的块。

如果有兴趣设置自己的 bigtable 实例,请参阅 Solana GitHub 存储库中的以下文档:solana-labs/solana-bigtable

参考

https://docs.anza.xyz/operations/setup-an-rpc-node
https://github.com/Fankouzu/solana-basic-ui/blob/fd33c886c8331eab112dadd4a42646ffb8167cec/docs/SolanaDocumention/more/exchange.md?plain=1#L76
https://github.com/rpcpool/solana-rpc-ansible/blob/3e926a16c847f55e7adf864e0cd679043089b787/README.md#access-to-historical-data
https://github.com/solana-labs/solana-bigtable/blob/79234622ab0cb26b6f4a158ddb7eace9c19186a0/warehouse-basic.sh

Solana核心概念-跨程序调用 (CPI)

跨程序调用(CPI)是指一个程序调用另一个程序的指令(instruction)。这种机制允许 Solana 程序的可组合性。 这种机制允许 Solana 程序的可组合性。

你可以将指令视为程序向网络公开的 API 端点,而 CPI 则是一个 API 内部调用另一个 API。

当一个程序发起对另一个程序的跨程序调用(CPI)时:

  • 调用程序(A)从初始交易中获得的签名者(signer)权限扩展到被调用程序(B)
  • 被调用程序(B)可以进一步对其它程序进行 CPI,最多深度为 4(例如 B->C,C->D)
  • 程序可以代表从其程序 ID 派生的 PDAs 进行“签名”

Solana 程序运行时定义了一个名为 max_invoke_stack_height 的常量,其值设定为 5。 这表示程序指令调用堆栈的最大高度。 堆栈高度从交易指令的 1 开始,每次程序调用另 一个指令时增加 1。 此设置有效地将 CPI 的调用深度限制为 4。

关键点

  • CPI 使 Solana 程序指令能够直接调用另一个程序的指令。
  • 调用程序的签名者权限扩展到被调用程序。
  • 在进行 CPI 时,程序可以代表从其自身程序 ID 派生的 PDAs 进行“签名”。
  • 被调用程序可以对其它程序进行额外的 CPI,最多深度为 4。

如何编写 CPI

编写 CPI 指令遵循与构建添加到交易中的 instruction 相同的模式。在底层,每个 CPI 指令必须指定以下信息:

  • 程序地址:指定被调用的程序
  • 账户:列出指令读取或写入的每个账户,包括其它程序
  • 指令数据:指定要调用的程序上的指令,以及指令所需的任何附加数据(函数参数)

根据你要调用的程序,可能有一些 crate 提供了用于构建指令的辅助函数。 然后,程序使 用solana_program crate 中的以下函数之一执行 CPI:

  • invoke —— 当没有 PDA 签名者时使用
  • invoke_signed —— 当调用程序需要使用从其程序 ID 派生的 PDA 进行签名时使用

基础 CPI

invoke函数用于进行不需要 PDA 签名者的 CPI。 在进行 CPI 时,提供给调用程序的签名者自动扩 展到被调用程序。

pub fn invoke(
    instruction: &Instruction,
    account_infos: &[AccountInfo<'_>]
) -> Result<(), ProgramError>

这是一个在 Solana Playground 上的示例程序,该程序使用invoke函数调用系统程序上的转账指令。你也可以参 考基础 CPI 指南了解更多细节。 你也可 以参考基础 CPI 指南了解更多细节。

带 PDA 签名者的 CPI

invoke_signed函 数用于进行需要 PDA 签名者的 CPI。 用于派生签名者 PDA 的种子作为signer_seeds传 递给invoke_signed函数。

你可以参考程序派生地址页面了解 PDA 的派生方式。

pub fn invoke_signed(
    instruction: &Instruction,
    account_infos: &[AccountInfo<'_>],
    signers_seeds: &[&[&[u8]]]
) -> Result<(), ProgramError>

运行时使用授予调用程序的权限来确定可以扩展到被调用程序的权限。 在此上下文中,权 限指的是签名者和可写账户。 例如,如果调用程序正在处理的指令包含签名者或可写账 户,那么调用程序可以调用另一个也包含了该签名者和/或可写账户的指令。

虽然 PDAs 没有私钥 ,但它们仍然可以通过 CPI 在指令中充当签名者。为了验证 PDA 是从调用程序派生的,生成 PDA 所用的种子必须作 为signers_seeds包含在内。 为了验证 PDA 是从调用程序派生的,生成 PDA 所用的种子 必须作为signers_seeds包含在内。

当 CPI 被处理时,Solana 运行时使用signers_seeds和调用程序的program_id 进 行内部调用create_program_address。 如果找到有效的 PDA,该地址 将被添加为有效签名者 。

这是一个在 Solana Playground 上的示例程序,该程序使用invoke_signed函数调用系统程序上的转账指令,并带有 PDA 签名者。 你可以参 考带 PDA 签名者的 CPI 指南了 解更多细节。