一、增加工作量证明
(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();
}