比特币很热,这句话说起来有些轻描淡写。虽然加密货币在未来还有一些不确定,但区块链,这个推动比特币的技术,会越来越受欢迎。
区块链几乎可以适合在各个行业应用,它可能也会破坏企业自动化。
本篇文章将重点介绍区块链架构,特别是展示了“不可篡改,仅能追加”的分布式帐本如何和代码实现联系起来。
作为开发者,与简单阅读技术文章相比,在代码中查看原理,能更好的理解其工作原理。不管你怎么看,至少对我来说是这样的。那么,我们现在就在开始吧!
一个简单的区块链
首先让我们快速重温一下区块链。
区块链中的块包含一些头信息和任何类型的一组数据或一组事务。这个区块链从第一个块(称为创世纪)开始。随着交易的添加/追加,根据块内可存储多少交易来创建新块。
当超过块阈值大小时,将创建一个新的事务块。新块与前一个块相关联,因此被称为区块链。
不变性
区块链是不可修改的,它用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)被散列并连接到其他区块。
所有节点都可以验证新块,现有块可以很容易证明是有效的。所以一个矿工试图添加一个虚假的区块或试图调整旧交易的节点是不可能的。
单元测试
你还可以看到所有这些概念与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)。 这些例子在白皮书中有更详细的介绍。
下一步建议你的学习步骤是学习更多基于生产环境的开源区块链框架。 一个很好的例子是HyperLedger Fabric(https://www.hyperledger.org/projects/fabric 。这将是我们下一篇文章的主题,敬请关注!