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)