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