区块链的技术定义

  1. 区块链的存储基于分布式数据库;
  2. 数据库是区块链的数据载体,区块链是交易的业务逻辑载体;
  3. 区块链按时间序列化区块数据,整个网络有一个最终确定状态;
  4. 区块链只对添加有效,对其他操作无效;
  5. 交易基于非对称加密的公私钥验证;
  6. 区块链网络要求拜占庭将军容错;
  7. 共识算法能够“解决”双花问题。

区块链的核心技术组成

P2P 网络协议

一般 P2P 网络技术要解决两个主要问题,第一是资源定位,第二是资源获取,其中节点发现和局域网穿透是属于资源定位问题,节点交互协议是属于资源获取问题。

网络连接

区块链其实是基于 TCP/IP 网络协议的,这与 HTTP 协议、SMTP 协议是处在同一层,也就是应用层。

以 HTTP 协议为代表的、与服务端的交互模式在区块链上被彻底打破了,变更为完全的点对点拓扑结构,这也是以太坊提出的 Web3.0 的由来。

比特币的 P2P 网络是一个非常复杂的结构,考虑到矿池内部的挖矿交互协议与轻节点。仅仅讨论全节点这种场景下的 P2P 网络发现与路由。比特币的 P2P 网络基于 TCP 构建,主网默认通信端口为 8333。

以太坊的 P2P 网络则与比特币不太相同,以太坊 P2P 网络是一个完全加密的网络,提供 UDP 和 TCP 两种连接方式,主网默认 TCP 通信端口是 30303,推荐的 UDP 发现端口为 30301。

拓扑结构

P2P 网络拓扑结构有很多种,有些是中心化拓扑,有些是半中心化拓扑,有些是全分布式拓扑结构。

比特币全节点组成的网络是一种全分布式的拓扑结构,节点与节点之间的传输过程更接近“泛洪算法”,即:交易从某个节点产生,接着广播到临近节点,临近节点一传十十传百,直至传播到全网。

全节点与 SPV 简化支付验证客户端之间的交互模式,更接近半中心化的拓扑结构,也就是 SPV 节点可以随机选择一个全节点进行连接,这个全节点会成为 SPV 节点的代理,帮助 SPV 节点广播交易。

节点发现

节点发现是任何区块链节点接入区块链 P2P 网络的第一步。

初始节点发现

在比特币网络中,初始节点发现一共有两种方式。

第一种叫做 DNS-seed,又称 DNS 种子节点,DNS 就是中心化域名查询服务,比特币的社区维护者会维护一些域名。

第二种方式就是,代码中硬编码( hard-code )了一些地址,这些地址我们称之为种子节点(seed-node),当基于DNS的种子节点全部失效时,会尝试连接hard-code的种子节点。

启动后节点发现

在 Bitcoin 的网络中,一个节点可以将自己维护的对等节点列表 (peer list) 发送给临近节点,所以在初始节点发现之后,你的节点要做的第一件事情就是向对方要列表

所以在每次需要发送协议消息的时候,它会花费固定的时间尝试和已存的节点列表中的节点建立链接,如果有任何一个节点在超时之前可以连接上,就不用去 DNS seed 获取地址,一般来说,这种可能性很小,尤其是全节点数目非常多的情况下。

在以太坊网络中,也会维护类似的一个节点列表 (NodeTable),但是这个节点列表与比特币的简单维护不同,它采用了 P2P 网络协议中一个成熟的算法,叫做 Kademlia 网络,简称 KAD 网络。

它使用了 DHT 来定位资源,全称 Distributed Hash Table,中文名为分布式哈希表。KAD 网络会维护一个路由表,用于快速定位目标节点。由于 KAD 网络基于 UDP 通信协议,所以以太坊节点的节点发现是基于 UDP 的,如果找到节点以后,数据交互又会切换到 TCP 协议上。

黑名单与长连接

公有区块链面临的网络环境是非常开放的,任何人只要下载好钱包,打开运行就进入了这个 P2P 网络,这也会带来被攻击的可能。

所以在比特币的代码中,会有一段去控制逻辑,你可以手动将你认为可疑的节点移除并加入禁止列表,同时去配置可信的节点。当然,以上并不属于客户端的标准协议的一部分,任何人都可以实现属于自己的 P2P 网络层。

以太坊上有针对账户进行的黑名单处理,但是这属于业务层。

局域网穿透

当你在局域网运行一个区块链节点,在公网是发现不了的,公网上的节点只能被动接受连接,并不能主动发起连接

NAT 技术非常常见,这里使用的是源 NAT,简而言之就是替换 TCP 报文中的源地址并映射到内网地址。

UPnP 是通用即插即用(Universal Plug and Play)的缩写,它主要用于设备的智能互联互通,所有在网络上的设备马上就能知道有新设备加入。

这些设备彼此之间能互相通信,更能直接使用或者控制它,一切都不需要人工设置。

比特币和以太坊均使用了 UPnP 协议作为局域网穿透工具,只要局域网中的路由设备支持 NAT 网关功能、支持 UPnP 协议,即可将你的区块链节点自动映射到公网上。

节点交互协议

一旦节点建立连接以后,节点之间的交互是遵循一些特定的命令,这些命令写在消息的头部,消息体写的则是消息内容。

命令分为两种,一种是请求命令,一种是数据交互命令。

节点连接完成要做的第一件事情叫做握手操作。这一点在比特币和以太坊上的流程是差不多的,就是相互问候一下,提供一些简要信息。

比如先交换一下版本号,看看是否兼容。只是以太坊为握手过程提供了对称加密,而比特币没有。

握手完毕之后,无论交互什么信息,都是需要保持长连接的,在比特币上有 PING/PONG 这两种类型的消息,这很明显就是用于保持节点之间长连接的心跳而设计的;而在以太坊的设计中,将 PING/PONG 协议移到了节点发现的过程中。

请求命令一般分为发起者请求,比如比特币中的 getaddr 命令是为了获取对方的可用节点列表,inv 命令则提供了数据传输,消息体中会包含一个数据向量。

区块链最重要的功能就是同步区块链,而同步区块恰巧是最考验 P2P 网络能力的。区块同步方式分为两种,第一种叫做 HeaderFirst,它提供了区块头先同步,同步完成以后再从其他节点获得区块体。

第二种叫做 BlockFirst,这种区块同步的方式比较简单粗暴,就是从其他节点获取区块必须是完整的。第一种方案提供了较好的交互过程,减轻了网络负担。这两种同步方式会直接体现在节点交互协议上,他们使用的命令逻辑完全不同。

共识算法与分布式一致性算法

经典的分布式一致性算法

经典分布式一致性算法有 Raft 协议,Raft 协议是一种强 Leader 的一致性算法,它的吞吐量基本就是 Leader 的吞吐量,它无法抵御节点恶意篡改数据的攻击。

稍微复杂一点的就是 Paxos 协议,Paxos 能提供不同场合不同种类的一致性算法,所以 Paxos 有很多变种,经典 Paxos 是 Leaderless 的,有变种是强 Leader 型的,叫做 Fast Paxos

以上两种都是不提供拜占庭容错的系统

PBFT 全称实用性拜占庭容错系统(Practical Byzantine Fault Tolerance, PBFT),PBFT 是一种状态机,要求所有节点共同维护一个状态,所有节点采取的行动一致,PBFT 非常适合联盟链等对性能具有较高要求的场合,超级账本项目中的 Fabric 框架默认采用的就是 PBFT 的修改版本。

区块链共识算法

区块链的共识算法,在某些场合直接称作基于经济学的博弈算法,以区别于经典分布式一致性算法思路,它的整体思路就是让攻击者的攻击成本远远大于收益

区块链中的共识算法目前具有工业成熟度的是 PoW,另外两种比较成熟的是 PoS 和 DPoS,其次还有一些变种和单一币种使用的共识算法,例如 Ripple 共识、PoC 共识(概念性证明)、PoE 共识(存在性证明)。

在使用 PoW 共识算法的情况下,容错阈值是 50%,而 PBFT 及其变种的容错阈值是 33% 左右,这里的百分比是指作弊节点占全网节点的比例。

PoX 类的算法其实都延续了 PoW 的设计理念,相比较经典分布式一致性算法,PoX 类算法通过概率选择记账者降低了潜在的提案者,另外是延长了达成最终一致性的时间。

第一条降低了系统通信复杂度,每次记账系统的确定性其实是概率确定的,又由于被选中需要付出成本,此处才提高了记账成本阈值,结合区块链的基础代币设计,是一个非常天才的想法。

PoW共识

PoW 全称 Proof of Work,中文名是工作量证明,PoW 共识机制其实是一种设计思路,而不是一种具体的实现。

PoW 的核心设计思路是提出一个计算难题,但是这个难题答案的验证过程是非常容易的,这种特性我们称之为计算不对称特性

PoW 挖矿算法大致分为两个大类,第一类叫做计算困难,第二类叫内存困难。

从理论上来看,PoW 会一直有 51% 算力攻击的问题,即攻击者只需要购买超过全网 51% 算力设备,即可发起“双花攻击”,甚至“重放攻击”等多种高收益攻击,这个问题目前没有解决方案。

除了 51% 攻击,PoW 共识还有自私挖矿的问题,自私挖矿是一种特殊的攻击类型,不会影响区块链正常运转,但是会形成矿霸,间接造成 51% 攻击

PoW 挖矿的基本逻辑和步骤Hash (block_header) < Target所有矿工的目标值是一样的,只要计算结果哈希小于目标值即可

PoS共识机制

PoS 全称是 Proof of Stake,中文翻译为权益证明

CoinAge,字面意思就是币数量乘以天数

PoS 则将计算能力更换为财产证明,就是节点所拥有的币龄越多,获得的记账的概率就越大。这有点像公司的股权结构,股权占比大的合伙人话语权越重。

PoS 系统中,这个公式变更为Hash (block_header) < Target * CoinAge多引入了一个变量叫做 CoinAge,也就是币龄,这个变量为会造成每个矿工看到的目标值不一样,如果你的币龄越大,也就意味着你的获得答案越容易。这里的 Target 与 PoW 一致,与全网难度成反比,用来控制出块速度的。

PoS 似乎完美地解决了 PoW 挖矿资源浪费的问题,甚至还顺带解决了 51% 攻击的问题(如果挖矿者发起 51% 攻击,就需要拥有全网 51% 的币或币龄,这几乎不可能办到,即使你成功地实施了 51% 攻击,那么也意味着作为全网最大的持币大户的你,损失也会最大)

PoS 遇到的第一个问题就是币发行的问题。一开始的时候,只有创始区块上有币,意味着只有这一个节点可以挖矿,所以让币分散出去才能让整个网络壮大,那么如何分散出去又是另外一个难题了。早期 PoS 币种基本都采用了分阶段挖矿,即第一阶段是 PoW 挖矿,到第二阶段才是 PoS 挖矿。随着 ERC20 类型的标准合约代币的出现,这个问题被解决了,不再需要第一阶段改成 PoW,也可以将代币分散出去。

第二个问题是由于币龄是与时间挂钩的,这也意味着用户可以无限囤积一定的币,等过了很久再一次性挖矿发起攻击;所以解决方案是:PoS 机制需要引入一个时间上限来控制时间因素的自然增长。

第三个问题是虽然引入了时间上下限,用户还是倾向于囤积代币,这会造成币流通的不充分;基于此,所以瑞迪币引入了币龄按时间衰减,构造了权益速度证明,鼓励用户流动代币,而不是倾向于囤积代币。

第四个问题是离线攻击,即使引入了时间上下限,时间仍然是自然流动的,也就是可以不需要求挖矿节点长时间在线。挖矿是可以离线的,这简直是灾难,所以任意一个 PoS 机制的实践形式都必须避免这个问题,因为网络节点数量的多少直接关系到区块链网络的健壮性。

无成本利益问题无论以币龄还是币数量作为 PoS 的参数,都无法避免,这也就意味着分叉非常方便。

由于以太坊部分采用了 PoS 共识,它的名字叫做 Casper,它必须解决上述无成本利益问题攻击。所以 Casper 协议要求PoS 矿工需通过抵押保证金的方法对共识结果进行下注

DPoS共识机制

DPoS 全称是 Delegated Proof of Stake,中文翻译过来是代理权益证明。

DPoS 与其他共识机制的第一个区别,就是交易确认时间短

DPoS 共识算法就是将 PoS 共识算法中的记账者转换为指定节点数组成的小圈子,而不是所有人都可以参与记账,这个圈子可能是 21 个节点,也有可能是 101 个节点,这一点取决于设计,只有这个圈子中的节点才能获得记账权。这将极大地提高系统的吞吐量,因为更少的节点也就意味着网络和节点的可控。

TPS = transactions / block_time

TPS 表示区块链每秒能确认的交易数, transactions 是由区块大小 block_size 和平均每笔交易大小决定的,而区块大小受全网网络状态 network_bandwidth 限制,也是由记账节点之间物理带宽 witness_performance 决定的。

记账节点的个数 witness_count 直接决定了物理带宽的上限,因为记账节点数量越多,则对物理带宽要求越高,对网络的稳定性要求也越高。

公式变成了下面的样子

TPS = (block_size *network_bandwidth* witness_performance) /
(block_time * witness_count)

要提高 TPS,可以提升分子项,降低分母项,也就是增大区块大小 block_size、提升记账节点网络带宽 network_bandwidth、提升记账节点处理性能 witness_performance,减小区块时间 block_time、减小记账节点数量 witness_count。

分子项基本受限于物理资源的上限,目前工业水平制造的物理资源的使用上限基本就是整个项的上限了,所以可操作性不大。

而分母项是由共识算法决定的,所以从区块时间,以及记账节点数入手,DPoS 算法便正是从这两项着手的。

首先改动的便是限制记账节点的数量,也就是见证人的数量。

在 PoW 和 PoS 中可以看到,成为记账节点是无需门槛的,你可以随时参与挖矿,随时退出。

那这会带来什么问题呢,首先无法确定记账节点的数量,其次无法确定记账节点之间的网络环境,记账节点数越多网络环境越复杂,这些不确定性会增大网络分区的概率,从而导致区块链分叉。

如果我们事先规定好记账节点的数量,接着让全网所有节点可以投票决定哪些节点可以成为记账节点,这样就限制并减小了分母项 witness_count,这个过程称作投票选举。

因为记账节点数量不多,那么可以在共识算法中可以规定出块时间为一个固定值,这个值可以很小,通过轮流出块的方式来进行记账。

DPoS 算法确立两个原则:

  • 投票选举过程一定要保证最大权益所有者最终能控制全网,因为一旦出了问题,他们的损失最大
  • 与 PoW、PoS 一样,所有节点仅承认“最长”链

DPoS 是社区治理加上共识算法,不再是单纯的技术共识,这是与 PoW、PoS 算法最大的不同。

DPoS 的基本假设是相信节点是好的,所以尽可能快速选择记账节点,而把问题发生后的修复过程推迟到投票中,可以说 DPoS 并不考虑拜占庭容错问题,把拜占庭容错推给了社区治理,而在社区治理上可归纳为一切皆投票。

加密签名算法

哈希算法

哈希算法具有下面的 4 种特性。

  1. **原像不可逆。**原像不可逆是指对于任意给定的 h,都无法依据 h 自身的信息推导出 z。
  2. **难题友好性。**难题友好性通俗的理解就是如果要得到难题答案,你只能暴力枚举,没有比这更好的方法。在 h = HASH( X | z ) 中,从 h 无法推导出 z,只能不断地计算尝试,那么 z 所在的数值集合构成了 X,X 的大小是哈希算法的安全因子之一。
  3. **发散性。**发散性是指对于任意的 z,即使我们只改动非常少的信息量,例如改动 1 个比特位生成 z’,那么 HASH(z) 与 HASH(z’) 就是两个大相径庭的结果,完全不相似。
  4. **抗碰撞性。**抗碰撞性是指对于任意两个不相同的 z,那么他们对应的 h 值也不同。如果对于任意的 y 不等于 z,则 HASH(y) 不等于 HASH(z);满足上述定义哈希特性的算法,我们也称作具有严格抗碰撞性。如果我们任意给定一个 z,你都无法找到另外一个 z’,使得其值也等于 h,满足这样的哈希特性的算法就有弱抗碰撞性。

目前流行的 Hash 算法包括了 MD5、SHA-1 和 SHA-2,其中 MD5 被证明不具有强抗碰撞性。SHA (Secure Hash Algorithm)是一个 Hash 函数族,分为 SHA-1、SHA-2、SHA-3,代表了三代哈希标准,目前使用比较多的是 SHA-2 系列。

第一代的 SHA-1 基于 MD4 设计,并且模仿了该算法,SHA-1 已被证明了不具备“强抗碰撞性”,所以安全性不够高。

为了提高安全性,第二代 SHA-2 一共包含了 SHA-224、SHA-256、SHA-384,和 SHA-512 算法(统称为 SHA-2),它们跟 SHA-1 算法原理类似。SHA-3 相关算法也已被提出,它的出现并不是要取代 SHA-2,因为 SHA-2 目前并没有出现明显的弱点。

由于对 MD5、和 SHA-1 出现成功的破解,我们需要一个不同与之前算法,可替换的加密散列算法,也就是现在的 SHA-3。

哈希算法的一个重要应用是默克尔树(Merkle tree),默克尔树是一种数据结构,通常是一个二叉树,也有可能是多叉树,它以特定的方式逐层向上计算,直到顶部,最顶层叫做默克尔根,默克尔树最为常见和最简单的是二叉默克尔树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OMoBMpZs-1680955391619)(null)]

非对称加密算法

对称密钥要求加解密过程均使用相同的密钥,而非对称加密可以提供一对钥匙,私钥自己保管,公钥可以公开。

常见的对称加密算法有 DES、3DES、AES、IDEA, 常见的非对称加密算法有 RSA、ECC 等。

在非对称算法中,私钥一般是通过一个随机数产生的,这个随机数我们也叫做种子,从这个角度来说,知道了这个随机数也就等于知道了私钥,不过私钥的产生范围非常大,在比特币中是 2 的 256 次方,差不多在 10 的 70 方数量级上。

如果你产生随机数的算法足够均匀分布,私钥碰撞的可能性比中了 1 亿大奖同时被雷劈中的概率还要小数亿倍。所以区块链对产生随机数的算法要求比较高,它要求真实的均匀随机分布,而不是计算机伪随机数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YFkDnszM-1680955391649)(null)]

从私钥到公钥,是由非对称加密算法保证的,这种算法在比特币中选择的是 ECDSA 算法,ECDSA 算法中选择的椭圆曲线名为 secp256k1。

而从公钥到地址,是由哈希算法保证的,在这一步使用了 SHA256 和 RIPEMD160。椭圆曲线加密算法 ECC 利用了“寻找离散对数”的难解性提供了单向不可逆性

在区块链上,一个比特币交易的产生由两部分组成,第一部分是签名加锁,对应到的是交易的输出、第二部分是解锁花费,对应到的是交易的输入,当我们构造一笔交易的时候必然会用到私钥,这是所有数字货币资产控制权由私钥保证的根本原因。

账户与交易模型

普通账户模型

当前余额是记录在某个地方的,只需要读出来即可,这种设计我们叫做账户余额模型

普通账户模型具有自定义数据类型的优点,但是却需要自己设计事务机制

UTXO 模型

UTXO 全称是:“Unspent Transaction Output”,这指的是:未花费的交易输出。这里面三个单词分别表示 “未花费的”“交易”“输出”

UTXO 的核心设计思路是无状态,它记录的是交易事件,而不记录最终状态,也就是说只记录变更事件,用户需要根据历史记录自行计算余额。

输入和输出组成了交易,输入和输入需要满足一些约束条件:

  1. 任意一个交易必须至少一个输入、一个输出;
  2. 输入必须全部移动,不能只使用部分,所以才产生了第二个输出指向村长自己;
    要自己设计事务机制
UTXO 模型

UTXO 全称是:“Unspent Transaction Output”,这指的是:未花费的交易输出。这里面三个单词分别表示 “未花费的”“交易”“输出”

UTXO 的核心设计思路是无状态,它记录的是交易事件,而不记录最终状态,也就是说只记录变更事件,用户需要根据历史记录自行计算余额。

输入和输出组成了交易,输入和输入需要满足一些约束条件:

  1. 任意一个交易必须至少一个输入、一个输出;
  2. 输入必须全部移动,不能只使用部分,所以才产生了第二个输出指向村长自己;
  3. 输入金额 = 输出金额之和 + 交易手续费,这里必须是等式。