在本系列前两篇文章中,我们向大家展示了如何通过精炼的Java代码实现一个简单的区块链。包括生成块,验证块数据,广播通信等等,这一篇让我们聚焦在如何实现 PoW算法。

大家都无不惊呼比特币、以太坊及其他加密电子货币的持续狂热,特别是对于刚接触这个领域的新手,不断得听到张三李四通过 GPU “挖矿”而聚集价值数万乃至数百万加密电子货币。那么“挖矿”到底是什么? 它是如何工作的? 相信对于程序员来说,没有什么比自己动手实践一遍“挖矿”算法更好的学习办法了。

在这篇文章中,让我们一起逐个解读每一个问题,并最终编写出自己的“挖矿”算法。这个算法称为工作证明算法(Proof-of-Work),它是比特币和以太坊这两种最流行的加密货币的基础。

什么是“挖矿”?

加密电子货币因为稀缺才具有价值。以现在的比特币为例,如果任何人任何时候都可以随意“制造”比特币,那么作为电子货币它会变得毫无价值。比特币通过算法来控制产出的速率并在大约122年内达到最大量。这种随着时间推移缓慢、稳定并逐步产出的方式有效避免了通货膨胀。

比特币的产出是通过给予“获胜矿工”奖励来实现,为了获取比特币奖励矿工之间会进行竞争。这个过程之所以被称为“挖矿”,是因为它类似于“Gold Rush”时每个黄金矿工通过辛苦劳作并最终(希望)找到一点黄金。

“挖矿”是如何工作的?

如果 Google 一下这个问题,你会得到大量的结果。简单来说,“挖矿”就是“解决一个数学难题”的过程。我们先来了解一些密码学和哈希算法的知识。

密码学简要介绍

单向加密以人类可读的文本(明文)作为输入,比如“Hello world”这个字符串,再通过一个数学函数产生出难以辨认的输出(密文)。 这类函数或算法的性质和复杂性各不相同。 算法越复杂,逆向工程就越困难。

以流行的 SHA-256 算法为例。 通过这个网站可以让你计算任意给定输入的输出,也就是 SHA-256 哈希值。比如让我们输入“Hello world”,看看得到了什么:

通过不断尝试计算“Hello world”的哈希值。你会发现每次的结果都完全相同。 这个过程称为幂等性。

加密算法一个最基本的特性是,非常难以通过反向工程来求解输入,但是非常容易验证输出。比如上面的例子,你可以很容易验证给定输入“Hello world”的SHA-256哈希值是否正确,但很难通过给定的哈希值判断它的输入是什么。这就是为什么将这种类型的算法称为单向加密。

比特币使用 Double SHA-256,它将 SHA-256 求得的哈希值作为输入再次计算 SHA-256 哈希值。 为了简化,我们只使用一次SHA-256。

挖矿

回到加密电子货币中,比特币就是通过让参与者利用这样的加密算法求解出符合特定条件的哈希值来实现“挖矿”过程。具体来说,比特币要求参与者通过 double SHA-256 算法计算出“前导0”超过若干位的哈希值,第一个求解出来的参与者就是“获胜的矿工”。

比如,我们求“886”这个字符串的 SHA-256 哈希值:

可以看到,是一个“前导0”为3位的哈希值(前三位是0)。

回忆我们前面说到的“单向加密”的特点:

任何人都可以很容易地验证“886”是否产生3位“前导0”的哈希值。但为了找到这样一个能产生3位“前导0”的输入(就是这里的“886”),我们做了大量繁琐的计算工作:从一个很大的数字和字母集合中逐个计算它们的哈希值并判断是否满足上述条件。如果我是第一个找到“886”的人,那其他人通过验证就能判断我做了这样大量繁琐的工作。在比特币、以太坊中这样的过程就称为工作证明算法。

“如果我运气非常好,第一次尝试就找到了一个符合条件的(输入)值呢?” —— 这是非常不可能的,你可以试试随意输入一些字母和数字。

比特币中实际的算法和约束要比上说要求复杂,当然也更难(要求更多位的“前导0”)。同时它也可以动态调整难度,目标是确保每隔10分钟产出一次比特币,不管参与“挖矿”的人多还是少。

差不多可以动手了

了解了足够的背景知识,接着我们就用 Java语言来编码实践下工作量证明(Proof-of-Work)算法。建议你阅读之前的系列文章,因为下面工作证明算法部分会涉及之前的代码。

Proof-of-work

创建新块并加入到链上之前需要完成“工作量证明”过程。我们先写一个简单的函数来检查给定的哈希值是否满足要求。

  • 哈希值必须具有给定位的“前导0”

  • “前导0”的位数是由难度(difficulty)决定的

  • 可以动态调整难度(difficulty)来确保 Proof-of-Work 更难解

下面就是 isHashValid 这个函数:

    /**
* 校验HASH的合法性
*
* @param hash
* @param difficulty
* @return
*/
public static boolean isHashValid(String hash, int difficulty) {
String prefix = repeat("0", difficulty);
return hash.startsWith(prefix);
}   private static String repeat(String str, int repeat) {
final StringBuilder buf = new StringBuilder();
for (int i = 0; i < repeat; i++) {
buf.append(str);
}
return buf.toString();
}

我们定义prefix 变量,它代表“前导0”,接着检查哈希值是否具有满足条件的“前导0”,然后返回 True 或 False 。

我们修改之前生成块的generateBlock 函数:

public static Block generateBlock(Block oldBlock, int vac) {
Block newBlock = new Block(); newBlock.setIndex(oldBlock.getIndex() + 1);
newBlock.setTimestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
newBlock.setVac(vac);
newBlock.setPrevHash(oldBlock.getHash());
newBlock.setDifficulty(difficulty); /*
* 这里的 for 循环很重要: 获得 i 的十六进制表示 ,将 Nonce 设置为这个值,并传入 calculateHash 计算哈希值。
* 之后通过上面的 isHashValid 函数判断是否满足难度要求,如果不满足就重复尝试。 这个计算过程会一直持续,直到求得了满足要求的
* Nonce 值,之后通过 handleWriteBlock 函数将新块加入到链上。
*/
for (int i = 0;; i++) {
String hex = String.format("%x", i);
newBlock.setNonce(hex);
if (!isHashValid(calculateHash(newBlock), newBlock.getDifficulty())) {
System.out.printf("%s do more work!\n", calculateHash(newBlock));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
LOGGER.error("error:", e);
}
continue;
} else {
System.out.printf("%s work done!\n", calculateHash(newBlock));
newBlock.setHash(calculateHash(newBlock));
break;
}
}
return newBlock;
}

篇幅有限,我已经将完整代码发布在 Github上,可以从这里获得。

跑起来看看

启动程序,在浏览器中访问 http://localhost:4567

接着通过 RestClient 来发送一个包含vac数据的POST 请求。我们观察命令行窗口,不断得计算哈希值,如果不满足难度要求就继续重试,直到找到满足要求的哈希值及 Nonce

可以看到最后一个哈希值满足我们设定的难度要求(1位“前导0”)。我们再来刷新下浏览器:

可以看到第二个块创建成功并加到链上了,其中Nonce 就是通过Proof-of-Work计算出来满足难度要求的值。

下一步

到这里要先祝贺你,上面的内容很有价值。尽管我们的示例中使用了非常低的难度,但本质上,工作证明算法就是比特币、以太坊等区块链的重要组成根本。

对于下一步应该深入区块链的哪个方向,我们推荐可以学习如何通过 IPFS存取大文件并与区块链打通。[IPFS]

此外相比 Proof-of-Work,Proof-of-Stake 算法正越来越受到关注和青睐,你也可以学习如何将本文的 PoW 算法改为实现 PoS 算法。

只用120行Java代码写一个自己的区块链-3挖矿算法的更多相关文章

  1. 只用120行Java代码写一个自己的区块链

    区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Java 语言来实现一个简单的区块链,用不到 120 行代码来揭示区 ...

  2. 只用120行Java代码写一个自己的区块链-2网络

    已经看完第一章的内容了吗,欢迎回来. 上一章我们介绍了关于怎么去编写自己的区块链,完成哈希和新块的校验.但是它只是在一个终端(结点)上跑.我们怎么样来连接其他结点以及贡献新的块呢,怎么样广播到其他结点 ...

  3. 只用120行Java代码写一个自己的区块链-4实现真正的p2p网络

    在之前的文章中,我们模拟了节点网络通讯,很多朋友反馈说,他们想看真正的节点网络通讯而不是单节点的模拟.本章将满足你们.

  4. 只用200行Go代码写一个自己的区块链!

    Coral Health · 大约23小时之前 · 220 次点击 · 预计阅读时间 7 分钟 · 不到1分钟之前 开始浏览 区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大 ...

  5. 只用200行Go代码写一个自己的区块链!(转)

    区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Go 语言来实现一个简单的区块链,用不到 200 行代码来揭示区块链 ...

  6. 用java代码写一个简单的网上购物车程序

    需求:1.写一个商品类,有商品编号.商品名称.商品分类.商品单价属性.2.写一个商品条目信息类,有商品和数量两个属性,有商品总价格方法. 3.写一个购物车类,有添加商品方法.查看订单信息,删除商品,修 ...

  7. 如何用java代码写一个堆栈

    public class Stack { int[] data; int maxSize; int top; public Stack(int maxSize) { this.maxSize = ma ...

  8. 用 Python 构建一个极小的区块链

    虽然有些人认为区块链是一个早晚会出现问题的解决方案,但是毫无疑问,这个创新技术是一个计算机技术上的奇迹.那么,究竟什么是区块链呢? 区块链 以比特币(Bitcoin)或其它加密货币按时间顺序公开地记录 ...

  9. 如何用70行Java代码实现深度神经网络算法

    http://www.tuicool.com/articles/MfYjQfV 如何用70行Java代码实现深度神经网络算法 时间 2016-02-18 10:46:17  ITeye 原文  htt ...

随机推荐

  1. 详细解析@Resource和@Autowired的区别 , 以及@Qualifier的作用

    (41)  (0) 首先 . @Resource是javax.annotation 包中的注解类 , 是jdk中封装的 . @AutoWired是spring的中注解,依赖于spring上下文. 相同 ...

  2. Antlr 在 idea 中正确使用的方式

    问题 Caused by: java.io.InvalidClassException: org.antlr.v4.runtime.atn.ATN; Could not deserialize ATN ...

  3. 2018-8-10考试 T3. 朝暮(akekure)

    题目大意:有$n$个点和$m$条边的图($n - 1 \leq m \leq n + 5$),每个点要么黑要么白,两个黑点不可以相邻,问方案数 题解:可以发现当图为一棵树的时候只需要一个树形$DP$ ...

  4. 洛谷 P2611 [ZJOI2012]小蓝的好友 解题报告

    P2611 [ZJOI2012]小蓝的好友 题目描述 终于到达了这次选拔赛的最后一题,想必你已经厌倦了小蓝和小白的故事,为了回馈各位比赛选手,此题的主角是贯穿这次比赛的关键人物--小蓝的好友. 在帮小 ...

  5. 【bzoj2756 奇怪的游戏】

    Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 4403  Solved: 1226[Submit][Status][Discuss] Descript ...

  6. oralce的客户端sqlplus

    安装完oracle后,默认的客户端是sqlplus,还有一个公司常用的是PLSQLdeveloper 客户端软件,另外Navicat primie这个可以连接mysql.sqlserver.oracl ...

  7. es6+最佳入门实践(6)

    6.Symbol用法 6.1.什么是Symbol? Symbol是es6中一种新增加的数据类型,它表示独一无二的值.es5中我们把数据类型分为基本数据类型(字符串.数字.布尔.undefined.nu ...

  8. java封装的使用

    一:前言 其实以前我们来学习java特性的时候,对于封装好想觉得没什么用处,至少我那个时候的感觉(不知道是不是我学的太浅薄了~),现在由于项目从零开始做得,在做得过程中我感觉到原来封装是这样用的. 二 ...

  9. Java输入输出流备忘

    重要博客: http://blog.csdn.net/hguisu/article/details/7418161 File dir = new File("\\root");   ...

  10. C++中的各种“神奇”东西

    将光标放到任意的位置 void gotoxy(int x,int y)//位置函数 { COORD pos; pos.X=x; pos.Y=y; SetConsoleCursorPosition(Ge ...