☰
Grin 创世块自动生成工具:gen_gen 生成流程与源码全解析
2026/10/14 15:39:21 网站建设 项目流程
  • 区块链
  • Web3

【免费下载链接】grin

Minimal implementation of the Mimblewimble protocol.

项目地址:https://gitcode.com/gh_mirrors/gr/grin
点击查看免费下载

本篇指南以仓库中 etc/gen_gen/README.md 为核心,完整还原 Grin 用于自动生成与发布新创世块(genesis block)的gen_gen工具:它的七步生成流程、依赖与前置文件、从获取 Bitcoin 链头到回写genesis.rs的完整源码实现,以及当前仓库中已提交的 mainnet/testnet 创世块长什么样。读完之后,你可以独立读懂创世块每个字段的来源,并能按文档构建和运行该工具(在其可构建的前提下)。

1. gen_gen 是什么,以及它当前的状态

gen_gen是一个独立的小 crate(包名grin_gen_gen,二进制名gen_gen),用于以自动化方式生成并发布 Grin 的创世块。README 开篇即说明:

This crate isn't strictly part of grin but allows the generation and release of a new Grin Genesis in an automated fashion.

也就是说,它不属于 grin 节点本体的常规构建链路,而是一个面向"发网"这一低频运维动作的一次性工具。当前仓库中它处于被禁用状态,有两处源码级证据:

  • 其Cargo.toml已被重命名为 _Cargo.toml。README 明确写道:"This crate'sCargo.tomlfile has been disabled by renaming it to_Cargo.toml. It no longer builds due to changes in the project structure."
  • 根 Cargo.toml 的 workspace 定义中显式排除了它:exclude = ["etc/gen_gen"](第 16 行)。

从源码结构看,无法构建的原因可以推断为:_Cargo.toml依赖grin_wallet = { path = "../../wallet" },而当前仓库快照中已不存在wallet/目录(wallet 已拆分为独立仓库);且src/bin/gen_gen.rs使用的旧版 chrono API(Utc.ymd(...).and_hms(...))也与现行core/src/genesis.rs中使用的Utc.with_ymd_and_hms(...)不一致。因此本文以"解读历史工具与创世块机制"为主线,同时给出当前仓库中仍然有效的验证入口。

2. 创世块生成流程:README 的七个步骤

etc/gen_gen/README.md 给出的标准流程如下(完整保留原文语义):

  1. 准备好用作 coinbase 的多签 output 与 kernel。在 Grin mainnet 的实际操作中,这一步由理事会(council)的财务负责人完成并保管密钥,可以提前几天准备。
  2. 从公开 API 获取最新的 Bitcoin 区块哈希。
  3. 构建创世块:以准备好的 coinbase 为奖励,Bitcoin 区块哈希作为prev_root。区块时间戳被设置为未来 30 分钟(源码中实际为 45 分钟,见第 4.4 节),留出足够时间完成构建和启动节点。
  4. 挖掘区块,使 PoW 至少包含一个合法的 Cuckatoo Cycle 证明。此步骤不要求达到任何特定难度。
  5. 用 PoW 定稿区块,设置一个足够高的难度(具体数值另行商定)。
  6. 把区块信息提交到 GitHub(即写回core/src/genesis.rs并提交)。
  7. 打版本标签 1.0.0(README 原文括号里的自嘲:"scary")。

这七步与src/bin/gen_gen.rs的main()实现几乎一一对应,下面逐节拆解。

3. 依赖与运行前置文件

3.1 crate 依赖(_Cargo.toml)

_Cargo.toml 声明了二进制目标[[bin]] name = "gen_gen",path = "src/bin/gen_gen.rs",其依赖分为三类:

依赖用途
grin_core/grin_chain/grin_store/grin_keychain/grin_util(path 依赖)构造Block、初始化临时链、计算 txhashset 根、密钥派生
grin_wallet(path../../wallet)读取本地加密钱包种子wallet.seed并派生 coinbase
cuckoo_miner = "1.0.0"+grin_miner_plugin = "1.0.0"加载挖矿插件并驱动 Cuckaroo29 求解
curl+serde_json请求三个公开 Bitcoin 链头 API 并解析 JSON
chrono/rpassword/serde_json时间戳计算、交互式输入钱包密码、JSON 处理

3.2 运行前必须存在的三个文件

main()开头(src/bin/gen_gen.rs 第 47–65 行)对三个路径做存在性检查,缺失即panic!,提示 "make sure you're running this from the gen_gen directory":

常量路径说明
GENESIS_RS_PATH../../core/src/genesis.rs最终要回写创世块信息的源码文件
PLUGIN_PATH./cuckaroo_mean_cuda_29.cuckoopluginCuckaroo29 CUDA 挖矿插件(不随仓库分发)
WALLET_SEED_PATH./wallet.seed本地加密钱包种子文件(运行时交互输入密码)

4. 实现逐段解析(src/bin/gen_gen.rs)

4.1 三个公开 API 交叉验证 Bitcoin 链头

代码定义了三条链头查询端点(第 39–41 行):

static BCHAIN_INFO_URL: &str = "https://blockchain.info/latestblock"; static BCYPHER_URL: &str = "https://api.blockcypher.com/v1/btc/main"; static BCHAIR_URL: &str = "https://api.blockchair.com/bitcoin/blocks?limit=2";

main()中分别请求三个 API 得到h1、h2、h3,若三者不一致则直接panic!("Bitcoin chain head is inconsistent, please retry ...")(第 67–77 行)。这是一个典型的多源交叉验证设计:创世块的prev_root将被永久刻进代码,任何单点 API 故障或返回错误都可能导致锚定到错误的 Bitcoin 区块,因此三源一致是硬前提。

4.2 从本地钱包种子构建 coinbase 奖励

let gen = core::genesis::genesis_main(); // 以现有 mainnet 创世块模板为底 let seed = wallet::WalletSeed::from_file( &wallet::WalletConfig::default(), &rpassword::prompt_password_stdout("Password: ").unwrap(), ).unwrap(); let keychain: ExtKeychain = seed.derive_keychain(false).unwrap(); let key_id = ExtKeychain::derive_key_id(3, 1, 0, 0, 0); let reward = core::libtx::reward::output(&keychain, &key_id, 0).unwrap(); gen = gen.with_reward(reward.0, reward.1); // (coinbase Output, coinbase TxKernel)

关键点:

  • 密钥材料(wallet.seed)始终留在本地,工具只向core/src/genesis.rs写入最终的 commitment、range proof 与 excess 签名等不可逆数据——这正是 Mimblewimble 隐私模型下"创世币由多签托管、公链只存承诺"的实现方式。
  • core::libtx::reward::output(keychain, key_id, fees)(core/src/libtx/reward.rs 第 28–83 行)会完成:对reward(fees)金额做 Pedersen 承诺、生成 bulletproof 范围证明、构造OutputFeatures::Coinbase的 output,再用 excess 对 coinbase kernel 签名。
  • 奖励金额的来源是 core/src/consensus.rs 第 42–48 行:REWARD = BLOCK_TIME_SEC * GRIN_BASE(注释:one grin per second on average,即每 60 秒区块约 60 GRIN),reward(fee) = REWARD + fee。gen_gen 传入fees = 0,因此创世块 coinbase 就是 60 GRIN 的初始铸币。
  • 对 mainnet,README 说明真实操作中这一步由理事会财务负责的多签地址完成,工具源码展示的是"本地种子派生"的通用路径。

4.3 用临时链计算 txhashset 三个根

core::global::set_local_chain_type(core::global::ChainTypes::UserTesting); let tmp_chain = setup_chain(".grin.tmp", core::pow::mine_genesis_block().unwrap()); tmp_chain.set_txhashset_roots(&mut gen).unwrap();
  • setup_chain(第 269–287 行)在本地建一个 LMDB 目录.grin.tmp,通过chain::Chain::init初始化一条临时链;其初始创世块来自core::pow::mine_genesis_block()(core/src/pow.rs 第 73–85 行)——对genesis_dev()用内置 miner 现场挖一个小难度证明。
  • set_txhashset_roots(chain/src/chain.rs 第 762–794 行)把待发布的创世块b应用到临时 txhashset 状态上,从而求出 header 所需的output_root、range_proof_root、kernel_root,以及output_mmr_size/kernel_mmr_size(注释特别指出 size 必须先于 root 写入,因为output_root依赖output_mmr_size)。

这一步体现了 Grin 的存储结构:区块头里的三个根是PM-MMR(partially mutable merkle mountain range)状态根,必须经由真实链存储才能正确计算,无法手工伪造。

4.4 写入 timestamp 与 prev_root(顺序有讲究)

// sets the timestamp and prev_root from the bitcoin block (needs to be // after set_txhashset roots to not get overwritten) gen.header.timestamp = Utc::now() + Duration::minutes(45); gen.header.prev_root = core::core::hash::Hash::from_hex(&h1).unwrap();
  • 未来时间戳:代码里实际是45 分钟(README 写的是 30 min),目的相同——留出编译、发布与启动节点的窗口,使区块"出生"时时间戳尚未过期。
  • prev_root锚定 Bitcoin:Grin 创世块没有"父区块",于是把当时最新的 Bitcoin 区块哈希写入prev_root,把新链锚定到 Bitcoin 链的某一具体高度上。注释明确了顺序约束:必须在set_txhashset_roots之后写,否则会被覆盖。
  • 从源码结构看,prev_root之后只被验证/展示使用(例如写入最终genesis.rs),它承担的是"创世时刻的链锚"角色。

4.5 用挖矿插件挖出一个 Cuckaroo29 环

core::global::set_local_chain_type(core::global::ChainTypes::Mainnet); let plugin_lib = cuckoo::PluginLibrary::new(PLUGIN_PATH).unwrap(); let mut params = plugin_lib.get_default_params(); params.mutate_nonce = false; // 插件不碰 nonce,由外层循环递增 let solver_ctx = plugin_lib.create_solver_ctx(&mut params); let mut nonce = 0; while solver_sols.num_sols == 0 { gen.header.pow.nonce = nonce; let _ = plugin_lib.run_solver(solver_ctx, gen.header.pre_pow(), nonce, 1, &mut solver_sols, &mut solver_stats); ... nonce += 1; }

要点:

  • 求解器由cuckoo_miner加载外部 CUDA 插件cuckaroo_mean_cuda_29.cuckooplugin,通过grin_miner_plugin的run_solver接口以"一次一个 nonce、直到num_sols > 0"的方式循环。
  • 每次求解的输入是gen.header.pre_pow()(即除 nonce 外的 header 哈希),这正是 Cuckoo 系列 PoW 的工作点——在由 header 哈希生成的 base graph 上找环(参见 doc/pow/pow.md 对 cuckoo base 与环的图解)。
  • 此阶段不追求难度,只要拿到一个合法环即可,对应 README 第 4 步"at least a valid Cuckatoo Cycle"。

4.6 校验:secondary 断言、verify_size 与整块 validate

gen.header.pow.proof.nonces = solver_sols.sols[0].to_u64s(); assert!(gen.header.pow.is_secondary(), "Not a secondary header"); core::pow::verify_size(&gen.header).unwrap(); gen.validate(&BlindingFactor::zero(), Arc::new(util::RwLock::new(LruVerifierCache::new()))).unwrap();
  • Cuckaroo29 的 29-bit 证明是 Grin 的secondary proof(次级证明),is_secondary()断言保证挖出来的正是主链要求的证明类型。
  • core::pow::verify_size(core/src/pow.rs 第 62–71 行)验证证明大小/合法性;Block::validate则做整块校验(含 coinbase 输出、范围证明与 kernel 签名)。
  • 最后打印两份指纹供人工比对与提交:cyclehash(header 哈希)与序列化后整个区块的 full hash:
Final genesis cyclehash: ... Final genesis full hash: ...

4.7 回写 core/src/genesis.rs:12 个字段的模板替换

update_genesis_rs(第 160–267 行)是"提交到 GitHub"这一步的自动化实现:它打开core/src/genesis.rs,对每一行包含REPLACE标记的行做模板替换,依次回填 12 个字段(每个字段找到一次后即从替换表移除,避免重复替换):

字段来源
timestampgen.header.timestamp(年/月/日/时/分/秒)
prev_root三源验证后的 Bitcoin 区块哈希
output_root/range_proof_root/kernel_root临时链set_txhashset_roots计算结果
total_kernel_offsetheader 中的 kernel offset(创世块为零值)
nonce/noncesPoW nonce 与 Cuckaroo 证明的节点列表
excess/excess_sigcoinbase kernel 的 excess 承诺与签名
commit/proofcoinbase output 的 Pedersen 承诺与范围证明字节

替换完成后程序提示人工做最后两件事(第 155–156 行):

genesis.rs has been updated, check it and run mainnet_genesis_hash test also check bitcoin block {} hasn't been orphaned.

即运行创世块哈希测试,并确认所锚定的 Bitcoin 区块没有被重组成孤块——这是把"发网"从纯代码操作升级为需要双人复核的准共识行为的设计。

5. 最终成果:当前仓库中的 genesis.rs

经过上述流程,core/src/genesis.rs 中现在保存着三个创世块构造函数:genesis_dev()、genesis_test()与genesis_main()(第 157–266 行)。以 mainnet 为例,其已提交的关键字段:

字段值
height0
timestamp2019-01-15 16:01:26 UTC
prev_root0000000000000000002a8bc32f43277fe9c063b9c99ea252b483941dcd06e217(Bitcoin 区块哈希,前导零来自 Bitcoin 自身难度)
output_root/range_proof_root/kernel_root三个 PM-MMR 状态根(源码中为固定 hex)
total_difficulty2^34
secondary_scaling1856
nonce41
PoW 证明Cuckaroo29(edge_bits: 29),由42 个节点编号组成的环
coinbase1 个OutputFeatures::Coinbase输出(承诺 + bulletproof)与 1 个 coinbase kernel(excess + 签名)

文件末尾的测试(第 268–308 行)把创世块哈希固定为断言值,任何对创世块代码的改动都会让 CI 失败,例如 mainnet 的:

#[test] fn mainnet_genesis_hash() { global::set_local_chain_type(global::ChainTypes::Mainnet); let gen_hash = genesis_main().hash(); ... assert_eq!(gen_hash.to_hex(), "40adad0aec27797b48840aa9e00472015c21baea118ce7a2ff1a82c0f8f5bf82"); ... }

这就是 gen_gen 流程"回写后跑测试"一环在仓库中的长期形态——创世块被测试常量永久钉死。

6. 使用方式

按 README 的 Usage 一节:

  1. 构建该 crate(在etc/gen_gen目录下cargo build --release,需先把_Cargo.toml恢复为Cargo.toml并补齐wallet依赖,见第 1 节说明);
  2. 在其根目录运行:
./target/release/gen-gen --coinbase <file> --difficulty <u64> --tag <version>

需要如实说明:从源码结构看,当前仓库中的main()并未解析这些命令行参数,而是使用第 3.2 节列出的三个硬编码路径与内置流程;README 中的--coinbase、--difficulty、--tag参数是该工具的接口设计/使用说明,实际行为以源码为准。运行前提还包括:本机可访问三个 Bitcoin 公开 API、具备 CUDA 环境以加载.cuckooplugin插件、准备好加密的wallet.seed。

7. 设计要点小结

  • 多源验证锚点:prev_root锚定的 Bitcoin 哈希必须经三个独立 API 交叉一致,杜绝单点数据污染永久进入代码。
  • 隐私与托管分离:创世币的多签私钥留在理事会(或本地wallet.seed)手中,链上只存在承诺、范围证明与签名,符合 Mimblewimble 的基本模型(参见 doc/translations/mimblewimble.md)。
  • 根不可手搓:三个 MMR 根必须经由grin_chain的真实链状态(临时链.grin.tmp)计算,保证了创世块与节点验证逻辑的同一性。
  • 先合法、后难度:先挖"任意合法 Cuckatoo 环"再"另行商定高难度",把 PoW 的合法性与发网参数的商定解耦;最终 mainnet 创世块以total_difficulty = 2^34定稿。
  • 代码即账本锚:成果回写genesis.rs并用mainnet_genesis_hash等测试把哈希钉死,配合"检查 Bitcoin 区块未孤块"的人工复核,形成可审计的发网闭环。
  • 区块链
  • Web3

【免费下载链接】grin

Minimal implementation of the Mimblewimble protocol.

项目地址:https://gitcode.com/gh_mirrors/gr/grin
点击查看免费下载

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

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

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

立即咨询