ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Solana Tick 校验机制深度解析:从 Slot 结构设计到恶意传输惩罚

Solana Tick 校验机制深度解析:从 Slot 结构设计到恶意传输惩罚 Solana Tick 校验机制深度解析从 Slot 结构设计到恶意传输惩罚【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solanaSolana 的共识依赖于 Proof of HistoryPoH时间链而 Tick计时单元是连接 PoH 与 Slot 的基石。本篇技术指南基于 tick-verification.md 提案完整梳理 Slot 中 Tick 的生成标准、校验规则与失败处理并结合ledger、core、sdk等模块的真实源码说明 Blockstore 如何接收 shred、ReplayStage 如何回放验证 Tick以及哪些异常会触发标记死亡dead与罚没证明slashing proof。读完本文你将掌握 Solana 中ticks_per_slot、hashes_per_tick、LAST_SHRED_IN_SLOT三个关键参数/标志的精确语义理解一条恶意或损坏的传输如何在系统内被识别、丢弃或触发惩罚并能在实际节点运维中准确解读相关错误日志与测试用例。一、背景Tick 在 Solana 链式结构中的位置在深入校验规则之前先明确几个核心概念Slot时隙Solana 中一个 Leader 连续出块的时间单位每个 Slot 由固定数量的 Tick 组成。Tick计时单元PoH 哈希链上的心跳标记。Leader 在哈希链上滚动一定数量的哈希后产生一个 TickTick 之间不含交易只推进时钟。Shred碎片Slot 内数据Entries被切分成的网络传输单元带 FEC 纠删编码并携带ShredFlags标志位。关键配置项在 sdk/src/poh_config.rs 的PohConfig中定义target_tick_duration集群目标 Tick 速率当前由DEFAULT_TICKS_PER_SECOND换算而来hashes_per_tick: None时进入低功耗模式Validator 直接休眠该时长而非持续哈希hashes_per_tick每产生一个 Tick 之前需要滚动的哈希数量。None表示启用低功耗模式。// sdk/src/poh_config.rs pub struct PohConfig { /// The target tick rate of the cluster. pub target_tick_duration: Duration, /// The target total tick count to be produced; used for testing only pub target_tick_count: Optionu64, /// How many hashes to roll before emitting the next tick entry. /// None enables Low power mode, which makes the validator sleep /// for target_tick_duration instead of hashing pub hashes_per_tick: Optionu64, }二、Slot 结构Tick 的正确形态定义提案 tick-verification.md 首先规定了合法 Slot 必须具备的结构特征这是后续所有校验的基准每个 Slot 必须恰好包含ticks_per_slot个 Tick。该参数来自创世配置GenesisConfig见 sdk/src/genesis_config.rs主网默认值为 800即每 Slot 400ms、每 Tick 400ms/800500µs。Slot 中的最后一个 shred 必须且只能包含最后一个 Tick 的全部内容不能混入其他 Entry交易。Leader 必须为承载最后一个 Tick 的 shred 打上LAST_SHRED_IN_SLOT标志。相邻两个 Tick 之间必须间隔恰好hashes_per_tick个哈希这是 PoH 时间链连续性校验的核心。LAST_SHRED_IN_SLOT标志在 ledger/src/shred.rs 中以ShredFlags位标志的形式定义且隐含DATA_COMPLETE_SHRED语义// ledger/src/shred.rs // LAST_SHRED_IN_SLOT also implies DATA_COMPLETE_SHRED. // So it cannot be LAST_SHRED_IN_SLOT if not also DATA_COMPLETE_SHRED. const LAST_SHRED_IN_SLOT 0b1100_0000;对应的方法Shred::last_in_slot()/set_last_in_slot()ledger/src/shred.rs 第 514、523 行附近供 Blockstore 与 ReplayStage 在插入和回放时读取、设置该标志。三、恶意传输的两种处理路径提案将恶意或错误的传输T划分为两类处理策略核心考量是集群是否可能对某个合法替代传输T达成共识3.1 存在合法替代传输T需要罚没证明Slashing Proof如果 Leader 能够在不违反重复传输罚没规则的前提下同时生成错误传输T与某个替代传输T例如T是T的子集那么集群必须容忍两种传输同时存活的可能性。此时不能简单地把T标记为死亡因为集群可能已经围绕T达成共识——一旦误判会把一条合法链的父 Slot 判死导致链分叉。这类情况必须收集slashing proof罚没证明来惩罚作恶 Leader即提交两份相互矛盾的 shred 作为证据。3.2 不存在合法替代直接标记 Slot 死亡如果错误传输不存在任何合法的替代版本则可以直接把该 Slot 标记为dead死亡/不可回放。此时是否需要 slashing proof 取决于取证可行性例如两份互相矛盾的签名 shred 是否都能被获取。这一二分法是整个 Tick 校验错误处理的设计主线能证伪的作恶行为走罚没无法证伪的错误直接判死从而在安全性与活性之间取得平衡。四、Blockstore 接收 shred 时的校验与冲突检测提案 tick-verification.md 规定Blockstore 在insert_shreds过程中入口见 ledger/src/blockstore.rs 的insert_shreds_handle_duplicate/do_insert_shreds对每个新 shreds分三种情况处理s带LAST_SHRED_IN_SLOT标志检查该 Slot 是否已存在索引更大的 shredss.index s.index。若存在s与s共同构成一份 slashing proof——同一个 Slot 的 Leader 既声称最后 shred 是索引 i又发送了索引更大的 shred自相矛盾。Blockstore 已收到带LAST_SHRED_IN_SLOT标志、索引为i的 shreds若新来的s.index i则同样构成 slashing proof且 Blockstore不会插入s。同一索引的重复 shred完全相同的重复 shred 会被忽略而同一索引但内容不同的 shrednon-duplicate属于可罚没条件细节见提案中引用的Leader Duplicate Block Slashing章节。上述规则在源码中有精确对应。should_insert_data_shredledger/src/blockstore.rs 第 1611 行起实现了两类检查索引不小于已记录的last_index当slot_meta.last_index已存在且shred_index last_index时拒绝插入并通过store_duplicate_slotPossibleDuplicateShred::LastIndexConflict(shred, ending_shred)上报冲突即构造罚没证据同时输出blockstore_error数据点日志。LAST_SHRED_IN_SLOT但索引小于已接收数last_in_slot shred_index slot_meta.received时同样视为冲突拒绝插入并记录 duplicate。// ledger/src/blockstore.rsshould_insert_data_shred 核心逻辑 let last_index slot_meta.last_index; if last_index.map(|ix| shred_index ix).unwrap_or_default() { // ... store_duplicate_slot push(PossibleDuplicateShred::LastIndexConflict(...)) return false; // 不插入等待罚没流程 } if last_in_slot shred_index slot_meta.received { // ... 同样记录 LastIndexConflict拒绝插入 return false; }此外insert_shreds_handle_duplicate会把检测到的duplicate_shreds逐个交给handle_duplicate回调如 gossip 层的duplicate_shred处理见 gossip/src/duplicate_shred.rs将冲突证据广播到集群为后续罚没或判死提供依据。在blockstore_meta.rs中SlotMeta.last_index在首次收到is_last_in_slot的 shred 时被写入ledger/src/blockstore.rs 第 4058 行if is_last_in_slot slot_meta.last_index.is_none() { slot_meta.last_index Some(index) }成为后续所有last_index冲突检测的基准。五、ReplayStage 回放与 Tick 验证三种失败场景提案规定ReplayStage 从 Blockstore 回放 Entries实现在 core/src/replay_stage.rs逐 Slot 跟踪已见 Tick 数量并验证相邻 Tick 之间恰好有hashes_per_tick个哈希。当最后一个 shred 的 Tick 被回放完毕后再核对 Tick 总数。三种失败场景均导致Slot 标记为 dead失败场景 1哈希间隔错误任意两个连续 Tick 之间的哈希数! hashes_per_tick→ 标记 Slot 死亡。失败场景 2Tick 数量错误Tick 总数! ticks_per_slot→ 标记 Slot 死亡。失败场景 3缺少收尾 shredTick 数已达到ticks_per_slot但始终未见LAST_SHRED_IN_SLOT标志的 shred → 标记 Slot 死亡。5.1 最后一个 shred 必须是 Tick当 ReplayStage 遇到带LAST_SHRED_IN_SLOT标志的 shred 时额外校验该签名 shred 必须能被反序列化为一个 Tick。若反序列化失败或反序列化结果是一个普通 Entry交易条目则同样标记 Slot 死亡——这正对应提案中最后一个 shred 只能包含最后一个 Tick 的全部内容的结构要求。5.2 源码中的判死实现与错误类型mark_dead_slot是判死动作的实现入口core/src/replay_stage.rs 第 2181 行起它会记录replay-stage-mark_dead_slot数据点并更新blockstore.slots_stats.mark_dead(slot)。ReplayStage 的单元测试完整覆盖了上述失败场景是理解校验语义的最佳教材core/src/replay_stage.rs 第 4790 行起测试构造的非法输入期望错误test_dead_fork_invalid_tick_hash_count用hashes_per_tick - 1个哈希构造 TickBlockError::InvalidTickHashCounttest_dead_fork_invalid_slot_tick_count用entry::create_ticks(ticks_per_slot 1, ...)生成超量 TickBlockError::TooManyTickstest_dead_fork_entry_verification_failure使用错误的 blockhash 构造 EntryBlockError::InvalidEntryHashtest_dead_fork_bad_tick_hash_count对应场景生成ticks_per_slot - 1个 TickBlockError::TooFewTicks例如test_dead_fork_invalid_tick_hash_count的核心代码// core/src/replay_stage.rs let too_few_hashes_tick Entry::new(blockhash, hashes_per_tick - 1, vec![]); entries_to_test_shreds([too_few_hashes_tick], slot, slot.saturating_sub(1), false, 0, true); // 期望 Err(BlockstoreProcessorError::InvalidBlock(BlockError::InvalidTickHashCount))这里Entry::new的第二参数正是该 Tick 前滚动的哈希数将其设为hashes_per_tick - 1即可精确触发失败场景 1反向印证了校验逻辑Replay 会逐 Entry 统计哈希数并与hashes_per_tick严格比对。5.3 Tick 的生成端PohService为完整理解校验的对称性还需要知道 Tick 在出块端如何产生。create_ticksentry/src/poh.rs按hashes_per_tick在 PoH 哈希链上滚动并生成指定数量的 Tickpoh_service/poh_recorderpoh/src/poh_service.rs、poh/src/poh_recorder.rs则负责在 Leader 出块期间按ticks_per_slot控制 Slot 边界。校验端与生成端共用同一组GenesisConfig参数ticks_per_slot定义于 sdk/src/clock.rshashes_per_tick定义于PohConfig从而保证生成什么、验证什么严格一致。六、验证链路全景与故障排查指引将以上各环节串起来一条 shred 从网络到达节点的完整验证链路如下Shred 层ledger/src/shred.rs反序列化、签名验证、ShredFlags含LAST_SHRED_IN_SLOT解析Blockstore 层ledger/src/blockstore.rsshould_insert_data_shred做last_index/received冲突检测冲突 shred 生成PossibleDuplicateShred证据并拒绝插入相同索引的完全重复 shred 直接忽略Replay 层core/src/replay_stage.rs逐 Entry 回放校验哈希间隔hashes_per_tick、Tick 总数ticks_per_slot、收尾 shred 是否存在且必须是 Tick任一不满足即mark_dead_slot共识/罚没层带LAST_SHRED_IN_SLOT的标志冲突索引互相矛盾构成 slashing proof交由集群治理流程惩罚作恶 Leader。运维观测要点当节点日志出现blockstore_error数据点包含received index slot.last_index等描述或replay-stage-mark_dead_slot数据点、以及BlockError::{InvalidTickHashCount, TooManyTicks, TooFewTicks, InvalidEntryHash}等错误时说明节点检测到了违反 Tick 校验规则的传输。多数情况下这是恶意 Leader 或网络故障的信号Blockstore 会自行拒绝/判死对应 Slot若同一 Slot 反复出现LastIndexConflict则说明存在可提交的罚没证据应收集相关 shred 供治理流程使用。七、总结Tick 校验是 Solana 将时间转化为可验证事实的核心机制其设计可归纳为三条原则严格的结构契约ticks_per_slot个 Tick、Tick 间恰好hashes_per_tick个哈希、末 shred 只能是 Tick 且必须带LAST_SHRED_IN_SLOT标志收放有度的错误处理能构造合法替代传输的恶意行为收集 slashing proof其余错误直接判死 Slot兼顾共识安全与惩罚可执行性全链路双端对称生成端PohService与验证端Blockstore/ReplayStage共用同一套PohConfig与GenesisConfig参数配合 core/src/replay_stage.rs 中的系统性单元测试InvalidTickHashCount、TooManyTicks、TooFewTicks等确保规则在代码层面被精确执行。这一机制保证了任何偏离规范的时间链传输都无法被回放为合法区块从而维护了整个 Solana 网络时间一致性与账本完整性。【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solana创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表