只用120行Java代码写一个自己的区块链-3挖矿算法
在本系列前两篇文章中,我们向大家展示了如何通过精炼的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挖矿算法的更多相关文章
- 只用120行Java代码写一个自己的区块链
区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Java 语言来实现一个简单的区块链,用不到 120 行代码来揭示区 ...
- 只用120行Java代码写一个自己的区块链-2网络
已经看完第一章的内容了吗,欢迎回来. 上一章我们介绍了关于怎么去编写自己的区块链,完成哈希和新块的校验.但是它只是在一个终端(结点)上跑.我们怎么样来连接其他结点以及贡献新的块呢,怎么样广播到其他结点 ...
- 只用120行Java代码写一个自己的区块链-4实现真正的p2p网络
在之前的文章中,我们模拟了节点网络通讯,很多朋友反馈说,他们想看真正的节点网络通讯而不是单节点的模拟.本章将满足你们.
- 只用200行Go代码写一个自己的区块链!
Coral Health · 大约23小时之前 · 220 次点击 · 预计阅读时间 7 分钟 · 不到1分钟之前 开始浏览 区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大 ...
- 只用200行Go代码写一个自己的区块链!(转)
区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Go 语言来实现一个简单的区块链,用不到 200 行代码来揭示区块链 ...
- 用java代码写一个简单的网上购物车程序
需求:1.写一个商品类,有商品编号.商品名称.商品分类.商品单价属性.2.写一个商品条目信息类,有商品和数量两个属性,有商品总价格方法. 3.写一个购物车类,有添加商品方法.查看订单信息,删除商品,修 ...
- 如何用java代码写一个堆栈
public class Stack { int[] data; int maxSize; int top; public Stack(int maxSize) { this.maxSize = ma ...
- 用 Python 构建一个极小的区块链
虽然有些人认为区块链是一个早晚会出现问题的解决方案,但是毫无疑问,这个创新技术是一个计算机技术上的奇迹.那么,究竟什么是区块链呢? 区块链 以比特币(Bitcoin)或其它加密货币按时间顺序公开地记录 ...
- 如何用70行Java代码实现深度神经网络算法
http://www.tuicool.com/articles/MfYjQfV 如何用70行Java代码实现深度神经网络算法 时间 2016-02-18 10:46:17 ITeye 原文 htt ...
随机推荐
- Qt5.6关联VS2013,配置VAssistX
1. 安装Qt qt-creator-opensource-windows-x86-4.2.0.exe 2. 安装Qt VS插件 qt-vs-addin-1.2.5.exe 3. 配置ASSISTX ...
- Delphi 7学习开发控件(续)
继上次我们学习开发一个简单的画线控件后,基本的制作控件步骤已经清楚了,这次我们继续加深学习控件的制作.我们打开Delphi 7创建一个应用程序,拖动LineTo控件到窗体上,仔细看左边的对象设计器,可 ...
- Java IO 小结
Java IO 的学习需要明白流设计的体系结构,这样才能在实际需要的时候,通过API文档查阅,快速实现功能.
- Hibernate domain对象说明
一个domain对象对应于数据库的一张表(也可以表示出表关系) domain对象必须带一个无参构造函数 建议有一个无意义id,作为主键 建议非final,否则无法使用Hibernate的高级特性(懒加 ...
- BZOJ3522 POI2014HOT-Hotels(树形dp)
分两种情况.三点两两lca相同:在三点的lca处对其统计即可,显然其离lca距离应相同:某点在另两点lca的子树外部:对每个点统计出与其距离x的点有多少个即可. 可以长链剖分做到线性,当然不会. #i ...
- Powershell快速入门
Powershell快速入门 来源: https://blog.csdn.net/u011054333/article/details/72567590 https://blog.csdn.net/u ...
- 解决IE下页面空白或者报错:[vuex] vuex requires a Promise polyfill in this browser
[vuex] vuex requires a Promise polyfill in this browser 上述错误的原因是不支持 Promise 方法,导致页面出现空白无法加载. 解决方法如下: ...
- CSS样式权重的级联cascade的概念
我们知道,firefox在众多浏览器中是对css 2高度兼容的一款浏览器,那是我能够编写一个中型b2b网站的时候(并不能说是我遇到过的难题)在禅意花园中看到的一个案例,说的是某个菜单在css中定义了以 ...
- MySQL使用笔记(二)数据库基本操作
By francis_hao Dec 11,2016 数据库是什么 数据库是什么呢?对于MySQL来说,数据库是存储数据库对象的容器,参考[1]中的简单解释是:数据库是一个拥有特定排放顺序的文件 ...
- 近期对于windows服务的理解
1.APP.config的作用 在开发环境下时,根目录下的APP.config里面会填写一些参数之类的.当生成之后,这些参数将会被自动生成在*.exe文件目录中.如图: 其中,.exe文件为Win ...