使用Java语言从零开始创建区块链

目前网络上关于区块链入门、科普的文章不少,本文就不再赘述区块链的基本概念了,如果对区块链不是很了解的话,可以看一下我之前收集的一些入门学习资源:http://blog.51cto.com/zero01/2066321

对区块链技术感到新奇的我们,都想知道区块链在代码上是怎么实现的,所以本文是实战向的,毕竟理论我们都看了不少,但是对于区块链具体的实现还不是很清楚,本文就使用Java语言来实现一个简单的区块链。

但是要完全搞懂区块链并非易事,对于一门较为陌生的技术,我们需要在理论+实践中学习,通过写代码来学习技术会掌握得更牢固,构建一个区块链可以加深对区块链的理解。

准备工作

掌握基本的JavaSE以及JavaWeb开发,能够使用Java开发简单的项目,并且需要了解HTTP协议。

我们知道区块链是由区块的记录构成的不可变、有序的链结构,记录可以是交易、文件或任何你想要的数据,重要的是它们是通过哈希值(hashes)链接起来的。

如果你还不是很了解哈希是什么,可以查看这篇文章:https://learncryptography.com/hash-functions/what-are-hash-functions

环境描述

JDK1.8

Tomcat 9.0

Maven 3.5

JSON 20160810

javaee-api 7.0

pom.xml文件配置内容:

然后还需要一个HTTP客户端,比如Postman,Linux命令行下的curl或其它客户端,我这里使用的是Postman。

Blockchain类

首先创建一个Blockchain类,在构造器中创建了两个主要的集合,一个用于储存区块链,一个用于储存交易列表,本文中所有核心的主要代码都写在这个类里,方便随时查看,在实际开发则不宜这么做,应该把代码拆分仔细降低耦合度。

以下是Blockchain类的框架代码:

Blockchain类用来管理区块链,它能存储交易,加入新块等,下面我们来进一步完善这些方法。

区块的结构

首先需要说明一下区块的结构,每个区块包含属性:索引(index),时间戳(timestamp),交易列表(transactions),工作量证明(稍后解释)以及前一个区块的Hash值。

以下是一个区块的结构:

到这里,区块链的概念就清楚了,每个新的区块都包含上一个区块的Hash,这是关键的一点,它保障了区块链不可变性。如果***者破坏了前面的某个区块,那么后面所有区块的Hash都会变得不正确。不理解的话,慢慢消化,可以参考区块链记账原理(https://learnblockchain.cn/2017/10/25/whatbc/)。

由于需要计算区块的hash,所以我们得先编写一个用于计算hash值的工具类:

加入交易功能

接下来我们需要实现一个交易\记账功能,所以来完善newTransactions以及lastBlock方法:

newTransactions方法向列表中添加一个交易记录,并返回该记录将被添加到的区块 (下一个待挖掘的区块)的索引,等下在用户提交交易时会有用。

创建新块

当Blockchain实例化后,我们需要构造一个创世区块(没有前区块的第一个区块),并且给它加上一个工作量证明。

每个区块都需要经过工作量证明,俗称挖矿,稍后会继续讲解。

为了构造创世块,我们还需要完善剩下的几个方法,并且把该类设计为单例:

通过上面的代码和注释可以对区块链有直观的了解,接下来我们来编写一些简单的测试代码来测试一下这些代码能否正常工作:

运行结果:

通过以上的测试,可以很直观的看到区块链的数据,但是现在只是完成了初步的代码编写,还有几件事情还没做,接下来我们看看区块是怎么挖出来的。

理解工作量证明

新的区块依赖工作量证明算法(PoW)来构造。PoW的目标是找出一个符合特定条件的数字,这个数字很难计算出来,但容易验证。这就是工作量证明的核心思想。

为了方便理解,举个例子:

假设一个整数 x 乘以另一个整数 y 的积的 Hash 值必须以 0 结尾,即 hash(x * y) = ac23dc…0。设变量 x = 5,求 y 的值?

用Java实现如下:

结果是 y=21 ,因为:

在比特币中,使用称为Hashcash的工作量证明算法,它和上面的问题很类似。矿工们为了争夺创建区块的权利而争相计算结果。通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,会获得比特币奖励。

当然,在网络上非常容易验证这个结果。

实现工作量证明

让我们来实现一个相似PoW算法,规则是:寻找一个数 p,使得它与前一个区块的 proof 拼接成的字符串的 Hash 值以 4 个零开头:

衡量算法复杂度的办法是修改零开头的个数。使用4个来用于演示,你会发现多一个零都会大大增加计算出结果所需的时间。

现在Blockchain类基本已经完成了,接下来使用Servlet接收HTTP请求来进行交互。

Blockchain作为API接口

我们将使用Java Web中的Servlet来接收用户的HTTP请求,通过Servlet我们可以方便的将网络请求的数据映射到相应的方法上进行处理,现在我们来让Blockchain运行在基于Java Web上。

我们将创建三个接口:

/transactions/new 创建一个交易并添加到区块

/mine 告诉服务器去挖掘新的区块

/chain 返回整个区块链

注册节点ID

我们的“Tomcat服务器”将扮演区块链网络中的一个节点,而每个节点都需要有一个唯一的标识符,也就是id。在这里我们使用UUID来作为节点ID,我们需要在服务器启动时,将UUID设置到ServletContext属性中,这样我们的服务器就拥有了唯一标识,这一步我们可以配置监听类来完成,首先配置web.xml文件内容如下:

然后编写一个类实现ServletContextListener接口,在初始化方法中把uuid设置到ServletContext的属性中:

创建Servlet类

我们这里没有使用任何框架,所以我们需要通过最基本的Servlet来接收并处理用户的HTTP请求:

我们先来完成最简单的FullChain的代码,这个Servlet用于向客户端输出整个区块链的数据(JSON格式):

发送交易

然后是记录交易数据的功能,每一个区块都可以记录交易数据,发送到节点的交易数据结构如下:

实现代码如下:

挖矿

挖矿正是神奇所在,它很简单,只做了以下三件事:

计算工作量证明PoW

通过新增一个交易授予矿工(自己)一个币

构造新区块并将其添加到链中

代码实现如下:

注意交易的接收者是我们自己的服务器节点,我们做的大部分工作都只是围绕Blockchain类的方法进行交互。到此,我们的区块链就算完成了,我们来实际运行下。

运行区块链

由于我们这里也没有写前端的web页面,只写了后端的API,所以只能使用 Postman 之类的软件去和API进行交互。首先启动Tomcat服务器,然后通过post请求 http://localhost:8089/BlockChain_Java/transactions/new 来添加新的交易信息(注意我这里没有使用默认的8080端口,默认的情况下是8080端口):

但是这时候还没有新的区块可以写入这个交易信息,所以我们还需要请求 http://localhost:8089/BlockChain_Java/mine 来进行挖矿,挖出一个新的区块来存储这笔交易:

在挖了两次矿之后,就有3个块了,通过请求 http://localhost:8089/BlockChain_Java/chain 可以得到所有的区块块的信息:

一致性(共识)

我们已经有了一个基本的区块链可以接受交易和挖矿。但是区块链系统应该是分布式的。既然是分布式的,那么我们究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性的算法。

注册节点

在实现一致性算法之前,我们需要找到一种方式让一个节点知道它相邻的节点。每个节点都需要保存一份包含网络中其它节点的记录。因此让我们新增几个接口:

/nodes/register 接收URL形式的新节点列表

/nodes/resolve执行一致性算法,解决任何冲突,确保节点拥有正确的链

我们需要修改下BlockChain的构造函数并提供一个注册节点方法:

我们用 HashSet 集合来储存节点,这是一种避免出现重复添加节点的简单方法。

实现共识算法

前面提到,冲突是指不同的节点拥有不同的链,为了解决这个问题,规定最长的、有效的链才是最终的链,换句话说,网络中有效最长链才是实际的链。

我们使用以下算法,来达到网络中的共识:

第一个方法 validChain() 用来检查是否是有效链,遍历每个块验证hash和proof.

第2个方法 resolveConflicts() 用来解决冲突,遍历所有的邻居节点,并用上一个方法检查链的有效性, 如果发现有效更长链,就替换掉自己的链

让我们添加两个Servlet,一个用来注册节点,一个用来解决冲突:

我们可以在不同的机器运行节点,或在一台机机开启不同的网络端口来模拟多节点的网络,这里在同一台机器开启不同的端口演示,配置两个不同端口的服务器即可,我这里启动了两个节点:http://localhost:8089 和 http://localhost:8066。

两个节点互相进行注册:

然后在8066节点上挖两个块,确保是更长的链:

接着在8089节点上访问接口/nodes/resolve ,这时8089节点的链会通过共识算法被8066节点的链取代:

通过共识算法保持一致性后,两个节点的区块链数据就都是一致的了:

到此为止我们就完成了一个区块链的开发,虽然这只是一个最基本的区块链,而且在开发的过程中也没有考虑太多的程序设计方面的问题,而是以最基本、原始的方式进行开发的。但是我们不妨以这个简单的区块链为基础,发挥自己的能力动手去重构、扩展、完善这个区块链程序,直至成为自己的一个小项目。

Java团长

专注于Java干货分享

扫描上方二维码获取更多Java干货

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180825A1BQON00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券