伪代码,UTXO模型示意
摘要:代码视角下的比特币交易:从原理到实践的深度解析比特币,作为全球首个去中心化数字货币,其核心魅力不仅在于价格的波动,更在于其背后由代码驱动的、透明且安全的交易机制,理解比特币交易,离不开对其底层代码的探...
代码视角下的比特币交易:从原理到实践的深度解析
比特币,作为全球首个去中心化数字货币,其核心魅力不仅在于价格的波动,更在于其背后由代码驱动的、透明且安全的交易机制,理解比特币交易,离不开对其底层代码的探究,本文将从代码的视角,深入剖析比特币交易的原理、流程及关键实现细节。
比特币交易的“燃料”:UTXO模型
在深入代码之前,必须理解比特币独特的交易模型——UTXO(Unspent Transaction Output,未花费交易输出),这与传统银行账户的余额概念截然不同。
- 核心思想:比特币的“余额”并非一个单一数值,而是所有未被花费的UTXO的总和,每一笔交易都消耗(花费)一个或多个UTXO,并创建一个或多个新的UTXO。
- 代码体现:当一笔交易被创建时,其输入(Inputs)会引用之前交易产生的未花费输出(UTXOs),一笔交易可能有两个输入,分别来自A和B之前收到的UTXO,然后创建一个输出给接收者C,并可能将找零(如果输入总额大于输出总额)返回给A或B的另一个地址。
def __init__(self, tx_id, output_index, amount, script_pub_key):
self.tx_id = tx_id # 引用交易的ID
self.output_index = output_index # 引用输出的索引
self.amount = amount # UTXO的面值
self.script_pub_key = script_pub_key # 锁定脚本(定义了谁能花费这个UTXO)
class Transaction:
def __init__(self):
self.inputs = [] # 输入列表,每个输入包含对UTXO的引用和签名脚本
self.outputs = [] # 输出列表,每个输出包含金额和锁定脚本
# 示例:一笔交易消耗两个UTXO,产生一个输出和找零
tx = Transaction()
# 假设utxo1和utxo2是用户之前未花费的输出
tx.inputs.append(Input(utxo1.tx_id, utxo1.output_index, utxo1.amount, user_signature))
tx.inputs.append(Input(utxo2.tx_id, utxo2.output_index, utxo2.amount, user_signature))
# 创建新的UTXO给接收者
tx.outputs.append(Output(total_amount - fee, receiver_script_pub_key))
# 创建找零UTXO给发送者
tx.outputs.append(Output(fee, sender_script_pub_key))
比特币交易的“灵魂”:脚本(Script)
比特币交易的安全性依赖于脚本系统,它定义了花费UTXO的条件,脚本是一种基于堆栈的、简单的编程语言,运行在比特币的虚拟机(Script Interpreter)中。
- 锁定脚本(Locking Script / ScriptPubKey):放置在UTXO输出中,规定了花费该UTXO必须满足的条件,最常见的是“Pay-to-Public-Key-Hash (P2PKH)”脚本,它要求提供签名和公钥,并能验证该签名是用对应私钥对交易哈希值签名得到的。
- 解锁脚本(Unlocking Script / ScriptSig):由交易创建者在输入中提供,包含了满足锁定脚本条件的数据(如签名、公钥)。
- 执行过程:当节点验证一笔交易时,会将每个输入的解锁脚本和其引用的UTXO的锁定脚本组合在一起,依次执行,如果脚本执行结果为“True”(堆栈顶为非零),则该输入有效。
# 伪代码:P2PKH脚本验证简化示意
def verify_p2pkh_script(unlocking_script, locking_script):
# unlocking_script: <signature> <public_key>
# locking_script: <OP_DUP> <OP_HASH160> <hash_of_public_key> <OP_EQUALVERIFY> <OP_CHECKSIG>
stack = []
# 1. 解压解锁脚本并压入堆栈
stack.extend(unlocking_script.split())
# 2. 执行锁定脚本指令
# OP_DUP: 复制堆栈顶元素
stack.append(stack[-1]) # 简化示意
# OP_HASH160: 对堆栈顶元素(公钥)进行RIPEMD160(SHA256)哈希
public_key = stack.pop()
public_key_hash = hash160(public_key)
stack.append(public_key_hash)
# OP_EQUALVERIFY: 比较堆栈顶两个元素是否相等,不等则失败
if stack.pop() != locking_script.split()[2]: # 假设locking_script中存储了hash_of_public_key
return False
# OP_CHECKSIG: 使用签名和公钥验证交易哈希
signature = stack.pop()
if not verify_signature(signature, public_key, transaction_hash):
return False
return stack == [1] # 验证成功,堆栈顶为真(非零)
构建一笔比特币交易:代码实践
构建一笔实际的比特币交易,代码层面需要完成以下关键步骤:
- 确定输入(选择UTXOs):发送者需要从自己控制的UTXOs中选择足够的金额来支付目标金额和手续费,这通常需要查询比特币节点的UTXO集。
- 构建输出:指定接收者的地址(通过公钥生成)和金额,以及找零地址和金额。
- 创建交易:组装输入和输出,生成原始交易数据。
- 签名输入:对每个输入,使用对应的私钥创建数字签名,并将签名和公钥放入解锁脚本,这是保障交易所有权的关键步骤。
- 广播交易:将签名后的交易广播到比特币网络,由矿工打包进区块。
# 伪代码:构建并签名一笔比特币交易(简化)
import hashlib
import ecdsa # 假设使用ecdsa库进行签名
# 假设我们有以下信息
private_key = "用户私钥"
sender_address = "发送者地址" # 从private_key派生
receiver_address = "接收者地址"
amount_to_send = 0.5 # BTC
fee = 0.001 # BTC
utxos_to_spend = [...] # 从节点获取的足够UTXO列表
# 1. 构建原始交易(未签名)
raw_tx = {
"version": 1,
"locktime": 0,
"inputs": [],
"outputs": []
}
for utxo in utxos_to_spend:
raw_tx["inputs"].append({
"txid": utxo["txid"],
"vout": utxo["vout"],
"scriptSig": "", # 留空,待签名
"sequence": 0xFFFFFFFF
})
raw_tx["outputs"].append({
"value": amount_to_send,
"scriptPubKey": address_to_scriptPubKey(receiver_address) # 将接收者地址转换为锁定脚本
})
# 计算总输入金额并添加找零
total_input = sum(utxo["amount"] for utxo in utxos_to_spend)
change_amount = total_input - amount_to_send - fee
if change_amount > 0:
raw_tx["outputs"].append({
"value": change_amount,
"scriptPubKey": address_to_scriptPubKey(sender_address) # 找零到发送者地址
})
# 2. 对输入进行签名
# 注意:实际签名需要对特定格式的交易哈希进行签名,且每个输入单独签名
for i, input in enumerate(raw_tx["inputs"]):
# 构建签名哈希(Sighash),这通常需要对交易数据进行序列化并修改
sighash = calculate_sighash(raw_tx, i, utxos_to_spend[i]["scriptPubKey"])
# 使用私钥签名
sk = ecdsa.SigningKey.from_string(bytes.fromhex(private_key), curve=ecdsa.SECP256k1)
signature = sk.sign(sighash, hashfunc=hashlib.sha256, sigencode=ecdsa.util.sigencode_der_canonize)
# 获取公钥
vk = sk.get_verifying_key()
public_key = vk.to_string("compressed")
# 构建解锁脚本 (scriptSig)
raw_tx["inputs"][i]["scriptSig"] = bytes.fromhex(signature.hex()) + bytes.fromhex(public_key.hex())
# 3. 广播交易(这里省略网络广播代码)
# signed_tx_hex = serialize_raw_tx(raw_tx)
# broadcast_transaction(signed_tx_hex)
代码之外:安全性与注意事项
虽然代码是比特币交易的基石,但实际操作中还需注意:
- 私钥安全:代码中的私钥一旦泄露,资产将面临被盗风险,必须妥善保管私钥,使用硬件钱包等安全措施。
- 网络节点:与比特币网络交互的节点代码(如Bitcoin Core的代码)决定了交易的有效性和
