第一章 介绍

传统货币的防止双花,必须在一个中心化(centralized)的清算所(clearing house)里清算。比特币则用全局选举的机制达到共识,来清算双花问题。本质上来讲,就是把一个固定的中心化的清算过程,用选举的方式转化为无数个去中心化的局部清算过程。这就弥补了在比特币之前出现的数字货币的缺点。 clearing 在比特币网络里就被称作 mining 。

钱包是保存地址和管理密钥的地方。不要害怕公开自己的钱包地址。

全节点可以获取全部的交易信息,也因此可以验证交易,发出交易。硬件钱包是运转在专有硬件上的节点(树莓派?),冷钱包就是纸一类的东西。离线钱包是非常安全的。

第二章 比特币原理

比特币最多可以分割到一亿分之一的大小。

Transaction就像是复式记账法账簿上的行,即每行都有输入和输出。

简单来说,每一笔交易包含一个或多个“输入”,输入是针对一个比特币账号的提款(而不是债务)。 这笔交易的另一面,有一个或多个“输出”,被当成信用点数记入到比特币账户中。这些输入和输出的总额(负债和信用)不需要相等。相反,当输出累加略少于输入量时,两者的差额就代表了一笔隐含的“矿工费”,这也是将交易放进账 簿的矿工所收集到的一笔小额支付。
此处输入图片的描述

简而言之,每个交易的输出是下一个交易的输入,意味着交易是编号的。

交易是被编号的输入输出

比特币中的找零(change),是经常会被发送到钱包的新地址里(出于隐私原因),或者返回原地址(出于默认配置)。

化零为整的例子:

此处输入图片的描述

化整为零的例子(发工资):

此处输入图片的描述

钱包可以离线生成transaction。

Alice只需要指定目标地址和金额,其余的 细节钱包应用会在后台自动完成。很重要的一点是,钱包应用甚至可以在完全离线时建立交易。就像在家里写张支票, 之后放到信封发给银行一样,比特币交易建立和签名时不用连接比特币网络。只有在执行交易时才需要将交易发送到网络。

钱包只存储与本地址相关 transaction,是所有数据的子集,是 filtered 过的。

大多数钱包应用跟踪着钱包中某个地址的所有可用输出。

查询某个地址的 UTXO,可以看到transaction 的格式,注意看,value的单位是聪(和以太坊一样,以太坊的value最小单位是伟):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
curl https://blockchain.info/unspent?active=1Cdid9KFAaatwczBwBttQcwXYCpvK8h7FK
{
"unspent_outputs":[
{
"tx_hash":"186f9f998a5...2836dd734d2804fe65fa35779",
"tx_index":104810202,
"tx_output_n": 0,
"script":"76a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a888ac",
"value": 10000000,
"value_hex": "00989680",
"confirmations":0
}
]
}

这个格式里的 script就是锁定这个 UTXO的关键。这是一个留给未来使用这个UTXO的使用者的谜题-谁能拿出一个signature,可以和这个script里提供的锁定地址(也就是交易里的价值获得者)相匹配,就可以得到使用这个UTXO的权力。但这个script字段是什么意思呢?

value_hex 是 value 的16进制值。

私钥可以产生公钥,公钥可以散列出地址。交易里应该同时存在地址、签名和公钥。

交易被包在一起放进区块中时需要极大的计算量来证明,但只需少量计算 就能验证它们已被证明。

挖矿在构建区块时会创造新的比特币,和一个中央银行印发新的纸币很类似。

描述挖矿的一个好方法是将之类比为一个巨大的多人数独谜题游戏。一旦有人发现正解之后,这个数独游戏会自动调整 困难度以使游戏每次需要大约10分钟解决。想象一个有几千行几千列的巨大数独游戏。如果给你一个已经完成的数独, 你可以很快地验证它。然而,如果这个数独只有几个方格里有数字其余方格都为空的话,就会花费非常长的时间来解 决。这个数独游戏的困难度可以通过改变其大小(更多或更少行列)来调整,但即使它非常大时验证它也是相当容易 的。而比特币中的 “谜题” 是基于哈希加密算法的,其展现了相似的特性:非对称地,它解起来困难而验证很容易,并且它的困难度可以调整。

新交易不断地从用户钱包和应用流入比特币网络。当比特币网络上 的节点看到这些交易时,会先将它们放到各自节点维护的一个临时的未经验证的交易池中。。当矿工构建一个新区块时, 会将这些交易从这个交易池中拿出来放到这个新区块中,然后通过尝试解决一个非常困难的问题(也叫工作量证明)以 证明这个新区块的合法性。

工作量证明是为了证明这个区块是合法的。

这些交易被加进新区块时,以交易费用高的优先以及其它的一些规则进行排序。矿工一旦从网络上收到一个新区块时, 会意识到在这个区块上的解题竞赛已经输掉了,会马上开始下一个新区块的挖掘工作。它会立刻将一些交易和这个新区块的数字指纹放在一起开始构建下一个新区块,并开始给它计算工作量证明。每个矿工会在他的区块中包含一个特殊的交易,将新生成的比特币(当前每区块为12.5比特币)作为报酬支付到他自己的比特币地址,再加上块中所有交易的交易费用的总和作为自己的报酬。如果他找到了使得新区块有效的解法,他就会得到这笔报酬,因为这个新区块被加入到了总区块链中,他添加的这笔报酬交易也会变成可消费的。

手续费(fee)的排序是存在的。

Alice的交易被网络拿到后放进未验证交易池中。一旦被挖矿软件验证,它就被包含在由Jing的采矿池生成的新块(称为候选块)中。 参与该采矿池的所有矿工立即开始计算候选块的工作证明。大约 在Alice的钱包第一次将这个交易发送出来五分钟后,Jing的ASIC矿机发现了新区块的正解并将这个新区块发布到网络上后,一旦被其它矿机验证,它们就会立即投身到构建新区块的竞赛中。

所谓的候选区块也是存在的。

全索引客户端可以追钱款的来 源,从第一次有比特币在区块里生成的那一刻开始,按交易与交易间的关系顺藤摸瓜,直到Bob的交易地址。

特别地,轻客户端通过确认交易在哪个区块,以及后面还有多少个区块来确认一个支付的合法性。

在大多数情况下,UTXO总是是很难整齐地被拿来支付某些支出。总是很容易出现聚合(aggregating)几个 UTXO 用来支付一笔交易的情况。

第三章 比特币核心

最初的中本聪实现已经被吸收进了比特币核心,是其他比特币系统的参考实现。在这里bitcoin core就不是核心团队,而是核心系统了。

比特币核心架构图如下:

比特币核心架构图

不要使用比特币核心的钱包,应该使用支持标准(如BIP-39和BIP-32)的现代钱包。

在18年出,比特币核心需要2g的内存和160g的硬盘。比特币核心的节点会索引交易数据,这样应该可以提升一些搜索交易的速度。在下载完2009年以来全部的交易以前,比特币核心节点无法处理交易和更新余额(查账都做不到?)。

不想依赖其他人而验证交易的人总是需要一个自己的节点。

获取一个交易并且对它进行解码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
$ bitcoin-cli decoderawtransaction 0100000001186f9f998a5aa6f048e51dd8419a14d8
a0f1a8a2836dd734d2804fe65fa35779000000008b483045022100884d142d86652a3f47ba474
6ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298
cad530a863ea8f53982c09db8f6e381301410484ecc0d46f1918b30928fa0e4ed99f16a0fb4fd↵
e0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa↵
336a8d752adfffffffff0260e31600000000001976a914ab68025513c3dbd2f7b92a94e0581f5
d50f654e788acd0ef8000000000001976a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8
88ac00000000

{
"txid": "0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2",
"size": 258,
"version": 1,
"locktime": 0,
"vin": [
{
"txid": "7957a35fe64f80d234d76d83a2...8149a41d81de548f0a65a8a999f6f18",
"vout": 0,
"scriptSig": {
"asm":"3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1decc...",
"hex":"483045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1de..."
},
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.01500000,
"n": 0,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 ab68...5f654e7 OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a914ab68025513c3dbd2f7b92a94e0581f5d50f654e788ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": [
"1GdK9UzpHBzqzX2A9JFP3Di4weBwqgmoQA"
]
}
},
{
"value": 0.08450000,
"n": 1,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 7f9b1a...025a8 OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a888ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": [
"1Cdid9KFAaatwczBwBttQcwXYCpvK8h7FK"
]
}
}
]
}

交易的实际存储总是十六进制编码。而总能解码为一个 JSON 对象。
注意看上面的交易细节,一个输入产生了两个输出,一个是真实转账,一个是找零转账,实际上应该也可以支持多个输入。vin则是上一次交易,可以看到上一次交易也有散列形式的txid。

提示交易ID在交易被确认之前不具有权威性。 在区块链中缺少交易哈希并不意味着交易未被处理。 这被称为“交易可扩展性”,因为在块中确认之前可以修改交易哈希。 确认后,txid是不可改变的和权威的。

这段话的意思,恐怕是赞同交易可扩展性的,但交易可扩展性明明是有问题的。

块可以由块高度或块哈希引用。

用块高度可以得到块散列,用块散列可以得到块内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$ bitcoin-cli getblock 0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b3↵
1b2cc7bdc4

{
"hash": "0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4",
"confirmations": 37371,
"size": 218629,
"height": 277316,
"version": 2,
"merkleroot": "c91c008c26e50763e9f548bb8b2fc323735f73577effbc55502c51eb4cc7cf2e",
"tx": [
"d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f",
"b268b45c59b39d759614757718b9918caf0ba9d97c56f3b91956ff877c503fbe",
"04905ff987ddd4cfe603b03cfb7ca50ee81d89d1f8f5f265c38f763eea4a21fd",
"32467aab5d04f51940075055c2f20bbd1195727c961431bf0aff8443f9710f81",
"561c5216944e21fa29dd12aaa1a45e3397f9c0d888359cb05e1f79fe73da37bd",
[... hundreds of transactions ...]
"78b300b2a1d2d9449b58db7bc71c3884d6e0579617e0da4991b9734cef7ab23a",
"6c87130ec283ab4c2c493b190c20de4b28ff3caf72d16ffa1ce3e96f2069aca9",
"6f423dbc3636ef193fd8898dfdf7621dcade1bbe509e963ffbff91f696d81a62",
"802ba8b2adabc5796a9471f25b02ae6aeee2439c679a5c33c4bbcee97e081196",
"eaaf6a048588d9ad4d1c092539bd571dd8af30635c152a3b0e8b611e67d1a1af",
"e67abc6bd5e2cac169821afc51b207127f42b92a841e976f9b752157879ba8bd",
"d38985a6a1bfd35037cb7776b2dc86797abbb7a06630f5d03df2785d50d5a2ac",
"45ea0a3f6016d2bb90ab92c34a7aac9767671a8a84b9bcce6c019e60197c134b",
"c098445d748ced5f178ef2ff96f2758cbec9eb32cb0fc65db313bcac1d3bc98f"
],
"time": 1388185914,
"mediantime": 1388183675,
"nonce": 924591752,
"bits": "1903a30c",
"difficulty": 1180923195.258026,
"chainwork": "000000000000000000000000000000000000000000000934695e92aaf53afa1a",
"previousblockhash": "0000000000000002a7bbd25a417c0374cc55261021e8a9ca74442b01284f0569",
"nextblockhash": "000000000000000010236c269dd6ed714dd5db39d36b33959079d78dfd431ba7"
}

当然除了命令行的方式,也可以有各种各样的客户端来实现比特币的全节点。

第四章 密钥和地址

现代密码学的用处:

  • 秘密写作
  • 用签名来证明秘密
  • 用数字指纹来证明数据真实性

具有讽刺意味的是,加密不是比特币的重要组成部分,因为它的通信和交易数据没有加密,也不需要加密来保护资金。

公开通信,公开存储,所以所有人都可以查看。

大多数比特币交易都需要一个有效的签名才会被存储在区块链。只有有效的密钥才能产生有效的数字签名,因此拥有~密钥副本就拥有了对该帐户的比特币的控制权。用于支出资金的数字签名也称为见证(witness),密码术中使用的术语。 比特币交易中的见证数据证明了所用资金的真正归谁所有。

有签名才有见证,隔离见证即隔离签名相关部分。

密钥是成对出现的,由一个私钥和一个公钥所组成。公钥就像银行的帐号,而私钥就像控制账户的PIN码或支票的签名。

一般而言,由公钥生成的地址对应公钥。但比特币地址并不只能代表公钥,它还可以代表其他支付对象,譬如脚本。比特币地址把收款方抽象起来了。

公钥加密发明于上个世纪70年代。

素数幂和椭圆曲线乘法函数,在数学上都是不可逆的。

在比特币系统中,我们用公钥加密创建一个密钥对,用于控制比特币的获取。密钥对包括一个私钥,和由其衍生出的唯 一的公钥。公钥用于接收比特币,而私钥用于比特币支付时的交易签名。

私钥->公钥->地址。

公钥和私钥之间的数学关系,使得私钥可用于生成特定消息的签名。此签名可以在不泄露私钥的同时对公钥进行验证,大致是通过OP_CHECKSIG一类操作码。

支付比特币时,比特币的当前所有者需要在交易中提交其公钥和签名(每次交易的签名都不同,但均从同一个私钥生 成)。比特币网络中的所有人都可以通过所提交的公钥和签名进行验证,并确认该交易是否有效,即确认支付者在该时刻对所交易的比特币拥有所有权。

我们通常可以随机选择一个数字(k)作为私钥,然后通过椭圆曲线相乘算法得到一个公钥(K)。然后使用一个单向散列函数得到一个地址(A)。

私钥生成公钥,公钥生成地址

为什么在比特币中使用非对称密码学? 它不是用于“加密”(make secret)交易。相反,非对称密码学的有用属性是生成数字签名的能力。 可以将私钥应用于交易的数字指纹以产生数字签名。 该签名只能由知晓私钥的人生成。

换言之,只有非对称加密才能在确保私钥保密的情况下产生签名。私钥就是一个随机选出的数字而已。一个比特币地址中的所有资金的控制取决于相应私钥的所有权和控制权。

你可以用硬币、铅笔和纸来随机生成你的私钥:掷硬币256次,用纸和笔记录正反面并转换为0和1,随机得到的256位二进制数字可作为比特币钱包的私钥。该私钥可进一步生成公钥。

生成一个比特币私钥在本质上与“在1 到2^256之间选一个数字”无异。

更准确地说,私钥可以是1和n-1之间的任何数字,其中n是一个常数(n=1.158 * 10^77,略小于2^256),并被定义为由比特币所使用的椭圆曲线的阶(见椭圆曲线密码学解释)。要生成这样的一个私钥,我们随机选择一个256位的数字,并检查它是否小于n-1。从编程的角度来看,一般是通过在一个密码学安全的随机源中取出一长串随机字节,对其使用SHA256哈希算法进行运算,这样就可以方便地产生一个256位的数字。如果运算结果小于n-1,我们就有了一个合适的私钥。否则,我们就用另一个随机数再重复一次。

要使用比特币核心客户端生成一个新的密钥,可使用 getnewaddress 命令。出于安全考虑,命令运行后只 显示生成的公钥,而不显示私钥。如果要bitcoind显示私钥,可以使用 dumpprivkey 命令。 dumpprivkey 命令会把私钥以 Base58校验和编码格式显示,这种私钥格式被称为钱包导入格式(WIF,Wallet Import Format)

为什么与私钥签名有关的编码都是 Base58 编码?

通过椭圆曲线乘法可以从私钥计算得到公钥,这是不可逆转的过程:K = k * G 。其中k是私钥,G是被称为生成点的常数点,而K是所得公钥。其反向运算,被称为“寻找离散对数”——已知公钥K来求出私钥k——是非常困难的,就像去试验所有可能的k值,即暴力搜索。在演示如何从私钥生成公钥之前,我们先稍微详细学习下椭圆曲线密码学。

因为所有比特币用户的生成点是相同的,一个私钥k乘以G将 得到相同的公钥K。

比特币的地址总是1开头的。它本质上只是公钥的指纹。公钥的计算方法是双层散列。以公钥 K 为输入,计算其SHA256哈希值,并以此结果计算RIPEMD160 哈希值,得到一个长度为160位(20字节)的数字:

A = RIPEMD160(SHA256(K))

但我们常见的比特币地址还更近一层,使用了一个 Base58Check 的机制,提供了一个可读的、有校验和的版本。

公钥生成地址的过程

Base58是Base64编码格式的子集,同样使用大小写字母和10个数字,但舍弃了一些容易错 读和在特定字体中容易混淆的字符。具体地,Base58不含Base64中的0(数字0)、O(大写字母o)、l(小写字母 L)、I(大写字母i),以及“+”和“/”两个字符。简而言之,Base58就是由不包括(0,O,l,I)的大小写字母和数字组成。

为了增加防止打印和转录错误的安全性,Base58Check是一种常用在比特币中的Base58编码格式,比特币有内置的检查错误的编码。检验和是添加到正在编码的数据末端的额外4个字节。校验和是从编码的数据的哈希值中得到的,所以可以用来检测并避免转录和输入中产生的错误。使用Base58check编码时,解码软件会计算数据的校验和并和编码中自带的校验和进行对比。二者不匹配则表明有错误产生,那么这个Base58Check的数据就是无效的。一个错误比特币地址就不会被钱包软件认为是有效的地址,否则这种错误会造成资金的丢失。

换言之,比特币地址的最后两个8个字符是校验和?

为了将数据(数字)转换成Base58Check格式,首先我们要对数据添加一个称作“版本字节”的前缀,这个前缀用来识别编码的数据的类 型。例如,比特币地址的前缀是0(十六进制是0x00),而对私钥编码时前缀是128(十六进制是0x80)。 表4-1会列出一些常见版本的前缀。

接下来,我们计算“双哈希”校验和,意味着要对之前的结果(前缀和数据)运行两次SHA256哈希算法:

checksum = SHA256(SHA256(prefix+data))

在产生的长32个字节的哈希值(两次哈希运算)中,我们只取前4个字节。这4个字节就作为检验错误的代码或者校验和。校验码会添加到数据之后。

为什么头四个字节就可以拿来当校验和并说明数据变化?

所以比特币的 Base58Check 地址,是一个加头(1字节)加尾(四字节)的数据。

Base58Check 的过程
注意,这个图的上半部分是增加Check的过程,加头加尾以后还要 Base58 Encoding才行。

Base58Check编码的比特币地址是以1开头的,而Base58Check编码的私钥WIF是以5开头的。下图展示了一些版本前缀和他们对应的Base58格式:

地址前缀和对应的Base58编码

压缩格式公钥渐渐成为了各种不同的比特币客户端的默认格式,它可以大大减少交易所需的字节数,同时也让存储区块链所需的磁盘空间变小。

BIP0038提出了一个通用标准,使用一个口令加密私钥并使用Base58Check对加密的私钥进行编码,这样加密的私钥就可以安全地保存在备份介质里,安全地在钱包间传输,保持密钥在任何可能被暴露情况下的安全性。这个加密标准使
用了AES,这个标准由NIST建立,并广泛应用于商业和军事应用的数据加密。

BIP0038加密方案是:输入一个比特币私钥,通常使用WIF编码过,base58chek字符串的前缀“5”。此外BIP0038加密方案需要一个长密码作为口令,通常由多个单词或一段复杂的数字字母字符串组成。BIP0038加密方案的结果是一个由
base58check编码过的加密私钥,前缀为6P。如果你看到一个6P开头的的密钥,这就意味着该密钥是被加密过,并需要一个口令来转换(解码)该密钥回到可被用在任何钱包WIF格式的私钥(前缀为5)。许多钱包APP现在能够识别
BIP0038加密过的私钥,会要求用户提供口令解码并导入密钥。第三方APP,诸如非常好用基于浏览器的Bit Address ,
可以被用来解码BIP00038的密钥。

以3开头的地址,是P2SH(Pay-to-Script Hash),有时候被错误地称为多重签名或多重签名地址。它指定比特币交易的受益人,为脚本的散列(hash of script),而不是公钥的主人,也就不同于P2PKH(Pay-to-Public-Key-Hash)。

本质上,谁能解锁一个UTXO,谁就拥有了特定比特币的所有权。解锁的脚本,就是满足UTXO要求的过程。传统的UTXO解锁的过程就是提供pubkeyhash和验证signature。但P2Sh的解锁要求是由脚本决定的。

不同于P2PKH交易发送资金到传统1开头的比特币地址,资金被发送到3开头的地址时,需要的不仅仅是一个公钥的哈希值和一个私钥签名作为所有者证明。在创建地址的时候,这些要求会被指定在脚本中,所有对地址的输入都会被这些要求阻隔。

一个P2SH地址从交易脚本中创建,它定义谁能消耗这个交易输出(后面“P2SH(Pay-to-Script-Hash)”一节对此有 详细的介绍)。编码一个P2SH地址涉及使用一个在创建比特币地址用到过的双重哈希函数,并且只能应用在脚本而不是公钥:

script hash = RIPEMD160(SHA256(script))

产生的脚本哈希由Base58Check版本前缀为5的版本、编码后得到开头为3的编码地址。举例如下:

1
2
3
4
5
$ echo dup hash160 [ 89abcdefabbaabbaabbaabbaabbaabbaabbaabba ] equalverify checksig > script

$ bx script-encode < script | bx sha256 | bx ripemd160 | bx base58check-encode --version 5

3F6i6kwkevjR7AsAd4te2YB2zZyASEm1HM

可以看出这就是把一段解锁代码,放进一个脚本里面,再对脚本进行散列,而不是直接对公钥进行散列求地址的方法。

P2SH函数最常见的实现是多重签名地址脚本,但也可能编码其他类型的交易脚本。多重签名地址脚本,顾名思义,底层脚本需要多个签名来证明所有权,此后才能消费资金。设计比特币多重签名特性是需要从总共N个密钥中需要M个签名(也被称为“阈值”),被称为M-N多签名,其 中M是等于或小于N。

靓号(Vanity)地址包含了人类可读信息的有效比特币地址。

第五章 钱包

非确定性钱包只是一把私钥,Just a Bunch Of Keys。JBOK。这种钱包难以备份和保存私钥,因为私钥之间没有相互联系。

而确定性钱包,特别是分层确定性钱包,却可以通过一个最初的种子,还原出全部的钱包结构来。分层确定性钱包一个母扩展就可以产生40亿对密钥(一半普通密钥,一半强密钥),还可以借此衍生出更多孙密钥。生成密钥的方法,还是单项离散方程从公共的种子生成衍生私钥。

第六章 交易

比特币交易是比特币系统中最重要的部分。根据比特币系统的设计原理,系统中任何其他的部分都是为了确保比特币交易可以被生成、能在比特币网络中得以传播和通过验证,并最终添加入全球比特币交易总账簿(比特币区块链)。比特币交易的本质是数据结构,这些数据结构中含有比特币交易参与者价值转移的相关信息。比特币区块链是一本全球复式记账总账簿,每个比特币交易都是在比特币区块链上的一个公开记录。

我们在各种比特币应用程序用户界面中看到的大多数高级结构实际上并不存在于比特币系统中。

一个常见的示例交易脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"version": 1,
"locktime": 0,
"vin": [
{
"txid":"7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
"vout": 0,
"scriptSig": "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.01500000,
"scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY OP_CHECKSIG"
},
{
"value": 0.08450000,
"scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG",
}
]
}

这里的vin是一个数组。它的每个对象的对象的含义如下:

1
2
3
4
5
6
7
8
9
 {    # 引用的交易 id
"txid":"7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
# 交易中的某个vout。
"vout": 0,
# 解锁脚本
"scriptSig": "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
# 序列号
"sequence": 4294967295
}

由 txid 和 vout 定位一个被引用交易的 vout。vout如果没有被用过,那它就是一个UTXO。整个区块链上拥有一个动态变化的UTXO集,有新的UTXO产生,UTXO集就会变大,有旧的UTXO被用掉,则UTXO集就会变小。

比特币可以再分,UTXO却不可以再分,UTXO 总是作为一个整体被消耗掉的。所以在支付的时候,钱包会优先地自动选择自己能够碰触到的UTXO来拼凑出交易的输入来。如果不用编程的方式的话,用户无法手动选择要使用的UTXO。交易总是输入 UTXO 而产生 UTXO 的,如果输入的 UTXO 含有的价值超出了真正要支付的价值,则必然产生找零的 UTXO(收入比特币地址依然是转出比特币地址的 UTXO)。如果没有找零的UTXO,则输入和输出的差值全部变成矿工费。

交易费即输入总和减输出总和的余量:交易费 = 求和(所有输入) - 求和(所有输出)

换言之,如果所有的输入和所有的输出的差值为0,怎会产生0矿工费用的交易。矿工会自己决定是否要确认0手续费的交易。实际上区块链的矿工收费的标准并不是按传递的价值来计算的,而是按照产生的交易占用的字节大小来计算的。在现代的矿工节点中,低手续费的交易甚至不会被节点传递(relay)到区块链网络的其他部分中。在Bitcoin Core里,有一个可以被修改的参数 minrelaytxfee,决定了矿工将以什么标准传递交易。事实上,越多散碎 UTXO 的交易越复杂,占用的字节数越多,矿工就倾向于收取越贵的费用。

一个 UTXO 实际上就是 vout 的形式,如:

1
2
3
4
5
6
7
8
"vout": [
{
# 我们在其他地方看到的比特币价值总是以聪(satoshi /)为单位的,但这里是以比特币为单位解码的。
"value": 0.10000000,
# 锁定脚本
"scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG"
}
]

有意思的是,并不是一定要有 vin 才有 vout,coinbase 交易就是只有 vout 才有接下来的 vin 的。vout 是鸡,vin 是蛋。

好了,来谈谈最关键的解锁脚本和锁定脚本吧。

脚本公钥(scriptPubKey) == “锁定脚本”(locking script)== 见证脚本(witness script) == 加密难题(cryptographic puzzle)

解锁脚本(unlocking script) == 脚本签名(ScriptSig) == 见证(witness)

这些术语在不同的抽象层次上都意味着同样的东西。在transaction的字段里面,这些脚本都是script开头的。

这些脚本都是以类似 Forth 的基于堆栈的逆波兰表达式语言书写的。经过深思熟虑的安全考量,这些脚本被特意设计成不可循环或使用复杂控制流的非图灵完备语言,这就意味着有限的复杂性和可预见的执行次数(可见图灵完备的语言天然就无法抗停机问题)。这样就不可能设计出逻辑炸弹,滥用比特币网络的能力进而进行“拒绝服务攻击”。

所有的脚本都会以去中心化的形式在每个节点上执行,所有执行脚本所需要的信息都包含在脚本中。这实际上就是去中心化共识机制的基石了。

解锁一笔 UTXO 交易的过程,大概就是在一个逻辑栈上先执行解锁脚本,再执行锁定脚本的过程

解锁脚本加上锁定脚本

  1. 栈是先进后出的,所以会先把交易签名和公钥入栈(实际上这就在链上暴露了公钥,所以公钥一定不是秘密)。

  2. 然后执行OP_DUP,在栈顶复制一份公钥。
    然后执行OP_HASH160,进行双层散列,即RIPEMD160(SHA256(K))操作,但没有做加头加尾的BASE58CHECK操作,也就是说离比特币收款地址还有一步之遥。

  3. 把这个加密谜题最关键的部分,PubKHash 放入栈上作为第二个操作数。

  4. 执行 OP_EQUALVERIFY 操作,如果结果为TRUE,移除两个用来比较的操作数。

  5. 此时栈上还是原封不动的sig和 PubK,然后用 OP_CHECKSIG,进行ECDSA的验证签名,把结果的 TRUE 或者 FALSE 留在栈顶,作为验证的最终结果。

从栈的角度来看脚本执行的过程

这个加密谜题的本质,就是把一个完整的栈上可递归的等式的左半部分撕掉,只留下右半部分作为谜题。而右半部分里的PubKHash还可以拿来 BASE58CHECK 编码一下,作为比特币钱包地址的凭据。

上面描述的解锁过程,是一个典型的 P2PKH(Pay-to-Public-Key-Hash)过程。当然我们还可以在上面再进行更复杂的演化,通过制造更强的P2SH等机制,来支持脚本的可变化锁定脚本,因此产生了可变化的解锁脚本。

不包含P2PKH的交易也可以被确认。

每天都有数百个不包含P2PKH输出的交易在块上被确认。 blockchain浏览器经常向他们发出红色警告信息,表示无法解码地址。以下链接包含未完全解码的最新的“奇怪交易”:https://blockchain.info/strange-transactions。

这里的sig,是私钥加交易的具体信息的签名。大致是sig(transaction, private key)的结果。数字签名是用于证明数字消息或文档的真实性的数学方案。 有效的数字签名给了一个容易接受的理由去相信:1)该消息是由已知的发送者(身份认证性)创建的; 2)发送方不能否认已发送消息(不可否认性;3)消息在传输中未被更改(完整性)。

区块链钱包或者浏览器维护一个 UTXO 大集合。虽然区块链底层的数据结构是链式的,但并不代表查询每个账户地址的余额的时候,它们会每次都扫描数百万个交易,数万个区块。它们会不断监听区块链网络里的一切,并且保证自己在正确的链上,然后用锁定脚本里的散列值生成特定的地址,以特定的地址不断增量式地扫描自己的存量数据和新数据,挑选出符合要求的交易,放在优化过的,供查询用的数据结构里,生成真正的account balance。换言之,真正的存档数据都是放在链上的,而客户端节点可以通过流式计算的方法,把UTXO的链式读写模型,重构转化为类似account balance的可查询数据结构。例如:

比特币钱包通过扫描区块链并聚集所有属于该用户的UTXO来计算该用户的余额 。大多数钱包维护一个数据库或使用数据库服务来存储所有UTXO的快速参考集,这些UTXO由用户所有的密钥来控制花费行为。

我们总是从更高级的数据结构里面读取真实数据。

第七章 高级交易和脚本

特殊的高级交易涉及的脚本是复杂脚本,这涉及到多重签名脚本、P2SH等技术。

多重签名脚本通常会先列出N把密钥,然后再左边列出一个数字M,这样只要收集够M个密钥的签名,就可以执行解锁操作。

1
2
3
4
5
6
7
8
9
10
11
# 多重签名脚本的一般形式
M <Public Key 1> <Public Key 2> ... <Public Key N> N CHECKMULTISIG

# 一个23多重签名脚本
2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG

# 配上解锁脚本和验证脚本
<Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG

# 但以上的解锁脚本是有bug的,CHECKMULTISIG会弹出M(个签名) + N(把公钥) + 2(数字MN+ 1个操作数。正确的解锁脚本还要多加一个无用的dumb操作数。
0 <Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG

P2SH在2012年为简化复杂交易脚本而引入,而作为一种交易类型被引入(P2PKH是基本交易类型)。多重签名脚本太复杂,以至于使用过程太复杂,也更浪费全节点的RAM。P2SH才是complex script的解决之道。

在P2SH 支付中,复杂的锁定脚本被电子指纹所取代,电子指纹是指密码学中的哈希值。P2SH的含义是,向与该哈希匹配的脚本支付(用脚本而不是用公钥来解开加密谜题),当输出被支付时,该脚本将在后续呈现。

数字指纹的对比可以用如下表格来呈现:

没有P2SH的复杂脚本
|脚本名|脚本形式|
|:—:|:—:|
|Locking Script|2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 CHECKMULTISIG|
|Unlocking Script|Sig1 Sig2|

有P2SH的复杂脚本

脚本名 脚本形式
Redeem Script 2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 CHECKMULTISIG
Locking Script HASH160 <20-byte hash of redeem script> EQUAL
Unlocking Script Sig1 Sig2

赎回脚本即锁定脚本的原始形式。

可以看出,赎回脚本以指纹的形式被存为锁定脚本。解锁脚本还是完整的解锁脚本,但解锁的过程是把一部分拆出来对照锁定脚本。这也意味着,P2SH只简化了锁定脚本。P2SH的这个形式,还使得 UTXO 的生成者,也就是Transaction 的Sender少生成一些数据(vout占的字节更少),也就少进行一些复杂计算,也少花一些矿工费(还记得矿工费本质上是和交易占用的字节有关吗?)。而 UTXO 的接收者(receipt)就需要填上原长度的解锁脚本了,主要的矿工费用由他们负担。

P2SH的另一重要特征是它能将脚本哈希编译为一个地址(其定义请见BIP0013 /BIP-13)。P2SH地址是基于Base58编码的一 个含有20个字节哈希的脚本,就像比特币地址是基于Base58编码的一个含有20个字节的公钥。由于P2SH地址采用5作为前缀,这导致基于Base58编码的地址以“3”开头。

P2SH的优点:

  • 在交易输出中,复杂脚本由简短电子指纹取代,使得交易代码变短。
  • 脚本能被编译为地址,支付指令的发出者和支付者的比特币钱包不需要复杂工序就可以执行P2SH。
  • P2SH将构建脚本的重担转移至接收方,而非发送方。
  • P2SH将长脚本数据存储的负担从输出方(存储于UTXO集,影响内存)转移至输入方(存储在区块链里面)。
  • P2SH将长脚本数据存储的重担从当前(支付时)转移至未来(花费时)。
  • P2SH将长脚本的交易费成本从发送方转移至接收方,接收方在使用该笔资金时必须含有赎回脚本。

其实从这里也可以看出来,没有用过的UTXO都是存在内存里的,用过的UTXO就变成 transaction 的隐含引用被放在区块链里了。

P2SH脚本不能递归。

请记住不能将P2SH植入P2SH赎回脚本,因为P2SH不能自循环。虽然在技术上可以将RETURN包含在赎回脚本中,但由于规则中没有策略阻止您执行此操作,因此在验证期间执行RETURN将导致交易被标记为无效,因此这是不实际的。

P2SH脚本的赎回脚本是在使用 UTXO 的时候才第一次出现在网络中的,这也就意味着,有可能Hash填错了,就永远没有正确的赎回脚本能花掉那个 UTXO。

比特币有可能被滥用:

比特币的去中心特点和时间戳账本机制,即区块链技术,其潜在运用将大大超越支付领域。许多开发者试图充分发挥交易脚本语言的安全性和可恢复性优势,将其运用于电子公证服务、证券认证和智能合约等领域。很多早期的开发者利用比特币这种能将交易数据放到区块链上的技术进行了很多尝试 ,例如,为文件记录电子指纹,则任何人都可以通过该机制在特定的日期建立关于文档存在性的证明。

运用比特币的区块链技术存储与比特币支付不相关数据的做法是一个有争议的话题。许多开发者认为其有滥用的嫌疑,因而试图予以阻止。另一些开发者则将之视为区块链技术强大功能的有力证明,从而试图给予大力支持。那些反对非支付相关应用的开发者认为这样做将引致“区块链膨胀”,因为所有的区块链节点都将以消耗磁盘存储空间为成本,负担存储此类 数据的任务。

更为严重的是,此类交易仅将比特币地址当作自由组合的20个字节而使用,进而会产生不能用于交易的UTXO。因为比特币地址只是被当作数据使用,并不与私钥相匹配,所以会导致UTXO不能被用于交易,因而是一种伪支付行为。因此,这些交易永远不会被花费,所以永远不会从UTXO集中删除,并导致UTXO数据库的大小永远增加或“膨胀”。

在0.9版的比特币核心客户端上,通过采用Return操作符最终实现了妥协。Return允许开发者在交易输出上增加80字节的非交易数据。然后,与伪交易型的UTXO不同,Return创造了一种明确的可复查的非交易型输出,此类数据无需存储于UTXO集。Return输出被记录在区块链上,它们会消耗磁盘空间,也会导致区块链规模的增加,但 它们不存储在UTXO集中,因此也不会使得UTXO内存膨胀,更不会以消耗代价高昂的内存为代价使全节点都不堪重负。

比特币需要时间锁。时间锁保证了一笔资金在一定时间过后才能被使用。

比特币从一开始就有一个(有缺陷的)交易级时间锁定功能,由nLocktime实现(这同时也是比特币核心代码里的字段名)。

在大多数交易中将其设置为零,以指示即时传播和执行。如果nLocktime不为零,低于5亿,则将其解释为块高度,这意味着交易无效,并且在指定的块高度之前未被中继或包含在块链中。

如果超过5亿,它被解释为Unix纪元时间戳(自Jan-1-1970之后的秒数),并且交易在指定时间之前无效。指定未来块或时间的nLocktime的交易必须由始发系统持有,并且只有在有效后才被发送到比特币网络。

nLocktime 锁定的是 transaction 生效的时间,也就意味着在那个时间段后transaction才生效,transaction里的 vout 才能被使用。这也就意味着这个 transaction 的发起者可以直接发起双花攻击。

2015年12月,引入了一种新形式的时间锁进行比特币软分叉升级。根据BIP-65中的规范,脚本语言添加了一个名为CHECKLOCKTIMEVERIFY(CLTV)的新脚本操作符。 CLTV是每个输出的时间锁定,而不是每个交易的时间锁定,与nLocktime的情况一样。这允许在应用时间锁的方式上具有更大的灵活性(实际上就是更细粒度的锁定)。

CLTV不会取代nLocktime,而是限制特定的UTXO,并通过将nLocktim设置为更大或相等的值,从而达到在未来才能花费这笔钱的目的(这样说来,到底CLTV会不会把 vout 锁定的同时锁定 vin 呢?)。

一个没有加过时间锁定的锁定脚本(赎回脚本)如下:

1
DUP HASH160 <Bob's Public Key Hash> EQUALVERIFY CHECKSIG

而加上时间锁定以后,它就变成这样:

1
<now + 3 months> CHECKLOCKTIMEVERIFY DROP DUP HASH160 <Bob's Public Key Hash> EQUALVERIFY CHECKSIG

nLocktime和CLTV都是绝对时间锁定,它们指定绝对时间点。nSequence 是一种相对时间戳,脚本级相对时间锁定使用CHECKSEQUENCEVERIFY(CSV)操作码实现(具体内容见原文)。

比特币脚本可以实现条件控制语句,实现复杂脚本。但这种复杂脚本是不是图灵完备的,要看操作码的表达能力的范畴了。

隔离见证是对公示规则的升级。在2017年8月1日,由一个 BIP-9 的软分叉在主网上激活。在密码学里,见证就是一个密码学谜题的解。而在比特币中,见证就是对UTXO的密码学条件(cryptographic condition)的满足。一个签名是一种见证,但见证并不仅限于签名,可能有更宽泛的形式。

我们常见的解锁脚本里,签名数据作为至关重要的见证本来是嵌入在vin里面的。隔离见证的简单形式就是把见证从解锁脚本里移出来,放到一个transaction伴侣见证数据结构里。

我们需要隔离见证的原因:

  • 见证数据不再是交易id(也就是散列值的一部分),因为见证数据本身已经不在交易里了。在这之前,见证数据是唯一一个可以被第三方修改的值,因为第三方可以修改签名的值,在不影响交易至关重要属性(输入、输出、数额)的前提下,修改交易的id(可见这个散列算法要求的输入是交易的全部),从而欺骗一些不良实现的认为交易不可更改的钱包,产生拒绝服务攻击。这使得一些依赖于高级交易创建的特性得以实现,特别是闪电网络。
  • 脚本版本化。从隔离见证以后locking script就有了版本,可以在以后通过软升级引入新的操作码。
  • 节省硬盘(扩容本身只是第三个好处)。把见证数据移出transaction,间接地扩大了区块的容量。而且全节点可以在验证交易正确完成以后,删除见证数据,见证数据也不必在所有全节点之间传输和存储。
  • 签名验证优化。之前的计算复杂度可以达到二次方,现在的时间复杂度可以达到线性复杂度。
  • 离线签名改进。

隔离见证不是关于改进如何构造事务的,而是关于如何构造输出的。事务并不分为隔离见证事务和非隔离见证事务,但输出可以分为隔离见证输出和非隔离见证输出。以往的UTXO要求解锁脚本把见证数据内联到input里,但隔离见证可以让见证数据放在input之外。

软分叉总是向后兼容的。对于老客户端,隔离见证的输出就像anyone can spend的output,即一个空的signature也可以解开这个crypto puzzles。

所谓的向后兼容,是一个很有意思的概念,指的是新节点升级以后的语法,依然被老节点所接受,老节点天然支持新节点的语法,老节点在设计之初,就已经给出了新语法的足够的设计空间,即使老节点自己不用新语法,也不妨碍新语法的使用。具体的例子是,含有这种 anyone can spend的 output 的交易是非标准交易,新节点是不验证、不转发也不打包的,但一旦被新节点打包进了区块中,老节点在验证无误的时候,可以接受。

这种anyone can spend的output,如果被老节点试图使用,会通不过新节点的验证,这时候就会变成写两条链。新节点发出的区块,则可以被老节点验证通过。这样如果新节点的算力最终超过了老节点的算力,新链会压倒老链。

我们把一个P2PKH的锁定脚本:

1
DUP HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 EQUALVERIFY CHECKSIG

转化成
1
2
# 这里这个散列值就是原本的 PKH,不再有其他操作码了
0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7

这个脚本被推入栈顶以后,自然是连签名都不需要就可以解锁的(栈顶的这串数字天然等于true?)。而对于新的客户端,0是隔离见证的版本,而这串数字则是PKH。
对于解锁脚本,scriptSig就成为了空字符串(反正对旧客户端,empty signature也可以解锁),而 transaction 外还专门有一个witness的字段,放置scriptSig的内容。这类脚本叫做Pay-to-Witness-Public-Key-Hash (P2WPKH)。

它被spend的时候,本来应该是这样:

1
2
3
4
5
6
7
[...]
Vin” : [
"txid": "0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2",
"vout": 0,
"scriptSig": “<Bob’s scriptSig>”,
]
[...]

因为引入了隔离见证,变成了这样:

1
2
3
4
5
6
7
8
9
10
11
[...]
“Vin” : [
"txid": "0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2",
"vout": 0,
# 原scriptSig变空
"scriptSig": “”,
]
[...]
# 原封不动地移到这个位置来
“witness”: “<Bob’s witness data>”
[...]

P2WPKH应该由reciept来生成,不应该由sender从一个已知的公钥生成(岂不是要求频繁换公钥?),而且应该由压缩(compressed)公钥来生成,未来的升级里面,P2WPKH的script可能导致这个output不可花费。P2WPKH应该由reciept的钱包由私钥衍生的压缩公钥诞生。

同理,P2SH的output经过隔离见证的升级以后,产生的输出(锁定脚本)P2WSH会是这样的:

1
2
# 这里的散列就是赎回脚本的散列了
0 a9b7b38d972cabc7961dbfbcb841ad4508d133c47ba87457b4a0e8aae86dbb89

它被使用起来则是这样的,注意看transaction外多出来的witness区域:

1
2
3
4
5
6
7
8
9
[...]
Vin” : [
"txid": "abcdef12345...",
"vout": 0,
"scriptSig": “”,
]
[...]
“witness”: “<SigA> <SigB> <2 PubA PubB PubC PubD PubE 5 CHECKMULTISIG>”
[...]

而使用P2SH的payment原本应该是这样的:

1
2
3
4
5
6
[...]
“Vin” : [
"txid": "abcdef12345...",
"vout": 0,
"scriptSig": “<SigA> <SigB> <2 PubA PubB PubC PubD PubE 5 CHECKMULTISIG>”,
]

P2WPKH的散列值长度是20字节,而P2WSH的散列值长度则是32字节。他们都是一个版本号加上一个本来的H而略去了所有多余的操作码。

隔离见证的升级,因为以上所述的细节,分为两步,首先钱包要产生隔离见证的输出,然后使用隔离见证的钱包,要被构造进支持隔离见证的交易里。

因为新旧协议的升级有间隙的问题,所以隔离见证的钱包,也可以用隔离见证的交易花费非隔离见证的output,这样可以减轻交易费用。

再谈隔离见证的好处。

引入隔离见证后,scriptSig的部分变空了。也就没有第三方篡改签名,进而影响txid的可能,可以认为txid是不可变的了。也就消除了交易延展性问题。而见证的部分也产生了一个wtxid。wtxid就是transaction + 见证数据后的散列id。由此可以推出两个结论,首先,如果见证数据为空,则wtxid等于txid,其次,wtxid可以被认为是可延展的。

隔离见证可以降低全网的交易费用。

第八章 比特币网络

P2P网络要求网络中的节点彼此对等,不存在专门的Client,Server节点,依靠扁平的拓扑结构通信(IP网络就是这样一个结构)。

除了比特币P2P协议之外,比特币网络中也包含其他协议。例如Stratum协议就被应用于挖矿、以及轻量级或移动端比特币钱包之中。网关(gateway)路由服务器提供这些协 议,使用比特币P2P协议接入比特币网络,并把网络拓展到运行其他协议的各个节点。例如,Stratum服务器通过 Stratum协议将所有的Stratum挖矿节点连接至比特币主网络、并将Stratum协议桥接(bridge)至比特币P2P协议之 上。

尽管节点之间的地位完全平等,但角色又有不同。各种节点的结构大致上是:

各种节点的组件

一些节点保有一份完整的、最新的区块链拷贝,这样的节点被称为“全节点”。全节点能够独立自主地校验所有交易,而不需借由任何外部参照。另外还有一些节点只保留了区块链的一部分,它们通过一种名为“简易支付验证(SPV)”的方 式来完成交易验证。这样的节点被称为“SPV节点”,又叫“轻量级节点”。

挖矿节点通过运行在特殊硬件设备上的工作量证明(proof-of-work)算法,以相互竞争的方式创建新的区块。一些挖矿 节点同时也是全节点,保有区块链的完整拷贝;还有一些参与矿池挖矿的节点是轻量级节点,它们必须依赖矿池服务器维护的全节点进行工作。

换言之轻量级挖矿节点总是矿池全节点的附庸。

越来越多的用户钱包都是SPV节点, 尤其是运行于诸如智能手机等资源受限设备上的比特币钱包应用;而这正变得越来越普遍。

整个比特币网络

比特币传播网络是一种尝试最小化矿工之间传输块的延迟的网络。原始的比特币传播网络是由核心开发商Matt Corallo于2015年创建的,以便能够以非常低的延迟在矿工之间快速同步块。该网络由世界各地的亚马逊Web服务基础架构上托管的几个专门的节点组成,并且连接大多数矿工和采矿池。

新节点总是要找到第一个比特币对等节点,然后连入比特币网络中。

新节点如何找到对等体? 第一种方法是使用多个“DNS种子”来查询DNS,这些DNS服务器提供比特币节点的IP地址列表。 其中一些DNS种子提供了稳定的比特币侦听节点的静态IP地址列表。 一些DNS种子是BIND(Berkeley Internet Name Daemon)的自定义实现,它从搜索器或长时间运行的比特币节点收集的比特币节点地址列表中返回一个随机子集。 Bitcoin Core客户端包含五种不同DNS种子的名称。 不同DNS种子的所有权和多样性的多样性为初始引导过程提供了高水平的可靠性。 在Bitcoin Core客户端中,使用DNS种子的选项由选项switch -dnsseed控制(默认设置为1,以使用DNS种子)。

或者,不知道网络的引导节点必须被给予至少一个比特币节点的IP地址,之后可以通过进一步介绍来建立连接。 命令行参数-seednode可用于连接到一个节点,仅用于将其用作种子。 在使用初始种子节点形成介绍后,客户端将断开连接并使用新发现的对等体。

节点必须连接到若干不同的对等节点才能在比特币网络中建立通向比特币网络的种类各异的路径(path)。由于节点可以随时加入和离开,通讯路径是不可靠的。因此,节点必须持续进行两项工作:在失去已有连接时发现新节点,并在其他节点启动时为其提供帮助。节点启动时只需要一个连接,因为第一个节点可以将它引荐给它的对等节点,而这些节点又会进一步提供引荐。一个节点,如果连接到大量的其他对等节点,这既没必要,也是对网络资源的浪费。在启动完成 后,节点会记住它最近成功连接的对等节点;因此,当重新启动后它可以迅速与先前的对等节点网络重新建立连接。如果先前的网络的对等节点对连接请求无应答,该节点可以使用种子节点进行重启动。

并非所有的节点都有能力储存完整的区块链。许多比特币客户端被设计成运行在空间和功率受限的设备上,如智能电话、平板电脑、嵌入式系统等。对于这样的设备,通过简化的支付验证(SPV)的方式可以使它们在不必存储完整区块链的情况下进行工作。这种类型的客端被称为SPV客户端或轻量级客户端。随着比特币的使用热潮,SPV节点逐渐变成比特币节点(尤其是比特币钱包)所采用的最常见的形式。

SPV节点只需下载区块头,而不用下载包含在每个区块中的交易信息。由此产生的不含交易信息的区块链,大小只有完整区块链的1/1000。SPV节点不能构建所有可用于消费的UTXO的全貌,这是由于它们并不知道网络上所有交易的完整信息。SPV节点验证交易时所使用的方法略有不同,这个方法需依赖对等节点“按需”提供区块链相关部分的局部视图。

打个比方来说,每个全节点就像是一个在陌生城市里的游客,他带着一张包含每条街道、每个地址的详细地图。相比之 下,SPV节点就像是这名陌生城市里的游客只知道一条主干道的名字,通过随机询问该城市的陌生人来获取分段道路指示。虽然两种游客都可以通过实地考察来验证一条街是否存在,但没有地图的游客不知道每个小巷中有哪些街道,也不知道附近还有什么其他街道。没有地图的游客在“教堂街23号”的前面,并不知道这个城市里是否还有其他若干条“教堂街 23号”,也不知道面前的这个是否是要找的那个。对他来说,最好的方式就是向足够多的人问路,并且希望其中一部分人不是要试图抢劫他。

简易支付验证是通过参考交易在区块链中的深度,而不是高度,来验证它们。一个拥有完整区块链的节点会构造一条验证链,这条链是由沿着区块链按时间倒序一直追溯到创世区块的数千区块及交易组成。而一个SPV节点会验证所有区块的链(但不是所有的交易),并且把区块链和有关交易链接起来。

例如,一个全节点要检查第300,000号区块中的某个交易,它会把从该区块开始一直回溯到创世区块的300,000个区块全部都链接起来,并建立一个完整的UTXO数据库,通过确认该UTXO是否还未被支付来证实交易的有效性。SPV节点则不能验证UTXO是否还未被支付。相反地,SPV节点会在该交易信息和它所在区块之间用merkle路径(见“ Merkle 树”章节)建立一条链接。然后SPV节点一直等待,直到序号从300,001到300,006的六个区块堆叠在该交易所在的区块之上,并通过确立交易的深度是在第300,006区块~第300,001区块之下来验证交易的有效性。事实上,如果网络中的其他节点都接受了第300,000区块,并通过足够的工作在该块之上又生成了六个区块,根据代理网关协议,就可以证明该交易不是双重支付。

这一段其实没有讲清楚,按照How does an SPV wallet use the headers that it downloads?的观点,SPV节点会询问完整节点,一个特定的transaction在哪里,全节点会给出全部区块头(以显示区块之间的确认性),以及特定的 Merkle 相关路径,以证明,某一个transaction属于某一个区块头,所有的区块头属于canonical chain。

比特币网络中几乎每个节点都会维护一份未确认交易的临时列表,被称为内存池或交易池。节点们利用这个池来追踪记录那些被网络所知晓、但还未被区块链所包含的交易。例如,保存用户钱包的节点会利用这个交易池来记录那些网络已经接收但还未被确认的、属于该用户钱包的预支付信息。

随着交易被接收和验证,它们被添加到交易池并通知到相邻节点处,从而传播到网络中。

有些节点的实现还维护一个单独的孤立交易池。如果一个交易的输入与某未知的交易有关,如与缺失的父交易相关,该 孤立交易就会被暂时储存在孤立交易池中直到父交易的信息到达。

第九章 区块链

比特币核心使用谷歌的LevelDB来存储区块(C++版本,Fabric使用Go版本)。

区块链可以被视作一个垂直的栈。

每个区块的散列值,是对区块头二次散列的结果。

一个区块只有一个父区块,但可以暂时拥有多个子区块,即分叉。分叉的出现,是因为多个子区块(近乎)同时被发现。

因为区块链有巨大的前后相关性,只要稍微修改一个中段区块,它的nonce以及后代的nonce都要重算,所以从中段修改一个区块要引起一个巨大的计算量问题(瀑布xiaoyin),几乎不可能做到。

你可以把区块链想象成地质构造中的地质层或者是冰川岩芯样品。表层可能会随着季节而变化,甚至在沉积之前就被风吹走了。但是越往深处,地质层就变得越稳定。到了几百英尺深的地方,你看到的将是保存了数百万年但依然保持历史原状的岩层。在区块链里,最近的几个区块可能会由于区块链分叉所引发的重新计算而被修改。最新的六个区块就像几英寸深的表土层。但是,超过这六块后,区块在区块链中的位置越深,被改变的可能性就越小。在100个区块以后,区块链已经足够稳定,这时Coinbase交易(包含新挖出的比特币的交易)可以被支付。几千个区块(一个月)后的区块链将变成确定的历史,永远不会改变。

区块的数据结构包括:区块大小(4字节)、区块头(80字节)、交易计数器(1-9字节)、交易(不定字节)。

而区块头则包括:版本(4字节)、前区块散列(32字节)、默克尔树根(32字节)、时间戳(4字节)、难度目标(4字节)、随机数nonce(4字节)。

有意思的是,区块的散列值并不是区块数据结构的一部分,传输时不携带它,存储在区块链里也没塔,而是被节点临时计算出来的(on-the-fly)。可以被存储在外部独立数据库里面,从而被更快地查找和检索。

也可以用高度来唯一确认一个区块,但有时候一个高度对应的是并不只是一个区块。区块高度也不是区块数据结构的一部分,也可以用单独的数据库来存储。在以太坊中,一个客户端只能获取最近256个区块的细节。

创世区块被编码进链的节点里,而不是通过传输同步过去的。

一个区块被解码出来往往是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"size" : 43560,
"version" : 2,
"previousblockhash" :
"00000000000000027e7ba6fe7bad39faf3b5a83daed765f05f7d1b71a1632249",
"merkleroot" :
"5e049f4030e0ab2debb92378f53c0a6e09548aea083f3ab25e1d94ea1155e29d",
"time" : 1388185038,
"difficulty" : 1180923195.25802612,
"nonce" : 4215469401,
"tx" : [
"257e7497fb8bc68421eb2c7b699dbab234831600e7352f0d9e6522c7cf3f6c77",

#[... many more transactions omitted ...]

"05cfd38f6ae6aa83674cc99e4d75a1458c165b7ab84725eda41d018a09176634"
]
}

现实中的区块链的结构往往是:

现实中区块链的结构

区块中的交易总是以默克尔树的形式被表示出来的。在计算机科学中,树往往是带有分支的数据结构。默克尔树的叶子节点也并不存有transaction本身,而是transaction的双重SHA-256散列值。像这样:

此处输入图片的描述

因为Merkle树是二叉树,所以它需要偶数个叶子节点。如果仅有奇数个交易需要归纳,那最后的交易就会被复制一份以构成偶数个叶子节点,这种偶数个叶子节点的树也被称为平衡树。

当区块大小由16笔交易(4KB)急剧增加至65,535笔交易(16MB)时,为证明交易存在的Merkle路径长度增长极其缓慢,仅仅从128字节到512字节。有了Merkle树,一个节点能够仅下载区块头(80字节/区块),然后通过从一个满节点回溯一条小的Merkle路径就能认证一笔交易的存在,而不需要存储或者传输大量区块链中大多数内容,这些内容可能有几个G的大小。这种不需要维护一条完整的区块链的节点,又被称作简单支付验证(SPV)节点,它不需要下载整个区块而通过Merkle路径去验证交易的存在。即使只是几条默克尔树的路径,也是难以通过伪造的方式来提供的,所以SPV是可信的。

例如,一个SPV节点想知道它钱包中某个比特币地址即将到达的支付。该节点会在节点间的通信链接上建立起bloom过滤器,限制只接受含有目标比特币地址的交易。当对等体探测到某交易符合bloom过滤器,它将以Merkleblock消息的形式发送该区块。Merkleblock消息包含区块头和一条连接目标交易与Merkle根的Merkle路径。SPV节点能够使用该路径找到与该交易相关的区块,进而验证对应区块中该交易的有无。SPV节点同时也使用区块头去关联区块和区块链中的其余区块。这两种关联,交易与区块、区块和区块链,就可以证明交易存在于区块链。简而言之,SPV节点会收到少于1KB的有关区块头和Merkle路径的数据,其数据量比一个完整的区块(目前大约有1MB)少了一千多倍。

当前的测试网络(testnet)本身已经是第三次重启了。这个网络本身设计出来只是作为一个低价值的网络,但经常被人提高难度,所以需要从创世区块开始重新构造和挖掘。

第十章 挖矿和共识

比特币本质上是M0的cash,而不是M2的money。

挖矿虽然创造了新货币,但挖矿最主要的目的是实现了去中心化的。

比特币一共20,999,999,980个,将在2140年发行完毕。随着时间的发展,挖矿的奖励将越来越少,而矿工将逐渐转变为依赖交易费。

总创造的比特币的曲线:

总创造的比特币的曲线

通货膨胀导致货币缓慢但不可避免的贬值,这是一种隐性税收的形式,惩罚在银行存钱的人从而实现解救债务人(包括政府这个最大的债务人)。 政府控制下的货币容易遭受债务发行的道德风险,之后可能会以牺牲储蓄者为代价,通过贬值来抹去债务。

每一个新区块的到达,既是旧的竞赛的结束,也是新的竞赛的开始的发令枪。每一个矿工总是先从自己的交易池里制造出候选区块,然再开始寻找nonce作为自己的工作量证明的证据的。

一个合法区块的第一个交易(且只有第一个)是coinbase交易。

​任何时候,主链都是累计了最多难度的区块链。主链也会有一些分支,这些分支中的区块与主链上的区块互为“兄弟”区块。这些区 块是有效的,但不是主链的一部分。保留这些分支的目的是如果在未来的某个时刻它们中的一个延长了并在难度值上超 过了主链,那么后续的区块就会引用它们。

。比特币将区块间隔设计为10分钟,是在更快速的交易确认和更低的分叉概率间作出的妥协。更短的区块产生间隔会让交易清算更快地完成,也会导致更加频繁地区块链分叉。与之相对地,更长的间隔会减少分叉数量,却会导致更长的清算时间。

原本设计的nonce值不够大,只有4字节。有时候穷举了这4字节的空间,还是不能找到合适的nonce,这时候,只能往两个方向扩展:coinbase的脚本以及修改timestamp。8个字节的额外随机数,加上4个字节的“标准”随机数,允许矿工每秒尝试2^96(8后面跟28个零)种可能性而无需修改时间戳。如果未来矿工穿过了以上所有的可能性,他们还可以通过修改时间戳来解决。同样,coinbase脚本中也有更多额外的空间可以为将来随机数的扩展做准备。

比特币的共识机制依赖于这样一个前提,那就是绝大多数的矿工,出于自己利益最大化的考虑,都会通过诚实地挖矿来维持整个比特币系统。然而,当一个或者一群拥有了整个系统中大量算力的矿工出现之后,他们就可以通过攻击比特币的共识机制来达到破坏比特币网络的安全性和可靠性的目的。

想象这么一个场景,一群矿工控制了整个比特币网络51%的算力,他们联合起来打算攻击整个比特币系统。由于这群矿工可以生成绝大多数的块,他们就可以通过故意制造块链分叉来实现“双重支付”或者通过拒绝服务的方式来阻止特定的交易或者攻击特定的钱包地址。

咖啡店老板 Bob 愿意在 Alice 给自己的转账交易确认数为零的时候就向其提供咖啡,这是因为这种小额交易遭遇“51%攻击”的风险和顾客购物的即时性(Alice能立即拿到咖啡)比起来,显得微不足道。这就和大部分的咖啡店对低于25美元 的信用卡消费不会费时费力地向顾客索要签名是一样的,因为和顾客有可能撤销这笔信用卡支付的风险比起来,向用户索要信用卡签名的成本更高。

比特币的分叉,体现在各个层面上,既包括软件的分叉,也包括共识算法客户端的分叉。分叉就是从同一份历史数据中分出不同的发展。分叉的结果如果是能收敛的还好,不能收敛的话,实际上区块链组织就分裂成了几个小组织,算力就分散了。

第十一章 比特币安全

比特币的去中心化安全模型很大程度上将权力移交到用户手上,随之而来的是用户们保管好密钥的责任。

传统的安全体系基于一个称为信任根(ROOT OF TRUST)的概念,它指的总体系统或应用程序中一个可信赖的安全核心。安全体系像一圈同心圆一样围绕着信任根源来进行开发,像层层包裹的洋葱一样,信任从内至外依次延伸。每一层都构建于更可信的内层之上,通过访问控制,数字签名,加密和其他安全方式确保可信。随着软件系统变得越来越复杂,它们更可能出现问题,安全更容易受到威胁。其结果是,软件系统变得越复杂,就越难维护安全性。信任根的概念确保绝大多数的信任被置于一个不是过于复杂系统的一部分,因此该系统的这部分也相对坚固,而更复杂的软件则在它之上构建。这样的安全体系随着规模扩大而不断重复出现,首先信任根建立于单个系统的硬件内,然后将该信任根通过操作系统扩展到更高级别的系统服务,最后逐次扩散到圈内多台服务器上。

第十二章 比特币应用

支付通道是在比特币区块链之外双方之间交换的比特币交易的无信任机制。这些交易,如果在比特币区块链上结算,则是有效的,然而他们却是在链外被持有的,以期票的形式等待最终批量结算。由于交易尚未结算,因此他们可以在没有通常的结算延迟的情况下进行交换,从而可以满足极高的交易吞吐量,低(亚毫秒)延迟和精细(satoshi级)粒度。

实际上,通道 一词是一个比喻。状态通道是区块链外,由双方之间的交换状态代表的虚拟结构。实际上没有“渠道”,底层数据传输机制并不是渠道。我们使用通道这个术语来表示链外双方之间的关系和共享状态。

为了进一步解释这个概念,想一想TCP流。从高层协议的角度来看,它是一个横跨互联网连接两个应用程序的“socket”。但是,如果您查看网络流量,TCP流只是IP数据包之上的虚拟通道。 TCP流的每个端点通过排序并组装IP数据包以产生字节流的错觉。实际上在背后,所有的数据包都是断开分散的。同理,支付通道只是一系列交易。如果妥善排序和连接,即使您不信任通道的另一方,(经过排序连接后的交易)也可以创建可以信任的可兑换的债务。

闪电网络是第二层路由技术。它可以应用于支持一些基本功能的任何区块链,如多重签名交易,时间锁定和基本的智能合约。

如果闪电网络搭建在在比特币网络之上,则比特币网络可以大大提高容量,隐私性,粒度和速度,而不会牺牲无中介机构的无信任操作原则: