Java语言实现区块链(三)

一、增加工作量证明

(1)修改Block类,增加nonce字段,并重新生成构造函数。

// 区块
public class Block {
    private int id; // 区块ID
    private String content; // 区块数据
    private String hash; // 区块哈希
    private int nonce;  // 随机数

    public Block() {}

    public Block(int id, String content, String hash, int nonce) {
        this.id = id;
        this.content = content;
        this.hash = hash;
        this.nonce = nonce;
    }

    ...
}

(2)修改NoteBook类,增加mine方法,该方法用于生成nonce的值。这个过程称为“挖矿”。

//挖矿的过程就是求取一个符合特定规则的hash值
private static int mine(String content) {
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
        String hash = HashUtils.sha256(i + content);
        // 假设以0000开头的哈希值就是满足条件的哈希
        if (hash.startsWith("0000")) {
            return i;
        }
    }
    throw new RuntimeException("挖矿失败");
}

(3)修改addGenesisBlock和addBlock方法。设置随机数。

// 生成随机数
int nonce = mine(data);
// 生成并添加区块
blocks.add(new Block(
    blocks.size() + 1,
    data,
    HashUtils.sha256(nonce + data),
    nonce
));

(4)修改NoteBook的check方法,生成tempHash的时候需要把nonce加进去。

//String tempHash = HashUtils.sha256(block.getContent());
String tempHash = HashUtils.sha256(block.getNonce() + block.getContent());

(5)在页面显示none字段。

(6)运行程序,效果如下图所示:

 

二、增加PrevHash字段

(1)修改Block类,增加prevHash字段,该字段指向上一区块的hash。

// 区块
public class Block {
    private int id; // 区块ID
    private String content; // 区块数据
    private String hash; // 区块哈希
    private int nonce;  // 随机数
    private String prevHash; // 上一区块的哈希

    public Block() {}

    public Block(int id, String content, String hash, int nonce, String prevHash) {
        this.id = id;
        this.content = content;
        this.hash = hash;
        this.nonce = nonce;
        this.prevHash = prevHash;
    }

    ...

}

(2)修改NoteBook类。

第一步:修改addGenesisBlock方法,指定创世块prevHash的值为64个0。

// 指定创世块PrevHash
String prevHash = "0000000000000000000000000000000000000000000000000000000000000000";
// 生成随机数
int nonce = mine(data + prevHash);
// 生成并添加区块
blocks.add(new Block(
    blocks.size() + 1,
    data,
    HashUtils.sha256(nonce + data + prevHash),
    nonce,
    prevHash
));

注意:在挖矿和生成哈希时候,也要把prevHash加入计算。

第二步:修改addBlock方法,指定普通区块的prevHash为当前最后区块的哈希值。

// 获取最后区块
Block lastBlock = blocks.get(blocks.size() - 1);
// 生成随机数
int nonce = mine(data + lastBlock.getHash());
// 生成并添加区块
blocks.add(new Block(
    blocks.size() + 1,
    data,
    HashUtils.sha256(nonce + data + lastBlock.getHash()),
    nonce,
    lastBlock.getHash()
));

(3)修改页面,把prevHash字段显示出来。

(4)运行程序,效果如下图所示:

 

三、校验prevHash

修改NoteBook的check方法,对每一个普通区块都要校验prevHash。由于创世块的prevHash是固定值,所以创世块只需要校验hash即可,不需要校验prevHash。

// 校验数据
public String check() {
    StringBuilder sb = new StringBuilder();
    // 遍历每一个区块
    for (int i = 0; i < blocks.size(); i++) {
        Block block = blocks.get(i);
        int id = block.getId();
        String hash = block.getHash();
        String content = block.getContent();
        int nonce = block.getNonce();
        String prevHash = block.getPrevHash();
        // 校验hash
        String tempHash = HashUtils.sha256(nonce + content + prevHash);
        if (!tempHash.equals(hash)) {
            sb.append("编号为" + id + "的区块哈希有问题,请检查...<br>");
        }
        // 校验prevHash
        if (i > 0) {
            // 获取上一区块
            Block preBlock = blocks.get(i - 1);
            if (!prevHash.equals(preBlock.getHash())) {
                sb.append("编号为" + id + "的prehash有问题,请注意检查<br>");
            }
        }
    }
    return sb.toString();
}

 

 

 

 

 

 

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/zhongliwen1981/article/details/89707451