比特币区块链系统
比特币区块链系统
比特币区块链系统架构
比特币节点分类
比特币系统的节点总体上可以分为“全节点”、“轻节点”等两大类,其中发挥核心作用的是全节点。
全节点是比特币系统中功能最完整的节点,全节点一般要求保持一直在线,主要负责执行以下功能:
- 参与区块记账权的竞争,通过PoW共识机制竞争下一个新区块的出块权,获得出块权的节点将获得系统激励,激励的方式就是奖励一定数量的比特币,也是比特币这种加密货币的唯一产生来源。
- 存储完整的区块链与账本数据,每个全节点都保存了一个相同区块链与账本数据副本,自2009年比特币系统上线运行至今,每个全节点的区块链与账本数据存储容量已超过500GB。
- 提供P2P网络路由与同步服务,监听系统网络上的交易、区块及事件信息,对接收到的交易和区块数据进行验证,并通过Gossip协议进行数据分发与同步。
轻节点主要负责提供交易和钱包功能不参与出块权的竞争计算,不会存储完整的区块链,只需要保存每个区块的区块头信息,以及与节点自身相关的交易信息,可以发起简单支付验证请求SPV(Simplified Payment Verification),向全节点请求数据来验证交易,也提供P2P网络的路由功能。
比特币区块链系统逻辑架构
比特币系统数据层
区块与区块链
在比特币系统中,区块(Block)是区块链系统中最基本的数据单元,用于表示和记录区块链系统一段时间内发生的交易和状态结果的数据结构,是区块链系统各节点竞争完成一次共识计算的结果,多个区块采用链式结构(子区块区块头中存储着父区块区块头的哈希)链接在一起就构成了区块链(Blockchain)。每个区块又由区块头(Block Head)和区块体(Block Body)两部分组成。
在区块链系统的所有区块中,第一个区块被称为“创世区块”(2009年1月3日,中本聪生成了创世区块,1月9日出现了序号为1的区块与创世区块相连,这标志着区块链的诞生)。每个区块中存储了一定数量的交易数据,都由交易发起人的数字签名来保证其真实性和合法性,从第二个区块开始,每个区块都保存了前一个区块(父区块)的区块头的哈希值,区块之间首尾相互连接就构成链式结构,因而先前区块里的任何数据都不可被篡改。
比特币系统每个区块的大小不超过1MB,区块整体数据结构如下表所示。
|字段|大小|描述|
|:—-:|:—-:|:—-:|
|区块大小|4字节|记录区块的大小,单位是字节|
|区块头|80字节|区块头信息|
|交易技术|1-9字节|区块中包括的交易的数量(因为区块的大小限制在1M,所以交易的数量远远无法超过这个值,一般包含2000到3000笔交易)|
|交易列表|可变大小|区块中包含的交易数据(交易的数量与交易的数据大小都是不确定的)|
每个区块的区块头的数据结构如下:
区块头的大小为80字节
|字段|大小|描述|
|:—-:|:—-:|:—-:|
|当前区块的哈希(hash)|32字节|当前区块的区块头的哈希值|
|区块链网络确认数(confirmations)|4字节|当前区块确认数|
|区块高度(height)|4字节|从创世区块到当前区块的长度|
|版本(version)|4字节|区块的版本,用于版本升级检查|
|前一个区块的哈希(previousblockhash)|32字节|前一个区块的区块头哈希|
|Merkel树根哈希(merkleroot)|32字节|对区块中包含的所有交易创建的一棵Merkle树,该Merkle树的根哈希值|
|区块的时间戳(time)|4字节|区块创建的时间|
|区块的难渡目标(bits/target)|4字节|竞争计算该区块的PoW工作量证明难度目标|
|随机数(nonce)|4字节|32位的任意随机数,竞争计算该区块的PoW工作量证明问题求解|
|区块链上的总计工作量(chainwork)|4字节|区块链上所有的区块的计算工作量|
我们可以发现以上的区块头的数据结构中包含的字段长度和是大于80字节的。实际上在区块中区块头实际包含的字段为:
- 版本(version) 4字节
- 父区块哈希(previous block hash)32字节
- Merkle树根哈希(merkle root)32字节
- 时间戳(time stamp)4字节
- 难度目标(bits)4字节
-随机数(nonce)4字节
账本数据
在一个传统的具有支付功能的系统中,每个用户都有一个资金账户,支付系统会对每个账户的余额进行单独地记录和管理。当系统中有用户之间发生了支付的交易,系统会分别对参与交易的账户的余额信息进行检查和修改。
例如,甲向乙转账50元,首先需要检查甲的账户中有50元的余额(这个先后顺序是重要的),再从甲的账户中扣除50元,并向乙的账户中添加50元。可以看到,为了保证整个系统的正确性,系统需要确保对应的支付前提条件,如甲的账户中至少有50元的余额,同时也需要保证整个支付交易过程的原子性、一致性、隔离性及持久性(Atomicity、Consistency、Isolation、Durability,ACID),即保证从甲账户扣减金额和向乙账户增加相同数量的金额这两个操作必须同时执行和完成,一旦受其他事件影响中断,甲和乙的账户必须恢复到交易前的状态(状态回滚)。
账户地址
在比特币系统中没有直接的“账户”概念,而是用“账户地址”来代表用户的账户,相当于银行卡卡号,任何人都可以通过你的账户地址给你转账比特币。
比特币的账户地址就是用户的公钥经过哈希计算及Base58编码运算后生成的160位(20字节)的字符串,账户地址计算生成流程。
账本数据模型UTXO
比特币系统账本没有采用传统的“账户/余额”模型(比特币区块链不会直接跟踪每个地址的比特币余额),而是提出了一种独特的UTXO(Unspent Transaction Output)未消费的交易输出模型,简称比特币UTXO模型。UTXO是一个包含交易数据和对应的执行代码的数据结构,所有的UTXO条目构成了比特币系统的“账本”。
UTXO模型的本质是通过交易记录来构成系统账本,而不是通过账户信息构成账本。在比特币的每一笔支付交易中,都有“交易输入 input”(标识资金来源)和“交易输出 output”(标识资金去向),且每个交易都可以有多个交易输入和多个交易输出,交易之间按照时间戳的先后顺序排列,且任何一个交易中的交易输入都是其前序的某个交易中产生的“交易输出”,而所有交易的最初的交易输入都来自比特币系统节点生成区块得到的激励(比特币)(即Coinbase交易(创币交易))。
比特币系统中的每个“账户”可以视为对应着某个地址,比特币区块链不会直接跟踪每个地址的比特币余额,而是在区块链中存储交易数据的整个历史,比特币系统通过“交易池”跟踪区块链网络中所有UTXO的集合,而某个地址在某个时间点所具有的“余额”,是通过检查、求和与该地址相关的所有UTXO来计算。当使用UTXO时,它将从交易池中被删除,这将实时在计算余额时反映出来。
交易数据结构
在比特币的每一笔交易数据中,都包含一个或多个“交易输入”(标识资金来源)、一个或多个“交易输出”(标识资金去向)、交易时间戳等信息。
比特币系统交易数据结构定义:
|字段|大小|描述|
|:—-:|:—-:|:—-|
|版本|4字节|当前交易的版本,用于版本升级检查,除非有重大升级的情况下,版本号基本无变化,是一个比较固定的值|
|交易哈希|32字节|当前交易的哈希值,唯一标识和索引该交易|
|交易输入|可变大小|交易的输入可能是一个或多个|
|交易输出|可变大小|交易的输出可能是一个或多个|
|锁定时间(多义字段)|4字节|当前交易被加到区块的最早时间,如果值为0,表示需要立即被加入到区块中;如果值大于0而小于5亿,表示区块高度;如果大于5亿就表示一个Unix时间戳|
交易输入的数据结构定义:
|字段|大小|描述|
|:—-:|:—-:|:—-|
|前置交易哈希|32字节|当前交易要使用的比特币金额来自之前交易的唯一标识|
|前置交易的输出序号|4字节|当前交易使用的比特币金额来自之前交易的输出序号|
|解锁脚本|可变大小|解锁脚本用于求解或满足要使用的比特币金额对应的锁定脚本|
交易输出的数据结构定义:
|字段|大小|描述|
|:—-:|:—-:|:—-|
|输出金额|8字节|当前交易输出的比特币金额,单位是聪(SAT),1比特币(BTC)= 1亿聪|
|锁定脚本|可变大小|锁定脚本会附上一个加密难题,规定将来要在使用这笔比特币金额时需要满足的条件|
OP_DUP:复制栈顶元素,并将副本放置于栈顶
OP_CHECKSIG:OP_CHECKSIG 是用于验证 tx 输入签名是否有效的脚本操作码。OP_CHECKSIG 希望堆栈上有两个值。按照堆栈深度的顺序,这两个值分别是公钥和脚本签名。这两个值通常是通过运行我们试图验证的事务输入的 scriptSig 脚本获得的。scriptSig 脚本运行后会被删除,但堆栈保持不变。然后,运行现在正在使用的上一个事务输出中的 scriptPubKey 脚本,通常以 OP_CHECKSIG 结束。
TODO: 为什么OP_CHECKSIG不需要被签名数据就可以验证(为了避免文章过于冗长,单开一篇,记得在这里放上链接)
OP_CHECKSIG
脚本的使用就是将解锁脚本与锁定脚本拼接,执行脚本。
例如:
解锁脚本:张三的
执行过程:
张三的\
张三的\
OP_DUP,复制张三的\
OP_HASH160,对位于栈顶的张三的\
张三的\
OP_EQUAL, 取出栈顶的两个元素进行比较,若相同,则移除元素,继续执行;若栈顶两元素不同,则中断执行,返回失败。 —> OP_CHECKSIG,检查签名,根据结果返回成功或失败(此时栈内元素尚有张三的\
状态数据
在比特币系统中,交易表示一次价值转移操作,会导致账本状态的一次改变,如增加了一条交易记录;区块表示记录一段时间内发生的交易和状态结果,是对当前账本状态的一次共识和确认;链是由一个个区块按照发生时间顺序串联而成,可以看作是整个区块链状态变化的日志记录。
默克尔树
TODO: 默克尔树单开一篇
比特币网络层
比特币网络采用非结构化P2P网络,不存在中心服务器。在区块链网络存在“全节点”和“轻节点”等,发挥不同作用的节点:
- 全节点是比特币网络中功能最完整的节点。全节点需要始终在线,参与出块竞争计算,存储整个区块链的完整信息,监听网络中传播的交易信息,收集并验证交易的是否合法,并且提供P2P网络的路由功能。
- 轻节点不需要参加出块竞争计算,只存储区块链的区块头信息,以及与自身有关的交易信息,可以发起简单支付验证请求(Simplified Payment Verification,SPV),向全节点请求数据来验证交易,并提供P2P路由功能。轻节点主要提供交易功能,但往往也提供钱包功能。
比特币系统的P2P网络基于TCP构建,默认RPC通信服务端口是8332,默认数据同步端口是8333,比特币系统的P2P网络主要采用了Gossip协议来实现节点发现、节点连接、区块广播、交易广播等功能。
非结构化P2P网络
TODO: 新开一篇什么是结构化P2P与非结构化P2P,记得放链接
节点发现管理
当一个新的比特币网络节点启动后,为了能加入比特币网络,它必须至少发现一个网络中的活动节点并与之建立连接。在比特币网络中,一个新启动的节点可以通过两种方式发现其它网络节点:
- (1) 利用DNS种子节点
比特币系统的客户端会维护一个列表,列表中记录了长期稳定运行的节点(这些节点一般由区块链社区维护),这些节点也被称之为DNS(中心化域名查询服务)种子(DNS-seed)。新节点可以连接到种子节点,快速发现网络中的其它节点。DNS种子节点一般由比特币社区成员维护,种子节点通过自动扫描网络获取活跃节点的IP地址,如果某节点运行在默认的8332端口,将被添加到种子节中。
注意:DNS种子节点查询结果是未被认证的,而且恶意节点操作者或网络中间人攻击者可以只返回被攻击者控制的节点的IP地址,在攻击者自己的网络中隔离程序,并且允许攻击者壮大它的交易和区块。 - (2) 节点引荐
新启动节点可以不使用种子节点,而是指定一个节点的IP地址,新节点将与指定的节点建立连接,并将指定的节点作为DNS种子节点,在引荐信息形成之后断开与该节点的连接,并与新发现的节点连接。
在比特币系统中,节点通过发送version消息连接其它节点,该消息一般包含以下信息:
- PROTOCOL_VERSION:当前节点的比特币P2P协议的版本号
- nLocalServices:节点支持的本地服务列表
- nTime:当前时间戳
- addrYou:当前节点可见的远程节点的IP地址
- addrMe:当前节点的本地IP地址
- subver:当前节点运行的软件的子版本号
- baseHeight:当前节点上的区块链的高度
如果接收到version消息的节点愿意与当前节点建立对等连接,则会发回一个verack消息,并发送version消息,等待两次握手成功之后,则两节点间建立起对等连接。
节点之间一旦连接,新节点将会发送一条包含自己IP地址的addr消息给对等节点,对等节点收到以后又会向与它连接的相邻节点发送addr消息,这样新节点的IP地址就会在P2P网络进行广播。此外,新节点还可以发送getaddr消息,要求对等节点把已知的节点的IP地址发送过来。通过这种方式,新节点可以找到需要连接的其它对等节点。
为了维持与对等节点的连接,节点默认情况下每 30 分钟内会给对等节点至少发送一次信息。如果超过 90 分钟没有收到回复,节点会认为连接已经断开。
数据分发与同步
交易广播
在比特币系统中,节点为了向比特币系统发送一笔交易,需要向邻近的对等全节点发送Inv消息(即Inventory消息,是比特币网络协议中的一种消息类型。当一个节点发现一个新的交易或区块时,它会将这个对象的哈希值包装在一个Inv消息中,然后将这个Inv消息广播给它的对等节点。这样,其他节点就知道了有一个新的交易或区块被发现。)。如果接收到对等节点返回的GetData消息,节点再使用Tx消息(即Transaction消息,也是比特币网络协议中的一种消息类型。当一个节点收到一个Inv消息并发现它还没有存储相关的交易或区块时,它会向发送Inv消息的节点发送一个GetData消息,请求完整的交易或区块数据。然后,发送Inv消息的节点会将完整的交易或区块数据包装在一个Tx消息中,发送给请求的节点。)向对等节点发送交易信息。对等节点接收到交易信息后,将以同样的方式向其它邻近节点转发交易信息。
交易池
比特币网络中每个节点都会维护一个未确认交易列表,称为“交易池”。节点使用交易池记录并跟踪等待被区块链系统确认的交易。例如,具有钱包功能的节点会使用交易池来记录那些已发送到网络但还未被确认的,只与该节点上的钱包相关的预支付交易信息。
某些节点还维护一个单独的“孤立交易池”。所谓“孤立交易”是指,如果一个交易的输入与某未知的交易有关,如与缺失的父交易相关,该孤立交易就会被暂时存储在孤立交易池中直到父交易的信息到达。当一个交易被添加到交易池时,会同时检查孤立交易池,看是否有某个孤立交易引用了此交易的输出(子交易)。任何匹配的孤立交易会被进行验证。如果验证有效,它们会从孤立交易池中删除,并添加到交易池中。
区块广播与同步
当节点挖出一个新的区块时,使用以下方式将其发送给其他区块
- 出块节点将区块信息广播给所有已知的全节点
- 节点A验证收到的区块信息
- 若节点A验证区块合法,则向节点A的临近节点广播Inv消息(此时Inv消息中并不包含区块具体信息,而是只包含节点A验证过的区块的区块头等相关信息)
- 节点B接收到节点A发送的Inv消息,若节点B确认从未收到过这个区块信息,则向节点A发送Getdata消息,要求得到交易记录,以及区块的具体信息
- 节点A收到Getdata消息后将具体的区块信息发送给节点B(这一系列的消息验证都是为了减少网络带宽消耗)
- 由于节点A已经验证区块信息,节点A在此区块后继续进行竞争计算。节点B验证区块信息后重复以上过程
Gossip协议
TODO: 单开一篇
比特币区块链系统共识层
在比特币系统中,采用了PoW(Proof of Work)工作量证明共识机制,比特币网络节点如果想生成一个新的区块并写入区块链,必须通过求解工作量证明难题来竞争分布式账本的记账权,在节点成功求解出难题后,会马上将新区块对全网进行广播,网络的其它节点收到广播后,会立刻对其进行验证。如果验证通过,则表明已经有节点已求解出难题,获得当前区块的记账权,网络节点就选择接受这个新区块,记录到节点本地的账本中,然后进行下一个区块的竞争计算。
比特币系统通过共识机制竞争计算生成新区块的过程,被称为“挖矿(Mine)”,因此比特币网络节点又被称为“矿工(Miner)”,后续如以太坊等其它区块链系统都延用了这种说法。
比特币区块链系统PoW共识机制
比特币区块链系统PoW的特点
比特币系统的PoW共识机制具有两大特点:
- 比特币系统PoW共识机制采用的“难题”具有难以解答,但很容易验证答案的正确性的特点,同时求解难题的“难度”,即比特币网络节点平均解出一个难题所消耗时间,是可以通过调整难题中的部分参数来进行控制的,因此比特币系统可以很好地控制链增长的速度;
- 通过控制区块链的增长速度,保证了如果一个节点成功解出难题完成了新区块的创建,该区块能够以更快的速度在所有节点之间传播,并且得到其他节点的验证,再结合比特币系统所采取的“最长链有效”的评判机制,就能够在大多数(超过比特币网络51%算力)节点都是诚实的情况下,避免恶意节点对区块链的控制。
竞争出块冲突
比特币系统的PoW共识机制在运行过程中,有可能出现两个或多个不同的节点不分先后求解出满足要求的哈希值结果,都认为自己获得了出块权,并向系统网络广播自己生成新区块,这将导致节点竞争出块冲突。
比特币区块链激励层
在比特币系统中,比特币网络约每10分钟生成一个不超过1MB大小的区块,用于记录这10分钟内发生的验证过的交易内容,并将区块串联到最长的链尾部,每个区块的成功提交者可以得到系统一定数量的比特币的奖励(该奖励将作为区块内的第一个CoinBase交易,并将在一定区块数后才能使用),以及用户附加到交易上的支付服务费用。因此,即使没有任何用户交易,比特币网络也可以自行产生合法的区块并生成奖励。每个区块的奖励最初是50个比特币,每隔21万个区块(约4年时间)自动减半,最终比特币总量稳定在2100万个(即使永远也不会到达这个值)。
比特币系统的激励机制主要包括以下要点:
- 加密货币总量固定:比特币总量不超过 2100 万个。
- 出块激励:每当有节点获得一个区块的记账权,比特币系统就会发行出新的比特币作为对节点的奖励。一个区块产生的比特币数量都会按几何级数递减,每产出 21 万个区块,获得奖励的比特币数量就会减少 50% 。截至2022年,每个区块的奖励已降低为6.25个比特币,是比特币系统上线时的八分之一。
- 交易激励:用户会在交易中包含交易费,作为处理交易的服务费支付给获得区块记账权的节点。