汇总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
复制代码