Solana - 全量同步节点数据

2024-12-11 10:05:00

背景

当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端口
Validator172.18.39.93/data/agave/config/bootstrap-validator8899
ExplorerRpc172.18.34.76/data/testnet-rpc/rpc-explorer8888
PublicRpc1172.18.34.76/data/testnet-rpc/rpc-18890
PublicRpc2172.18.34.76/data/testnet-rpc/rpc-28892
PublicRpc3172.18.34.76/data/testnet-rpc/rpc-38894

测试查询

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使用CUDATODO
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_msTPU 接收器中等待数据包合并的毫秒数。
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_timeoutBigTable 支持的 RPC 请求超时前的秒数
rpc_bigtable_instance_name要上传到的 Bigtable 实例的名称
rpc_bigtable_app_profile_id请求中使用的 Bigtable 应用程序配置文件 ID
rpc_bigtable_max_message_sizeBigtable Grpc 客户端使用的最大编码和解码消息大小
rpc_pubsub_worker_threadsPubSub 工作线程
rpc_pubsub_enable_block_subscription启用不稳定的 RPC PubSub blockSubscribe 订阅
rpc_pubsub_enable_vote_subscription启用不稳定的 RPC PubSub voteSubscribe 订阅
rpc_pubsub_max_active_subscriptionsRPC PubSub 在所有连接中接受的最大有效订阅数。
rpc_pubsub_queue_capacity_itemsRPC PubSub 在所有连接中存储的最大通知数量。
rpc_pubsub_queue_capacity_bytesRPC PubSub 在所有连接中存储的通知的最大总大小。
rpc_pubsub_notification_threadsRPC 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_sizerpc服务接受的最大请求体大小
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 中找到它,它可以从那里提取数据。

参考

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

当前页面是本站的「Baidu MIP」版。发表评论请点击:完整版 »