[译] 用 Java 代码实现区块链

用 Java 代码实现区块链

让咱们来看看用 Java 代码实现区块链的可能性。咱们从基本原理出发,开发一些代码来演示它们是如何融合在一块儿的。html

比特币(Bitcoin)煊赫一时 —— 多么的轻描淡写。虽然数字加密货币的前景尚不明确,但区块链 —— 用于驱动比特币的技术 —— 却很是流行。前端

区块链的应用领域还没有探索完毕。它也有可能会破坏企业自动化。关于区块链的工做原理,有不少可用的信息。咱们有一个深度区块链的免费白皮书(无需注册)。java

本文将重点介绍区块链体系结构,特别是经过简单的代码示例演示“不可变,仅附加”的分布式帐本是如何工做的。android

做为开发者,阅读代码会比阅读技术文章更容易理解。至少对我来讲是这样。那么咱们开始吧!ios

简述区块链

首先咱们简要总结下区块链。区块包含一些头信息和任意一组数据类型或一组交易。该链从第一个(初始)区块开始。随着交易被添加/扩展,将基于区块中能够存储多少交易来建立新区块。git

当超过区块阀值大小时,将建立一个新的交易区块。新区块与前一个区块链接,所以称为区块链。程序员

不可变性

由于交易时会计算 SHA-256 哈希值,因此区块链是不可变的。区块链的内容也被哈希则提供了惟一的标识符。此外,相连的前一个区块的哈希也会被在区块的头信息中散列并储存。github

这就是为何试图篡改区块基本上是不可能的,至少以目前的计算能力是这样的。下面是一个展现区块属性的 Java 类的部分定义。算法

...
public class Block<T extends Tx>; {
public long timeStamp;
private int index;
private List<T> transactions = new ArrayList<T>();
private String hash;
private String previousHash;
private String merkleRoot;
private String nonce = "0000";

// 缓存事务用 SHA256 哈希
    public Map<String,T> map = new HashMap<String,T>();
...
复制代码

注意,注入的泛型类型为 Tx 类型。这容许交易数据发生变化。此外,previousHash 属性将引用前一个区块的哈希值。稍后将描述 merkleRootnonce 属性。后端

区块哈希值

每一个区块能够计算一个哈希。这其实是连接在一块儿的全部区块属性的哈希,包括前一个区块的哈希和由此计算而得的 SHA-256 哈希。

下面是在 Block.java 类中定义的计算哈希值的方法。

...
public void computeHash() {
     Gson parser = new Gson(); // 可能应该缓存这个实例
     String serializedData = parser.toJson(transactions);  
     setHash(SHA256.generateHash(timeStamp + index + merkleRoot + serializedData + nonce + previousHash));
     }
...
复制代码

交易被序列化为 JSON 字符串,所以能够在哈希以前将其追加到块属性中。

区块链经过接受交易来管理区块。当到达预约阀值时,就建立一个区块。下面是 SimpleBlockChain.java 的部分实现:

...
...
public class SimpleBlockchain<T extends Tx> {
public static final int BLOCK_SIZE = 10;
public List<Block<T>> chain = new ArrayList<Block<T>>();

public SimpleBlockchain() {
// 建立初始区块
chain.add(newBlock());
}

...
复制代码

注意,chain 属性维护了一个类型为 Tx 的区块列表。此外,无参构造器 会在建立初始链表时初始化“初始”区块。下面是 newBlock() 方法源码。

...
public Block<T> newBlock() {
int count = chain.size();
String previousHash = "root";

if (count > 0)
previousHash = blockChainHash();

Block<T> block = new Block<T>();

block.setTimeStamp(System.currentTimeMillis());
block.setIndex(count);
block.setPreviousHash(previousHash);
return block;
}
...
复制代码

这个方法将会建立一个新的区块实例,产生合适的值,并分配前一个块的哈希(这将是链头的哈希),而后返回这个实例。

在将区块添加到链中以前,能够经过将新区块的上一个哈希与链的最后一个区块(头)进行比较来验证区块,以确保它们匹配。SimpleBlockchain.java 描述了这一过程。

....
public void addAndValidateBlock(Block<T> block) {

// 比较以前的区块哈希,若是有效则添加
Block<T> current = block;
for (int i = chain.size() - 1; i >= 0; i--) {
Block<T> b = chain.get(i);
if (b.getHash().equals(current.getPreviousHash())) {
current = b;
} else {

throw new RuntimeException("Block Invalid");
}

}

this.chain.add(block);
}
...
复制代码

整个区块链经过循环整个链来验证,确保区块的哈希仍然与前一个区块的哈希匹配。

如下是 SimpleBlockChain.java validate() 方法的实现。

...
public boolean validate() {

String previousHash = null;
for (Block<T> block : chain) {
String currentHash = block.getHash();
if (!currentHash.equals(previousHash)) {
return false;
}

previousHash = currentHash;

}

return true;

}
...
复制代码

你能够看到,试图以任何方式伪造交易数据或任何其余属性都是很是困难的。并且,随着链的增加,它会继续变得很是、很是、很是困难,基本上是不可能的 —— 除非量子计算机可用!

添加交易

区块链技术的另外一个重要技术点是它是分布式的。区块链只增的特性很好地帮助了它在区块链网络的节点之间的复制。节点一般以点对点的方式进行通讯,就像比特币那样,但不必定非得是这种方式。其余区块链实现使用分散的方法,好比使用基于 HTTP 协议的 API。这都是题外话了。

交易能够表明任何东西。交易能够包含要执行的代码(例如,智能合约)或存储和追加有关某种业务交易的信息。

智能合约:旨在以数字形式来促进、验证或强制执行合约谈判及履行的计算机协议。

就比特币而言,交易包含全部者帐户中的金额和其余帐户的金额(例如,在帐户之间转移比特币金额)。交易中还包括公钥和帐户 ID,所以传输须要保证安全。但这是比特币特有的。

交易被添加到网络中并被池化;它们不在区块中或链自己中。

这是区块链共识机制发挥做用的地方。如今有许多通过验证的共识算法和模式,不过那已经超出了本文的范围。

挖矿是比特币区块链使用的共识机制。这就是下文讨论的共识类型。共识机制收集交易,用它们构建一个区块,而后将该区块添加到链中。区块链会在新的交易区块被添加以前验证它。

默克尔树

交易被哈希并添加到区块中。默克尔树被用来计算默克尔根哈希。默克尔树是一种内部节点的值是两个子节点值的哈希值的平衡二叉树。而默克尔根,就是默克尔树的根节点。

该树用于区块交易的验证。若是在交易中更改了一些信息,默克尔根将失效。此外,在分布式中,它们还能够加速传输区块,由于该结构只容许添加和验证整个交易区块所需的单个交易哈希分支。

如下是 Block.java 类中的方法,它从交易列表中建立了一个默克尔树。

...
public List<String> merkleTree() {
ArrayList<String> tree = new ArrayList<>();
// 首先,
// 将全部交易的哈希做为叶子节点添加到树中。
for (T t : transactions) {
tree.add(t.hash());
}
int levelOffset = 0; // 当前处理的列表中的偏移量。
//  当前层级的第一个节点在整个列表中的偏移量。
// 每处理完一层递增,
// 当咱们到达根节点时(levelSize == 1)中止。
for (int levelSize = transactions.size(); levelSize > 1; levelSize = (levelSize + 1) / 2) {
// 对于该层上的每一对节点:
for (int left = 0; left < levelSize; left += 2) {
// 在咱们没有足够交易的状况下,
// 右节点和左节点
// 能够同样。
int right = Math.min(left + 1, levelSize - 1);
String tleft = tree.get(levelOffset + left);
String tright = tree.get(levelOffset + right);
tree.add(SHA256.generateHash(tleft + tright));
}
// 移动至下一层
levelOffset += levelSize;
}
return tree;
}

...
复制代码

此方法用于计算区块的默克尔树根。伴随项目有一个默克尔树单元测试,它试图将交易添加到一个区块中,并验证默克尔根是否已经更改。下面是单元测试的源码。

...
@Test
public void merkleTreeTest() {

// 建立链,添加交易

SimpleBlockchain<Transaction> chain1 = new SimpleBlockchain<Transaction>();

chain1.add(new Transaction("A")).add(new Transaction("B")).add(new Transaction("C")).add(new Transaction("D"));

// 获取链中的区块
Block<Transaction> block = chain1.getHead();

System.out.println("Merkle Hash tree :" + block.merkleTree());

//从区块中获取交易
Transaction tx = block.getTransactions().get(0);

// 查看区块交易是否有效,它们应该是有效的
block.transasctionsValid();
assertTrue(block.transasctionsValid());

// 更改交易数据
tx.setValue("Z");

//当区块的默克尔根与计算出来的默克尔树不匹配时,区块不该该是有效。
assertFalse(block.transasctionsValid());

}

...
复制代码

此单元测试模拟验证交易,而后经过共识机制以外的方法改变区块中的交易,例如,若是有人试图更改交易数据。

记住,区块链是只增的,当块区链数据结构在节点之间共享时,区块数据结构(包括默克尔根)被哈希并链接到其余区块。全部节点均可以验证新的区块,而且现有的区块能够很容易地被证实是有效的。所以,若是一个挖矿者想要添加一个伪造的区块或者节点来调整原有的交易是不可能的。

挖矿和工做量证实

在比特币世界中,将交易组合成区块,而后提交给链中的成员进行验证的过程叫作“挖矿”。

更宽泛地说,在区块链中,这被称为共识。如今有好几种通过验证的分布式共识算法,使用哪一种机制取决于你有一个公共的仍是私有的区块链。咱们的白皮书对此进行了更为深刻的描述,但本文的重点是区块链的原理,所以这个例子中咱们将使用一个工做量证实(POW)的共识机制。

所以,挖掘节点将侦听由区块链执行的交易,并执行一个简单的数学任务。这个任务是用一个不断改变的一次性随机数(nonce)来生成带有一连串以 0 开头的区块哈希值,直到一个预设的哈希值被找到。

Java 示例项目有一个 Miner.java 类,其中的 proofOfWork(Block block) 方法实现以下所示。

private String proofOfWork(Block block) {

String nonceKey = block.getNonce();
long nonce = 0;
boolean nonceFound = false;
String nonceHash = "";

Gson parser = new Gson();
String serializedData = parser.toJson(transactionPool);
String message = block.getTimeStamp() + block.getIndex() + block.getMerkleRoot() + serializedData
+ block.getPreviousHash();

while (!nonceFound) {

nonceHash = SHA256.generateHash(message + nonce);
nonceFound = nonceHash.substring(0, nonceKey.length()).equals(nonceKey);
nonce++;

}

return nonceHash;

}
复制代码

一样,这是简化的,可是一旦收到必定量的交易,这个挖矿算法会为区块计算一个工做量证实的哈希。该算法简单地循环并建立块的SHA-256散列,直到产生前导数字哈希。

这可能须要不少时间,这就是为何特定的GPU微处理器已经被实现来尽量快地执行和解决这个问题的缘由。

单元测试

你能够在 GitHub上看到结合了这些概念的 Java 示例的 JUnit 测试。

运行一下,看看这个简单的区块链是如何工做的。

另外,若是你是 C# 程序员的话,其实(我不会告诉任何人),咱们也有用 C# 写的示例。下面是 C# 区块链实现的示例

最后的思考

但愿这篇文章能让你对区块链技术有必定的了解,并有充足的兴趣继续研究下去。

本文介绍的全部示例都用于咱们的深度区块链白皮书 (无需注册便可阅读). 这些例子在白皮书中有更详细的说明。

另外,若是你想在 Java 中看到完整的区块链实现,这里有一个开源项目 BitcoinJ 的连接。你能够看到上文的概念在实际生产中一一实现。

若是是这样的话,推荐你看看更贴近生产的开源区块链框架。一个很好的示例是 HyperLedger Fabric,这将是我下一篇文章的主题 —— 请持续关注!


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索