彙總BlockChain Bitcoin Ethereum 相關的技術原理的Tipsnode
https://github.com/heimashi/block_chain_tipsgit
Hash算法在區塊鏈技術中被普遍應用,例如錢包的帳戶地址、哈希指針、工做量證實的挖坑Hash算法、交易的Merkle樹等等,掌握Hash算法的技術原理對於理解區塊鏈很是重要。 Hash算法又成爲散列算法、哈希算法等,定義以下:github
例以下面經過命令行計算字符串」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
碰撞阻力數組
隱祕性 或者稱爲不可逆bash
敏感性網絡
謎題友好數據結構
下面咱們用NodeJs來實現一個最簡單的哈希鏈表,即區塊鏈,詳細代碼見HashChain.js:app
/* * 定義區塊的數據結構,暫包含兩個參數: * previousHash:前一個區塊的Hash值 * data:區塊中記錄的數據 */
class Block {
constructor(previousHash, data) {
this.previousHash = previousHash.toString();
this.data = data;
}
}
複製代碼
/* * 建立第一區塊,即創世塊: * previousHash:沒有前一個區塊,故寫爲0 * data:區塊中記錄的數據 */
var getGenesisBlock = () => {
return new Block("0", "I'm the genesis block");
};
複製代碼
//blockchain即區塊鏈,初始化時會實例化創世塊
var blockchain = [getGenesisBlock()];
複製代碼
//根據傳人的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();
};
複製代碼
//將生成的新區塊加入區塊鏈中
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庫封裝成了交互命令行,方便你們驗證代碼,測試的步驟以下:
HashChainCli$ help
Commands:
help [command...] Provides help for a given command.
exit Exits application.
bc show blockchain
add <data> add block to chain
複製代碼
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一般也被稱做Hash Tree,顧名思義,就是存儲hash值的一棵樹.
在p2p網絡下載網絡以前,先從可信的源得到文件的Merkle Tree樹根。一旦得到了樹根,就能夠從其餘從不可信的源獲取 Merkle tree。Merkle Tree和Hash List的主要區別是,能夠直接下載並當即驗證Merkle Tree的一個分支。由於能夠將文件切分紅小的數據塊,這樣若是有一塊數據損壞,僅僅從新下載這個數據塊就好了。
詳細代碼見MerkleTree.js
/* * 定義樹節點,包含三個參數: * data:區塊中記錄的Hash值 * leftChildHash:左子節點的Hash值 * rightChildHash:右子節點的Hash值 */
class TreeNode {
constructor(data, leftChildHash, rightChildHash) {
this.data = data;
this.leftChildHash = leftChildHash.toString();
this.rightChildHash = rightChildHash.toString();
}
}
複製代碼
/* * 根據傳人對數組,爲數組生成一棵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);
}
複製代碼
測試上面的步驟以下:
MerkleCli$ buildTree aa bb
[ [ TreeNode {
data: '5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9',
leftChildHash: '',
rightChildHash: '' },
TreeNode {
data: '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b',
leftChildHash: '',
rightChildHash: '' } ],
[ TreeNode {
data: 'fa13bb36c022a6943f37c638126a2c88fc8d008eb5a9fe8fcde17026807feae4',
leftChildHash: '5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9',
rightChildHash: '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' } ] ]
複製代碼
非對稱加密的例子以下(以RSA算法爲例),詳細代碼見index.js:
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加密解密data參數
var testRSA = (data) => {
var encrypted = key.encrypt(data, 'base64');
console.log('encrypted: ', encrypted);
var decrypted = key.decrypt(encrypted, 'utf8');
console.log('decrypted: ', decrypted);
}
複製代碼
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
複製代碼
數字簽名技術基於非對稱加密算法,可用於身份認證確認來源以及數據的完整不可篡改,比特幣用數字簽名技術來保證交易的身份認證及完整不被篡改。
數字簽名流程:(以寫信舉例)
用代碼簡單演示上面的過程,詳細代碼見sign.js:
//用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};
}
複製代碼
//驗證簽名
var verifySignature = (msg) => {
//收信人拿到簽名後,用公鑰解密簽名拿到信的摘要
var decryptedDataHash = key.decrypt(msg.sign, 'utf8');
//計算data的摘要值
var dataHash = CryptoJS.SHA256(msg.content).toString();
return dataHash === decryptedDataHash;
}
複製代碼
RSA_Cli$ TestSign abdfdf
{ content: 'abdfdf',
sign: 'uYDnPbbPjQ82ufHY9F6aAE/nwgZqKykVkArGlUGijn4ptHDRjFIibRhiwJPOfSt0zv9AHOuS3gGo65FUkpQbU0SYhQik+bCKwief/f8Rsneyz8kD7cPLp0KgItO0YTsT/OTBJkLEXfCL8jxtzS+38PX+zoWH4u4u7rm7DhuL/OVY5vviUi5ZAUq/9JkJVJ41ocPbGB+XIweZ0Q3RGh9d3c0VXbyLhUbNyRkCcMxquAPtA2XGz+7CwJ4eL987YF0B' }
verify: true
複製代碼
對於一些二進制串不方便查看,一般會採用編碼手段轉化爲可視的字符串,例如Base6四、Base58編碼等,在比特幣地址中就利用了Base58編碼來提升帳戶公鑰的可視化
echo -n aaafsfsf | base64
YWFhZnNmc2Y=
複製代碼
Base58編碼 58個字符,跟Base64相似,去掉了一些容易看錯的、易產生歧義的字符。
代碼實例,詳細代碼見baseEncode.js:
var Base58 = require('base58');
Base58.encode('aaa');
Base58.decode('ccc');
複製代碼
node baseEncode.js
Base58_Cli$ encode 123456
CGy
Base58_Cli$ decode CGy
123456
複製代碼