Example #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"])
    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)