用 Java 15 行代码亲手对抗 SHA256:零基础理解 PoW 挖矿

·

以太坊 2.0 之后侧链不断分叉,但「共识机制」永远是面试必问。想要一次搞懂 工作量证明(Proof of Work,PoW)又不被 100 页白皮书本天书击倒?本文教你 95% 纯英文变量名零第三方库不带区块链钱包广告Java 手写 PoW 生块流程

关键词:PoW机制、Java区块链、SHA256碰撞、比特币难度调节、nonce随机值、创世区块、挖矿模拟


什么是 PoW

PoW,即 工作量证明机制,网络把一道「难于计算、却极易校验」的 Hash 题扔给矿工。矿工跑满 CPU,谁先找到满足条件的哈希,谁就拿到记账权与区块奖励;全网其他节点一秒钟就能验证答案正确。

一句话:挖矿 = 在 SHA256 的汪洋大海里反复撒网,直到捞到一串以 N 个 0 开头的中奖数字。


一个普通交易如何变成区块

  1. 矿工打包交易,组成 data
  2. 用上一区块哈希 preHash、当前时间戳 timestamp、难度值 diff 等拼接字符串。
  3. 修改随机数 nonce,不断计算 SHA256 直到结果前缀 连续 diff 个 0
  4. 提交新区块,全网轻节点秒级校验,接受即上链。

本文只挖前两步:生块 + 接链,后续广播、持久化留给你课后挑战。


Java 从零撸一条迷你链

1. Block 实体

跳过无用 getter/setter,保留最关键 7 个属性:

private String preHash;   // 上一区块 hash
private String hashCode;  // 当前区块 hash
private Timestamp ts;     // 时间戳
private int diff;         // 前导0个数
private String data;      // 交易
private int index;        // 高度
private int nonce;        // 随机值

2. 创世区块生成

首个区块没爹没娘,把 preHash = 0 即可。设定小难度值 diff=4,本地毫秒级即可挖到:

public Block genesis(String data){
    this.preHash="0";
    this.ts=new Timestamp(System.currentTimeMillis());
    this.diff=4;
    this.data=data;
    this.index=1;
    this.nonce=0;
    this.hashCode=genHash();
    return this;
}

3. SHA256 工具方法

用标准 java.security.MessageDigest 简单包装:

public static String sha256(String input){
    try{
        MessageDigest md=MessageDigest.getInstance("SHA-256");
        byte[]hash=md.digest(input.getBytes(StandardCharsets.UTF_8));
        StringBuilder hex=new StringBuilder();
        for(byte b:hash){
            String h=Integer.toHexString(0xff & b);
            if(h.length()==1) hex.append('0');
            hex.append(h);
        }
        return hex.toString();
    }catch(Exception e){
        throw new RuntimeException(e);
    }
}

4. 挖矿核心:暴力 nonce 循环

PoW 挖矿模拟器 方法 pow(int diff, Block block) 中,先把 “难度” 转成 0000 这样一段前缀:

String target="0".repeat(diff);

随后死循环迭加 nonce

do{
    block.setNonce(block.getNonce()+1);
    hash=sha256(block.header());   // header = index+nonce+diff+ts
}while(!hash.startsWith(target));

第 43257 次时,我们样例机器终于输出:

0000e5b2a3f6a4b1c7c82f...    // 4 个 0 通关

👉 亲手跑 numer 次 SHA256,才发现挖矿有多暴力!


5. 把区块挂进链

为了别把自己绕进复杂 Merkle Tree,我们用一条 单向链表 模拟即可:

Node head = new Node(genesisBlock,null);
Node second = new Node(nextBlock,head);

控制台完整链条打印:

{genesis} -> {block2, nonce=43257, diff=4, preHash=...}

FAQ:新手最常问的 6 件事

Q1:难度值 dif=5 为什么一下跑到 90 万 nonce?
A:概率按 16 进制算,每增加一位 0,工作量 ≈ +16 倍。家用笔记本 4 秒变 80 秒纯属正常。

Q2:真正比特币怎么动态调 diff?
A:每 2016 个区块统计前两周出块时间,若低于 10 分钟就上调难度,保证平均 10 分钟落成新区块。

Q3:Java 写 PoW 会慢死吗?
A:Java 单线程跑不过 C/Go,但多线程 + ForkJoinPool 可逼近 C 的效率。生产级矿机直接走 ASIC,不算我们用 Java 的事。

Q4:nonce 最大值会炸 int?
A:int 上限 2^31-1 足够模拟。真实矿池用 long,概率问题才是天花板。

Q5:演示里一直 fixed diff=4,怎么写动态算 diff?
A:序贯链表长度一旦满 2016 块,统计总耗时 realTime,重置 diff = 预期时间 / realTime * oldDiff。

Q6:本文代码能直接部署到主网吗?
A:不能。没有 P2P 网络、交易池验证、MPT 状态树、签名脚本。用于教学 + 面试秀肌肉足矣。


课后彩蛋:代码仓库一键跑

把完整工程塞到 IDE,右键执行 Minichain.main,即见创世区块秒出与新区块「挖矿成功」。
👉 点击获取完整源码,3 分钟上手 Java PoW 课程对照表


小结

深度学习不必 1000 行: