區塊鏈主要技術總結 block_chain_tips

block_chain_tips

彙總BlockChain Bitcoin Ethereum 相關的技術原理的Tipsnode

https://github.com/heimashi/block_chain_tipsgit

Tip 1 - 區塊鏈主要技術 - Hash

Hash算法在區塊鏈技術中被普遍應用,例如錢包的帳戶地址、哈希指針、工做量證實的挖坑Hash算法、交易的Merkle樹等等,掌握Hash算法的技術原理對於理解區塊鏈很是重要。 Hash算法又成爲散列算法、哈希算法等,定義以下:github

  • 把任意長度的輸入,經過散列算法,變換成固定長度的輸出,該輸出就是散列值。
  • 常見的Hash算法:md5 sha1 sha256等
  • 三個特徵
    • 輸入任意大小
    • 輸出位數是固定的 例如md5輸出是128位、sha1輸出是160位、sha256是256位
    • 對應n位的字符串其哈希值計算的複雜度是O(n)

例以下面經過命令行計算字符串」abc「的md5值、sha1值和sha256值算法

md5 -s abc
MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72

echo -n abc | shasum -a 1
a9993e364706816aba3e25717850c26c9cd0d89d

echo -n abc | shasum -a 256
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
複製代碼

加密的哈希函數

哈希函數要用來加密,成爲加密的哈希函數須要知足一些特性,附加的特性以下:npm

  • 碰撞阻力數組

    • 碰撞是指兩個不一樣的輸入產生了相同的輸出,例如A經過Hash獲得C,而B經過Hash也獲得C,而實際上A和B不相同就產生了碰撞
    • 而md5已經被證實會出現碰撞,Sha256還沒找到碰撞,雖然輸入空間遠遠大於輸出空間,理論上是必然會出現碰撞,可是例如Sha256哈希算法,輸出空間2^256要測出碰撞目前計算能力還沒能找到碰撞
    • 應用:信息摘要 具備碰撞阻力的Hash算法就能夠用來做爲摘要算法,摘要能夠這麼通俗的理解它的含義:咱們在寫論文的時候,寫完了文章的內容之後會給文章寫一個文章的摘要,這個摘要就是和這篇文章的內容是一一對應的關係。
  • 隱祕性 或者稱爲不可逆bash

    • 根據輸出值沒有可行的辦法算出輸入值,不可逆
    • 應用:承諾 例如我寫了一封信後,把信的內容放進信封裏,同時把內容的Hash值告訴你,不用告訴你信的內容,我就等於給你承諾了信的內容確實且不能被篡改
  • 敏感性網絡

    • 輸入值稍微的改動就會致使輸出結果大的改動,找不到變更的規律
  • 謎題友好數據結構

    • 想找到y值所對應的輸入,假定在輸入集合中,有一部分是很是隨機的,那麼他將很是難以求得y值對應的輸入
    • 應用:搜索謎題

哈希鏈表 或者稱爲區塊鏈 Block Chain

img_hash_chain
經過上面的Hash值做爲鏈表的指針就稱爲哈希鏈表,與傳統的鏈表結構不同的是,傳統的鏈表指針記錄的是前一個節點的物理地址,而哈希鏈表的指針指向前一個節點的Hash值,第一個節點是頭節點,沒有父親節點,故通常指針值爲0。

  • 做用:鏈表內容不可篡改、可追溯

下面咱們用NodeJs來實現一個最簡單的哈希鏈表,即區塊鏈,詳細代碼見HashChain.jsapp

  • 一、首先咱們定義區塊的結構,像上圖所示,一個區塊有兩個參數,previousHash表明前一個區塊的Hash值,data表明區塊中記錄的數據:
/* * 定義區塊的數據結構,暫包含兩個參數: * previousHash:前一個區塊的Hash值 * data:區塊中記錄的數據 */
class Block {
    constructor(previousHash, data) {
        this.previousHash = previousHash.toString();
        this.data = data;
    }
}
複製代碼
  • 二、建立這個哈希鏈表的第一個元素,通常稱爲創世區塊,該區塊沒有前面的區塊,故把它的previousHash寫死爲0:
/* * 建立第一區塊,即創世塊: * previousHash:沒有前一個區塊,故寫爲0 * data:區塊中記錄的數據 */
var getGenesisBlock = () => {
    return new Block("0", "I'm the genesis block");
};
複製代碼
  • 三、初始化哈希鏈表,初始化的時候區塊鏈中只有一個創世區塊
//blockchain即區塊鏈,初始化時會實例化創世塊
var blockchain = [getGenesisBlock()];
複製代碼
  • 四、開始生成新的區塊,構建新區塊的過程當中,新區塊的previousHash值就是現有的區塊鏈中最後的那個區塊的Sha256值:
//根據傳人的data內容產生新的區塊
var generateNextBlock = (blockData) => {
    var previousBlock = getLatestBlock();
    return new Block(calculateHashForBlock(previousBlock), blockData);
};

//得到區塊鏈中的最後一個區塊
var getLatestBlock = () => blockchain[blockchain.length - 1];

//用Sha256計算區塊的Hash
var calculateHashForBlock = (block) => {
    return CryptoJS.SHA256(block.previousHash + block.data).toString();
};
複製代碼
  • 五、將生成的區塊添加到區塊鏈中,添加過程當中要驗證Hash指針是否一致
//將生成的新區塊加入區塊鏈中
var addBlock = (newBlock) => {
    if (isValidNewBlock(newBlock, getLatestBlock())) {
        blockchain.push(newBlock);
    }
};

//判斷新的區塊是否合法
var isValidNewBlock = (newBlock, previousBlock) => {
    if (calculateHashForBlock(previousBlock) !== newBlock.previousHash) {
        console.log('invalid previoushash');
        return false;
    } 
    return true;
};
複製代碼

通過上面5個步驟就能夠構建出一條哈希鏈表了,我將上面的步驟用vorpal庫封裝成了交互命令行,方便你們驗證代碼,測試的步驟以下:

  • 一、進入項目的code/tip1目錄,執行npm install後,再執行npm start,進入測試的命令行環境
  • 二、運行help查看幫助,主要提供了兩個工具命令行,執行bc命令能夠查看當前的區塊鏈的具體結構,執行add 能夠添加新的區塊到區塊鏈中,另外執行exit能夠退出命令行環境
HashChainCli$ help

  Commands:

    help [command...]  Provides help for a given command.
    exit               Exits application.
    bc                 show blockchain
    add <data>         add block to chain
複製代碼
  • 三、例如連續執行add aaa 以及 add bbb後,再經過執行bc命令能夠看到區塊鏈中就有了3個區塊:
HashChainCli$ add aaa
Add new block:
Block {
  previousHash: 'f1e3bb3792a062390c160101f0bca9ed156588bd1d126b066bf6abd7a1f99e06',
  data: 'aaa' }
HashChainCli$ add bbb
Add new block:
Block {
  previousHash: '83e065b4902f908ec291b99d390580e7693734707f16749fad8289c871936073',
  data: 'bbb' }
HashChainCli$ bc
[ Block { previousHash: '0', data: 'I\'m the genesis block' },
  Block {
    previousHash: 'f1e3bb3792a062390c160101f0bca9ed156588bd1d126b066bf6abd7a1f99e06',
    data: 'aaa' },
  Block {
    previousHash: '83e065b4902f908ec291b99d390580e7693734707f16749fad8289c871936073',
    data: 'bbb' } ]
複製代碼

哈希二叉樹 Merkle tree

Merkle tree一般也被稱做Hash Tree,顧名思義,就是存儲hash值的一棵樹.

  • 先由全部數據生成最底層的節點的Hash
  • 底層底節點再兩兩組合生成父節點,父節點的值是兩個孩子節點的值相加的Hash值
  • 逐層生成樹後直到達到根節點Merkle Root

在p2p網絡下載網絡以前,先從可信的源得到文件的Merkle Tree樹根。一旦得到了樹根,就能夠從其餘從不可信的源獲取 Merkle tree。Merkle Tree和Hash List的主要區別是,能夠直接下載並當即驗證Merkle Tree的一個分支。由於能夠將文件切分紅小的數據塊,這樣若是有一塊數據損壞,僅僅從新下載這個數據塊就好了。

img_hash_merkle_tree

  • 默克爾樹(又叫哈希樹) 應用於文件系統和p2p網絡中
  • 應用:
    • 隸屬證實 非隸屬證實
    • 快速比較大量數據
    • 快速定位修改。
    • 零知識證實 隱私友好的所在性證實

詳細代碼見MerkleTree.js

  • 1.定義樹節點對數據結構,包含三個參數:data:區塊中記錄的Hash值 leftChildHash:左子節點的Hash值 rightChildHash:右子節點的Hash值
/* * 定義樹節點,包含三個參數: * data:區塊中記錄的Hash值 * leftChildHash:左子節點的Hash值 * rightChildHash:右子節點的Hash值 */
class TreeNode {
    constructor(data, leftChildHash, rightChildHash) {
        this.data = data;
        this.leftChildHash = leftChildHash.toString();
        this.rightChildHash = rightChildHash.toString();
    }
}
複製代碼
  • 2.由數組來生成簡單的Merkle樹
/* * 根據傳人對數組,爲數組生成一棵Merkle Tree */
var buildMerkleTree = (dataArr) => {
    var merkleTree = [];
    var index = 0;
    var tmpList = [];
    for(var data in dataArr){
        var dataHash = CryptoJS.SHA256(data).toString();
        var node = new TreeNode(dataHash, "", "");
        tmpList.push(node);
    }
    merkleTree.push(tmpList);

    while(merkleTree[index].length>1){
        index++;
        var size = 0;
        var tmpNodeList = [];
        var maxSize = merkleTree[index-1].length;
        while(size<maxSize){
            var leftNode = merkleTree[index-1][size++];
            if(size<maxSize){
                var rightNode = merkleTree[index-1][size++];
                var dataHash = CryptoJS.SHA256(leftNode.data+rightNode.data).toString();
                var newNode = new TreeNode(dataHash, leftNode.data, rightNode.data);
                tmpNodeList.push(newNode);
            }else{
                var newNode = new TreeNode(leftNode.data, leftNode.data, "");
                tmpNodeList.push(newNode);
            }

        }

        merkleTree.push(tmpNodeList);
        
    }
    
    console.log(merkleTree);
}
複製代碼

測試上面的步驟以下:

  • 一、進入項目的code/tip1目錄,執行npm install後,再執行npm MerkleTree.js,進入測試的命令行環境
  • 二、運行help查看幫助,主要提供了兩個工具命令行,執行buildTree dataArr命令能夠生成樹結構,buildTree aa bb
MerkleCli$ buildTree aa bb
[ [ TreeNode {
      data: '5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9',
      leftChildHash: '',
      rightChildHash: '' },
    TreeNode {
      data: '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b',
      leftChildHash: '',
      rightChildHash: '' } ],
  [ TreeNode {
      data: 'fa13bb36c022a6943f37c638126a2c88fc8d008eb5a9fe8fcde17026807feae4',
      leftChildHash: '5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9',
      rightChildHash: '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' } ] ]
複製代碼

Tip 2 - 區塊鏈主要技術 - 數字簽名和非對稱加密

非對稱加密

  • 加密分爲兩類
    • 對稱加密 密鑰只有一個,加密和解密都用同一個key,例如AES DES加密算法
    • 非對稱加密 密鑰成對出現,分爲私鑰和公鑰,私鑰加密後公鑰能解密,公鑰加密後只有私鑰能解密。例如RSA ECC(橢圓曲線算法)

非對稱加密的例子以下(以RSA算法爲例),詳細代碼見index.js

  • 一、經過RSA算法產生一對公鑰私鑰
var NodeRSA = require('node-rsa');

//產生512位的RSA key
var key = new NodeRSA({b: 512});

//打印出公鑰
var publicDer = key.exportKey('public');
console.log("public key:", publicDer);

//打印出私鑰
var privateDer = key.exportKey('private');
console.log("private key:", privateDer);
複製代碼
  • 二、經過RSA算法和上一步生成的一對key來加密和解密
//用RSA加密解密data參數
var testRSA = (data) => {
    var encrypted = key.encrypt(data, 'base64');
    console.log('encrypted: ', encrypted);
    var decrypted = key.decrypt(encrypted, 'utf8');
    console.log('decrypted: ', decrypted);
}
複製代碼
  • 三、測試上面的代碼,進入code/tip2/目錄,執行npm install, npm start後進入控制檯,再執行TestRSA data裏測試加密解密的過程
public key: -----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKTRtMTmmcVRjicwtTymoJYo388qK2dt
YTKcMOUgrZ/BImwA5pGSz/iJJvmaQDI58Jj0lwU3TzLiv/1JyPWcxX8CAwEAAQ==
-----END PUBLIC KEY-----
private key: -----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJBAKTRtMTmmcVRjicwtTymoJYo388qK2dtYTKcMOUgrZ/BImwA5pGS
z/iJJvmaQDI58Jj0lwU3TzLiv/1JyPWcxX8CAwEAAQJAIrSlw/Bq4MnTjR0MjMDp
f7ULq6vNh/HYTbfl89l1tfW2hO8HdjSirytzt2SDYuaiUKawsmtYYvyfy5QrgrWY
QQIhANqe+VWocev2S5AoKmlr1QXHYeUkgFMvK1USawNBP+gDAiEAwP/UbtQgk8yN
YL7hw1g9WDT8e7BobPt2EUbO6OXC6dUCIDziwXYFr5STx3+icA1kJrOxT6ZNgB+q
p1rOAlepuG6ZAiAjp19MNh3qj/BSPhEg8E0s3WUDSJyR/YZbPLR+q+ttHQIgYP0M
/ndhIXgmjLwXphFp5IBQ/x7NDQAn+72kde1GeZ4=
-----END RSA PRIVATE KEY-----
RSA_Cli$ help

  Commands:

    help [command...]  Provides help for a given command.
    exit               Exits application.
    TestRSA <data>     用RSA加密解密data參數
 RSA_Cli$ TestRSA aaaaaa
encrypted:  I94KvnCB+hVYp+3Vb+MWswpjJgs/Kou/OdhdouogM7W0RTguwdbNc/2qscfYrcZER3KsgnIIOKyhoioYqwp9fw==
decrypted:  aaaaaa
複製代碼

數字簽名

數字簽名技術基於非對稱加密算法,可用於身份認證確認來源以及數據的完整不可篡改,比特幣用數字簽名技術來保證交易的身份認證及完整不被篡改。

數字簽名流程:(以寫信舉例)

  • 1,A想寫一封信給B,信的內容爲X
  • 2,A將信的內容X進行摘要獲得Y
  • 3,而後將摘要Y用本身的私鑰加密獲得Z,加密獲得串就稱爲簽名
  • 4,以後將信X和簽名Z一塊兒發給B
  • 5,B擁有A的公鑰,拿到信和簽名後,經過公鑰解密出簽名Z獲得解密後的Y’,而後將信X進行摘要獲得Y,而後比較Y和Y’是否相等

用代碼簡單演示上面的過程,詳細代碼見sign.js

  • 一、獲取data內容的簽名,簽名後將信的內容data和簽名一塊兒發給用戶
//用RSA和key來對data參數簽名
var getSignature = (data) => {
    //先經過摘要算法把要簽名的data通過摘要獲得其Hash值
    var dataHash = CryptoJS.SHA256(data).toString();
    
    //把data的摘要用私鑰進行加密,而後將簽名和data內容一塊兒發給收信人
    var signature = key.encrypt(dataHash, 'base64');

    return {content:data, sign: signature};
}
複製代碼
  • 二、用戶拿到內容data和簽名後,再用公鑰解密後對比data的哈希值是否一致
//驗證簽名
var verifySignature = (msg) => {
    //收信人拿到簽名後,用公鑰解密簽名拿到信的摘要
    var decryptedDataHash = key.decrypt(msg.sign, 'utf8');
    //計算data的摘要值
    var dataHash = CryptoJS.SHA256(msg.content).toString();
    return dataHash === decryptedDataHash;
}
複製代碼
  • 三、測試上面的代碼,進入code/tip2/目錄,執行npm install, node sign.js後進入控制檯,再執行TestSign data裏測試數字簽名驗證過程
RSA_Cli$ TestSign abdfdf
{ content: 'abdfdf',
  sign: 'uYDnPbbPjQ82ufHY9F6aAE/nwgZqKykVkArGlUGijn4ptHDRjFIibRhiwJPOfSt0zv9AHOuS3gGo65FUkpQbU0SYhQik+bCKwief/f8Rsneyz8kD7cPLp0KgItO0YTsT/OTBJkLEXfCL8jxtzS+38PX+zoWH4u4u7rm7DhuL/OVY5vviUi5ZAUq/9JkJVJ41ocPbGB+XIweZ0Q3RGh9d3c0VXbyLhUbNyRkCcMxquAPtA2XGz+7CwJ4eL987YF0B' }
verify: true
複製代碼

Base58編碼

對於一些二進制串不方便查看,一般會採用編碼手段轉化爲可視的字符串,例如Base6四、Base58編碼等,在比特幣地址中就利用了Base58編碼來提升帳戶公鑰的可視化

  • Base64編碼 64個字符,2^6表示2^8,故會增長內存暫用
    • 數字:0,1,2,3,4,5,6,7,8,9共10個
    • 小寫字母:a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,
    • 共26個大寫字母:A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,共26個
    • 符號+以及斜槓/
echo -n aaafsfsf | base64
YWFhZnNmc2Y=
複製代碼
  • Base58編碼 58個字符,跟Base64相似,去掉了一些容易看錯的、易產生歧義的字符。

    • 去掉了0(零),O(大寫字母O)
    • 去掉了I(大寫的字母i),l(小寫的字母L)
    • 去掉了影響雙擊選擇的字符,/, +
    • 結果字符集正好58個字符(包括9個數字,24個大寫字母,25個小寫字母)
  • 代碼實例,詳細代碼見baseEncode.js

var Base58 = require('base58');
Base58.encode('aaa');
Base58.decode('ccc');
複製代碼
  • 測試Base58,進入code/tip2/目錄,執行npm install, node baseEncode.js後進入控制檯
node baseEncode.js
Base58_Cli$ encode 123456
CGy
Base58_Cli$ decode CGy
123456
複製代碼

Tip 3 - 區塊鏈主要技術 - P2P網絡

Tip 4 - 共識機制 - POW工做量證實

Tip 5 - 共識機制 - POS

相關文章
相關標籤/搜索