URI: 
       twallet: fix is_mine/can_sign. don't just rely on ks, also check script - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit ab4e2dd9f0a083a7e1664a167dd9a1547289d80c
   DIR parent 0d33da2f95951a4b13755927876c9a17b9485f82
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Wed, 12 Feb 2020 18:07:08 +0100
       
       wallet: fix is_mine/can_sign. don't just rely on ks, also check script
       
       Previously a standard (single-sig) wallet would consider a multisig txin as is_mine
       (if the keystore found its pubkey in the txin).
       
       fixes #5948
       
       Diffstat:
         M electrum/keystore.py                |      33 +++++++++++++++++++++----------
         M electrum/tests/test_wallet_vertica… |      52 +++++++++++++++++++++++++++++++
         M electrum/wallet.py                  |      21 ++++++++++++++++-----
       
       3 files changed, 91 insertions(+), 15 deletions(-)
       ---
   DIR diff --git a/electrum/keystore.py b/electrum/keystore.py
       t@@ -33,7 +33,7 @@ from abc import ABC, abstractmethod
        
        from . import bitcoin, ecc, constants, bip32
        from .bitcoin import deserialize_privkey, serialize_privkey
       -from .transaction import Transaction, PartialTransaction, PartialTxInput, PartialTxOutput
       +from .transaction import Transaction, PartialTransaction, PartialTxInput, PartialTxOutput, TxInput
        from .bip32 import (convert_bip32_path_to_list_of_uint32, BIP32_PRIME,
                            is_xpub, is_xprv, BIP32Node, normalize_bip32_derivation,
                            convert_bip32_intpath_to_strpath)
       t@@ -78,16 +78,21 @@ class KeyStore(Logger, ABC):
            def _get_tx_derivations(self, tx: 'PartialTransaction') -> Dict[str, Union[Sequence[int], str]]:
                keypairs = {}
                for txin in tx.inputs():
       -            if txin.is_complete():
       +            keypairs.update(self._get_txin_derivations(txin))
       +        return keypairs
       +
       +    def _get_txin_derivations(self, txin: 'PartialTxInput') -> Dict[str, Union[Sequence[int], str]]:
       +        if txin.is_complete():
       +            return {}
       +        keypairs = {}
       +        for pubkey in txin.pubkeys:
       +            if pubkey in txin.part_sigs:
       +                # this pubkey already signed
       +                continue
       +            derivation = self.get_pubkey_derivation(pubkey, txin)
       +            if not derivation:
                        continue
       -            for pubkey in txin.pubkeys:
       -                if pubkey in txin.part_sigs:
       -                    # this pubkey already signed
       -                    continue
       -                derivation = self.get_pubkey_derivation(pubkey, txin)
       -                if not derivation:
       -                    continue
       -                keypairs[pubkey.hex()] = derivation
       +            keypairs[pubkey.hex()] = derivation
                return keypairs
        
            def can_sign(self, tx: 'Transaction', *, ignore_watching_only=False) -> bool:
       t@@ -98,6 +103,14 @@ class KeyStore(Logger, ABC):
                    return False
                return bool(self._get_tx_derivations(tx))
        
       +    def can_sign_txin(self, txin: 'TxInput', *, ignore_watching_only=False) -> bool:
       +        """Returns whether this keystore could sign this txin."""
       +        if not ignore_watching_only and self.is_watching_only():
       +            return False
       +        if not isinstance(txin, PartialTxInput):
       +            return False
       +        return bool(self._get_txin_derivations(txin))
       +
            def ready_to_sign(self) -> bool:
                return not self.is_watching_only()
        
   DIR diff --git a/electrum/tests/test_wallet_vertical.py b/electrum/tests/test_wallet_vertical.py
       t@@ -1478,6 +1478,58 @@ class TestWalletSending(TestCaseForTestnet):
                self.assertEqual((0, 10995000, 0), wallet1.get_balance())
                self.assertEqual((0, 10495000, 0), wallet2.get_balance())
        
       +    @mock.patch.object(wallet.Abstract_Wallet, 'save_db')
       +    def test_standard_wallet_cannot_sign_multisig_input_even_if_cosigner(self, mock_save_db):
       +        """Just because our keystore recognizes the pubkeys in a txin, if the prevout does not belong to the wallet,
       +        then wallet.is_mine and wallet.can_sign should return False (e.g. multisig input for single-sig wallet).
       +        (see issue #5948)
       +        """
       +        wallet_2of2 = WalletIntegrityHelper.create_multisig_wallet(
       +            [
       +                # seed: frost repair depend effort salon ring foam oak cancel receive save usage
       +                # convert_xkey(wallet.get_master_public_key(), "p2wsh")
       +                keystore.from_xpub('Vpub5gqF73Wpbp9ThwEgZKHLjBDthsatXjajYvrN8CVnkdBYeTR1M1sfZFQqQ5wpKHGhnwKhzgMhaWrtgKG2LthCzxjd653KqKVUAw7UrwYnbKQ'),
       +                # seed: bitter grass shiver impose acquire brush forget axis eager alone wine silver
       +                # convert_xkey(wallet.get_master_public_key(), "p2wsh")
       +                keystore.from_xpub('Vpub5gSKXzxK7FeKNi2WPNW9iuA48SbJRZvKFBwtgucpegMWPdohQPeK2DoR6XFtC7BBLsHhfWDAPKaiecqJ7jTzYSfeg5YATowmPcgCWxARabT')
       +            ],
       +            '2of2', gap_limit=2,
       +            config=self.config
       +        )
       +        wallet_frost = self.create_standard_wallet_from_seed('frost repair depend effort salon ring foam oak cancel receive save usage')
       +
       +        # bootstrap wallet_2of2
       +        funding_tx = Transaction('020000000001018ed0132bb5f35d097572081524cd5e847c895e765b93d5af46b8a8bef621244a0100000000fdffffff0220a1070000000000220020302981db44eb5dad0dab3987134a985b360ae2227a7e7a10cfe8cffd23bacdc9b07912000000000016001442b423aab2aa803f957084832b10359beaa2469002473044022065c5e28900b4706487223357e8539e176552e3560e2081ac18de7c26e8e420ba02202755c7fc8177ff502634104c090e3fd4c4252bfa8566d4eb6605bb9e236e7839012103b63bbf85ec9e5e312e4d7a2b45e690f48b916a442e787a47a6092d6c052394c5966a1900')
       +        funding_txid = funding_tx.txid()
       +        self.assertEqual('0c2f5981981a6cb69d7b729feceb55be7962b16dc41e8aaf64e5203f7cb604d0', funding_txid)
       +        wallet_2of2.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
       +
       +        # create tx
       +        outputs = [PartialTxOutput.from_address_and_value('tb1qfrlx5pza9vmez6vpx7swt8yp0nmgz3qa7jjkuf', 100_000)]
       +        coins = wallet_2of2.get_spendable_coins(domain=None)
       +        tx = wallet_2of2.make_unsigned_transaction(coins=coins, outputs=outputs, fee=5000)
       +        tx.set_rbf(True)
       +        tx.locktime = 1665628
       +
       +        partial_tx = tx.serialize_as_bytes().hex()
parazyd.org:70 /git/electrum/commit/ab4e2dd9f0a083a7e1664a167dd9a1547289d80c.gph:122: line too long