深入理解比特币:(1)钱包和地址

回顾

上一章我们了解了比特币钱包的生成过程,一般过程如下:

  1. 生成一个随机数作为种子。

  2. 使用种子生成一个随机私钥。

  3. 从私钥派生公钥。

  4. 使用公钥生成比特币地址。

  5. 打印私钥、公钥和地址。

今天我们会拆解分析这背后的原理。主要涉及到的是 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^312^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 的大致原理:

  1. 首先,该函数接收一个私钥作为输入。私钥是一个随机数,通常由椭圆曲线算法生成。在 Bitcoin 中,私钥是一个 256 位的整数。

  2. 接下来,函数使用 secp256k1 椭圆曲线算法中的基点(也称为生成点)来计算公钥。基点是一个预定义的曲线上的固定点,对于 secp256k1 曲线来说是已知的。

  3. 使用私钥和基点,函数执行椭圆曲线点乘运算,将私钥乘以基点。这个运算的结果是一个新的点,即公钥点。公钥点是椭圆曲线上的一个点,它具有一对坐标 (x, y)。

  4. 最后,函数将公钥点的坐标进行编码,生成一个压缩或非压缩的公钥。压缩公钥只包含 x 坐标和一个标志位,非压缩公钥包含 x 和 y 坐标。在 Bitcoin 中,压缩公钥更常见,因为它更短,节省了存储空间。

具体实现这里不展开。这里得到一个普通公钥,然后可以直接用它得到地址,也可以先扩展,再得到地址。

从普通公钥到扩展公钥

在 Bitcoin 中,有两种类型的公钥:普通公钥(Regular Public Key)和扩展公钥(Extended Public Key)。它们之间的区别如下:

  1. 普通公钥:普通公钥是通过对私钥执行椭圆曲线点乘运算生成的公钥。它只包含公钥的坐标(通常是压缩形式的 x 坐标),没有其他额外的信息。普通公钥用于验证签名和生成 Bitcoin 地址。

  2. 扩展公钥:扩展公钥是基于 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"开头

生成过程

  1. hash_public_key(public_key):

    • 首先将提供的公钥(public_key)进行双重哈希运算,先使用SHA-256,再使用RIPEMD-160。

    • 输出结果:哈希的公钥(hashed_public_key)。

  2. add_network_prefix(hashed_public_key, network_prefix):

    • 在哈希的公钥(hashed_public_key)前面添加网络前缀(network_prefix)。网络前缀(network_prefix)通常用于标识该地址是在主网(Mainnet)还是测试网(Testnet)上。

    • 输出结果:添加了网络前缀的扩展哈希的公钥(extended_hashed_public_key)。

  3. calculate_checksum(extended_hashed_public_key):

    • 对扩展哈希的公钥(extended_hashed_public_key)进行两次SHA-256哈希运算,然后取结果的前4个字节作为校验和(checksum)。

    • 输出结果:校验和(checksum)。

  4. 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_sigscript_pubkey 是两个重要的脚本,它们分别用于交易输入和输出。这些脚本由操作码组成,用于验证交易的有效性。

  • script_sig 是交易输入中的解锁脚本。包括签名和公钥。

  • script_pubkey 是交易输出中的锁定脚本。它规定了接收比特币的条件,通常是一个公钥哈希。

下面我们演示这一过程:

  1. 首先,将 script_sigscript_pubkey 合并成一个完整的脚本。

  2. 然后,将这个完整的脚本放入比特币脚本解释器中执行。

  3. 如果执行成功,脚本返回 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)

栈的变化过程如下:

  1. 初始时,栈为空。

  2. 执行 script_sig,将数字签名和公钥推入栈中:

    • 栈:<Signature> <Public_Key>
  3. 执行 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 使用公钥验证签名的有效性,如果验证通过则继续,否则抛出错误。

  4. 最终,栈中仅剩余一个元素,表示验证通过。

安全性

由于交易可以随便创建,那么能否让 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_HASH160OP_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 字段中提取出来进行验证。验证的步骤包括:

  1. 提取出交易的输入、输出、签名和公钥。

  2. 计算交易数据的哈希。

  3. 使用公钥对签名进行验证,确保它与交易数据哈希匹配。

  4. 验证交易输出脚本中的地址与公钥哈希匹配。

  5. 确认交易金额和其他条件是否满足。

如果所有条件都满足,则交易被视为有效。

为什么叫隔离见证

因为实现了将交易中的签名信息(见证数据)与其它交易数据分离。

解释这个名称的两个部分:

  1. 见证(Witness):这里的“见证”指的是交易签名,证明了交易发起者拥有他们所声称的比特币。。

  2. 隔离(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: 再次表示需两个操作数。

验证过程

  1. 提取出 Witness Script 和签名数据。

  2. 对每个输入进行以下操作:

    • 将见证脚本和对应的签名数据推入栈中。

    • 执行见证脚本,验证签名和条件是否满足。

  3. 如果见证脚本执行成功,则交易有效,否则交易无效。

具体栈执行过程如下:

  1. 将签名压入栈: 栈的状态(从顶部到底部):

    • <Signature2>

    • <Signature1>

  2. 开始执行witness_script

    • OP_2:表示需要两个签名,验证一下栈上有两个操作数即可,不改变栈的状态。

    • 接下来,公钥被压入栈:

      • 21 02<Public_Key_2>

      • 21 02<Public_Key_1>

      • <Signature2>

      • <Signature1>

    • OP_2再次压入栈,表示这是一个2-of-2 multisig,不改变上述栈的状态。

  3. 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的工作原理如下:

  1. Merkle树构建:UTXO集合的每个元素都被哈希化,并插入到一个Merkle树中。Merkle树的根哈希代表了整个UTXO集合的状态。

  2. 证明生成:当一个UTXO被花费时,节点会生成一个Merkle证明,证明该UTXO之前存在于UTXO集合中。这个证明随后会被包含在新的区块中。

  3. 证明验证:当节点接收到新的区块时,它会验证区块中包含的Merkle证明,以确保所有被花费的UTXO都是有效的。

  4. 增量更新:节点只需要存储Merkle树的根哈希和少量的Merkle证明,就可以验证新的区块。当新的UTXO被创建时,节点会更新Merkle树,并生成新的根哈希。

通过这种方式,Utreexo显著减少了全节点所需的存储空间,同时也提高了验证交易和区块的速度。

Utreexo目前还没有在比特币的主网上实现,它仍然是一个研究和开发中的项目。