区块链是由一个个所有人能够访问的区块构成的公共数据库。这好像没有什么特别的,不过它们有个有意思的特性:它们是无法变的。要是1个区块被插入到区块链中,除非让剩下的另外区块失效,不然这是不会再被更改的。* m* z0 s. {" _. j6 d, g# Y: @
区块链是由千千万万的区块连接在一起的。链上的区块根据某一方法容许人们检验到是不是有人控制了以前的一切区块。) l2 k* O6 X7 i! } F& e
那么人们怎样保证数据的完整性呢?每一区块都含有1个基于其内容计算出来的hash。同时也涵盖了前1个区块的hash。/ k4 t, E& o1 ?1 S
下面是一个区块类用JavaScript写出去大概的模样:
- const SHA256 = require(“crypto-js/sha256”);: @; }; L: Q. I% `3 G
- class Block {/ v- t7 e. e. k; [
- constructor(index, timestamp, data, previousHash = ‘’) {
- this.index = index;; y" U7 f; B" d' S! ?( T7 _
- this.previousHash = previousHash;
- this.timestamp = timestamp;8 o( D" j% z2 J2 i6 d5 i
- this.data = data;
- this.hash = this.calculateHash();: {* f- t! i9 d! b
- } S+ m0 e, Y8 Y2 E: j6 g6 y
- calculateHash() {7 u# @5 _4 t& F. u) p3 [
- return SHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data)).toString();) m; s2 e. J7 Y r# i' V8 v3 A8 k
- }+ i# I$ C, s8 v# P1 X; G, {
- }
因为JavaScript中并不支持sha256所以我引入了crypto-js库。然后我定义了一个构造函数来初始化我区块的属性。每一个区块上都被赋予了index属性来告知我们这个区块在整个链上的位置。我们同时也生成了一个时间戳,以及需要在区块里存储的一些数据。最后是前一个区块的hash。. p; S6 y9 |% P8 H( C, Q" n2 `
创造一个链3 ?$ `/ S) V& w" t; | e
现在我们可以在Blockchain类中将区块链接起来了!下面是用JavaScript实现的代码:
- class Blockchain{
- constructor() {
- this.chain = [this.createGenesisBlock()];
- }
- createGenesisBlock() {
- return new Block(0, “01/01/2017”, “Genesis block”, “0”);
- }
- getLatestBlock() {
- return this.chain[this.chain.length - 1];1 t/ j6 ]4 ~4 j$ G y5 y
- }
- addBlock(newBlock) { T. L; ? F. l2 m" |4 \) y: V
- newBlock.previousHash = this.getLatestBlock().hash;
- newBlock.hash = newBlock.calculateHash();) w+ s0 G/ A" @- N3 i& a/ y
- this.chain.push(newBlock);
- }
- isChainValid() {: Z4 L2 U! w& ]4 D
- for (let i = 1; i 《 this.chain.length; i++){* [ M3 G* d' B0 y" B$ M
- const currentBlock = this.chain[i];
- const previousBlock = this.chain[i - 1];! i% C5 U: M" b S6 l& m
- if (currentBlock.hash !== currentBlock.calculateHash()) {
- return false;4 Q( O; ]& |5 l1 M; A- Y1 W/ R5 Y
- }
- if (currentBlock.previousHash !== previousBlock.hash) {
- return false;) A% t! I; w" \4 {' d8 x
- }
- }) q, q# Y! o: `+ U1 _1 w6 ^
- return true;' o ]. T! |. i- s# ~- u
- }
- }
在构造函数里,我通过创建一个包含创世块的数组来初始化整个链。第一个区块是特殊的,因为它不能指向前一个区块。我还添加了下面两个方法:
· getLatestBlock()返回我们区块链上最新的区块。
· addBlock()负责将新的区块添加到我们的链上。为此,我们
将前一个区块的hash添加到我们新的区块中。这样我们就可以保持整个链的完整性。因为只要我们变更了最新区块的内容,我们就需要重新计算它的hash。当计算完成后,我将把这个区块推进链里(一个数组)。& D5 D' R F a2 n) \1 W( s
最后,我创建一个isChainValid()来确保没有人篡改过区块链。它会遍历所有的区块来检查每个区块的hash是否正确。它会通过比较previousHash来检查每个区块是否指向正确的上一个区块。如果一切都没有问题它会返回true否则会返回false。
使用区块链
我们的区块链类已经写完啦,可以真正的开始使用它了!
8 L! q1 ]! P3 h1 u' w4 F% _
let savjeeCoin = new Blockchain();
savjeeCoin.addBlock(new Block(1, “20/07/2017”, { amount: 4 }));: @9 r8 v1 _* Z& v5 e1 s
savjeeCoin.addBlock(new Block(2, “20/07/2017”, { amount: 8 }));
在这里我仅仅是创建了一个区块链的实例,并且命名它为SavjeeCoin!之后我在链上添加了一些区块。区块里可以包含任何你想要放的数据,不过在上面的代码里,我选择添加了一个带有amount属性的对象。* v) F% E9 D, h; O* m; z7 w
试着操作吧!
在介绍里我曾说过区块链是不可变的。一旦添加,区块就不可能再变更了。让我们试一下! o: b" N9 q" H7 B* S% m
// 检查是否有效(将会返回true)
console.log(‘Blockchain valid? ’ + savjeeCoin.isChainValid());
// 现在尝试操作变更数据) _5 a* w- c2 b* d2 S
savjeeCoin.chain[1].data = { amount: 100 };" R" J: N m4 z2 d6 t, N
// 再次检查是否有效 (将会返回false)
console.log(“Blockchain valid? ” + savjeeCoin.isChainValid());
我会在一开始通过运行isChainValid()来验证整个链的完整性。我们操作过任何区块,所以它会返回true。
之后我将链上的第一个(索引为1)区块的数据进行了变更。之后我再次检查整个链的完整性,发现它返回了false。我们的整个链不再有效了。
其实区块链的原理并不复杂,这个小例子证明了区块链的工作原理。