Diem 状态同步器(State Synchronizer)技术规范详解:三种同步协议、网络消息格式与核心模块协作
2026/9/23 1:44:25 网站建设 项目流程

Diem 状态同步器(State Synchronizer)技术规范详解:三种同步协议、网络消息格式与核心模块协作

【免费下载链接】diemDiem’s mission is to build a trusted and innovative financial network that empowers people and businesses around the world.项目地址: https://gitcode.com/gh_mirrors/di/diem

导读:本文以 Diem 开源仓库中的 State Synchronizer 规范文档 为主体,系统讲解 Diem 节点如何通过状态同步组件将本地账本(ledger)同步到 Diem 支付网络的最新状态。文章将覆盖状态同步的整体流程、验证器同步(Validator sync)、全节点同步(Full Node sync)与 Waypoint 同步三种协议的技术要求、GetChunkRequest/GetChunkResponse等网络消息格式,并结合 state-sync-v1 的实际 Rust 源码,说明这些协议在实现层面的落地方式与关键配置参数。读完本文,你将掌握 Diem 状态同步的协议设计、消息结构、验证规则与模块边界,能够读懂并定位相关源码。

一、组件定位与核心职责

State Synchronizer(状态同步器)是 Diem 节点中的一个系统组件,其职责是在 Diem 支付网络中的各个节点之间同步账本状态(ledger state)。借助状态同步,节点可以发现并更新到更新的账本状态,其一般流程如下:

  1. 节点向远端对等节点(peer)发送GetChunkRequest
  2. 如果远端对等节点存在更新的账本状态,它会返回对应的GetChunkResponse,其中携带一批交易以及"这些交易已由共识处理"的对应证明(proof);
  3. 请求方利用响应数据,通过重新执行(re-executing)交易的方式推进本地账本状态。

该组件解决的问题与常见分布式系统中的数据追赶场景一致:节点掉线一段时间、网络分区恢复、新节点启动等情形都会导致节点账本落后于网络最新状态,此时必须依靠状态同步完成追赶。

从源码结构看,该组件在当前仓库中的实现位于 state-sync/state-sync-v1(含coordinator.rschunk_request.rschunk_response.rsrequest_manager.rsbootstrapper.rs等模块),其库入口 lib.rs 中的注释点明了组件用途:"Used to perform catching up between nodes for committed states. Used for node restarts, network partitions, full node syncs"(用于在节点间对已提交状态进行追赶,适用于节点重启、网络分区、全节点同步等场景)。

二、三类受支持的同步协议

Diem 状态同步支持三种同步协议,每种协议都要求组件同时具备**请求方(requester)响应方(responder)**两种角色能力:

协议请求方(Requester)响应方(Responder)触发场景
Validator sync验证器(validator)验证器验证器落后于共识状态时追赶
Full Node sync全节点(full node)验证器或全节点全节点发现并同步到最新账本状态
Waypoint sync验证器或全节点验证器或全节点节点初始化时按 waypoint 同步到指定状态

验证器同步(Validator sync)

参与者:请求方为验证器,响应方为验证器。

一般情况下,验证器通过参与共识协议获知最新账本状态,但仍有可能落后——例如节点离线一段时间,或发生网络分区。此时落后的验证器可以依赖状态同步追赶至最新状态。

发送请求:请求方发送GetChunkRequest,其中target设置为TargetType::TargetLedgerInfo;目标LedgerInfoWithSignatures由共识模块的sync_to请求提供(即共识主动要求状态同步到某一目标账本信息)。

处理请求 / 发送响应:响应方依据请求参数,从存储模块获取一段TransactionListWithProof(带证明的交易列表),满足以下约束:

  • 首笔交易的版本为known_version + 1
  • 交易数量上限为limit
  • 这些交易属于同一个 epoch。

随后响应方发送GetChunkResponse,其中txn_list_with_proof设为上述交易列表,response_li设为ResponseLedgerInfo::VerifiableLedgerInfo,包含:

  • 对应请求target中的账本信息;或
  • 若这些交易属于更早的 epoch,则设为该 epoch 结束时对应的带签名账本信息。

接收 / 验证响应:请求方执行以下验证:

  1. txn_list_with_proof的起始版本 == 本地状态版本 + 1;
  2. 依据本地状态的可信 epoch,验证ResponseLedgerInfo::VerifiableLedgerInfo中的账本信息;
  3. 通过执行模块的ChunkExecutor::execute_and_commit_chunk调用执行并存储这些交易。

若已同步到目标状态,则通知共识模块其sync_to请求已完成;若尚未完成,则基于新的本地状态再次按本协议发送下一个 chunk 请求,如此循环直到达标。

全节点同步(Full Node sync)

参与者:请求方为全节点,响应方为验证器或全节点。

全节点不参与共识,因此只能依赖状态同步器来发现并同步到更新的账本状态。

发送请求:请求方发送GetChunkRequesttarget设置为TargetType::HighestAvailable

处理请求 / 发送响应:响应方同样从存储模块获取满足三条约束(起始版本known_version + 1、上限limit笔、同属一个 epoch)的TransactionListWithProof,并发送GetChunkResponse,其中response_li设置为ResponseLedgerInfo::ProgressiveLedgerInfo

全节点长轮询协议(long-poll):如果请求的目标状态在响应方处尚不可用,响应方可将该请求缓存timeout_ms毫秒。若在此时间窗口内目标账本状态变为可用(即响应方自身推进了账本状态),则立即为该请求发送对应的 chunk 响应。

接收响应:请求方执行以下验证:

  1. txn_list_with_proof的起始版本 == 本地状态版本 + 1;
  2. 依据本地状态的可信 epoch,验证ResponseLedgerInfo::ProgressiveLedgerInfo中的所有账本信息;
  3. 通过ChunkExecutor::execute_and_commit_chunk执行并存储交易。

随后基于新的本地状态继续发送下一个 chunk 请求,直至追上最新状态。

Waypoint 同步(Waypoint sync)

参与者:请求方与响应方均为验证器或全节点。

Waypoint 同步发生在节点初始化阶段,且必须在节点参与其他状态同步协议之前完成。当节点启动时,可以指定一个 waypoint,将本地状态同步到该 waypoint 对应的版本。

发送请求:请求方发送GetChunkRequesttarget设置为TargetType::Waypoint,其中Waypoint携带的版本与节点初始化时提供的 waypoint 版本一致。

处理请求 / 发送响应:响应方依据请求参数,从存储模块获取满足约束(起始版本known_version + 1、上限limit笔、同属一个 epoch)的TransactionListWithProof,并发送GetChunkResponse,其中response_li设置为ResponseLedgerInfo::LedgerInfoForWaypoint

接收响应:请求方执行以下验证:

  1. 依据节点初始化时提供的 waypoint,验证waypoint_li
  2. txn_list_with_proof的起始版本 == 本地状态版本 + 1;
  3. 通过ChunkExecutor::execute_and_commit_chunk执行并存储交易。

若处理完该响应后尚未同步到 waypoint 对应版本,则基于更新后的账本状态按本协议继续发送下一个 chunk 请求。

三、网络协议:线上消息格式

本节说明状态同步器在网络上传输的消息的技术细节。实际实现中,所有消息被封装为StateSyncMessage枚举(仅含GetChunkRequestGetChunkResponse两个变体),定义见 network.rs,并通过 DiemNet 网络栈传输(StateSyncEvents/StateSyncSender分别是网络到状态同步、状态同步到网络的封装接口)。

GetChunkRequest 请求结构

struct GetChunkRequest { known_version: u64, current_epoch: u64, limit: u64, target: TargetType }

字段说明:

  • known_version——请求方最新账本状态的版本号。
  • current_epoch——请求方最新账本状态所处的 epoch。若请求方的状态已推进到 epochx的末尾,则下一次请求的current_epoch应为x + 1
  • limit——请求的交易数量(chunk 大小)。
  • target——本次请求的同步目标,见下节TargetType

仓库中的实际实现与规范一致,见 chunk_request.rs,并额外提供了new构造方法与Display实现(便于日志输出请求的 known version、epoch、limit 与 target)。

TargetType 同步目标类型

TargetType表示一次GetChunkRequest的同步目标,其中携带的所有LedgerInfoWithSignatures都用于证明请求方想要同步到的账本状态:

enum TargetType { TargetLedgerInfo(LedgerInfoWithSignatures), HighestAvailable { target_li: Option<LedgerInfoWithSignatures>, timeout_ms: u64, }, Waypoint(Version), }

子类型说明:

  • TargetLedgerInfo——LedgerInfoWithSignatures表示请求方请求同步到的目标账本状态(用于验证器同步场景)。
  • HighestAvailable
    • target_li——若无已知的带签名账本信息可作为目标,则设为None(通常在全节点首次发送 chunk 请求时设为None);否则携带一个目标账本信息。
    • timeout_ms——请求方发送携带该目标类型的 chunk 请求后、在发送下一个 chunk 请求之前等待的时间间隔。
  • Waypoint(Version)——对指定版本的同步请求,该版本与节点初始化时提供的 waypoint 版本一致。

从源码注释(chunk_request.rs)可以补充更深入的设计动机:

  • TargetLedgerInfo已标记为 DEPRECATED:源码注释指出该类型仅为了向后兼容而保留,状态同步已避免发送此类请求而改用HighestAvailable,并计划在下一个破坏性版本中移除(对应 issue 编号为 8013)。因此读者阅读旧文档或旧版本代码时可能看到该类型,但新实现已迁移。
  • HighestAvailabletarget_li语义:它支持"同步请求方落后于响应方太多"的场景。如果响应方的最新账本信息版本持续前进,即便同步请求方持续收到并同步交易,这些交易也可能永远得不到账本信息(LedgerInfo)背书——因为账本信息只有在覆盖到其版本的全部交易收到之后才能提交,而交易必须得到账本信息背书才能在存储查询时显示为已提交。为避免同步追赶期间交易永远得不到账本信息背书(或同步请求方已同步版本与被提交账本信息版本的差距不断增大),该目标类型可以同时做到:(1) 询问最高可用账本信息;(2) 指定一个目标账本信息用于构建请求的交易证明。通过 (1),同步请求方可以在同步到较早的目标账本信息之后,存储该账本信息以便后续做目标同步。
  • target_li未指定,响应方将基于其最高账本信息构建响应。

TargetType还提供了epoch()version()辅助方法(chunk_request.rs):前者提取目标账本信息所属 epoch(Waypoint返回None),后者提取目标版本。

GetChunkResponse 响应结构

GetChunkResponse是对应GetChunkRequest的网络响应消息。响应中包含的交易 chunk 全部属于对应请求known_epoch(即请求中的current_epoch)指定的 epoch,永远不会跨越 epoch 边界

struct GetChunkResponse { response_li: ResponseLedgerInfo, txn_list_with_proof: TransactionListWithProof, }

字段说明:

  • response_li——响应中的所有证明都相对于该账本信息构建;账本信息验证的具体方式取决于其类型,见下节ResponseLedgerInfo
  • txn_list_with_proof——与响应携带的账本信息对应的交易 chunk 及其证明。

实际实现见 chunk_response.rs,源码注释同样强调:"The returned chunk is bounded by the end of the known_epoch of the requester (i.e., a chunk never crosses epoch boundaries)."(返回的 chunk 以请求方 known_epoch 的末尾为界,即 chunk 绝不跨越 epoch 边界)。Display实现会输出响应账本信息类型以及交易覆盖的版本区间(如versions [first - last])。

ResponseLedgerInfo 响应账本信息

enum ResponseLedgerInfo { VerifiableLedgerInfo(LedgerInfoWithSignatures), ProgressiveLedgerInfo { target_li: LedgerInfoWithSignatures, highest_li: Option<LedgerInfoWithSignatures>, }, LedgerInfoForWaypoint { waypoint_li: LedgerInfoWithSignatures, end_of_epoch_li: Option<LedgerInfoWithSignatures>, } }

子类型说明:

  • VerifiableLedgerInfo——包含可用于验证响应中txn_list_with_proof的带签名账本信息。与TargetLedgerInfo类似,源码注释也标记其为 DEPRECATED(仅为向后兼容保留,由ProgressiveLedgerInfo取代)。
  • ProgressiveLedgerInfo
    • target_li——用于验证响应中txn_list_with_proof的带签名账本信息;
    • highest_li——若响应方本地状态中存在不同于target_li的更高账本信息,则设为此最高账本信息,否则为None
  • LedgerInfoForWaypoint
    • waypoint_li——对应请求TargetType::Waypoint指定版本处账本状态的带签名账本信息;
    • end_of_epoch_li——可选:若txn_list_with_proof中的交易 chunk 终止于某个 epoch 边界,则为该 epoch 结束对应的带签名账本信息,否则为None

ResponseLedgerInfo提供version()方法(chunk_response.rs),返回证明所相对构建的账本信息版本(三种类型分别取li.ledger_info().version()target_li的版本、waypoint_li的版本)。

四、源码级实现:协调器与长轮询机制

规范描述的是"应当如何",而 state-sync-v1 给出了"实际如何"的实现。核心是StateSyncCoordinator(见 coordinator.rs),其start()函数运行一个无限事件循环,根据外部与内部(本地)请求触发动作。源码注释明确描述了两种工作模式:

  • 全节点模式(FullNode):向预定义的静态对等节点持续发送无限流式 chunk 请求(若父节点在其已提交版本于超时窗口内变高,则会返回 chunk 响应);
  • 验证器模式(Validator):按需为特定目标账本信息生成 chunk 请求,以同步到该目标。

事件循环(coordinator.rs)通过futures::select!同时监听四类事件源:

  1. 共识通知consensus_listener):处理ConsensusNotification::SyncToTarget(即共识发起的sync_to请求,对应验证器同步协议)与ConsensusNotification::NotifyCommit(共识提交通知,状态同步需要把提交结果转发给 mempool 并维护自身状态);
  2. 客户端消息client_events):处理GetSyncState(查询同步状态)与WaitForInitialization(等待初始化完成,即 waypoint 同步完成);
  3. 网络事件network_events):处理NewPeer/LostPeer(对等节点上线/下线,动态启用或禁用请求管理器中的对等节点)以及Event::Message(收到远端发来的GetChunkRequest/GetChunkResponse,交由process_chunk_message处理);
  4. 定时器interval):以tick_interval_ms为周期调用check_progress()检查同步进度,超时未进展则触发重试或广播。

长轮询(long-poll)在实现层面体现为协调器维护的一张订阅表subscriptions: HashMap<PeerNetworkId, PendingRequestInfo>(coordinator.rs),PendingRequestInfo记录了每个挂起请求的过期时间、已知版本、请求 epoch、目标账本信息与 chunk 上限。当响应方收到一个HighestAvailable请求而目标状态尚未可用时,会把该请求登记为订阅,并在到期前一旦有新信息可用就主动通知对方——这与规范中"缓存响应timeout_ms"的描述完全对应。

请求重试与多播策略由RequestManager(request_manager.rs)负责:协调器构造时依据节点角色计算重试超时(coordinator.rs)——全节点为tick_interval_ms + long_poll_timeout_ms,验证器为tick_interval_ms * 2——并传入多播超时multicast_timeout_ms;若在限定时间内向一定数量的网络发送 chunk 请求仍无进展,下一次同步请求将多播(发送到更多网络)。

五、配置参数:StateSyncConfig

状态同步行为可通过节点配置中的state_sync节调整,配置结构体StateSyncConfig定义于 state_sync_config.rs,各项默认值如下:

参数默认值含义
chunk_limit1000每次状态同步请求的 chunk 大小(请求的交易笔数上限)
client_commit_timeout_ms5_000状态同步客户端处理一次提交通知的超时(毫秒)
long_poll_timeout_ms10_000远端对等节点长轮询的默认超时(毫秒)
max_chunk_limit1000合法 chunk 上限(用于健全性检查,拒绝异常请求)
max_timeout_ms120_000合法超时上限(用于健全性检查)
mempool_commit_timeout_ms5_000协调器等待 mempool 提交确认(ack)的超时(毫秒)
multicast_timeout_ms30_000状态同步默认多播超时:若向一定数量网络发送请求仍无进展,下一次请求将多播到更多网络
sync_request_timeout_ms60_000处理同步请求时确保其有进展的超时(即两次提交之间的最大间隔)
tick_interval_ms100检查状态同步进度的周期(毫秒),即协调器主循环的 tick 间隔

这些参数直接驱动上一节的协调器行为:tick_interval_ms控制interval定时器、long_poll_timeout_ms控制长轮询等待窗口、chunk_limit对应GetChunkRequestlimit字段、multicast_timeout_ms控制请求的多播策略。理解这些默认值有助于在部署验证器或全节点时合理调优同步行为。

六、抽象模块接口:状态同步器的协作边界

状态同步器不是孤岛,它需要与多个外部模块交互。规范将交互关系抽象为如下模块,其中标注"示例接口(非严格必需)"的部分仅用于说明协作方式,实现细节由各实现方自定。

Network 网络

状态同步器与网络模块交互,向网络中的对等节点发送网络协议中定义的消息。规范给出的示例接口:

/// Sends `message` to `recipient` fn send_to(recipient: PeerId, message: StateSyncMessage)

实际实现中,网络侧通过StateSyncEvents(网络事件流)与StateSyncSender(发送封装)与状态同步层对接,二者均为NetworkEvents/NetworkSender的薄封装(见 network.rs),StateSyncSender可克隆、可发送到独立异步任务。

Consensus 共识

状态同步器处理共识的StateComputer::sync_to调用,通过执行验证器同步协议完成,并在账本状态达到共识请求中账本信息指定的版本后通知共识。该交互的协调与实现属于实现细节(仓库中的对应物是consensus_notificationscrate 提供的ConsensusSyncNotificationConsensusNotificationListener,见 coordinator.rs)。

Mempool 交易池

状态同步器在处理 chunk 响应时,会通知 mempool 哪些交易已成功执行并提交,使 mempool 能清除这些交易。仓库中对应mempool_notifications::MempoolNotificationSender接口(见 coordinator.rs),协调器结构体持有mempool_notifier字段,并配置了mempool_commit_timeout_ms超时。

Execution 执行

状态同步器使用执行模块的ChunkExecutor接口来执行并存储交易。该接口在 executor-types/src/lib.rs 中定义,实际实现在 executor/src/lib.rs 的Executor<V>::execute_and_commit_chunk:其流程为

  1. 重置执行器缓存以与最新已同步状态保持一致;
  2. 验证输入交易列表(verify_chunk,依据已独立验证的目标账本信息验证证明);
  3. 执行交易并提交。

源码签名(executor/src/lib.rs)为:

fn execute_and_commit_chunk( &self, txn_list_with_proof: TransactionListWithProof, // 已独立验证的目标 LI:证明相对于该版本构建 verified_target_li: LedgerInfoWithSignatures, // 可选的 epoch 结束账本信息;不允许携带 epoch 变更 LI 的 chunk 直接结束 epoch epoch_change_li: Option<LedgerInfoWithSignatures>, ) -> Result<Vec<ContractEvent>>

Storage 存储

状态同步器与存储模块交互,以TransactionListWithProof形式获取一段交易及其对应证明。规范给出的示例接口:

/// Returns a batch of transactions starting at `known_version` with /// max size `limit` with proofs built against `target_version` fn get_chunk( &self, known_version: u64, limit: u64, target_version: u64, ) -> Result<TransactionListWithProof>

七、总结

Diem 状态同步器是连接共识、网络、执行与存储的关键枢纽:它让落后的验证器能够追赶共识进度(Validator sync)、让全节点能够持续跟踪网络最新状态(Full Node sync)、让新启动的节点能够按 waypoint 完成初始化(Waypoint sync)。三套协议的骨架高度一致——请求方发送携带目标类型的GetChunkRequest,响应方返回不跨 epoch 边界的交易 chunk 与证明,请求方验证并重新执行交易——区别主要在于目标类型(TargetLedgerInfo/HighestAvailable/Waypoint)与响应账本信息类型(VerifiableLedgerInfo/ProgressiveLedgerInfo/LedgerInfoForWaypoint)的选择,以及长轮询等配套机制。

若需继续深入,建议按以下路径阅读仓库源码:

  • 协议消息定义:chunk_request.rs、chunk_response.rs;
  • 同步主流程:coordinator.rs、request_manager.rs;
  • 网络封装:network.rs;
  • 执行接口:executor-types/src/lib.rs、executor/src/lib.rs;
  • 配置项:state_sync_config.rs。

此外,仓库中state-sync/state-sync-v1/tests目录下包含状态同步的测试用例,可用于观察各协议在具体场景下的预期行为。

【免费下载链接】diemDiem’s mission is to build a trusted and innovative financial network that empowers people and businesses around the world.项目地址: https://gitcode.com/gh_mirrors/di/diem

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询