Exemplo n.º 1
0
    def sign_with_descriptor(self, wname, d1, d2, all_sighashes=False):
        # to derive addresses
        desc1 = Descriptor.from_string(d1)
        desc2 = Descriptor.from_string(d2)
        # recv addr 2
        addr1 = desc1.derive(2).address(NETWORKS['regtest'])
        # change addr 3
        addr2 = desc2.derive(3).address(NETWORKS['regtest'])
        res = sim.query(f"bitcoin:{addr1}?index=2", [True])
        # check it's found
        self.assertFalse(b"Can't find wallet" in res)

        # to add checksums
        d1 = rpc.getdescriptorinfo(d1)["descriptor"]
        d2 = rpc.getdescriptorinfo(d2)["descriptor"]
        rpc.createwallet(wname, True, True)
        w = rpc.wallet(wname)
        res = w.importmulti([{
                "desc": d1,
                "internal": False,
                "timestamp": "now",
                "watchonly": True,
                "range": 10,
            },{
                "desc": d2,
                "internal": True,
                "timestamp": "now",
                "watchonly": True,
                "range": 10,
            }],{"rescan": False})
        self.assertTrue(all([k["success"] for k in res]))
        wdefault.sendtoaddress(addr1, 0.1)
        rpc.mine()
        psbt = w.walletcreatefundedpsbt([], [{wdefault.getnewaddress(): 0.002}], 0, {"includeWatching": True, "changeAddress": addr2}, True)
        unsigned = psbt["psbt"]
        sighashes = [None]
        if all_sighashes:
            sh = [SIGHASH.ALL, SIGHASH.NONE, SIGHASH.SINGLE]
            sighashes += sh + [s | SIGHASH.ANYONECANPAY for s in sh]
        tx = PSBT.from_base64(unsigned)
        for sh in sighashes:
            for inp in tx.inputs:
                inp.sighash_type = sh
            unsigned = tx.to_base64().encode()
            # confirm signing
            if sh in [SIGHASH.ALL, None]:
                signed = sim.query(b"sign "+unsigned, [True])
            else:
                # confirm warning
                signed = sim.query(b"sign "+unsigned, [True, True])
            # signed tx
            self.assertTrue(signed.startswith(b"cHNi"))
            combined = rpc.combinepsbt([unsigned.decode(), signed.decode()])
            final = rpc.finalizepsbt(combined)
            self.assertTrue(final["complete"])
            # broadcast
            res = rpc.testmempoolaccept([final["hex"]])
            self.assertTrue(res[0]["allowed"])
Exemplo n.º 2
0
    def test_miniscript_compat(self):
        """Test we can parse Miniscript Descriptors created by other implementations"""
        generalistic_descs = [
                "wsh(thresh(1,pk(xpub6BaZSKgpaVvibu2k78QsqeDWXp92xLHZxiu1WoqLB9hKhsBf3miBUDX7PJLgSPvkj66ThVHTqdnbXpeu8crXFmDUd4HeM4s4miQS2xsv3Qb/*)))#cwycq5x",
                "wsh(multi(2,xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRmF8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU/*,xpub6AaffFGfH6WXfm6pwWzmUMuECQnoLeB3agMKaLyEBZ5ZVfwtnS5VJKqXBt8o5ooCWVy2H87GsZshp7DeKE25eWLyd1Ccuh2ZubQUkgpiVux/*))#n3cj9mhy",
                "wsh(andor(thresh(1,pk(xpub6BaZSKgpaVvibu2k78QsqeDWXp92xLHZxiu1WoqLB9hKhsBf3miBUDX7PJLgSPvkj66ThVHTqdnbXpeu8crXFmDUd4HeM4s4miQS2xsv3Qb/*)),and_v(v:multi(2,03b506a1dbe57b4bf48c95e0c7d417b87dd3b4349d290d2e7e9ba72c912652d80a,0295e7f5d12a2061f1fd2286cefec592dff656a19f55f4f01305d6aa56630880ce),older(6)),thresh(2,pkh(xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRmF8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU/*),a:pkh(xpub6AaffFGfH6WXfm6pwWzmUMuECQnoLeB3agMKaLyEBZ5ZVfwtnS5VJKqXBt8o5ooCWVy2H87GsZshp7DeKE25eWLyd1Ccuh2ZubQUkgpiVux/*))))#76jsyzdg",
                ]

        for desc in generalistic_descs:
            Descriptor.from_string(desc)
Exemplo n.º 3
0
def parse_key(masterpub: str):
    """Parses masterpub or descriptor and returns a tuple: (Descriptor, network)
    To create addresses use descriptor.derive(num).address(network=network)
    """
    network = None
    # probably a single key
    if "(" not in masterpub:
        k = Key.from_string(masterpub)
        if not k.is_extended:
            raise ValueError("The key is not a master public key")
        if k.is_private:
            raise ValueError("Private keys are not allowed")
        # check depth
        if k.key.depth != 3:
            raise ValueError(
                "Non-standard depth. Only bip44, bip49 and bip84 are supported with bare xpubs. For custom derivation paths use descriptors."
            )
        # if allowed derivation is not provided use default /{0,1}/*
        if k.allowed_derivation is None:
            k.allowed_derivation = AllowedDerivation.default()
        # get version bytes
        version = k.key.version
        for network_name in NETWORKS:
            net = NETWORKS[network_name]
            # not found in this network
            if version in [net["xpub"], net["ypub"], net["zpub"]]:
                network = net
                if version == net["xpub"]:
                    desc = Descriptor.from_string("pkh(%s)" % str(k))
                elif version == net["ypub"]:
                    desc = Descriptor.from_string("sh(wpkh(%s))" % str(k))
                elif version == net["zpub"]:
                    desc = Descriptor.from_string("wpkh(%s)" % str(k))
                break
        # we didn't find correct version
        if network is None:
            raise ValueError("Unknown master public key version")
    else:
        desc = Descriptor.from_string(masterpub)
        if not desc.is_wildcard:
            raise ValueError("Descriptor should have wildcards")
        for k in desc.keys:
            if k.is_extended:
                net = detect_network(k)
                if net is None:
                    raise ValueError(f"Unknown version: {k}")
                if network is not None and network != net:
                    raise ValueError("Keys from different networks")
                network = net
    return desc, network
Exemplo n.º 4
0
 def fill_output(cls, out: OutputScope, desc: Descriptor) -> bool:
     """
     Fills derivations and all other information in PSBT output
     from derived descriptor
     """
     if desc.script_pubkey() != out.script_pubkey:
         return False
     out.redeem_script = desc.redeem_script()
     out.witness_script = desc.witness_script()
     out.bip32_derivations = {
         key.get_public_key(): DerivationPath(key.origin.fingerprint,
                                              key.origin.derivation)
         for key in desc.keys
     }
     return True
Exemplo n.º 5
0
    def sign_with_descriptor(self, d1, d2, root):
        rpc = daemon.rpc
        wname = random_wallet_name()
        # to derive addresses
        desc1 = Descriptor.from_string(d1)
        desc2 = Descriptor.from_string(d2)
        # recv addr 2
        addr1 = desc1.derive(2).address(net)
        # change addr 3
        addr2 = desc2.derive(3).address(net)

        # to add checksums
        d1 = add_checksum(str(d1))
        d2 = add_checksum(str(d2))
        rpc.createwallet(wname, True, True)
        w = daemon.wallet(wname)
        res = w.importmulti([{
            "desc": d1,
            "internal": False,
            "timestamp": "now",
            "watchonly": True,
            "range": 10,
        }, {
            "desc": d2,
            "internal": True,
            "timestamp": "now",
            "watchonly": True,
            "range": 10,
        }], {"rescan": False})
        self.assertTrue(all([k["success"] for k in res]))
        wdefault = daemon.wallet()
        wdefault.sendtoaddress(addr1, 0.1)
        daemon.mine()
        psbt = w.walletcreatefundedpsbt([], [{
            wdefault.getnewaddress(): 0.002
        }], 0, {
            "includeWatching": True,
            "changeAddress": addr2
        }, True)
        unsigned = psbt["psbt"]
        psbt = PSBT.from_string(unsigned)
        psbt.sign_with(root)
        final = rpc.finalizepsbt(str(psbt))
        self.assertTrue(final["complete"])
        # test accept
        res = rpc.testmempoolaccept([final["hex"]])
        self.assertTrue(res[0]["allowed"])
Exemplo n.º 6
0
    def test_legacy_sh(self):
        cosigner = "[12345678/41h/2h/0h]tpubDCUwbXdGiV5qFWMyaHBfdtDv1AZtUJmENrLMGEooEdyYka1Gk5FrBdoTp54EFWxopWi9H7udD4d8CxMNa2GUECRCodbBkd4eZfiQzbMXWU3"
        path = "49h/1h/0h/2h"
        fgp = sim.query("fingerprint").decode()
        xpub = sim.query(f"xpub m/{path}").decode()
        d1 = f"sh(sortedmulti(1,[{fgp}/{path}]{xpub}/0/*,{cosigner}/0/*))"
        d2 = f"sh(sortedmulti(1,[{fgp}/{path}]{xpub}/1/*,{cosigner}/1/*))"
        # combined with default derivation {0,1}/*
        d3 = f"sh(sortedmulti(1,[{fgp}/{path}]{xpub}/" + "{0,1}/*" + f",{cosigner}/"+"{0,1}/*))"
        res = sim.query("addwallet legsh&"+d3, [True])

        addr = Descriptor.from_string(d1).derive(5).address(NETWORKS['regtest'])
        # check it finds the wallet correctly
        sc = Descriptor.from_string(d1).derive(5).redeem_script().data.hex()
        res = sim.query(f"showaddr sh m/{path}/0/5 {sc}", [True])
        self.assertEqual(res.decode(), addr)

        wname = wallet_prefix+"_legsh"
        self.sign_with_descriptor(wname, d1, d2)
Exemplo n.º 7
0
 def test_invalid(self):
     with self.assertRaises(Exception):
         Descriptor.from_string("wsh(tr(%s/0/*))" % ROOT)
     with self.assertRaises(Exception):
         Descriptor.from_string("sh(tr(%s/0/*))" % ROOT)
     # x-only is only allowed in tr
     Descriptor.from_string(
         "tr(b4ca2da5380d9aeb5ca67e4f18c487ae9b668748517e12b788496f63765e2efa)"
     )
     with self.assertRaises(Exception):
         Descriptor.from_string(
             "wpkh(b4ca2da5380d9aeb5ca67e4f18c487ae9b668748517e12b788496f63765e2efa)"
         )
Exemplo n.º 8
0
 def test_descriptor(self):
     descstr = "tr(%s/0/*)" % ROOT.to_public()
     desc = Descriptor.from_string(descstr)
     self.assertTrue(desc.is_taproot)
     self.assertEqual(str(desc), descstr)
     for i, expected in enumerate(DERIVED_ADDRESSES):
         d = desc.derive(i)
         addr = d.address(NET)
         self.assertEqual(addr, expected)
         self.assertEqual(d.script_pubkey(),
                          address_to_scriptpubkey(expected))
Exemplo n.º 9
0
    def test_no_derivation(self):
        """Default wallet without derivation path"""
        path = "84h/1h/0h"
        xpub = sim.query(f"xpub m/{path}").decode()
        d1 = f"wpkh({xpub}/0/*)"
        d2 = f"wpkh({xpub}/1/*)"
        wname = wallet_prefix+"_wpkh_noder"

        addr = Descriptor.from_string(d1).derive(5).address(NETWORKS['regtest'])

        self.sign_with_descriptor(wname, d1, d2)
Exemplo n.º 10
0
    def test_with_private(self):
        cosigner = "[12345678/41h/2h/0h]tprv8ffduPBLPgcNBZcXgCPo91WbmhXdpLKq8gtq7C6KnXbcqMPrj2NhSWp8WCjFarhBDi2TnPf7AcndKJZeF6Eq3uXXEbsbQZpk94cmrtopNH4"
        cosigner_public = "[12345678/41h/2h/0h]tpubDCMg3oDaY4J352eKZr4PYRAiLj3ZyfWjhzVcPi8dCoQ1fqedMRCHd1RzgLHXN7fbqMFCquaby2ETYKoVmjGLU7rdadZ2MeprqeHrJWHLKYn"
        cosigner2 = "cUHFVwhcD4jJnVQkuDw5MdhZQVf2t6qSF6miA7UDnDSyiBNSZ4st"
        cosigner2_public = "0307208358ae8ccce81c81ea7a89683ee854e1002e05be8df39cb0ca5ed44eac29"
        path = "49h/1h/0h/2h"
        fgp = sim.query("fingerprint").decode()
        xpub = sim.query(f"xpub m/{path}").decode()
        d1 = f"wsh(sortedmulti(3,[{fgp}/{path}]{xpub}/0/*,{cosigner_public}/0/*,"+f"{cosigner2_public}))"
        d2 = f"wsh(sortedmulti(3,[{fgp}/{path}]{xpub}/1/*,{cosigner_public}/1/*,"+f"{cosigner2_public}))"
        # combined with default derivation {0,1}/*
        d3 = f"wsh(sortedmulti(3,[{fgp}/{path}]{xpub}/" + "{0,1}/*" + f",{cosigner}/"+"{0,1}/*,"+f"{cosigner2}))"
        res = sim.query("addwallet wshpriv&"+d3, [True])

        addr = Descriptor.from_string(d1).derive(5).address(NETWORKS['regtest'])
        # check it finds the wallet correctly
        sc = Descriptor.from_string(d1).derive(5).witness_script().data.hex()
        res = sim.query(f"showaddr wsh m/{path}/0/5 {sc}", [True])
        self.assertEqual(res.decode(), addr)

        wname = wallet_prefix+"_wshprv"
        self.sign_with_descriptor(wname, d1, d2)
Exemplo n.º 11
0
def main():
    for dstr in classic_descriptors:
        # replace spaces and new lines
        dstr = dstr.replace("\n", "").replace(" ", "")
        # parse
        desc = Descriptor.from_string(dstr)
        # print(desc.policy)
        derived = desc.derive(12)
        print("Derived descriptor:\n%s" % derived)
        addr = derived.address(NETWORKS['test'])
        print("Address: %s\n" % addr)

    for dstr in paired_descriptors:
        # replace spaces and new lines
        dstr = dstr.replace("\n", "").replace(" ", "")
        # parse
        desc = Descriptor.from_string(dstr)
        # print(desc.policy)
        # 12-th address, testnet, 1st branch (change)
        # branch_index is the index of the allowed set, so if the set is {33,45} then branch_index=1 will derive using 45 index
        derived = desc.derive(12, branch_index=1)
        print("Derived descriptor:\n%s" % derived)
        addr = derived.address(NETWORKS['test'])
        print("Address: %s\n" % addr)

    for dstr in miniscript_descriptors:
        # replace spaces and new lines
        dstr = dstr.replace("\n", "").replace(" ", "")
        # parse
        desc = Descriptor.from_string(dstr)
        # print(desc.policy)
        # 12-th address, testnet, 1st branch (change)
        # if branch_index is not provided, we use the first index in all branches
        derived = desc.derive(12)
        print("Derived descriptor:\n%s" % derived)
        addr = derived.address(NETWORKS['test'])
        print("Address: %s\n" % addr)
Exemplo n.º 12
0
    def test_wpkh(self):
        """Native segwit single-sig"""
        path = "84h/1h/0h"
        fgp = sim.query("fingerprint").decode()
        xpub = sim.query(f"xpub m/{path}").decode()
        d1 = f"wpkh([{fgp}/{path}]{xpub}/0/*)"
        d2 = f"wpkh([{fgp}/{path}]{xpub}/1/*)"
        wname = wallet_prefix+"_wpkh"

        addr = Descriptor.from_string(d1).derive(5).address(NETWORKS['regtest'])
        # check it finds the wallet correctly
        res = sim.query(f"showaddr wpkh m/{path}/0/5", [True])
        self.assertEqual(res.decode(), addr)

        self.sign_with_descriptor(wname, d1, d2)
Exemplo n.º 13
0
    def test_strange_derivation(self):
        path = "84h/1h/0h"
        fgp = sim.query("fingerprint").decode()
        xpub = sim.query(f"xpub m/{path}").decode()
        d1 = f"wpkh([{fgp}/{path}]{xpub}/44/8/*)"
        d2 = f"wpkh([{fgp}/{path}]{xpub}/55/8/*)"
        # combined with default derivation {0,1}/*
        d3 = f"wpkh([{fgp}/{path}]{xpub}"+ "/{44,55}/8/*)"
        res = sim.query("addwallet weird&"+d3, [True])

        addr = Descriptor.from_string(d1).derive(5).address(NETWORKS['regtest'])
        # check it finds the wallet correctly
        res = sim.query(f"showaddr wpkh m/{path}/44/8/5", [True])
        self.assertEqual(res.decode(), addr)

        wname = wallet_prefix+"_weird"
        self.sign_with_descriptor(wname, d1, d2)
Exemplo n.º 14
0
    def test_sh_wpkh(self):
        """Native segwit single-sig"""
        path = "49h/1h/0h"
        fgp = sim.query("fingerprint").decode()
        xpub = sim.query(f"xpub m/{path}").decode()
        d1 = f"sh(wpkh([{fgp}/{path}]{xpub}/0/*))"
        d2 = f"sh(wpkh([{fgp}/{path}]{xpub}/1/*))"
        # combined with default derivation {0,1}/*
        d3 = f"sh(wpkh([{fgp}/{path}]{xpub}"+ "/{0,1}/*))"
        wname = wallet_prefix+"_sh_wpkh"
        res = sim.query("addwallet shwpkh&"+d3, [True])

        addr = Descriptor.from_string(d1).derive(5).address(NETWORKS['regtest'])
        # check it finds the wallet correctly
        res = sim.query(f"showaddr sh-wpkh m/{path}/0/5", [True])
        self.assertEqual(res.decode(), addr)

        self.sign_with_descriptor(wname, d1, d2)
Exemplo n.º 15
0
    def test_sighashes_legacy(self):
        """Native segwit single-sig"""
        path = "44h/1h/0h"
        fgp = sim.query("fingerprint").decode()
        xpub = sim.query(f"xpub m/{path}").decode()
        # legacy sighashes
        d1 = f"pkh([{fgp}/{path}]{xpub}/0/*)"
        d2 = f"pkh([{fgp}/{path}]{xpub}/1/*)"
        d3 = f"pkh([{fgp}/{path}]{xpub}"+ "/{0,1}/*)"
        wname = wallet_prefix+"_pkhsighash"

        res = sim.query("addwallet pkhsighsh&"+d3, [True])
        addr = Descriptor.from_string(d1).derive(5).address(NETWORKS['regtest'])
        # check it finds the wallet correctly
        res = sim.query(f"showaddr pkh m/{path}/0/5", [True])
        self.assertEqual(res.decode(), addr)

        self.sign_with_descriptor(wname, d1, d2, all_sighashes=True)
Exemplo n.º 16
0
    def test_descriptors(self):
        keys = [
            "[abcdef12/84h/22h]xpub6F6wWxm8F64iBHNhyaoh3QKCuuMUY5pfPPr1H1WuZXUXeXtZ21qjFN5ykaqnLL1jtPEFB9d94CyZrcYWKVdSiJKQ6mLGEB5sfrGFBpg6wgA/{0,1}/*",
            "03e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b14130",
            "[12345678/44h/12]xpub6BwcvdstHTJtLpp1WxUiQCYERWSB66XY5JrCpw71GAJxcJ6s2AiUoEK4Nzt6UDaTmanUiSe6TY2RoFturKNLXeWBhwBF6WBNghr8cr7qnjk/{0,1}/*",
            "[12345a78/42h/15]03e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b14130",
        ]

        dd = [
            ("wsh(or_d("
             "c:pk_k(020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261),"
             "c:pk_k(0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352)"
             "))",
             "21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261ac7364210250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352ac68"
             ),

            # # pkh - 8e5d7457d33a978d1c3c1e440f92a195e00cc7d8
            # ("wsh(v:pk_h(03e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b14130))", None),
            ("sh(wsh(and_v(or_c(pk(%s),or_c(pk(%s),v:older(1000))),pk(%s))))" %
             tuple(keys[-3:]),
             "2103e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b14130ac642103b8fa5d5959fa4027ccbf0736a86ccde4242e3051ea363437b4ff0d52598d7cecac6402e803b26968682103e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b14130ac"
             ),
            ("sh(or_b(pk(%s),s:pk(%s)))" % tuple(keys[:2]),
             "2103801b3a4e3ca0d61d469445621561c47f6c1424d0fd353a44c2c3ebb84ae78f59ac7c2103e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b14130ac9b"
             ),
            ("wsh(or_d(pk(%s),pkh(%s)))" % tuple(keys[-2:]),
             "2103b8fa5d5959fa4027ccbf0736a86ccde4242e3051ea363437b4ff0d52598d7cecac736476a9148e5d7457d33a978d1c3c1e440f92a195e00cc7d888ac68"
             ),
            ("wsh(and_v(v:pk(%s),or_d(pk(%s),older(12960))))" %
             tuple(keys[:2]),
             "2103801b3a4e3ca0d61d469445621561c47f6c1424d0fd353a44c2c3ebb84ae78f59ad2103e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b14130ac736402a032b268"
             ),
            ("wsh(andor(pk(%s),older(1008),pk(%s)))" % tuple(keys[:2]),
             "2103801b3a4e3ca0d61d469445621561c47f6c1424d0fd353a44c2c3ebb84ae78f59ac642103e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b14130ac6702f003b268"
             ),
            ("wsh(t:or_c(pk(%s),and_v(v:pk(%s),or_c(pk(%s),v:hash160(e7d285b4817f83f724cd29394da75dfc84fe639e)))))"
             % tuple(keys[:3]),
             "2103801b3a4e3ca0d61d469445621561c47f6c1424d0fd353a44c2c3ebb84ae78f59ac642103e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b14130ad2103b8fa5d5959fa4027ccbf0736a86ccde4242e3051ea363437b4ff0d52598d7cecac6482012088a914e7d285b4817f83f724cd29394da75dfc84fe639e88686851"
             ),
            ("wsh(andor(pk(%s),or_i(and_v(v:pkh(%s),hash160(e7d285b4817f83f724cd29394da75dfc84fe639e)),older(1008)),pk(%s)))"
             % tuple(keys[:3]),
             "2103801b3a4e3ca0d61d469445621561c47f6c1424d0fd353a44c2c3ebb84ae78f59ac642103b8fa5d5959fa4027ccbf0736a86ccde4242e3051ea363437b4ff0d52598d7cecac676376a9148e5d7457d33a978d1c3c1e440f92a195e00cc7d888ad82012088a914e7d285b4817f83f724cd29394da75dfc84fe639e876702f003b26868"
             ),
            ("wsh(multi(2,%s,%s,%s))" % tuple(keys[:3]),
             "522103801b3a4e3ca0d61d469445621561c47f6c1424d0fd353a44c2c3ebb84ae78f592103e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b141302103b8fa5d5959fa4027ccbf0736a86ccde4242e3051ea363437b4ff0d52598d7cec53ae"
             ),
            ("wsh(thresh(3,pk(%s),s:pk(%s),s:pk(%s),sdv:older(12960)))" %
             tuple(keys[:3]),
             "2103801b3a4e3ca0d61d469445621561c47f6c1424d0fd353a44c2c3ebb84ae78f59ac7c2103e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b14130ac937c2103b8fa5d5959fa4027ccbf0736a86ccde4242e3051ea363437b4ff0d52598d7cecac937c766302a032b26968935387"
             ),
            ("wsh(multi(10,"
             "0373b665b6fe153c5872de1344339ee60588491257d2c34567aa026af237143a6c,"
             "02916ee61974fc4892afb2d3cad4c13472138b5521411de24a78910afb97b95f22,"
             "0244efc096ea3b7df99071b1cfa1630144e20d8ccd1540e726034a051aa1802d3b,"
             "02d9c51dc3f4088d5ce0b83f188fb14901b98c1c9e8cf771c49b7b441e56272b8a,"
             "03094990a34af21ef3ed766c8e0cb1e44f5e0d80412bbe00a2ade82a024ca91d23,"
             "02722a386ad0f6d7f1261808a3e70fab143303bd2264283486411c3183ea3ed1c3,"
             "036070b1f2995d8ffda8478ef55affd39795689a3982d54b12180397b1ad1f5f75,"
             "026515fa7603c10c44f6d316ae7592b5899d46d87ac1e574ec53de8b59f95efad6,"
             "038c8f919f70062c084376223fd8b4f0c08958e70499df496411dde83a1bb64b0d,"
             "02d0ea7084e344b56625277b074d15a15301b9d96b0b2dd9fc905e01fc3de408e1))",
             "5a210373b665b6fe153c5872de1344339ee60588491257d2c34567aa026af237143a6c2102916ee61974fc4892afb2d3cad4c13472138b5521411de24a78910afb97b95f22210244efc096ea3b7df99071b1cfa1630144e20d8ccd1540e726034a051aa1802d3b2102d9c51dc3f4088d5ce0b83f188fb14901b98c1c9e8cf771c49b7b441e56272b8a2103094990a34af21ef3ed766c8e0cb1e44f5e0d80412bbe00a2ade82a024ca91d232102722a386ad0f6d7f1261808a3e70fab143303bd2264283486411c3183ea3ed1c321036070b1f2995d8ffda8478ef55affd39795689a3982d54b12180397b1ad1f5f7521026515fa7603c10c44f6d316ae7592b5899d46d87ac1e574ec53de8b59f95efad621038c8f919f70062c084376223fd8b4f0c08958e70499df496411dde83a1bb64b0d2102d0ea7084e344b56625277b074d15a15301b9d96b0b2dd9fc905e01fc3de408e15aae"
             ),
            ("wsh(andor("
             "multi(4,"
             "036070b1f2995d8ffda8478ef55affd39795689a3982d54b12180397b1ad1f5f75,"
             "026515fa7603c10c44f6d316ae7592b5899d46d87ac1e574ec53de8b59f95efad6,"
             "038c8f919f70062c084376223fd8b4f0c08958e70499df496411dde83a1bb64b0d,"
             "02d0ea7084e344b56625277b074d15a15301b9d96b0b2dd9fc905e01fc3de408e1),"
             "and_v("
             "v:multi(6,"
             "03856d447f1b890cc6e0e0114cd5bac58662c37ce7f458c458b72bd396597edfc7,"
             "03e080e99896384aa8a07da837b2042a4c0d824eeaa8d51e6c9cff20682be75d4f,"
             "02c6d258e728005d4d00e55ac4b87786df507921b3ba3efec244a47f4a2e61b4b0,"
             "02edfc1d6088f9b6470ed4550d8bf2326ebebc0464a7f78581fa7283fc54edecf0,"
             "02f3630d1f51b2ebaaf1c7ebae9c24318279d4cff5ad16cb290b6d26edf96dca9c,"
             "0353ecc8e7b1cc90d405cd6fc9d9f24d44b6b5649abc2773f28a6ca4fa7a4cd629),"
             "older(144)),"
             "thresh(5,"
             "pkh(1ad3ca2d247b8e8888e41f89ac8bef217d83f33f),"
             "a:pkh(f94f2eadc9c1bc3a8b8c2c6364af2c070fd41206),"
             "a:pkh(3c306c2c97e4ba62ac0d7fb3965aba66b28e8959),"
             "a:pkh(ba7b9e846eb6b16420976c6bead54d9bb2b08d35),"
             "a:pkh(379ed952eb4740386acc59c2d28d9aa62e63968d),"
             "a:pkh(c30d2795e70b1ee6f8af0b33d9460d60cfcf10b3))))",
             "5421036070b1f2995d8ffda8478ef55affd39795689a3982d54b12180397b1ad1f5f7521026515fa7603c10c44f6d316ae7592b5899d46d87ac1e574ec53de8b59f95efad621038c8f919f70062c084376223fd8b4f0c08958e70499df496411dde83a1bb64b0d2102d0ea7084e344b56625277b074d15a15301b9d96b0b2dd9fc905e01fc3de408e154ae6476a9141ad3ca2d247b8e8888e41f89ac8bef217d83f33f88ac6b76a914f94f2eadc9c1bc3a8b8c2c6364af2c070fd4120688ac6c936b76a9143c306c2c97e4ba62ac0d7fb3965aba66b28e895988ac6c936b76a914ba7b9e846eb6b16420976c6bead54d9bb2b08d3588ac6c936b76a914379ed952eb4740386acc59c2d28d9aa62e63968d88ac6c936b76a914c30d2795e70b1ee6f8af0b33d9460d60cfcf10b388ac6c93558767562103856d447f1b890cc6e0e0114cd5bac58662c37ce7f458c458b72bd396597edfc72103e080e99896384aa8a07da837b2042a4c0d824eeaa8d51e6c9cff20682be75d4f2102c6d258e728005d4d00e55ac4b87786df507921b3ba3efec244a47f4a2e61b4b02102edfc1d6088f9b6470ed4550d8bf2326ebebc0464a7f78581fa7283fc54edecf02102f3630d1f51b2ebaaf1c7ebae9c24318279d4cff5ad16cb290b6d26edf96dca9c210353ecc8e7b1cc90d405cd6fc9d9f24d44b6b5649abc2773f28a6ca4fa7a4cd62956af029000b268"
             ),
            ("wsh(sortedmulti(2,%s,%s,%s))" % tuple(keys[:3]),
             "522103801b3a4e3ca0d61d469445621561c47f6c1424d0fd353a44c2c3ebb84ae78f592103b8fa5d5959fa4027ccbf0736a86ccde4242e3051ea363437b4ff0d52598d7cec2103e7d285b4817f83f724cd29394da75dfc84fe639ed147a944e7e6064703b1413053ae"
             ),
            ("wpkh(%s)" % keys[0],
             "0014f8f93df2160de8fd3ca716e2f905c74da3f9839f"),
            ("sh(wpkh(%s))" % keys[0],
             "0014f8f93df2160de8fd3ca716e2f905c74da3f9839f"),
            ("pkh(%s)" % keys[0],
             "76a914f8f93df2160de8fd3ca716e2f905c74da3f9839f88ac"),
        ]

        for i, (d, a) in enumerate(dd):
            s = BytesIO(d.encode())
            sc = Descriptor.from_string(d)
            self.assertEqual(str(sc), d)
            # get top level script
            scc = sc.witness_script() or sc.redeem_script(
            ) or sc.script_pubkey()
            self.assertEqual(len(scc.data), sc.script_len)
            schex = hexlify(scc.data).decode()
            self.assertEqual(schex, a)
            self.assertEqual(str(sc), d)
Exemplo n.º 17
0
API = "https://blockstream.info/testnet/api"
# we will generate testnet addresses
network = NETWORKS['test']

# after GAP_LIMIT addresses without any transactions
# we will stop querying
GAP_LIMIT = 20

# You can either provide a combined descriptor with {0,1} branches,
# or iterate over descriptors (recv and change descriptors)
# Seed: session spawn august alpha trap spider thing swim finish motor neutral across
# Here we use combined descriptor (Bitcoin Core doesn't support that though)
# Here we use vpub, but it's the same as this tpub: tpubDC93uE1NfJMF37r4EL87CHBUEtScESkBNo6Ym3DYCqKdmdtsL8ZqK39aHfaESmSn9ZohH1vzQjDchsuAXRDGXuowXZSXj3fY7PJ9yBAhWst
# native segwit
desc = Descriptor.from_string(
    "wpkh([911cf0a8/84h/1h/0h]vpub5Y6tmeqrefJq4jGy7RZmaBf6Zq44MpG7zwToqhNd6uoyv9bhkbPcvUAU1DaGvTBhYP3BAVDzxJgUF8BRhAY13zzSjHJNshNKyyaTS4F5hnr/{0,1}/*)"
)
# desc = Descriptor.from_string("sh(wpkh([911cf0a8/49h/1h/0h]upub5EQTr2VnHFmuJc3btwZcETwhDCzvXF8hqK2AS57ZN5fzxa6LGMtoAKuyc12F7rBpKUyocqfc8kCTzHdTmJh1i6672pu3JzobNHu39oW9Btd/{0,1}/*))")
# desc = Descriptor.from_string("pkh([911cf0a8/44h/1h/0h]tpubDD958ijpckkrCrWzY4jTwuCafkK1gijyJVbc96EViYN29Ac7K9eUyzSTrwQuoGUvwpzQMHh2fT8JGtnYHjTFWRXJAEs48s1nZSpG92hC1yb/{0,1}/*)")

# where to send
DESTINATION = "2N6AUY73q79SPzGvgPhR9biETV7DZffTQz9"
# amount to send in sat
AMOUNT = 10_000
# if change is less than DUST_LIMIT we don't create UTXO for it
DUST_LIMIT = 100

# to speed up connection to the API
s = requests.session()

# last known block height, can be used as locktime in the transaction
Exemplo n.º 18
0
    def create_wallet(self, name, sigs_required, key_type, keys, devices):
        try:
            walletsindir = [
                wallet["name"]
                for wallet in self.rpc.listwalletdir()["wallets"]
            ]
        except:
            walletsindir = []
        wallet_alias = alias(name)
        i = 2
        while (os.path.isfile(
                os.path.join(self.working_folder, "%s.json" % wallet_alias))
               or os.path.join(self.rpc_path, wallet_alias) in walletsindir):
            wallet_alias = alias("%s %d" % (name, i))
            i += 1

        arr = key_type.split("-")
        descs = [key.metadata["combined"] for key in keys]
        recv_descs = ["%s/0/*" % desc for desc in descs]
        change_descs = ["%s/1/*" % desc for desc in descs]
        if len(keys) > 1:
            recv_descriptor = "sortedmulti({},{})".format(
                sigs_required, ",".join(recv_descs))
            change_descriptor = "sortedmulti({},{})".format(
                sigs_required, ",".join(change_descs))
        else:
            recv_descriptor = recv_descs[0]
            change_descriptor = change_descs[0]
        for el in arr[::-1]:
            recv_descriptor = "%s(%s)" % (el, recv_descriptor)
            change_descriptor = "%s(%s)" % (el, change_descriptor)

        if is_liquid(self.chain):
            blinding_key = None
            if len(devices) == 1:
                blinding_key = devices[0].blinding_key
            if not blinding_key:
                desc = Descriptor.from_string(recv_descriptor.split("#")[0])
                # For now we use sha256(b"blinding_key", xor(chaincodes)) as a blinding key
                # where chaincodes are corresponding to xpub of the first receiving address
                xor = bytearray(32)
                desc_keys = desc.derive(0).keys
                for k in desc_keys:
                    if k.is_extended:
                        chaincode = k.key.chain_code
                        for i in range(32):
                            xor[i] = xor[i] ^ chaincode[i]
                secret = hashlib.sha256(b"blinding_key" + bytes(xor)).digest()
                blinding_key = ec.PrivateKey(secret).wif()
            if blinding_key:
                recv_descriptor = f"blinded(slip77({blinding_key}),{recv_descriptor})"
                change_descriptor = (
                    f"blinded(slip77({blinding_key}),{change_descriptor})")

        recv_descriptor = AddChecksum(recv_descriptor)
        change_descriptor = AddChecksum(change_descriptor)

        # v20.99 is pre-v21 Elements Core for descriptors
        if self.bitcoin_core_version_raw >= 209900:
            # Use descriptor wallet
            self.rpc.createwallet(os.path.join(self.rpc_path, wallet_alias),
                                  True, True, "", False, True)
        else:
            self.rpc.createwallet(os.path.join(self.rpc_path, wallet_alias),
                                  True)

        w = self.WalletClass(
            name,
            wallet_alias,
            "{} of {} {}".format(sigs_required, len(keys), purposes[key_type])
            if len(keys) > 1 else purposes[key_type],
            addrtypes[key_type],
            "",
            -1,
            "",
            -1,
            0,
            0,
            recv_descriptor,
            change_descriptor,
            keys,
            devices,
            sigs_required,
            {},
            [],
            os.path.join(self.working_folder, "%s.json" % wallet_alias),
            self.device_manager,
            self,
        )
        # save wallet file to disk
        if w and self.working_folder is not None:
            w.save_to_file()
        # get Wallet class instance
        if w:
            self.wallets[name] = w
            return w
        else:
            raise ("Failed to create new wallet")
Exemplo n.º 19
0
    def test_miniscript(self):
        # and(pk(A),after(100)) -> and_v(v:pk(A),after(100))
        path = "49h/1h/0h/2h"
        fgp = sim.query("fingerprint").decode()
        xpub = sim.query(f"xpub m/{path}").decode()
        desc = f"wsh(and_v(v:pk([{fgp}/{path}]{xpub}"+"/{0,1}/*),after(10)))"
        res = sim.query("addwallet mini&"+desc, [True])

        wname = wallet_prefix+"_mini"
        d = Descriptor.from_string(desc)

        addr = d.derive(5).address(NETWORKS['regtest'])
        # check it finds the wallet correctly
        sc = d.derive(5).witness_script().data.hex()
        res = sim.query(f"showaddr wsh m/{path}/0/5 {sc}", [True])
        self.assertEqual(res.decode(), addr)

        d1 = d.derive(2, branch_index=0)
        d2 = d.derive(3, branch_index=1)
        # recv addr 2
        addr1 = d1.address(NETWORKS['regtest'])
        # change addr 3
        addr2 = d2.address(NETWORKS['regtest'])
        res = sim.query(f"bitcoin:{addr1}?index=2", [True])
        # check it's found
        self.assertFalse(b"Can't find wallet" in res)

        rpc.createwallet(wname, True, True)
        w = rpc.wallet(wname)
        res = w.importmulti([{
                "scriptPubKey": {"address": addr1},#d1.script_pubkey().data.hex(),
                # "witnessscript": d1.witness_script().data.hex(),
                # "pubkeys": [k.sec().hex() for k in d1.keys],
                "internal": False,
                "timestamp": "now",
                "watchonly": True,
            },{
                "scriptPubKey": {"address": addr2},#d2.script_pubkey().data.hex(),
                # "witnessscript": d2.witness_script().data.hex(),
                # "pubkeys": [k.sec().hex() for k in d2.keys],
                "internal": True,
                "timestamp": "now",
                "watchonly": True,
            }],{"rescan": False})
        self.assertTrue(all([k["success"] for k in res]))
        wdefault.sendtoaddress(addr1, 0.1)
        rpc.mine()
        unspent = w.listunspent()
        self.assertTrue(len(unspent) > 0)
        unspent = [{"txid": u["txid"], "vout": u["vout"]} for u in unspent[:1]]
        tx = w.createrawtransaction(unspent, [{wdefault.getnewaddress(): 0.002},{addr2: 0.09799}])
        psbt = PSBT.from_base64(w.converttopsbt(tx))
        # locktime magic :)
        psbt.tx.locktime = 11
        psbt.tx.vin[0].sequence = 10
        # fillinig psbt with data
        psbt.inputs[0].witness_script = d1.witness_script()
        pub = ec.PublicKey.parse(d1.keys[0].sec())
        psbt.inputs[0].bip32_derivations[pub] = DerivationPath(bytes.fromhex(fgp), bip32.parse_path(f"m/{path}")+[0,2])
        tx = w.gettransaction(unspent[0]["txid"])
        t = Transaction.from_string(tx["hex"])
        psbt.inputs[0].witness_utxo = t.vout[unspent[0]["vout"]]

        psbt.outputs[1].witness_script = d2.witness_script()
        pub2 = ec.PublicKey.parse(d2.keys[0].sec())
        psbt.outputs[1].bip32_derivations[pub2] = DerivationPath(bytes.fromhex(fgp), bip32.parse_path(f"m/{path}")+[1,3])

        unsigned = psbt.to_base64()
        # confirm signing
        signed = sim.query("sign "+unsigned, [True])
        stx = PSBT.from_base64(signed.decode())
        # signed tx
        t = psbt.tx
        # print(stx)
        t.vin[0].witness = Witness([stx.inputs[0].partial_sigs[pub], psbt.inputs[0].witness_script.data])
        # broadcast
        with self.assertRaises(Exception):
            res = rpc.sendrawtransaction(t.serialize().hex())
        rpc.mine(11)
        res = rpc.sendrawtransaction(t.serialize().hex())
        rpc.mine()
        self.assertEqual(len(bytes.fromhex(res)), 32)