ARTICLE DETAIL

资讯详情

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

Bitcoin Core TestGen:地址与私钥测试向量的数据驱动生成指南

Bitcoin Core TestGen:地址与私钥测试向量的数据驱动生成指南 Bitcoin Core TestGen地址与私钥测试向量的数据驱动生成指南【免费下载链接】bitcoinBitcoin Core integration/staging tree项目地址: https://gitcode.com/GitHub_Trending/bi/bitcoin本文围绕 contrib/testgen/README.md 所描述的 TestGen 工具集展开讲解如何用它为 Bitcoin Core 的数据驱动data-driven测试生成 Base58Check 与 Bech32/Bech32m 地址、WIF 格式私钥的合法/非法测试向量并深入 生成脚本 的实现细节与 C 消费端 的对接方式。读完本文你能独立完成测试向量的重新生成、理解向量 JSON 的元数据语义并掌握非法向量受控损坏的设计思路。一、TestGen 在仓库中的定位contrib/testgen/README.md 对 TestGen 的定义只有一句话但信息量很足Utilities to generate test vectors for the>./gen_key_io_test_vectors.py valid 70 ../../src/test/data/key_io_valid.json ./gen_key_io_test_vectors.py invalid 70 ../../src/test/data/key_io_invalid.json参数含义来自 脚本入口 的解析逻辑位置取值说明第 1 个参数valid/invalid省略时默认valid选择生成器gen_valid_vectors或gen_invalid_vectors第 2 个参数整数如70省略时为0通过itertools.islice截断的向量条数0表示不截断合法向量是无限生成器实际必须给个数标准输出JSON以json.dump(data, sys.stdout, sort_keysTrue, indent4)输出因此用重定向写入目标文件确定性是这套工具最重要的工程属性入口显式执行random.seed(42)L252所以同一版本脚本 同一参数在任意机器上产出的字节序列完全一致。这也意味着脚本或模板发生任何改动后重新生成的 JSON 都会与仓库中已检入的文件产生 diff——这正是 Bitcoin Core 工作流中scripted-diffREADME 开头所指的 To use inside a scripted-diff的用法把向量重生成作为代码变更的一部分提交让评审者能直接看到新向量。三、向量 JSON 的数据结构3.1 合法向量每条合法向量是一个三元素数组[编码字符串, 载荷十六进制, 元数据对象]。从 key_io_valid.json 中摘录一个真实样本[ 1FsSia9rv4NeEwvJ2GvXrX7LyxYspbN2mo, 76a914a31c06bd463e3923bc1aadbde48b16976c08071788ac, { chain: main, isPrivkey: false } ]第 0 个元素Base58Check 地址此处是主网 P2PKH 地址第 1 个元素C 侧期望解析出的完整脚本十六进制——76a914…88ac正是OP_DUP OP_HASH160 20字节 OP_EQUALVERIFY OP_CHECKSIG的序列化这与脚本里pubkey_prefix (OP_DUP, OP_HASH160, 20)、pubkey_suffix (OP_EQUALVERIFY, OP_CHECKSIG)的定义一一对应L32-L35第 2 个元素元数据键集合由 metadata_keys 固定为[isPrivkey, chain, isCompressed, tryCaseFlip]生成时只保留非None的键L156。元数据语义在 C 端 key_io_tests.cpp 消费isPrivkey区分 WIF 私钥与公钥地址决定走DecodeSecret还是DecodeDestination断言分支chain取值main/testnet4/signet/regtestC 端用ChainTypeFromString解析后调用SelectParams切换链参数再校验私钥与地址的前缀字节是链相关的isCompressed仅私钥向量携带断言privkey.IsCompressed()与之相符tryCaseFlipbech32 向量携带true要求 C 端把字符串大小写翻转后再次解码并断言其有效性见 L62-L75。当前检入的 70 条合法向量在四条链上的分布为main18 条、testnet418 条、signet18 条、regtest16 条。3.2 非法向量非法向量更简单[编码字符串]单元素数组即可测试代码注释说明允许额外元素用于注释L132。key_io_invalid.json 开头是两条手工边缘用例空串和单字符x对应 gen_invalid_vectors 中的两个yield其后是随机生成的损坏样本例如1GAdfviErV2Ew95FPtZyikz2qGP3gyCB6Hyu94sedAkPpA523m3fQwps9YKUZkKgQckGPKhRsFR长度超长的 Base58 串。四、合法向量的生成原理4.1 Base58Check 模板表templates 是一张 16 行模板表每行形如(前缀字节, 载荷长度, 后缀字节, 元数据, 期望脚本前缀, 期望脚本后缀)。前缀字节直接来自脚本顶部的版本常量L20-L29版本字节类型出现链0主网 P2PKH 公钥地址main5主网 P2SH 脚本地址main111测试网/Signet/Regtest P2PKHtest、signet、regtest196测试网/Signet/Regtest P2SHtest、signet、regtest128主网 WIF 私钥无压缩标记后缀main239测试系 WIF 私钥test、signet、regtest注意主网私钥有两行模板一行后缀为空非压缩元数据isCompressed: False一行后缀为(1,)压缩私钥末尾多一个0x01压缩标记元数据isCompressed: True——这解释了 JSON 中主网私钥为何存在 64 字符32 字节与 65 字符33 字节两种长度。4.2 Bech32 模板表bech32_templates 按 HRP人类可读前缀×见证版本×程序长度组合出 15 行bc主网v0 P2WPKH20 字节BECH32、v0 P2WSH32 字节BECH32、v1 P2TR32 字节BECH32M、v2 2 字节自定义程序BECH32Mtb测试系覆盖 v0/v1 及 v3 16 字节等组合bcrtRegtest覆盖 v0/v1 及 v16 40 字节这种极端长度组合。v0/v1 分别用 BECH32 与 BECH32M 编码这一分界正是 BIP-350 对 Taproot 引入的编码升级生成时通过bech32_encode(encoding, hrp, [witver] convertbits(witprog, 8, 5))完成 8→5 bit 分组gen_valid_bech32_vector。4.3 自校验闭环gen_valid_vectors 是一个无限生成器轮询所有模板产出向量后先用脚本内置的is_valid()断言其确实合法Base58 路径按前缀/载荷长度/后缀逐模板匹配Bech32 路径调用decode_segwit_address试解 bc/tb/bcrt 三个 HRP。这个生成即验证的闭环保证写进 JSON 的合法向量不会因模板改动而静默失效。五、非法向量的受控损坏设计非法向量生成器 gen_invalid_base58_vector 与 gen_invalid_bech32_vector 不是纯随机乱串而是按概率对合法结构做单维度定点破坏配合 bech32_ng_templates 中显式声明的结构性违例确保覆盖面破坏维度Base58 侧实现概率/条件前缀字节随机化corrupt_prefix randbool(0.2)20%载荷长度随机化按指数分布取max(int(random.expovariate(0.5)), 50)字节20%后缀脚本 opcode 序列随机化corrupt_suffix randbool(0.2)20%行内字符损坏10% 概率在串尾追加或中部替换一个 base58 字符random.randint(0,10) 1校验和天然损坏上述任一破坏都会使base58_to_byte的校验和断言失败结构性Bech32 侧则覆盖no_data空数据10%、大小写翻转swapcase10%BIP-173 规定全大写须整体合法——翻转后校验失败、非法 HRP如tc、bt、非法见证版本17、15、违例程序长度1、41、16、编码不匹配v1 却用 BECH32v0 却用 BECH32M、以及模板显式指定的校验和位翻转invalid_checksum与非法字符替换invalid_char利用CHARSET替换实现。生成的每条候选都经过is_valid(val)过滤只有确实无法被解码器接受的字符串才会进入 invalid JSONL246-L247。这保证 negative 测试集里不存在名义非法、实际合法的脏数据。六、C 端数据驱动测试如何消费向量6.1 三个测试用例src/test/key_io_tests.cpp 定义了三个 Boost 用例全部以BasicTestingSetup为 fixturekey_io_valid_parseL24-L82按元数据中的chain调用SelectParams私钥走DecodeSecret并断言IsValid、压缩标志、逐字节比对载荷公钥走DecodeDestinationGetScriptForDestination断言脚本十六进制与向量一致随后翻转大小写再解码验证结果与tryCaseFlip相符最后做交叉断言——私钥当地址解码必须失败、地址当私钥解码必须失败。key_io_valid_genL85-L119方向相反的往返测试——从十六进制载荷构造CKey/脚本调用EncodeSecret/EncodeDestination断言输出字符串与向量的编码完全一致。parse 与 gen 双向合围任何一端实现漂移都会暴露。key_io_invalidL123-L148对每条非法字符串在 MAIN/TESTNET/SIGNET/REGTEST 四条链上分别调用DecodeDestination与DecodeSecret要求全部拒绝。6.2 JSON 如何进入二进制向量并非在运行时读文件而是构建期嵌入src/test/CMakeLists.txt 中target_json_data_sources(test_bitcoin ... data/key_io_invalid.json data/key_io_valid.json ...)将 JSON 编译为 C 字符串常量测试代码通过#include test/data/key_io_valid.json.h拿到json_tests::key_io_valid再交给 read_json 解析。因此向量更新必须重新生成 重新构建才能生效也天然保证了测试数据与可执行文件版本严格一致。七、实操注意事项命令的工作目录README 中的../../src/test/data/...假定你在contrib/testgen/下执行若改为从仓库根目录运行则命令应写作python3 contrib/testgen/gen_key_io_test_vectors.py valid 70 src/test/data/key_io_valid.json。条数要匹配当前检入文件均为 70 条若生成其他数量C 测试仍可运行它按实际长度遍历但会偏离仓库当前状态。脚本与检入数据的历史差异仓库现版脚本的templates中测试链元数据写的是test而检入的 key_io_valid.json 里出现的键值是testnet4C 端 chaintype.cpp 同时兼容testnet与testnet4两种写法。可以推断检入向量由较早版本脚本生成当前脚本重跑会产生元数据键值 diff属于预期行为而非错误。依赖关系脚本头部通过sys.path.append(... ../../test/functional)复用 test_framework 中的address、segwit_addr、script模块L14-L18因此它必须在完整仓库树内运行单独拷贝脚本会因缺少这些模块而失败。八、小结TestGen 展示了数据驱动测试的完整闭环模板表把何为合法形式化前缀/长度/后缀/HRP/见证版本/编码受控损坏把何为非法穷举化固定随机种子保证可复现target_json_data_sources把数据编译期固化进测试二进制parse/gen 双向用例再锁死实现与数据的一致性。当你在 src/key_io.cpp、src/consensus/amount.h 相关编码路径上做了改动重新运行 contrib/testgen/README.md 给出的两条命令、审阅向量 diff、再跑test_bitcoin中的 key_io 用例就是标准的验证流程。【免费下载链接】bitcoinBitcoin Core integration/staging tree项目地址: https://gitcode.com/GitHub_Trending/bi/bitcoin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表