news 2026/9/20 11:32:54

Aptos Protos Rust 开发指南:aptos-protos 生成的 Protobuf 类型与使用详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Aptos Protos Rust 开发指南:aptos-protos 生成的 Protobuf 类型与使用详解

Aptos Protos Rust 开发指南:aptos-protos 生成的 Protobuf 类型与使用详解

【免费下载链接】aptos-coreAptos is a layer 1 blockchain built to support the widespread use of blockchain through better technology and user experience.项目地址: https://gitcode.com/GitHub_Trending/ap/aptos-core

导读

protos/rust是 Aptos 区块链(aptos-core)中aptos-protosRust crate 的所在地,它承载着由 Protobuf 定义自动生成的 Rust 类型与 gRPC 客户端代码,是 Aptos 索引器(Indexer)、数据流服务与全节点内部通信的底层数据契约。本文以 protos/rust/README.md 为骨架,结合 transaction.proto 等原始定义与生成脚本,系统讲解该 crate 的结构、使用方法、底层数据模型与代码生成流水线。读完本文,你将掌握如何在 Rust 项目中引入aptos-protos、直接使用Transaction等生成的强类型结构解析链上数据,并理解这些类型从.proto到 Rust 源码的完整诞生链路。


一、aptos-protos 是什么

Aptos 的全链数据(区块、交易、事件、状态写入等)需要通过统一、跨语言、向前兼容的数据格式进行传输与存储,这一需求由 Protobuf 承担。protos/rust目录是这套 Protobuf 定义的Rust 语言绑定

  • 原始定义集中在 protos/proto 下,按aptos/<service>/v1/*.proto的路径组织;
  • 通过代码生成工具产出 Rust 源码(protos/rust/src/pb),以 crate 形式发布;
  • crate 名为aptos-protos,描述为 "Code generated from Aptos protobuf definitions"(见 Cargo.toml),当前版本为1.3.1,License 为 Apache-2.0。

值得一提的是,同一份.proto定义还同步生成了 Python 绑定 与 TypeScript 绑定,保证了不同技术栈消费链上数据时使用同一套语义模型。

1.1 crate 的依赖构成

查看 Cargo.toml,aptos-protos依赖四个核心库,它们决定了生成代码的形态与能力:

依赖作用
prostGoogle 官方认可的纯 Rust Protobuf 实现,负责消息编解码(Message/Encode/Decode trait)
tonic基于prost+hyper的 gRPC 框架,生成的服务端/客户端代码依赖它
pbjson提供 Protobuf 与 JSON 之间的相互转换,方便 REST 场景与调试
serde配合prost-serde插件为生成结构实现Serialize/Deserialize

也就是说,你拿到的每一个生成类型都自带三种能力:二进制 BCS/Proto 编解码(prost)、JSON 序列化(pbjson+serde)、gRPC 通信(tonic),这为"存储用二进制、传输走 gRPC、调试看 JSON"的生产链路提供了完整支撑。


二、快速上手:导入与使用生成的类型

protos/rust/README.md给出的用法非常简洁,先看原始示例,再展开说明。

2.1 在 Cargo.toml 中引入

在项目依赖中加入:

[dependencies] aptos-protos = "1.3.1"

若需要消费本地仓库源码,也可以使用 path 依赖指向protos/rust目录;该 crate 已发布到 crates.io(见 CHANGELOG.md 中 "Initial release to crates.io" 的记录)。

2.2 导入生成的 struct

use aptos_protos::transaction::v1::Transaction;

导入路径与 lib.rs 的导出方式一一对应:lib.rs将生成的pb模块重新导出为pub use pb::aptos::*,而 pb/mod.rs 中按aptos -> transaction -> v1嵌套声明了子模块,因此aptos_protos::transaction::v1::Transaction精确指向生成的aptos.transaction.v1.Transaction消息。

2.3 使用生成类型

fn parse(transaction: Transaction) { // Parse the transaction. let version = transaction.version; // u64,交易全局版本号 let txn_type = transaction.r#type(); // TransactionType 枚举 if let Some(user) = transaction.user { // oneof 字段直接作为 Option println!("sender: {}", user.request.as_ref().map(|r| r.sender.as_str()).unwrap_or("")); } }

几点实践提示(均能从生成代码对应的 proto 定义中得到印证):

  • oneof字段映射为 Rust 的Option(如Transaction.txn_data中的block_metadatagenesisuser等),用if let Some(...)分支即可取出对应子类型;
  • repeated字段映射为Vec<T>(如Block.transactions);
  • enum字段通过prost生成为 Rust enum,字段名与枚举名冲突时(如字段type)Rust 侧会自动加r#原始标识符前缀;
  • uint64字段默认映射为u64,同时 proto 中的[jstype = JS_STRING]仅为 JS 侧优化,不影响 Rust 类型;
  • 时间戳统一使用aptos.util.timestamp.Timestamp(对应 timestamp.proto),这是一个包裹秒数与纳秒数的标准消息,而非prost内置类型。

2.4 一个完整的数据读取示例

结合索引器场景,一个典型的消费流程是:通过 gRPC 拉取TransactionsResponse(见下文第四节),对每个Transaction解析出版本、类型、发送方与事件:

use aptos_protos::transaction::v1::{Transaction, transaction::TransactionType}; fn process_batch(transactions: Vec<Transaction>) { for txn in transactions { if txn.r#type() == TransactionType::User { if let Some(user) = &txn.user { if let Some(req) = &user.request { println!( "version={} sender={} seq={}", txn.version, req.sender, req.sequence_number ); } } for event in &user.events { println!(" event type_str={} data={}", event.type_str, event.data); } } } }

三、底层数据模型:transaction.proto 全解析

所有 Rust 生成类型的语义源头都在 transaction.proto。理解这份定义,等于理解了生成 struct 的全部字段。下面按层次拆解。

3.1 Block:区块的容器

Block是索引器消费的最小"批次"单位,注释给出了三个关键约定(见 transaction.proto):

  • 区块内交易按version严格递增排序,每个区块以BlockMetadataTransaction开头,下一个BlockMetadataTransaction表示区块结束;
  • height严格单调递增、无空洞,且是唯一标识;
  • 创世交易(version 0)位于 height 为 0 的区块内。

字段包括timestamp(区块内所有交易共享该时间戳)、heighttransactions(交易列表)与chain_id(防止多链数据混入同一流水线)。

3.2 Transaction:四种/六种交易类型的统一视图

Transaction是全文核心。它先通过TransactionType枚举区分交易类别(见 transaction.proto):

枚举值含义
TRANSACTION_TYPE_GENESIS创世交易,承载所有核心合约与验证者信息
TRANSACTION_TYPE_BLOCK_METADATA区块元数据交易,由链自身生成用于"分组"交易
TRANSACTION_TYPE_STATE_CHECKPOINT状态检查点/区块结尾交易
TRANSACTION_TYPE_USER用户发起的普通交易
TRANSACTION_TYPE_VALIDATOR验证者交易(DKG、JWK 更新等)
TRANSACTION_TYPE_BLOCK_EPILOGUE区块收尾交易,携带 gas/输出上限是否触达等收尾信息

随后通过oneof txn_data承载对应的细分消息:block_metadatagenesisstate_checkpointuservalidatorblock_epilogue。此外还有infoTransactionInfo,见下)、epochblock_heightsize_infoTransactionSizeInfo,含交易字节数、事件/写操作的字节明细,供容量规划使用)。

注意 proto 中保留了若干字段号空洞(5-19、11-19、22 等),注释明确写了 "values 5-19 skipped for no reason",这是为未来扩展预留的兼容性空间。

3.3 TransactionInfo:交易执行结果

TransactionInfo(transaction.proto)是交易执行后的"回执":包含hashstate_change_hashevent_root_hashstate_checkpoint_hash(可选)、gas_usedsuccessvm_statusaccumulator_root_hash以及changesWriteSetChange列表,即本次交易对账本状态的全部增删改)。

3.4 UserTransactionRequest:用户交易请求体

UserTransactionRequest(transaction.proto)刻画一笔用户交易的输入面:sendersequence_numbermax_gas_amountgas_unit_priceexpiration_timestamp_secspayloadTransactionPayload)与signatureSignature)。

TransactionPayload通过Type枚举 +oneof payload支持五类负载(见 transaction.proto):

  • EntryFunctionPayload:最常用的"入口函数调用",含functionEntryFunctionId:模块地址+模块名+函数名)、type_argumentsarguments(字符串化参数)与便于展示的entry_function_id_str
  • ScriptPayload:原始 Move 脚本,含bytecode与 ABI;
  • WriteSetPayload:治理/系统级直接写集;
  • MultisigPayload:多签账户委托执行;
  • EncryptedTransactionPayload:加密交易负载(区分failed_decryptiondecrypted两种状态)。

ExtraConfigV1还提供了可选的多签地址与重放保护随机数(replay_protection_nonce)扩展位。

3.5 Signature 家族:签名方案的演进

Signature枚举覆盖了从经典到新型的签名体系(transaction.proto):ed25519multi_ed25519multi_agentfee_payersingle_sender。其中SingleSender内部是AccountSignature,而AccountSignature进一步支持single_key_signaturemulti_key_signatureabstraction(账户抽象签名)。

AnyPublicKeyAnySignature则是"任意密钥/签名"的统一抽象:AnyPublicKey.Type涵盖 ED25519、SECP256K1_ECDSA、SECP256R1_ECDSA、Keyless、Federated Keyless 与 SLH-DSA(SHA2-128s);AnySignature通过signature_variantoneof 提供对应实现,并标记了signature旧字段在 1.10 版本起废弃([deprecated = true])。MultiKeySignature则通过signatures_required描述多密钥签名的阈值要求。

3.6 WriteSetChange:状态变更的六种形态

每次交易对链上状态的改动最终落到WriteSetChange(transaction.proto),其Type枚举与oneof change一一对应:

Type消息语义
TYPE_DELETE_MODULEDeleteModule删除模块
TYPE_DELETE_RESOURCEDeleteResource删除账户资源
TYPE_DELETE_TABLE_ITEMDeleteTableItem删除表项
TYPE_WRITE_MODULEWriteModule写入模块字节码
TYPE_WRITE_RESOURCEWriteResource写入账户资源
TYPE_WRITE_TABLE_ITEMWriteTableItem写入表项

每条变更都携带state_key_hash,用于与默克尔状态树对齐;资源/表项类变更还附带了type_strdata等便于展示的字符串字段。

3.7 Move 类型系统

为了让链上类型信息可序列化,proto 完整复刻了 Move 的类型系统:

  • MoveTypes枚举(transaction.proto)覆盖 bool、u8~u256、i8~i256、address、signer、vector、struct、泛型参数、引用与不可解析类型;
  • MoveType通过oneof content递归表达vectorstructMoveStructTag)、generic_type_param_indexreferencemutable+被引用类型)与unparsable
  • MoveModule/MoveFunction/MoveStruct等描述模块 ABI,MoveAbility枚举对应 Move 的 copy/drop/store/key 四种能力。

这套设计使得下游(如 BigQuery 导出、区块浏览器)无需解析 BCS 即可直接理解交易的语义。


四、围绕同一数据模型的 gRPC 服务

除纯数据类型外,仓库还定义了三个服务,生成的 Rust 代码同样位于aptos_protos中(对应 pb 下的aptos.indexer.v1.rsaptos.internal.fullnode.v1.rsaptos.remote_executor.v1.rs)。

4.1 Indexer 原始数据服务(aptos.indexer.v1)

定义在 raw_data.proto:

service RawData { rpc GetTransactions(GetTransactionsRequest) returns (stream TransactionsResponse); }

GetTransactionsRequest支持四个参数:

  • starting_version:流的起始版本;
  • transactions_count:可选,不填则无限流式输出;
  • batch_size:每个TransactionsResponse的批大小,默认 1000,超过 1000 会被拒绝
  • transaction_filter:可选,仅返回满足过滤条件的交易(对应 filter.proto,支持按发送方/接收方等维度做布尔组合过滤)。

TransactionsResponse返回交易批次、chain_idprocessed_rangefirst_version/last_version,便于消费者跟踪进度与断点续传)。

4.2 全节点内部数据服务(aptos.internal.fullnode.v1)

定义在 fullnode_data.proto,供索引器处理器直连全节点拉取数据。其流协议有明确的状态机约定:StreamStatus: INIT(带起始版本)→ 循环TransactionsOutput数据批次 →StreamStatus: BATCH_END(带结束版本),直到流终止。服务暴露两个 RPC:Ping(健康检查,可返回FullnodeInfo)与GetTransactionsFromNode(流式取交易)。每个TransactionsFromNodeResponse都附带chain_id以确保多链隔离。

4.3 远程执行器服务(aptos.remote_executor.v1)

定义在 network_msg.proto,用于节点间远程执行任务的网络消息封装(NetworkMessage携带任务 ID、输入与目标),是执行层分布式调度(如远程状态同步执行)的基础。


五、从 .proto 到 Rust 源码:代码生成流水线

了解生成机制有助于你判断"某个字段为何长这样"以及如何复现生成过程。

5.1 buf 配置

仓库使用 buf。模块级 buf.yaml 设定了两条质量红线:

  • breaking.use: [FILE]:以文件为单位检测破坏性变更,防止字段号/类型被随意改动;
  • lint.use: [DEFAULT]:启用 buf 默认 lint 规则,并对三个例外做了ignore_only豁免:aptos/util/timestamp/timestamp.protoPACKAGE_VERSION_SUFFIX,因该时间戳包已被广泛采用不宜改动)、raw_data.protofullnode_data.protoSERVICE_SUFFIXRPC_RESPONSE_STANDARD_NAME,因服务命名与响应复用是刻意为之)。

5.2 Rust 生成模板

buf.rust.gen.yaml 指定了四个插件,全部输出到rust/src/pb/

插件产出
prost核心消息结构体与编解码实现,并开启file_descriptor_set(保留原始 FileDescriptorSet,便于反射与动态解析)
prost-serde为每个结构生成 serdeSerialize/Deserialize(对应*.serde.rs文件)
prost-crate(strategy: all, no_features)生成 crate 级聚合模块(对应 mod.rs)
tonic生成 gRPC 服务端/客户端 trait 与桩代码(对应*.tonic.rs文件)

这也解释了src/pb/下同名.rs文件三件套(如aptos.transaction.v1.rs+.serde.rs+.tonic.rs)的由来。

5.3 一键构建脚本

build_protos.sh 串联了完整流程,前置要求依次检查pre-commitbufpoetry

  1. 遍历*.gen.yaml模板,跳过 Python 模板,执行buf generate --template <file>生成 Rust 与 TS 代码;
  2. 进入python/目录,poetry install后执行poetry run poe generate(Python 侧没有用 buf,而是采用grpc_tools.protoc工具链,脚本注释解释了原因:不借助远程 registry 或源码编译 grpc 时,用 buf 生成 Python 代码并不方便);
  3. 最后pre-commit run --all-files跑一遍格式与 lint。

仓库中 python/generate.sh 与 TS 侧package.jsonbuild脚本是这条流水线在各自语言侧的落点。


六、常见使用场景与注意事项

6.1 索引器消费链上数据

最主流的用法:通过aptos.indexer.v1::raw_data_client::RawDataClient(tonic 生成的客户端)发起GetTransactions流式请求,将返回的Transaction反序列化后写入数据库或消息队列。注意batch_size上限为 1000,需要更高吞吐请自行聚合多个批次;用processed_range记录已消费的last_version可安全断点续传。

6.2 调试与测试

由于生成类型实现了 serde,你可以直接用serde_json打印交易结构:

let json = serde_json::to_string_pretty(&transaction)?; println!("{}", json);

这对于校验索引数据、编写对比测试(golden file)非常实用。

6.3 兼容性注意事项

  • 保持与aptos-protos版本对应的 proto 定义一致,升级 crate 时注意breaking检测与 CHANGELOG.md;
  • AnySignature.signature字段已标记废弃(>=1.10 起),新代码应改用signature_variant
  • 所有oneof消息在 Rust 侧为Option,解引用子结构时注意as_ref(),避免无谓的 clone。

七、参与贡献

protos/rust/README.md建议贡献者参阅仓库根目录的 CONTRIBUTING.md(若修改的是 Protobuf 定义本身,需同步关注 protos/proto/buf.yaml 的 lint/breaking 约束,并通过 build_protos.sh 重新生成各语言绑定)。由于生成代码是机器产出,通常不应手工编辑src/pb/下的文件,修改入口一律在.proto源文件。


总结

aptos-protos并非一个简单的"生成代码仓库",它是 Aptos 链上数据统一语义模型的 Rust 落地:

  • 类型层面TransactionBlockSignatureWriteSetChangeMoveType等消息完整覆盖了交易生命周期与 Move 类型系统;
  • 服务层面RawDataFullnodeDataRemoteExecutor三个 gRPC 服务打通了索引、节点直连与远程执行三条数据通路;
  • 工程层面:buf 的 lint/breaking 检测与脚本化生成流程保证了跨语言绑定的长期一致性与兼容性。

掌握了 transaction.proto 这一份核心契约,你就能在 Rust 中熟练消费 Aptos 的任意链上数据——这正是 Aptos 索引器生态和各类数据服务的地基。

【免费下载链接】aptos-coreAptos is a layer 1 blockchain built to support the widespread use of blockchain through better technology and user experience.项目地址: https://gitcode.com/GitHub_Trending/ap/aptos-core

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 7:01:43

半导体物理基础与芯片设计关键技术解析

1. 芯片设计中的半导体物理基础半导体物理是芯片设计的根基&#xff0c;就像建筑需要坚实的地基一样。我从业十几年&#xff0c;见过太多工程师因为物理基础不扎实而在设计时走弯路。让我们从最基础的能带理论开始&#xff0c;聊聊这些看似抽象的概念如何直接影响芯片性能。1.1…

作者头像 李华
网站建设 2026/9/20 5:11:13

从零到贡献者:Folo社区协作全攻略

从零到贡献者&#xff1a;Folo社区协作全攻略 你是否曾想为开源项目贡献力量&#xff0c;却被复杂的协作流程吓退&#xff1f;本文将以Folo项目为例&#xff0c;带你一步步掌握从环境搭建到代码提交的完整流程&#xff0c;让你轻松成为社区贡献者。读完本文&#xff0c;你将能…

作者头像 李华
网站建设 2026/9/20 16:57:10

过拟合与欠拟合:模型训练中的两大拦路虎及解决套路

在模型训练这条路上&#xff0c;我不知道你们有没有过这种经历&#xff1a;训练集上loss低得漂亮&#xff0c;验证集上一测直接拉胯&#xff1b;或者反过来&#xff0c;训练了好几轮loss死活降不下去&#xff0c;模型像块木头一样怎么点都不开窍。这两种情况&#xff0c;就是机…

作者头像 李华
网站建设 2026/9/20 16:56:59

Spring生态下ChatModel接口的同步与流式调用设计

1. ChatModel接口体系概览在现代对话系统开发中&#xff0c;处理同步和异步通信模式是每个开发者都会遇到的挑战。Spring生态下的ChatModel接口体系通过精巧的设计&#xff0c;实现了这两种模式的统一处理。这个设计不仅优雅地解决了实际问题&#xff0c;还为我们展示了Java函数…

作者头像 李华