Example #1
0
def test_slip132_test_vector() -> None:
    """SLIP132 test vector

    https://github.com/satoshilabs/slips/blob/master/slip-0132.md
    """
    mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
    kpath = "./0/0"
    test_vectors: List[Tuple[bytes, str, str, str, str]] = [
        (
            NETWORKS["mainnet"]["bip32_prv"],
            "m / 44h / 0h / 0h",
            "xprv9xpXFhFpqdQK3TmytPBqXtGSwS3DLjojFhTGht8gwAAii8py5X6pxeBnQ6ehJiyJ6nDjWGJfZ95WxByFXVkDxHXrqu53WCRGypk2ttuqncb",
            "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj",
            "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA",
        ),
        (
            NETWORKS["mainnet"]["slip132_p2wpkh_p2sh_prv"],
            "m / 49h / 0h / 0h",
            "yprvAHwhK6RbpuS3dgCYHM5jc2ZvEKd7Bi61u9FVhYMpgMSuZS613T1xxQeKTffhrHY79hZ5PsskBjcc6C2V7DrnsMsNaGDaWev3GLRQRgV7hxF",
            "ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP",
            "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
        ),
        (
            NETWORKS["mainnet"]["slip132_p2wpkh_prv"],
            "m / 84h / 0h / 0h",
            "zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE",
            "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs",
            "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu",
        ),
    ]
    for version, der_path, prv, pub, addr_str in test_vectors:
        addr = addr_str.encode()
        rxprv = bip32.mxprv_from_bip39_mnemonic(mnemonic, "")
        mxprv = bip32.derive(rxprv, der_path, version)
        assert prv.encode() == mxprv
        mxpub = bip32.xpub_from_xprv(mxprv)
        assert pub.encode() == mxpub
        xpub = bip32.derive(mxpub, kpath)
        address = slip132.address_from_xpub(xpub)
        assert addr == address
        address = slip132.address_from_xkey(xpub)
        assert addr == address
        xprv = bip32.derive(mxprv, kpath)
        address = slip132.address_from_xkey(xprv)
        assert addr == address
        if version == NETWORKS["mainnet"]["bip32_prv"]:
            address = base58address.p2pkh(xpub)
            assert addr == address
            address = base58address.p2pkh(xprv)
            assert addr == address
        elif version == NETWORKS["mainnet"]["slip132_p2wpkh_p2sh_prv"]:
            address = base58address.p2wpkh_p2sh(xpub)
            assert addr == address
            address = base58address.p2wpkh_p2sh(xprv)
            assert addr == address
        elif version == NETWORKS["mainnet"]["slip132_p2wpkh_prv"]:
            address = bech32address.p2wpkh(xpub)
            assert addr == address
            address = bech32address.p2wpkh(xprv)
            assert addr == address
Example #2
0
def test_exceptions() -> None:

    with pytest.raises(ValueError, match="not a private or public key: "):
        # invalid checksum
        xprv = "xppp9s21ZrQH143K2oxHiQ5f7D7WYgXD9h6HAXDBuMoozDGGiYHWsq7TLBj2yvGuHTLSPCaFmUyN1v3fJRiY2A4YuNSrqQMPVLZKt76goL6LP7L"
        p2pkh(xprv)

    xpub = "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"
    with pytest.raises(ValueError, match="not a private key: "):
        bip32.xpub_from_xprv(xpub)

    xpub_dict = bip32.deserialize(xpub)
    xpub_dict = bip32.deserialize(xpub_dict)
    xpub_dict["chain_code"] = (xpub_dict["chain_code"])[:-1]
    with pytest.raises(ValueError, match="invalid chain code length: "):
        xpub_dict = bip32.deserialize(xpub_dict)
    xpub_dict = bip32.deserialize(xpub)
    xpub_dict[
        "chain_code"] = "length is 32 but not a chaincode"  # type: ignore
    with pytest.raises(ValueError, match="invalid chain code"):
        xpub_dict = bip32.deserialize(xpub_dict)

    seed = "5b56c417303faa3fcba7e57400e120a0"
    with pytest.raises(ValueError, match="unknown private key version: "):
        version = b"\x04\x88\xAD\xE5"
        bip32.rootxprv_from_seed(seed, version)

    with pytest.raises(ValueError, match="too many bits for seed: "):
        bip32.rootxprv_from_seed(seed * 5)

    with pytest.raises(ValueError, match="too few bits for seed: "):
        bip32.rootxprv_from_seed(seed[:-2])
Example #3
0
    def test_p2pkh_from_pubkey(self):
        # https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses
        pub = "02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352"
        addr = p2pkh(pub)
        self.assertEqual(addr, b'1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs')
        _, h160, _, _ = h160_from_b58address(addr)
        self.assertEqual(h160, hash160(pub))

        uncompr_pub = bytes_from_point(
            point_from_octets(pub), compressed=False)
        addr = p2pkh(uncompr_pub, compressed=False)
        self.assertEqual(addr, b'16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM')
        _, h160, _, _ = h160_from_b58address(addr)
        self.assertEqual(h160, hash160(uncompr_pub))

        # trailing/leading spaces in string
        pub = '  02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352'
        addr = p2pkh(pub)
        self.assertEqual(addr, b'1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs')
        _, h160, _, _ = h160_from_b58address(addr)
        self.assertEqual(h160, hash160(pub))

        pub = '02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352  '
        addr = p2pkh(pub)
        self.assertEqual(addr, b'1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs')
Example #4
0
    def test_one_prvkey_multiple_addresses(self):

        msg = "Paolo is afraid of ephemeral random numbers"

        # Compressed WIF
        wif = b'Kx45GeUBSMPReYQwgXiKhG9FzNXrnCeutJp4yjTd5kKxCitadm3C'
        pubkey, network = pubkey_info_from_prvkey(wif)
        address1 = p2pkh(pubkey, network)
        address2 = p2wpkh_p2sh(pubkey, network)
        address3 = p2wpkh(pubkey, network)

        # sign with no address (or compressed P2PKH)
        sig1 = bms.sign(msg, wif)
        # True for Bitcoin Core
        self.assertTrue(bms.verify(msg, address1, sig1))
        # True for Electrum p2wpkh_p2sh
        self.assertTrue(bms.verify(msg, address2, sig1))
        # True for Electrum p2wpkh
        self.assertTrue(bms.verify(msg, address3, sig1))

        # sign with p2wpkh_p2sh address (BIP137)
        sig2 = bms.sign(msg, wif, address2)
        # False for Bitcoin Core
        self.assertFalse(bms.verify(msg, address1, sig2))
        # True for BIP137 p2wpkh_p2sh
        self.assertTrue(bms.verify(msg, address2, sig2))
        # False for BIP137 p2wpkh
        self.assertFalse(bms.verify(msg, address3, sig2))

        # sign with p2wpkh address (BIP137)
        sig3 = bms.sign(msg, wif, address3)
        # False for Bitcoin Core
        self.assertFalse(bms.verify(msg, address1, sig3))
        # False for BIP137 p2wpkh_p2sh
        self.assertFalse(bms.verify(msg, address2, sig3))
        # True for BIP137 p2wpkh
        self.assertTrue(bms.verify(msg, address3, sig3))

        # uncompressed WIF / P2PKH address
        q, network, _ = prvkey_info_from_prvkey(wif)
        wif2 = wif_from_prvkey(q, network, False)
        pubkey, network = pubkey_info_from_prvkey(wif2)
        address4 = p2pkh(pubkey, network)

        # sign with uncompressed P2PKH
        sig4 = bms.sign(msg, wif2, address4)
        # False for Bitcoin Core compressed p2pkh
        self.assertFalse(bms.verify(msg, address1, sig4))
        # False for BIP137 p2wpkh_p2sh
        self.assertFalse(bms.verify(msg, address2, sig4))
        # False for BIP137 p2wpkh
        self.assertFalse(bms.verify(msg, address3, sig4))
        # True for Bitcoin Core uncompressed p2pkh
        self.assertTrue(bms.verify(msg, address4, sig4))

        self.assertRaises(ValueError, bms.sign, msg, wif2, address1)
        self.assertRaises(ValueError, bms.sign, msg, wif, address4)
Example #5
0
    def test_slip32_test_vector(self):
        """SLIP32 test vector

        https://github.com/satoshilabs/slips/blob/master/slip-0132.md
        """
        mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
        kpath = "./0/0"
        test_vectors = [
            [
                MAIN_xprv, "m / 44h / 0h / 0h",
                b'xprv9xpXFhFpqdQK3TmytPBqXtGSwS3DLjojFhTGht8gwAAii8py5X6pxeBnQ6ehJiyJ6nDjWGJfZ95WxByFXVkDxHXrqu53WCRGypk2ttuqncb',
                b'xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj',
                b'1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA'
            ],
            [
                MAIN_yprv, "m / 49h / 0h / 0h",
                b'yprvAHwhK6RbpuS3dgCYHM5jc2ZvEKd7Bi61u9FVhYMpgMSuZS613T1xxQeKTffhrHY79hZ5PsskBjcc6C2V7DrnsMsNaGDaWev3GLRQRgV7hxF',
                b'ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP',
                b'37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf'
            ],
            [
                MAIN_zprv, "m / 84h / 0h / 0h",
                b'zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE',
                b'zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs',
                b'bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu'
            ],
        ]
        for v in test_vectors:
            rxprv = bip32.rootxprv_from_bip39mnemonic(mnemonic, '', v[0])
            mxprv = bip32.derive(rxprv, v[1])
            self.assertEqual(v[2], mxprv)
            mxpub = bip32.xpub_from_xprv(mxprv)
            self.assertEqual(v[3], mxpub)
            xpub = bip32.derive(mxpub, kpath)
            address = slip32.address_from_xpub(xpub)
            self.assertEqual(v[4], address)
            address = slip32.address_from_xkey(xpub)
            self.assertEqual(v[4], address)
            xprv = bip32.derive(mxprv, kpath)
            address = slip32.address_from_xkey(xprv)
            self.assertEqual(v[4], address)
            if v[0] == MAIN_xprv:
                address = base58address.p2pkh(xpub)
                self.assertEqual(v[4], address)
                address = base58address.p2pkh(xprv)
                self.assertEqual(v[4], address)
            elif v[0] == MAIN_yprv:
                address = base58address.p2wpkh_p2sh(xpub)
                self.assertEqual(v[4], address)
                address = base58address.p2wpkh_p2sh(xprv)
                self.assertEqual(v[4], address)
            elif v[0] == MAIN_zprv:
                address = bech32address.p2wpkh(xpub)
                self.assertEqual(v[4], address)
                address = bech32address.p2wpkh(xprv)
                self.assertEqual(v[4], address)
Example #6
0
def test_exceptions() -> None:

    pubkey = "02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352"
    payload = b"\xf5" + hash160(pubkey)
    invalid_address = b58encode(payload)
    with pytest.raises(ValueError, match="invalid base58 address prefix: "):
        h160_from_b58address(invalid_address)

    with pytest.raises(ValueError, match="not a private or public key: "):
        p2pkh(pubkey + "00")
Example #7
0
    def test_p2pkh(self):

        script_type = 'p2pkh'

        # self-consistency
        pubkey = "04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4"
        payload = hash160(pubkey)
        script = encode(['OP_DUP', 'OP_HASH160', payload,
                         'OP_EQUALVERIFY', 'OP_CHECKSIG'])

        # straight to the scriptPubKey
        scriptPubKey = p2pkh(pubkey)
        self.assertEqual(scriptPubKey.hex(), script.hex())

        # to the scriptPubKey in two steps (through payload)
        scriptPubKey = scriptPubKey_from_payload(script_type, payload)
        self.assertEqual(scriptPubKey.hex(), script.hex())

        # back from the scriptPubKey to the payload
        script_type2, payload2, m2 = payload_from_scriptPubKey(scriptPubKey)
        self.assertEqual(script_type, script_type2)
        self.assertEqual(0, m2)
        self.assertEqual(payload.hex(), payload2.hex())
        script_type2, payload2, m2 = payload_from_scriptPubKey(script)
        self.assertEqual(script_type, script_type2)
        self.assertEqual(0, m2)
        self.assertEqual(payload.hex(), payload2.hex())

        # data -> payload is not invertible (hash functions)

        # address
        network = 'mainnet'
        address = base58address.p2pkh(pubkey, network)
        address2 = address_from_scriptPubKey(scriptPubKey, network)
        self.assertEqual(address, address2)
        prefix = p2pkh_prefix_from_network(network)
        address2 = b58address_from_h160(prefix, payload)
        self.assertEqual(address, address2)

        scriptPubKey2, network2 = scriptPubKey_from_address(address)
        self.assertEqual(scriptPubKey2, scriptPubKey)
        self.assertEqual(network2, network)

        # documented test case: https://learnmeabitcoin.com/guide/p2pkh
        payload = "12ab8dc588ca9d5787dde7eb29569da63c3a238c"
        script = "76a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac"
        scriptPubKey = scriptPubKey_from_payload(script_type, payload)
        self.assertEqual(scriptPubKey.hex(), script)
        network = 'mainnet'
        address = b"12higDjoCCNXSA95xZMWUdPvXNmkAduhWv"
        address2 = address_from_scriptPubKey(scriptPubKey, network)
        self.assertEqual(address, address2)
        scriptPubKey2, network2 = scriptPubKey_from_address(address)
        self.assertEqual(scriptPubKey2, scriptPubKey)
        self.assertEqual(network2, network)

        # Invalid size: 11 bytes instead of 20
        self.assertRaises(
            ValueError, scriptPubKey_from_payload, "00" * 11, 'p2pkh')
Example #8
0
    def test_msgsign_p2pkh(self):
        msg = "test message"
        # sigs are taken from (Electrum and) Bitcoin Core

        q = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"

        # uncompressed
        wif1u = wif_from_prvkey(q, "mainnet", False)
        self.assertEqual(
            wif1u, b"5KMWWy2d3Mjc8LojNoj8Lcz9B1aWu8bRofUgGwQk959Dw5h2iyw")
        pubkey1u, _ = pubkeyinfo_from_prvkey(wif1u)
        add1u = base58address.p2pkh(pubkey1u)
        self.assertEqual(add1u, b"1HUBHMij46Hae75JPdWjeZ5Q7KaL7EFRSD")
        sig1u = bms.sign(msg, wif1u)
        self.assertTrue(bms.verify(msg, add1u, sig1u))
        self.assertEqual(sig1u[0], 27)
        exp_sig1u = ("G/iew/NhHV9V9MdUEn/LFOftaTy1ivGPKPKyMlr8OSokNC755fAxpST"
                     "hNRivwTNsyY9vPUDTRYBPc2cmGd5d4y4=").encode()
        self.assertEqual(bms.serialize(*sig1u), exp_sig1u)

        # compressed
        wif1c = wif_from_prvkey(q, "mainnet", True)
        self.assertEqual(
            wif1c, b"L41XHGJA5QX43QRG3FEwPbqD5BYvy6WxUxqAMM9oQdHJ5FcRHcGk")
        pubkey1c, _ = pubkeyinfo_from_prvkey(wif1c)
        add1c = base58address.p2pkh(pubkey1c)
        self.assertEqual(add1c, b"14dD6ygPi5WXdwwBTt1FBZK3aD8uDem1FY")
        sig1c = bms.sign(msg, wif1c)
        self.assertTrue(bms.verify(msg, add1c, sig1c))
        self.assertEqual(sig1c[0], 31)
        exp_sig1c = ("H/iew/NhHV9V9MdUEn/LFOftaTy1ivGPKPKyMlr8OSokNC755fAxpST"
                     "hNRivwTNsyY9vPUDTRYBPc2cmGd5d4y4=").encode()
        self.assertEqual(bms.serialize(*sig1c), exp_sig1c)

        self.assertFalse(bms.verify(msg, add1c, sig1u))
        self.assertFalse(bms.verify(msg, add1u, sig1c))

        rf, r, s, = sig1c
        sig1c_malleated_rf = bms.serialize(rf + 1, r, s)
        self.assertFalse(bms.verify(msg, add1c, sig1c_malleated_rf))
        sig1c_malleated_s = bms.serialize(rf, r, ec.n - s)
        self.assertFalse(bms.verify(msg, add1c, sig1c_malleated_s))
        sig1c_malleated_rf_s = bms.serialize(rf + 1, r, ec.n - s)
        self.assertTrue(bms.verify(msg, add1c, sig1c_malleated_rf_s))
Example #9
0
    def test_msgsign_p2pkh(self):
        msg = 'test message'
        # sigs are taken from (Electrum and) Bitcoin Core

        # first private key
        q1 = 91634880152443617534842621287039938041581081254914058002978601050179556493499

        # uncompressed
        wif1u = wif_from_prvkey(q1, 'mainnet', False)
        self.assertEqual(
            wif1u, b'5KMWWy2d3Mjc8LojNoj8Lcz9B1aWu8bRofUgGwQk959Dw5h2iyw')
        pubkey1u, _ = pubkey_info_from_prvkey(wif1u)
        add1u = base58address.p2pkh(pubkey1u)
        self.assertEqual(add1u, b'1HUBHMij46Hae75JPdWjeZ5Q7KaL7EFRSD')
        sig1u = bms.sign(msg, wif1u)
        self.assertTrue(bms.verify(msg, add1u, sig1u))
        self.assertEqual(sig1u[0], 27)
        exp_sig1u = b'G/iew/NhHV9V9MdUEn/LFOftaTy1ivGPKPKyMlr8OSokNC755fAxpSThNRivwTNsyY9vPUDTRYBPc2cmGd5d4y4='
        self.assertEqual(bms.serialize(*sig1u), exp_sig1u)

        # compressed
        wif1c = wif_from_prvkey(q1, 'mainnet', True)
        self.assertEqual(
            wif1c, b'L41XHGJA5QX43QRG3FEwPbqD5BYvy6WxUxqAMM9oQdHJ5FcRHcGk')
        pubkey1c, _ = pubkey_info_from_prvkey(wif1c)
        add1c = base58address.p2pkh(pubkey1c)
        self.assertEqual(add1c, b'14dD6ygPi5WXdwwBTt1FBZK3aD8uDem1FY')
        sig1c = bms.sign(msg, wif1c)
        self.assertTrue(bms.verify(msg, add1c, sig1c))
        self.assertEqual(sig1c[0], 31)
        exp_sig1c = b'H/iew/NhHV9V9MdUEn/LFOftaTy1ivGPKPKyMlr8OSokNC755fAxpSThNRivwTNsyY9vPUDTRYBPc2cmGd5d4y4='
        self.assertEqual(bms.serialize(*sig1c), exp_sig1c)

        self.assertFalse(bms.verify(msg, add1c, sig1u))
        self.assertFalse(bms.verify(msg, add1u, sig1c))

        rf, r, s, = sig1c
        sig1c_malleated_rf = bms.serialize(rf + 1, r, s)
        self.assertFalse(bms.verify(msg, add1c, sig1c_malleated_rf))
        sig1c_malleated_s = bms.serialize(rf, r, ec.n - s)
        self.assertFalse(bms.verify(msg, add1c, sig1c_malleated_s))
        sig1c_malleated_rf_s = bms.serialize(rf + 1, r, ec.n - s)
        self.assertTrue(bms.verify(msg, add1c, sig1c_malleated_rf_s))
Example #10
0
    def test_testnet(self):
        # bitcoin core derivation style
        rootxprv = "tprv8ZgxMBicQKsPe3g3HwF9xxTLiyc5tNyEtjhBBAk29YA3MTQUqULrmg7aj9qTKNfieuu2HryQ6tGVHse9x7ANFGs3f4HgypMc5nSSoxwf7TK"

        # m / 0h / 0h / 51h
        addr1 = b"mfXYCCsvWPgeCv8ZYGqcubpNLYy5nYHbbj"
        indexes = [0x80000000, 0x80000000, 0x80000000 + 51]
        addr = p2pkh(xpub_from_xprv(derive(rootxprv, indexes)))
        self.assertEqual(addr, addr1)
        path = "m/0h/0h/51h"
        addr = p2pkh(xpub_from_xprv(derive(rootxprv, path)))
        self.assertEqual(addr, addr1)

        # m / 0h / 1h / 150h
        addr2 = b"mfaUnRFxVvf55uD1P3zWXpprN1EJcKcGrb"
        indexes = [0x80000000, 0x80000000 + 1, 0x80000000 + 150]
        addr = p2pkh(xpub_from_xprv(derive(rootxprv, indexes)))
        self.assertEqual(addr, addr2)
        path = "m/0h/1h/150h"
        addr = p2pkh(xpub_from_xprv(derive(rootxprv, path)))
        self.assertEqual(addr, addr2)
Example #11
0
def test_derive_from_account() -> None:

    seed = "bfc4cbaad0ff131aa97fa30a48d09ae7df914bcc083af1e07793cd0a7c61a03f65d622848209ad3366a419f4718a80ec9037df107d8d12c19b83202de00a40ad"
    rmxprv = bip32.rootxprv_from_seed(seed)

    der_path = "m / 44 h / 0 h"
    mxpub = bip32.xpub_from_xprv(bip32.derive(rmxprv, der_path))

    test_vectors = [
        [0, 0],
        [0, 1],
        [0, 2],
        [1, 0],
        [1, 1],
        [1, 2],
    ]

    for branch, index in test_vectors:
        full_path = der_path + f"/{branch}/{index}"
        addr = p2pkh(bip32.derive(rmxprv, full_path)).decode()
        assert addr == p2pkh(bip32.derive_from_account(mxpub, branch,
                                                       index)).decode()

    errmsg = "invalid private derivation at branch level"
    with pytest.raises(ValueError, match=errmsg):
        bip32.derive_from_account(mxpub, 0x80000000, 0, True)

    errmsg = "invalid branch: "
    with pytest.raises(ValueError, match=errmsg):
        bip32.derive_from_account(mxpub, 2, 0)

    errmsg = "invalid private derivation at address index level"
    with pytest.raises(ValueError, match=errmsg):
        bip32.derive_from_account(mxpub, 0, 0x80000000)

    der_path = "m / 44 h / 0"
    mxpub = bip32.xpub_from_xprv(bip32.derive(rmxprv, der_path))
    errmsg = "public derivation at account level"
    with pytest.raises(UserWarning, match=errmsg):
        bip32.derive_from_account(mxpub, 0, 0)
Example #12
0
def test_address_from_wif() -> None:

    q = 0x19E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725

    test_cases: List[Tuple[bool, str, str, str]] = [
        (
            False,
            "mainnet",
            "5J1geo9kcAUSM6GJJmhYRX1eZEjvos9nFyWwPstVziTVueRJYvW",
            "1LPM8SZ4RQDMZymUmVSiSSvrDfj1UZY9ig",
        ),
        (
            True,
            "mainnet",
            "Kx621phdUCp6sgEXPSHwhDTrmHeUVrMkm6T95ycJyjyxbDXkr162",
            "1HJC7kFvXHepkSzdc8RX6khQKkAyntdfkB",
        ),
        (
            False,
            "testnet",
            "91nKEXyJCPYaK9maw7bTJ7ZcCu6dy2gybvNtUWF1LTCYggzhZgy",
            "mzuJRVe3ERecM6F6V4R6GN9B5fKiPC9HxF",
        ),
        (
            True,
            "testnet",
            "cNT1UjhUuGWN37hnmr754XxvPWwtAJTSq8bcCQ4pUrdxqxbA1iU1",
            "mwp9QoLuLK65XZUFKhPtvfujBjmgkZnmPx",
        ),
    ]
    for compressed, network, wif, address in test_cases:
        assert wif.encode() == wif_from_prvkey(q, network, compressed)
        assert prvkeyinfo_from_prvkey(wif) == (q, network, compressed)
        b58 = p2pkh(wif)
        assert b58 == address.encode()
        _, payload, net, is_script = h160_from_b58address(b58)
        assert net == network
        assert not is_script
        if compressed:
            b32 = p2wpkh(wif)
            assert (payload, network,
                    is_script) == witness_from_b32address(b32)[1:]
            b = p2wpkh_p2sh(wif)
            _, payload2, net, is_script = h160_from_b58address(b)
            assert is_script
            assert (hash160(b"\x00\x14" + payload), network) == (payload2, net)
        else:
            err_msg = "not a private or compressed public key: "
            with pytest.raises(ValueError, match=err_msg):
                p2wpkh(wif)  # type: ignore
            with pytest.raises(ValueError, match=err_msg):
                p2wpkh_p2sh(wif)  # type: ignore
Example #13
0
def test_derive() -> None:

    test_vectors = {
        "xprv9s21ZrQH143K2ZP8tyNiUtgoezZosUkw9hhir2JFzDhcUWKz8qFYk3cxdgSFoCMzt8E2Ubi1nXw71TLhwgCfzqFHfM5Snv4zboSebePRmLS":
        [
            ["m / 0 h / 0 h / 463 h", "1DyfBWxhVLmrJ7keyiHeMbt7N3UdeGU4G5"],
            ["M / 0H / 0h // 267' / ", "11x2mn59Qy43DjisZWQGRResjyQmgthki"],
        ],
        "tprv8ZgxMBicQKsPe3g3HwF9xxTLiyc5tNyEtjhBBAk29YA3MTQUqULrmg7aj9qTKNfieuu2HryQ6tGVHse9x7ANFGs3f4HgypMc5nSSoxwf7TK":
        [
            ["m / 0 h / 0 h / 51 h", "mfXYCCsvWPgeCv8ZYGqcubpNLYy5nYHbbj"],
            ["m / 0 h / 1 h / 150 h", "mfaUnRFxVvf55uD1P3zWXpprN1EJcKcGrb"],
        ],
    }

    for rootxprv, value in test_vectors.items():
        for der_path, address in value:
            assert address == p2pkh(bip32.derive(rootxprv, der_path)).decode()

            b_indexes, _ = bip32._indexes_from_path(der_path)
            indexes = [int.from_bytes(b_index, "big") for b_index in b_indexes]
            assert address == p2pkh(bip32.derive(rootxprv, indexes)).decode()
Example #14
0
def test_msgsign_p2pkh() -> None:
    msg = "test message"
    # sigs are taken from (Electrum and) Bitcoin Core

    q = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"

    # uncompressed
    wif1u = wif_from_prvkey(q, "mainnet", False)
    assert wif1u == b"5KMWWy2d3Mjc8LojNoj8Lcz9B1aWu8bRofUgGwQk959Dw5h2iyw"
    add1u = base58address.p2pkh(wif1u)
    assert add1u == b"1HUBHMij46Hae75JPdWjeZ5Q7KaL7EFRSD"
    sig1u = bms.sign(msg, wif1u)
    assert bms.verify(msg, add1u, sig1u)
    assert sig1u[0] == 27
    exp_sig1u = "G/iew/NhHV9V9MdUEn/LFOftaTy1ivGPKPKyMlr8OSokNC755fAxpSThNRivwTNsyY9vPUDTRYBPc2cmGd5d4y4="
    assert bms.encode(*sig1u) == exp_sig1u.encode()

    # compressed
    wif1c = wif_from_prvkey(q, "mainnet", True)
    assert wif1c == b"L41XHGJA5QX43QRG3FEwPbqD5BYvy6WxUxqAMM9oQdHJ5FcRHcGk"
    add1c = base58address.p2pkh(wif1c)
    assert add1c == b"14dD6ygPi5WXdwwBTt1FBZK3aD8uDem1FY"
    sig1c = bms.sign(msg, wif1c)
    assert bms.verify(msg, add1c, sig1c)
    assert sig1c[0] == 31
    exp_sig1c = "H/iew/NhHV9V9MdUEn/LFOftaTy1ivGPKPKyMlr8OSokNC755fAxpSThNRivwTNsyY9vPUDTRYBPc2cmGd5d4y4="
    assert bms.encode(*sig1c) == exp_sig1c.encode()

    assert not bms.verify(msg, add1c, sig1u)
    assert not bms.verify(msg, add1u, sig1c)

    rf, r, s = sig1c

    sig1c_malleated_rf = bms.encode(rf + 1, r, s)
    assert not bms.verify(msg, add1c, sig1c_malleated_rf)
    sig1c_malleated_s = bms.encode(rf, r, ec.n - s)
    assert not bms.verify(msg, add1c, sig1c_malleated_s)
    sig1c_malleated_rf_s = bms.encode(rf + 1, r, ec.n - s)
    assert bms.verify(msg, add1c, sig1c_malleated_rf_s)
Example #15
0
    def test_p2pkh_from_wif(self):
        seed = b"00" * 32  # better be random
        rxprv = bip32.rootxprv_from_seed(seed)
        path = "m/0h/0h/12"
        xprv = bip32.derive(rxprv, path)
        wif = wif_from_prvkey(xprv)
        self.assertEqual(
            wif, b'KyLk7s6Z1FtgYEVp3bPckPVnXvLUWNCcVL6wNt3gaT96EmzTKZwP')
        pubkey, _ = pubkey_info_from_prvkey(wif)
        address = p2pkh(pubkey)
        xpub = bip32.xpub_from_xprv(xprv)
        address2 = slip32.address_from_xpub(xpub)
        self.assertEqual(address, address2)

        self.assertRaises(ValueError, wif_from_prvkey, xpub)
Example #16
0
def test_p2pkh_from_wif() -> None:
    seed = b"\x00" * 32  # better be a documented test case
    rxprv = bip32.rootxprv_from_seed(seed)
    path = "m/0h/0h/12"
    xprv = bip32.derive(rxprv, path)
    wif = wif_from_prvkey(xprv)
    assert wif == b"L2L1dqRmkmVtwStNf5wg8nnGaRn3buoQr721XShM4VwDbTcn9bpm"
    pubkey, _ = pubkeyinfo_from_prvkey(wif)
    address = p2pkh(pubkey)
    xpub = bip32.xpub_from_xprv(xprv)
    address2 = slip132.address_from_xpub(xpub)
    assert address == address2

    err_msg = "not a private key: "
    with pytest.raises(ValueError, match=err_msg):
        wif_from_prvkey(xpub)
Example #17
0
def test_p2pkh_from_wif() -> None:
    seed = b"00" * 32  # better be a documented test case
    rxprv = bip32.rootxprv_from_seed(seed)
    path = "m/0h/0h/12"
    xprv = bip32.derive(rxprv, path)
    wif = wif_from_prvkey(xprv)
    assert wif == b"KyLk7s6Z1FtgYEVp3bPckPVnXvLUWNCcVL6wNt3gaT96EmzTKZwP"
    pubkey, _ = pubkeyinfo_from_prvkey(wif)
    address = p2pkh(pubkey)
    xpub = bip32.xpub_from_xprv(xprv)
    address2 = slip132.address_from_xpub(xpub)
    assert address == address2

    err_msg = "not a private key: "
    with pytest.raises(ValueError, match=err_msg):
        wif_from_prvkey(xpub)
Example #18
0
def test_p2pkh() -> None:

    # self-consistency
    pubkey = (
        "04 "
        "cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf"
        "f7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4")
    payload = hash160(pubkey)
    script_pubkey = script.serialize(
        ["OP_DUP", "OP_HASH160", payload, "OP_EQUALVERIFY", "OP_CHECKSIG"])
    assert script_pubkey == p2pkh(pubkey)

    # to the script_pubkey in two steps (through payload)
    script_type = "p2pkh"
    assert script_pubkey == script_pubkey_from_payload(script_type, payload)

    # back from the script_pubkey to the payload
    assert (script_type, payload,
            0) == payload_from_script_pubkey(script_pubkey)

    # base58 address
    network = "mainnet"
    address = base58address.p2pkh(pubkey, network)
    assert address == address_from_script_pubkey(script_pubkey, network)
    prefix = NETWORKS[network].p2pkh
    assert address == b58address_from_h160(prefix, payload, network)

    # back from the address to the script_pubkey
    assert (script_pubkey, network) == script_pubkey_from_address(address)

    # documented test case: https://learnmeabitcoin.com/guide/p2pkh
    payload = "12ab8dc588ca9d5787dde7eb29569da63c3a238c"
    script_pubkey = "76a914" + payload + "88ac"
    assert script_pubkey == script_pubkey_from_payload(script_type,
                                                       payload).hex()
    address = b"12higDjoCCNXSA95xZMWUdPvXNmkAduhWv"
    assert address == address_from_script_pubkey(script_pubkey, network)
    assert (bytes.fromhex(script_pubkey),
            network) == script_pubkey_from_address(address)

    # invalid size: 11 bytes instead of 20
    err_msg = "invalid size: "
    with pytest.raises(BTClibValueError, match=err_msg):
        script_pubkey_from_payload(script_type, "00" * 11)
Example #19
0
    def test_segwit(self):

        msg = "test"
        wif = "L4xAvhKR35zFcamyHME2ZHfhw5DEyeJvEMovQHQ7DttPTM8NLWCK"
        pubkey, _ = pubkeyinfo_from_prvkey(wif)
        p2pkh = base58address.p2pkh(pubkey)
        p2wpkh = bech32address.p2wpkh(pubkey)
        p2wpkh_p2sh = base58address.p2wpkh_p2sh(pubkey)

        # p2pkh base58 address (Core, Electrum, BIP137)
        exp_sig = ("IBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IO"
                   "I0dpo3uGAEpCz9eepXLrA5kF35MXuU=").encode()
        self.assertTrue(bms.verify(msg, p2pkh, exp_sig))
        sig = bms.sign(msg, wif)  # no address: p2pkh assumed
        self.assertTrue(bms.verify(msg, p2pkh, sig))
        self.assertEqual(bms.serialize(*sig), exp_sig)

        # p2wpkh-p2sh base58 address (Electrum)
        self.assertTrue(bms.verify(msg, p2wpkh_p2sh, sig))

        # p2wpkh bech32 address (Electrum)
        self.assertTrue(bms.verify(msg, p2wpkh, sig))

        # p2wpkh-p2sh base58 address (BIP137)
        # different first letter in sig because of different rf
        exp_sig = ("JBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IO"
                   "I0dpo3uGAEpCz9eepXLrA5kF35MXuU=").encode()
        self.assertTrue(bms.verify(msg, p2wpkh_p2sh, exp_sig))
        sig = bms.sign(msg, wif, p2wpkh_p2sh)
        self.assertTrue(bms.verify(msg, p2wpkh_p2sh, sig))
        self.assertEqual(bms.serialize(*sig), exp_sig)

        # p2wpkh bech32 address (BIP137)
        # different first letter in sig because of different rf
        exp_sig = ("KBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IO"
                   "I0dpo3uGAEpCz9eepXLrA5kF35MXuU=").encode()
        self.assertTrue(bms.verify(msg, p2wpkh, exp_sig))
        sig = bms.sign(msg, wif, p2wpkh)
        self.assertTrue(bms.verify(msg, p2wpkh, sig))
        self.assertEqual(bms.serialize(*sig), exp_sig)
Example #20
0
def test_segwit() -> None:

    msg = "test"
    wif = "L4xAvhKR35zFcamyHME2ZHfhw5DEyeJvEMovQHQ7DttPTM8NLWCK"
    b58_p2pkh = base58address.p2pkh(wif)
    b58_p2wpkh = bech32address.p2wpkh(wif)
    b58_p2wpkh_p2sh = base58address.p2wpkh_p2sh(wif)

    # p2pkh base58 address (Core, Electrum, BIP137)
    exp_sig = "IBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IOI0dpo3uGAEpCz9eepXLrA5kF35MXuU="
    assert bms.verify(msg, b58_p2pkh, exp_sig)
    sig = bms.sign(msg, wif)  # no address: p2pkh assumed
    assert bms.verify(msg, b58_p2pkh, sig)
    assert bms.encode(*sig) == exp_sig.encode()

    # p2wpkh-p2sh base58 address (Electrum)
    assert bms.verify(msg, b58_p2wpkh_p2sh, sig)

    # p2wpkh bech32 address (Electrum)
    assert bms.verify(msg, b58_p2wpkh, sig)

    # p2wpkh-p2sh base58 address (BIP137)
    # different first letter in sig because of different rf
    exp_sig = "JBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IOI0dpo3uGAEpCz9eepXLrA5kF35MXuU="
    assert bms.verify(msg, b58_p2wpkh_p2sh, exp_sig)
    sig = bms.sign(msg, wif, b58_p2wpkh_p2sh)
    assert bms.verify(msg, b58_p2wpkh_p2sh, sig)
    assert bms.encode(*sig) == exp_sig.encode()

    # p2wpkh bech32 address (BIP137)
    # different first letter in sig because of different rf
    exp_sig = "KBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IOI0dpo3uGAEpCz9eepXLrA5kF35MXuU="
    assert bms.verify(msg, b58_p2wpkh, exp_sig)
    sig = bms.sign(msg, wif, b58_p2wpkh)
    assert bms.verify(msg, b58_p2wpkh, sig)
    assert bms.encode(*sig) == exp_sig.encode()
Example #21
0
    def test_mainnet(self):
        # bitcoin core derivation style
        rootxprv = "xprv9s21ZrQH143K2ZP8tyNiUtgoezZosUkw9hhir2JFzDhcUWKz8qFYk3cxdgSFoCMzt8E2Ubi1nXw71TLhwgCfzqFHfM5Snv4zboSebePRmLS"

        # m / 0h / 0h / 463h
        addr1 = b"1DyfBWxhVLmrJ7keyiHeMbt7N3UdeGU4G5"
        indexes = [0x80000000, 0x80000000, 0x800001CF]
        addr = p2pkh(xpub_from_xprv(derive(rootxprv, indexes)))
        self.assertEqual(addr, addr1)
        path = "m / 0h / 0h / 463h"
        addr = p2pkh(xpub_from_xprv(derive(rootxprv, path)))
        self.assertEqual(addr, addr1)

        # m / 0h / 0h / 267h
        addr2 = b"11x2mn59Qy43DjisZWQGRResjyQmgthki"
        indexes = [0x80000000, 0x80000000, 0x8000010B]
        addr = p2pkh(xpub_from_xprv(derive(rootxprv, indexes)))
        self.assertEqual(addr, addr2)
        path = "M / 0H / 0h // 267' / "
        addr = p2pkh(xpub_from_xprv(derive(rootxprv, path)))
        self.assertEqual(addr, addr2)

        seed = "bfc4cbaad0ff131aa97fa30a48d09ae7df914bcc083af1e07793cd0a7c61a03f65d622848209ad3366a419f4718a80ec9037df107d8d12c19b83202de00a40ad"
        xprv = rootxprv_from_seed(seed)
        xpub = "xpub661MyMwAqRbcFMYjmw8C6dJV97a4oLss6hb3v9wTQn2X48msQB61RCaLGtNhzgPCWPaJu7SvuB9EBSFCL43kTaFJC3owdaMka85uS154cEh"
        self.assertEqual(xpub_from_xprv(xprv).decode(), xpub)

        ind = "./0/0"
        addr = p2pkh(xpub_from_xprv(derive(xprv, ind)))
        self.assertEqual(addr, b"1FcfDbWwGs1PmyhMVpCAhoTfMnmSuptH6g")

        ind = "./0/1"
        addr = p2pkh(xpub_from_xprv(derive(xprv, ind)))
        self.assertEqual(addr, b"1K5GjYkZnPFvMDTGaQHTrVnd8wjmrtfR5x")

        ind = "./0/2"
        addr = p2pkh(xpub_from_xprv(derive(xprv, ind)))
        self.assertEqual(addr, b"1PQYX2uN7NYFd7Hq22ECMzfDcKhtrHmkfi")

        ind = "./1/0"
        addr = p2pkh(xpub_from_xprv(derive(xprv, ind)))
        self.assertEqual(addr, b"1BvSYpojWoWUeaMLnzbkK55v42DbizCoyq")

        ind = "./1/1"
        addr = p2pkh(xpub_from_xprv(derive(xprv, ind)))
        self.assertEqual(addr, b"1NXB59hF4QzYpFrB7o6usLBjbk2D3ZqxAL")

        ind = "./1/2"
        addr = p2pkh(xpub_from_xprv(derive(xprv, ind)))
        self.assertEqual(addr, b"16NLYkKtvYhW1Jp86tbocku3gxWcvitY1w")

        # version/key mismatch in extended parent key
        temp = b58decode(rootxprv)
        bad_xprv = b58encode(temp[0:45] + b"\x01" + temp[46:], 78)
        self.assertRaises(ValueError, derive, bad_xprv, 1)
        # derive(bad_xprv, 1)

        # version/key mismatch in extended parent key
        xpub = xpub_from_xprv(rootxprv)
        temp = b58decode(xpub)
        bad_xpub = b58encode(temp[0:45] + b"\x00" + temp[46:], 78)
        self.assertRaises(ValueError, derive, bad_xpub, 1)
        # derive(bad_xpub, 1)

        # no private/hardened derivation from pubkey
        self.assertRaises(ValueError, derive, xpub, 0x80000000)
Example #22
0
    def test_mainnet_versions(self):

        # data cross-checked with Electrum and
        # https://jlopp.github.io/xpub-converter/

        # 128 bits
        raw_entr = bytes.fromhex("6" * 32)
        # 12 words
        mnemonic = bip39.mnemonic_from_entropy(raw_entr, "en")
        seed = bip39.seed_from_mnemonic(mnemonic, "")

        # p2pkh BIP44
        # m / 44h / coin_typeh / accounth / change / address_index
        path = "m/44h/0h/0h"
        version = NETWORKS["mainnet"]["bip32_prv"]
        rootprv = rootxprv_from_seed(seed, version)
        xprv = derive(rootprv, path)
        xpub = xpub_from_xprv(xprv)
        exp = "xpub6C3uWu5Go5q62JzJpbjyCLYRGLYvexFeiepZTsYZ6SRexARkNfjG7GKtQVuGR3KHsyKsAwv7Hz3iNucPp6pfHiLvBczyK1j5CtBtpHB3NKx"
        self.assertEqual(xpub.decode(), exp)
        # first addresses
        xpub_ext = derive(xpub, "./0/0")  # external
        address = p2pkh(xpub_ext)
        exp_address = b"1DDKKVHoFWGfctyEEJvrusqq6ipEaieGCq"
        self.assertEqual(address, exp_address)
        xpub_int = derive(xpub, "./1/0")  # internal
        address = p2pkh(xpub_int)
        exp_address = b"1FhKoffreKHzhtBMVW9NSsg3ZF148JPGoR"
        self.assertEqual(address, exp_address)

        # legacy segwit (p2wsh-p2sh)
        # m / 49h / coin_typeh / accounth / change / address_index
        path = "m/49h/0h/0h"
        version = NETWORKS["mainnet"]["slip32_p2wsh_p2sh_prv"]
        rootprv = rootxprv_from_seed(seed, version)
        xprv = derive(rootprv, path)
        xpub = xpub_from_xprv(xprv)
        exp = "ypub6YBGdYufCVeoPVmNXfdrWhaBCXsQoLKNetNmD9bPTrKmnKVmiyU8f1uJqwGdmBb8kbAZpHoYfXQTLbWpkXc4skQDAreeCUXdbX9k8vtiHsN"
        self.assertEqual(xpub.decode(), exp)
        # first addresses
        xpub_ext = derive(xpub, "./0/0")  # external
        address = p2wpkh_p2sh(xpub_ext)
        exp_address = b"3FmNAiTCWe5kPMgc4dtSgEdY8VuaCiJEH8"
        self.assertEqual(address, exp_address)
        xpub_int = derive(xpub, "./1/0")  # internal
        address = p2wpkh_p2sh(xpub_int)
        exp_address = b"34FLgkoRYX5Q5fqiZCZDwsK5GpXxmFuLJN"
        self.assertEqual(address, exp_address)

        # legacy segwit (p2wpkh-p2sh)
        # m / 49h / coin_typeh / accounth / change / address_index
        path = "m/49h/0h/0h"
        version = NETWORKS["mainnet"]["slip32_p2wpkh_p2sh_prv"]
        rootprv = rootxprv_from_seed(seed, version)
        xprv = derive(rootprv, path)
        xpub = xpub_from_xprv(xprv)
        exp = "Ypub6j5Mkne6mTDAp4vkUL6qLmuyvKug1gzxyA2S8QrvqdABQW4gVNrQk8mEeeE7Kcp2z4EYgsofYjnxTm8b3km22EWt1Km3bszdVFRcipc6rXu"
        self.assertEqual(xpub.decode(), exp)

        # native segwit (p2wpkh)
        # m / 84h / coin_typeh / accounth / change / address_index
        path = "m/84h/0h/0h"
        version = NETWORKS["mainnet"]["slip32_p2wpkh_prv"]
        rootprv = rootxprv_from_seed(seed, version)
        xprv = derive(rootprv, path)
        xpub = xpub_from_xprv(xprv)
        exp = "zpub6qg3Uc1BAQkQvcBUYMmZHSzbsshSon3FvJ8yvH3ZZMjFNvJkwSji8UUwghiF3wvpvSvcNWVP8kfUhc2V2RwGp6pTC3ouj6njj956f26TniN"
        self.assertEqual(xpub.decode(), exp)
        # first addresses
        xpub_ext = derive(xpub, "./0/0")  # external
        address = p2wpkh(xpub_ext)
        exp_address = b"bc1q0hy024867ednvuhy9en4dggflt5w9unw4ztl5a"
        self.assertEqual(address, exp_address)
        xpub_int = derive(xpub, "./1/0")  # internal
        address = p2wpkh(xpub_int)
        exp_address = b"bc1qy4x03jyl88h2zeg7l287xhv2xrwk4c3ztfpjd2"
        self.assertEqual(address, exp_address)

        # native segwit (p2wsh)
        # m / 84h / coin_typeh / accounth / change / address_index
        path = "m/84h/0h/0h"
        version = NETWORKS["mainnet"]["slip32_p2wsh_prv"]
        rootprv = rootxprv_from_seed(seed, version)
        xprv = derive(rootprv, path)
        xpub = xpub_from_xprv(xprv)
        exp = "Zpub72a8bqjcjNJnMBLrV2EY7XLQbfji28irEZneqYK6w8Zf16sfhr7zDbLsVQficP9j9uzbF6VW1y3ypmeFKf6Dxaw82WvK8WFjcsLyEvMNZjF"
        self.assertEqual(xpub.decode(), exp)
Example #23
0
    def test_testnet_versions(self):

        # data cross-checked with Electrum and
        # https://jlopp.github.io/xpub-converter/

        # 128 bits
        raw_entr = bytes.fromhex("6" * 32)
        # 12 words
        mnemonic = bip39.mnemonic_from_entropy(raw_entr, "en")
        seed = bip39.seed_from_mnemonic(mnemonic, "")

        # p2pkh BIP44
        # m / 44h / coin_typeh / accounth / change / address_index
        path = "m/44h/1h/0h"
        version = NETWORKS["testnet"]["bip32_prv"]
        rootprv = rootxprv_from_seed(seed, version)
        xprv = derive(rootprv, path)
        xpub = xpub_from_xprv(xprv)
        exp = "tpubDChqWo2Xi2wNsxyJBE8ipcTJHLKWcqeeNUKBVTpUCNPZkHzHTm3qKAeHqgCou1t8PAY5ZnJ9QDa6zXSZxmjDnhiBpgZ7f6Yv88wEm5HXVbm"
        self.assertEqual(xpub.decode(), exp)
        # first addresses
        xpub_ext = derive(xpub, "./0/0")  # external
        address = p2pkh(xpub_ext)
        exp_address = b"moutHSzeFWViMNEcvBxKzNCMj2kca8MvE1"
        self.assertEqual(address, exp_address)
        xpub_int = derive(xpub, "./1/0")  # internal
        address = p2pkh(xpub_int)
        exp_address = b"myWcXdNais9ExumnGKnNoJwoihQKfNPG9i"
        self.assertEqual(address, exp_address)

        # legacy segwit (p2wsh-p2sh)
        # m / 49h / coin_typeh / accounth / change / address_index
        path = "m/49h/1h/0h"
        version = NETWORKS["testnet"]["slip32_p2wsh_p2sh_prv"]
        rootprv = rootxprv_from_seed(seed, version)
        xprv = derive(rootprv, path)
        xpub = xpub_from_xprv(xprv)
        exp = "upub5Dj8j7YrwodV68mt58QmNpSzjqjso2WMXEpLGLSvskKccGuXhCh3dTedkzVLAePA617UyXAg2vdswJXTYjU4qjMJaHU79GJVVJCAiy9ezZ2"
        self.assertEqual(xpub.decode(), exp)
        # first addresses
        xpub_ext = derive(xpub, "./0/0")  # external
        address = p2wpkh_p2sh(xpub_ext)
        exp_address = b"2Mw8tQ6uT6mHhybarVhjgomUhHQJTeV9A2c"
        self.assertEqual(address, exp_address)
        xpub_int = derive(xpub, "./1/0")  # internal
        address = p2wpkh_p2sh(xpub_int)
        exp_address = b"2N872CRJ3E1CzWjfixXr3aeC3hkF5Cz4kWb"
        self.assertEqual(address, exp_address)

        # legacy segwit (p2wsh-p2sh)
        # m / 49h / coin_typeh / accounth / change / address_index
        path = "m/49h/1h/0h"
        version = NETWORKS["testnet"]["slip32_p2wpkh_p2sh_prv"]
        rootprv = rootxprv_from_seed(seed, version)
        xprv = derive(rootprv, path)
        xpub = xpub_from_xprv(xprv)
        exp = "Upub5QdDrMHJWmBrWhwG1nskCtnoTdn91PBwqWU1BbiUFXA2ETUSTc5KiaWZZhSoj5c4KUBTr7Anv92P4U9Dqxd1zDTyQkaWYfmVP2U3Js1W5cG"
        self.assertEqual(xpub.decode(), exp)

        # native segwit (p2wpkh)
        # m / 84h / coin_typeh / accounth / change / address_index
        path = "m/84h/1h/0h"
        version = NETWORKS["testnet"]["slip32_p2wpkh_prv"]
        rootprv = rootxprv_from_seed(seed, version)
        xprv = derive(rootprv, path)
        xpub = xpub_from_xprv(xprv)
        exp = "vpub5ZhJmduYY7M5J2qCJgSW7hunX6zJrr5WuNg2kKt321HseZEYxqJc6Zso47aNXQw3Wf3sA8kppbfsxnLheUNXcL3xhzeBHLNp8fTVBN6DnJF"
        self.assertEqual(xpub.decode(), exp)
        # first addresses
        xpub_ext = derive(xpub, "./0/0")  # external
        # explicit network is required to discriminate from testnet
        address = p2wpkh(xpub_ext, "regtest")
        exp_address = b"bcrt1qv8lcnmj09rpdqwgl025h2deygur64z4hqf7me5"
        self.assertEqual(address, exp_address)
        xpub_int = derive(xpub, "./1/0")  # internal
        # explicit network is required to discriminate from testnet
        address = p2wpkh(xpub_int, "regtest")
        exp_address = b"bcrt1qqhxvky4y6qkwpvdzqjkdafmj20vs5trmt6y8w5"
        self.assertEqual(address, exp_address)

        # native segwit (p2wsh)
        # m / 84h / coin_typeh / accounth / change / address_index
        path = "m/84h/1h/0h"
        version = NETWORKS["testnet"]["slip32_p2wsh_prv"]
        rootprv = rootxprv_from_seed(seed, version)
        xprv = derive(rootprv, path)
        xpub = xpub_from_xprv(xprv)
        exp = "Vpub5kbPtsdz74uSibzaFLuUwnFbEu2a5Cm7DeKhfb9aPn8HGjoTjEgtBgjirpXr5r9wk87r2ikwhp4P5wxTwhXUkpAdYTkagjqp2PjMmGPBESU"
        self.assertEqual(xpub.decode(), exp)
Example #24
0
    def test_address_from_wif(self):
        # uncompressed mainnet
        wif1 = "5J1geo9kcAUSM6GJJmhYRX1eZEjvos9nFyWwPstVziTVueRJYvW"
        pubkey, network = pubkey_info_from_prvkey(wif1)
        b58 = p2pkh(pubkey)
        self.assertEqual(b58, b'1LPM8SZ4RQDMZymUmVSiSSvrDfj1UZY9ig')
        self.assertRaises(ValueError, p2wpkh, pubkey)
        self.assertRaises(ValueError, p2wpkh_p2sh, pubkey)

        # compressed mainnet
        wif2 = "Kx621phdUCp6sgEXPSHwhDTrmHeUVrMkm6T95ycJyjyxbDXkr162"
        pubkey, network = pubkey_info_from_prvkey(wif2)
        b58 = p2pkh(pubkey)
        self.assertEqual(b58, b'1HJC7kFvXHepkSzdc8RX6khQKkAyntdfkB')
        b32 = p2wpkh(pubkey)
        self.assertEqual(h160_from_b58address(
            b58)[1:], witness_from_b32address(b32)[1:])
        h160 = h160_from_b58address(b58)[1]
        b = p2wpkh_p2sh(pubkey)
        self.assertEqual(hash160(b'\x00\x14' + h160),
                         h160_from_b58address(b)[1])

        self.assertEqual(prvkey_info_from_prvkey(
            wif1)[0], prvkey_info_from_prvkey(wif2)[0])

        # uncompressed testnet
        wif1 = "91gGn1HgSap6CbU12F6z3pJri26xzp7Ay1VW6NHCoEayNXwRpu2"
        pubkey, network = pubkey_info_from_prvkey(wif1)
        b58 = p2pkh(pubkey, network, None)
        self.assertEqual(b58, b'mvgbzkCSgKbYgaeG38auUzR7otscEGi8U7')
        self.assertRaises(ValueError, p2wpkh, pubkey)
        self.assertRaises(ValueError, p2wpkh_p2sh, pubkey)

        # compressed testnet
        wif2 = "cMzLdeGd5vEqxB8B6VFQoRopQ3sLAAvEzDAoQgvX54xwofSWj1fx"
        pubkey, network = pubkey_info_from_prvkey(wif2)
        b58 = p2pkh(pubkey, network, None)
        self.assertEqual(b58, b'n1KSZGmQgB8iSZqv6UVhGkCGUbEdw8Lm3Q')
        b32 = p2wpkh(pubkey, network)
        self.assertEqual(h160_from_b58address(
            b58)[1:], witness_from_b32address(b32)[1:])
        h160 = h160_from_b58address(b58)[1]
        b = p2wpkh_p2sh(pubkey, network)
        self.assertEqual(hash160(b'\x00\x14' + h160),
                         h160_from_b58address(b)[1])

        self.assertEqual(prvkey_info_from_prvkey(
            wif1)[0], prvkey_info_from_prvkey(wif2)[0])

        # uncompressed mainnet, trailing/leading spaces in string
        wif1 = "  5J1geo9kcAUSM6GJJmhYRX1eZEjvos9nFyWwPstVziTVueRJYvW"
        pubkey, network = pubkey_info_from_prvkey(wif1)
        b58 = p2pkh(pubkey)
        self.assertEqual(b58, b'1LPM8SZ4RQDMZymUmVSiSSvrDfj1UZY9ig')
        self.assertRaises(ValueError, p2wpkh, pubkey)
        self.assertRaises(ValueError, p2wpkh_p2sh, pubkey)

        # compressed mainnet, trailing/leading spaces in string
        wif2 = "Kx621phdUCp6sgEXPSHwhDTrmHeUVrMkm6T95ycJyjyxbDXkr162  "
        pubkey, network = pubkey_info_from_prvkey(wif2)
        b58 = p2pkh(pubkey)
        self.assertEqual(b58, b'1HJC7kFvXHepkSzdc8RX6khQKkAyntdfkB')
        b32 = p2wpkh(pubkey)
        self.assertEqual(h160_from_b58address(
            b58)[1:], witness_from_b32address(b32)[1:])
        h160 = h160_from_b58address(b58)[1]
        b = p2wpkh_p2sh(pubkey)
        self.assertEqual(hash160(b'\x00\x14' + h160),
                         h160_from_b58address(b)[1])
Example #25
0
    def test_exceptions(self):

        msg = 'test'
        wif = 'KwELaABegYxcKApCb3kJR9ymecfZZskL9BzVUkQhsqFiUKftb4tu'
        pubkey, _ = pubkey_info_from_prvkey(wif)
        address = base58address.p2pkh(pubkey)
        exp_sig = b'IHdKsFF1bUrapA8GMoQUbgI+Ad0ZXyX1c/yAZHmJn5hSNBi7J+TrI1615FG3g9JEOPGVvcfDWIFWrg2exLNtoVc='
        self.assertTrue(bms.verify(msg, address, exp_sig))

        # Invalid recovery flag: 26
        _, r, s = bms.deserialize(exp_sig)
        self.assertRaises(ValueError, bms.serialize, 26, r, s)
        #bms.serialize(26, r, s)

        # short exp_sig
        exp_sig = b'IHdKsFF1bUrapA8GMoQUbgI+Ad0ZXyX1c/yAZHmJn5hNBi7J+TrI1615FG3g9JEOPGVvcfDWIFWrg2exLoVc='
        self.assertRaises(ValueError, bms._verify, msg, address, exp_sig)
        self.assertFalse(bms.verify(msg, address, exp_sig))

        # Invalid recovery flag: 26
        exp_sig = b'GpNLHqEKSzwXV+KwwBfQthQ848mn5qSkmGDXpqshDuPYJELOnSuRYGQQgBR4PpI+w2tJdD4v+hxElvAaUSqv2eU='
        self.assertRaises(ValueError, bms._verify, msg, address, exp_sig)
        self.assertFalse(bms.verify(msg, address, exp_sig))
        #bms._verify(msg, address, exp_sig)

        # Invalid recovery flag: 66
        exp_sig = b'QpNLHqEKSzwXV+KwwBfQthQ848mn5qSkmGDXpqshDuPYJELOnSuRYGQQgBR4PpI+w2tJdD4v+hxElvAaUSqv2eU='
        self.assertRaises(ValueError, bms._verify, msg, address, exp_sig)
        self.assertFalse(bms.verify(msg, address, exp_sig))
        #bms._verify(msg, address, exp_sig)

        # Pubkey mismatch: compressed wif, uncompressed address
        wif = 'Ky1XfDK2v6wHPazA6ECaD8UctEoShXdchgABjpU9GWGZDxVRDBMJ'
        address = '19f7adDYqhHSJm2v7igFWZAqxXHj1vUa3T'
        self.assertRaises(ValueError, bms.sign, msg, wif, address)
        #bms.sign(msg, wif, address)

        # Pubkey mismatch: uncompressed wif, compressed address
        wif = '5JDopdKaxz5bXVYXcAnfno6oeSL8dpipxtU1AhfKe3Z58X48srn'
        address = '1DAag8qiPLHh6hMFVu9qJQm9ro1HtwuyK5'
        self.assertRaises(ValueError, bms.sign, msg, wif, address)
        #bms.sign(msg, wif, address)

        msg = 'test'
        wif = 'L4xAvhKR35zFcamyHME2ZHfhw5DEyeJvEMovQHQ7DttPTM8NLWCK'
        pubkey, _ = pubkey_info_from_prvkey(wif)
        p2pkh = base58address.p2pkh(pubkey)
        p2wpkh = bech32address.p2wpkh(pubkey)
        p2wpkh_p2sh = base58address.p2wpkh_p2sh(pubkey)

        wif = 'Ky1XfDK2v6wHPazA6ECaD8UctEoShXdchgABjpU9GWGZDxVRDBMJ'
        # Mismatch between p2pkh address and key pair
        self.assertRaises(ValueError, bms.sign, msg, wif, p2pkh)
        # bms.sign(msg, wif, p2pkh)

        # Mismatch between p2wpkh address and key pair
        self.assertRaises(ValueError, bms.sign, msg, wif, p2wpkh)
        # bms.sign(msg, wif, p2wpkh)

        # Mismatch between p2wpkh_p2sh address and key pair
        self.assertRaises(ValueError, bms.sign, msg, wif, p2wpkh_p2sh)
        # bms.sign(msg, wif, p2wpkh_p2sh)

        # Invalid recovery flag (39) for base58 address
        exp_sig = b'IHdKsFF1bUrapA8GMoQUbgI+Ad0ZXyX1c/yAZHmJn5hSNBi7J+TrI1615FG3g9JEOPGVvcfDWIFWrg2exLNtoVc='
        _, r, s = bms.deserialize(exp_sig)
        sig = bms.serialize(39, r, s)
        self.assertRaises(ValueError, bms._verify, msg, p2pkh, sig)
        #bms._verify(msg, p2pkh, sig)

        # Invalid recovery flag (35) for bech32 address
        exp_sig = b'IBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IOI0dpo3uGAEpCz9eepXLrA5kF35MXuU='
        _, r, s = bms.deserialize(exp_sig)
        sig = bms.serialize(35, r, s)
        self.assertRaises(ValueError, bms._verify, msg, p2wpkh, sig)
Example #26
0
def test_one_prvkey_multiple_addresses() -> None:

    msg = "Paolo is afraid of ephemeral random numbers"

    # Compressed WIF
    wif = b"Kx45GeUBSMPReYQwgXiKhG9FzNXrnCeutJp4yjTd5kKxCitadm3C"
    addr_p2pkh_compressed = p2pkh(wif)
    addr_p2wpkh_p2sh = p2wpkh_p2sh(wif)
    addr_p2wpkh = p2wpkh(wif)

    # sign with no address
    sig1 = bms.sign(msg, wif)
    # True for Bitcoin Core
    bms.assert_as_valid(msg, addr_p2pkh_compressed, sig1)
    assert bms.verify(msg, addr_p2pkh_compressed, sig1)
    # True for Electrum p2wpkh_p2sh
    bms.assert_as_valid(msg, addr_p2wpkh_p2sh, sig1)
    assert bms.verify(msg, addr_p2wpkh_p2sh, sig1)
    # True for Electrum p2wpkh
    bms.assert_as_valid(msg, addr_p2wpkh, sig1)
    assert bms.verify(msg, addr_p2wpkh, sig1)

    # sign with no p2pkh address
    sig1 = bms.sign(msg, wif, addr_p2pkh_compressed)
    # True for Bitcoin Core
    bms.assert_as_valid(msg, addr_p2pkh_compressed, sig1)
    assert bms.verify(msg, addr_p2pkh_compressed, sig1)
    # True for Electrum p2wpkh_p2sh
    bms.assert_as_valid(msg, addr_p2wpkh_p2sh, sig1)
    assert bms.verify(msg, addr_p2wpkh_p2sh, sig1)
    # True for Electrum p2wpkh
    bms.assert_as_valid(msg, addr_p2wpkh, sig1)
    assert bms.verify(msg, addr_p2wpkh, sig1)

    err_msg = "invalid recovery flag: "

    # sign with p2wpkh_p2sh address (BIP137)
    sig2 = bms.sign(msg, wif, addr_p2wpkh_p2sh)
    # False for Bitcoin Core
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, addr_p2pkh_compressed, sig2)
    assert not bms.verify(msg, addr_p2pkh_compressed, sig2)
    # True for BIP137 p2wpkh_p2sh
    bms.assert_as_valid(msg, addr_p2wpkh_p2sh, sig2)
    assert bms.verify(msg, addr_p2wpkh_p2sh, sig2)
    # False for BIP137 p2wpkh
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, addr_p2wpkh, sig2)
    assert not bms.verify(msg, addr_p2wpkh, sig2)

    # sign with p2wpkh address (BIP137)
    sig3 = bms.sign(msg, wif, addr_p2wpkh)
    # False for Bitcoin Core
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, addr_p2pkh_compressed, sig3)
    assert not bms.verify(msg, addr_p2pkh_compressed, sig3)
    # False for BIP137 p2wpkh_p2sh
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, addr_p2wpkh_p2sh, sig3)
    assert not bms.verify(msg, addr_p2wpkh_p2sh, sig3)
    # True for BIP137 p2wpkh
    bms.assert_as_valid(msg, addr_p2wpkh, sig3)
    assert bms.verify(msg, addr_p2wpkh, sig3)

    # uncompressed WIF / p2pkh address
    q, network, _ = prvkeyinfo_from_prvkey(wif)
    wif2 = wif_from_prvkey(q, network, False)
    addr_p2pkh_uncompressed = p2pkh(wif2)

    # sign with uncompressed p2pkh
    sig4 = bms.sign(msg, wif2, addr_p2pkh_uncompressed)
    # False for Bitcoin Core compressed p2pkh
    with pytest.raises(BTClibValueError, match="wrong p2pkh address: "):
        bms.assert_as_valid(msg, addr_p2pkh_compressed, sig4)
    assert not bms.verify(msg, addr_p2pkh_compressed, sig4)
    # False for BIP137 p2wpkh_p2sh
    # FIXME: puzzling error message
    # it should have been "wrong p2wpkh-p2sh address: "
    with pytest.raises(BTClibValueError, match="wrong p2pkh address: "):
        bms.assert_as_valid(msg, addr_p2wpkh_p2sh, sig4)
    assert not bms.verify(msg, addr_p2wpkh_p2sh, sig4)
    # False for BIP137 p2wpkh
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, addr_p2wpkh, sig4)
    assert not bms.verify(msg, addr_p2wpkh, sig4)
    # True for Bitcoin Core uncompressed p2pkh
    bms.assert_as_valid(msg, addr_p2pkh_uncompressed, sig4)
    assert bms.verify(msg, addr_p2pkh_uncompressed, sig4)

    # unrelated different wif
    wif3 = b"KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617"
    addr_p2pkh_compressed = p2pkh(wif3)
    addr_p2wpkh_p2sh = p2wpkh_p2sh(wif3)
    addr_p2wpkh = p2wpkh(wif3)

    # False for Bitcoin Core compressed p2pkh
    with pytest.raises(BTClibValueError, match="wrong p2pkh address: "):
        bms.assert_as_valid(msg, addr_p2pkh_compressed, sig1)
    assert not bms.verify(msg, addr_p2pkh_compressed, sig1)
    # False for BIP137 p2wpkh_p2sh
    with pytest.raises(BTClibValueError, match="wrong p2wpkh-p2sh address: "):
        bms.assert_as_valid(msg, addr_p2wpkh_p2sh, sig1)
    assert not bms.verify(msg, addr_p2wpkh_p2sh, sig1)
    # False for BIP137 p2wpkh
    with pytest.raises(BTClibValueError, match="wrong p2wpkh address: "):
        bms.assert_as_valid(msg, addr_p2wpkh, sig1)
    assert not bms.verify(msg, addr_p2wpkh, sig1)

    # FIXME: puzzling error message
    err_msg = "not a private or compressed public key for mainnet: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.sign(msg, wif2, addr_p2pkh_compressed)

    err_msg = "mismatch between private key and address"
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.sign(msg, wif, addr_p2pkh_uncompressed)
Example #27
0
from btclib.base58address import p2pkh, p2wpkh_p2sh
from btclib.base58wif import wif_from_prvkey
from btclib.bech32address import p2wpkh
from btclib.bms import encode, sign, verify
from btclib.to_prvkey import prvkeyinfo_from_prvkey
from btclib.to_pubkey import pubkeyinfo_from_prvkey

msg = "Paolo is afraid of ephemeral random numbers"
print("\n0. Message:", msg)

wif = b"Kx45GeUBSMPReYQwgXiKhG9FzNXrnCeutJp4yjTd5kKxCitadm3C"
print("1. Compressed WIF:", wif.decode())
pubkey, network = pubkeyinfo_from_prvkey(wif)

print("2. Addresses")
address1 = p2pkh(pubkey)
print("      p2pkh:", address1.decode())
address2 = p2wpkh_p2sh(pubkey)
print("p2wpkh_p2sh:", address2.decode())
address3 = p2wpkh(pubkey)
print("     p2wpkh:", address3.decode())

print("\n3. Sign message with no address (or with compressed p2pkh address):")
sig1 = sign(msg, wif)
print(f"rf1: {sig1[0]}")
print(f" r1: {hex(sig1[1]).upper()}")
print(f" s1: {hex(sig1[2]).upper()}")

bsmsig1 = encode(*sig1)
print("4. Serialized signature:")
print(bsmsig1.decode())
Example #28
0
def test_exceptions() -> None:

    msg = "test"
    wif = "KwELaABegYxcKApCb3kJR9ymecfZZskL9BzVUkQhsqFiUKftb4tu"
    address = base58address.p2pkh(wif)
    exp_sig = "IHdKsFF1bUrapA8GMoQUbgI+Ad0ZXyX1c/yAZHmJn5hSNBi7J+TrI1615FG3g9JEOPGVvcfDWIFWrg2exLNtoVc="
    assert bms.verify(msg, address, exp_sig)

    _, r, s = bms.decode(exp_sig)
    err_msg = "invalid recovery flag: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.encode(26, r, s)

    exp_sig = "IHdKsFF1bUrapA8GMoQUbgI+Ad0ZXyX1c/yAZHmJn5hNBi7J+TrI1615FG3g9JEOPGVvcfDWIFWrg2exLoVc="
    err_msg = "wrong signature length: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, address, exp_sig)
    assert not bms.verify(msg, address, exp_sig)

    exp_sig = "GpNLHqEKSzwXV+KwwBfQthQ848mn5qSkmGDXpqshDuPYJELOnSuRYGQQgBR4PpI+w2tJdD4v+hxElvAaUSqv2eU="
    err_msg = "invalid recovery flag: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, address, exp_sig)
    assert not bms.verify(msg, address, exp_sig)
    exp_sig = "QpNLHqEKSzwXV+KwwBfQthQ848mn5qSkmGDXpqshDuPYJELOnSuRYGQQgBR4PpI+w2tJdD4v+hxElvAaUSqv2eU="
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, address, exp_sig)
    assert not bms.verify(msg, address, exp_sig)

    # compressed wif, uncompressed address
    wif = "Ky1XfDK2v6wHPazA6ECaD8UctEoShXdchgABjpU9GWGZDxVRDBMJ"
    address = b"19f7adDYqhHSJm2v7igFWZAqxXHj1vUa3T"
    err_msg = "mismatch between private key and address"
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.sign(msg, wif, address)

    # uncompressed wif, compressed address
    wif = "5JDopdKaxz5bXVYXcAnfno6oeSL8dpipxtU1AhfKe3Z58X48srn"
    address = b"1DAag8qiPLHh6hMFVu9qJQm9ro1HtwuyK5"
    err_msg = "not a private or compressed public key for mainnet: "
    # FIXME puzzling error message
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.sign(msg, wif, address)

    msg = "test"
    wif = "L4xAvhKR35zFcamyHME2ZHfhw5DEyeJvEMovQHQ7DttPTM8NLWCK"
    b58_p2pkh = base58address.p2pkh(wif)
    b58_p2wpkh = bech32address.p2wpkh(wif)
    b58_p2wpkh_p2sh = base58address.p2wpkh_p2sh(wif)

    wif = "Ky1XfDK2v6wHPazA6ECaD8UctEoShXdchgABjpU9GWGZDxVRDBMJ"
    err_msg = "mismatch between private key and address"
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.sign(msg, wif, b58_p2pkh)
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.sign(msg, wif, b58_p2wpkh)
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.sign(msg, wif, b58_p2wpkh_p2sh)

    # Invalid recovery flag (39) for base58 address
    exp_sig = "IHdKsFF1bUrapA8GMoQUbgI+Ad0ZXyX1c/yAZHmJn5hSNBi7J+TrI1615FG3g9JEOPGVvcfDWIFWrg2exLNtoVc="
    _, r, s = bms.decode(exp_sig)
    sig = bms.encode(39, r, s)
    err_msg = "invalid recovery flag: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, b58_p2pkh, sig)

    # Invalid recovery flag (35) for bech32 address
    exp_sig = "IBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IOI0dpo3uGAEpCz9eepXLrA5kF35MXuU="
    _, r, s = bms.decode(exp_sig)
    sig = bms.encode(35, r, s)
    err_msg = "invalid recovery flag: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bms.assert_as_valid(msg, b58_p2wpkh, sig)
Example #29
0
def test_ledger() -> None:
    """Hybrid ECDSA Bitcoin message signature generated by Ledger"""

    mnemonic = (
        "barely sun snack this snack relief pipe attack disease boss enlist lawsuit"
    )

    # non-standard leading 31 in DER serialization
    derivation_path = "m/1"
    msg = b"\xfb\xa3\x1f\x8cd\x85\xe29#K\xb3{\xfd\xa7<?\x95oL\xee\x19\xb2'oh\xa7]\xd9A\xfeU\xd8"
    dersig_hex_str = "3144022012ec0c174936c2a46dc657252340b2e6e6dd8c31dd059b6f9f33a90c21af2fba022030e6305b3ccf88009d419bf7651afcfcc0a30898b93ae9de9aa6ac03cf8ec56b"

    # pubkey derivation
    rprv = bip32.mxprv_from_bip39_mnemonic(mnemonic)
    xprv = bip32.derive(rprv, derivation_path)

    # the actual message being signed
    magic_msg = bms._magic_message(msg)

    # save key_id and patch dersig
    dersig = bytes.fromhex(dersig_hex_str)
    key_id = dersig[0]
    dersig = b"\x30" + dersig[1:]

    r, s = dsa.deserialize(dersig)

    # ECDSA signature verification of the patched dersig
    dsa.assert_as_valid(magic_msg, xprv, dersig, ec, hf)
    assert dsa.verify(magic_msg, xprv, dersig)

    # compressed address
    addr = base58address.p2pkh(xprv)

    # equivalent Bitcoin Message Signature (non-serialized)
    rec_flag = 27 + 4 + (key_id & 0x01)
    btcmsgsig = (rec_flag, r, s)

    # Bitcoin Message Signature verification
    bms.assert_as_valid(msg, addr, btcmsgsig)
    assert bms.verify(msg, addr, btcmsgsig)
    assert not bms.verify(magic_msg, addr, btcmsgsig)

    bms.sign(msg, xprv)

    # standard leading 30 in DER serialization
    derivation_path = "m/0/0"
    msg_str = "hello world"
    dersig_hex_str = "3045022100967dac3262b4686e89638c8219c5761017f05cd87a855edf034f4a3ec6b59d3d0220108a4ef9682b71a45979d8c75c393382d9ccb8eb561d73b8c5fc0b87a47e7d27"

    # pubkey derivation
    rprv = bip32.mxprv_from_bip39_mnemonic(mnemonic)
    xprv = bip32.derive(rprv, derivation_path)

    # the actual message being signed
    magic_msg = bms._magic_message(msg_str)

    # save key_id and patch dersig
    dersig = bytes.fromhex(dersig_hex_str)
    key_id = dersig[0]
    dersig = b"\x30" + dersig[1:]

    r, s = dsa.deserialize(dersig)

    # ECDSA signature verification of the patched dersig
    dsa.assert_as_valid(magic_msg, xprv, dersig, ec, hf)
    assert dsa.verify(magic_msg, xprv, dersig)

    # compressed address
    addr = base58address.p2pkh(xprv)

    # equivalent Bitcoin Message Signature (non-serialized)
    rec_flag = 27 + 4 + (key_id & 0x01)
    btcmsgsig = (rec_flag, r, s)

    # Bitcoin Message Signature verification
    bms.assert_as_valid(msg_str, addr, btcmsgsig)
    assert bms.verify(msg_str, addr, btcmsgsig)
    assert not bms.verify(magic_msg, addr, btcmsgsig)