ARTICLE DETAIL

资讯详情

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

Linera 协议链与证书存储抽象详解:从 `Storage` trait 到 `DbStorage` 的实现剖析

Linera 协议链与证书存储抽象详解:从 `Storage` trait 到 `DbStorage` 的实现剖析 Linera 协议链与证书存储抽象详解从Storagetrait 到DbStorage的实现剖析【免费下载链接】linera-protocolMain repository for the Linera protocol项目地址: https://gitcode.com/GitHub_Trending/li/linera-protocollinera-storage是 Linera 协议Main repository for the Linera protocol中负责单个链chain与证书certificate持久化存储抽象的核心模块。它把链状态视图、已确认区块证书、Blob、事件、网络描述等协议级数据与底层键值数据库解耦上层链工作器chain worker、区块导出器block exporter只面向统一的Storagetrait 编程底层则通过DbStorage桥接 Memory / RocksDB / ScyllaDB 等任意KeyValueDatabase实现。读完本文你将掌握 Linera 存储层的整体架构、Storagetrait 的完整能力清单、RootKey分区键设计、多级缓存与时钟抽象的实现细节以及如何在 CLI 工具链中使用这套存储。一、模块定位为链与证书而生的存储抽象该模块的定位在 linera-storage/README.md 中只有一句话但含义极重This module defines the storage abstractions for individual chains and certificates.这句话点明了linera-storage与linera-views的分工linera-views见 linera-views/README.md提供通用、可组合的视图views与键值存储抽象如Context、RootView、KeyValueStore、KeyValueDatabase解决如何把任意结构化状态映射到键值空间linera-storage则在这一通用抽象之上定义协议语义层链的状态ChainStateView、区块证书ConfirmedBlockCertificate、Blob 及其状态、事件流、网络描述等都是 Linera 协议自己的数据模型。整个 crate 由三个源文件构成linera-storage 目录文件职责src/lib.rs定义Storagetrait、Clocktrait、ChainRuntimeContext、ResultReadCertificates以及覆盖全部能力的泛型测试套件src/db_storage.rsDbStorage默认实现RootKey分区、多级缓存、批量写入、双存储分配、WallClock/TestClocksrc/decode_key.rs内置二进制linera-storage-decode-key用于解码各后端存储中的分区键该 crate 遵循 Apache 2.0 协议见 LICENSE参与贡献请参照 CONTRIBUTING.md。二、Storagetrait协议级存储的统一契约Storagetraitsrc/lib.rs是整个模块的门面。它要求实现方提供三类关联类型pub trait Storage: linera_base::util::traits::AutoTraits Sized { /// 核心协议链工作器等使用的底层存储上下文 type Context: ContextExtra ChainRuntimeContextSelf Clone static; /// 时钟类型 type Clock: Clock Clone Send Sync; /// 区块导出器使用的存储上下文 type BlockExporterContext: ContextExtra u32 Clone; ... }2.1 方法全景五类操作把 trait 中的方法按语义分组可以清晰看到它覆盖了链协议所需的全部持久化诉求① 链状态load_chain(chain_id)加载一条链的ChainStateView。文档与代码注释明确指出src/lib.rs每次调用都会新建一个ChainStateView实例若同一时刻存在同一链的多个活跃实例它们会竞争访问持久化存储可能造成状态损坏——因此上层必须保证同一链同一时刻只有一个实例create_chain(description)以简单方式初始化一条链用于测试与创建创世状态实现中会写入一条链描述 Blob、一个BlobState::GENESIS状态再load_chain并initialize_if_needed后savesrc/lib.rs。② Blob 及其状态存在性contains_blob、contains_blob_state、missing_blobs批量返回缺失的 Blob ID读取read_blob、read_blobs、read_blob_state、read_blob_states写入write_blob、write_blobs、maybe_write_blobs仅当 Blob 已有 blob state 时才写入返回值标识每个 Blob 是否真的被写入、maybe_write_blob_states仅在不存在或新区块纪元更新时写入、write_blobs_and_certificateBlob 与证书原子批量写入。③ 证书与已确认区块contains_certificate、read_certificate、read_certificates、read_certificates_raw返回原始字节对(lite_certificate_bytes, confirmed_block_bytes)供 RPC 层零反序列化转发、read_confirmed_block、read_confirmed_blocks按高度索引查询read_certificate_hashes_by_heights、read_certificates_by_heights、read_certificates_by_heights_raw内存去重缓存cache_certificate、cache_blob、cache_confirmed_block——注释强调必须使用这些方法而非直接Arc::new来维护每种内容只分配一次one allocation per content的不变量src/lib.rs。④ 事件流read_event、contains_event、read_events_from_index从某个StreamId的指定索引开始遍历、write_events事件与区块高度的反查索引read_event_block_heights。⑤ 网络级元数据与委员会read_network_description/write_network_description网络名称、创世配置哈希、创世时间戳、创世委员会 Blob 哈希、管理链 IDget_or_load_committee_by_hash先查进程级共享委员会缓存miss 时从 Blob 存储按BlobType::Committee读取并反序列化src/lib.rscommittee_for_epoch通过管理链上的EPOCH_STREAM_NAME事件流定位每个纪元epoch的委员会 Blob 哈希纪元 0 则直接用创世委员会src/lib.rsis_epoch_revoked通过管理链的REMOVED_EPOCH_STREAM_NAME事件判断某纪元委员会是否已被撤销。⑥ 枚举能力list_blob_ids、list_chain_ids、list_event_ids供运维与数据库工具如 linera-service/src/cli/main.rs 中的ListBlobIds/ListChainIds/ListEventIds命令枚举存储中的全部数据。2.2 默认实现应用字节码的懒加载trait 还通过默认方法提供了load_contract/load_service的完整实现src/lib.rs先尝试从TransactionTracker取 Blob 内容交易内联否则回落到read_blob随后把压缩字节码交给thread_pool在阻塞线程中解压最后按VmRuntime分派VmRuntime::Wasm需要启用with_wasm_runtime对应wasmer或wasmtimefeature否则 panic 并提示在编译linera-storage时开启相应 featureVmRuntime::Evm需要启用with_revm对应revmfeature。这正是 Cargo.toml 中wasmer、wasmtime、revm三个 feature 存在的意义存储层本身不绑定任何虚拟机由编译期 feature 决定可加载的应用运行时。2.3ChainRuntimeContext把存储注入执行引擎ChainRuntimeContextSsrc/lib.rs是Storage::Context的Extra类型实现了linera_execution的ExecutionRuntimeContext把存储、链 ID、线程池、执行运行时配置、用户合约/服务缓存统一打包给执行引擎使用。它内部维护papaya::HashMapApplicationId, UserContractCode与papaya::HashMapApplicationId, UserServiceCode两级内存缓存get_user_contract/get_user_service命中即返回未命中才回落storage.load_contract/storage.load_servicesrc/lib.rs。三、DbStorage默认实现的四个核心设计DbStorageDatabase, Clock WallClocksrc/db_storage.rs是Storage的参考实现字段包括ArcDatabase、Clock、ThreadPool、可选WasmRuntime、用户合约/服务缓存、共享委员会缓存、StorageCaches与ExecutionRuntimeConfig。3.1RootKey分区键驱动的数据布局所有数据按RootKey枚举src/db_storage.rs进行分区partition每个变体对应一个独立的分区根键pub enum RootKey { NetworkDescription, // 网络描述全局唯一 BlockExporterState(u32), // 区块导出器状态按 ID ChainState(ChainId), // 链状态按链 ID BlockHash(CryptoHash), // 证书 已确认区块按区块哈希 BlobId(BlobId), // Blob 及其状态按 Blob ID Event(ChainId), // 链的事件按链 ID BlockByHeight(ChainId), // 高度 → 哈希索引 EventBlockHeight(ChainId), // 事件 → 高度索引 }在每个分区内部具体键使用单字节常量区分条目类型src/db_storage.rs常量值用途BLOB_KEY[0]Blob 内容root key 含 Blob IDBLOB_STATE_KEY[1]Blob 状态LITE_CERTIFICATE_KEY[2]lite 证书哈希 轮次 验证者签名BLOCK_KEY[3]已确认区块证书值NETWORK_DESCRIPTION_KEY[4]网络描述例如证书的写入MultiPartitionBatch::add_certificatesrc/db_storage.rs会在BlockHash(hash)分区下同时写入 lite 证书与区块值两份 BCS 序列化数据并顺带维护两个索引BlockByHeight(chain_id)分区下写入chain_id height → hash支持按高度反查证书EventBlockHeight(chain_id)分区下把区块体中每个事件映射到其所在高度支持事件 → 区块高度反查。分区设计也直接决定了后端适配同一分区的键可以合并成一次范围查询而跨分区的批量读取如read_blobs则通过futures::try_join_all并发点查——注释src/db_storage.rs指出这分别贴合 RocksDB 的并发点查调度与 ScyllaDB 的 shard-aware 驱动路由。3.2 三级读取路径缓存 → 原始字节 → 反序列化以证书读取为例src/db_storage.rs读取路径设计了三层组装缓存caches.certificate命中则直接返回共享Arc零反序列化原始字节缓存caches.certificate_raw命中则调用deserialize_and_cache_certificate反序列化一次并回填组装缓存数据库read_multi_values_bytes一次取回 lite 证书与区块值先插入原始字节缓存再反序列化。deserialize_and_cache_certificatesrc/db_storage.rs把LiteCertificateConfirmedBlock重新组装成完整证书同时回填confirmed_block缓存若哈希对不上则返回ViewError::InconsistentEntries。这种单次分配路径与 2.1 节提到的cache_*方法共同保证了协议中同内容只存在一份Arc的内存效率。3.3 多级缓存StorageCaches与StorageCacheConfigStorageCachessrc/db_storage.rs聚合了 8 个缓存全部基于linera_cache::ValueCache内部以ArcV存储天然支持跨消费者内存共享缓存键 → 值说明blobBlobId → BlobBlob 内容confirmed_blockCryptoHash → ConfirmedBlock已确认区块certificateCryptoHash → ConfirmedBlockCertificate组装好的证书certificate_rawCryptoHash → (Vecu8, Vecu8)原始序列化字节对eventEventId → Vecu8事件字节block_hash_by_height(ChainId, BlockHeight) → CryptoHash高度索引event_block_heightEventId → BlockHeight事件反查索引network_descriptionOnceLockNetworkDescription网络描述只写一次缓存大小由StorageCacheConfigsrc/db_storage.rs统一配置测试默认值DEFAULT_STORAGE_CACHE_CONFIGsrc/db_storage.rs为各缓存 1000 项、清理间隔沿用linera_cache::DEFAULT_CLEANUP_INTERVAL_SECS。生产环境可在 CLI 层覆盖该配置见第五节。所有读写操作都带有 Prometheus 埋点with_metricsfeature 开启见 src/db_storage.rs并以source标签区分cache与db两种数据来源方便观测缓存命中率。模块还提供init_metrics()src/lib.rs一次性注册linera-base、linera-cache、linera-chain、linera-execution、linera-views与本模块的全部指标避免冷启动时面板空白。3.4 时钟抽象WallClock与TestClockStorage::Clock提供current_time()、sleep_until()、sleep_for()src/lib.rs。与linera_base::time::timer::sleep不同这里要求睡眠也遵循时钟本身以便测试能在虚拟时间中确定性推进。WallClocksrc/db_storage.rs生产实现直接对接系统时间与linera_base::time::timer::sleepTestClocksrc/db_storage.rswith_testing下可用所有克隆共享同一时间支持set设置时间并唤醒所有到点睡眠者、add推进时间差甚至set_sleep_callback决定每次睡眠是否自动推进时间——这让链上定时器逻辑可以在测试中快进。四、批处理写入与双存储分配4.1MultiPartitionBatch跨分区并发写入MultiPartitionBatchsrc/db_storage.rs按 root key 聚合待写入的键值对。write_batchsrc/db_storage.rs对每个分区独立open_shared并构造Batch然后用try_join_all并发提交——不同分区的写入互不阻塞。write_blobs_and_certificatesrc/db_storage.rs即通过一个 batch 同时写入 Blob、证书、高度索引与事件高度索引并在成功后主动填充block_hash_by_height与event_block_height缓存使后续读取直接命中内存。4.2ChainStatesFirstAssignment双存储后端分配策略DualStoreRootKeyAssignment是linera-views双存储dual store机制的分配策略接口。ChainStatesFirstAssignmentsrc/db_storage.rs的实现约定root key 以CHAIN_ID_TAG 2开头即ChainState分区判定见is_chain_statesrc/db_storage.rs→ 分配到第一个存储StoreInUse::First其余所有分区 → 分配到第二个存储。这允许把高频更新的链状态放在更快的存储如 RocksDB把证书/Blob 等大对象放在另一个后端实现异构存储组合。五、如何使用连接、feature 与运维工具5.1 连接与创建DbStorageDatabase, WallClock提供两个核心构造方法src/db_storage.rs// 连接指定命名空间不存在则创建 pub async fn maybe_create_and_connect( config: Database::Config, namespace: str, wasm_runtime: OptionWasmRuntime, cache_sizes: StorageCacheConfig, ) - ResultSelf, Database::Error; // 仅连接已存在的存储 pub async fn connect( config: Database::Config, namespace: str, wasm_runtime: OptionWasmRuntime, cache_sizes: StorageCacheConfig, ) - ResultSelf, Database::Error;注意DEFAULT_NAMESPACE常量src/lib.rs为default未指定命名空间时使用。测试场景下还提供make_test_storage/new_for_testing/connect_for_testingsrc/db_storage.rs它们使用随机命名空间与TestClock确保测试互不干扰。5.2 在 CLI 中的接入linera-service/src/cli/main.rs 中数据库工具命令DeleteAll、DeleteNamespace、CheckExistence、Initialize、ListNamespaces、ListBlobIds、ListChainIds、ListEventIds通过RunnableWithStore泛型拿到StorageCacheConfig再调用DbStorage::D, _::maybe_create_and_connect(...)实例化存储。linera-service的 storage.rs 直接pub use linera_storage::StorageCacheConfig即 CLI 的缓存配置参数直接透传自本 crate。5.3 feature 开关Cargo.toml 定义了以下 feature按需组合feature作用revm启用 EVM 运行时linera-execution/revmtest测试支持linera-execution/test、linera-views/testwasmer/wasmtime选择 Wasm 运行时后端二选一或都启用scylladb/rocksdb接入对应的键值后端metrics启用 Prometheus 指标web编译到 Web?Sendtrait 变体5.4 运维工具linera-storage-decode-keycrate 内置一个可执行文件linera-storage-decode-keyCargo.toml、src/decode_key.rs用于把后端存储中的分区键 blob 解码为RootKey变体方便直接排查数据库内容从位置参数或 stdin每行一个读取十六进制键容忍0x前缀并跳过非 hex 行——因此可以直接把cqlsh的表导出结果管道进来--strip-bytes N剥掉前 N 个字节再解码后端可能给分区键加包装前缀--scylla等价于--strip-bytes 1因为 ScyllaDB 后端会给每个 root key 前置一个0x00字节见linera-views::backends::scylla_db的get_big_root_key。其测试src/decode_key.rs验证了NetworkDescription、BlockExporterState(7)、ChainState、BlobId的往返解码以及 Scylla 前缀剥离逻辑。六、测试与质量保证linera-storage的测试分两层共同构成任何Storage实现都必须通过的契约通用泛型测试src/lib.rstest_storage_features用#[test_case]同时挂载MemoryDatabase与启用scylladbfeature 时ScyllaDbDatabase依次执行链/区块导出器、Blob 与 Blob 状态、证书与高度索引、事件流、网络描述五组子测试验证读写、缺失检测、maybe_write_*语义等行为实现细节测试src/db_storage.rs验证RootKey的 BCS 序列化约定list_blob_ids/list_chain_ids/事件前缀查询都依赖这些字节布局、add_certificate是否写入高度索引、按高度读证书的顺序/乱序/缺失/重复高度语义、多链同高度隔离、以及按哈希读 按高度读的一致性。这些测试还承担了跨实现的可移植性保证任何第三方接入linera-views后端的人只需让自己的存储实现通过test_storage_features即可确信它满足 Linera 协议的存储契约。七、小结linera-storage以一句话的 README 定义了 Linera 协议存储层的全部语义而真正的深度藏在源码中Storagetrait是链、证书、Blob、事件、网络描述的协议级读写契约附带Clock与执行上下文注入DbStorage通过RootKey分区键组织数据布局用组装缓存 → 原始字节缓存 → 数据库三级路径平衡内存效率与延迟用MultiPartitionBatch实现跨分区并发写入可插拔后端借助linera-views的KeyValueDatabase抽象与ChainStatesFirstAssignment双存储分配让 Memory、RocksDB、ScyllaDB 乃至异构组合都能以同一套接口运行可观测与可运维通过metricsfeature 的source标签区分缓存/数据库来源通过linera-storage-decode-key直接解码底层分区键。无论你是想为 Linera 接入新的存储后端、排查链状态不一致问题还是理解链上数据的落盘布局linera-storage都是值得从 README 出发、向src/lib.rs与src/db_storage.rs深入的首选入口。【免费下载链接】linera-protocolMain repository for the Linera protocol项目地址: https://gitcode.com/GitHub_Trending/li/linera-protocol创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表