1. 程式人生 > >區塊鏈:通過演示Demo理解區塊鏈執行原理

區塊鏈:通過演示Demo理解區塊鏈執行原理

BlockChain Demo

開啟比特幣Demo演示網頁區塊鏈Demo演示地址,我們可以看到如下頁面。

這裡寫圖片描述

點選開始演示

這裡寫圖片描述

接下來是BlockChain Demo 2.0的新功能介紹

這裡寫圖片描述

關於Demo功能區的介紹

這裡寫圖片描述

顯示每個區塊儲存的資訊

這裡寫圖片描述

介紹區塊鏈中區塊的索引

這裡寫圖片描述

介紹建立區塊時候的時間戳
這裡寫圖片描述

介紹區塊中hash雜湊加密

這裡寫圖片描述

介紹區塊hash雜湊中前導零也就是難度的介紹

這裡寫圖片描述

// cosnt Block = reuqire("./Block.js");

// class Blockchain {
  // constructor() {
    // this.blockchain = [Block.genesis()];
    this.difficulty = 3
; // } // get() { ... } // get latestBlock() { ... } isValidHashDifficulty(hash) { for (var i = 0; i < hash.length; i++) { if (hash[i] !== "0") { break; }; } return i >= this.difficulty; } // }; // module.exports = Blockchain;

介紹Hash雜湊的生成規則

(索引+上一個雜湊+時間戳+資料+隨機數)
=雜湊 (0 +“0”+ 1508270000000 +“歡迎使用Blockchain Demo 2.0!”+ 604)= 000dc75a315c77a1f9c98fb6247d03dd18ac52632d7dc6a9920261d8109b37cf

這裡寫圖片描述

介紹關於上一個區塊中的Hash值
這裡寫圖片描述

關於區塊中的資料,這也是我們關注的

這裡寫圖片描述

關於資料的變化,資料的微小變化都可以給Hash值帶來巨大的變化。Hash值的是否有效,是我們設定的前導零難度決定的,符合前導零難度的規則才算是有效的Hash

這裡寫圖片描述

假如一個區塊鏈中的某一個區塊中Hash值失效,那麼它後面的區塊都會失效
這裡寫圖片描述

生成有效Hash雜湊的唯二變數就是資料和隨機數,當我們需要的資料固定時

,只有挖掘匹配的前導零難度的隨機數才能尋找到新的區塊

這裡寫圖片描述

// const Block = require("./Block.js");
// const crypto = require("crypto");

// class Blockchain {
  // constructor() { ... }
  // get() { ... }
  // get latestBlock() { ... }
  // isValidHashDifficulty(hash) { ... }
  // calculateHashForBlock(block) { ... }
  // calculateHash(...) { ... }

  mine(data) {
    const newBlock = this.generateNextBlock(data);
    try {
      this.addBlock(newBlock);
    } catch (err) {
      throw err;
    };
  }
// };

// module.exports = Blockchain;

匹配查詢有效Hash的數字,從0開始找,直到找到。隨著難度的不斷增加,有效雜湊的數量減少,因此匹配的計算力也會逐漸增大

這裡寫圖片描述

新增新的區塊

這裡寫圖片描述

// const Block = require("./Block.js");
// const crypto = require("crypto");

// class Blockchain {
  // constructor() { ... }
  // get() { ... }
  // get latestBlock() { ... }
  // isValidHashDifficulty(hash) { ... }
  // calculateHashForBlock(block) { ... }
  // calculateHash(...) { ... }
  // mine(data) { ... }
  // generateNextBlock(data) { ... }

  addBlock(newBlock) {
    if (this.isValidNewBlock(newBlock, this.latestBlock)) {
      this.blockchain.push(newBlock);
    } else {
      throw "Error: Invalid block";
    }
  }
// };

// module.exports = Blockchain;

新增區塊需要滿足的要求

  1. 上一個區塊的索引+1等於新的索引
  2. 上一個區塊的hash要等於下一個區塊的前導hash
  3. hash要滿足前導零難度要求
  4. 正確的hash值

這裡寫圖片描述

// const Block = require("./Block.js");
// const crypto = require("crypto");

// class Blockchain {
  // constructor() { ... }
  // get() { ... }
  // get latestBlock() { ... }
  // isValidHashDifficulty(hash) { ... }
  // calculateHashForBlock(block) { ... }
  // calculateHash(...) { ... }
  // mine(data) { ... }
  // generateNextBlock(data) { ... }
  // addBlock(newBlock) { ... }

  isValidNextBlock(nextBlock, previousBlock) {
    const nextBlockHash = this.calculateHashForBlock(nextBlock);

    if (previousBlock.index + 1 !== nextBlock.index) {
      return false;
    } else if (previousBlock.hash !== nextBlock.previousHash) {
      return false;
    } else if (nextBlockHash !== nextBlock.hash) {
      return false;
    } else if (!this.isValidHashDifficulty(nextBlockHash)) {
      return false;
    } else {
      return true;
    }
  }
// };

// module.exports = Blockchain;

全球的計算機網路共同維護,因此要確保區塊鏈的安全性正確性一致性

P2p.js

const wrtc = require('wrtc');
const Exchange = require('peer-exchange');
const p2p = new Exchange("Blockchain Demo 2.0", { wrtc: wrtc });
const net = require("net");

class PeerToPeer {
  constructor(blockchain) {
    this.peers = [];
    this.blockchain = blockchain;
  }

  startServer(port) {
    const server = net
      .createServer(socket =>
        p2p.accept(socket, (err, conn) => {
          if (err) {
            throw err;
          } else {
            this.initConnection.call(this, conn);
          }
        })
      )
      .listen(port);
  }
}

module.exports = PeerToPeer;

同伴訊息:連結對等方新增到網路

// const wrtc = require('wrtc');
// const Exchange = require('peer-exchange');
// const p2p = new Exchange(...);
// const net = require("net");

// class PeerToPeer {
  // constructor(blockchain) { ... }
  // startServer(port) { ... }

  discoverPeers() {
    p2p.getNewPeer((err, conn) => {
      if (err) {
        throw err;
      } else {
        this.initConnection.call(this, conn);
      }
    });
  }
// }

// module.exports = PeerToPeer;

同伴訊息:Demo的三種同伴訊息的狀態

  • 藍色:當前有效的
  • 綠色:連線的
  • 紅色:斷開連線的

改變:直接點選頭像
連結:在紅色對等方單擊加號的圖示
檢視:在綠色的對等體單擊簡訊圖示
斷開:點選綠色對等方上面的加號圖示

這裡寫圖片描述

P2p.js

// const wrtc = require('wrtc');
// const Exchange = require('peer-exchange');
// const p2p = new Exchange(...);
// const net = require("net");

// class PeerToPeer {
  // constructor(blockchain) { ... }
  // startServer(port) { ... }
  // discoverPeers() { ... }

  connectToPeer(host, port) {
    const socket = net.connect(port, host, () =>
      p2p.connect(socket, (err, conn) => {
        if (err) {
          throw err;
        } else {
          this.initConnection.call(this, conn);
        }
      })
    );
  }

  closeConnection() {
    p2p.close(err => {
      throw err;
    })
  }
// }

// module.exports = PeerToPeer;

同伴訊息:確定誰擁有最新的區塊鏈

Blockchain.js

// const wrtc = require('wrtc');
// const Exchange = require('peer-exchange');
// const p2p = new Exchange(...);
// const net = require("net");
const messageType = {
  REQUEST_LATEST_BLOCK: 0,
  RECEIVE_LATEST_BLOCK: 1,
  REQUEST_BLOCKCHAIN: 2,
  RECEIVE_BLOCKCHAIN: 3,
};
const {
  REQUEST_LATEST_BLOCK,
  RECEIVE_LATEST_BLOCK,
  REQUEST_BLOCKCHAIN,
  RECEIVE_BLOCKCHAIN,
  REQUEST_TRANSACTIONS,
  RECEIVE_TRANSACTIONS
} = messageType;

// class PeerToPeer { ... }
// module.exports = PeerToPeer;

class Messages {
  static getLatestBlock() {
    return {
      type: REQUEST_LATEST_BLOCK
    };
  }

  static sendLatestBlock(block) {
    return {
      type: RECEIVE_LATEST_BLOCK,
      data: block
    };
  }

  static getBlockchain() {
    return {
      type: REQUEST_BLOCKCHAIN
    };
  }

  static sendBlockchain(blockchain) {
    return {
      type: RECEIVE_BLOCKCHAIN,
      data: blockchain
    };
  }
}

邏輯圖

這裡寫圖片描述

P2p.js

// const wrtc = require('wrtc');
// const Exchange = require('peer-exchange');
// const p2p = new Exchange(...);
// const net = require("net");
// const messageType = { ... };
// const { ... } = messageType;

// class PeerToPeer {
  // constructor(blockchain) { ... }
  // startServer(port) { ... }
  // discoverPeers() { ... }
  // connectToPeer(host, port) { ... }
  // closeConnection() { ... }

  broadcastLatest() {
    this.broadcast(Messages.sendLatestBlock(this.blockchain.latestBlock));
  }

  broadcast(message) {
    this.peers.forEach(peer => this.write(peer, message));
  }

  write(peer, message) {
    peer.write(JSON.stringify(message));
  }

  initConnection(connection) {
    this.peers.push(connection);
    this.initMessageHandler(connection);
    this.initErrorHandler(connection);
    this.write(connection, Messages.getLatestBlock());
  }

  initMessageHandler(connection) {
    connection.on("data", data => {
      const message = JSON.parse(data.toString("utf8"));
      this.handleMessage(connection, message);
    });
  }

  initErrorHandler(connection) {
    connection.on("error", err => {
      throw err;
    });
  }

  handleMessage(peer, message) {
    switch (message.type) {
      case REQUEST_LATEST_BLOCK:
        this.write(peer, Messages.sendLatestBlock(this.blockchain.latestBlock));
        break;
      case REQUEST_BLOCKCHAIN:
        this.write(peer, Messages.sendBlockchain(this.blockchain.get()));
        break;
      case RECEIVE_LATEST_BLOCK:
        this.handleReceivedLatestBlock(message, peer);
        break;
      case RECEIVE_BLOCKCHAIN:
        this.handleReceivedBlockchain(message);
        break;
      default:
        throw "Received invalid message.";
    }
  }
// }

// module.exports = PeerToPeer;
// class Messages { ... }

這裡寫圖片描述

P2p.js

// const wrtc = require('wrtc');
// const Exchange = require('peer-exchange');
// const p2p = new Exchange(...);
// const net = require("net");
// const messageType = { ... };
// const { ... } = messageType;

// class PeerToPeer {
  // constructor(blockchain) { ... }
  // startServer(port) { ... }
  // discoverPeers() { ... }
  // connectToPeer(host, port) { ... }
  // closeConnection() { ... }
  // broadcastLatest() { ... }
  // broadcast(message) { ... }
  // write(peer, message) { ... }
  // initConnection(connection) { ... }
  // initMessageHandler(connection) { ... }
  // initErrorHandler(connection) { ... }
  // handleMessage(peer, message) { ... }

  handleReceivedLatestBlock(message, peer) {
    const receivedBlock = message.data;
    const latestBlock = this.blockchain.latestBlock;

    if (latestBlock.hash === receivedBlock.previousHash) {
      try {
        this.blockchain.addBlock(receivedBlock);
      } catch(err) {
        throw err;
      }
    } else if (receivedBlock.index > latestBlock.index) {
      this.write(peer, Messages.getBlockchain());
    } else {
      // Do nothing.
    }
  }
// }

// module.exports = PeerToPeer;
// class Messages { ... }

這裡寫圖片描述

P2p.js

handleReceivedLatestBlock(message, peer) {
    // if (latestBlock.hash === receivedBlock.previousHash) {
    // ...
    } else if (receivedBlock.index > latestBlock.index) {
      this.write(peer, Messages.getBlockchain());
    } else {
      // Do nothing.
    }
  }

  handleReceivedBlockchain(message) {
    const receivedChain = message.data;

    try {
      this.blockchain.replaceChain(receivedChain);
    } catch(err) {
      throw err;
    }
  }

這裡寫圖片描述

 isValidChain(chain) {
    if (JSON.stringify(chain[0]) !== JSON.stringify(Block.genesis)) {
      return false;
    }

    const tempChain = [chain[0]];
    for (let i = 1; i < chain.length; i = i + 1) {
      if (this.isValidNextBlock(chain[i], tempChain[i - 1])) {
        tempChain.push(chain[i]);
      } else {
        return false;
      }
    }
    return true;
  }

  isChainLonger(chain) {
    return chain.length > this.blockchain.length;
  }

  replaceChain(newChain) {
    if (this.isValidChain(newChain) && this.isChainLonger(newChain)) {
      this.blockchain = JSON.parse(JSON.stringify(newChain));
    } else {
      throw "Error: invalid chain";
    }
  }

這裡寫圖片描述

這裡寫圖片描述

終端演示

安裝命令列工具

npm install blockchain-cli -g

blockchain

blockchian ->後面輸入blockchain或者bc檢視創始區塊結構。

創世區塊
這裡寫圖片描述

  • Index (Block #): 第幾個區塊? (創世區塊鏈的索引為0)
  • Hash: 當前區塊的hash值
  • Previous Hash: 上一個區塊的hash值
  • Timestamp: 當前區塊建立時的時間戳
  • Data: 儲存在當前區塊上的交易資訊
  • Nonce: 在找到有效區塊之前,我們經歷的迭代次數

mine data資料

這裡寫圖片描述

建立好了第一個區塊

Hash是怎麼計算的?

Hash值是一個十六進位制固定長度為64位的唯一的標識。

hash值是由index, previous block hash, timestamp, block data, 和 nonce 作為輸入資料計算而得。

CryptoJS.SHA256(index + previousHash + timestamp + data + nonce)

四個前導0是有效雜湊的最低要求。 所需的前導0的數量稱為難度

下面的方法驗證hash難度是否有效。

function isValidHashDifficulty(hash, difficulty) {
  for (var i = 0, b = hash.length; i < b; i ++) {
      if (hash[i] !== '0') {
          break;
      }
  }
  return i >= difficulty;
}

nonce是一個用來找到滿足條件的hash值的數字。

let nonce = 0;
let hash;
let input;
while(!isValidHashDifficulty(hash)) {     
  nonce = nonce + 1;
  input = index + previousHash + timestamp + data + nonce;
  hash = CryptoJS.SHA256(input)
}

nonce值一直迭代,直到hash值有效為止。在我們案例中一個有效的hash值是最少有4個前導0。找到nonce值以滿足合適條件的hash值的過程就叫做挖礦

隨著難度的增加,可能的有效雜湊數減少。 使用較少可能的有效雜湊,需要更多的處理能力才能找到有效的雜湊。