区块链学习笔记——概念与简单JS实现

在哔哩哔哩上看 是落拓呀 的区块链简明教程,趁热打铁,把自己学习过程记录下来 本项目的 Github 地址 ↓ [github repo=“taurusxin/taurusxincoin” /]

区块

区块即是每一次的交易,比如A转账给B十元,那么这条交易就写在这个区块内 区块里面实际包含的是

  • 交易数据
  • 本次交易的 hash
  • 前一次交易的 hash

其中本一次交易的 hash 为 “本次交易的数据 + 上一次交易的hash” 的 hash 值 用简明的伪代码表示就是

1
2
3
4
5
Block = {
  data = '',
  hash = sha256(data + previousHash),
  previousHash = ''
}

链就是一系列区块的总和,与之相联通的就是 hash 值,每一次的交易都有它的 hash,与链表的作用机制相似 可以理解为一个链表,简明代码表示就是一个数组

1
2
3
Chain = {
  blocks[Block]
}

JS实现

这里用 node.js 来实现一个简单的区块链

初始化项目,安装 crypto-js 依赖

1
2
npm init
npm install crypto-js -S

构建 Block 和 Chain 对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Block {
  constructor(data, previousHash) {
    this.data = data
    this.previousHash = previousHash
    this.hash = this.computeHash()
  }

  computeHash() {
    return sha256(this.data + this.previousHash).toString()
  }
}

构造函数传入了这个对象的 data previousHash 数据,然后定义了对象成员 hash ,用于计算当前区块的 hash

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Chain {
  constructor() {
    this.chain = [this.bigBang()]
  }

  bigBang() {
    const genisisBlcok = new Block('我是祖先', '')
    return genisisBlcok
  }

  getLatestBlock() {
    return this.chain[this.chain.length - 1]
  }

  addBlockToChain(newBlock) {
    // find the nearest block's hash
    // which is the new block's previous hash
    newBlock.previousHash = this.getLatestBlock().hash
    newBlock.hash = newBlock.computeHash()
    this.chain.push(newBlock)
  }

  // check the previous hash is equal to previous block's hash
  validateChain() {
    if (this.chain.length === 1) {
      if (this.chain[0].hash !== this.chain[0].computeHash()) {
        return false
      }
      return true
    }
    // check if current data has been change
    for (let i = 1; i < this.chain.length; i++) {
      const blockToValidate = this.chain[i]
      if (blockToValidate.hash !== blockToValidate.computeHash()) {
        console.log('data has been change')
        return false
      }

      const previousBlcok = this.chain[i - 1]
      if (blockToValidate.previousHash !== previousBlcok.hash) {
        console.log('previous and current block lost connection')
        return false
      }
    }
    return true
  }
}

最初始的 chain 只包含一个祖先区块,它没有 previousHash,并用 bigBang 方法进行初始化,addBlockToChain 方法实现了添加一个区块,并自动计算其 hash validateChain() 方法,从三种情况去校验这个链是否合法。

  1. 只有一个祖先区块时,校验祖先区块数据是否被篡改
  2. 从第二个区块开始遍历是否有数据被篡改
  3. 从第二个区块开始遍历是否有hash值被修改

检验代码是否有效

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const taurusxinChain = new Chain()

const block1 = new Block('转账十元', '')
taurusxinChain.addBlockToChain(block1)
const block2 = new Block('转账十个十元', '')
taurusxinChain.addBlockToChain(block2)

console.log(taurusxinChain)
console.log(taurusxinChain.validateChain())

// try to change the block data
taurusxinChain.chain[1].data = '转账一百个十元'
console.log(taurusxinChain.validateChain())

// try to change the block hash
taurusxinChain.chain[1].hash = taurusxinChain.chain[1].computeHash()
console.log(taurusxinChain.validateChain())

输出结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Chain {
  chain: [
    Block {
      data: '我是祖先',
      previousHash: '',
      hash: 'dbf4cebe91cf0212be8ee04289855fc17e0085d45a7b4c69eeb79e7c636b48fc'
    },
    Block {
      data: '转账十元',
      previousHash: 'dbf4cebe91cf0212be8ee04289855fc17e0085d45a7b4c69eeb79e7c636b48fc',
      hash: 'f135810d2aa85703b2f7356877b95de3dad00955830d570568dd42dd52b93500'
    },
    Block {
      data: '转账十个十元',
      previousHash: 'f135810d2aa85703b2f7356877b95de3dad00955830d570568dd42dd52b93500',
      hash: 'ec3caa66201967755657f30156f38207de23c897caa9aa3ca972f413b9a6a749'
    }
  ]
}
true
data has been change
false
previous and current block lost connection
false

第一次生成区块链,校验成功,输出true 尝试更改第二个区块的数据,输出 data has been change 尝试更改第二个区块的hash值,输出 previous and current block lost connection

下一阶段

  • 工作量证明
  • 生成钱包地址
  • 加密
Licensed under CC BY-NC-SA 4.0