4.1 P2PKH支付到P2WPKH,legacy to Segwit
4.1 P2PKH支付到P2WPKH,legacy to Segwit
这一节主要是学习如何解锁传统地址:
from bitcoinutils.setup import setup
from bitcoinutils.utils import to_satoshis
from bitcoinutils.transactions import Transaction, TxInput, TxOutput
from bitcoinutils.keys import P2wpkhAddress, P2pkhAddress, PrivateKey
from bitcoinutils.script import Script
def main():
# 设置测试网
setup('testnet')
# 发送方信息 - Legacy P2PKH
from_private_key = PrivateKey('cPeon9fBsW2BxwJTALj3hGzh9vm8C52Uqsce7MzXGS1iFJkPF4AT')
from_pub_key = from_private_key.get_public_key()
from_address_str = "myYHJtG3cyoRseuTwvViGHgP2efAvZkYa4"
# 创建P2PKH地址对象,用于构造锁定脚本
from_address = P2pkhAddress(from_address_str)
# 接收方 Segwit 地址
to_address = P2wpkhAddress('tb1qckeg66a6jx3xjw5mrpmte5ujjv3cjrajtvm9r4')
print(f"\n发送方 Legacy 地址: {from_address_str}")
print(f"接收方 Segwit 地址: {to_address.to_string()}")
# 创建交易输入
txin = TxInput(
'34b90a15d0a9ec9ff3d7bed2536533c73278a9559391cb8c9778b7e7141806f7',
1 # vout
)
# 计算金额(单位:BTC)
total_input = 0.00029606 # 输入金额
amount_to_send = 0.00029400 # 发送金额
fee = total_input-amount_to_send # 所有剩余金额作为手续费
# 创建交易输出
txout = TxOutput(to_satoshis(amount_to_send), to_address.to_script_pub_key())
# 创建交易
tx = Transaction([txin], [txout])
print("\n未签名的交易:")
print(tx.serialize())
# 对于Legacy P2PKH地址,锁定脚本是标准的P2PKH格式
# OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
p2pkh_script = from_address.to_script_pub_key()
# 签名交易
sig = from_private_key.sign_input(
tx,
0,
p2pkh_script # 使用P2PKH锁定脚本
)
# 获取公钥
public_key = from_pub_key.to_hex()
# 设置解锁脚本 - 对于P2PKH,解锁脚本是 <signature> <pubkey>
txin.script_sig = Script([sig, public_key])
# 获取签名后的交易
signed_tx = tx.serialize()
print("\n已签名的交易:")
print(signed_tx)
print("\n交易信息:")
print(f"从地址 (Legacy): {from_address_str}")
print(f"到地址 (Segwit): {to_address.to_string()}")
print(f"发送金额: {amount_to_send} BTC")
print(f"手续费: {fee} BTC")
print(f"交易大小: {tx.get_size()} bytes")
print("\n您可以在这里广播交易:")
print("https://mempool.space/testnet/tx/push")
if __name__ == "__main__":
main()
程序运行的结果是这样的:https://mempool.space/testnet/tx/bf41b47481a9d1c99af0b62bb36bc864182312f39a3e1e06c8f6304ba8e58355
代码片段解析
1、通过PrivateKey函数获得私钥 , from_private_key = PrivateKey('cPe...)
2、通过私钥的get_public_key()方法获得公钥, from_private_key.get_public_key()
3、通过P2pkhAddress函数,通过传入地址字符串,获得地址对象。这样就发送地址、接受地址的对象都构建完毕, to_address = P2wpkhAddress('tb1qckeg66a6jx3xjw5mrpmte5ujjv3cjrajtvm9r4')
4、接下来构建交易输入,txin来自于前序UTXO,也就是前序交易id+输出索引,
txin = TxInput(
'34b90a15d0a9ec9ff3d7bed2536533c73278a9559391cb8c9778b7e7141806f7',
1 # vout
)
5、构建交易输出,填入要输出的金额,以及接受地址的script_pubkey
txout = TxOutput(to_satoshis(amount_to_send), to_address.to_script_pub_key())
6、组装交易,有输入有输出,输入是前序交易ID+索引,输出是金额和地址(锁定脚本)
tx = Transaction([txin], [txout])
7、获得发送者的scriptpubkey方便,签名
p2pkh_script = from_address.to_script_pub_key()
8、签名交易,这里的用私钥的sign_input方法,里面传入了交易的内容;0是对一个输入签名,如果我们输入找了两个UTXO,是要分别签名的,这个留在以后讲;第三个参数是要传入发送着的锁定脚本,因为是发送着要签名证明是自己能控制这个地址的私钥
sig = from_private_key.sign_input(
tx,
0,
p2pkh_script # 使用发送者的P2PKH锁定脚本
)
9、构建解锁脚本,签名+公钥
txin.script_sig = Script([sig, public_key])
10、交易序列化并发送交易
signed_tx = tx.serialize()
https://mempool.space/testnet/tx/bf41b47481a9d1c99af0b62bb36bc864182312f39a3e1e06c8f6304ba8e58355 的浏览器各字段解析
先看左边,就是整体解锁的过程:
ScriptSig (HEX) 就是ScriptSig (ASM)的16进制HEX表示,而ASM的意思,就是人可以读懂的:
OP_PUSHBYTES_71 3044022055c309fe3f6099f4f881d0fd960923eb91aff0d8ef3501a2fc04dce99aca609d0220174b9aec4fc22f6f81b637bbafec9554e497ec2d9f3ca4992ee4209dd047443d01 就是签名。这里是私钥签名以后,证明这里面包含的私钥可以和下属的公钥是配对的。
OP_PUSHBYTES_33 02898711e6bf63f5cbe1b38c05e89d6c391c59e9f8f695da44bf3d20ca674c8519 这里说明公钥就是02898711e6bf63f5cbe1b38c05e89d6c391c59e9f8f695da44bf3d20ca674c8519,不出所料的话,对他做hash160,哈希值就是c5b28d6bba91a2693a9b1876bcd3929323890fb2
hash160(02898711e6bf63f5cbe1b38c05e89d6c391c59e9f8f695da44bf3d20ca674c8519) = c5b28d6bba91a2693a9b1876bcd3929323890fb2
先看前序交易的锁定脚本,就是
OP_DUP OP_HASH160 OP_PUSHBYTES_20 c5b28d6bba91a2693a9b1876bcd3929323890fb2 OP_EQUALVERIFY OP_CHECKSIG
这一段的HEX其实就是
76a914c5b28d6bba91a2693a9b1876bcd3929323890fb288ac
去前序看看对不对。
结合我们之前的动画的图片,再理解一遍:

Last updated