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依赖四个核心库,它们决定了生成代码的形态与能力:
| 依赖 | 作用 |
|---|---|
prost | Google 官方认可的纯 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_metadata、genesis、user等),用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(区块内所有交易共享该时间戳)、height、transactions(交易列表)与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_metadata、genesis、state_checkpoint、user、validator、block_epilogue。此外还有info(TransactionInfo,见下)、epoch、block_height、size_info(TransactionSizeInfo,含交易字节数、事件/写操作的字节明细,供容量规划使用)。
注意 proto 中保留了若干字段号空洞(5-19、11-19、22 等),注释明确写了 "values 5-19 skipped for no reason",这是为未来扩展预留的兼容性空间。
3.3 TransactionInfo:交易执行结果
TransactionInfo(transaction.proto)是交易执行后的"回执":包含hash、state_change_hash、event_root_hash、state_checkpoint_hash(可选)、gas_used、success、vm_status、accumulator_root_hash以及changes(WriteSetChange列表,即本次交易对账本状态的全部增删改)。
3.4 UserTransactionRequest:用户交易请求体
UserTransactionRequest(transaction.proto)刻画一笔用户交易的输入面:sender、sequence_number、max_gas_amount、gas_unit_price、expiration_timestamp_secs、payload(TransactionPayload)与signature(Signature)。
TransactionPayload通过Type枚举 +oneof payload支持五类负载(见 transaction.proto):
EntryFunctionPayload:最常用的"入口函数调用",含function(EntryFunctionId:模块地址+模块名+函数名)、type_arguments、arguments(字符串化参数)与便于展示的entry_function_id_str;ScriptPayload:原始 Move 脚本,含bytecode与 ABI;WriteSetPayload:治理/系统级直接写集;MultisigPayload:多签账户委托执行;EncryptedTransactionPayload:加密交易负载(区分failed_decryption与decrypted两种状态)。
ExtraConfigV1还提供了可选的多签地址与重放保护随机数(replay_protection_nonce)扩展位。
3.5 Signature 家族:签名方案的演进
Signature枚举覆盖了从经典到新型的签名体系(transaction.proto):ed25519、multi_ed25519、multi_agent、fee_payer与single_sender。其中SingleSender内部是AccountSignature,而AccountSignature进一步支持single_key_signature、multi_key_signature与abstraction(账户抽象签名)。
AnyPublicKey与AnySignature则是"任意密钥/签名"的统一抽象: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_MODULE | DeleteModule | 删除模块 |
TYPE_DELETE_RESOURCE | DeleteResource | 删除账户资源 |
TYPE_DELETE_TABLE_ITEM | DeleteTableItem | 删除表项 |
TYPE_WRITE_MODULE | WriteModule | 写入模块字节码 |
TYPE_WRITE_RESOURCE | WriteResource | 写入账户资源 |
TYPE_WRITE_TABLE_ITEM | WriteTableItem | 写入表项 |
每条变更都携带state_key_hash,用于与默克尔状态树对齐;资源/表项类变更还附带了type_str、data等便于展示的字符串字段。
3.7 Move 类型系统
为了让链上类型信息可序列化,proto 完整复刻了 Move 的类型系统:
MoveTypes枚举(transaction.proto)覆盖 bool、u8~u256、i8~i256、address、signer、vector、struct、泛型参数、引用与不可解析类型;MoveType通过oneof content递归表达vector、struct(MoveStructTag)、generic_type_param_index、reference(mutable+被引用类型)与unparsable;MoveModule/MoveFunction/MoveStruct等描述模块 ABI,MoveAbility枚举对应 Move 的 copy/drop/store/key 四种能力。
这套设计使得下游(如 BigQuery 导出、区块浏览器)无需解析 BCS 即可直接理解交易的语义。
四、围绕同一数据模型的 gRPC 服务
除纯数据类型外,仓库还定义了三个服务,生成的 Rust 代码同样位于aptos_protos中(对应 pb 下的aptos.indexer.v1.rs、aptos.internal.fullnode.v1.rs、aptos.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_id与processed_range(first_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.proto(PACKAGE_VERSION_SUFFIX,因该时间戳包已被广泛采用不宜改动)、raw_data.proto与fullnode_data.proto(SERVICE_SUFFIX与RPC_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-commit、buf、poetry:
- 遍历
*.gen.yaml模板,跳过 Python 模板,执行buf generate --template <file>生成 Rust 与 TS 代码; - 进入
python/目录,poetry install后执行poetry run poe generate(Python 侧没有用 buf,而是采用grpc_tools.protoc工具链,脚本注释解释了原因:不借助远程 registry 或源码编译 grpc 时,用 buf 生成 Python 代码并不方便); - 最后
pre-commit run --all-files跑一遍格式与 lint。
仓库中 python/generate.sh 与 TS 侧package.json的build脚本是这条流水线在各自语言侧的落点。
六、常见使用场景与注意事项
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 落地:
- 类型层面:
Transaction、Block、Signature、WriteSetChange、MoveType等消息完整覆盖了交易生命周期与 Move 类型系统; - 服务层面:
RawData、FullnodeData、RemoteExecutor三个 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),仅供参考