交易(一)


图片来源

本篇是一篇译文,原文地址
交易,是比特币系统中最重要的组成部分。比特币系统的设计,围绕着确保交易的创建/传播/合法性验证,最后将其添加全局大账本而展开。
交易,是参与者之间传输的数据结构。每笔交易,是比特币上区块链(全球复式记账账本)的公共实体。

交易的生命周期

交易的生命周期,从交易的创建开始;接着,被一个或多个签名授权资金花费;随后,该交易被广播在比特币网络中,每个网络节点(参与者)将验证交易的有效性,并传播该交易,直到其达到(或者说几乎到达)所有的区块链节点。

一旦被足够的区块记录并确认,交易将被永久的记录在比特币账本中,并所有的区块所接受。新的资金拥有者,可以将其在新的交易中花费它们,这就是一个新的交易生命周期的开始了。

创建交易

与支票支付类似,交易表达了一个意图:转移价值,它是不可见的,直到其在金融系统中被提交并执行。如支票一样,交易最终的创建者,不需要签名交易。然而,支票引用的是特定账户作为资金来源,而比特币交易引用的是特定的先前的交易作为其来源,而不是账户。

交易可以被在线创建,也可以被离线创建,甚至用户可以创建一个还没有被签名者授权的交易。举例而言,应付文件可能会处理被 CEO 签名的支付支票。类似的,应付文员也可以创建一个比特币交易,然后再让 CEO 签名使其合法。

一旦交易被创建,其被相关资金拥有者进行了签名。如果其被正确的创建并签名,且签名是合法的,其将完成相关价值的转移。最终,合法的交易将发送到比特币网络中,直到矿工将他们打包成区块包含进公共账本(区块链)中。

将交易广播到比特币网路

交易需要被发布到比特币网络中,以便其能传播并被记录在区块链上。本质上讲,一个交易数据大概在 300 到 400 字节,将到达成千上万个网络节点上。发送者不需要信任他们广播的节点,节点也不需要信任发送者,不需要查证发送者的身份。因为,交易是被签名的,不包含任何机密信息,比如私钥或者证书,它可以被公开地通过任何底层的网络传输。不像是信用卡交易,举例而言,在信用卡交易中会包含敏感数据,需要传输在加密网络中,一个比特币交易可以被传输在任何网络中。只要交易能够达到节点中,并被传播下去,第一个是谁都没关系。

比特币交易可以通过不可安全的网络完成传递,比如 WIFI/ 蓝牙/ NFC / 调频信号/ 条形码/ 粘贴复制到web表单中。极端情况下,甚至可以通过封包无线电,卫星数据转发,短波,光谱,跳频 来完成。比特币交易,还可以通过表情包编码并发布的公共论坛。因此,任何人,都可以创建并执行比特币交易。

在比特币网络中传递交易

一旦交易被发送网络中任意节点上,它的合法性将被验证。如果是合法的,节点将其传播给它连接的其他节点,同时,成功的消息将被反馈给交易创建者。
如果交易是不合法的,节点将拒绝它,并返回创建者错误信息。

比特币网路,是一个点对点网络,这就意味着,每个比特币节点都会连接到一些其他节点上,在节点启动的时候,会根据点对点协议去发现附近节点。
这个网络就是一个松散的相互连接的 mesh,没有固定的拓扑结构使得所有节点都互连。
包括交易和区块,都是从一个节点,往其相连的节点上传播。
一个合法的交易,被任意节点接收后,将被发送给其相连的三到四个附近节点,以此类推,几秒钟内,以指数传播的速度,将比将被传播到整个网路中。

比特币网路被设计为这种高效且有弹性的方式快速传播交易和区块到整个网络中。为了防止垃圾信息/ 服务攻击/ 以及其他有害攻击,每个节点都将在传播数据之前验证数据的合法性。如果交易有问题,将不会被传播。更详细的解释,可以看 交易的独立验证

交易的数据结构

交易,是一种数据结构。这种结构对价值转移做了编码:价值的来源,称为 input;价值的目的地,称为 output。交易的输入和输出不会关联到账号或者身份信息。反而,所有价值其实是绑定在一个特殊的密钥上。知道该密钥的任何人,都可以解锁并获取其关联的比特币。

交易结构的数据包括:

大小 描述
4 bytes Version 指定交易应该遵守的规则版本
1-9 bytes(VarInt) Input Counter 包含有多少输入
Variable Inputs 一个或者多个交易输入
1-9 bytes(VarInt) Output Counter 包含有多少输出
Variable Onputs 一个或者多个交易输出
4 bytes Locktime 时间戳或者区块号

交易的 Locktime
Locktime 定义了交易可以被添加到区块链中的最早时间。通常其被设置为 0,即立即执行。如果 Locktime 是非 0 的,但低于 500 百万,它将被解释为区块高度。如果该值超过 500 百万,将被解释为 Unix 时间戳。

交易的输出和输入

交易的基础,是 未花费交易输出(UTXO)。UTXO 是整个网络中,将比特币货币锁定给一个指定的拥有者,并将其记录在区块链中的不可分割的货币单元。
比特币网络中可用的 UNXO 目前已经有数百万个。当用户接收到比特币时,其数量以 UTXO 的形式记录在区块链上。因此,用户的比特币实际上就是分散在各个区块中的数百个交易中的 UTXO 中。事实上,在区块链中并没有存储一个地址或者账户的余额,只有这些分散的 UTXO,锁定到了指定的拥有者。用户比特币余额的概念,是钱包应用中的一个概念。钱包将扫描整个区块链,聚合属于指定用户的所有 UTXO,从而计算出余额。

TIP
在比特币中,没有账户或者余额,只有分散在区块链上的未花费交易输出。

一个 UTXO 的面额,可以是一聪的任意倍。就像美元,可以向下分割到小数点后两位,作为美分。比特币可以分割到小数点后8位,作为一聪。
虽然,UTXO 可以是任意值,一旦被创建,它将不可再分割,就像一个硬币不能再被砍成两半。如果一个 UTXO 比交易中期望的额度高,它也将完全被消耗,多出来的部分,将被重新创建。换而言之,如果你有 20 个比特币的 UTXO,但是你想支付 1 比特币,你的交易必须消耗整个 20 个比特币的 UTXO,输出部分将有两项:一个用于支付想发给指定接收者的 1 比特币,另外 19 个比特币返回到钱包。因此,很多比特币交易将产生零钱。

想象一次,去商店买了 1.5$ 的饮料,你会去钱包中找到足够 1.5$ 的硬币或者纸钞。如果有的话,你可能会找到恰好 1.5$ 的钱(一美元纸币,两个25美分硬币,或者 6 个 25 美分硬币),也有可能,你找到了 5 美元纸币。如果你给 5 美元给商店,她应该找你 3.5 美元零钱,并将这 3.5 美元放回钱包,用以后续可能的交易。

相似的,比特币交易必须从用户可用的 UTXO 中开始,用户无法切割 UTXO,就像他们无法将1美元纸币切割成两半来使用。用户的钱包应用程序会选择能够满足用户交易的 UTXO ,这个额度,应当大于或等于用户期望交易出去的额度。

实际上,比特币应用可以使用一些策略,满足支付额度:组合若干个较小额度;找到满足支付需求的额度;或者使用一个较大额度的但是要找零。这些复杂的花费 UTXO 的装配方式,是在用户钱包中自动完成的,对用户不可见。除非你是开发者,否则这些都给你没有关系。

UTXO 的消耗,在交易中被成为交易输入;创建 UTXO 的过程被称为交易输出。这样一来,比特币价值随着交易链不断输入和输出而前向移动。通过签名的方式,解锁消耗 UTXO,通过校验签名即新拥有者比特币地址的方式,锁定 UTXO。

在这条输入输出链上,一个例外的交易是 铸币交易(coinbase transaction)。这种交易,将是每个区块中的第一个交易,是为了奖励给获胜的矿工。

TIP
输入和输出谁是第一个?先有鸡还是现有蛋?严格的说,现有铸币交易,铸币交易产生新的比特币,这种交易,没有输入。

交易输出

每个交易中产生的 输出,被记录在比特币账本中。除了一种特例(查看数据输出(OPT_RETURN)),几乎所有的输出,都创建了可花费的 UTXO,拥有者可以使用这些 UTXO 进行后续的交易。向某人发送比特币,就是向指定的地址注册 UTXO.

每个全节点比特币客户端,都将跟踪 UTXO ,并将它们存储在内存中,称为 UTXO 集合或者 UTXO 池。新的交易消耗将从该池中消耗 UTXO.

交易输出包含两个部分:

  • 比特币总额,单位为 聪,比特币最小单位。
  • 锁定脚本,通过指定的条件,将总额锁定到满足条件的输出上。

交易脚本语言,使用在锁定脚本中,如下表所示:

大小 描述
8 bytes Amount 比特币额度,以 聪 为单位(10^-8 比特币)
1-9 bytes(VarInt) Locking-Script Size 锁定脚本的长度
Variable Lock-Script 定义要花费的输出的指定条件

如下所示,我们使用 blockchain.info 中的 API ,查找指定地址的 UTXO.

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
# get unspent outputs from blockchain API

import json
import requests

# example address
address = '1Dorian4RoXcnBv9hnQ4Y2C1an6NJ4UrjX'

# The API URL is https://blockchain.info/unspent?active=<address>
# It returns a JSON object with a list "unspent_outputs", containing UTXO, like this:
#{ "unspent_outputs":[
# {
# "tx_hash":"ebadfaa92f1fd29e2fe296eda702c48bd11ffd52313e986e99ddad9084062167",
# "tx_index":51919767,
# "tx_output_n": 1,
# "script":"76a9148c7e252f8d64b0b6e313985915110fcfefcf4a2d88ac",
# "value": 8000000,
# "value_hex": "7a1200",
# "confirmations":28691
# },
# ...
#]}

resp = requests.get('https://blockchain.info/unspent?active=%s' % address)
utxo_set = json.loads(resp.text)["unspent_outputs"]

for utxo in utxo_set:
print "%s:%d - %ld Satoshis" % (utxo['tx_hash'], utxo['tx_output_n'], utxo['value'])

运行该脚本,我们将看到交易 ID,冒号,指定 UTXO 的索引,以及 UTXO 值。

1
2
3
4
5
6
$ python get-utxo.py
ebadfaa92f1fd29e2fe296eda702c48bd11ffd52313e986e99ddad9084062167:1 - 8000000 Satoshis
6596fd070679de96e405d52b51b8e1d644029108ec4cbfe451454486796a1ecf:0 - 16050000 Satoshis
74d788804e2aae10891d72753d1520da1206e6f4f20481cc1555b7f2cb44aca0:0 - 5000000 Satoshis
b2affea89ff82557c60d635a2a3137b8f88f12ecec85082f7d0a1f82ee203ac4:0 - 10000000 Satoshis
...

花费条件

交易输出,将特定的额度(以聪为单位)于产权负担相关联。在大多数情况下,锁定脚本将锁定输出到指定的比特币地址,因此,相当于转换该总额的所有权。当 Alice 向 Bob的咖啡 店支付一杯咖啡费用,她的交易就是创建一个 0.015 比特币的输出并将其锁定到咖啡店的比特币地址。0.015 比特币的输出,将被记录在区块链上,称为 UNXO 池的一部分,并将显示在 Bob 钱包余额中可见。当 Bob 选择要花费这部分 UTXO 时,他的交易将释放这部分产权负担,解锁这部分输出,并使用 Bob 的私钥签名。

交易输入

简单地说,交易输入就是指向 UTXO 的指针。它们通过交易哈希和序列号指向特定的 UTXO。为了花费 UTXO,交易的输入应当包含解锁脚本,以满足花费条件。解锁脚本通常是一个证明比特币拥有权限的签名。

当用户支付时,它们的钱包将选择可以使用的 UTXO 构建一个交易,举个例子,为了支付 0.015 比特币,钱包应用选择了 0.01 UTXO 和 0.005 UTXO,选择这二者,进行期望的交易输入。

在下面的代码块中,我们使用了 “greedy” 算法选择可用的 UTXO 以完成特定的支付。在这个示例中,可用的 UTXO 被放在一个常量数组中,但是实际上,可用的 UTXO 可能通过 RPC 调用 BitCoin Core,或者第三方 API 来完成。

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
# Selects outputs from a UTXO list using a greedy algorithm.

from sys import argv

class OutputInfo:

def __init__(self, tx_hash, tx_index, value):
self.tx_hash = tx_hash
self.tx_index = tx_index
self.value = value

def __repr__(self):
return "<%s:%s with %s Satoshis>" % (self.tx_hash, self.tx_index,
self.value)

# Select optimal outputs for a send from unspent outputs list.
# Returns output list and remaining change to be sent to
# a change address.
def select_outputs_greedy(unspent, min_value):
# Fail if empty.
if not unspent:
return None
# Partition into 2 lists.
lessers = [utxo for utxo in unspent if utxo.value < min_value]
greaters = [utxo for utxo in unspent if utxo.value >= min_value]
key_func = lambda utxo: utxo.value
if greaters:
# Not-empty. Find the smallest greater.
min_greater = min(greaters)
change = min_greater.value - min_value
return [min_greater], change
# Not found in greaters. Try several lessers instead.
# Rearrange them from biggest to smallest. We want to use the least
# amount of inputs as possible.
lessers.sort(key=key_func, reverse=True)
result = []
accum = 0
for utxo in lessers:
result.append(utxo)
accum += utxo.value
if accum >= min_value:
change = accum - min_value
return result, "Change: %d Satoshis" % change
# No results found.
return None, 0

def main():
unspent = [
OutputInfo("ebadfaa92f1fd29e2fe296eda702c48bd11ffd52313e986e99ddad9084062167", 1, 8000000),
OutputInfo("6596fd070679de96e405d52b51b8e1d644029108ec4cbfe451454486796a1ecf", 0, 16050000),
OutputInfo("b2affea89ff82557c60d635a2a3137b8f88f12ecec85082f7d0a1f82ee203ac4", 0, 10000000),
OutputInfo("7dbc497969c7475e45d952c4a872e213fb15d45e5cd3473c386a71a1b0c136a1", 0, 25000000),
OutputInfo("55ea01bd7e9afd3d3ab9790199e777d62a0709cf0725e80a7350fdb22d7b8ec6", 17, 5470541),
OutputInfo("12b6a7934c1df821945ee9ee3b3326d07ca7a65fd6416ea44ce8c3db0c078c64", 0, 10000000),
OutputInfo("7f42eda67921ee92eae5f79bd37c68c9cb859b899ce70dba68c48338857b7818", 0, 16100000),
]

if len(argv) > 1:
target = long(argv[1])
else:
target = 55000000

print "For transaction amount %d Satoshis (%f bitcoin) use: " % (target, target/10.0**8)
print select_outputs_greedy(unspent, target)

if __name__ == "__main__":
main()

当我们运行 select-utxo.py 脚本,它将创建一个 UTXO 集合,用于支付 55,000,000 聪(0.55比特币)的比特币。如果你使用了目标支付额度作为参数,脚本将选择 UTXO 以满足该额度。

1
2
3
$ python select-utxo.py 50000000
For transaction amount 50000000 Satoshis (0.500000 bitcoin) use:
([<7dbc497969c7475e45d952c4a872e213fb15d45e5cd3473c386a71a1b0c136a1:0 with 25000000 Satoshis>, <7f42eda67921ee92eae5f79bd37c68c9cb859b899ce70dba68c48338857b7818:0 with 16100000 Satoshis>, <6596fd070679de96e405d52b51b8e1d644029108ec4cbfe451454486796a1ecf:0 with 16050000 Satoshis>], 'Change: 7150000 Satoshis')

一旦,UTXO 被选定,钱包将为每个 UNXO 生成包含签名的解锁脚本,即,使它们是可花费的,且能够满足锁定脚本的条件。下表中,将描述每个交易输入的数据结构。

大小 描述
32 bytes 交易哈希 指向一个包含了可花费 UTXO 的交易
4 bytes Output Index 将被花费的 UTXO 的索引数量
1-9 bytes 解锁脚本的长度 解锁脚本的长度
Variable Unlocking-Script 脚本脚本
4 bytes Sequence Number 目前该值不可用,设置为 0xFFFFFFFF

© 2024 YueGS