微信号:we21cto

介绍:21CTO(21CTO.com)是中国项级技术专家的学习与服务平台.我们为CTO、技术总监、架构师等技术专家提供高质量的资讯、问答、活动等产品,同时与企业连接,提供技术咨询、研发、运维、技术支持、培训及人才招聘等服...

使用Java编写区块链的代码实现

2018-04-13 23:58 David Pitt

21CTO导读:我们来看看使用Java如何开发区块链。我们从最初的规则开始建立,并展示一些代码来展示如何将它们融合在一起。


比特币很热,这句话说起来有些轻描淡写。虽然加密货币在未来还有一些不确定,但区块链,这个推动比特币的技术,会越来越受欢迎。


区块链几乎可以适合在各个行业应用,它可能也会破坏企业自动化。


本篇文章将重点介绍区块链架构,特别是展示了“不可篡改,仅能追加”的分布式帐本如何和代码实现联系起来。


作为开发者,与简单阅读技术文章相比,在代码中查看原理,能更好的理解其工作原理。不管你怎么看,至少对我来说是这样的。那么,我们现在就在开始吧!


一个简单的区块链


首先让我们快速重温一下区块链。


区块链中的块包含一些头信息和任何类型的一组数据或一组事务。这个区块链从第一个块(称为创世纪)开始。随着交易的添加/追加,根据块内可存储多少交易来创建新块。


当超过块阈值大小时,将创建一个新的事务块。新块与前一个块相关联,因此被称为区块链。


不变性


区块链是不可修改的,它用SHA-256哈希来进行事务计算。块的内容也被哈希加密,并提供唯一的标识符。并且,前一个区块的链接哈希也存储在块头中。


这也就是为什么想篡改区块链基本上是不可能的,至少目前的计算能力都不能反解密。


下面是部分的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";

// caches Transaction SHA256 hashes

    public Map<String,T> map = new HashMap<String,T>();

...


说明:注入的泛类型是Tx类型,允许交易数据的不同类型。此外,previousHash属性将引用前一个区块的哈希,包括merkleRoot和nonce属性的描述。


哈希块(散列)


每个区块都可以计算哈希。是连接在一起的所有块属性的哈希,包括前一个块的哈希和从中计算的SHA-256哈希。



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


...

public void computeHash() {

     Gson parser = new Gson(); // probably should cache this instance

     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() {

// create genesis block

chain.add(newBlock());

}

...


注意,链属性包含一个用Tx类型的块列表。此外,no arg的链构函数将创建一个初始“创世”区块。


以下是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;

}

...


newBlock方法将用来创建一个新的块实例,适当的种子值,以及分配前一个块的散列,之后返回整个块。


通过将新块的前一个哈希与链的最后一个块(头部)进行比较以确保它们匹配,在添加新块之前加以验证。以下有一个SimpleBlockchain.java方法来描述。请看如下代码:


....

public void addAndValidateBlock(Block<T> block) {

// compare previous block hash, add if valid

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,因此传输可以安全完成。


交易可被添加到网络并汇总,他们可能并不在一个区块链。


此为区块链共识机制发挥价值的地方,有许多经过验证的共识算法和模式,可以参考其它文章。


挖矿是比特币区块链都使用的共识机制。这是本文要进一步要讨论的共识类型。共识机制收集交易,之后创建一个新区块,验证完新的交易块后,再将该区块添加到链中。


默克尔(Merkle)树


事务被哈希处理后添加到区块中。


我们把数据分成小的数据块,有相应地哈希和它对应。但是往上走,并不是直接去运算根哈希,而是把相邻的两个哈希合并成一个字符串,然后运算这个字符串的哈希,这样每两个哈希就结婚生子,得到了一个”子哈希“。如果最底层的哈希总数是单数,那到最后必然出现一个单身哈希,这种情况就直接对它进行哈希运算,所以也能得到它的子哈希。于是往上推,依然是一样的方式,可以得到数目更少的新一级哈希,最终必然形成一棵倒挂的树,到了树根的这个位置,这一代就剩下一个根哈希了,我们把它叫做 Merkle Root。


在p2p网络下载网络之前,先从可信的源获得文件的Merkle Tree树根。一旦获得了树根,就可以从其他从不可信的源获取Merkle tree。通过可信的树根来验证接受到的Merkle Tree。如果Merkle Tree是损坏的或者虚假的,就从其他源获得另一个Merkle Tree,直到获得一个与可信树根匹配的Merkle Tree。



Merkle树用来验证区块的Merkle树根。以下是单元测试的源代码:



public List<String> merkleTree() {

ArrayList<String> tree = new ArrayList<>();

// Start by adding all the hashes of the transactions as leaves of the

// tree.

for (T t : transactions) {

tree.add(t.hash());

}

int levelOffset = 0; // Offset in the list where the currently processed

// level starts.

// Step through each level, stopping when we reach the root (levelSize

// == 1).

for (int levelSize = transactions.size(); levelSize > 1; levelSize = (levelSize + 1) / 2) {

// For each pair of nodes on that level:

for (intvelOffset + left);

String tright = tree.get(levelOffset + right);

tree.add(SHA256.generateHash(tleft + tright)); left = 0; left < levelSize; left += 2) {

// The right hand node can be the same as the left hand, in the

// case where we don't have enough

// transactions.

int right = Math.min(left + 1, levelSize - 1);

String tleft = tree.get(le

}

// Move to the next level.

levelOffset += levelSize;

}

return tree;

}

...


这个单元测试用来模拟验证事务,接下来在共识机制外的区块中更改事务。


请记住,区块链是仅附加的,并且由于区块链数据结构在节点之间共享,所以区块数据结构(包括Merkle Root)被散列并连接到其他区块。 


所有节点都可以验证新块,现有块可以很容易证明是有效的。所以一个矿工试图添加一个虚假的区块或试图调整旧交易的节点是不可能的。


挖矿工作证明


将交易组合成块,然后将其提交给供应链成员验证的过程,在比特币世界中被称为“挖矿”。


更通俗地说,在区块链中,这被称为共识。有不同类型的经过验证的分布式一致性算法。 使用哪种机制取决于你是否拥有公共的区块链。白皮书更深入地描述了这一点,但本文侧重于区块链机制,所以将应用工作量证明共识机制。


挖掘节点监听区块链正在执行的交易,并执行一个简单的数学题。 这个题使用一个在每次迭代中改变的nonce值产生一个预定的前导零集合的块哈希,直到找到前导零哈希。


本示例项目包含一个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#来写的相同例子,链接:https://github.com/in-the-keyhole/khs-blockchain-csharp-example。


总结 


希望这篇文章能够为您继续研究区块链技术,提供足够的兴趣与洞察力。


本文中介绍的所有示例都在我们深入的区块链白皮书中使用 (https://keyholesoftware.com/wp-content/uploads/Blockchain-For-The-Enterprise-Keyhole-White-Paper.pdf)。 这些例子在白皮书中有更详细的介绍。


另外,如果你想在Java中看到一个完整的区块链实现,这里有一个到开源的BitcoinJ项目的链接(https://github.com/bitcoinj/bitcoinj)。 您将在实际生产环境中看到这些概念的实际应用。


下一步建议你的学习步骤是学习更多基于生产环境的开源区块链框架。 一个很好的例子是HyperLedger Fabric(https://www.hyperledger.org/projects/fabric 。这将是我们下一篇文章的主题,敬请关注!


作者:David Pitt

编译:洛逸

原文:https://dzone.com/articles/blockchain-implementation-with-java-code


 
21CTO 更多文章 编程初学开发者的十大误区 Gartner:2018年前沿技术预测 分布式系统中常见技术解决的问题是什么? 一个老程序员的忠告:不要一辈子靠技术生存 码农岗简历石沉大海?让Twitter HR来给你把把脉
猜您喜欢 使用Websocket超时 Java性能优化指南 ,及唯品会的实战(修订版) HTML XHTML HTML5 Web性能优化最佳实践:JavaScript&CSS篇 FIT2CLOUD正式支持VMWare vSphere和OpenStack,帮助用户统一管理不同类型云资源