区块链主要技术总结 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

相关文章
相关标签/搜索