- 区块链
- Web3
【免费下载链接】grin
Minimal implementation of the Mimblewimble protocol.
本篇指南以仓库中 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 给出的标准流程如下(完整保留原文语义):
- 准备好用作 coinbase 的多签 output 与 kernel。在 Grin mainnet 的实际操作中,这一步由理事会(council)的财务负责人完成并保管密钥,可以提前几天准备。
- 从公开 API 获取最新的 Bitcoin 区块哈希。
- 构建创世块:以准备好的 coinbase 为奖励,Bitcoin 区块哈希作为
prev_root。区块时间戳被设置为未来 30 分钟(源码中实际为 45 分钟,见第 4.4 节),留出足够时间完成构建和启动节点。 - 挖掘区块,使 PoW 至少包含一个合法的 Cuckatoo Cycle 证明。此步骤不要求达到任何特定难度。
- 用 PoW 定稿区块,设置一个足够高的难度(具体数值另行商定)。
- 把区块信息提交到 GitHub(即写回
core/src/genesis.rs并提交)。 - 打版本标签 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.cuckooplugin | Cuckaroo29 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 个字段(每个字段找到一次后即从替换表移除,避免重复替换):
| 字段 | 来源 |
|---|---|
timestamp | gen.header.timestamp(年/月/日/时/分/秒) |
prev_root | 三源验证后的 Bitcoin 区块哈希 |
output_root/range_proof_root/kernel_root | 临时链set_txhashset_roots计算结果 |
total_kernel_offset | header 中的 kernel offset(创世块为零值) |
nonce/nonces | PoW nonce 与 Cuckaroo 证明的节点列表 |
excess/excess_sig | coinbase kernel 的 excess 承诺与签名 |
commit/proof | coinbase 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 为例,其已提交的关键字段:
| 字段 | 值 |
|---|---|
height | 0 |
timestamp | 2019-01-15 16:01:26 UTC |
prev_root | 0000000000000000002a8bc32f43277fe9c063b9c99ea252b483941dcd06e217(Bitcoin 区块哈希,前导零来自 Bitcoin 自身难度) |
output_root/range_proof_root/kernel_root | 三个 PM-MMR 状态根(源码中为固定 hex) |
total_difficulty | 2^34 |
secondary_scaling | 1856 |
nonce | 41 |
| PoW 证明 | Cuckaroo29(edge_bits: 29),由42 个节点编号组成的环 |
| coinbase | 1 个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 一节:
- 构建该 crate(在
etc/gen_gen目录下cargo build --release,需先把_Cargo.toml恢复为Cargo.toml并补齐wallet依赖,见第 1 节说明); - 在其根目录运行:
./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.
相关推荐
PocketFlow代码生成:自动编程工作流全解析
PocketFlow代码生成:自动编程工作流全解析 PocketFlow是一个极简的LLM框架,仅用100行代码即可实现让大型语言模型(LLM)自我编程的功能。
人工智能大模型AI Agent工作流自动化RAGcJSON代码生成工具:自动生成ANSI C JSON解析代码
cJSON代码生成工具:自动生成ANSI C JSON解析代码 引言 在嵌入式系统开发中,JSON(JavaScript Object Notation)作为一
序列化OpenEMR代码生成工具:自动创建医疗系统CRUD模块
OpenEMR代码生成工具:自动创建医疗系统CRUD模块 在医疗系统开发中,CRUD(创建、读取、更新、删除)模块是基础且高频的需求。手动编写这些重复代码不仅耗
医疗健康后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考