【区块链】——区块链学习初探(一)
一、宏观理解区块链——区块链不是技术而是技术集合
1. 本质
区块链不是比特币,也不是分布式数据库。区块链步入神坛的原因是提出了一个能够解决数据隐私、安全、可信的存储方式与交换方式,加上一个“去中心化”思想。他不是一门新鲜的技术,而是众多‘老技术’的集合:
- 分布式存储
- 分布式网络——P2P
- 密码学——编码加密与非对称加密(RSA、ECC)
- 分布式一致性——共识算法
- 智能合约——一段(存在链上的)在特定条件下按照特定规则响应的代码。监视(来自链上的)事件,将指定(链上)的数据按照一定规则处理返回结果(到链上)。于2.0引入用于尝试扩展单一的比特币区块链到其它业务中。
- 以太坊——在各个节点建立以太坊虚拟机【EVM】,通过智能合约对区块链的应用范围进行了扩展。
去中心化【Decentration】,首先说明一下这是一个翻译错误,准确的翻译应该是非中心化的,因为系统的网络架构分类除了中心化外,还有多中心、弱中心 、多中心、分布式,不可能实现绝对的无中心。
以百度网盘为例,显然用到了分布式数据库,但是这个数据库的管理者是唯一的——百度,百度拥有绝对权力控制这个数据库,尽管有用户隐私协议和用户安全协议的存在,也不能100%的保证数据对服务方是绝对隐私的,也不能保证数据在服务方是绝对安全的。去中心化旨在移除这个‘管理者’,由数据拥有者自己管理自己的数据,同时维护其他人数据的准确性。权力交给人民,人人为我,我为人人。
2. 架构
网上有很多区块链的技术架构方案,分辨针对不同的业务场景有不同的分层方法、层间关系及维度定义,但其实他们都有共通性,如下图。
首先是建立在基础硬件设施上的,提供区块链最基本的数据存储实现的‘基础数据层’。基础数据层首先要解决的就是数据【Data】的分布式存储。之后便是记录数据状态变化的区块链结构,又称账本【Ledger】。区块链顾名思义,就是区块【Block】构成的链【Chain】,区块中存储数据状态的数据,区块间有序的连接在一起就形成了(记录变化的)区块链。默克尔哈希树【MerkelHashTree】就是实现‘有序的连接’的方法。
之后就是‘数据交换层’,定义了数据交换,又称‘交易【Transaction,Tx】’的数据结构、交易的方式、交易的流程以及交易过程的安全。通过非对称加密的公钥私钥实现交易的安全性保密性,通过数字签名的方式实现交易的唯一性可验证性。还有就是确保交易能被除交易相关者外的其它网络参与者认可的共识机制。
在之上便是实现区块链网络的‘网络层’。网络层包括以P2P为基础的分布式网络,实现各个节点之间的通信方式,节点访问权限等。最重要的是引入智能合约为上层应用提供扩展的可能。发行、分配机制是指在一些金融类区块链应用往往会给网络的参与者一些奖励来促进激励他们参与到账本构建的工作中。
二、从0开始搭建一个区块链Demo——以比特币区块链为例
1.分布式数据/文件的存储方式
脱离了‘管理者’这个概念,如何实现‘网盘’呢,这里参考的是P2P网络中的BT方案。类比BT的tracker 服务器,他只存储分散在各个用户节点上的分片文件位置,而实际的存储、上传、下载业务都由业务的双方“私下”自己完成。
如图所示,以数据上传为例,数据会在区块链应用中先被分片(Shard),再被加密(Encrypte),之后一方面通过复制副本或纠删编码算法将加密分片变成多个(Replicated)分发给网络中参与者(包括自己)的数据存储节点,另一方面将加密分片变成Hash值与加密分片的存储地址一并写入账本中,当账本中的记录积累到一定数量时打包成区块广播给其他参与者。
数据可以分布式存储,但账本必须是一个节点一份,这也是区块链的缺点。这种特性造成的问题是帐本不能太大,至少不能超过区块链网络中最小结点的存储以及处理能力。所以,这制约了总交易数据记录条数,进而也影响了能写入区块链的单条交易数据的大小。所以交易中只能有数据的唯一描述信息,如地址、uuid等,不能存整个数据。这也同时带来一个问题,如何保证原始(全量)数据的准确性?最好的做法就是遍历原始(全量)数据,一条条加入记录就当从0开始增量。
2.区块的数据结构及构建方式
首先说区块都包括什么
字段名 | 类型 | 字段意义 |
---|---|---|
Index |
Int |
区块链中区块的深度 |
CreateTimestamp |
Long |
创建时间戳 |
CreateTimeUse |
Long |
创建(生成Hash)用时,ms |
Info |
Transaction[] |
块中包含的数据(应该被加密) |
Hash |
String |
该块的sha256哈希 |
PreviousHash |
String |
对上一个块的哈希的引用 |
HashNonce |
Int |
用来得到符合Hash难度的 Hash串的变量 |
HashDifficulty |
Int |
Hash难度 |
由这些字段我们可以展开一系列方法。其实在Block这里只有一个重要的方法,那就是Block的生成(构造方法),流程如下:
- 获得上一个Block的Index并加一赋给Index,如果是第一个Block(创世块)则是1
- 获取当前时间戳并赋给CreateTimestamp
- 对Block的数据进行加密并存在Info字段中
- 初始化随机Hash拼接量HashNonce为0(马上说他是干嘛的)
- 获取上一个Block的Hash难度HashDifficulty并赋给HashDifficulty(马上说他是干嘛的)
- 计算Hash并赋给Hash
- 计算创建用时并赋给CreateTimestamp
重点来了,Hash是怎么生成的?
Hash是通过先拼接前面的字段Index、CreateTimestamp、Info、PreviousHash、HashDifficulty和HashNonce为一个长串DataLongString,之后使用SHA-256编码方法生成的。也就是说当前块的Hash中除了包含本块信息外,还包含上一块的唯一性信息。这样一来即便是打乱链中的顺序也不会改变真实Black的顺序,即便是人为修改块内数据也无法通过后面业务中的Hash验证,因为一个小小的修改都是牵一发而动全身。
之后这里说明一下刚刚的欠账HashNonce和HashDifficulty,这两个字段是为了比特币区块链常用共识机制‘工作量证明【Proof of Work,PoW】’服务的,正常的其它区块链是不包含这些字段的。这里先不谈共识机制是怎么回事,单说一下HashNonce和HashDifficulty是怎么运作的。PoW规定,不是随便一条正常生成的Hash就能直接用,包含必要信息仅仅是第一步,你还要满足Hash对应的16进制字符串的二进制翻译字符串满足开头的HashDifficulty位均是0,所以为了解决这个难题,必须在原有不变的信息上拼接一个可变的信息HashNonce,从0开始遍历直到满足要求。
3. 链的数据结构及构建方式
这个在字段上没什么好说的
字段名 | 类型 | 字段意义 |
---|---|---|
Chain |
Block[] |
区块数组 |
LastIndex |
Int |
链尾Block的Index |
LastHash |
String |
链尾Block的Hash |
LastHashDifficulty |
Int |
链尾Block的LastHashDifficulty |
方法上,构建方法上在创建时要顺带建立创世块,这个块中可以不包含任何有价值的信息。
之后是替换链方法replaceChain,通过某种规则,如比特币区块链中的最长原则,当其他节点存储的链长度大于自己的时,废除自己链Copy一份最长链。
最后对于比特币区块链还有一个方法,更新HashDifficulty的方法resetHashDifficulty。该方法的目的是根据当前链的添加新块的难度,判断是否要提高或降低难度。
4.非对称加密
非对称加密不做赘述,众所周知非对称加密机制中有公钥和私钥两个东西来保证数据在交换时的安全性保密性。
在区块链交易中,交易双方都有一个公钥一个私钥,生成方式是:
“一个自定义的比特串 >> 私钥 >> 公钥”
公钥则当作交易用户的地址,可以类比成银行卡号。私钥用来给交易签名保证交易的合法性,可以类比成银行卡密码。
5.交易机制与数字签名
比如,参与者A曾几何时拥有了50块大洋,现在他要把其中30块施舍给身无分文的参与者B,这个流程在正常数据库中的变化如下:
交易前A的库中余额为50,B的余额为0,交易后A的库中余额为30,B的余额为20。
那么在区块链账本中,这个流程是怎么完成的呢?首先看两个问题:
- 在一般中心化系统中有一个指定的地址指定的管理者来管理账户数据,那么在非中心化的系统中该怎么管理账户数据呢?
- 现实生活中同一张钞票被交到一个商户A手中时不可能同时出现在另一个商户B手中,但是在电子交易中,数字具有极强的可复制性,如何保证数字在被‘消费’给商户A时不会同时‘消费’给商户B?
以比特币区块链为例,他引入了一个概念“未使用的可用输出(余额)【UnspentTransactionOutput,UTxO】”。简言之就是不使用账户余额来形成账本,而是使用交易本身形成账本。具体来说就是,每一笔交易Tx都是由“交易输入【TranscationIn,TxI】”和“交易输出【TranscationOut,TxO】”构成的,TxI表示的是资产来源,把一部分资产从参与者A中解锁【Unlock】。TxO表示资产去向,把解锁的资产再锁定【Relock】给参与者B。每笔交易可以包含多个TxI和TxO,而且每个TxI都是引用唯一旧的的UTxO,每个TxO又会变成新的UTxO。
还以这个AB的交易为例,曾几何时A通过某个交易收入了50,此时再账本中就有了一个关于参与者A的UTxO。
UTxO的数据结构如下:
字段名 |
类型 |
字段意义 |
---|---|---|
TxId |
String |
产生这个UTxO的交易ID |
ToAddress |
String |
资产转移到的参与者地址(公钥),也就是现在拥有这笔资产的参与者地址 |
Index |
Int |
UTxO在产生这个UTxO的交易的TxO列表的位置 |
Info |
Data |
交易的内容 |
Index和TxId使得每一个UTxO变得唯一,而ToAddress和Info则说明了在账本上该参与者拥有这一份资产。
在处理这个A给B转20的交易时。UTxO模式的方法流程是:
- 从账本中找到满足20的UTxO记录(可以是多个,直到够为止)UTxOs,摸摸兜看钱够不够。
- 遍历UTxOs为每一个UTxO创建一个TxI,数据结构如下
字段名 |
类型 |
字段意义 |
---|---|---|
OutId |
String |
引用的UTxO的TxId |
OutIndex |
Int |
引用的UTxO的Index |
Sign |
String |
用资产拥有者的私钥签名串,此时还是没签名的,这里是空的 |
- 再根据场景,建立转出给B 20的TxO和找零给A 30的TxO,数据结构如下
字段名 |
类型 |
字段意义 |
---|---|---|
ToAddress |
String |
资产转移到的参与者地址(公钥) |
Info |
Data |
转移的资产 |
- 将这一个TxI和两个TxO形成一个交易Tx,并根据TxI的OutId、OutIndex和TxO的ToAddress生成Tx的ID
- 根据Tx的ID以及交易发起的参与者A的私钥进行签名,写在TxI的Sign
- 将Tx广播到网络中(通常是堆积几个Tx形成Txs再发)。
- 网络中所有参与者包括交易发起者自己,可以根据这个Tx生成新的Block拼接在Chain里,谁快(谁长)就用谁的,同时也使用Tx的TxO的ToAddress(公钥)验证交易准确性。