# 比特币地址详解

By [heqing](https://paragraph.com/@vilink) · 2022-04-30

---

参考文档
====

比特币地址说明：[https://zh.thedev.id/mastering-bitcoin-cash/3-keys-addresses-wallets.html](https://zh.thedev.id/mastering-bitcoin-cash/3-keys-addresses-wallets.html) csdn: [https://blog.csdn.net/BitTribeLab/article/details/102834123](https://blog.csdn.net/BitTribeLab/article/details/102834123) 比特币地址维基百科：[https://en.bitcoin.it/wiki/List\_of\_address\_prefixes](https://en.bitcoin.it/wiki/List_of_address_prefixes) secpk256k1: [https://en.bitcoin.it/wiki/Secp256k1](https://en.bitcoin.it/wiki/Secp256k1) 比特币地址到私钥：[http://aandds.com/blog/bitcoin-key-addr.html](http://aandds.com/blog/bitcoin-key-addr.html)

私钥
==

私钥本质就是一个随机数，一个长度为256位的数值

私钥的编码
-----

为了标识或者存储这个随机大整数，可以用 256 比特位表示，这 256 比特位可以编码为：

1.  16 进制形式；
    
2.  Wallet Import Format (WIF)；
    
3.  WIF-compressed 形式。
    

随机生成一个32自己数据

    $ openssl rand -hex 32              # 随机生成 32 字节随机数，以 16 进制形式显示
    d9815f47582f890ef4f818fd5dec98a6419fda23e57f1fed62ee0b9f79b1a785
    $ openssl rand -hex 32              # 再测试一次
    3bd5af025525af11f4471f299d600af51a6d697374b39ad19224a91b748a17ec
    
    # 或者自己记一个数字，12345678，然后转成16进制即可
    0000000000000000000000000000000000000000000000000000000000bc614e
    

WIF、WIF-compressed 的编码及解码过程
---------------------------

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import hashlib
    import base58
    from typing import Union
    
    
    def scrub_input(hex_str_or_bytes: Union[str, bytes]) -> bytes:
        if isinstance(hex_str_or_bytes, str):
            hex_str_or_bytes = bytes.fromhex(hex_str_or_bytes)
        return hex_str_or_bytes
    
    
    # wallet import format key - base58 encoded format
    # https://bitcoin.stackexchange.com/questions/9244/private-key-to-wif
    def gen_wif_key(private_key: Union[str, bytes], compressed_WIF: bool = False) -> bytes:
        private_key = scrub_input(private_key)
    
        # prepended mainnet version byte to private key
        mainnet_private_key = b'\x80' + private_key
        if compressed_WIF:
            mainnet_private_key = b'\x80' + private_key + b'\x01'
        # perform SHA-256 hash on the mainnet_private_key
        sha256 = hashlib.sha256()
        sha256.update(mainnet_private_key)
        hash = sha256.digest()
    
        # perform SHA-256 on the previous SHA-256 hash
        sha256 = hashlib.sha256()
        sha256.update(hash)
        hash = sha256.digest()
    
        # create a checksum using the first 4 bytes of the previous SHA-256 hash
        # append the 4 checksum bytes to the mainnet_private_key
        checksum = hash[:4]
    
        hash = mainnet_private_key + checksum
    
        # convert mainnet_private_key + checksum into base58 encoded string
        return base58.b58encode(hash)
    
    
    def decode_wif(wif: str) -> bytes:
        compressed = False
        if wif.startswith('K') or wif.startswith('L'):
            compressed = True
        decoded = base58.b58decode(wif)
        if compressed:
            private_key = decoded[1:-5]  # [80 xxx 1 checksum]
        else:
            private_key = decoded[1:-4]  # [80 xxx checksum]
        return private_key
    
    
    prikey = '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'  # hex
    wif = gen_wif_key(prikey)
    print(wif)  # 5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ
    wif_compressed = gen_wif_key(prikey, True)
    print(wif_compressed)  # KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617
    
    prikey = decode_wif('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ')
    print(prikey.hex())  # 0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d
    

比特币地址
=====

比特币是建立在密码学基础上的，主要涉及到哈希函数、公钥密码学和数学签名，其贯穿始终的密码学在比特币中并不是为了加密，而是为了证明所有权。比特币交易和节点数据并没有被加密，反而是公开。

比特币公钥
-----

1.  公钥K是椭圆曲线上的一个点，它可以由私钥计算出来，定义的公式为 K = kG.其中k为私钥，G为base point是椭圆曲线secp256k1的一个参数。 如果私钥k="0x1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd",那么通过计算可以得到K的两个坐标为： x="f028892bad7ed57d2fb57bf33081d5cfcf6f9ed3d3d7f159c2e2fff579dc341a" y="07cf33da18bd734c600b96a72bbc4749d5141c90ec8ac328ae52ddfe2e505bdb"
    
2.  公钥是一个曲线上的坐标，要表达这个坐标，又产生了两种存储方式：
    

*   Uncompressed 形式：就是把两个坐标（x, y）直接连接在一起，再在前面加个 0x04 前缀即可；
    
*   Compressed 形式，就是当y为偶数时，编码为02x，当y为奇数时，编码为 03x;
    

1.  基本原理：通过椭圆曲线 $y^2=x^3 + 7$ 和编码的x值，我们可以计算得出y,然后完整拼装的出坐标点原始公钥数据。
    
2.  下面是通过私钥计算公钥的代码
    

    import ecdsa
    from ecdsa.ellipticcurve import PointJacobi
    
    
    def derive_public_key(private_key: bytes, compressed: bool = False) -> bytes:
        Q: PointJacobi = int.from_bytes(private_key, byteorder='big') * ecdsa.curves.SECP256k1.generator
        xstr: bytes = Q.x().to_bytes(32, byteorder='big')
        ystr: bytes = Q.y().to_bytes(32, byteorder='big')
        if compressed:
            parity: int = Q.y() & 1
            return (2 + parity).to_bytes(1, byteorder='big') + xstr
        else:
            return b'\04' + xstr + ystr
    
    
    prikey = bytearray.fromhex('1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd')
    uncompressed_pubkey = derive_public_key(prikey, False)
    print("uncompressed public key =", uncompressed_pubkey.hex())
    compressed_pubkey = derive_public_key(prikey, True)
    print("compressed public key =", compressed_pubkey.hex())
    

公钥和地址
-----

由于比特币UTXO的账户模型，用户的可用余额，实际上是别人转入的**未使用的交易输出**。所以比特币的地址可以抽象定义为，锁定某一个**未使用的交易输出**的hash数据；而比特币锁定某个交易输出的数据就根据交易的类型不同，有了很多形式。

### P2PKH支付公钥哈希：(Pay-to-Pubkey Hash)

!Unsupported embed

下面是python的示例形式：

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    import hashlib
    import base58
    
    """
        P2PKH（Pay-to-Public-Key-Hash）
            是最原始的地址，由公钥通过 Hash 计算后得到，它们都以 1 开头。
        
        我们以前面介绍过的 Compressed 公钥（03f028892bad7ed57d2fb57bf33081d5cfcf6f9ed3d3d7f159c2e2fff579dc341a）为例，介绍一下从公钥转换得到地址的步骤：
        1. 计算其 SHA256
        2. 对上面结果计算 RIPEMD-160 哈希
        3. 计算 checksum，规则是对上面结果前面加上版本号（主网为 0x00，测试网为 0x6f），然后计算两次 SHA256
        4. 用格式 [version][ripemd160_hash][checksum] 构造出结果
        5. 对上面结果进行 Base58Check 编码
    
        用 Compressed 公钥计算出来的，被称为“Compressed 地址”；
        如果用 Uncompressed 公钥重复进行上面过程，则会得到另外一个地址，称为“Uncompressed 地址”。
        一个比特币私钥对应两个地址（Compressed/Uncompressed 地址），它们都是合法的。
    
    
    """
    def sha256(inputs: bytes) -> bytes:
        """ Computes sha256 """
        sha = hashlib.sha256()
        sha.update(inputs)
        return sha.digest()
    
    
    def ripemd160(inputs: bytes) -> bytes:
        """ Computes ripemd160 """
        rip = hashlib.new('ripemd160')
        rip.update(inputs)
        return rip.digest()
    
    
    def base58_cksum(inputs: bytes) -> bytes:
        """ Computes base 58 four bytes check sum """
        s1 = sha256(inputs)
        s2 = sha256(s1)
        checksum = s2[0:4]
        return checksum
    
    
    def pubkey_compressed_to_uncompressed(compressed_pubkey: bytes) -> bytes:
        """ Converts compressed pubkey to uncompressed format """
        assert len(compressed_pubkey) == 33
        # modulo p which is defined by secp256k1's spec
        p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
        x = int.from_bytes(compressed_pubkey[1:33], byteorder='big')
        y_sq = (pow(x, 3, p) + 7) % p
        y = pow(y_sq, (p + 1) // 4, p)
        if compressed_pubkey[0] % 2 != y % 2:
            y = p - y
        y_bytes = y.to_bytes(32, byteorder='big')
        return b'\04' + compressed_pubkey[1:33] + y_bytes  # x + y
    
    
    def pubkey_to_p2pkh_addr(pubkey: bytes, version: bytes) -> bytes:
        """ Derives legacy (p2pkh) address from pubkey """
        out1 = sha256(pubkey)
        out2 = ripemd160(out1)
        # Base-58 encoding with a checksum
        checksum = base58_cksum(version + out2)
        address = base58.b58encode(version + out2 + checksum)
        return address
    
    
    pubkey = '03f028892bad7ed57d2fb57bf33081d5cfcf6f9ed3d3d7f159c2e2fff579dc341a'
    
    pubkey_uncompressed = b''
    pubkey_compressed = b''
    
    if pubkey.startswith('04'):  # uncompressed
        pubkey_uncompressed = bytes.fromhex(pubkey)
        if ord(bytearray.fromhex(pubkey[-2:])) % 2 == 0:
            pubkey_compressed_hex_str = '02' + pubkey[2:66]
        else:
            pubkey_compressed_hex_str = '03' + pubkey[2:66]
        pubkey_compressed = bytes.fromhex(pubkey_compressed_hex_str)
    else:  # compressed
        pubkey_uncompressed = pubkey_compressed_to_uncompressed(bytes.fromhex(pubkey))
        pubkey_compressed = bytes.fromhex(pubkey)
    
    print("compressed public key =", pubkey_compressed.hex())
    print("uncompressed public key =", pubkey_uncompressed.hex())
    main_version = b'\x00'  # 0x00 for mainnet, 
    test_version = b'\x6f'  # 0x6f for testnet
    
    addr_compressed = pubkey_to_p2pkh_addr(pubkey_compressed, main_version)
    addr_uncompressed = pubkey_to_p2pkh_addr(pubkey_uncompressed, main_version)
    
    test_addr_compressed = pubkey_to_p2pkh_addr(pubkey_compressed, test_version)
    test_addr_uncompressed = pubkey_to_p2pkh_addr(pubkey_uncompressed, test_version)
    print("mainnet address (uncompressed) = ", addr_uncompressed)  # 1424C2F4bC9JidNjjTUZCbUxv6Sa1Mt62x
    print("mainnet address (compressed) = ", addr_compressed)  # 1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy
    
    print("testnet address (uncompressed) = ", test_addr_compressed)  # mxdivjAqQSQj4LrAMX1XLQidyfU3pCWeS7
    print("testnet address (compressed) = ", test_addr_uncompressed)  # miY1V5L3QDaZVjrMT2Sw2WhHn63GzsNFQB
    

### P2SH支付脚本地址：(Pay-to-Script-Hash)

!Unsupported embed

示例

    
    from bitcoinutils.setup import setup
    from bitcoinutils.transactions import Transaction, TxInput, TxOutput, Sequence
    from bitcoinutils.keys import P2pkhAddress, P2shAddress, PrivateKey
    from bitcoinutils.script import Script
    from bitcoinutils.constants import TYPE_RELATIVE_TIMELOCK
    
    
    def main():
        # always remember to setup the network
        setup('testnet')
    
        #
        # This script creates a P2SH address containing a CHECKSEQUENCEVERIFY plus
        # a P2PKH locking funds with a key as well as for 20 blocks
        #
    
        # set values
        relative_blocks = 20
    
        seq = Sequence(TYPE_RELATIVE_TIMELOCK, relative_blocks)
    
        # secret key corresponding to the pubkey needed for the P2SH (P2PKH) transaction
        p2pkh_sk = PrivateKey('cRvyLwCPLU88jsyj94L7iJjQX5C2f8koG4G2gevN4BeSGcEvfKe9')
    
        # get the address (from the public key)
        p2pkh_addr = p2pkh_sk.get_public_key().get_address()
    
        # create the redeem script
        redeem_script = Script([seq.for_script(), 'OP_CHECKSEQUENCEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', p2pkh_addr.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG'])
    
        # create a P2SH address from a redeem script
        addr = P2shAddress.from_script(redeem_script)
        print(addr.to_string())
    
    if __name__ == "__main__":
        main() # 2N48oA8YokPa4uumpAgiuKfPwxXy2wRxrbk
    

### 隔离见证地址

1.  隔离见证地址是什么？ "隔离见证"（Segregated Witness，或缩写为 segwit）是比特币的一次大的升级，于 2017 年在主网上激活。 具体指的是把见证数据（即 scriptSig 中的内容）从 Transaction 信息里抽离出来，单独存放。
    
2.  解决了什么问题？
    

*   隔离见证的最大好处之一是消除了"Transaction Malleability", 一般翻译成"交易延展性";
    
*   减少了手续费，见证数据通常在交易的总大小中占了很大比重，通过将见证数据从交易中移出，隔离见证提高了比特币的可扩展性。
    

#### 隔离见证兼容地址(Nested Segwit Address) \[ P2SH-P2WPKH\]

在原生的隔离见证地址提出之前，原有的地址基础上，实现隔离见证的方式是在P2SH中嵌入 Pay-to-Witness-Public-Key-Hash（P2WPKH）的方式来实现的。这种形式即为兼容的隔离见证地址，和 P2SH的格式是一样的，节点不升级也能正常使用隔离见证。

例子：

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import hashlib
    import base58
    
    
    """
        Pay-to-Script Hash (P2SH) 地址由 Script Hash 的 Base58Check 编码得到。由于进行 Base58Check 编码时，对 P2SH 地址指定的版本前缀为 5，这导致这类地址以“3”开头。
        在原生的隔离见证地址提出之前，生成隔离见证地址的方式是在 P2SH 中嵌入 Pay-to-Witness-Public-Key-Hash（P2WPKH）
        
        细节可参考：https://bitcointalk.org/index.php?topic=5229211.0
    
    
    
    """
    
    def sha256(inputs: bytes) -> bytes:
        """ Computes sha256 """
        sha = hashlib.sha256()
        sha.update(inputs)
        return sha.digest()
    
    def ripemd160(inputs: bytes) -> bytes:
        """ Computes ripemd160 """
        rip = hashlib.new('ripemd160')
        rip.update(inputs)
        return rip.digest()
    
    
    def base58_cksum(inputs: bytes) -> bytes:
        """ Computes base 58 four bytes check sum """
        s1 = sha256(inputs)
        s2 = sha256(s1)
        checksum = s2[0:4]
        return checksum
    
    def pubkey_to_p2sh_p2wpkh_addr(pubkey_compressed: bytes) -> bytes:
        """ Derives p2sh-segwit (p2sh p2wpkh) address from pubkey """
        pubkey_hash = sha256(pubkey_compressed)
        rip = ripemd160(pubkey_hash)
        redeem_script = b'\x00\x14' + rip  # 0x00: OP_0, 0x14: PushData
        redeem_hash = sha256(redeem_script)
        redeem_rip = ripemd160(redeem_hash)
        # Base-58 encoding with a checksum
        version = b'\x05'  # 0x05 for mainnet, 0xc4 for testnet
        checksum = base58_cksum(version + redeem_rip)
        address = base58.b58encode(version + redeem_rip + checksum)
        return address
    
    def pubkey_to_p2sh_p2wpkh_addr_test(pubkey_compressed: bytes) -> bytes:
        """ Derives p2sh-segwit (p2sh p2wpkh) address from pubkey """
        pubkey_hash = sha256(pubkey_compressed)
        rip = ripemd160(pubkey_hash)
        redeem_script = b'\x00\x14' + rip  # 0x00: OP_0, 0x14: PushData
        redeem_hash = sha256(redeem_script)
        redeem_rip = ripemd160(redeem_hash)
        # Base-58 encoding with a checksum
        version = b'\xc4'  # 0x05 for mainnet, 0xc4 for testnet
        checksum = base58_cksum(version + redeem_rip)
        address = base58.b58encode(version + redeem_rip + checksum)
        return address
    
    pubkey = '03f028892bad7ed57d2fb57bf33081d5cfcf6f9ed3d3d7f159c2e2fff579dc341a'
    addr_p2sh_segwit = pubkey_to_p2sh_p2wpkh_addr(bytes.fromhex(pubkey))
    addr_p2sh_segwit_test = pubkey_to_p2sh_p2wpkh_addr_test(bytes.fromhex(pubkey))
    
    print("p2sh-segwit address", addr_p2sh_segwit)  # 3FyC6EYuxW22uj4CaEGjNCjxeg7gHyFeVv
    print("p2sh-segwit test address", addr_p2sh_segwit_test)  # 2N7XQ9yUwZxXP7WgkFMtbz9jDs2Kr2njYRy
    

#### 原生隔离见证地址 （Native Segwit Address）\[ P2WPKH 或 P2WSH \]

bc1 开头的地址，是由新的隔离见证脚本生成的地址（P2WPKH 或 P2WSH），是纯正的隔离见证地址。

1.  Bech32 编码由 3 部分组成：
    

*   human-readable part，比特币主网固定为 bc；
    
*   separator，固定为 1；
    
*   data part，由数字和小写字母组成，但排除这 4 个： 1, b, i, o （注：10 个数字加上 26 个小写字母，再减去这 4 个排除的字符，可得 32 个字符）。
    

1.  有个缺点：如果地址的最后一个字符是 p，则在紧接着 p 之前的位置插入或者删除任意数量的字符 q 都不会使其 checksum 失效。为了缓解 Bech32 的上述缺点，在 BIP0350 中提出了 Bech32m 地址：
    

*   对于版本为 0 的原生隔离见证地址，使用以前的 Bech32；
    
*   对于版本为 1（或者更高）的原生隔离见证地址，则使用新的 Bech32m。
    
*   对于 Bech32m 地址，当版本为 1 时，它们总是以 bc1p 开头（即 Taproot 地址）
    

1.  例子**P2WPKH**（其实就是原来P2PKH的隔离见证格式）
    

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import hashlib
    from typing import Optional
    
    
    def sha256(inputs: bytes) -> bytes:
        """ Computes sha256 """
        sha = hashlib.sha256()
        sha.update(inputs)
        return sha.digest()
    
    
    def ripemd160(inputs: bytes) -> bytes:
        """ Computes ripemd160 """
        rip = hashlib.new('ripemd160')
        rip.update(inputs)
        return rip.digest()
    
    
    # From https://github.com/sipa/bech32/blob/master/ref/python/segwit_addr.py
    """Reference implementation for Bech32 and segwit addresses."""
    CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
    
    def bech32_polymod(values):
        """Internal function that computes the Bech32 checksum."""
        generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
        chk = 1
        for value in values:
            top = chk >> 25
            chk = (chk & 0x1ffffff) << 5 ^ value
            for i in range(5):
                chk ^= generator[i] if ((top >> i) & 1) else 0
        return chk
    
    
    def bech32_hrp_expand(hrp):
        """Expand the HRP into values for checksum computation."""
        return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
    
    
    def bech32_verify_checksum(hrp, data):
        """Verify a checksum given HRP and converted data characters."""
        return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1
    
    
    def bech32_create_checksum(hrp, data):
        """Compute the checksum values given HRP and data."""
        values = bech32_hrp_expand(hrp) + data
        polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
        return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
    
    
    def bech32_encode(hrp, data):
        """Compute a Bech32 string given HRP and data values."""
        combined = data + bech32_create_checksum(hrp, data)
        return hrp + '1' + ''.join([CHARSET[d] for d in combined])
    
    
    def bech32_decode(bech):
        """Validate a Bech32 string, and determine HRP and data."""
        if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
                (bech.lower() != bech and bech.upper() != bech)):
            return (None, None)
        bech = bech.lower()
        pos = bech.rfind('1')
        if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
            return (None, None)
        if not all(x in CHARSET for x in bech[pos+1:]):
            return (None, None)
        hrp = bech[:pos]
        data = [CHARSET.find(x) for x in bech[pos+1:]]
        if not bech32_verify_checksum(hrp, data):
            return (None, None)
        return (hrp, data[:-6])
    
    
    def convertbits(data, frombits, tobits, pad=True):
        """General power-of-2 base conversion."""
        acc = 0
        bits = 0
        ret = []
        maxv = (1 << tobits) - 1
        max_acc = (1 << (frombits + tobits - 1)) - 1
        for value in data:
            if value < 0 or (value >> frombits):
                return None
            acc = ((acc << frombits) | value) & max_acc
            bits += frombits
            while bits >= tobits:
                bits -= tobits
                ret.append((acc >> bits) & maxv)
        if pad:
            if bits:
                ret.append((acc << (tobits - bits)) & maxv)
        elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
            return None
        return ret
    
    
    def decode(hrp, addr):
        """Decode a segwit address."""
        hrpgot, data = bech32_decode(addr)
        if hrpgot != hrp:
            return (None, None)
        decoded = convertbits(data[1:], 5, 8, False)
        if decoded is None or len(decoded) < 2 or len(decoded) > 40:
            return (None, None)
        if data[0] > 16:
            return (None, None)
        if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
            return (None, None)
        return (data[0], decoded)
    
    
    def encode(hrp: str, witver: int, witprog: bytes) -> Optional[str]:
        """Encode a segwit address."""
        ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
        if decode(hrp, ret) == (None, None):
            return None
        return ret
    
    def pubkey_to_segwit_addr(pubkey: bytes) -> Optional[str]:
        hrp = "bc"               # "bc" for mainnet, "tb" for testnet
        witver = 0
        witprog = ripemd160(sha256(pubkey))
        addr = encode(hrp, witver, witprog)
        return addr
    
    pubkey_hex = '0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798'
    addr = pubkey_to_segwit_addr(bytearray.fromhex(pubkey_hex))
    print(addr)         # bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
    

1.  例子**P2WSH**（其实就是原来P2SH的隔离见证格式）
    

    
    from bitcoinutils.setup import setup
    from bitcoinutils.script import Script
    from bitcoinutils.keys import P2wshAddress, PrivateKey
    
    
    
    def main():
        # always remember to setup the network
        setup('testnet')
        
        #
        # P2WSH
        #
        p2wpkh_key = PrivateKey.from_wif('cNn8itYxAng4xR4eMtrPsrPpDpTdVNuw7Jb6kfhFYZ8DLSZBCg37')
        script = Script(['OP_1', p2wpkh_key.get_public_key().to_hex(), 'OP_1', 'OP_CHECKMULTISIG'])
        p2wsh_addr = P2wshAddress.from_script(script)
        print("P2WSH of P2PK:", p2wsh_addr.to_string() )
    
    if __name__ == "__main__":
        main() # tb1qy4kdfavhluvnhpwcqmqrd8x0ge2ynnsl7mv2mdmdskx4g3fc6ckq8f44jg

---

*Originally published on [heqing](https://paragraph.com/@vilink/rUlprbfgPZYeqjqHJ2Gb)*
