一条 Share 是怎样诞生的?从 Coinbase 到 Nonce —— ESP32 矿机固件深度解析(第4/15篇)
从 Coinbase 到 Share:比特币工作构造与 Nonce 校验
摘要:ASIC 并不理解 Stratum JSON,它需要固定格式的 SHA-256 工作。本文分析
components/stratum/mining.c,解释 Coinbase、Merkle Root、区块头、难度和版本滚动如何串成一条可验证的数据链。
关键词:Bitcoin、Coinbase、Merkle Root、SHA-256、Nonce、矿池难度
1. 一份 mining.notify 缺少什么
矿池通知通常提供 prevhash、coinbase1、coinbase2、Merkle 树分支、version、nBits 和 nTime。Coinbase 中间故意留出:
coinbase1 + extranonce1 + extranonce2 + coinbase2
extranonce1 由矿池在订阅时分配,用来区分连接;extranonce2 由设备递增,用来从一条通知产生大量互不相同的 Merkle Root。这解决了单个 Nonce 空间很快被高速 ASIC 耗尽的问题。
2. Coinbase 与 Merkle Root
calculate_coinbase_tx_hash() 把十六进制字段解码后执行双 SHA-256。随后 calculate_merkle_root_hash() 逐层与矿池提供的 Merkle 树兄弟节点组合:
root0 = SHA256d(coinbase)
root1 = SHA256d(root0 || branch0)
root2 = SHA256d(root1 || branch1)
...

这里最容易出错的是字节序。Bitcoin 在协议显示、内部哈希和 ASIC 字段之间混合使用大端展示与小端序列化。仓库中的 reverse_32bit_words()、reverse_endianness_per_word() 不是重复工具,而是在不同硬件接口边界做变换。
任何字节序改动都应使用已知区块向量测试,而不能只看矿池是否偶尔接受 Share。components/stratum/test/test_mining.c 已覆盖 Coinbase、Merkle Root、BM job 和 Nonce 校验,是重构这部分代码时最重要的安全网。
3. 构造 ASIC 作业
construct_bm_job() 将通知和 Merkle Root 转为 bm_job。除区块头字段外,还包含矿池难度、job id、extranonce2、版本 mask 等软件元数据。硬件 job id 只有 7 位空间,因此软件维护 128 个活动槽位。
ASIC 扫描一个 job 的理论周期由下式估算:
t = 2^32 / (frequency * small_core_count * asic_count)
代码据此控制下发节奏。如果下发太慢,ASIC 空转;太快则复用 job id,尚未返回的 Nonce 可能对应已释放对象。
4. Nonce 为什么必须在 ESP32 再验一次
ASIC 返回 nonce、job_id 和 rolled_version。test_nonce_value() 用这些字段重建 80 字节区块头,再双 SHA-256,最后把 256 位结果转换成难度。
只有当:
nonce_diff >= pool_diff
才发送 mining.submit。这一步既过滤硬件错误,也区分“达到芯片内部筛选难度”和“达到矿池 Share 难度”。如果达到全网难度,设备还会更新 block found 状态,但最终是否成为有效区块仍以网络接受为准。
5. 难度计算中的精度问题
代码部分路径使用 double 表示难度,系统状态又把网络难度保存为 uint64_t。对普通 Share 足够,但在极高难度和最佳难度统计中要注意:IEEE-754 double 只有 53 位整数精度。
如果目标是精确展示而非仅比较阈值,可以保留 256 位 target,比较时使用定长大整数;展示时再转换成科学计数法。这样“计算正确性”和“UI 可读性”不会绑在同一数据类型上。
6. 改进建议
首先,把字节序写进类型或函数名,例如 hash_internal_le、header_wire,减少“再 reverse 一次试试”的调试方式。其次,对矿池输入建立严格长度上限,并对 extranonce2_len 做栈数组尺寸保护。最后,为 job id 复用增加 generation counter;仅验证 7 位 id,无法区分相隔一轮的同号作业。
挖矿工作构造的专业性不在代码行数,而在每一步都能用确定向量复现。矿池接受 Share 是最终结果,但单元测试、字节序约定和对象生命周期才是长期可维护的基础。
更多推荐


所有评论(0)