nautilus_trader Derive 适配器:EIP-712 自托管签名管线的第三方材料引用与字节级等价性验证
2026/9/11 10:37:40 网站建设 项目流程

nautilus_trader Derive 适配器:EIP-712 自托管签名管线的第三方材料引用与字节级等价性验证

【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader

本篇技术指南聚焦于 nautilus_trader 的 Derive 适配器如何以"第三方材料"的方式引用 Derive.xyz 官方 Python 签名 SDK(v2-action-signing-python),并以该 SDK 为行为基准构建 Rust 端 EIP-712 签名管线的字节级等价性测试(oracle 向量验证)。读完本文,你将掌握这份第三方许可声明的完整含义、oracle 向量夹具(fixture)的生成与重放机制、签名管线的底层实现细节,以及未来上游版本升级时的"再固定 + 再生成"流程。

一、文档定位:一份"有技术内涵"的第三方许可证声明

仓库中的 THIRD_PARTY_LICENSES.md 虽然以许可证声明命名,但它的技术分量远超普通合规文件。它回答了一个关键工程问题:nautilus_trader 的 Rust 版 Derive 自托管签名协议实现,其行为正确性以什么为基准来验证?

答案是该文档与源码共同揭示的:

  • Rust 端src/signing/目录下的 EIP-712 签名管线是原始实现(original implementation),并非对上游代码的移植或封装;
  • 上游 Python SDK 仅作为行为参考(behavioral reference),用于生成 oracle 向量并记录在 signing_trade_action_vectors.json 中;
  • 向量由 generate_oracle.py 生成,向量元数据中记录固定的上游修订版本。

这种"独立实现 + 官方参考实现做等价性对拍"的做法,让适配器在完全自研的同时,又能与 Derive 官方生态保持字节级一致。

二、第三方材料清单(原文档核心内容)

原文档声明的第三方材料如下,这是整个验证体系的依据:

项目
上游项目Derive.xyz -v2-action-signing-python
用途作为 Rust EIP-712 签名管线的行为参考,生成 oracle 向量
固定修订版本d1914d61985e33559244da242892c7255b6fd0ca
固定版本号0.0.13(2025-08-21 提交)
归属(作者)Derive.xyz<joshua@derive.xyz>、8baller<8baller@station.codes>(声明于上游pyproject.toml
许可证MIT(经 pyproject classifier 声明;固定修订版本的上游仓库未附带 LICENSE 文件)
向量生成脚本generate_oracle.py
向量输出文件signing_trade_action_vectors.json

两点需要特别留意:

  1. 许可证信息以 pyproject classifier 为准:上游仓库在固定修订版本上没有 LICENSE 文件,因此 MIT 判定来自pyproject.toml中的声明。这一细节同时被写进了 oracle 向量的元数据(见下文)。
  2. 上游来源:项目地址为github.com/derivexyz/v2-action-signing-python(仓库源码中 signing/mod.rs 的文档注释亦注明了该来源)。

三、为什么需要第三方参考:Derive 自托管签名协议

Derive 是构建在 Derive Chain 上的去中心化衍生品交易协议。其安全模型中,每一个改变状态的请求都必须携带一个 EIP-712 typed-data 签名,由会话密钥(session key)对 secp256k1 私钥签名,并由智能合约钱包验证。这意味着交易、改单、撤单等动作的签名格式是协议级约定,容不得半点字节偏差——任何编码顺序、填充方式或哈希组合的错误,都会导致合约端验签失败或签名被拒绝。

这正是引入官方 SDK 作为 oracle 的原因:Rust 实现必须与官方 Python 实现产生完全相同的字节序列,才能确信协议兼容。

协议常量

协议常量来自 Derive 官方 Protocol Constants 参考文档,并固化在 consts.rs 中:

  • Domain separator:mainnet 为0xd96e5f90...c60441b,testnet 为0x9bcf4dc0...e3dd1105
  • Action typehash0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17(跨网络一致);
  • Trade module 合约地址:mainnet0xB8D20c2B...5b5e7b,testnet0x87F28638...3f2be
  • 最小签名 TTLMIN_SIGNATURE_TTL = 5 分钟(v2 文档要求signature_expiry_sec至少在未来 5 分钟);
  • 触发单签名 TTLTRIGGER_ORDER_SIGNATURE_TTL = 31 天(mainnet 要求触发单的过期时间在 30~90 天区间,一天缓冲规避时钟漂移);
  • 小数定标:所有链上十进制字段以 1e18 定点整数表示(DECIMAL_SCALE)。

这些常量既用于生产配置的默认值,也作为 oracle 测试的比对基准——见下文第五节。

四、oracle 向量:字节级等价性的证据链

4.1 向量夹具的结构

signing_trade_action_vectors.json 是验证的核心夹具,结构为metadata + vectors

  • metadata记录了来源(github.com/derivexyz/v2-action-signing-python)、上游版本0.0.13、固定修订版本d1914d...、生成脚本路径、许可证声明(MIT,含"上游无 LICENSE 文件"的说明)以及一条重要注释:签名使用 RFC 6979 确定性随机数,因此每个值都是字节级相等目标(byte-equality target)
  • vectors是 5 组测试向量(对应 5 个行为分支,见下节)。

每组向量完整记录了从输入到签名的全链路中间值:

domain_separator / action_typehash / module_address / subaccount_id nonce / signature_expiry_sec / owner / session_key / signer trade(asset_address、sub_id、limit_price、amount、max_fee、recipient_id、is_bid) module_data(ABI 编码后的完整字节) module_data_hash / action_hash / typed_data_hash / signature

以第一组向量limit_buy_round_mainnet为例(节选):

{ "case": "limit_buy_round_mainnet", "environment": "mainnet", "domain_separator": "0xd96e5f90797da7ec8dc4e276260c7f3f87fedf68775fbe1ef116e996fc60441b", "action_typehash": "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17", "module_address": "0xB8D20c2B7a1Ad2EE33Bc50eF10876eD3035b5e7b", "subaccount_id": 30769, "nonce": 1695836058725001, "signature_expiry_sec": 2147483647, "trade": { "asset_address": "0x000000000000000000000000000000000000abcd", "sub_id": "42", "limit_price": "100", "amount": "1", "max_fee": "1000", "recipient_id": 30769, "is_bid": true }, "module_data_hash": "0xc9adef7e1b0648c010e846ee4a30ad72a3320279ab75b986e296dd9b9cb39c10", "signature": "0x9f28ed4e5b014e5214143d4060ea7758e4c8f837a3287841909b046caf43a6fd18a7707f30241f6fafff7ba53f839381390e13354264d984ea648de6c20369d31c" }

注意向量中的会话密钥0x2ae8be44...a4816bbd和所有者地址0x8772185a...5d698来自上游 SDK 自带的测试套件,不控制任何真实资金

4.2 五个行为分支的覆盖设计

generate_oracle.py 中的CASES数组精心选择了 5 组输入,覆盖交易编码器和 action-hash 组合的各个行为分支:

用例覆盖点
limit_buy_round_mainnetmainnet 环境、整数价格、买单(bid)
limit_sell_fractional_testnettestnet 环境、小数价格与数量(3500.01 / 1.25)、卖单
sell_negative_amount_testnet负数 amount(-0.75)的 ABI 编码(int256 补码)
option_buy_large_sub_id_mainnet超出 64 位范围的期权 sub_id(2^95 量级)的 uint256 编码
limit_buy_zero_max_fee_testnetmax_fee为零的边界情形

由于private/orderprivate/trigger_orderprivate/replace都通过同一条 EIP-712 管线对TradeModuleData签名,一组 trade 模块向量即可覆盖全部签名面(signing surface)。

4.3 为什么字节相等是有效的验证

oracle 成立的关键前提是RFC 6979 确定性随机数:相同输入在任何实现、任何时间运行都产生相同签名。这意味着 Rust 实现与 Python SDK 对相同输入产出的module_datamodule_data_hashaction_hashtyped_data_hash乃至最终 65 字节签名,都必须与夹具逐字节相等(byte equality),而不是"语义等价"。

五、Rust 签名管线的源码级实现

5.1 三步哈希管线

signing/mod.rs 的模块文档给出了完整管线:

action_hash = keccak256(abi.encode( [bytes32, uint, uint, address, bytes32, uint, address, address], [ACTION_TYPEHASH, subaccount_id, nonce, module_address, keccak256(module_data_abi_encoded), signature_expiry_sec, owner, signer], )) typed_data_hash = keccak256(0x1901 || DOMAIN_SEPARATOR || action_hash) signature = secp256k1_sign(typed_data_hash, signer_key)

对应实现分布在 eip712.rs 中:

  • compute_action_hash:将 8 字段 ABI 元组(typehash、subaccount、nonce、module、module_data_hash、expiry、owner、signer)编码后取 keccak256。元组字段的顺序是承载字节等价性的协议约定——测试test_compute_action_hash_pins_byte_layout用固定期望值0x509b526a...锁死布局,任何字段交换、删除或重排都会立刻被检测;
  • compute_typed_data_hash:拼接0x19 0x01前缀 + domain separator + action_hash 后取 keccak256,测试test_compute_typed_data_hash_includes_19_01_prefix专门验证前缀参与哈希;
  • SignedAction::sign:先校验过期时间(见 5.3),再走完"模块编码 → 模块哈希 → action 哈希 → typed-data 哈希 → secp256k1 签名"全流程,签名结果以 65 字节原始形式保存,并通过signature_hex()输出0x前缀的 130 字符十六进制串。

5.2 Trade 模块的 ABI 编码

modules/trade.rs 实现TradeModuleData,其 ABI 元组为(address, uint256, int256, int256, uint256, uint256, bool),对应(asset_address, sub_id, limit_price, amount, max_fee, recipient_id, is_bid)

  • 全部为静态类型,编码结果是7 个 32 字节字(测试test_encode_produces_seven_static_words断言长度恰为 224 字节);
  • limit_priceamount在 ABI 层是有符号 int256(即使价格通常非负),max_fee是无符号 uint256 且拒绝负数
  • 十进制字段乘以DECIMAL_SCALE(1e18)转为定点整数;负数amount编码为 int256 补码(测试test_encode_negative_amount_is_two_complement断言符号扩展字节为0xff);
  • 地址左填充 12 字节零、sub_id大端 uint256、is_bid布尔打包为0x01/0x00,均有对应单元测试逐一锁定;
  • 编码指纹测试test_keccak_of_encoded_payload_is_stable将编码结果的 keccak256 锁死为0xc9adef7e...9cb39c10——这恰好与 oracle 向量中的module_data_hash一致,形成测试与夹具的交叉印证。

5.3 过期时间与随机数管理

签名安全还依赖两个支撑模块:

  • 过期校验SignedAction::sign会先以MIN_SIGNATURE_TTL(5 分钟)校验signature_expiry_sec,不足则返回TypedDataError::ExpiryTooSoon(测试test_sign_rejects_expiry_that_is_too_soon以 1 分钟过期验证拒绝路径);系统时钟早于 UNIX 纪元则返回ClockBeforeEpoch
  • nonce 管理:nonce.rs 实现进程级(wallet, subaccount)随机数分配器。Derive 的 venue schema 定义唯一 nonce 为 UTC 毫秒 + 至多 3 位后缀,分配器采用utc_ms * 1000 + suffix格式,保证:同毫秒内单调递增、时钟回拨时在最后逻辑毫秒上继续推进(而非倒退)、按(wallet, subaccount)独立跟踪(钱包地址统一转小写,避免 checksum 形式产生重复 nonce)、DashMap分片 +compare_exchange循环保证并发安全。

5.4 签名上下文解析

signing/context.rs 的resolve_signing_context将凭据(钱包地址、会话密钥、子账户 ID)与 DeriveExecutionClientConfig 结合,解析出完整的SigningContext:domain separator、typehash、trade module 地址等协议常量优先取配置覆盖值,未配置时回落为consts.rs中的环境默认值max_fee_per_contract为必填项。

六、等价性测试如何在 Rust 中重放

oracle 测试直接内嵌在 eip712.rs 的#[cfg(test)]模块中,通过include_str!("../../test_data/common/signing_trade_action_vectors.json")将夹具编译进测试二进制。核心测试链包括:

  1. test_oracle_fixture_records_upstream_provenance:仅强制校验元数据四要素(source、version、revision、generator)非空——刻意不锁定具体值,这样未来换用新上游版本重新生成夹具时无需改动测试结构;
  2. test_oracle_vectors_use_production_protocol_constants:逐一断言夹具中每个向量的 domain separator、typehash、module 地址与common::consts中生产的常量一致,防止"夹具自洽但脱离线上配置"的陷阱;
  3. test_signing_matches_upstream_sdk_oracle_vectors:真正的对拍测试——对每个向量,用 Rust 实现重新编码module_data、计算module_data_hashaction_hashtyped_data_hash并签名,逐字节断言与上游夹具相等

此外还有签名可恢复性测试(test_sign_produces_recoverable_signature,从 65 字节签名恢复地址并与会话密钥比对,模拟合约端反向验签)和签名在 Debug 输出中脱敏(REDACTED)测试。

七、版本固定策略与未来升级路径

固定修订版本是 oracle 有效性的前提。当前钉在d1914d61985e33559244da242892c7255b6fd0ca(0.0.13)。文档明确给出了升级流程:

1. 更换上游版本(例如未来的 V3 signer):重新固定修订版本 2. 重新运行 generate_oracle.py 生成新夹具(元数据记录新 revision) 3. Rust 测试结构保持不变

也就是说,"换版本"只是一次re-pin + 再生成,不涉及 Rust 测试逻辑的任何改动——这正是元数据校验测试刻意不锁定具体版本值的原因。

重新生成夹具的操作步骤(记录在 generate_oracle.py 的模块文档中):

git clone https://github.com/derivexyz/v2-action-signing-python cd v2-action-signing-python git checkout <upstream_revision> # UPSTREAM_REVISION in this script python3 -m venv .venv .venv/bin/pip install . .venv/bin/python <nautilus_trader 仓库根目录>/crates/adapters/derive/tests/oracle-py/generate_oracle.py

生成器按自身所在位置解析默认输出路径,因此无论从哪个工作目录运行,都会把夹具写入test_data/common/signing_trade_action_vectors.json(也可用--out参数覆盖)。

值得注意的工程细节:生成器在记录 SDK 私有计算的action_hashtyped_data_hash时,会独立重算typed-data hash(keccak256(0x1901 || domain || action_hash))并与之比对,不一致即报错——这为夹具本身提供了防篡改护栏,避免"以错误实现为基准"。

八、许可证合规要点

从合规角度看,该声明明确了:

  • 上游v2-action-signing-pythonMIT许可证使用(依据 pyproject classifier 声明);
  • 由于固定修订版本上游仓库未附带 LICENSE 文件,归属与许可信息需以官方元数据为准;
  • 归属作者为 Derive.xyz(joshua@derive.xyz)与 8baller(8baller@station.codes)。

这一信息同时被冗余记录在 oracle 夹具的metadata.license字段中,形成文档与数据的双重留痕,便于审计与追溯。

九、相关文件索引

  • 第三方许可证声明:crates/adapters/derive/licenses/THIRD_PARTY_LICENSES.md
  • 签名模块总览:crates/adapters/derive/src/signing/mod.rs
  • EIP-712 实现与 oracle 测试:crates/adapters/derive/src/signing/eip712.rs
  • Trade 模块 ABI 编码器:crates/adapters/derive/src/signing/modules/trade.rs
  • Nonce 分配器:crates/adapters/derive/src/signing/nonce.rs
  • 签名上下文解析:crates/adapters/derive/src/signing/context.rs
  • 协议常量:crates/adapters/derive/src/common/consts.rs
  • 适配器配置:crates/adapters/derive/src/config.rs
  • Oracle 生成器:crates/adapters/derive/tests/oracle-py/generate_oracle.py
  • Oracle 向量夹具:crates/adapters/derive/test_data/common/signing_trade_action_vectors.json
  • Derive 适配器集成测试:crates/adapters/derive/tests/integration/

十、小结

这份第三方许可证声明揭示的,其实是 nautilus_trader Derive 适配器在加密签名领域的一种高置信度工程实践:独立实现协议、以官方 SDK 生成字节级 oracle、在 CI 测试中逐字节对拍、以固定版本保证可复现。它既满足了第三方材料的归属与许可合规要求,又为自托管签名这种"错一个字节就失败"的协议提供了可验证的正确性保证。理解这套机制,无论是审查适配器代码、排查签名问题,还是规划上游升级,都能事半功倍。

【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader

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

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

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

立即咨询