예제 #1
0
def clean_psbt(b64psbt):
    psbt = PSBT()
    psbt.deserialize(b64psbt)
    for inp in psbt.inputs:
        if inp.witness_utxo is not None and inp.non_witness_utxo is not None:
            inp.non_witness_utxo = None
    return psbt.serialize()
예제 #2
0
 def test_valid_psbt(self):
     for valid in self.data['valid']:
         with self.subTest(valid=valid):
             psbt = PSBT()
             psbt.deserialize(valid)
             serd = psbt.serialize()
             self.assertEqual(valid, serd)
예제 #3
0
    def _generate_and_finalize(self, unknown_inputs, psbt):
        if not unknown_inputs:
            # Just do the normal signing process to test "all inputs" case
            sign_res = self.do_command(self.dev_args + ['signtx', psbt['psbt']])
            finalize_res = self.wrpc.finalizepsbt(sign_res['psbt'])
            self.assertTrue(sign_res["signed"])
            self.assertTrue(finalize_res["complete"])
        else:
            # Sign only input one on first pass
            # then rest on second pass to test ability to successfully
            # ignore inputs that are not its own. Then combine both
            # signing passes to ensure they are actually properly being
            # partially signed at each step.
            first_psbt = PSBT()
            first_psbt.deserialize(psbt['psbt'])
            second_psbt = PSBT()
            second_psbt.deserialize(psbt['psbt'])

            # Blank master fingerprint to make hww fail to sign
            # Single input PSBTs will be fully signed by first signer
            for psbt_input in first_psbt.inputs[1:]:
                for pubkey, path in psbt_input.hd_keypaths.items():
                    psbt_input.hd_keypaths[pubkey] = KeyOriginInfo(b"\x00\x00\x00\x01", path.path)
                for pubkey, (leaves, origin) in psbt_input.tap_bip32_paths.items():
                    psbt_input.tap_bip32_paths[pubkey] = (leaves, KeyOriginInfo(b"\x00\x00\x00\x01", origin.path))
            for pubkey, path in second_psbt.inputs[0].hd_keypaths.items():
                second_psbt.inputs[0].hd_keypaths[pubkey] = KeyOriginInfo(b"\x00\x00\x00\x01", path.path)
            for pubkey, (leaves, origin) in second_psbt.inputs[0].tap_bip32_paths.items():
                second_psbt.inputs[0].tap_bip32_paths[pubkey] = (leaves, KeyOriginInfo(b"\x00\x00\x00\x01", origin.path))

            single_input = len(first_psbt.inputs) == 1

            # Process the psbts
            first_psbt = first_psbt.serialize()
            second_psbt = second_psbt.serialize()

            # First will always have something to sign
            first_sign_res = self.do_command(self.dev_args + ['signtx', first_psbt])
            self.assertTrue(first_sign_res["signed"])
            self.assertTrue(single_input == self.wrpc.finalizepsbt(first_sign_res['psbt'])['complete'])
            # Second may have nothing to sign (1 input case)
            # and also may throw an error(e.g., ColdCard)
            second_sign_res = self.do_command(self.dev_args + ['signtx', second_psbt])
            if 'psbt' in second_sign_res:
                if single_input:
                    self.assertFalse(second_sign_res["signed"])
                else:
                    self.assertTrue(second_sign_res["signed"])
                self.assertTrue(not self.wrpc.finalizepsbt(second_sign_res['psbt'])['complete'])
                combined_psbt = self.wrpc.combinepsbt([first_sign_res['psbt'], second_sign_res['psbt']])

            else:
                self.assertTrue('error' in second_sign_res)
                combined_psbt = first_sign_res['psbt']

            finalize_res = self.wrpc.finalizepsbt(combined_psbt)
            self.assertTrue(finalize_res['complete'])
            self.assertTrue(self.wrpc.testmempoolaccept([finalize_res['hex']])[0]["allowed"])
        return finalize_res['hex']
예제 #4
0
    def _test_signtx(self, input_types, multisig_types, external,
                     op_return: bool):
        # Import some keys to the watch only wallet and send coins to them
        keypool_desc = self.do_command(self.dev_args +
                                       ['getkeypool', '--all', '30', '50'])
        import_result = self.wrpc.importdescriptors(keypool_desc)
        self.assertTrue(import_result[0]['success'])
        sh_wpkh_addr = self.wrpc.getnewaddress('', 'p2sh-segwit')
        wpkh_addr = self.wrpc.getnewaddress('', 'bech32')
        pkh_addr = self.wrpc.getnewaddress('', 'legacy')
        tr_addr = None
        if "tap" in input_types:
            tr_addr = self.wrpc.getnewaddress("", "bech32m")

        in_amt = 1
        number_inputs = 0
        # Single-sig
        if "segwit" in input_types:
            self.wpk_rpc.sendtoaddress(sh_wpkh_addr, in_amt)
            self.wpk_rpc.sendtoaddress(wpkh_addr, in_amt)
            number_inputs += 2
        if "legacy" in input_types:
            self.wpk_rpc.sendtoaddress(pkh_addr, in_amt)
            number_inputs += 1
        if "tap" in input_types:
            assert tr_addr is not None
            self.wpk_rpc.sendtoaddress(tr_addr, in_amt)
            number_inputs += 1
        # Now do segwit/legacy multisig
        xpubs: Dict[bytes, KeyOriginInfo] = {}
        if "legacy" in multisig_types:
            sh_multi_desc, sh_multi_addr, sh_multi_xpubs = self._make_multisig(
                "legacy")

            xpubs.update(sh_multi_xpubs)

            sh_multi_import = {
                'desc': sh_multi_desc,
                "timestamp": "now",
                "label": "shmulti"
            }
            multi_result = self.wrpc.importdescriptors([sh_multi_import])
            self.assertTrue(multi_result[0]['success'])

            self.wpk_rpc.sendtoaddress(sh_multi_addr, in_amt)
            number_inputs += 1
        if "segwit" in multisig_types:
            sh_wsh_multi_desc, sh_wsh_multi_addr, sh_wsh_xpubs = self._make_multisig(
                "p2sh-segwit")
            wsh_multi_desc, wsh_multi_addr, wsh_xpubs = self._make_multisig(
                "bech32")

            xpubs.update(sh_wsh_xpubs)
            xpubs.update(wsh_xpubs)

            sh_wsh_multi_import = {
                'desc': sh_wsh_multi_desc,
                "timestamp": "now",
                "label": "shwshmulti"
            }
            wsh_multi_import = {
                'desc': wsh_multi_desc,
                "timestamp": "now",
                "label": "wshmulti"
            }

            multi_result = self.wrpc.importdescriptors(
                [sh_wsh_multi_import, wsh_multi_import])
            self.assertTrue(multi_result[0]['success'])
            self.assertTrue(multi_result[1]['success'])

            self.wpk_rpc.sendtoaddress(wsh_multi_addr, in_amt)
            self.wpk_rpc.sendtoaddress(sh_wsh_multi_addr, in_amt)
            number_inputs += 2

        self.wpk_rpc.generatetoaddress(6, self.wpk_rpc.getnewaddress())

        # Spend different amounts, with increasing number of inputs until the wallet is swept
        utxos = self.wrpc.listunspent()
        for i in range(1, number_inputs + 1):
            # Create a psbt spending the above
            change_addr = self.wpk_rpc.getrawchangeaddress()

            out_val = i / 4
            outputs = [{
                self.wpk_rpc.getnewaddress('', 'legacy'): out_val
            }, {
                self.wpk_rpc.getnewaddress('', 'p2sh-segwit'): out_val
            }, {
                self.wpk_rpc.getnewaddress('', 'bech32'): out_val
            }]
            if self.emulator.supports_taproot:
                outputs.append(
                    {self.wpk_rpc.getnewaddress("", "bech32m"): out_val})
            if op_return:
                outputs.append({
                    "data":
                    "000102030405060708090a0b0c0d0e0f10111213141516171819101a1b1c1d1e1f"
                })
            psbt = self.wrpc.walletcreatefundedpsbt(
                utxos[:i], outputs, 0, {
                    "includeWatching": True,
                    "changeAddress": change_addr,
                    "subtractFeeFromOutputs": [0, 1, 2],
                }, True)["psbt"]

            # We need to modify the psbt to include our xpubs as Core does not include xpubs
            psbt_obj = PSBT()
            psbt_obj.deserialize(psbt)
            psbt_obj.xpub = xpubs
            psbt = psbt_obj.serialize()

            if external:
                # Sign with unknown inputs in two steps
                self._generate_and_finalize(True, psbt)
            # Sign all inputs all at once
            final_tx = self._generate_and_finalize(False, psbt)

        # Send off final tx to sweep the wallet
        self.wrpc.sendrawtransaction(final_tx)
예제 #5
0
 def test_invalid_psbt(self):
     for invalid in self.data['invalid']:
         with self.subTest(invalid=invalid):
             with self.assertRaises(PSBTSerializationError):
                 psbt = PSBT()
                 psbt.deserialize(invalid)