P2SH-为比特币赋能的脚本

P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M<=N, N<=16),地址通常是以3开头的格式。当然,p2sh除了用来做多重签名之外,能做的事情简直无数。

P2PK

比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:

1
04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG
  • 0x04开头,表示是未压缩的
  • 0x04后面紧接这的是64字节是公钥内容
  • OP_CHECKSIG:操作码,用于花费的时执行验证签名

那么验证时(即花费这笔输出)堆栈内容为:

1
2
3
4
5
花费脚本为:
<signature>

连接前向的输出,完整的堆栈:
<signature> <pubkey> OP_CHECKSIG

OP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。

P2PKH

后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:

1
OP_DUP OP_HASH160 <public_key_hash> OP_EQUALVERIFY OP_CHECKSIG
  • OP_DUP: 操作码,复制栈顶元素
  • OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希
  • public_key_hash:公钥的哈希值,20字节
  • OP_EQUALVERIFY:操作码,判断是否相等
  • OP_CHECKSIG:操作码,用于花费的时执行验证签名

此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:

1
2
3
4
5
花费脚本为:
<signature> <pubkey>

连接前向的输出,完整的堆栈:
<signature> <pubkey> OP_DUP OP_HASH160 <pubkey_hash> OP_EQUALVERIFY OP_CHECKSIG

验证运算过程:

  1. 从前往后找OP操作码,首先遇到OP_DUP
    • 执行后堆栈为:<signature> <pubkey> <pubkey> OP_HASH160 <pubkey_hash> OP_EQUALVERIFY OP_CHECKSIG
  2. 执行OP_HASH160
    • 执行后堆栈为:<signature> <pubkey> <pubkey_hash> <pubkey_hash> OP_EQUALVERIFY OP_CHECKSIG
  3. 执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配
    • 执行后堆栈为:<signature> <pubkey> OP_CHECKSIG
    • 到这里就与P2PKH一致了
  4. 执行OP_CHECKSIG,验证签名。

这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?

原始多重签名

随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:

1
m {pubkey}...{pubkey} n OP_CHECKMULTISIG

2/2的一个原始多重签名示例:

1
2
3
4
2
04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4
0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af
2 OP_CHECKMULTISIG

验证时的堆栈内容为:

1
2
3
4
5
花费脚本为:
0 <sig_1> <...> <sig_M>

连接前向的输出,完整的堆栈:
0 <sig_1> <...> <sig_M> M <pubkey_1> <...> <pubkey_N> N OP_CHECKMULTISIG

OP_CHECKMULTISIG的运行过程稍微复杂一些:

  1. 弹出最后一个数,就是N,公钥总数。
    • 执行后堆栈为:0 <sig_1> <...> <sig_M> M <pubkey_1> <...> <pubkey_N>
  2. 弹出N个堆栈元素,就是这N把公钥。
    • 执行后堆栈为:0 <sig_1> <...> <sig_M> M
  3. 弹出签名数量M,即需要M个签名数量。
    • 执行后堆栈为:0 <sig_1> <...> <sig_M>
  4. 弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。
    • 执行后堆栈为:0
  5. 弹出最后一个元素0
    • 0OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。

这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。

P2SH

因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:

1
OP_HASH160 <redeem_script_hash> OP_EQUAL

其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:

1
2
3
RedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG

20-byte-hash-value = RIPEMD-160(RedeemScript)

RedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。

验证时的堆栈内容为:

1
2
3
4
5
花费脚本为:
OP_0 <sig_1> <...> <sig_M> <redeemScript>

连接前向的输出,完整的堆栈:
OP_0 <sig_1> <...> <sig_M> <redeemScript> OP_HASH160 <redeemScriptHash> OP_EQUAL

验证过程分为两大步骤,第一步骤:

  1. 执行OP_HASH160,计算HASH值:<redeemScript> OP_HASH160
    • 执行后堆栈为:OP_0 <sig_1> <...> <sig_M> <redeemScriptHash><redeemScriptHash> OP_EQUAL
  2. 执行OP_EQUAL,验证两个哈希值是否相等
    • 执行后堆栈为:OP_0 <sig_1> <...> <sig_M>

第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:

1
OP_0 <sig_1> <...> <sig_M> M <pubkey_1> <...> <pubkey_N> N OP_CHECKMULTISIG

当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。

软分叉的关键点

在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。

当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。

激活

P2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。

为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。

影响深远

P2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。


参考

谈谈软分叉升级规范BIP8-后矿工时代的软分叉方式

版权声明:欢迎转载,请注明出处

之前说过软分叉升级规范BIP9,BIP9可以说是经过良好设计的软分叉升级方案,可同时进行多个分叉的升级并且流程也很科学合理。

BIP9

简单回顾一下BIP9的主要特性:

  1. 95%的高投票阈值,块投票必须达到95%以上的支持率才能进入锁定期,然后触发激活。
  2. 设定了投票时间窗口,有起始、终止时间,在窗口期内未激活的软分叉只能终止或者重新设定发起新一轮投票。
  3. 块时间采用相邻11个块的块中位数时间。

BIP9截止到当前(2019年02月)共使用过两次:

  1. CSV(BIP68, BIP112, and BIP113)
  2. SegWit(BIP141, BIP143, and BIP147)

在CSV软分叉中进展一切正常,但SegWit软分叉中却在投票阶段延迟了大半年,块投票一直无法达到95%的激活阈值,以至于后来出现了BIP148(即UASF,user-activated soft fork)和BIP91。SegWit在BIP9中设定的时间区间是:2016年11月15日~2017年11月15日。

这里我们稍微说一下BIP148和BIP91,只有理解了这个过程才能理解为何会有BIP8(这些事情其实已经发生挺久了,2017年中,快两年前的事情了)。值得一提的是,BIP148和BIP91的代码,从来没有进入Bitcoin Core版本的代码库。

BIP148

BIP148,更熟悉的名称是UASF,目的是强制性进行激活SegWit:若块时间处于2017年08月01日与2017年11月15日,且SegWit尚未激活或没有进入锁定期,则直接拒绝不支持SegWit投票的块。BIP148通过用户自行更新代码,下载Bitcoin Core的代码然后打补丁的方式,来声明和实施。

UASF是非常激进的升级方式,直接抛开了矿工算力的投票,不管算力是否投票支持SegWit,这些UASF全节点到时候会直接抛弃掉非SegWit的块。这在当时还非常认可矿工和算力的时代,对于矿工和算力来说简直是晴天霹雳:直接撇开了!

当然,很多交易所对这一事件进行了预演:假设分叉会发生,并上市了推演分叉后的两个币种期货,把价格交给市场去判断。

BIP91

BIP91,目的是也是激活SegWit,通过降低SegWit激活阈值至80%来间接完成,其自身采用类似BIP9的方式进行部署:

  1. 激活时间区间是:2017年06月1日 ~ 2017年11月15日。
  2. 块时间窗口非常短,不再是BIP9中的2016个块,而是336个块,大约2.33天。
  3. 激活阈值为80%,不再是BIP9的95%。

BIP148(UASF)直接撇开矿工由全节点直接激活,而BIP91依然把选择权给了矿工:通过块投票进行激活。最终,由UASF主导的强大社区压力下,BIP91很快通过80%的块投票阈值进入锁定并激活了。BIP91一旦激活,则意味着后面的块必须进行支持SegWit投票,间接促成了SegWit通过95%的块投票阈值并锁定激活了。

以上即是SegWit激活受阻大半年后,UASF&BIP91间接促成SegWit激活的历史过程。

BIP8

BIP8是在BIP9基础之上的改进:

  1. 采用更加精确的块高度窗口代替块时间窗口,消除了块时间的不稳定性。
  2. 统计周期依然是与BIP9一致的2016个块。
  3. 几乎不再有失败的状态,除非编码之初设定的高度已经是过去高度。
  4. 设立激活起始块高度,一旦当前高度大于起始块高度,则开始计算是否激活。起始块高度必须超过当前高度4320个块,约30天。
  5. 设立截止块高度,无论投票是否通过,都在截止块高度达到时进行强制激活。截止高度通常是起始高度的52416块之后,约一年。
  6. 在抵达截止块高度前,若投票超过阈值,阈值与BIP9一致为95%,则提前进入锁定期并随之激活。

BIP8 States

总结,如果某个软分叉遵循BIP8激活机制的话,一旦部署了,那么矿工可以进行投票提前激活,或者在一年后的截止高度抵达时自动激活。

BIP8的主要意义

  1. 取消了矿工的否决权:要么投票主动提前激活,要么不投票被动等抵达截止高度自动激活。
    • Vote转变成Signal。
    • 算力大小是价格高低的结果,严格来说是链的法币日产量决定算力规模。
    • 价格由市场供需决定,体现为共识主导价格。
  2. 社区的决策机制发生了根本性改变:从小圈子投票变成了一定程度上的普选。
    • 无论是算力投票还是持币投票,由于存在中间代理层(例如矿池、交易所、钱包等),均无法避免最终沦为一定程度上的小圈子投票。
    • 一个全节点即一张选票,全节点构成最广泛的共识。
    • 全节点一方面制约着算力,另一方面也制约代码。无论是矿霸还是码霸,均无法强制约束全节点的行为。

后记

从中本聪白皮书时代以来,大多数人坚信着“One CPU One Vote”的民主观念,在经历了2017的SegWit激活历程后,其中还硬分叉诞生了Bitcoin Cash(BCH),终于得到脱胎换骨的革新。大家发现只有全节点才是最终的堡垒和武器,运行全节点是平等自由的互联网所赋予的谁也剥夺不了的权利。


参考

新的软分叉升级规范BIP9

比特币的软/硬分叉升级一直采用块的version字段来完成。由于是一个分布式系统,必然需要采用灰度发布模式。

传统的升级过程

在实施BIP9前是这样升级的,当前块版本为version,那么新块版本是version + 1,当近1000个块中的版本超过95%都是新版本时,则触发启用新特性,同时不再接收旧版本号的块。由于中间存在1000个块的窗口期,大约一周,所以给出足够的时间给当前网络中的节点实施升级。

图1.0

上图是来自BTC.COM统计的比特币历史上几个升级过程,最近的v3升级v4的过程大约花费了一个半月左右。前面几次的升级时间更长。

这种依次递增版本号的方法,有一个明显的弊端:每次仅能进行一个特性升级。当需要同时进行多个升级时,则无法完成。BIP9的诞生就是为了解决这个问题的,同时把向下兼容性升级过程制定了规范。

BIP9特征

  1. 支持多个特性同时升级
  2. 新增投票时间区间
  3. 新增锁定期

块的version字段是4字节,意味着有32位比特,前三位目前固定为001,那么剩余29个比特可以用于特性设置,与激发某个特性,则将其对应的位设置为1

1
2
3
4
5
二进制格式:

001 0 0000 0000 0000 0000 0000 0000 0000
------ ------------------------------------
固定保留 29个特性标识位

状态变迁

BIP9某个特性的状态有:

  • DEFINED 定义。每个特性的升级设定会写到某一个版本的bitcoind中,并部署下去。
  • STARTED 启动。启动后会有两个可能的变迁状态:LOCKED_IN, FAILED
  • LOCKED_IN 锁定。一旦进入锁定态,则必然会激活。锁定期为2016个块。
  • ACTIVE 激活
  • FAILED 失败

图1.1

MTP是最近11个块的时间戳中位数。区块链上的块时间并不是按照高度递增而严格递增的,但MTP时间是严格递增的(早期可能不能严格遵守)。

能够进入锁定期(即得到全网同意)的条件是在2016个块中,支持的块数量达到95%,在测试网络上是75%。窗口期从之前的1000提升至2016个块,即一个难度周期,约两周。同时,还增加了2016个块的锁定期,之后才能正式激活特性。这比之前的升级,变得更加稳健,预留出充分的时间,同时意味着特性的落地周期变长。

首个采用BIP9的软分叉

BIP68/112/113是首个采用BIP9方式进行升级的软分叉,随bitcoind-v0.12.1发布。使能位采用最右侧位,起始时间(starttime)是2016-05-01 00:00:00,终止时间(timeout)是2017-05-01 00:00:00

若要支持BIP68/112/113软分叉,那么版本号应该是: 0x20000001,二进制为:0010 0000 0000 0000 0000 0000 0000 0001

另外BIP109(Classic 2MB HardFork)也是采用的是BIP9规范,使能位是最左侧位,但触发窗口期与锁定期的设定均未采用BIP9的定义。其版本号是:0x30000000,二进制为:0011 0000 0000 0000 0000 0000 0000 0000

如果版本号为:0x30000001,二进制为:0011 0000 0000 0000 0000 0000 0000 0001,则意味着同时支持BIP68/112/113BIP109

理解比特币脚本

作者:汪海波

其实我们可以这样看待比特币的交易:『交易的发起者悬赏若干比特币,在网络上贴出了一到数学题,谁解出了这道数学题,悬赏就归谁了』。 顺着这个思路,Alice对Bob的转账可以理解为『Alice把一道只有Bob才能解开的数学题发到网络上,Bob解出题并拿走了悬赏』。那么,每个交易数据中都会出现的『脚本』就是题和解,『脚本语言』就是用来描述题和解的工具。

图01

『输入脚本』和『输出脚本』

在这里我们先讨论单输入单输出的比特币交易,因为这样描述起来更方便且不影响对『脚本』的理解。
9c50cee8d50e273100987bb12ec46208cb04a1d5b68c9bea84fd4a04854b5eb1 这是一个单输入单输出交易,看下我们要关注的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Hash:
9c50cee8d50e273100987bb12ec46208cb04a1d5b68c9bea84fd4a04854b5eb1

输入交易:
前导输入的Hash:
437b95ae15f87c7a8ab4f51db5d3c877b972ef92f26fbc6d3c4663d1bc750149

输入脚本 scriptSig:
3045022100efe12e2584bbd346bccfe67fd50a54191e4f45f945e3853658284358d9c062ad02200121e00b6297c0874650d00b786971f5b4601e32b3f81afa9f9f8108e93c752201
038b29d4fbbd12619d45c84c83cb4330337ab1b1a3737250f29cec679d7551148a

输出交易:
转账值:
0.05010000 btc

输出脚本 scriptPubKey:
OP_DUP OP_HASH160 be10f0a78f5ac63e8746f7f2e62a5663eed05788 OP_EQUALVERIFY OP_CHECKSIG

假设Alice是转账发送者,Bob是接受者。那么『输入交易』表明了Alice要动用的比特币的来源,『输出交易』表明了Alice要转账的数额和转账对象——Bob。那么,你可能要问,数据中的『输入脚本』和『输出脚本』是不是就是题和解?对了一半!

Bitcoin Wiki中提到:

原先发送币的一方,控制脚本运行,以便比特币在下一个交易中使用。想花掉币的另一方必须把以前记录的运行为真的脚本,放到输入区。

换句话说,在一个交易中,『输出脚本』是数学题,『输入脚本』是题解,但不是这道数学题的题解。我开始看Wiki的时候,在这里遇到了一些障碍,没法理解『输入脚本』和『输出脚本』的联系。但是在考虑交易间的关系后,就明白了。

假设有这么一系列交易:

图02

  1. 上图的三个交易都是单输入单输出交易
  2. 每个『输入交易』『输出交易』中,都包含对应的『脚本』
  3. 交易a,Alice转账给Bob;交易b,Bob转账给Carol;交易c,Carol转账给Dave
  4. 当前交易的『输入』都引用前一个交易的『输出』,如交易b的『输入』引用交易a的『输出』

按照之前的说法,交易a中的『输出脚本』就是Alice为Bob出的数学题。那么,Bob想要引用交易a『输出交易』的比特币,就要解开这道数学题。题解是在交易b的『输入脚本』里给出的!Bob解开了这道题,获得了奖金,然后在交易b中为Carol出一道数学题,等待Carol来解…

所以说,下图中相同颜色的『输出』和『输入』才是一对题和解:

图03

脚本语言

Bitcoin Wiki给出的对脚本的解释:

比特币在交易中使用脚本系统,与FORTH(一种编译语言)一样,脚本是简单的、基于堆栈的、并且从左向右处理,它特意设计成非图灵完整,没有LOOP语句。

要理解比特币脚本,先要了解『堆栈』,这是一个后进先出(Last In First Out )的容器,脚本系统对数据的操作都是通过它完成的。比特币脚本系统中有两个堆栈:主堆栈和副堆栈,一般来说主要使用主堆栈。举几个简单的例子,看下指令是如何对堆栈操作的(完整的指令集在Wiki里可以找到):

  • 常数入栈:把一段常数压入到堆栈中,这个常数成为了栈顶元素
    图04

  • OP_DUP:复制栈顶元素
    图05

  • OP_EQUALVERIFY:检查栈顶两个元素是否相等
    图06

标准交易脚本

也就是P2PKH(Pay To Public Key Hash),我们常用的转账方式。Alice在转账给Bob的时候,『输出交易』中给出了Bob的『钱包地址』(等价于『公钥哈希』);当Bob想要转账给Carol的时候,他要证明自己拥有这个『钱包地址』对应的『私钥』,所以在『输入交易』中给出了自己的『公钥』以及使用『私钥』对交易的签名。看个实例:

交易b中有一个『输入交易』引用了交易a的『输出交易』,它们的脚本是一对题与解:

题:交易a的『输出脚本』,若干个脚本指令和转账接收方的『公钥哈希』

1
OP_DUP OP_HASH160 be10f0a78f5ac63e8746f7f2e62a5663eed05788 OP_EQUALVERIFY OP_CHECKSIG

解:交易b的『输入脚本』,这么一长串只是两个元素,『签名』和『公钥』(sig & pubkey)

1
2
3046022100ba1427639c9f67f2ca1088d0140318a98cb1e84f604dc90ae00ed7a5f9c61cab02210094233d018f2f014a5864c9e0795f13735780cafd51b950f503534a6af246aca301
03a63ab88e75116b313c6de384496328df2656156b8ac48c75505cd20a4890f5ab

下面来看下这两段脚本是如何执行,来完成『解题』过程的。

  1. 首先执行的是『输入脚本』。因为脚本是从左向右执行的,那么先入栈的是『签名』,随后是『公钥』
    图07

  2. 接着,执行的是『输出脚本』。从左向右执行,第一个指令是OP_DUP——复制栈顶元素
    图08

  3. OP_HASH160: 计算栈顶元素Hash,得到pubkeyhash
    图09

  4. 将『输出脚本』中的『公钥哈希』入栈,为了和前面计算得到的哈希区别,称它为pubkeyhash
    图10

  5. OP_EQUALVERIFY: 检查栈顶前两元素是否相等,如果相等继续执行,否则中断执行,返回失败
    图11

  6. OP_CHECKSIG: 使用栈顶前两元素执行签名校验操作,如果相等,返回成功,否则返回失败
    图12

这样一串指令执行下来,就可以验证这道数学题是否做对了,也就是说验明了想要花费『钱包地址』中比特币的人是否拥有对应的『私钥』。上面的执行过程是可以在脚本模拟器中执行的,能够看到每一步执行的状态,感兴趣的童鞋可以尝试一下。

其实除了标准的P2PKH交易脚本,还有P2SH的Multi-Sig脚本以及真正的『解谜交易』脚本,我们可以在今后接着讨论。


参考

[1] 比特币脚本
[2] 申屠青春(我看比特币
[3] Bitcoin Wiki, Script

Shadowsocks 加入比特币的支持

前言

加入比特币后达到的效果:只需知道服务提供方的比特币地址,就可以使用其服务。

假定17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E是服务提供方的比特币地址,那么有如下约定:

  • 17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E 即为服务方的比特币收款地址
  • 17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E 即为共享密钥(配置参数password)
  • 17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E.com 即为服务方的域名

假定客户端拥有地址 1CntTWhxxxxxxxxxxxxxxxxxxxxxx 及其私钥。首先保障该地址有一定数量的比特币,然后从该地址支付比特币至服务端地址,当服务方地址收到比特币后,将该客户地址添加到列表文件中,此时,客户即可使用对方的服务。

项目GitHub: https://github.com/bitkevin/shadowsocks-libev,如果shadowsocks接收的话会pull request回原来分支。

为了学习测试,可以使用测试服务器 17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E.com,费用是每天 0.00015 btc。服务端每分钟更新一次支付记录。

编译

编译步骤没有变化,主要是配置文件的差异。

1
2
3
4
# build shadowsocks
apt-get install build-essential autoconf libtool libssl-dev
./configure && make
make install

服务端

写入付款地址的记录

1
2
mkdir -p /var/ss
echo "1CntTWhxxxxxxxxxxxxxxxxxxxxxx" >> /var/ss/list-ss-server.txt

ss-server 启动时,指定参数 --bitcoin-list 即可:

1
ss-server -s 0.0.0.0 -p 443 -k 17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E -m aes-256-cfb -t 60 --workers 10 --fast-open --bitcoin-list /var/ss/list-ss-server.txt

同时,注册域名 17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E.com,并将其A记录指向到提供服务的IP地址上。

客户端

示例配置文件:

1
2
3
4
5
6
7
8
9
10
{
"server":"17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E.com",
"server_port":443,
"local_port":8999,
"password":"17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E",
"timeout":300,
"method":"aes-256-cfb",
"bitcoin_address":"1CntTWhxxxxxxxxxxxxxxxxxxxxxx",
"bitcoin_privkey":"L5eixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
  • bitcoin_address 支付地址,推荐新生成一个地址单独用于此,并保证该地址是你个人控制的
  • bitcoin_privkey 支付地址对应的私钥,base58格式,大部分钱包导出就是此格式。该私钥不会暴露至网络

保障地址 1CntTWhxxxxxxxxxxxxxxxxxxxxxx 有一定数量的比特币,然后从该地址付款至服务端地址 17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E,付款比特币数量尽量以天为整数即可,例如付款30天的费用:0.0045 btc = 0.00015 * 30。

其他

为了方便,提供了一个自动生成付款地址记录的脚本。安装至目录 /var/ss

1
2
3
apt-get install php5-cli php5-curl
mkdir -p /var/ss
cp scripts/generate.php /var/ss

拷贝后,需要修改其配置,generate.php 中的配置参数说明:

1
2
3
4
5
6
7
8
// 由于使用了 chain.com 的API,所以到 chain.com 注册一个账户,并免费获得一个API KEY。
$_CFG['API-KEY-ID'] = "DEMO-4a5e1e4";

// 服务端的地址,该地址用于收款等。
$_CFG['server_baddress'] = "17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E";

// 每天的价格,单位是聪。 15000 Satoshi = 0.00015 Btc。
$_CFG['satoshi_per_day'] = 15000;

ss-local/ss-server 的配置文件中去掉 --bitcoin-xxx 参数,则与不带bitcoin支持的shadowsocks行为一致。

比特币安全钱包的想法

从财富不可侵犯角度讲,比特币是人类史上最安全的货币。但令人郁闷的是,安全地存储比特币却遇到诸多麻烦。私钥机制使得其神圣不可侵犯,也使其资金安全遇到麻烦。

个人存储管理比特币非常不现实,安全性差,丢币概率高。在线钱包显得更加安全可靠一些,主要分两类:

  • 链下(Off-Chain),链下钱包服务即银行模式,若服务商出现任何问题,储户资金即出问题
  • 链上(On-Chain),链上钱包“理论上”安全,因其不掌握用户私钥(仅保存私钥的密文),但也不绝对,例如服务器被入侵后,钱包关键代码被恶意修改,导致能够恢复出用户私钥,无论多少币,均瞬间被转移

没有绝对安全的方案,只有相对安全实现,就是把出问题的概率降低得非常非常低。程序发生严重Bug、服务器被入侵这些都不能认为是极低概率事件,只是小概率而已,时间足够长的话几乎都会发生,回溯比特币这几年历史,发生过很多小概率、黑天鹅事件。

这里提出一种钱包的实现方案,尝试去解决一些安全隐患问题。方案分为服务端,客户端两个部分。

服务端

  • 数据服务。只读模式,包括不限于:
    • balance by address
    • unspent outputs
    • tx/block query
  • 广播服务。输入是公开的数据,协助将交易(Raw Tx)广播至比特币网络

客户端

  • 硬件部分
    • 支持如ECDSA, SHA256、Ripemd160等
    • 支持硬件隔离技术,存储敏感数据,如私钥数据
  • APP部分
    • 与硬件部分进行通信
    • 提供功能界面,实现钱包操作交互

客户端硬件部分像拉卡拉类似的一个东西,可以通过耳机等音频设备与手机APP通信。

  • 收款:当用户使用钱包时,插入硬件部分,启动APP,从硬件读取比特币地址列表,并去服务端查询地址数据,更新余额
  • 发款:APP根据比特币地址从服务端获取相关未花费交易,用户设置转出地址和金额,APP根据未花费交易构建待签名的空交易,将空交易传输至硬件并通知其签名。硬件从隔离区读取数据并由加密芯片完成签名后,将完整交易放入普通数据区,APP从普通数据区读取签名后的交易并提交至服务端广播
  • 数据备份:硬件支持AES双向加密算法,用户在APP输入备份密码,传至硬件,硬件从隔离区读取数据并由加密芯片完成AES加密,将加密后的数据放入普通数据区,由APP传输至手机、云盘等完成备份
  • 硬件遗失:为了防止遗失导致硬件破解丢币,敏感数据在隔离区中平时即以加密形式存在,每次使用时,输入进入密码,才能使用

安全点:

  • 服务端必须是只读模式的。只读的设计,保障客户端不会提交敏感数据,即使服务端被黑,也无法截获用户私钥
  • 客户端必须实现硬件隔离,保护私钥等敏感数据,使得操作系统无法读取敏感信息,木马、恶意软件就没有办法盗取

这只是一个初步的想法,可能存在重大缺陷,也许有团队已经在实现了,贴出来希望大家一起讨论。

比特币交易构成(二)

交易的构造、签名与广播

上篇介绍了交易结构、签名等,为了更直观的认识比特币,借助bitcoind演示手动构造并广播交易的完整过程。

普通交易

1. 找出未花费的币(unspent output)

通过命令:listunspent [minconf=1] [maxconf=9999999] ["address",...]列出某个地址未花费的币(交易),minconf/maxconf表示该笔收入交易的确认数范围,如果需要列出还未确认的交易,需将minconf设置为0。

执行:

1
bitcoind listunspent 0 100 '["1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3"]'

输出:

1
2
3
4
5
6
7
8
9
10
11
[
{
"txid" : "296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156",
"vout" : 0,
"address" : "1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3",
"account" : "",
"scriptPubKey" : "76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac",
"amount" : 0.19900000,
"confirmations" : 1
}
]

我们找到该地址的一个未花费交易,位于交易296ea7bf981b4499…9f0a41e589132156的第0个位置。

2. 创建待发送交易

创建待发送交易,由命令:createrawtransaction [{"txid":txid,"vout":n},...] {address:amount,...}来完成。我们将 0.1 BTC发送至 1Q8s4qDRbCbFypG5AFNR9tFC57PStkPX1x ,并支付 0.0001 BTC做为矿工费。输入交易的额度为 0.199 ,输出为 0.1 + 0.0001 = 0.1001 ,那么还剩余: 0.199 - 0.1001 = 0.0989 ,将此作为找零发回给自己。

执行:

1
2
3
bitcoind createrawtransaction \
'[{"txid":"296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156","vout":0}]' \
'{"1Q8s4qDRbCbFypG5AFNR9tFC57PStkPX1x":0.1, "1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3":0.0989}'

输出:

1
010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e290000000000ffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000

通过命令:decoderawtransaction <hex string>,可以将此段十六进制字符串解码。

执行:

1
bitcoind decoderawtransaction '010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e290000000000ffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000'

输出:

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
{
"txid" : "54f773a3fdf7cb3292fc76b46c97e536348b3a0715886dbfd2f60e115fb3a8f0",
"version" : 1,
"locktime" : 0,
"vin" : [
{
"txid" : "296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156",
"vout" : 0,
"scriptSig" : {
"asm" : "",
"hex" : ""
},
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 0.10000000,
"n" : 0,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 fdc7990956642433ea75cabdcc0a9447c5d2b4ee OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"1Q8s4qDRbCbFypG5AFNR9tFC57PStkPX1x"
]
}
},
{
"value" : 0.09890000,
"n" : 1,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 d6c492056f3f99692b56967a42b8ad44ce76b67a OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3"
]
}
}
]
}

至此,一个“空白交易”就构造好了,尚未使用私钥对交易进行签名,字段scriptSig是留空的,无签名的交易是无效的。此时的Tx ID并不是最终的Tx ID,填入签名后Tx ID会发生变化。

在手动创建交易时,务必注意输入、输出的值,非常容易犯错的是忘记构造找零输出(如非必要勿手动构造交易)。曾经有人构造交易时忘记找零,发生了支付 200 BTC 的矿工费的人间惨剧,所幸的是收录该笔交易的Block由著名挖矿团队“烤猫(Friedcat)”挖得,该团队非常厚道的退回了多余费用

3. 签名

交易签名使用命令:

1
2
3
signrawtransaction <hex string> \
[{"txid":txid,"vout":n,"scriptPubKey":hex,"redeemScript":hex},...] [<privatekey1>,...] \
[sighashtype="ALL"]
  • 第一个参数是创建的待签名交易的十六进制字符串;
  • 第二个参数有点类似创建交易时的参数,不过需要多出一个公钥字段scriptPubKey,其他节点验证交易时是通过公钥和签名来完成的,所以要提供公钥;如果是合成地址,则需要提供redeemScript
  • 第三个参数是即将花费的币所在地址的私钥,用来对交易进行签名,如果该地址私钥已经导入至bitcoind中,则无需显式提供;
  • 最后一个参数表示签名类型,在上一篇里,介绍了三种交易签名类型;

签名之前需要找到scriptPubKey,提取输入交易信息即可获取(也可以根据其公钥自行计算),由命令:getrawtransaction <txid> [verbose=0]完成。

执行:

1
bitcoind getrawtransaction 296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156 1

输出:

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
{
"hex" : "01000000010511331f639e974283d3909496787a660583dc88f41598d177e225b5f352314a000000006c493046022100be8c796122ec598295e6dfd6664a20a7e20704a17f76d3d925c9ec421ca60bc1022100cf9f2d7b9f24285f7c119c91f24521e5483f6b141de6ee55658fa70116ee04d4012103cad07f6de0b181891b5291a5bc82b228fe6509699648b0b53556dc0057eeb5a4ffffffff0160a62f01000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000",
"txid" : "296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156",
"version" : 1,
"locktime" : 0,
"vin" : [
{
"txid" : "4a3152f3b525e277d19815f488dc8305667a78969490d38342979e631f331105",
"vout" : 0,
"scriptSig" : {
"asm" : "3046022100be8c796122ec598295e6dfd6664a20a7e20704a17f76d3d925c9ec421ca60bc1022100cf9f2d7b9f24285f7c119c91f24521e5483f6b141de6ee55658fa70116ee04d401 03cad07f6de0b181891b5291a5bc82b228fe6509699648b0b53556dc0057eeb5a4",
"hex" : "493046022100be8c796122ec598295e6dfd6664a20a7e20704a17f76d3d925c9ec421ca60bc1022100cf9f2d7b9f24285f7c119c91f24521e5483f6b141de6ee55658fa70116ee04d4012103cad07f6de0b181891b5291a5bc82b228fe6509699648b0b53556dc0057eeb5a4"
},
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 0.19900000,
"n" : 0,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 d6c492056f3f99692b56967a42b8ad44ce76b67a OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3"
]
}
}
],
"blockhash" : "000000000000000488f18f7659acd85b2bd06a5ed2c4439eea74a8b968d16656",
"confirmations" : 19,
"time" : 1383235737,
"blocktime" : 1383235737
}

scriptPubKey位于”vout”[0]->”scriptPubKey”->”hex”,即: 76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac

签名使用ECDSA算法,对其,“空白交易”签名之,执行:

1
2
3
bitcoind signrawtransaction \
"010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e290000000000ffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000" \
'[{"txid":"296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156","vout":0,"scriptPubKey":"76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac"}]'

输出:

1
2
3
4
{
"hex" : "010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e29000000008c493046022100f9da4f53a6a4a8317f6e7e9cd9a7b76e0f5e95dcdf70f1b1e2b3548eaa3a6975022100858d48aed79da8873e09b0e41691f7f3e518ce9a88ea3d03f7b32eb818f6068801410477c075474b6798c6e2254d3d06c1ae3b91318ca5cc62d18398697208549f798e28efb6c55971a1de68cca81215dd53686c31ad8155cdc03563bf3f73ce87b4aaffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000",
"complete" : true
}

签名后,签名值会填入上文所述的空字段中,从而得到一个完整的交易。可通过上文介绍的命令decoderawtransaction <hex string>解码查看之。

最后一步,就是将其广播出去,等待网络传播至所有节点,约10~60秒广播至全球节点,取决与你的节点的网络连接状况。稍后一些时刻,就会进入Block中。广播由命令sendrawtransaction <hex string>来完成。如果没有运行节点,可以通过公共节点的API进行广播,例如:blockchain.info/pushtx

执行:

1
2
bitcoind sendrawtransaction \
"010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e29000000008c493046022100f9da4f53a6a4a8317f6e7e9cd9a7b76e0f5e95dcdf70f1b1e2b3548eaa3a6975022100858d48aed79da8873e09b0e41691f7f3e518ce9a88ea3d03f7b32eb818f6068801410477c075474b6798c6e2254d3d06c1ae3b91318ca5cc62d18398697208549f798e28efb6c55971a1de68cca81215dd53686c31ad8155cdc03563bf3f73ce87b4aaffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000"

输出:

1
b5f8da1ea9e02ec3cc0765f9600f49945e94ed4b0c88ed0648896bf3e213205d

返回的是Transaction Hash值,即该交易的ID。至此,交易构造、签名、发送的完整过程完成了。

合成地址交易

合成地址以3开头,可以实现多方管理资产,极大提高安全性,也可以轻松实现基于比特币原生的三方交易担保支付。一个M-of-N的模式:

1
m {pubkey}...{pubkey} n OP_CHECKMULTISIG

M和N需满足:

  • 1<=N<=3
  • 1<=M<=N

可以是1 of 11 of 22 of 3等组合,通常选择N=3

  • 1 of 3,最大程度私钥冗余。防丢私钥损失,3把私钥中任意一把即可签名发币,即使丢失2把都可以保障不受损失;
  • 2 of 3,提高私钥冗余度的同时解决单点信任问题。3把私钥任意2把私钥可签名发币,三方不完全信任的情形,即中介交易中,非常适用;
  • 3 of 3,最大程度解决资金信任问题,无私钥冗余。必须3把私钥全部签名才能发币,适用多方共同管理重要资产,但任何一方遗失私钥均造成严重损失;

合成地址的交易构造、签名、发送过程与普通交易类似,这里只介绍如何创建一个合成地址。大神Gavin Andresen已经演示过,下面内容摘自其gist.

首先,需要三对公钥、私钥。公钥创建地址、私钥用于签名。

1
2
3
4
5
6
# No.1
0491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f86 / 5JaTXbAUmfPYZFRwrYaALK48fN6sFJp4rHqq2QSXs8ucfpE4yQU
# No.2
04865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec6874 / 5Jb7fCeh1Wtm4yBBg3q3XbT6B525i17kVhy3vMC9AqfR6FH2qGk
# No.3
048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d46213 / 5JFjmGo5Fww9p8gvx48qBYDJNAzR9pmH5S389axMtDyPT8ddqmw

使用命令:createmultisig <nrequired> <'["key","key"]'>来合成,其中key为公钥,创建地址时仅需公钥。创建类型是2 of 3.

输入:

1
2
bitcoind createmultisig 2 \
'["0491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f86","04865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec6874","048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d46213"]'

输出:

1
2
3
4
{
"address" : "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC",
"redeemScript" : "52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d4621353ae"
}

得到的合成地址是:3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC,该地址没有公钥,仅有redeemScript,作用与公钥相同。后续的构造、签名、发送过程与上文普通地址交易类似,略去。

比特币交易构成(一)

简介

交易(Transaction)是比特币系统的信息载体,最小单元。而块(Block)就是将这些基础单元打包装箱,贴上封条,并串联起来。巨大算力保障了块的安全,也就保障了单个交易的安全。

类型

交易有三种常见类型:产量交易(Generation),合成地址交易(Script Hash),通用地址交易(Pubkey Hash)。该分类并非严格意义的,只是根据交易的输入输出做的简单区分。

Generation TX

每个Block都对应一个产量交易(Generation TX),该类交易是没有输入交易的,挖出的新币是所有币的源头。

Script Hash TX

该类交易目前不是很常见,大部分人可能没有听说过,但是非常有意义。未来应该会在某些场合频繁使用。该类交易的接受地址不是通常意义的地址,而是一个合成地址,以3开头(对,以3开头的也是比特币地址!)。三对公私钥,可以生成一个合成地址。在生成过程时指定n of 3中的n,n范围是[1, 3],若n=1,则仅需一个私钥签名即可花费该地址的币,若n=3,则需要三把私钥依次签名才可以。

Pubkey Hash TX

该类是最常见的交易类型,由N个输入、M个输出构成。

数据结构

交易中存放的是货币所有权的流转信息,所有权登记在比特币地址上(Public Key)。这些信息是全网公开的,以明文形式存储(比特币系统里的所有数据都是明文的),只有当需要转移货币所有权时,才需要用私钥签名来验证。

字段大小 描述 数据类型 解释
4 version, 版本 uint32_t 交易数据结构的版本号
1+ tx_in count, 输入数量 var_int 输入交易的数量
41+ tx_in tx_in[] 输入交易的数组,每个输入>=41字节
1+ tx_out count, 输出数量 var_int 输出地址的数量
9+ tx_out tx_out[] 输入地址的数组,每个输入>=9字节
4 lock_time, 锁定时间 uint32_t 见下方解释

lock_time是一个多意字段,表示在某个高度的Block之前或某个时间点之前该交易处于锁定态,无法收录进Block。

含义
0 立即生效
< 500000000 含义为Block高度,处于该Block之前为锁定(不生效)
>= 500000000 含义为Unix时间戳,处于该时刻之前为锁定(不生效)

若该笔交易的所有输入交易的sequence字段,均为INT32最大值(0xffffffff),则忽略lock_time字段。否则,该交易在未达到Block高度或达到某个时刻之前,是不会被收录进Block中的。

示例

为了演示方便,我们读取稍早期的块数据,以高度116219 Block为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ~  bitcoind getblock 0000000000007c639f2cbb23e4606a1d022fa4206353b9d92e99f5144bd74611           
{
"hash" : "0000000000007c639f2cbb23e4606a1d022fa4206353b9d92e99f5144bd74611",
"confirmations" : 144667,
"size" : 1536,
"height" : 116219,
"version" : 1,
"merkleroot" : "587fefd748f899f84d0fa1d8a3876fdb406a4bb8f54a31445cb72564701daea6",
"tx" : [
"be8f08d7f519eb863a68cf292ca51dbab7c9b49f50a96d13f2db32e432db363e",
"a387039eca66297ba51ef2da3dcc8a0fc745bcb511e20ed9505cc6762be037bb",
"2bd83162e264abf59f9124ca517050065f8c8eed2a21fbf85d454ee4e0e4c267",
"028cfae228f8a4b0caee9c566bd41aed36bcd237cdc0eb18f0331d1e87111743",
"3a06b6615756dc3363a8567fbfa8fe978ee0ba06eb33fd844886a0f01149ad62"
],
"time" : 1301705313,
"nonce" : 1826107553,
"bits" : "1b00f339",
"difficulty" : 68977.78463021,
"previousblockhash" : "00000000000010d549135eb39bd3bbb1047df8e1512357216e8a85c57a1efbfb",
"nextblockhash" : "000000000000e9fcc59a6850f64a94476a30f5fe35d6d8c4b4ce0b1b04103a77"
}

该Block里面有5笔交易,第一笔为Generation TX,解析出来看一下具体内容:

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
# ~  bitcoind getrawtransaction be8f08d7f519eb863a68cf292ca51dbab7c9b49f50a96d13f2db32e432db363e 1
{
"hex" : "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff070439f3001b0134ffffffff014034152a010000004341045b3aaa284d169c5ae2d20d0b0673468ed3506aa8fea5976eacaf1ff304456f6522fbce1a646a24005b8b8e771a671f564ca6c03e484a1c394bf96e2a4ad01dceac00000000",
"txid" : "be8f08d7f519eb863a68cf292ca51dbab7c9b49f50a96d13f2db32e432db363e",
"version" : 1,
"locktime" : 0,
"vin" : [
{
"coinbase" : "0439f3001b0134",
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 50.01000000,
"n" : 0,
"scriptPubKey" : {
"asm" : "045b3aaa284d169c5ae2d20d0b0673468ed3506aa8fea5976eacaf1ff304456f6522fbce1a646a24005b8b8e771a671f564ca6c03e484a1c394bf96e2a4ad01dce OP_CHECKSIG",
"hex" : "41045b3aaa284d169c5ae2d20d0b0673468ed3506aa8fea5976eacaf1ff304456f6522fbce1a646a24005b8b8e771a671f564ca6c03e484a1c394bf96e2a4ad01dceac",
"reqSigs" : 1,
"type" : "pubkey",
"addresses" : [
"1LgZTvoTJ6quJNCURmBUaJJkWWQZXkQnDn"
]
}
}
],
"blockhash" : "0000000000007c639f2cbb23e4606a1d022fa4206353b9d92e99f5144bd74611",
"confirmations" : 145029,
"time" : 1301705313,
"blocktime" : 1301705313
}

Generation TX的输入不是一个交易,而带有coinbase字段的结构。该字段的值由挖出此Block的人填写,这是一种“特权”:可以把信息写入货币系统(大家很喜欢用系统中的数据结构字段名来命名站点,例如blockchain、coinbase等,这些词的各种后缀域名都被抢注一空)。中本聪在比特币的第一个交易中的写入的coinbase值是:

1
"coinbase":"04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73"

将该段16进制转换为ASCII字符,就是那段著名的创世块留言:

1
The Times 03/Jan/2009 Chancellor on brink of second bailout for banks

接下来展示的是一个三个输入、两个输出的普通交易:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
# ~  bitcoind getrawtransaction 028cfae228f8a4b0caee9c566bd41aed36bcd237cdc0eb18f0331d1e87111743 1
{
"hex" : "0100000003c9f3b07ebfca68fd1a6339d0808fbb013c90c6095fc93901ea77410103489ab7000000008a473044022055bac1856ecbc377dd5e869b1a84ed1d5228c987b098c095030c12431a4d5249022055523130a9d0af5fc27828aba43b464ecb1991172ba2a509b5fbd6cac97ff3af0141048aefd78bba80e2d1686225b755dacea890c9ca1be10ec98173d7d5f2fefbbf881a6e918f3b051f8aaaa3fcc18bbf65097ce8d30d5a7e5ef8d1005eaafd4b3fbeffffffffc9f3b07ebfca68fd1a6339d0808fbb013c90c6095fc93901ea77410103489ab7010000008a47304402206b993231adec55e6085e75f7dc5ca6c19e42e744cd60abaff957b1c352b3ef9a022022a22fec37dfa2c646c78d9a0753d56cb4393e8d0b22dc580ef1aa6cccef208d0141042ff65bd6b3ef04253225405ccc3ab2dd926ff2ee48aac210819698440f35d785ec3cec92a51330eb0c76cf49e9e474fb9159ab41653a9c1725c031449d31026affffffffc98620a6c40fc7b3a506ad79af339541762facd1dd80ff0881d773fb72b230da010000008b483045022040a5d957e087ed61e80f1110bcaf4901b5317c257711a6cbc54d6b98b6a8563f02210081e3697031fe82774b8f44dd3660901e61ac5a99bff2d0efc83ad261da5b4f1d014104a7d1a57e650613d3414ebd59e3192229dc09d3613e547bdd1f83435cc4ca0a11c679d96456cae75b1f5563728ec7da1c1f42606db15bf554dbe8a829f3a8fe2fffffffff0200bd0105000000001976a914634228c26cf40a02a05db93f2f98b768a8e0e61b88acc096c7a6030000001976a9147514080ab2fcac0764de3a77d10cb790c71c74c288ac00000000",
"txid" : "028cfae228f8a4b0caee9c566bd41aed36bcd237cdc0eb18f0331d1e87111743",
"version" : 1,
"locktime" : 0,
"vin" : [
{
"txid" : "b79a4803014177ea0139c95f09c6903c01bb8f80d039631afd68cabf7eb0f3c9",
"vout" : 0,
"scriptSig" : {
"asm" : "3044022055bac1856ecbc377dd5e869b1a84ed1d5228c987b098c095030c12431a4d5249022055523130a9d0af5fc27828aba43b464ecb1991172ba2a509b5fbd6cac97ff3af01 048aefd78bba80e2d1686225b755dacea890c9ca1be10ec98173d7d5f2fefbbf881a6e918f3b051f8aaaa3fcc18bbf65097ce8d30d5a7e5ef8d1005eaafd4b3fbe",
"hex" : "473044022055bac1856ecbc377dd5e869b1a84ed1d5228c987b098c095030c12431a4d5249022055523130a9d0af5fc27828aba43b464ecb1991172ba2a509b5fbd6cac97ff3af0141048aefd78bba80e2d1686225b755dacea890c9ca1be10ec98173d7d5f2fefbbf881a6e918f3b051f8aaaa3fcc18bbf65097ce8d30d5a7e5ef8d1005eaafd4b3fbe"
},
"sequence" : 4294967295
},
{
"txid" : "b79a4803014177ea0139c95f09c6903c01bb8f80d039631afd68cabf7eb0f3c9",
"vout" : 1,
"scriptSig" : {
"asm" : "304402206b993231adec55e6085e75f7dc5ca6c19e42e744cd60abaff957b1c352b3ef9a022022a22fec37dfa2c646c78d9a0753d56cb4393e8d0b22dc580ef1aa6cccef208d01 042ff65bd6b3ef04253225405ccc3ab2dd926ff2ee48aac210819698440f35d785ec3cec92a51330eb0c76cf49e9e474fb9159ab41653a9c1725c031449d31026a",
"hex" : "47304402206b993231adec55e6085e75f7dc5ca6c19e42e744cd60abaff957b1c352b3ef9a022022a22fec37dfa2c646c78d9a0753d56cb4393e8d0b22dc580ef1aa6cccef208d0141042ff65bd6b3ef04253225405ccc3ab2dd926ff2ee48aac210819698440f35d785ec3cec92a51330eb0c76cf49e9e474fb9159ab41653a9c1725c031449d31026a"
},
"sequence" : 4294967295
},
{
"txid" : "da30b272fb73d78108ff80ddd1ac2f76419533af79ad06a5b3c70fc4a62086c9",
"vout" : 1,
"scriptSig" : {
"asm" : "3045022040a5d957e087ed61e80f1110bcaf4901b5317c257711a6cbc54d6b98b6a8563f02210081e3697031fe82774b8f44dd3660901e61ac5a99bff2d0efc83ad261da5b4f1d01 04a7d1a57e650613d3414ebd59e3192229dc09d3613e547bdd1f83435cc4ca0a11c679d96456cae75b1f5563728ec7da1c1f42606db15bf554dbe8a829f3a8fe2f",
"hex" : "483045022040a5d957e087ed61e80f1110bcaf4901b5317c257711a6cbc54d6b98b6a8563f02210081e3697031fe82774b8f44dd3660901e61ac5a99bff2d0efc83ad261da5b4f1d014104a7d1a57e650613d3414ebd59e3192229dc09d3613e547bdd1f83435cc4ca0a11c679d96456cae75b1f5563728ec7da1c1f42606db15bf554dbe8a829f3a8fe2f"
},
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 0.84000000,
"n" : 0,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 634228c26cf40a02a05db93f2f98b768a8e0e61b OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a914634228c26cf40a02a05db93f2f98b768a8e0e61b88ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"1A3q9pDtR4h8wpvyb8SVpiNPpT8ZNbHY8h"
]
}
},
{
"value" : 156.83000000,
"n" : 1,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 7514080ab2fcac0764de3a77d10cb790c71c74c2 OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a9147514080ab2fcac0764de3a77d10cb790c71c74c288ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"1Bg44FZsoTeYteRykC1XHz8facWYKhGvQ8"
]
}
}
],
"blockhash" : "0000000000007c639f2cbb23e4606a1d022fa4206353b9d92e99f5144bd74611",
"confirmations" : 147751,
"time" : 1301705313,
"blocktime" : 1301705313
}

字段hex记录了所有相关信息,后面显示的是hex解析出来的各类字段信息。下面把逐个分解hex内容(hex可以从上面的直接看到):

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
01000000   // 版本号,UINT32
03 // Tx输入数量,变长INT。3个输入。

/*** 第一组Input Tx ***/
// Tx Hash,固定32字节
c9f3b07ebfca68fd1a6339d0808fbb013c90c6095fc93901ea77410103489ab7
00000000 // 消费的Tx位于前向交易输出的第0个,UINT32,固定4字节
8a // 签名的长度, 0x8A = 138字节
// 138字节长度的签名,含有两个部分:公钥+签名
47 // 签名长度,0x47 = 71字节
3044022055bac1856ecbc377dd5e869b1a84ed1d5228c987b098c095030c12431a4d5249022055523130a9d0af5fc27828aba43b464ecb1991172ba2a509b5fbd6cac97ff3af01
41 // 公钥长度,0x41 = 65字节
048aefd78bba80e2d1686225b755dacea890c9ca1be10ec98173d7d5f2fefbbf881a6e918f3b051f8aaaa3fcc18bbf65097ce8d30d5a7e5ef8d1005eaafd4b3fbe
ffffffff // sequence,0xffffffff = 4294967295, UINT32, 固定4字节

/*** 第二组Input Tx。与上同理,省略分解 ***/
c9f3b07ebfca68fd1a6339d0808fbb013c90c6095fc93901ea77410103489ab7010000008a47304402206b993231adec55e6085e75f7dc5ca6c19e42e744cd60abaff957b1c352b3ef9a022022a22fec37dfa2c646c78d9a0753d56cb4393e8d0b22dc580ef1aa6cccef208d0141042ff65bd6b3ef04253225405ccc3ab2dd926ff2ee48aac210819698440f35d785ec3cec92a51330eb0c76cf49e9e474fb9159ab41653a9c1725c031449d31026affffffff

/*** 第三组Input Tx ***/
c98620a6c40fc7b3a506ad79af339541762facd1dd80ff0881d773fb72b230da010000008b483045022040a5d957e087ed61e80f1110bcaf4901b5317c257711a6cbc54d6b98b6a8563f02210081e3697031fe82774b8f44dd3660901e61ac5a99bff2d0efc83ad261da5b4f1d014104a7d1a57e650613d3414ebd59e3192229dc09d3613e547bdd1f83435cc4ca0a11c679d96456cae75b1f5563728ec7da1c1f42606db15bf554dbe8a829f3a8fe2fffffffff

02 // Tx输出数量,变长INT。两个输出。

/*** 第一组输出 ***/
00bd010500000000 // 输出的币值,UINT64,8个字节。字节序需翻转,~= 0x000000000501bd00 = 84000000 satoshi
19 // 输出目的地址字节数, 0x19 = 25字节,由一些操作码与数值构成
// 目标地址
// 0x76 -> OP_DUP(stack ops)
// 0xa9 -> OP_HASH160(crypto)
// 0x14 -> 长度,0x14 = 20字节
76 a9 14
// 地址的HASH160值,20字节
634228c26cf40a02a05db93f2f98b768a8e0e61b
// 0x88 -> OP_EQUALVERIFY(bit logic)
// 0xac -> OP_CHECKSIG(crypto)
88 ac

/*** 第二组输出 ***/
c096c7a603000000
19
76 a9 14 7514080ab2fcac0764de3a77d10cb790c71c74c2 88 ac

00000000 // lock_time,UINT32,固定4字节

Tx Hash,俗称交易ID,由hex得出:Tx Hash = SHA256(SHA256(hex))。由于每个交易只能成为下一个的输入,有且仅有一次,那么不存在输入完全相同的交易,那么就不存在相同的Tx Hash(SHA256碰撞概率极小,所以无需考虑Hash碰撞的问题,就像无需考虑地址私钥被别人撞到一样)。

即便如此,在系统里依然产生了相同的Tx Hash,是某位矿工兄弟挖出Block后,打包Block时忘记修改Generation Tx coinbase字段的值,币量相同且输出至相同的地址,那么就构造了两个完全一模一样的交易,分别位于两个Block的第一个位置。这个对系统不会产生什么问题,但只要花费其中一笔,另一个也被花费了。相同的Generation Tx相当于覆盖了另一个,白白损失了挖出的币。该交易ID为e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468,第一次出现在#91722,第二次出现在#91880

qq20131027-2

交易签名

签名是对所有权的验证,节点收到交易广播后,会对交易进行验证,通过后则收录进内存、打包进Block,否则,丢弃之。签名就类似传统纸质合同盖章、签字过程,合法转移所有权的保证手段。

签名类型

由于一个交易的输入、输出都可能具有多个,那么签名也具有多种类型,目前共三类:SIGHASH_ALL, SIGHASH_NONE, SIGHASH_SINGLE。

SIGHASH_ALL

该签名类型为默认类型,也是目前绝大部分交易采用的,顾名思义即签名整单交易。首先,组织所有输出、输入,就像上文分解Hex过程一样,每个输入都对应一个签名,暂时留空,其他包括sequence等字段均须填写,这样就形成了一个完整的交易Hex(只缺签名字段)。然后,每一个输入均需使用私钥对该段数据进行签名,签名完成后各自填入相应的位置,N个输入N个签名。简单理解就是:对于该笔单子,认可且只认可的这些输入、输出,并同意花费我的那笔输入。

SIGHASH_NONE

该签名类型是最自由松散的,仅对输入签名,不对输出签名,输出可以任意指定。某人对某笔币签名后交给你,你可以在任意时刻填入任意接受地址,广播出去令其生效。简单理解就是:我同意花费我的那笔钱,至于给谁,我不关心。

SIGHASH_SINGLE

该签名类型其次自由松散,仅对自己的输入、输出签名,并留空sequence字段。其输入的次序对应其输出的次序,比如输入是第3个,那么签名的输出也是第三个。简单理解就是:我同意花费我的那笔钱,且只能花费到我认可的输出,至于单子里的其他输入、输出,我不关心。