深入理解比特币:(1)钱包和地址
回顾
上一章我们了解了比特币钱包的生成过程,一般过程如下:
-
生成一个随机数作为种子。
-
使用种子生成一个随机私钥。
-
从私钥派生公钥。
-
使用公钥生成比特币地址。
-
打印私钥、公钥和地址。
今天我们会拆解分析这背后的原理。主要涉及到的是 BIP-0032.
私钥
私钥生成过程如下:
1 let mut entropy = [0u8; 32];
2 let sk = ExtendedPrivKey::new_master(Network::Bitcoin, &entropy)
可以看到,私钥来自于一个 32 bytes, 即 256 bit 的随机数。
主私钥
调用 ExtendedPrivKey::new_master
生成了一个主密钥,这背后使用了 HMAC-SHA512,后者可理解为:以密钥和消息为输入,通过SHA512哈希函数,结合密钥对消息进行多轮哈希处理,生成消息认证码。消息认证码是基于密钥和消息,通过特定算法生成的一段固定长度的值,可以简单理解为 hmac = hash(key * data)
也即类似 CRUD 程序员们熟知的加盐哈希的东西。
主私钥派生出子私钥
通过主私钥可以派生出一系列的子私钥,每个子私钥都与一个唯一的索引相关联。在派生子私钥的同时,也生成了新的链码,用于派生下一级的子私钥。可以重复以上步骤,从任何一级的私钥派生出更多的子私钥,形成层次结构。每个子私钥后续都可以转换成一个地址。
派生路径表示
实际开发中可能会看到这样的东西:
1 // 推导子私钥
2 let path = DerivationPath::from_str("m/0'/0/1").unwrap();
3 let derived_private_key = master_private_key.derive_priv(&Secp256k1::new(), &path).unwrap();
"m/0'/0/1"
用于推导比特币钱包中的子密钥。它遵循了 BIP32 中定义的路径规范。
在 BIP32 中,路径由一系列索引组成,用斜杠分隔。每个索引表示从当前密钥派生的子密钥的索引。
以下是 "m/0'/0/1"
路径的解释:
-
"m"
:表示根密钥(Master Key)。它是从根密钥开始的起点。 -
"0'"
:表示强化派生(Hardened Derivation)。这意味着从该索引开始的子密钥只能通过父密钥的私钥推导得到,而不能通过父密钥的公钥推导得到。强化派生的索引范围是从2^31
到2^32-1
。 -
"0"
:表示子密钥索引。在这种情况下,它是一个普通的派生索引,可以通过父密钥的公钥或私钥推导得到。 -
"1"
:表示子密钥索引。与上面的索引类似,它是一个普通的派生索引。
在上述示例中,"m/0'/0/1"
表示从根密钥开始,通过强化派生索引 0'
推导出第一个子密钥,然后通过普通的派生索引 0
推导出第二个子密钥,最后通过普通的派生索引 1
推导出最终的密钥。
从私钥派生公钥
这个过程是生成 secp256k1 椭圆曲线上的公钥
1let res = ffi::secp256k1_ec_pubkey_create(secp.ctx, &mut pk, sk.as_c_ptr());
secp256k1_ec_pubkey_create
的大致原理:
-
首先,该函数接收一个私钥作为输入。私钥是一个随机数,通常由椭圆曲线算法生成。在 Bitcoin 中,私钥是一个 256 位的整数。
-
接下来,函数使用 secp256k1 椭圆曲线算法中的基点(也称为生成点)来计算公钥。基点是一个预定义的曲线上的固定点,对于 secp256k1 曲线来说是已知的。
-
使用私钥和基点,函数执行椭圆曲线点乘运算,将私钥乘以基点。这个运算的结果是一个新的点,即公钥点。公钥点是椭圆曲线上的一个点,它具有一对坐标 (x, y)。
-
最后,函数将公钥点的坐标进行编码,生成一个压缩或非压缩的公钥。压缩公钥只包含 x 坐标和一个标志位,非压缩公钥包含 x 和 y 坐标。在 Bitcoin 中,压缩公钥更常见,因为它更短,节省了存储空间。
具体实现这里不展开。这里得到一个普通公钥,然后可以直接用它得到地址,也可以先扩展,再得到地址。
从普通公钥到扩展公钥
在 Bitcoin 中,有两种类型的公钥:普通公钥(Regular Public Key)和扩展公钥(Extended Public Key)。它们之间的区别如下:
-
普通公钥:普通公钥是通过对私钥执行椭圆曲线点乘运算生成的公钥。它只包含公钥的坐标(通常是压缩形式的 x 坐标),没有其他额外的信息。普通公钥用于验证签名和生成 Bitcoin 地址。
-
扩展公钥:扩展公钥是基于 BIP32 中定义的 HD(Hierarchical Deterministic)钱包标准与额外的链码(Chain Code)相关联,通过路径迭代计算得到。
从公钥派生地址
在示例代码中,我们生成了一个 P2PKH 地址:
1address = bitcoin::Address::p2pkh(&ext_pk.public_key, Network::Bitcoin);
P2PKH 地址是 Bitcoin 中最常见的地址类型,但还有其它各种类型,下面将进行介绍。
地址
概览
目前 BTC 的所有地址类型如下,他们的区别主要在于结构和验证机制:
地址类型 | 结构 | 验证机制 |
---|---|---|
P2PKH (pay-to-pubkey-hash) | 以1开头的Base58编码 | 使用公钥哈希进行验证,发送者需要提供与地址相关的公钥,并且提供的公钥哈希必须与地址中的哈希匹配。 |
P2SH (pay-to-script-hash) | 以3开头的Base58编码 | 使用脚本哈希进行验证,发送者需要提供解锁脚本以证明对地址中的脚本的控制权。 |
P2WPKH (pay-to-witness-pubkey-hash) | Bech32编码 | 使用隔离见证技术进行验证,直接验证公钥哈希,发送者需要提供与地址相关的公钥,并且提供的公钥哈希必须与地址中的哈希匹配。 |
P2WSH (pay-to-witness-script-hash) | Bech32编码 | 使用隔离见证技术进行验证,直接验证脚本哈希,发送者需要提供解锁脚本以证明对地址中的脚本的控制权。 |
P2PKH和P2SH是传统的地址类型,而P2WPKH和P2WSH是基于隔离见证技术的新型地址类型。
网络前缀的完整名单:https://en.bitcoin.it/wiki/List_of_address_prefixes
P2PKH(Pay-to-Public-Key-Hash)
是比特币交易中最常见的交易输出类型之一,它是比特币钱包中常见的地址格式之一。
特点:以"1"开头
生成过程
-
hash_public_key(public_key):
-
首先将提供的公钥(public_key)进行双重哈希运算,先使用SHA-256,再使用RIPEMD-160。
-
输出结果:哈希的公钥(hashed_public_key)。
-
-
add_network_prefix(hashed_public_key, network_prefix):
-
在哈希的公钥(hashed_public_key)前面添加网络前缀(network_prefix)。网络前缀(network_prefix)通常用于标识该地址是在主网(Mainnet)还是测试网(Testnet)上。
-
输出结果:添加了网络前缀的扩展哈希的公钥(extended_hashed_public_key)。
-
-
calculate_checksum(extended_hashed_public_key):
-
对扩展哈希的公钥(extended_hashed_public_key)进行两次SHA-256哈希运算,然后取结果的前4个字节作为校验和(checksum)。
-
输出结果:校验和(checksum)。
-
-
generate_P2PKH_address(public_key, network_prefix):
-
将提供的公钥(public_key)转换为字节串。
-
调用hash_public_key函数得到哈希的公钥(hashed_public_key)。
-
调用add_network_prefix函数添加网络前缀(network_prefix)。
-
调用calculate_checksum函数计算校验和(checksum)。
-
将校验和和扩展哈希的公钥合并,并对它们进行Base58编码,得到最终的P2PKH地址(address)。
-
输出结果:P2PKH地址(address)。
-
1import hashlib
2import base58
3
4def hash_public_key(public_key):
5 hashed_public_key = hashlib.new('ripemd160', hashlib.sha256(public_key).digest()).digest()
6 print("Hashed Public Key:", hashed_public_key.hex())
7 return hashed_public_key
8
9def add_network_prefix(hashed_public_key, network_prefix):
10 extended_hashed_public_key = network_prefix + hashed_public_key
11 print("Extended Hashed Public Key with Network Prefix:", extended_hashed_public_key.hex())
12 return extended_hashed_public_key
13
14def calculate_checksum(extended_hashed_public_key):
15 checksum = hashlib.sha256(hashlib.sha256(extended_hashed_public_key).digest()).digest()[:4]
16 print("Checksum:", checksum.hex())
17 return checksum
18
19def generate_P2PKH_address(public_key, network_prefix):
20 print("\nPublic Key:", public_key.hex())
21 hashed_public_key = hash_public_key(public_key)
22 extended_hashed_public_key = add_network_prefix(hashed_public_key, network_prefix)
23 checksum = calculate_checksum(extended_hashed_public_key)
24 extended_hashed_public_key_with_checksum = extended_hashed_public_key + checksum
25 address = base58.b58encode(extended_hashed_public_key_with_checksum)
26 print("P2PKH Address:", address.decode())
27 return address
28
29public_key_hex = "02792d4659d703e1999cdfcb641470754634f88e8252e3625f26e83df815dbe123"
30public_key_bytes = bytes.fromhex(public_key_hex)
31network_prefix = b'\x00' # Mainnet prefix
32
33address = generate_P2PKH_address(public_key_bytes, network_prefix)
交易结构和验证过程
简化结构如下(后面都是简化,便于理解)
1transaction:
2 version: 1
3 inputs:
4 - previous_txid: abcdef1234567890abcdef1234567890abcdef1234567890abcdef12345678
5 output_index: 0
6 script_sig: <Signature> <Public_Key>
7 sequence: 4294967295
8 outputs:
9 - value: 1000000000
10 script_pubkey: OP_DUP OP_HASH160 <Public_Key_Hash> OP_EQUALVERIFY OP_CHECKSIG
11 lock_time: 0
在比特币交易中,script_sig
和 script_pubkey
是两个重要的脚本,它们分别用于交易输入和输出。这些脚本由操作码组成,用于验证交易的有效性。
-
script_sig
是交易输入中的解锁脚本。包括签名和公钥。 -
script_pubkey
是交易输出中的锁定脚本。它规定了接收比特币的条件,通常是一个公钥哈希。
下面我们演示这一过程:
-
首先,将
script_sig
和script_pubkey
合并成一个完整的脚本。 -
然后,将这个完整的脚本放入比特币脚本解释器中执行。
-
如果执行成功,脚本返回
TRUE
,交易有效;如果失败,脚本返回FALSE
,交易无效。
示例代码如下(假设使用Python来模拟比特币脚本解释器):
1import hashlib
2
3# 定义脚本操作码
4OP_DUP = "OP_DUP"
5OP_HASH160 = "OP_HASH160"
6OP_EQUALVERIFY = "OP_EQUALVERIFY"
7OP_CHECKSIG = "OP_CHECKSIG"
8
9# 交易输入中的 script_sig
10script_sig = "<Signature> <Public_Key>"
11
12# 交易输出中的 script_pubkey
13script_pubkey = "OP_DUP OP_HASH160 <Public_Key_Hash> OP_EQUALVERIFY OP_CHECKSIG"
14
15# 合并成完整脚本
16complete_script = script_sig + " " + script_pubkey
17
18# 模拟比特币脚本解释器执行
19def execute_script(script):
20 stack = []
21 for op in script.split():
22 if op == "OP_DUP":
23 stack.append(stack[-1])
24 elif op == "OP_HASH160":
25 public_key_hash = hashlib.new('ripemd160')
26 public_key_hash.update(stack.pop())
27 stack.append(public_key_hash.digest())
28 elif op == "OP_EQUALVERIFY":
29 if stack.pop() != stack.pop():
30 return False
31 elif op == "OP_CHECKSIG":
32 # 在实际情况中,这里会进行数字签名验证,此处仅简化演示
33 return True
34 else:
35 stack.append(op)
36 return True if len(stack) == 1 else False
37
38# 执行脚本
39result = execute_script(complete_script)
40print("Transaction is valid:", result)
栈的变化过程如下:
-
初始时,栈为空。
-
执行
script_sig
,将数字签名和公钥推入栈中:- 栈:
<Signature>
<Public_Key>
- 栈:
-
执行
script_pubkey
:-
OP_DUP
复制栈顶元素,栈变为:<Signature>
<Public_Key>
<Public_Key>
-
OP_HASH160
将栈顶元素取哈希,栈变为:<Signature>
<Public_Key>
<Public_Key_Hash>
-
推送
<Public_Key_Hash>
入栈,栈变位:<Signature>
<Public_Key>
<Public_Key_Hash>
<Public_Key_Hash>
-
OP_EQUALVERIFY
比较栈顶两个元素是否相等,如果相等则继续,否则抛出错误:- 栈变为:
<Signature>
<Public_Key>
- 栈变为:
-
OP_CHECKSIG
使用公钥验证签名的有效性,如果验证通过则继续,否则抛出错误。
-
-
最终,栈中仅剩余一个元素,表示验证通过。
安全性
由于交易可以随便创建,那么能否让 script_sig 和 script_pubkey 直接返回 True,这样让任何人的钱都无条件转给我?
这当然是不行的,原因在于你花费的 UTXO 必须是属于你的。所以你只能让你的钱变得任何人都能支取。成慈善家了哈哈。
那么换个思路,能不能提交大量的这种无效交易,对比特币集群形成 DDOS 攻击?
理论上是可以的,甚至不需要是无效交易,提交大量的查询 RPC 都行。但是你的交易在最开始的签名验证一步就挂了。而且不要小看了现在的硬件防火墙,很容易识别出这种攻击模式,把你的 IP 拉黑。
P2SH (Pay-to-Script-Hash)
地址生成过程相同,但使用 0x05 版本前缀。
交易结构和验证过程
简化结构:
1transaction:
2 version: 1
3 inputs:
4 - previous_txid: <Previous_Transaction_ID>
5 output_index: <Output_Index>
6 script_sig: <Redeem_Script>
7 sequence: <Sequence_Number>
8 outputs:
9 - value: <Amount_in_Satoshis>
10 script_pubkey: OP_HASH160 <Script_Hash> OP_EQUAL
11 lock_time: 0
验证过程:
-
提取出
script_sig
中的赎回脚本(redeem script); -
计算赎回脚本的哈希值;
-
对比计算得到的哈希值是否与P2SH地址中的脚本哈希相匹配;
-
执行赎回脚本,验证交易的有效性。
验证过程与之前描述的相似,但是与P2PKH和P2WPKH交易不同,P2SH交易的 script_pubkey
中使用了 OP_HASH160
和 OP_EQUAL
操作码来验证提供的脚本是否与地址中的脚本匹配。
这里引入的赎回脚本概念我们详细介绍。
赎回脚本
赎回脚本(redeem script)是一个包含了特定条件的比特币脚本,用于解锁P2SH交易中的输出。赎回脚本可以包含各种条件,比如多重签名、时间锁定等。
假设有两个公钥:<Public_Key_1>
和 <Public_Key_2>
,需要两个签名才能解锁该交易输出。
赎回脚本的格式如下:
OP_2 <Public_Key_1> <Public_Key_2> OP_2 OP_CHECKMULTISIG
其中,OP_2
表示需要两个签名才能通过验证,<Public_Key_1>
和 <Public_Key_2>
是两个参与者的公钥,OP_CHECKMULTISIG
用于验证多重签名。
P2WPKH (Pay-to-Witness-Pubkey-Hash)
隔离见证(Segregated Witness)地址
普通的比特币地址通常以1
开头,而SegWit地址以bc1
开头。其结构如下:
bc1q<prefix><data_part><checksum>
其中:
-
bc1q
是SegWit地址的前缀,用于表示这是一个SegWit地址。 -
<prefix>
是SegWit地址的版本前缀。 -
<data_part>
是SegWit地址的数据部分,包含了公钥哈希或脚本哈希等信息。 -
<checksum>
是SegWit地址的校验和,用于确保地址的正确性。
生成
这部分逻辑已经封装到 bech32
模块,其内部原理不再详说。只需要知道 Bech32 地址相对于传统的比特币地址具有更好的错误检测和纠正能力,更短的地址长度,并且更具人类可读性和可打印性。
1import hashlib
2import bech32
3
4def segwit_address(pubkey_bytes):
5 # 计算公钥的哈希值(hash160)
6 pubkey_hash = hashlib.new('ripemd160', hashlib.sha256(pubkey_bytes).digest()).digest()
7
8 # 生成SegWit地址
9 witness_version = 0 # SegWit版本
10 witness_program = pubkey_hash
11 return bech32.encode('bc', witness_version, witness_program)
12
13# 示例公钥
14pubkey_bytes = bytes.fromhex('036dde89b1a6bc90c832e7b3fd7a583035c3c16f0ab1a1a3a430bb23929c4ad4b4')
15segwit_addr = segwit_address(pubkey_bytes)
16print("SegWit Address:", segwit_addr)
交易结构和验证过程
P2WPKH(Pay-to-Witness-Pubkey-Hash)交易结构与普通的比特币交易结构略有不同,主要区别在于输入和输出脚本的格式以及见证数据的使用。以下是一个P2WPKH交易的简化结构:
1transaction:
2 version: 1
3 inputs:
4 - previous_txid: <Previous_Transaction_ID>
5 output_index: <Output_Index>
6 script_sig: <Witness_Script>
7 sequence: <Sequence_Number>
8 witness:
9 - <Signature>
10 - <Public_Key>
11 outputs:
12 - value: <Amount_in_Satoshis>
13 script_pubkey: <P2WPKH_Output_Script>
14 lock_time: 0
在这个结构中:
-
<Witness_Script>
是见证脚本,用于验证见证数据的有效性。 -
<Signature>
是签名数据,用于证明交易的所有者同意将UTXO用于新的交易。 -
<Public_Key>
是用于验证签名的公钥。
下面是一个P2WPKH交易的示例:
1transaction:
2 version: 1
3 inputs:
4 - previous_txid: 5e6d91aa430b4387cc6b4d8fb5ec6de99b21ad3d218c07f4329cbda54b89a2ff
5 output_index: 0
6 script_sig:
7 sequence: 4294967295
8 witness:
9 - 304502210085ff3d536b38b1024f8f2695e2b24859c5b38bbaae1e7fda9fbf7173b9770e8402205717640a4d2f5167dcdb7a5108fbb3e58b007a0b54c2fd78e5f572d24b4dbd63
10 - 03a0bfa7353a4b7227c5f6f2591ec2b8e119a7a46f8aa3c5bfc46406a49ef06f10
11 outputs:
12 - value: 100000000
13 script_pubkey: 0014be3b5f5bdf0317a6b5a6c2950be15fe8062eb47f
14 lock_time: 0
在这个示例中,交易的输入引用了之前交易的输出,包括了签名数据和公钥作为见证数据。输出部分指定了要发送的金额和P2WPKH输出脚本。
你可能注意到,P2WPKH交易的输出脚本只包含公钥哈希,而不需要额外的脚本,因此,在P2WPKH交易中,Witness_Script
字段应该是空的。
P2WPKH交易的验证过程与传统交易类似,但需要将签名和公钥从 witness
字段中提取出来进行验证。验证的步骤包括:
-
提取出交易的输入、输出、签名和公钥。
-
计算交易数据的哈希。
-
使用公钥对签名进行验证,确保它与交易数据哈希匹配。
-
验证交易输出脚本中的地址与公钥哈希匹配。
-
确认交易金额和其他条件是否满足。
如果所有条件都满足,则交易被视为有效。
为什么叫隔离见证
因为实现了将交易中的签名信息(见证数据)与其它交易数据分离。
解释这个名称的两个部分:
-
见证(Witness):这里的“见证”指的是交易签名,证明了交易发起者拥有他们所声称的比特币。。
-
隔离(Segregated):在SegWit之前,每个交易的签名信息是与交易的其它部分(例如发送者和接收者的地址、交易金额等)一起存储在区块链上的。SegWit通过将签名信息“隔离”开来,存储在一个单独的结构中,改变了这一点。这样做的好处包括但不限于减少单个交易的大小,从而在同样大小的区块中容纳更多的交易,提高网络的吞吐量。
隔离见证的引入还解决了一个称为交易延展性(Transaction Malleability)的问题。在SegWit之前,交易的签名可以被更改(虽然不会影响交易的实质内容),这意味着同一笔交易可以有多个有效的ID。通过将签名数据从交易的其余部分中分离,SegWit确保了即使签名数据被更改,交易ID也保持不变,从而增强了比特币网络的安全性和灵活性。
具体而言,之前交易的签名可以被更改的原因在于比特币的签名算法(ECDSA)存在一种特性,使得给定的签名可以有多种有效的表示。这意味着,即使不更改交易的任何实质内容(如发送者、接收者和金额),也可以通过修改签名的某些部分来改变 txid(因为 txid 通过对交易数据进行哈希运算而生成的)。这种能力被称为交易延展性问题。
P2WSH (Pay-to-Witness-Script-Hash)
交易结构
简化结构:
1transaction:
2 version: 1
3 inputs:
4 - previous_txid: <Previous_Transaction_ID>
5 output_index: <Output_Index>
6 script_sig: <Witness_Script>
7 sequence: <Sequence_Number>
8 outputs:
9 - value: <Amount_in_Satoshis>
10 script_pubkey: OP_0 <Script_Hash>
11 lock_time: 0
在这个结构中:
-
<Witness_Script>
是见证脚本,是一个被签名的脚本,用于解锁之前交易中的输出。P2WSH 中,它不再为空。 -
<Script_Hash>
是见证脚本的哈希值,用于创建P2WSH地址。
示例:
1transaction:
2 version: 1
3 inputs:
4 - previous_txid: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
5 output_index: 0
6 script_sig: <Witness_Script>
7 sequence: 4294967295
8 outputs:
9 - value: 100000000
10 script_pubkey: OP_0 0014abcdef0123456789abcdef0123456789abcdef
11 lock_time: 0
<Witness_Script>
在交易签名之后才会被添加。一个 Witness Script 的示例:
1witness_script = "OP_2 21 02<Public_Key_1>21 02<Public_Key_2> OP_2 OP_CHECKMULTISIG"
-
21
:公钥的长度为 33 字节(0x21),因为比特币的公钥通常是 33 字节长。 -
02<Public_Key_1>
:表示第一个公钥,长度为 33 字节,以02
开头。 -
OP_CHECKMULTISIG
: 是一个操作码,用于验证是否有足够的签名与提供的公钥匹配。 -
OP_2
: 再次表示需两个操作数。
验证过程
-
提取出 Witness Script 和签名数据。
-
对每个输入进行以下操作:
-
将见证脚本和对应的签名数据推入栈中。
-
执行见证脚本,验证签名和条件是否满足。
-
-
如果见证脚本执行成功,则交易有效,否则交易无效。
具体栈执行过程如下:
-
将签名压入栈: 栈的状态(从顶部到底部):
-
<Signature2>
-
<Signature1>
-
-
开始执行
witness_script
:-
OP_2
:表示需要两个签名,验证一下栈上有两个操作数即可,不改变栈的状态。 -
接下来,公钥被压入栈:
-
21 02<Public_Key_2>
-
21 02<Public_Key_1>
-
<Signature2>
-
<Signature1>
-
-
OP_2
再次压入栈,表示这是一个2-of-2 multisig,不改变上述栈的状态。
-
-
OP_CHECKMULTISIG
开始执行,它期望栈的状态如下:-
需要的签名数量(2)
-
签名(
<Signature1>
,<Signature2>
) -
公钥数量(2)
-
公钥(
21 02<Public_Key_1>
,21 02<Public_Key_2>
)
OP_CHECKMULTISIG
会消耗这些元素,并验证提供的签名是否匹配公钥。 -
匹配则 OP_CHECKMULTISIG
执行成功,最终栈顶将留 OP_TRUE
,表示成功。
钱包
一个私钥就够了,但这不够安全。
余额机制:不直接存储余额,追踪所有与钱包地址相关的交易记录,累加输入与输出的差额,得出钱包地址的余额。
实现上,使用UTXO(Unspent Transaction Outputs)模型和索引来优化查询过程。详见下文。
钱包按功能分为:
-
Full-Service Wallets:包含私钥和公钥,允许用户完全控制其资金。Full-Service Wallets可以在线或离线使用,但在线使用时需要考虑安全问题。
-
Signing-Only Wallets:仅用于生成签名,而不存储比特币。用户可以在离线设备中生成签名,然后将签名带到其他地方进行广播。
-
Distributing-Only Wallets:在网络分发公钥哈希,不提供接收比特币的功能,也不存储私钥,因此不能用于生成交易或签名。
Wallet Import Format (WIF) 是一种私钥序列化形式,提供了校验和,便于保持完整性地传输,但不提供加密。BIP38 提供了加密的 WIF,增加了额外的安全层。
TRANSACTION(事务/交易/TX)
UTXO
花剩下的钱。
UTXO(Unspent Transaction Output)即是“未花费的交易输出”,BTC 核心所有权机制。在UTXO模型中,货币的所有权通过交易历史的记录来证明,而不是通过账户余额。每个UTXO都是之前交易中未花费的部分,可以用于未来的交易。
伪代码:
class UTXO: transaction_id: str # 交易ID output_index: int # 输出索引 value: int # 金额 script_pubkey: str # 脚本公钥,用于验证所有权
TX
用以前花剩下的钱付款,付剩下的以后用。
伪代码:
class Transaction: inputs: List[UTXO] # 交易输入 outputs: List[UTXO] # 交易输出
Coinbase Input
Coinbase 交易是用来奖励挖矿节点的特殊交易,它出现在每个新区块的头部,是 UTXO 的源头,无中生有地创造出比特币。
Utreexo
Utreexo的全称是"UTXO Set Refinement for Excessively Fast Execution of Opportunistic Transactions",它由Blockstream公司的Greg Maxwell和Pieter Wuille在2019年提出。
在传统的比特币实现中,全节点需要存储整个UTXO集合的副本,这意味着随着区块链的增长,节点的存储需求也会不断增加。Utreexo通过使用一种称为Merkle树的数据结构来优化这一过程,只存储UTXO集合的Merkle证明,而不是整个UTXO集合。
Utreexo的工作原理如下:
-
Merkle树构建:UTXO集合的每个元素都被哈希化,并插入到一个Merkle树中。Merkle树的根哈希代表了整个UTXO集合的状态。
-
证明生成:当一个UTXO被花费时,节点会生成一个Merkle证明,证明该UTXO之前存在于UTXO集合中。这个证明随后会被包含在新的区块中。
-
证明验证:当节点接收到新的区块时,它会验证区块中包含的Merkle证明,以确保所有被花费的UTXO都是有效的。
-
增量更新:节点只需要存储Merkle树的根哈希和少量的Merkle证明,就可以验证新的区块。当新的UTXO被创建时,节点会更新Merkle树,并生成新的根哈希。
通过这种方式,Utreexo显著减少了全节点所需的存储空间,同时也提高了验证交易和区块的速度。
Utreexo目前还没有在比特币的主网上实现,它仍然是一个研究和开发中的项目。