예제 #1
0
    def test_sign_verify(self):
        unsigned = PSBT.from_string(B64PSBT)
        signed = PSBT.from_string(B64SIGNED)
        tx = unsigned.tx
        values = [inp.utxo.value for inp in unsigned.inputs]
        scripts = [inp.utxo.script_pubkey for inp in unsigned.inputs]
        for i, inp in enumerate(signed.inputs):
            wit = inp.final_scriptwitness.items[0]
            sig = SchnorrSig.parse(wit[:64])
            if len(wit) == 65:
                sighash = wit[64]
            else:
                sighash = SIGHASH.DEFAULT
            hsh = tx.sighash_taproot(i,
                                     script_pubkeys=scripts,
                                     values=values,
                                     sighash=sighash)
            pub = PublicKey.from_xonly(inp.utxo.script_pubkey.data[2:])
            self.assertTrue(pub.schnorr_verify(sig, hsh))
            # check signing and derivation
            prv = ROOT.derive([0, i])
            tweaked = prv.taproot_tweak(b"")
            self.assertEqual(pub.xonly(), tweaked.xonly())
            sig2 = tweaked.schnorr_sign(hsh)
            self.assertTrue(pub.schnorr_verify(sig2, hsh))
            self.assertEqual(sig, sig2)
            # sign with individual pks
            sigcount = unsigned.sign_with(prv.key, SIGHASH.ALL)
            self.assertEqual(sigcount, 1)
            self.assertEqual(unsigned.inputs[i].final_scriptwitness,
                             signed.inputs[i].final_scriptwitness)

        for i, inp in enumerate(unsigned.inputs):
            prv = ROOT.derive([0, i])
            # remove final scriptwitness to test signing with root
            inp.final_scriptwitness = None
            # populate derivation
            inp.bip32_derivations[prv.key.get_public_key()] = DerivationPath(
                ROOT.my_fingerprint, [0, i])

        # test signing with root key
        counter = unsigned.sign_with(ROOT, SIGHASH.ALL)
        self.assertEqual(counter, 2)
        for inp1, inp2 in zip(unsigned.inputs, signed.inputs):
            self.assertEqual(inp1.final_scriptwitness,
                             inp2.final_scriptwitness)
            inp1.final_scriptwitness = None

        # test signing with psbtview, unsigned already has derivations
        stream = BytesIO(unsigned.serialize())
        psbtv = PSBTView.view(stream, compress=False)
        sigs = BytesIO()
        sigcount = psbtv.sign_with(ROOT, sigs, SIGHASH.ALL)
        self.assertEqual(sigcount, len(unsigned.inputs))
        v = sigs.getvalue()
        # check sigs are in the stream
        for inp in signed.inputs:
            self.assertTrue(inp.final_scriptwitness.items[0] in v)
예제 #2
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
예제 #3
0
def fill_external_wallet_derivations(psbt, wallet):
    # fill derivations for receiving wallets if they own address
    net = get_network(wallet.manager.chain)
    wallets = wallet.manager.wallets.values()
    for out in psbt.outputs:
        try:  # if we fail - not a big deal
            # check if output derivation is empty
            if out.bip32_derivations:
                continue
            try:
                # if there is no address representation - continue
                addr = out.script_pubkey.address(net)
            except:
                continue
            for w in wallets:
                # skip sending wallet
                if w == wallet:
                    continue
                if not w.is_address_mine(addr):
                    continue
                # we get here if wallet owns address
                info = w.get_address_info(addr)
                derivation = [int(info.change), int(info.index)]
                # we first one key and build derivation for it
                # it's enough for DIY to do the res
                k = w.keys[0]
                key = bip32.HDKey.from_string(k.xpub)
                if k.fingerprint != "":
                    fingerprint = bytes.fromhex(k.fingerprint)
                else:
                    fingerprint = key.my_fingerprint
                if k.derivation != "":
                    der = bip32.parse_path(k.derivation)
                else:
                    der = []
                pub = key.derive(derivation).key
                out.bip32_derivations[pub] = DerivationPath(
                    fingerprint, der + derivation)
                break
        except Exception as e:
            logger.exception(e)
예제 #4
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)
예제 #5
0
        change_output = None

        # if used - check the balance:
        balance = (res.get("chain_stats", {}).get("funded_txo_sum", 0) +
                   res.get("mempool_stats", {}).get("funded_txo_sum", 0) -
                   res.get("chain_stats", {}).get("spent_txo_sum", 0) -
                   res.get("mempool_stats", {}).get("spent_txo_sum", 0))
        # positive balance - we have utxos there
        if balance > 0:
            print(addr, "utxos!", balance)
            utxoarr = s.get(f"{API}/address/{addr}/utxo").json()

            # derivation information for utxos
            bip32_derivations = {}
            for k in d.keys:
                bip32_derivations[PublicKey.parse(k.sec())] = DerivationPath(
                    k.origin.fingerprint, k.origin.derivation)

            # for multisig this is important,
            # for native segwit single sig it's None
            ws = d.witness_script()
            rs = d.redeem_script()
            script_pubkey = d.script_pubkey()

            utxos += [{
                "txid":
                utxo["txid"],
                "vout":
                utxo["vout"],
                "value":
                utxo["value"],
                "witness_script":