1# Electrum - lightweight Bitcoin client
2# Copyright (C) 2015 Thomas Voegtlin
3#
4# Permission is hereby granted, free of charge, to any person
5# obtaining a copy of this software and associated documentation files
6# (the "Software"), to deal in the Software without restriction,
7# including without limitation the rights to use, copy, modify, merge,
8# publish, distribute, sublicense, and/or sell copies of the Software,
9# and to permit persons to whom the Software is furnished to do so,
10# subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be
13# included in all copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22# SOFTWARE.
23
24# Wallet classes:
25#   - Imported_Wallet: imported addresses or single keys, 0 or 1 keystore
26#   - Standard_Wallet: one HD keystore, P2PKH-like scripts
27#   - Multisig_Wallet: several HD keystores, M-of-N OP_CHECKMULTISIG scripts
28
29import os
30import sys
31import random
32import time
33import json
34import copy
35import errno
36import traceback
37import operator
38import math
39from functools import partial
40from collections import defaultdict
41from numbers import Number
42from decimal import Decimal
43from typing import TYPE_CHECKING, List, Optional, Tuple, Union, NamedTuple, Sequence, Dict, Any, Set
44from abc import ABC, abstractmethod
45import itertools
46import threading
47import enum
48
49from aiorpcx import TaskGroup, timeout_after, TaskTimeout, ignore_after
50
51from .i18n import _
52from .bip32 import BIP32Node, convert_bip32_intpath_to_strpath, convert_bip32_path_to_list_of_uint32
53from .crypto import sha256
54from . import util
55from .util import (NotEnoughFunds, UserCancelled, profiler,
56                   format_satoshis, format_fee_satoshis, NoDynamicFeeEstimates,
57                   WalletFileException, BitcoinException, MultipleSpendMaxTxOutputs,
58                   InvalidPassword, format_time, timestamp_to_datetime, Satoshis,
59                   Fiat, bfh, bh2u, TxMinedInfo, quantize_feerate, create_bip21_uri, OrderedDictWithIndex)
60from .simple_config import SimpleConfig, FEE_RATIO_HIGH_WARNING, FEERATE_WARNING_HIGH_FEE
61from .bitcoin import COIN, TYPE_ADDRESS
62from .bitcoin import is_address, address_to_script, is_minikey, relayfee, dust_threshold
63from .crypto import sha256d
64from . import keystore
65from .keystore import (load_keystore, Hardware_KeyStore, KeyStore, KeyStoreWithMPK,
66                       AddressIndexGeneric, CannotDerivePubkey)
67from .util import multisig_type
68from .storage import StorageEncryptionVersion, WalletStorage
69from .wallet_db import WalletDB
70from . import transaction, bitcoin, coinchooser, paymentrequest, ecc, bip32
71from .transaction import (Transaction, TxInput, UnknownTxinType, TxOutput,
72                          PartialTransaction, PartialTxInput, PartialTxOutput, TxOutpoint)
73from .plugin import run_hook
74from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL,
75                                   TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE)
76from .invoices import Invoice, OnchainInvoice, LNInvoice
77from .invoices import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED, PR_UNCONFIRMED, PR_TYPE_ONCHAIN, PR_TYPE_LN
78from .contacts import Contacts
79from .interface import NetworkException
80from .mnemonic import Mnemonic
81from .logging import get_logger
82from .lnworker import LNWallet
83from .paymentrequest import PaymentRequest
84from .util import read_json_file, write_json_file, UserFacingException
85
86if TYPE_CHECKING:
87    from .network import Network
88    from .exchange_rate import FxThread
89
90
91_logger = get_logger(__name__)
92
93TX_STATUS = [
94    _('Unconfirmed'),
95    _('Unconfirmed parent'),
96    _('Not Verified'),
97    _('Local'),
98]
99
100
101class BumpFeeStrategy(enum.Enum):
102    COINCHOOSER = enum.auto()
103    DECREASE_CHANGE = enum.auto()
104    DECREASE_PAYMENT = enum.auto()
105
106
107async def _append_utxos_to_inputs(*, inputs: List[PartialTxInput], network: 'Network',
108                                  pubkey: str, txin_type: str, imax: int) -> None:
109    if txin_type in ('p2pkh', 'p2wpkh', 'p2wpkh-p2sh'):
110        address = bitcoin.pubkey_to_address(txin_type, pubkey)
111        scripthash = bitcoin.address_to_scripthash(address)
112    elif txin_type == 'p2pk':
113        script = bitcoin.public_key_to_p2pk_script(pubkey)
114        scripthash = bitcoin.script_to_scripthash(script)
115    else:
116        raise Exception(f'unexpected txin_type to sweep: {txin_type}')
117
118    async def append_single_utxo(item):
119        prev_tx_raw = await network.get_transaction(item['tx_hash'])
120        prev_tx = Transaction(prev_tx_raw)
121        prev_txout = prev_tx.outputs()[item['tx_pos']]
122        if scripthash != bitcoin.script_to_scripthash(prev_txout.scriptpubkey.hex()):
123            raise Exception('scripthash mismatch when sweeping')
124        prevout_str = item['tx_hash'] + ':%d' % item['tx_pos']
125        prevout = TxOutpoint.from_str(prevout_str)
126        txin = PartialTxInput(prevout=prevout)
127        txin.utxo = prev_tx
128        txin.block_height = int(item['height'])
129        txin.script_type = txin_type
130        txin.pubkeys = [bfh(pubkey)]
131        txin.num_sig = 1
132        if txin_type == 'p2wpkh-p2sh':
133            txin.redeem_script = bfh(bitcoin.p2wpkh_nested_script(pubkey))
134        inputs.append(txin)
135
136    u = await network.listunspent_for_scripthash(scripthash)
137    async with TaskGroup() as group:
138        for item in u:
139            if len(inputs) >= imax:
140                break
141            await group.spawn(append_single_utxo(item))
142
143
144async def sweep_preparations(privkeys, network: 'Network', imax=100):
145
146    async def find_utxos_for_privkey(txin_type, privkey, compressed):
147        pubkey = ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed)
148        await _append_utxos_to_inputs(
149            inputs=inputs,
150            network=network,
151            pubkey=pubkey,
152            txin_type=txin_type,
153            imax=imax)
154        keypairs[pubkey] = privkey, compressed
155
156    inputs = []  # type: List[PartialTxInput]
157    keypairs = {}
158    async with TaskGroup() as group:
159        for sec in privkeys:
160            txin_type, privkey, compressed = bitcoin.deserialize_privkey(sec)
161            await group.spawn(find_utxos_for_privkey(txin_type, privkey, compressed))
162            # do other lookups to increase support coverage
163            if is_minikey(sec):
164                # minikeys don't have a compressed byte
165                # we lookup both compressed and uncompressed pubkeys
166                await group.spawn(find_utxos_for_privkey(txin_type, privkey, not compressed))
167            elif txin_type == 'p2pkh':
168                # WIF serialization does not distinguish p2pkh and p2pk
169                # we also search for pay-to-pubkey outputs
170                await group.spawn(find_utxos_for_privkey('p2pk', privkey, compressed))
171    if not inputs:
172        raise UserFacingException(_('No inputs found.'))
173    return inputs, keypairs
174
175
176async def sweep(
177        privkeys,
178        *,
179        network: 'Network',
180        config: 'SimpleConfig',
181        to_address: str,
182        fee: int = None,
183        imax=100,
184        locktime=None,
185        tx_version=None) -> PartialTransaction:
186
187    inputs, keypairs = await sweep_preparations(privkeys, network, imax)
188    total = sum(txin.value_sats() for txin in inputs)
189    if fee is None:
190        outputs = [PartialTxOutput(scriptpubkey=bfh(bitcoin.address_to_script(to_address)),
191                                   value=total)]
192        tx = PartialTransaction.from_io(inputs, outputs)
193        fee = config.estimate_fee(tx.estimated_size())
194    if total - fee < 0:
195        raise Exception(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d'%(total, fee))
196    if total - fee < dust_threshold(network):
197        raise Exception(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d\nDust Threshold: %d'%(total, fee, dust_threshold(network)))
198
199    outputs = [PartialTxOutput(scriptpubkey=bfh(bitcoin.address_to_script(to_address)),
200                               value=total - fee)]
201    if locktime is None:
202        locktime = get_locktime_for_new_transaction(network)
203
204    tx = PartialTransaction.from_io(inputs, outputs, locktime=locktime, version=tx_version)
205    rbf = bool(config.get('use_rbf', True))
206    tx.set_rbf(rbf)
207    tx.sign(keypairs)
208    return tx
209
210
211def get_locktime_for_new_transaction(network: 'Network') -> int:
212    # if no network or not up to date, just set locktime to zero
213    if not network:
214        return 0
215    chain = network.blockchain()
216    if chain.is_tip_stale():
217        return 0
218    # discourage "fee sniping"
219    locktime = chain.height()
220    # sometimes pick locktime a bit further back, to help privacy
221    # of setups that need more time (offline/multisig/coinjoin/...)
222    if random.randint(0, 9) == 0:
223        locktime = max(0, locktime - random.randint(0, 99))
224    return locktime
225
226
227
228class CannotBumpFee(Exception):
229    def __str__(self):
230        return _('Cannot bump fee') + ':\n\n' + Exception.__str__(self)
231
232class CannotDoubleSpendTx(Exception):
233    def __str__(self):
234        return _('Cannot cancel transaction') + ':\n\n' + Exception.__str__(self)
235
236class CannotCPFP(Exception):
237    def __str__(self):
238        return _('Cannot create child transaction') + ':\n\n' + Exception.__str__(self)
239
240class InternalAddressCorruption(Exception):
241    def __str__(self):
242        return _("Wallet file corruption detected. "
243                 "Please restore your wallet from seed, and compare the addresses in both files")
244
245
246class TxWalletDetails(NamedTuple):
247    txid: Optional[str]
248    status: str
249    label: str
250    can_broadcast: bool
251    can_bump: bool
252    can_cpfp: bool
253    can_dscancel: bool  # whether user can double-spend to self
254    can_save_as_local: bool
255    amount: Optional[int]
256    fee: Optional[int]
257    tx_mined_status: TxMinedInfo
258    mempool_depth_bytes: Optional[int]
259    can_remove: bool  # whether user should be allowed to delete tx
260    is_lightning_funding_tx: bool
261
262
263class Abstract_Wallet(AddressSynchronizer, ABC):
264    """
265    Wallet classes are created to handle various address generation methods.
266    Completion states (watching-only, single account, no seed, etc) are handled inside classes.
267    """
268
269    LOGGING_SHORTCUT = 'w'
270    max_change_outputs = 3
271    gap_limit_for_change = 10
272
273    txin_type: str
274    wallet_type: str
275    lnworker: Optional['LNWallet']
276
277    def __init__(self, db: WalletDB, storage: Optional[WalletStorage], *, config: SimpleConfig):
278        if not db.is_ready_to_be_used_by_wallet():
279            raise Exception("storage not ready to be used by Abstract_Wallet")
280
281        self.config = config
282        assert self.config is not None, "config must not be None"
283        self.db = db
284        self.storage = storage
285        # load addresses needs to be called before constructor for sanity checks
286        db.load_addresses(self.wallet_type)
287        self.keystore = None  # type: Optional[KeyStore]  # will be set by load_keystore
288        AddressSynchronizer.__init__(self, db)
289
290        # saved fields
291        self.use_change            = db.get('use_change', True)
292        self.multiple_change       = db.get('multiple_change', False)
293        self._labels                = db.get_dict('labels')
294        self._frozen_addresses      = set(db.get('frozen_addresses', []))
295        self._frozen_coins          = db.get_dict('frozen_coins')  # type: Dict[str, bool]
296        self.fiat_value            = db.get_dict('fiat_value')
297        self.receive_requests      = db.get_dict('payment_requests')  # type: Dict[str, Invoice]
298        self.invoices              = db.get_dict('invoices')  # type: Dict[str, Invoice]
299        self._reserved_addresses   = set(db.get('reserved_addresses', []))
300
301        self._freeze_lock = threading.Lock()  # for mutating/iterating frozen_{addresses,coins}
302
303        self._prepare_onchain_invoice_paid_detection()
304        self.calc_unused_change_addresses()
305        # save wallet type the first time
306        if self.db.get('wallet_type') is None:
307            self.db.put('wallet_type', self.wallet_type)
308        self.contacts = Contacts(self.db)
309        self._coin_price_cache = {}
310
311        self.lnworker = None
312
313    def save_db(self):
314        if self.storage:
315            self.db.write(self.storage)
316
317    def save_backup(self, backup_dir):
318        new_db = WalletDB(self.db.dump(), manual_upgrades=False)
319
320        if self.lnworker:
321            channel_backups = new_db.get_dict('imported_channel_backups')
322            for chan_id, chan in self.lnworker.channels.items():
323                channel_backups[chan_id.hex()] = self.lnworker.create_channel_backup(chan_id)
324            new_db.put('channels', None)
325            new_db.put('lightning_xprv', None)
326            new_db.put('lightning_privkey2', None)
327
328        new_path = os.path.join(backup_dir, self.basename() + '.backup')
329        new_storage = WalletStorage(new_path)
330        new_storage._encryption_version = self.storage._encryption_version
331        new_storage.pubkey = self.storage.pubkey
332        new_db.set_modified(True)
333        new_db.write(new_storage)
334        return new_path
335
336    def has_lightning(self) -> bool:
337        return bool(self.lnworker)
338
339    def can_have_lightning(self) -> bool:
340        # we want static_remotekey to be a wallet address
341        return self.txin_type == 'p2wpkh'
342
343    def can_have_deterministic_lightning(self) -> bool:
344        if not self.can_have_lightning():
345            return False
346        if not self.keystore:
347            return False
348        return self.keystore.can_have_deterministic_lightning_xprv()
349
350    def init_lightning(self, *, password) -> None:
351        assert self.can_have_lightning()
352        assert self.db.get('lightning_xprv') is None
353        assert self.db.get('lightning_privkey2') is None
354        if self.can_have_deterministic_lightning():
355            assert isinstance(self.keystore, keystore.BIP32_KeyStore)
356            ln_xprv = self.keystore.get_lightning_xprv(password)
357            self.db.put('lightning_xprv', ln_xprv)
358        else:
359            seed = os.urandom(32)
360            node = BIP32Node.from_rootseed(seed, xtype='standard')
361            ln_xprv = node.to_xprv()
362            self.db.put('lightning_privkey2', ln_xprv)
363        if self.network:
364            self.network.run_from_another_thread(self.stop())
365        self.lnworker = LNWallet(self, ln_xprv)
366        if self.network:
367            self.start_network(self.network)
368
369    async def stop(self):
370        """Stop all networking and save DB to disk."""
371        try:
372            async with ignore_after(5):
373                await super().stop()
374                if self.network:
375                    if self.lnworker:
376                        await self.lnworker.stop()
377                        self.lnworker = None
378        finally:  # even if we get cancelled
379            if any([ks.is_requesting_to_be_rewritten_to_wallet_file for ks in self.get_keystores()]):
380                self.save_keystore()
381            self.save_db()
382
383    def set_up_to_date(self, b):
384        super().set_up_to_date(b)
385        if b: self.save_db()
386
387    def clear_history(self):
388        super().clear_history()
389        self.save_db()
390
391    def start_network(self, network):
392        AddressSynchronizer.start_network(self, network)
393        if network:
394            if self.lnworker:
395                self.lnworker.start_network(network)
396                # only start gossiping when we already have channels
397                if self.db.get('channels'):
398                    self.network.start_gossip()
399
400    def load_and_cleanup(self):
401        self.load_keystore()
402        self.test_addresses_sanity()
403        super().load_and_cleanup()
404
405    @abstractmethod
406    def load_keystore(self) -> None:
407        pass
408
409    def diagnostic_name(self):
410        return self.basename()
411
412    def __str__(self):
413        return self.basename()
414
415    def get_master_public_key(self):
416        return None
417
418    def get_master_public_keys(self):
419        return []
420
421    def basename(self) -> str:
422        return self.storage.basename() if self.storage else 'no name'
423
424    def test_addresses_sanity(self) -> None:
425        addrs = self.get_receiving_addresses()
426        if len(addrs) > 0:
427            addr = str(addrs[0])
428            if not bitcoin.is_address(addr):
429                neutered_addr = addr[:5] + '..' + addr[-2:]
430                raise WalletFileException(f'The addresses in this wallet are not bitcoin addresses.\n'
431                                          f'e.g. {neutered_addr} (length: {len(addr)})')
432
433    def check_returned_address_for_corruption(func):
434        def wrapper(self, *args, **kwargs):
435            addr = func(self, *args, **kwargs)
436            self.check_address_for_corruption(addr)
437            return addr
438        return wrapper
439
440    def calc_unused_change_addresses(self) -> Sequence[str]:
441        """Returns a list of change addresses to choose from, for usage in e.g. new transactions.
442        The caller should give priority to earlier ones in the list.
443        """
444        with self.lock:
445            # We want a list of unused change addresses.
446            # As a performance optimisation, to avoid checking all addresses every time,
447            # we maintain a list of "not old" addresses ("old" addresses have deeply confirmed history),
448            # and only check those.
449            if not hasattr(self, '_not_old_change_addresses'):
450                self._not_old_change_addresses = self.get_change_addresses()
451            self._not_old_change_addresses = [addr for addr in self._not_old_change_addresses
452                                              if not self.address_is_old(addr)]
453            unused_addrs = [addr for addr in self._not_old_change_addresses
454                            if not self.is_used(addr) and not self.is_address_reserved(addr)]
455            return unused_addrs
456
457    def is_deterministic(self) -> bool:
458        return self.keystore.is_deterministic()
459
460    def _set_label(self, key: str, value: Optional[str]) -> None:
461        with self.lock:
462            if value is None:
463                self._labels.pop(key, None)
464            else:
465                self._labels[key] = value
466
467    def set_label(self, name: str, text: str = None) -> bool:
468        if not name:
469            return False
470        changed = False
471        with self.lock:
472            old_text = self._labels.get(name)
473            if text:
474                text = text.replace("\n", " ")
475                if old_text != text:
476                    self._labels[name] = text
477                    changed = True
478            else:
479                if old_text is not None:
480                    self._labels.pop(name)
481                    changed = True
482        if changed:
483            run_hook('set_label', self, name, text)
484        return changed
485
486    def import_labels(self, path):
487        data = read_json_file(path)
488        for key, value in data.items():
489            self.set_label(key, value)
490
491    def export_labels(self, path):
492        write_json_file(path, self.get_all_labels())
493
494    def set_fiat_value(self, txid, ccy, text, fx, value_sat):
495        if not self.db.get_transaction(txid):
496            return
497        # since fx is inserting the thousands separator,
498        # and not util, also have fx remove it
499        text = fx.remove_thousands_separator(text)
500        def_fiat = self.default_fiat_value(txid, fx, value_sat)
501        formatted = fx.ccy_amount_str(def_fiat, commas=False)
502        def_fiat_rounded = Decimal(formatted)
503        reset = not text
504        if not reset:
505            try:
506                text_dec = Decimal(text)
507                text_dec_rounded = Decimal(fx.ccy_amount_str(text_dec, commas=False))
508                reset = text_dec_rounded == def_fiat_rounded
509            except:
510                # garbage. not resetting, but not saving either
511                return False
512        if reset:
513            d = self.fiat_value.get(ccy, {})
514            if d and txid in d:
515                d.pop(txid)
516            else:
517                # avoid saving empty dict
518                return True
519        else:
520            if ccy not in self.fiat_value:
521                self.fiat_value[ccy] = {}
522            self.fiat_value[ccy][txid] = text
523        return reset
524
525    def get_fiat_value(self, txid, ccy):
526        fiat_value = self.fiat_value.get(ccy, {}).get(txid)
527        try:
528            return Decimal(fiat_value)
529        except:
530            return
531
532    def is_mine(self, address) -> bool:
533        if not address: return False
534        return bool(self.get_address_index(address))
535
536    def is_change(self, address) -> bool:
537        if not self.is_mine(address):
538            return False
539        return self.get_address_index(address)[0] == 1
540
541    @abstractmethod
542    def get_address_index(self, address: str) -> Optional[AddressIndexGeneric]:
543        pass
544
545    @abstractmethod
546    def get_address_path_str(self, address: str) -> Optional[str]:
547        """Returns derivation path str such as "m/0/5" to address,
548        or None if not applicable.
549        """
550        pass
551
552    @abstractmethod
553    def get_redeem_script(self, address: str) -> Optional[str]:
554        pass
555
556    @abstractmethod
557    def get_witness_script(self, address: str) -> Optional[str]:
558        pass
559
560    @abstractmethod
561    def get_txin_type(self, address: str) -> str:
562        """Return script type of wallet address."""
563        pass
564
565    def export_private_key(self, address: str, password: Optional[str]) -> str:
566        if self.is_watching_only():
567            raise Exception(_("This is a watching-only wallet"))
568        if not is_address(address):
569            raise Exception(f"Invalid bitcoin address: {address}")
570        if not self.is_mine(address):
571            raise Exception(_('Address not in wallet.') + f' {address}')
572        index = self.get_address_index(address)
573        pk, compressed = self.keystore.get_private_key(index, password)
574        txin_type = self.get_txin_type(address)
575        serialized_privkey = bitcoin.serialize_privkey(pk, compressed, txin_type)
576        return serialized_privkey
577
578    def export_private_key_for_path(self, path: Union[Sequence[int], str], password: Optional[str]) -> str:
579        raise Exception("this wallet is not deterministic")
580
581    @abstractmethod
582    def get_public_keys(self, address: str) -> Sequence[str]:
583        pass
584
585    def get_public_keys_with_deriv_info(self, address: str) -> Dict[bytes, Tuple[KeyStoreWithMPK, Sequence[int]]]:
586        """Returns a map: pubkey -> (keystore, derivation_suffix)"""
587        return {}
588
589    def is_lightning_funding_tx(self, txid: Optional[str]) -> bool:
590        if not self.lnworker or txid is None:
591            return False
592        return any([chan.funding_outpoint.txid == txid
593                    for chan in self.lnworker.channels.values()])
594
595    def get_tx_info(self, tx: Transaction) -> TxWalletDetails:
596        tx_wallet_delta = self.get_wallet_delta(tx)
597        is_relevant = tx_wallet_delta.is_relevant
598        is_any_input_ismine = tx_wallet_delta.is_any_input_ismine
599        fee = tx_wallet_delta.fee
600        exp_n = None
601        can_broadcast = False
602        can_bump = False
603        can_cpfp = False
604        tx_hash = tx.txid()  # note: txid can be None! e.g. when called from GUI tx dialog
605        is_lightning_funding_tx = self.is_lightning_funding_tx(tx_hash)
606        tx_we_already_have_in_db = self.db.get_transaction(tx_hash)
607        can_save_as_local = (is_relevant and tx.txid() is not None
608                             and (tx_we_already_have_in_db is None or not tx_we_already_have_in_db.is_complete()))
609        label = ''
610        tx_mined_status = self.get_tx_height(tx_hash)
611        can_remove = ((tx_mined_status.height in [TX_HEIGHT_FUTURE, TX_HEIGHT_LOCAL])
612                      # otherwise 'height' is unreliable (typically LOCAL):
613                      and is_relevant
614                      # don't offer during common signing flow, e.g. when watch-only wallet starts creating a tx:
615                      and bool(tx_we_already_have_in_db))
616        can_dscancel = False
617        if tx.is_complete():
618            if tx_we_already_have_in_db:
619                label = self.get_label_for_txid(tx_hash)
620                if tx_mined_status.height > 0:
621                    if tx_mined_status.conf:
622                        status = _("{} confirmations").format(tx_mined_status.conf)
623                    else:
624                        status = _('Not verified')
625                elif tx_mined_status.height in (TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED):
626                    status = _('Unconfirmed')
627                    if fee is None:
628                        fee = self.get_tx_fee(tx_hash)
629                    if fee and self.network and self.config.has_fee_mempool():
630                        size = tx.estimated_size()
631                        fee_per_byte = fee / size
632                        exp_n = self.config.fee_to_depth(fee_per_byte)
633                    can_bump = is_any_input_ismine and not tx.is_final()
634                    can_dscancel = (is_any_input_ismine and not tx.is_final()
635                                    and not all([self.is_mine(txout.address) for txout in tx.outputs()]))
636                    try:
637                        self.cpfp(tx, 0)
638                        can_cpfp = True
639                    except:
640                        can_cpfp = False
641                else:
642                    status = _('Local')
643                    can_broadcast = self.network is not None
644                    can_bump = is_any_input_ismine and not tx.is_final()
645            else:
646                status = _("Signed")
647                can_broadcast = self.network is not None
648        else:
649            assert isinstance(tx, PartialTransaction)
650            s, r = tx.signature_count()
651            status = _("Unsigned") if s == 0 else _('Partially signed') + ' (%d/%d)'%(s,r)
652
653        if is_relevant:
654            if tx_wallet_delta.is_all_input_ismine:
655                assert fee is not None
656                amount = tx_wallet_delta.delta + fee
657            else:
658                amount = tx_wallet_delta.delta
659        else:
660            amount = None
661
662        if is_lightning_funding_tx:
663            can_bump = False  # would change txid
664
665        return TxWalletDetails(
666            txid=tx_hash,
667            status=status,
668            label=label,
669            can_broadcast=can_broadcast,
670            can_bump=can_bump,
671            can_cpfp=can_cpfp,
672            can_dscancel=can_dscancel,
673            can_save_as_local=can_save_as_local,
674            amount=amount,
675            fee=fee,
676            tx_mined_status=tx_mined_status,
677            mempool_depth_bytes=exp_n,
678            can_remove=can_remove,
679            is_lightning_funding_tx=is_lightning_funding_tx,
680        )
681
682    def get_spendable_coins(self, domain, *, nonlocal_only=False) -> Sequence[PartialTxInput]:
683        confirmed_only = self.config.get('confirmed_only', False)
684        with self._freeze_lock:
685            frozen_addresses = self._frozen_addresses.copy()
686        utxos = self.get_utxos(domain,
687                               excluded_addresses=frozen_addresses,
688                               mature_only=True,
689                               confirmed_funding_only=confirmed_only,
690                               nonlocal_only=nonlocal_only)
691        utxos = [utxo for utxo in utxos if not self.is_frozen_coin(utxo)]
692        return utxos
693
694    @abstractmethod
695    def get_receiving_addresses(self, *, slice_start=None, slice_stop=None) -> Sequence[str]:
696        pass
697
698    @abstractmethod
699    def get_change_addresses(self, *, slice_start=None, slice_stop=None) -> Sequence[str]:
700        pass
701
702    def dummy_address(self):
703        # first receiving address
704        return self.get_receiving_addresses(slice_start=0, slice_stop=1)[0]
705
706    def get_frozen_balance(self):
707        with self._freeze_lock:
708            frozen_addresses = self._frozen_addresses.copy()
709        # note: for coins, use is_frozen_coin instead of _frozen_coins,
710        #       as latter only contains *manually* frozen ones
711        frozen_coins = {utxo.prevout.to_str() for utxo in self.get_utxos()
712                        if self.is_frozen_coin(utxo)}
713        if not frozen_coins:  # shortcut
714            return self.get_balance(frozen_addresses)
715        c1, u1, x1 = self.get_balance()
716        c2, u2, x2 = self.get_balance(
717            excluded_addresses=frozen_addresses,
718            excluded_coins=frozen_coins,
719        )
720        return c1-c2, u1-u2, x1-x2
721
722    def balance_at_timestamp(self, domain, target_timestamp):
723        # we assume that get_history returns items ordered by block height
724        # we also assume that block timestamps are monotonic (which is false...!)
725        h = self.get_history(domain=domain)
726        balance = 0
727        for hist_item in h:
728            balance = hist_item.balance
729            if hist_item.tx_mined_status.timestamp is None or hist_item.tx_mined_status.timestamp > target_timestamp:
730                return balance - hist_item.delta
731        # return last balance
732        return balance
733
734    def get_onchain_history(self, *, domain=None):
735        monotonic_timestamp = 0
736        for hist_item in self.get_history(domain=domain):
737            monotonic_timestamp = max(monotonic_timestamp, (hist_item.tx_mined_status.timestamp or 999_999_999_999))
738            yield {
739                'txid': hist_item.txid,
740                'fee_sat': hist_item.fee,
741                'height': hist_item.tx_mined_status.height,
742                'confirmations': hist_item.tx_mined_status.conf,
743                'timestamp': hist_item.tx_mined_status.timestamp,
744                'monotonic_timestamp': monotonic_timestamp,
745                'incoming': True if hist_item.delta>0 else False,
746                'bc_value': Satoshis(hist_item.delta),
747                'bc_balance': Satoshis(hist_item.balance),
748                'date': timestamp_to_datetime(hist_item.tx_mined_status.timestamp),
749                'label': self.get_label_for_txid(hist_item.txid),
750                'txpos_in_block': hist_item.tx_mined_status.txpos,
751            }
752
753    def create_invoice(self, *, outputs: List[PartialTxOutput], message, pr, URI) -> Invoice:
754        height=self.get_local_height()
755        if pr:
756            return OnchainInvoice.from_bip70_payreq(pr, height)
757        if '!' in (x.value for x in outputs):
758            amount = '!'
759        else:
760            amount = sum(x.value for x in outputs)
761        timestamp = None
762        exp = None
763        if URI:
764            timestamp = URI.get('time')
765            exp = URI.get('exp')
766        timestamp = timestamp or int(time.time())
767        exp = exp or 0
768        _id = bh2u(sha256d(repr(outputs) + "%d"%timestamp))[0:10]
769        invoice = OnchainInvoice(
770            type=PR_TYPE_ONCHAIN,
771            amount_sat=amount,
772            outputs=outputs,
773            message=message,
774            id=_id,
775            time=timestamp,
776            exp=exp,
777            bip70=None,
778            requestor=None,
779            height=height,
780        )
781        return invoice
782
783    def save_invoice(self, invoice: Invoice) -> None:
784        key = self.get_key_for_outgoing_invoice(invoice)
785        if not invoice.is_lightning():
786            assert isinstance(invoice, OnchainInvoice)
787            if self.is_onchain_invoice_paid(invoice, 0):
788                self.logger.info("saving invoice... but it is already paid!")
789            with self.transaction_lock:
790                for txout in invoice.outputs:
791                    self._invoices_from_scriptpubkey_map[txout.scriptpubkey].add(key)
792        self.invoices[key] = invoice
793        self.save_db()
794
795    def clear_invoices(self):
796        self.invoices = {}
797        self.save_db()
798
799    def clear_requests(self):
800        self.receive_requests = {}
801        self.save_db()
802
803    def get_invoices(self):
804        out = list(self.invoices.values())
805        out.sort(key=lambda x:x.time)
806        return out
807
808    def get_unpaid_invoices(self):
809        invoices = self.get_invoices()
810        return [x for x in invoices if self.get_invoice_status(x) != PR_PAID]
811
812    def get_invoice(self, key):
813        return self.invoices.get(key)
814
815    def import_requests(self, path):
816        data = read_json_file(path)
817        for x in data:
818            req = Invoice.from_json(x)
819            self.add_payment_request(req)
820
821    def export_requests(self, path):
822        write_json_file(path, list(self.receive_requests.values()))
823
824    def import_invoices(self, path):
825        data = read_json_file(path)
826        for x in data:
827            invoice = Invoice.from_json(x)
828            self.save_invoice(invoice)
829
830    def export_invoices(self, path):
831        write_json_file(path, list(self.invoices.values()))
832
833    def _get_relevant_invoice_keys_for_tx(self, tx: Transaction) -> Set[str]:
834        relevant_invoice_keys = set()
835        with self.transaction_lock:
836            for txout in tx.outputs():
837                for invoice_key in self._invoices_from_scriptpubkey_map.get(txout.scriptpubkey, set()):
838                    # note: the invoice might have been deleted since, so check now:
839                    if invoice_key in self.invoices:
840                        relevant_invoice_keys.add(invoice_key)
841        return relevant_invoice_keys
842
843    def get_relevant_invoices_for_tx(self, tx: Transaction) -> Sequence[OnchainInvoice]:
844        invoice_keys = self._get_relevant_invoice_keys_for_tx(tx)
845        invoices = [self.get_invoice(key) for key in invoice_keys]
846        invoices = [inv for inv in invoices if inv]  # filter out None
847        for inv in invoices:
848            assert isinstance(inv, OnchainInvoice), f"unexpected type {type(inv)}"
849        return invoices
850
851    def _prepare_onchain_invoice_paid_detection(self):
852        # scriptpubkey -> list(invoice_keys)
853        self._invoices_from_scriptpubkey_map = defaultdict(set)  # type: Dict[bytes, Set[str]]
854        for invoice_key, invoice in self.invoices.items():
855            if invoice.type == PR_TYPE_ONCHAIN:
856                assert isinstance(invoice, OnchainInvoice)
857                for txout in invoice.outputs:
858                    self._invoices_from_scriptpubkey_map[txout.scriptpubkey].add(invoice_key)
859
860    def _is_onchain_invoice_paid(self, invoice: Invoice, conf: int) -> Tuple[bool, Sequence[str]]:
861        """Returns whether on-chain invoice is satisfied, and list of relevant TXIDs."""
862        assert invoice.type == PR_TYPE_ONCHAIN
863        assert isinstance(invoice, OnchainInvoice)
864        invoice_amounts = defaultdict(int)  # type: Dict[bytes, int]  # scriptpubkey -> value_sats
865        for txo in invoice.outputs:  # type: PartialTxOutput
866            invoice_amounts[txo.scriptpubkey] += 1 if txo.value == '!' else txo.value
867        relevant_txs = []
868        with self.transaction_lock:
869            for invoice_scriptpubkey, invoice_amt in invoice_amounts.items():
870                scripthash = bitcoin.script_to_scripthash(invoice_scriptpubkey.hex())
871                prevouts_and_values = self.db.get_prevouts_by_scripthash(scripthash)
872                total_received = 0
873                for prevout, v in prevouts_and_values:
874                    tx_height = self.get_tx_height(prevout.txid.hex())
875                    if tx_height.height > 0 and tx_height.height <= invoice.height:
876                        continue
877                    if tx_height.conf < conf:
878                        continue
879                    total_received += v
880                    relevant_txs.append(prevout.txid.hex())
881                # check that there is at least one TXO, and that they pay enough.
882                # note: "at least one TXO" check is needed for zero amount invoice (e.g. OP_RETURN)
883                if len(prevouts_and_values) == 0:
884                    return False, []
885                if total_received < invoice_amt:
886                    return False, []
887        return True, relevant_txs
888
889    def is_onchain_invoice_paid(self, invoice: Invoice, conf: int) -> bool:
890        return self._is_onchain_invoice_paid(invoice, conf)[0]
891
892    def _maybe_set_tx_label_based_on_invoices(self, tx: Transaction) -> bool:
893        # note: this is not done in 'get_default_label' as that would require deserializing each tx
894        tx_hash = tx.txid()
895        labels = []
896        for invoice in self.get_relevant_invoices_for_tx(tx):
897            if invoice.message:
898                labels.append(invoice.message)
899        if labels and not self._labels.get(tx_hash, ''):
900            self.set_label(tx_hash, "; ".join(labels))
901        return bool(labels)
902
903    def add_transaction(self, tx, *, allow_unrelated=False):
904        is_known = bool(self.db.get_transaction(tx.txid()))
905        tx_was_added = super().add_transaction(tx, allow_unrelated=allow_unrelated)
906        if tx_was_added and not is_known:
907            self._maybe_set_tx_label_based_on_invoices(tx)
908            if self.lnworker:
909                self.lnworker.maybe_add_backup_from_tx(tx)
910        return tx_was_added
911
912    @profiler
913    def get_full_history(self, fx=None, *, onchain_domain=None, include_lightning=True):
914        transactions_tmp = OrderedDictWithIndex()
915        # add on-chain txns
916        onchain_history = self.get_onchain_history(domain=onchain_domain)
917        for tx_item in onchain_history:
918            txid = tx_item['txid']
919            transactions_tmp[txid] = tx_item
920        # add lnworker onchain transactions
921        lnworker_history = self.lnworker.get_onchain_history() if self.lnworker and include_lightning else {}
922        for txid, item in lnworker_history.items():
923            if txid in transactions_tmp:
924                tx_item = transactions_tmp[txid]
925                tx_item['group_id'] = item.get('group_id')  # for swaps
926                tx_item['label'] = item['label']
927                tx_item['type'] = item['type']
928                ln_value = Decimal(item['amount_msat']) / 1000   # for channel open/close tx
929                tx_item['ln_value'] = Satoshis(ln_value)
930            else:
931                if item['type'] == 'swap':
932                    # swap items do not have all the fields. We can skip skip them
933                    # because they will eventually be in onchain_history
934                    # TODO: use attr.s objects instead of dicts
935                    continue
936                transactions_tmp[txid] = item
937                ln_value = Decimal(item['amount_msat']) / 1000   # for channel open/close tx
938                item['ln_value'] = Satoshis(ln_value)
939        # add lightning_transactions
940        lightning_history = self.lnworker.get_lightning_history() if self.lnworker and include_lightning else {}
941        for tx_item in lightning_history.values():
942            txid = tx_item.get('txid')
943            ln_value = Decimal(tx_item['amount_msat']) / 1000
944            tx_item['lightning'] = True
945            tx_item['ln_value'] = Satoshis(ln_value)
946            key = tx_item.get('txid') or tx_item['payment_hash']
947            transactions_tmp[key] = tx_item
948        # sort on-chain and LN stuff into new dict, by timestamp
949        # (we rely on this being a *stable* sort)
950        transactions = OrderedDictWithIndex()
951        for k, v in sorted(list(transactions_tmp.items()),
952                           key=lambda x: x[1].get('monotonic_timestamp') or x[1].get('timestamp') or float('inf')):
953            transactions[k] = v
954        now = time.time()
955        balance = 0
956        for item in transactions.values():
957            # add on-chain and lightning values
958            value = Decimal(0)
959            if item.get('bc_value'):
960                value += item['bc_value'].value
961            if item.get('ln_value'):
962                value += item.get('ln_value').value
963            # note: 'value' and 'balance' has msat precision (as LN has msat precision)
964            item['value'] = Satoshis(value)
965            balance += value
966            item['balance'] = Satoshis(balance)
967            if fx and fx.is_enabled() and fx.get_history_config():
968                txid = item.get('txid')
969                if not item.get('lightning') and txid:
970                    fiat_fields = self.get_tx_item_fiat(tx_hash=txid, amount_sat=value, fx=fx, tx_fee=item['fee_sat'])
971                    item.update(fiat_fields)
972                else:
973                    timestamp = item['timestamp'] or now
974                    fiat_value = value / Decimal(bitcoin.COIN) * fx.timestamp_rate(timestamp)
975                    item['fiat_value'] = Fiat(fiat_value, fx.ccy)
976                    item['fiat_default'] = True
977        return transactions
978
979    @profiler
980    def get_detailed_history(
981            self,
982            from_timestamp=None,
983            to_timestamp=None,
984            fx=None,
985            show_addresses=False,
986            from_height=None,
987            to_height=None):
988        # History with capital gains, using utxo pricing
989        # FIXME: Lightning capital gains would requires FIFO
990        if (from_timestamp is not None or to_timestamp is not None) \
991                and (from_height is not None or to_height is not None):
992            raise Exception('timestamp and block height based filtering cannot be used together')
993
994        show_fiat = fx and fx.is_enabled() and fx.get_history_config()
995        out = []
996        income = 0
997        expenditures = 0
998        capital_gains = Decimal(0)
999        fiat_income = Decimal(0)
1000        fiat_expenditures = Decimal(0)
1001        now = time.time()
1002        for item in self.get_onchain_history():
1003            timestamp = item['timestamp']
1004            if from_timestamp and (timestamp or now) < from_timestamp:
1005                continue
1006            if to_timestamp and (timestamp or now) >= to_timestamp:
1007                continue
1008            height = item['height']
1009            if from_height is not None and from_height > height > 0:
1010                continue
1011            if to_height is not None and (height >= to_height or height <= 0):
1012                continue
1013            tx_hash = item['txid']
1014            tx = self.db.get_transaction(tx_hash)
1015            tx_fee = item['fee_sat']
1016            item['fee'] = Satoshis(tx_fee) if tx_fee is not None else None
1017            if show_addresses:
1018                item['inputs'] = list(map(lambda x: x.to_json(), tx.inputs()))
1019                item['outputs'] = list(map(lambda x: {'address': x.get_ui_address_str(), 'value': Satoshis(x.value)},
1020                                           tx.outputs()))
1021            # fixme: use in and out values
1022            value = item['bc_value'].value
1023            if value < 0:
1024                expenditures += -value
1025            else:
1026                income += value
1027            # fiat computations
1028            if show_fiat:
1029                fiat_fields = self.get_tx_item_fiat(tx_hash=tx_hash, amount_sat=value, fx=fx, tx_fee=tx_fee)
1030                fiat_value = fiat_fields['fiat_value'].value
1031                item.update(fiat_fields)
1032                if value < 0:
1033                    capital_gains += fiat_fields['capital_gain'].value
1034                    fiat_expenditures += -fiat_value
1035                else:
1036                    fiat_income += fiat_value
1037            out.append(item)
1038        # add summary
1039        if out:
1040            first_item = out[0]
1041            last_item = out[-1]
1042            if from_height or to_height:
1043                start_height = from_height
1044                end_height = to_height
1045            else:
1046                start_height = first_item['height'] - 1
1047                end_height = last_item['height']
1048
1049            b = first_item['bc_balance'].value
1050            v = first_item['bc_value'].value
1051            start_balance = None if b is None or v is None else b - v
1052            end_balance = last_item['bc_balance'].value
1053
1054            if from_timestamp is not None and to_timestamp is not None:
1055                start_timestamp = from_timestamp
1056                end_timestamp = to_timestamp
1057            else:
1058                start_timestamp = first_item['timestamp']
1059                end_timestamp = last_item['timestamp']
1060
1061            start_coins = self.get_utxos(
1062                domain=None,
1063                block_height=start_height,
1064                confirmed_funding_only=True,
1065                confirmed_spending_only=True,
1066                nonlocal_only=True)
1067            end_coins = self.get_utxos(
1068                domain=None,
1069                block_height=end_height,
1070                confirmed_funding_only=True,
1071                confirmed_spending_only=True,
1072                nonlocal_only=True)
1073
1074            def summary_point(timestamp, height, balance, coins):
1075                date = timestamp_to_datetime(timestamp)
1076                out = {
1077                    'date': date,
1078                    'block_height': height,
1079                    'BTC_balance': Satoshis(balance),
1080                }
1081                if show_fiat:
1082                    ap = self.acquisition_price(coins, fx.timestamp_rate, fx.ccy)
1083                    lp = self.liquidation_price(coins, fx.timestamp_rate, timestamp)
1084                    out['acquisition_price'] = Fiat(ap, fx.ccy)
1085                    out['liquidation_price'] = Fiat(lp, fx.ccy)
1086                    out['unrealized_gains'] = Fiat(lp - ap, fx.ccy)
1087                    out['fiat_balance'] = Fiat(fx.historical_value(balance, date), fx.ccy)
1088                    out['BTC_fiat_price'] = Fiat(fx.historical_value(COIN, date), fx.ccy)
1089                return out
1090
1091            summary_start = summary_point(start_timestamp, start_height, start_balance, start_coins)
1092            summary_end = summary_point(end_timestamp, end_height, end_balance, end_coins)
1093            flow = {
1094                'BTC_incoming': Satoshis(income),
1095                'BTC_outgoing': Satoshis(expenditures)
1096            }
1097            if show_fiat:
1098                flow['fiat_currency'] = fx.ccy
1099                flow['fiat_incoming'] = Fiat(fiat_income, fx.ccy)
1100                flow['fiat_outgoing'] = Fiat(fiat_expenditures, fx.ccy)
1101                flow['realized_capital_gains'] = Fiat(capital_gains, fx.ccy)
1102            summary = {
1103                'begin': summary_start,
1104                'end': summary_end,
1105                'flow': flow,
1106            }
1107
1108        else:
1109            summary = {}
1110        return {
1111            'transactions': out,
1112            'summary': summary
1113        }
1114
1115    def acquisition_price(self, coins, price_func, ccy):
1116        return Decimal(sum(self.coin_price(coin.prevout.txid.hex(), price_func, ccy, self.get_txin_value(coin)) for coin in coins))
1117
1118    def liquidation_price(self, coins, price_func, timestamp):
1119        p = price_func(timestamp)
1120        return sum([coin.value_sats() for coin in coins]) * p / Decimal(COIN)
1121
1122    def default_fiat_value(self, tx_hash, fx, value_sat):
1123        return value_sat / Decimal(COIN) * self.price_at_timestamp(tx_hash, fx.timestamp_rate)
1124
1125    def get_tx_item_fiat(
1126            self,
1127            *,
1128            tx_hash: str,
1129            amount_sat: int,
1130            fx: 'FxThread',
1131            tx_fee: Optional[int],
1132    ) -> Dict[str, Any]:
1133        item = {}
1134        fiat_value = self.get_fiat_value(tx_hash, fx.ccy)
1135        fiat_default = fiat_value is None
1136        fiat_rate = self.price_at_timestamp(tx_hash, fx.timestamp_rate)
1137        fiat_value = fiat_value if fiat_value is not None else self.default_fiat_value(tx_hash, fx, amount_sat)
1138        fiat_fee = tx_fee / Decimal(COIN) * fiat_rate if tx_fee is not None else None
1139        item['fiat_currency'] = fx.ccy
1140        item['fiat_rate'] = Fiat(fiat_rate, fx.ccy)
1141        item['fiat_value'] = Fiat(fiat_value, fx.ccy)
1142        item['fiat_fee'] = Fiat(fiat_fee, fx.ccy) if fiat_fee is not None else None
1143        item['fiat_default'] = fiat_default
1144        if amount_sat < 0:
1145            acquisition_price = - amount_sat / Decimal(COIN) * self.average_price(tx_hash, fx.timestamp_rate, fx.ccy)
1146            liquidation_price = - fiat_value
1147            item['acquisition_price'] = Fiat(acquisition_price, fx.ccy)
1148            cg = liquidation_price - acquisition_price
1149            item['capital_gain'] = Fiat(cg, fx.ccy)
1150        return item
1151
1152    def get_label(self, key: str) -> str:
1153        # key is typically: address / txid / LN-payment-hash-hex
1154        return self._labels.get(key) or ''
1155
1156    def get_label_for_txid(self, tx_hash: str) -> str:
1157        return self._labels.get(tx_hash) or self._get_default_label_for_txid(tx_hash)
1158
1159    def _get_default_label_for_txid(self, tx_hash: str) -> str:
1160        # if no inputs are ismine, concat labels of output addresses
1161        if not self.db.get_txi_addresses(tx_hash):
1162            labels = []
1163            for addr in self.db.get_txo_addresses(tx_hash):
1164                label = self._labels.get(addr)
1165                if label:
1166                    labels.append(label)
1167            return ', '.join(labels)
1168        return ''
1169
1170    def get_all_labels(self) -> Dict[str, str]:
1171        with self.lock:
1172            return copy.copy(self._labels)
1173
1174    def get_tx_status(self, tx_hash, tx_mined_info: TxMinedInfo):
1175        extra = []
1176        height = tx_mined_info.height
1177        conf = tx_mined_info.conf
1178        timestamp = tx_mined_info.timestamp
1179        if height == TX_HEIGHT_FUTURE:
1180            assert conf < 0, conf
1181            num_blocks_remainining = -conf
1182            return 2, f'in {num_blocks_remainining} blocks'
1183        if conf == 0:
1184            tx = self.db.get_transaction(tx_hash)
1185            if not tx:
1186                return 2, 'unknown'
1187            is_final = tx and tx.is_final()
1188            if not is_final:
1189                extra.append('rbf')
1190            fee = self.get_tx_fee(tx_hash)
1191            if fee is not None:
1192                size = tx.estimated_size()
1193                fee_per_byte = fee / size
1194                extra.append(format_fee_satoshis(fee_per_byte) + ' sat/b')
1195            if fee is not None and height in (TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED) \
1196               and self.config.has_fee_mempool():
1197                exp_n = self.config.fee_to_depth(fee_per_byte)
1198                if exp_n is not None:
1199                    extra.append('%.2f MB'%(exp_n/1000000))
1200            if height == TX_HEIGHT_LOCAL:
1201                status = 3
1202            elif height == TX_HEIGHT_UNCONF_PARENT:
1203                status = 1
1204            elif height == TX_HEIGHT_UNCONFIRMED:
1205                status = 0
1206            else:
1207                status = 2  # not SPV verified
1208        else:
1209            status = 3 + min(conf, 6)
1210        time_str = format_time(timestamp) if timestamp else _("unknown")
1211        status_str = TX_STATUS[status] if status < 4 else time_str
1212        if extra:
1213            status_str += ' [%s]'%(', '.join(extra))
1214        return status, status_str
1215
1216    def relayfee(self):
1217        return relayfee(self.network)
1218
1219    def dust_threshold(self):
1220        return dust_threshold(self.network)
1221
1222    def get_unconfirmed_base_tx_for_batching(self) -> Optional[Transaction]:
1223        candidate = None
1224        for hist_item in self.get_history():
1225            # tx should not be mined yet
1226            if hist_item.tx_mined_status.conf > 0: continue
1227            # conservative future proofing of code: only allow known unconfirmed types
1228            if hist_item.tx_mined_status.height not in (TX_HEIGHT_UNCONFIRMED,
1229                                                        TX_HEIGHT_UNCONF_PARENT,
1230                                                        TX_HEIGHT_LOCAL):
1231                continue
1232            # tx should be "outgoing" from wallet
1233            if hist_item.delta >= 0:
1234                continue
1235            tx = self.db.get_transaction(hist_item.txid)
1236            if not tx:
1237                continue
1238            # is_mine outputs should not be spent yet
1239            # to avoid cancelling our own dependent transactions
1240            txid = tx.txid()
1241            if any([self.is_mine(o.address) and self.db.get_spent_outpoint(txid, output_idx)
1242                    for output_idx, o in enumerate(tx.outputs())]):
1243                continue
1244            # all inputs should be is_mine
1245            if not all([self.is_mine(self.get_txin_address(txin)) for txin in tx.inputs()]):
1246                continue
1247            # do not mutate LN funding txs, as that would change their txid
1248            if self.is_lightning_funding_tx(txid):
1249                continue
1250            # tx must have opted-in for RBF (even if local, for consistency)
1251            if tx.is_final():
1252                continue
1253            # prefer txns already in mempool (vs local)
1254            if hist_item.tx_mined_status.height == TX_HEIGHT_LOCAL:
1255                candidate = tx
1256                continue
1257            return tx
1258        return candidate
1259
1260    def get_change_addresses_for_new_transaction(
1261            self, preferred_change_addr=None, *, allow_reusing_used_change_addrs: bool = True,
1262    ) -> List[str]:
1263        change_addrs = []
1264        if preferred_change_addr:
1265            if isinstance(preferred_change_addr, (list, tuple)):
1266                change_addrs = list(preferred_change_addr)
1267            else:
1268                change_addrs = [preferred_change_addr]
1269        elif self.use_change:
1270            # Recalc and get unused change addresses
1271            addrs = self.calc_unused_change_addresses()
1272            # New change addresses are created only after a few
1273            # confirmations.
1274            if addrs:
1275                # if there are any unused, select all
1276                change_addrs = addrs
1277            else:
1278                # if there are none, take one randomly from the last few
1279                if not allow_reusing_used_change_addrs:
1280                    return []
1281                addrs = self.get_change_addresses(slice_start=-self.gap_limit_for_change)
1282                change_addrs = [random.choice(addrs)] if addrs else []
1283        for addr in change_addrs:
1284            assert is_address(addr), f"not valid bitcoin address: {addr}"
1285            # note that change addresses are not necessarily ismine
1286            # in which case this is a no-op
1287            self.check_address_for_corruption(addr)
1288        max_change = self.max_change_outputs if self.multiple_change else 1
1289        return change_addrs[:max_change]
1290
1291    def get_single_change_address_for_new_transaction(
1292            self, preferred_change_addr=None, *, allow_reusing_used_change_addrs: bool = True,
1293    ) -> Optional[str]:
1294        addrs = self.get_change_addresses_for_new_transaction(
1295            preferred_change_addr=preferred_change_addr,
1296            allow_reusing_used_change_addrs=allow_reusing_used_change_addrs,
1297        )
1298        if addrs:
1299            return addrs[0]
1300        return None
1301
1302    @check_returned_address_for_corruption
1303    def get_new_sweep_address_for_channel(self) -> str:
1304        # Recalc and get unused change addresses
1305        addrs = self.calc_unused_change_addresses()
1306        if addrs:
1307            selected_addr = addrs[0]
1308        else:
1309            # if there are none, take one randomly from the last few
1310            addrs = self.get_change_addresses(slice_start=-self.gap_limit_for_change)
1311            if addrs:
1312                selected_addr = random.choice(addrs)
1313            else:  # fallback for e.g. imported wallets
1314                selected_addr = self.get_receiving_address()
1315        assert is_address(selected_addr), f"not valid bitcoin address: {selected_addr}"
1316        return selected_addr
1317
1318    def make_unsigned_transaction(
1319            self, *,
1320            coins: Sequence[PartialTxInput],
1321            outputs: List[PartialTxOutput],
1322            fee=None,
1323            change_addr: str = None,
1324            is_sweep=False,
1325            rbf=False) -> PartialTransaction:
1326
1327        if not coins:  # any bitcoin tx must have at least 1 input by consensus
1328            raise NotEnoughFunds()
1329        if any([c.already_has_some_signatures() for c in coins]):
1330            raise Exception("Some inputs already contain signatures!")
1331
1332        # prevent side-effect with '!'
1333        outputs = copy.deepcopy(outputs)
1334
1335        # check outputs
1336        i_max = None
1337        for i, o in enumerate(outputs):
1338            if o.value == '!':
1339                if i_max is not None:
1340                    raise MultipleSpendMaxTxOutputs()
1341                i_max = i
1342
1343        if fee is None and self.config.fee_per_kb() is None:
1344            raise NoDynamicFeeEstimates()
1345
1346        for item in coins:
1347            self.add_input_info(item)
1348
1349        # Fee estimator
1350        if fee is None:
1351            fee_estimator = self.config.estimate_fee
1352        elif isinstance(fee, Number):
1353            fee_estimator = lambda size: fee
1354        elif callable(fee):
1355            fee_estimator = fee
1356        else:
1357            raise Exception(f'Invalid argument fee: {fee}')
1358
1359        if i_max is None:
1360            # Let the coin chooser select the coins to spend
1361            coin_chooser = coinchooser.get_coin_chooser(self.config)
1362            # If there is an unconfirmed RBF tx, merge with it
1363            base_tx = self.get_unconfirmed_base_tx_for_batching()
1364            if self.config.get('batch_rbf', False) and base_tx:
1365                # make sure we don't try to spend change from the tx-to-be-replaced:
1366                coins = [c for c in coins if c.prevout.txid.hex() != base_tx.txid()]
1367                is_local = self.get_tx_height(base_tx.txid()).height == TX_HEIGHT_LOCAL
1368                base_tx = PartialTransaction.from_tx(base_tx)
1369                base_tx.add_info_from_wallet(self)
1370                base_tx_fee = base_tx.get_fee()
1371                relayfeerate = Decimal(self.relayfee()) / 1000
1372                original_fee_estimator = fee_estimator
1373                def fee_estimator(size: Union[int, float, Decimal]) -> int:
1374                    size = Decimal(size)
1375                    lower_bound = base_tx_fee + round(size * relayfeerate)
1376                    lower_bound = lower_bound if not is_local else 0
1377                    return int(max(lower_bound, original_fee_estimator(size)))
1378                txi = base_tx.inputs()
1379                txo = list(filter(lambda o: not self.is_change(o.address), base_tx.outputs()))
1380                old_change_addrs = [o.address for o in base_tx.outputs() if self.is_change(o.address)]
1381            else:
1382                txi = []
1383                txo = []
1384                old_change_addrs = []
1385            # change address. if empty, coin_chooser will set it
1386            change_addrs = self.get_change_addresses_for_new_transaction(change_addr or old_change_addrs)
1387            tx = coin_chooser.make_tx(
1388                coins=coins,
1389                inputs=txi,
1390                outputs=list(outputs) + txo,
1391                change_addrs=change_addrs,
1392                fee_estimator_vb=fee_estimator,
1393                dust_threshold=self.dust_threshold())
1394        else:
1395            # "spend max" branch
1396            # note: This *will* spend inputs with negative effective value (if there are any).
1397            #       Given as the user is spending "max", and so might be abandoning the wallet,
1398            #       try to include all UTXOs, otherwise leftover might remain in the UTXO set
1399            #       forever. see #5433
1400            # note: Actually it might be the case that not all UTXOs from the wallet are
1401            #       being spent if the user manually selected UTXOs.
1402            sendable = sum(map(lambda c: c.value_sats(), coins))
1403            outputs[i_max].value = 0
1404            tx = PartialTransaction.from_io(list(coins), list(outputs))
1405            fee = fee_estimator(tx.estimated_size())
1406            amount = sendable - tx.output_value() - fee
1407            if amount < 0:
1408                raise NotEnoughFunds()
1409            outputs[i_max].value = amount
1410            tx = PartialTransaction.from_io(list(coins), list(outputs))
1411
1412        # Timelock tx to current height.
1413        tx.locktime = get_locktime_for_new_transaction(self.network)
1414
1415        tx.set_rbf(rbf)
1416        tx.add_info_from_wallet(self)
1417        run_hook('make_unsigned_transaction', self, tx)
1418        return tx
1419
1420    def mktx(self, *,
1421             outputs: List[PartialTxOutput],
1422             password=None, fee=None, change_addr=None,
1423             domain=None, rbf=False, nonlocal_only=False,
1424             tx_version=None, sign=True) -> PartialTransaction:
1425        coins = self.get_spendable_coins(domain, nonlocal_only=nonlocal_only)
1426        tx = self.make_unsigned_transaction(
1427            coins=coins,
1428            outputs=outputs,
1429            fee=fee,
1430            change_addr=change_addr,
1431            rbf=rbf)
1432        if tx_version is not None:
1433            tx.version = tx_version
1434        if sign:
1435            self.sign_transaction(tx, password)
1436        return tx
1437
1438    def is_frozen_address(self, addr: str) -> bool:
1439        return addr in self._frozen_addresses
1440
1441    def is_frozen_coin(self, utxo: PartialTxInput) -> bool:
1442        prevout_str = utxo.prevout.to_str()
1443        frozen = self._frozen_coins.get(prevout_str, None)
1444        # note: there are three possible states for 'frozen':
1445        #       True/False if the user explicitly set it,
1446        #       None otherwise
1447        if frozen is None:
1448            return self._is_coin_small_and_unconfirmed(utxo)
1449        return bool(frozen)
1450
1451    def _is_coin_small_and_unconfirmed(self, utxo: PartialTxInput) -> bool:
1452        """If true, the coin should not be spent.
1453        The idea here is that an attacker might send us a UTXO in a
1454        large low-fee unconfirmed tx that will ~never confirm. If we
1455        spend it as part of a tx ourselves, that too will not confirm
1456        (unless we use a high fee but that might not be worth it for
1457        a small value UTXO).
1458        In particular, this test triggers for large "dusting transactions"
1459        that are used for advertising purposes by some entities.
1460        see #6960
1461        """
1462        # confirmed UTXOs are fine; check this first for performance:
1463        block_height = utxo.block_height
1464        assert block_height is not None
1465        if block_height > 0:
1466            return False
1467        # exempt large value UTXOs
1468        value_sats = utxo.value_sats()
1469        assert value_sats is not None
1470        threshold = self.config.get('unconf_utxo_freeze_threshold', 5_000)
1471        if value_sats >= threshold:
1472            return False
1473        # if funding tx has any is_mine input, then UTXO is fine
1474        funding_tx = self.db.get_transaction(utxo.prevout.txid.hex())
1475        if funding_tx is None:
1476            # we should typically have the funding tx available;
1477            # might not have it e.g. while not up_to_date
1478            return True
1479        if any(self.is_mine(self.get_txin_address(txin))
1480               for txin in funding_tx.inputs()):
1481            return False
1482        return True
1483
1484    def set_frozen_state_of_addresses(self, addrs: Sequence[str], freeze: bool) -> bool:
1485        """Set frozen state of the addresses to FREEZE, True or False"""
1486        if all(self.is_mine(addr) for addr in addrs):
1487            with self._freeze_lock:
1488                if freeze:
1489                    self._frozen_addresses |= set(addrs)
1490                else:
1491                    self._frozen_addresses -= set(addrs)
1492                self.db.put('frozen_addresses', list(self._frozen_addresses))
1493                return True
1494        return False
1495
1496    def set_frozen_state_of_coins(self, utxos: Sequence[str], freeze: bool) -> None:
1497        """Set frozen state of the utxos to FREEZE, True or False"""
1498        # basic sanity check that input is not garbage: (see if raises)
1499        [TxOutpoint.from_str(utxo) for utxo in utxos]
1500        with self._freeze_lock:
1501            for utxo in utxos:
1502                self._frozen_coins[utxo] = bool(freeze)
1503
1504    def is_address_reserved(self, addr: str) -> bool:
1505        # note: atm 'reserved' status is only taken into consideration for 'change addresses'
1506        return addr in self._reserved_addresses
1507
1508    def set_reserved_state_of_address(self, addr: str, *, reserved: bool) -> None:
1509        if not self.is_mine(addr):
1510            return
1511        with self.lock:
1512            if reserved:
1513                self._reserved_addresses.add(addr)
1514            else:
1515                self._reserved_addresses.discard(addr)
1516            self.db.put('reserved_addresses', list(self._reserved_addresses))
1517
1518    def can_export(self):
1519        return not self.is_watching_only() and hasattr(self.keystore, 'get_private_key')
1520
1521    def address_is_old(self, address: str, *, req_conf: int = 3) -> bool:
1522        """Returns whether address has any history that is deeply confirmed.
1523        Used for reorg-safe(ish) gap limit roll-forward.
1524        """
1525        max_conf = -1
1526        h = self.db.get_addr_history(address)
1527        needs_spv_check = not self.config.get("skipmerklecheck", False)
1528        for tx_hash, tx_height in h:
1529            if needs_spv_check:
1530                tx_age = self.get_tx_height(tx_hash).conf
1531            else:
1532                if tx_height <= 0:
1533                    tx_age = 0
1534                else:
1535                    tx_age = self.get_local_height() - tx_height + 1
1536            max_conf = max(max_conf, tx_age)
1537        return max_conf >= req_conf
1538
1539    def bump_fee(
1540            self,
1541            *,
1542            tx: Transaction,
1543            txid: str = None,
1544            new_fee_rate: Union[int, float, Decimal],
1545            coins: Sequence[PartialTxInput] = None,
1546            strategies: Sequence[BumpFeeStrategy] = None,
1547    ) -> PartialTransaction:
1548        """Increase the miner fee of 'tx'.
1549        'new_fee_rate' is the target min rate in sat/vbyte
1550        'coins' is a list of UTXOs we can choose from as potential new inputs to be added
1551        """
1552        txid = txid or tx.txid()
1553        assert txid
1554        assert tx.txid() in (None, txid)
1555        if not isinstance(tx, PartialTransaction):
1556            tx = PartialTransaction.from_tx(tx)
1557        assert isinstance(tx, PartialTransaction)
1558        tx.remove_signatures()
1559        if tx.is_final():
1560            raise CannotBumpFee(_('Transaction is final'))
1561        new_fee_rate = quantize_feerate(new_fee_rate)  # strip excess precision
1562        try:
1563            # note: this might download input utxos over network
1564            tx.add_info_from_wallet(self, ignore_network_issues=False)
1565        except NetworkException as e:
1566            raise CannotBumpFee(repr(e))
1567        old_tx_size = tx.estimated_size()
1568        old_fee = tx.get_fee()
1569        assert old_fee is not None
1570        old_fee_rate = old_fee / old_tx_size  # sat/vbyte
1571        if new_fee_rate <= old_fee_rate:
1572            raise CannotBumpFee(_("The new fee rate needs to be higher than the old fee rate."))
1573
1574        if not strategies:
1575            strategies = [BumpFeeStrategy.COINCHOOSER, BumpFeeStrategy.DECREASE_CHANGE]
1576        tx_new = None
1577        exc = None
1578        for strat in strategies:
1579            try:
1580                if strat == BumpFeeStrategy.COINCHOOSER:
1581                    tx_new = self._bump_fee_through_coinchooser(
1582                        tx=tx,
1583                        txid=txid,
1584                        new_fee_rate=new_fee_rate,
1585                        coins=coins,
1586                    )
1587                elif strat == BumpFeeStrategy.DECREASE_CHANGE:
1588                    tx_new = self._bump_fee_through_decreasing_change(
1589                        tx=tx, new_fee_rate=new_fee_rate)
1590                elif strat == BumpFeeStrategy.DECREASE_PAYMENT:
1591                    tx_new = self._bump_fee_through_decreasing_payment(
1592                        tx=tx, new_fee_rate=new_fee_rate)
1593                else:
1594                    raise NotImplementedError(f"unexpected strategy: {strat}")
1595            except CannotBumpFee as e:
1596                exc = e
1597            else:
1598                strat_used = strat
1599                break
1600        if tx_new is None:
1601            assert exc
1602            raise exc  # all strategies failed, re-raise last exception
1603
1604        target_min_fee = new_fee_rate * tx_new.estimated_size()
1605        actual_fee = tx_new.get_fee()
1606        if actual_fee + 1 < target_min_fee:
1607            raise CannotBumpFee(
1608                f"bump_fee fee target was not met (strategy: {strat_used}). "
1609                f"got {actual_fee}, expected >={target_min_fee}. "
1610                f"target rate was {new_fee_rate}")
1611        tx_new.locktime = get_locktime_for_new_transaction(self.network)
1612        tx_new.set_rbf(True)
1613        tx_new.add_info_from_wallet(self)
1614        return tx_new
1615
1616    def _bump_fee_through_coinchooser(
1617            self,
1618            *,
1619            tx: PartialTransaction,
1620            txid: str,
1621            new_fee_rate: Union[int, Decimal],
1622            coins: Sequence[PartialTxInput] = None,
1623    ) -> PartialTransaction:
1624        """Increase the miner fee of 'tx'.
1625
1626        - keeps all inputs
1627        - keeps all not is_mine outputs,
1628        - allows adding new inputs
1629        """
1630        assert txid
1631        tx = copy.deepcopy(tx)
1632        tx.add_info_from_wallet(self)
1633        assert tx.get_fee() is not None
1634        old_inputs = list(tx.inputs())
1635        old_outputs = list(tx.outputs())
1636        # change address
1637        old_change_addrs = [o.address for o in old_outputs if self.is_change(o.address)]
1638        change_addrs = self.get_change_addresses_for_new_transaction(old_change_addrs)
1639        # which outputs to keep?
1640        if old_change_addrs:
1641            fixed_outputs = list(filter(lambda o: not self.is_change(o.address), old_outputs))
1642        else:
1643            if all(self.is_mine(o.address) for o in old_outputs):
1644                # all outputs are is_mine and none of them are change.
1645                # we bail out as it's unclear what the user would want!
1646                # the coinchooser bump fee method is probably not a good idea in this case
1647                raise CannotBumpFee(_('All outputs are non-change is_mine'))
1648            old_not_is_mine = list(filter(lambda o: not self.is_mine(o.address), old_outputs))
1649            if old_not_is_mine:
1650                fixed_outputs = old_not_is_mine
1651            else:
1652                fixed_outputs = old_outputs
1653        if not fixed_outputs:
1654            raise CannotBumpFee(_('Could not figure out which outputs to keep'))
1655
1656        if coins is None:
1657            coins = self.get_spendable_coins(None)
1658        # make sure we don't try to spend output from the tx-to-be-replaced:
1659        coins = [c for c in coins if c.prevout.txid.hex() != txid]
1660        for item in coins:
1661            self.add_input_info(item)
1662        def fee_estimator(size):
1663            return self.config.estimate_fee_for_feerate(fee_per_kb=new_fee_rate*1000, size=size)
1664        coin_chooser = coinchooser.get_coin_chooser(self.config)
1665        try:
1666            return coin_chooser.make_tx(
1667                coins=coins,
1668                inputs=old_inputs,
1669                outputs=fixed_outputs,
1670                change_addrs=change_addrs,
1671                fee_estimator_vb=fee_estimator,
1672                dust_threshold=self.dust_threshold())
1673        except NotEnoughFunds as e:
1674            raise CannotBumpFee(e)
1675
1676    def _bump_fee_through_decreasing_change(
1677            self,
1678            *,
1679            tx: PartialTransaction,
1680            new_fee_rate: Union[int, Decimal],
1681    ) -> PartialTransaction:
1682        """Increase the miner fee of 'tx'.
1683
1684        - keeps all inputs
1685        - no new inputs are added
1686        - allows decreasing and removing outputs (change is decreased first)
1687        This is less "safe" than "coinchooser" method as it might end up decreasing
1688        e.g. a payment to a merchant; but e.g. if the user has sent "Max" previously,
1689        this is the only way to RBF.
1690        """
1691        tx = copy.deepcopy(tx)
1692        tx.add_info_from_wallet(self)
1693        assert tx.get_fee() is not None
1694        inputs = tx.inputs()
1695        outputs = tx._outputs  # note: we will mutate this directly
1696
1697        # use own outputs
1698        s = list(filter(lambda o: self.is_mine(o.address), outputs))
1699        # ... unless there is none
1700        if not s:
1701            s = outputs
1702            x_fee = run_hook('get_tx_extra_fee', self, tx)
1703            if x_fee:
1704                x_fee_address, x_fee_amount = x_fee
1705                s = list(filter(lambda o: o.address != x_fee_address, s))
1706        if not s:
1707            raise CannotBumpFee('No outputs at all??')
1708
1709        # prioritize low value outputs, to get rid of dust
1710        s = sorted(s, key=lambda o: o.value)
1711        for o in s:
1712            target_fee = int(math.ceil(tx.estimated_size() * new_fee_rate))
1713            delta = target_fee - tx.get_fee()
1714            i = outputs.index(o)
1715            if o.value - delta >= self.dust_threshold():
1716                new_output_value = o.value - delta
1717                assert isinstance(new_output_value, int)
1718                outputs[i].value = new_output_value
1719                delta = 0
1720                break
1721            else:
1722                del outputs[i]
1723                # note: we mutated the outputs of tx, which will affect
1724                #       tx.estimated_size() in the next iteration
1725        if delta > 0:
1726            raise CannotBumpFee(_('Could not find suitable outputs'))
1727
1728        return PartialTransaction.from_io(inputs, outputs)
1729
1730    def _bump_fee_through_decreasing_payment(
1731            self,
1732            *,
1733            tx: PartialTransaction,
1734            new_fee_rate: Union[int, Decimal],
1735    ) -> PartialTransaction:
1736        """Increase the miner fee of 'tx'.
1737
1738        - keeps all inputs
1739        - no new inputs are added
1740        - decreases payment outputs (not change!). Each non-ismine output is decreased
1741          proportionally to their byte-size.
1742        """
1743        tx = copy.deepcopy(tx)
1744        tx.add_info_from_wallet(self)
1745        assert tx.get_fee() is not None
1746        inputs = tx.inputs()
1747        outputs = tx.outputs()
1748
1749        # select non-ismine outputs
1750        s = [(idx, out) for (idx, out) in enumerate(outputs)
1751             if not self.is_mine(out.address)]
1752        # exempt 2fa fee output if present
1753        x_fee = run_hook('get_tx_extra_fee', self, tx)
1754        if x_fee:
1755            x_fee_address, x_fee_amount = x_fee
1756            s = [(idx, out) for (idx, out) in s if out.address != x_fee_address]
1757        if not s:
1758            raise CannotBumpFee("Cannot find payment output")
1759
1760        del_out_idxs = set()
1761        tx_size = tx.estimated_size()
1762        cur_fee = tx.get_fee()
1763        # Main loop. Each iteration decreases value of all selected outputs.
1764        # The number of iterations is bounded by len(s) as only the final iteration
1765        # can *not remove* any output.
1766        for __ in range(len(s) + 1):
1767            target_fee = int(math.ceil(tx_size * new_fee_rate))
1768            delta_total = target_fee - cur_fee
1769            if delta_total <= 0:
1770                break
1771            out_size_total = sum(Transaction.estimated_output_size_for_script(out.scriptpubkey.hex())
1772                                 for (idx, out) in s if idx not in del_out_idxs)
1773            for idx, out in s:
1774                out_size = Transaction.estimated_output_size_for_script(out.scriptpubkey.hex())
1775                delta = int(math.ceil(delta_total * out_size / out_size_total))
1776                if out.value - delta >= self.dust_threshold():
1777                    new_output_value = out.value - delta
1778                    assert isinstance(new_output_value, int)
1779                    outputs[idx].value = new_output_value
1780                    cur_fee += delta
1781                else:  # remove output
1782                    tx_size -= out_size
1783                    cur_fee += out.value
1784                    del_out_idxs.add(idx)
1785        if delta_total > 0:
1786            raise CannotBumpFee(_('Could not find suitable outputs'))
1787
1788        outputs = [out for (idx, out) in enumerate(outputs) if idx not in del_out_idxs]
1789        return PartialTransaction.from_io(inputs, outputs)
1790
1791    def cpfp(self, tx: Transaction, fee: int) -> Optional[PartialTransaction]:
1792        txid = tx.txid()
1793        for i, o in enumerate(tx.outputs()):
1794            address, value = o.address, o.value
1795            if self.is_mine(address):
1796                break
1797        else:
1798            raise CannotCPFP(_("Could not find suitable output"))
1799        coins = self.get_addr_utxo(address)
1800        item = coins.get(TxOutpoint.from_str(txid+':%d'%i))
1801        if not item:
1802            raise CannotCPFP(_("Could not find coins for output"))
1803        inputs = [item]
1804        out_address = (self.get_single_change_address_for_new_transaction(allow_reusing_used_change_addrs=False)
1805                       or self.get_unused_address()
1806                       or address)
1807        output_value = value - fee
1808        if output_value < self.dust_threshold():
1809            raise CannotCPFP(_("The output value remaining after fee is too low."))
1810        outputs = [PartialTxOutput.from_address_and_value(out_address, output_value)]
1811        locktime = get_locktime_for_new_transaction(self.network)
1812        tx_new = PartialTransaction.from_io(inputs, outputs, locktime=locktime)
1813        tx_new.set_rbf(True)
1814        tx_new.add_info_from_wallet(self)
1815        return tx_new
1816
1817    def dscancel(
1818            self, *, tx: Transaction, new_fee_rate: Union[int, float, Decimal]
1819    ) -> PartialTransaction:
1820        """Double-Spend-Cancel: cancel an unconfirmed tx by double-spending
1821        its inputs, paying ourselves.
1822        'new_fee_rate' is the target min rate in sat/vbyte
1823        """
1824        if not isinstance(tx, PartialTransaction):
1825            tx = PartialTransaction.from_tx(tx)
1826        assert isinstance(tx, PartialTransaction)
1827        tx.remove_signatures()
1828
1829        if tx.is_final():
1830            raise CannotDoubleSpendTx(_('Transaction is final'))
1831        new_fee_rate = quantize_feerate(new_fee_rate)  # strip excess precision
1832        try:
1833            # note: this might download input utxos over network
1834            tx.add_info_from_wallet(self, ignore_network_issues=False)
1835        except NetworkException as e:
1836            raise CannotDoubleSpendTx(repr(e))
1837        old_tx_size = tx.estimated_size()
1838        old_fee = tx.get_fee()
1839        assert old_fee is not None
1840        old_fee_rate = old_fee / old_tx_size  # sat/vbyte
1841        if new_fee_rate <= old_fee_rate:
1842            raise CannotDoubleSpendTx(_("The new fee rate needs to be higher than the old fee rate."))
1843        # grab all ismine inputs
1844        inputs = [txin for txin in tx.inputs()
1845                  if self.is_mine(self.get_txin_address(txin))]
1846        value = sum([txin.value_sats() for txin in inputs])
1847        # figure out output address
1848        old_change_addrs = [o.address for o in tx.outputs() if self.is_mine(o.address)]
1849        out_address = (self.get_single_change_address_for_new_transaction(old_change_addrs)
1850                       or self.get_receiving_address())
1851        locktime = get_locktime_for_new_transaction(self.network)
1852        outputs = [PartialTxOutput.from_address_and_value(out_address, value)]
1853        tx_new = PartialTransaction.from_io(inputs, outputs, locktime=locktime)
1854        new_tx_size = tx_new.estimated_size()
1855        new_fee = max(
1856            new_fee_rate * new_tx_size,
1857            old_fee + self.relayfee() * new_tx_size / Decimal(1000),  # BIP-125 rules 3 and 4
1858        )
1859        new_fee = int(math.ceil(new_fee))
1860        output_value = value - new_fee
1861        if output_value < self.dust_threshold():
1862            raise CannotDoubleSpendTx(_("The output value remaining after fee is too low."))
1863        outputs = [PartialTxOutput.from_address_and_value(out_address, value - new_fee)]
1864        tx_new = PartialTransaction.from_io(inputs, outputs, locktime=locktime)
1865        tx_new.set_rbf(True)
1866        tx_new.add_info_from_wallet(self)
1867        return tx_new
1868
1869    @abstractmethod
1870    def _add_input_sig_info(self, txin: PartialTxInput, address: str, *, only_der_suffix: bool) -> None:
1871        pass
1872
1873    def _add_txinout_derivation_info(self, txinout: Union[PartialTxInput, PartialTxOutput],
1874                                     address: str, *, only_der_suffix: bool) -> None:
1875        pass  # implemented by subclasses
1876
1877    def _add_input_utxo_info(
1878            self,
1879            txin: PartialTxInput,
1880            *,
1881            address: str = None,
1882            ignore_network_issues: bool = True,
1883    ) -> None:
1884        # We prefer to include UTXO (full tx) for every input.
1885        # We cannot include UTXO if the prev tx is not signed yet though (chain of unsigned txs),
1886        # in which case we might include a WITNESS_UTXO.
1887        address = address or txin.address
1888        if txin.witness_utxo is None and txin.is_segwit() and address:
1889            received, spent = self.get_addr_io(address)
1890            item = received.get(txin.prevout.to_str())
1891            if item:
1892                txin_value = item[1]
1893                txin.witness_utxo = TxOutput.from_address_and_value(address, txin_value)
1894        if txin.utxo is None:
1895            txin.utxo = self.get_input_tx(txin.prevout.txid.hex(), ignore_network_issues=ignore_network_issues)
1896        txin.ensure_there_is_only_one_utxo()
1897
1898    def _learn_derivation_path_for_address_from_txinout(self, txinout: Union[PartialTxInput, PartialTxOutput],
1899                                                        address: str) -> bool:
1900        """Tries to learn the derivation path for an address (potentially beyond gap limit)
1901        using data available in given txin/txout.
1902        Returns whether the address was found to be is_mine.
1903        """
1904        return False  # implemented by subclasses
1905
1906    def add_input_info(
1907            self,
1908            txin: PartialTxInput,
1909            *,
1910            only_der_suffix: bool = False,
1911            ignore_network_issues: bool = True,
1912    ) -> None:
1913        address = self.get_txin_address(txin)
1914        # note: we add input utxos regardless of is_mine
1915        self._add_input_utxo_info(txin, ignore_network_issues=ignore_network_issues, address=address)
1916        if not self.is_mine(address):
1917            is_mine = self._learn_derivation_path_for_address_from_txinout(txin, address)
1918            if not is_mine:
1919                return
1920        # set script_type first, as later checks might rely on it:
1921        txin.script_type = self.get_txin_type(address)
1922        txin.num_sig = self.m if isinstance(self, Multisig_Wallet) else 1
1923        if txin.redeem_script is None:
1924            try:
1925                redeem_script_hex = self.get_redeem_script(address)
1926                txin.redeem_script = bfh(redeem_script_hex) if redeem_script_hex else None
1927            except UnknownTxinType:
1928                pass
1929        if txin.witness_script is None:
1930            try:
1931                witness_script_hex = self.get_witness_script(address)
1932                txin.witness_script = bfh(witness_script_hex) if witness_script_hex else None
1933            except UnknownTxinType:
1934                pass
1935        self._add_input_sig_info(txin, address, only_der_suffix=only_der_suffix)
1936
1937    def can_sign(self, tx: Transaction) -> bool:
1938        if not isinstance(tx, PartialTransaction):
1939            return False
1940        if tx.is_complete():
1941            return False
1942        # add info to inputs if we can; otherwise we might return a false negative:
1943        tx.add_info_from_wallet(self)
1944        for txin in tx.inputs():
1945            # note: is_mine check needed to avoid false positives.
1946            #       just because keystore could sign, txin does not necessarily belong to wallet.
1947            #       Example: we have p2pkh-like addresses and txin is a multisig that involves our pubkey.
1948            if not self.is_mine(txin.address):
1949                continue
1950            for k in self.get_keystores():
1951                if k.can_sign_txin(txin):
1952                    return True
1953        return False
1954
1955    def get_input_tx(self, tx_hash: str, *, ignore_network_issues=False) -> Optional[Transaction]:
1956        # First look up an input transaction in the wallet where it
1957        # will likely be.  If co-signing a transaction it may not have
1958        # all the input txs, in which case we ask the network.
1959        tx = self.db.get_transaction(tx_hash)
1960        if not tx and self.network and self.network.has_internet_connection():
1961            try:
1962                raw_tx = self.network.run_from_another_thread(
1963                    self.network.get_transaction(tx_hash, timeout=10))
1964            except NetworkException as e:
1965                self.logger.info(f'got network error getting input txn. err: {repr(e)}. txid: {tx_hash}. '
1966                                 f'if you are intentionally offline, consider using the --offline flag')
1967                if not ignore_network_issues:
1968                    raise e
1969            else:
1970                tx = Transaction(raw_tx)
1971        if not tx and not ignore_network_issues:
1972            raise NetworkException('failed to get prev tx from network')
1973        return tx
1974
1975    def add_output_info(self, txout: PartialTxOutput, *, only_der_suffix: bool = False) -> None:
1976        address = txout.address
1977        if not self.is_mine(address):
1978            is_mine = self._learn_derivation_path_for_address_from_txinout(txout, address)
1979            if not is_mine:
1980                return
1981        txout.script_type = self.get_txin_type(address)
1982        txout.is_mine = True
1983        txout.is_change = self.is_change(address)
1984        if isinstance(self, Multisig_Wallet):
1985            txout.num_sig = self.m
1986        self._add_txinout_derivation_info(txout, address, only_der_suffix=only_der_suffix)
1987        if txout.redeem_script is None:
1988            try:
1989                redeem_script_hex = self.get_redeem_script(address)
1990                txout.redeem_script = bfh(redeem_script_hex) if redeem_script_hex else None
1991            except UnknownTxinType:
1992                pass
1993        if txout.witness_script is None:
1994            try:
1995                witness_script_hex = self.get_witness_script(address)
1996                txout.witness_script = bfh(witness_script_hex) if witness_script_hex else None
1997            except UnknownTxinType:
1998                pass
1999
2000    def sign_transaction(self, tx: Transaction, password) -> Optional[PartialTransaction]:
2001        if self.is_watching_only():
2002            return
2003        if not isinstance(tx, PartialTransaction):
2004            return
2005        # add info to a temporary tx copy; including xpubs
2006        # and full derivation paths as hw keystores might want them
2007        tmp_tx = copy.deepcopy(tx)
2008        tmp_tx.add_info_from_wallet(self, include_xpubs=True)
2009        # sign. start with ready keystores.
2010        for k in sorted(self.get_keystores(), key=lambda ks: ks.ready_to_sign(), reverse=True):
2011            try:
2012                if k.can_sign(tmp_tx):
2013                    k.sign_transaction(tmp_tx, password)
2014            except UserCancelled:
2015                continue
2016        # remove sensitive info; then copy back details from temporary tx
2017        tmp_tx.remove_xpubs_and_bip32_paths()
2018        tx.combine_with_other_psbt(tmp_tx)
2019        tx.add_info_from_wallet(self, include_xpubs=False)
2020        return tx
2021
2022    def try_detecting_internal_addresses_corruption(self) -> None:
2023        pass
2024
2025    def check_address_for_corruption(self, addr: str) -> None:
2026        pass
2027
2028    def get_unused_addresses(self) -> Sequence[str]:
2029        domain = self.get_receiving_addresses()
2030        # TODO we should index receive_requests by id
2031        in_use_by_request = [k for k in self.receive_requests.keys()
2032                             if self.get_request_status(k) != PR_EXPIRED]
2033        in_use_by_request = set(in_use_by_request)
2034        return [addr for addr in domain if not self.is_used(addr)
2035                and addr not in in_use_by_request]
2036
2037    @check_returned_address_for_corruption
2038    def get_unused_address(self) -> Optional[str]:
2039        """Get an unused receiving address, if there is one.
2040        Note: there might NOT be one available!
2041        """
2042        addrs = self.get_unused_addresses()
2043        if addrs:
2044            return addrs[0]
2045
2046    @check_returned_address_for_corruption
2047    def get_receiving_address(self) -> str:
2048        """Get a receiving address. Guaranteed to always return an address."""
2049        unused_addr = self.get_unused_address()
2050        if unused_addr:
2051            return unused_addr
2052        domain = self.get_receiving_addresses()
2053        if not domain:
2054            raise Exception("no receiving addresses in wallet?!")
2055        choice = domain[0]
2056        for addr in domain:
2057            if not self.is_used(addr):
2058                if addr not in self.receive_requests.keys():
2059                    return addr
2060                else:
2061                    choice = addr
2062        return choice
2063
2064    def create_new_address(self, for_change: bool = False):
2065        raise Exception("this wallet cannot generate new addresses")
2066
2067    def import_address(self, address: str) -> str:
2068        raise Exception("this wallet cannot import addresses")
2069
2070    def import_addresses(self, addresses: List[str], *,
2071                         write_to_disk=True) -> Tuple[List[str], List[Tuple[str, str]]]:
2072        raise Exception("this wallet cannot import addresses")
2073
2074    def delete_address(self, address: str) -> None:
2075        raise Exception("this wallet cannot delete addresses")
2076
2077    def get_onchain_request_status(self, r: OnchainInvoice) -> Tuple[bool, Optional[int]]:
2078        address = r.get_address()
2079        amount = r.get_amount_sat()
2080        received, sent = self.get_addr_io(address)
2081        l = []
2082        for txo, x in received.items():
2083            h, v, is_cb = x
2084            txid, n = txo.split(':')
2085            tx_height = self.get_tx_height(txid)
2086            height = tx_height.height
2087            if height > 0 and height <= r.height:
2088                continue
2089            conf = tx_height.conf
2090            l.append((conf, v))
2091        vsum = 0
2092        for conf, v in reversed(sorted(l)):
2093            vsum += v
2094            if vsum >= amount:
2095                return True, conf
2096        return False, None
2097
2098    def get_request_URI(self, req: OnchainInvoice) -> str:
2099        addr = req.get_address()
2100        message = self.get_label(addr)
2101        amount = req.amount_sat
2102        extra_query_params = {}
2103        if req.time:
2104            extra_query_params['time'] = str(int(req.time))
2105        if req.exp:
2106            extra_query_params['exp'] = str(int(req.exp))
2107        #if req.get('name') and req.get('sig'):
2108        #    sig = bfh(req.get('sig'))
2109        #    sig = bitcoin.base_encode(sig, base=58)
2110        #    extra_query_params['name'] = req['name']
2111        #    extra_query_params['sig'] = sig
2112        uri = create_bip21_uri(addr, amount, message, extra_query_params=extra_query_params)
2113        return str(uri)
2114
2115    def check_expired_status(self, r: Invoice, status):
2116        if r.is_lightning() and r.exp == 0:
2117            status = PR_EXPIRED  # for BOLT-11 invoices, exp==0 means 0 seconds
2118        if status == PR_UNPAID and r.exp > 0 and r.time + r.exp < time.time():
2119            status = PR_EXPIRED
2120        return status
2121
2122    def get_invoice_status(self, invoice: Invoice):
2123        if invoice.is_lightning():
2124            status = self.lnworker.get_invoice_status(invoice) if self.lnworker else PR_UNKNOWN
2125        else:
2126            if self.is_onchain_invoice_paid(invoice, 1):
2127                status =PR_PAID
2128            elif self.is_onchain_invoice_paid(invoice, 0):
2129                status = PR_UNCONFIRMED
2130            else:
2131                status = PR_UNPAID
2132        return self.check_expired_status(invoice, status)
2133
2134    def get_request_status(self, key):
2135        r = self.get_request(key)
2136        if r is None:
2137            return PR_UNKNOWN
2138        if r.is_lightning():
2139            assert isinstance(r, LNInvoice)
2140            status = self.lnworker.get_payment_status(bfh(r.rhash)) if self.lnworker else PR_UNKNOWN
2141        else:
2142            assert isinstance(r, OnchainInvoice)
2143            paid, conf = self.get_onchain_request_status(r)
2144            if not paid:
2145                status = PR_UNPAID
2146            elif conf == 0:
2147                status = PR_UNCONFIRMED
2148            else:
2149                status = PR_PAID
2150        return self.check_expired_status(r, status)
2151
2152    def get_request(self, key):
2153        return self.receive_requests.get(key)
2154
2155    def get_formatted_request(self, key):
2156        x = self.receive_requests.get(key)
2157        if x:
2158            return self.export_request(x)
2159
2160    def export_request(self, x: Invoice) -> Dict[str, Any]:
2161        key = self.get_key_for_receive_request(x)
2162        status = self.get_request_status(key)
2163        status_str = x.get_status_str(status)
2164        is_lightning = x.is_lightning()
2165        d = {
2166            'is_lightning': is_lightning,
2167            'amount_BTC': format_satoshis(x.get_amount_sat()),
2168            'message': x.message,
2169            'timestamp': x.time,
2170            'expiration': x.exp,
2171            'status': status,
2172            'status_str': status_str,
2173        }
2174        if is_lightning:
2175            assert isinstance(x, LNInvoice)
2176            d['rhash'] = x.rhash
2177            d['invoice'] = x.invoice
2178            d['amount_msat'] = x.get_amount_msat()
2179            if self.lnworker and status == PR_UNPAID:
2180                d['can_receive'] = self.lnworker.can_receive_invoice(x)
2181        else:
2182            assert isinstance(x, OnchainInvoice)
2183            paid, conf = self.get_onchain_request_status(x)
2184            d['amount_sat'] = x.get_amount_sat()
2185            d['address'] = x.get_address()
2186            d['URI'] = self.get_request_URI(x)
2187            if conf is not None:
2188                d['confirmations'] = conf
2189        # add URL if we are running a payserver
2190        payserver = self.config.get_netaddress('payserver_address')
2191        if payserver:
2192            root = self.config.get('payserver_root', '/r')
2193            use_ssl = bool(self.config.get('ssl_keyfile'))
2194            protocol = 'https' if use_ssl else 'http'
2195            base = '%s://%s:%d'%(protocol, payserver.host, payserver.port)
2196            d['view_url'] = base + root + '/pay?id=' + key
2197            if use_ssl and 'URI' in d:
2198                request_url = base + '/bip70/' + key + '.bip70'
2199                d['bip70_url'] = request_url
2200        return d
2201
2202    def export_invoice(self, x: Invoice) -> Dict[str, Any]:
2203        status = self.get_invoice_status(x)
2204        status_str = x.get_status_str(status)
2205        is_lightning = x.is_lightning()
2206        d = {
2207            'is_lightning': is_lightning,
2208            'amount_BTC': format_satoshis(x.get_amount_sat()),
2209            'message': x.message,
2210            'timestamp': x.time,
2211            'expiration': x.exp,
2212            'status': status,
2213            'status_str': status_str,
2214        }
2215        if is_lightning:
2216            assert isinstance(x, LNInvoice)
2217            d['invoice'] = x.invoice
2218            d['amount_msat'] = x.get_amount_msat()
2219            if self.lnworker and status == PR_UNPAID:
2220                d['can_pay'] = self.lnworker.can_pay_invoice(x)
2221        else:
2222            assert isinstance(x, OnchainInvoice)
2223            amount_sat = x.get_amount_sat()
2224            assert isinstance(amount_sat, (int, str, type(None)))
2225            d['amount_sat'] = amount_sat
2226            d['outputs'] = [y.to_legacy_tuple() for y in x.outputs]
2227            if x.bip70:
2228                d['bip70'] = x.bip70
2229                d['requestor'] = x.requestor
2230        return d
2231
2232    def receive_tx_callback(self, tx_hash, tx, tx_height):
2233        super().receive_tx_callback(tx_hash, tx, tx_height)
2234        self._update_request_statuses_touched_by_tx(tx_hash)
2235
2236    def add_verified_tx(self, tx_hash, info):
2237        super().add_verified_tx(tx_hash, info)
2238        self._update_request_statuses_touched_by_tx(tx_hash)
2239
2240    def undo_verifications(self, blockchain, above_height):
2241        reorged_txids = super().undo_verifications(blockchain, above_height)
2242        for txid in reorged_txids:
2243            self._update_request_statuses_touched_by_tx(txid)
2244
2245    def _update_request_statuses_touched_by_tx(self, tx_hash: str) -> None:
2246        # FIXME in some cases if tx2 replaces unconfirmed tx1 in the mempool, we are not called.
2247        #       For a given receive request, if tx1 touches it but tx2 does not, then
2248        #       we were called when tx1 was added, but we will not get called when tx2 replaces tx1.
2249        tx = self.db.get_transaction(tx_hash)
2250        if tx is None:
2251            return
2252        for txo in tx.outputs():
2253            addr = txo.address
2254            if addr in self.receive_requests:
2255                status = self.get_request_status(addr)
2256                util.trigger_callback('request_status', self, addr, status)
2257
2258    def make_payment_request(self, address, amount_sat, message, expiration):
2259        # TODO maybe merge with wallet.create_invoice()...
2260        #      note that they use incompatible "id"
2261        amount_sat = amount_sat or 0
2262        timestamp = int(time.time())
2263        _id = bh2u(sha256d(address + "%d"%timestamp))[0:10]
2264        expiration = expiration or 0
2265        return OnchainInvoice(
2266            type=PR_TYPE_ONCHAIN,
2267            outputs=[(TYPE_ADDRESS, address, amount_sat)],
2268            message=message,
2269            time=timestamp,
2270            amount_sat=amount_sat,
2271            exp=expiration,
2272            id=_id,
2273            bip70=None,
2274            requestor=None,
2275            height=self.get_local_height(),
2276        )
2277
2278    def sign_payment_request(self, key, alias, alias_addr, password):  # FIXME this is broken
2279        req = self.receive_requests.get(key)
2280        assert isinstance(req, OnchainInvoice)
2281        alias_privkey = self.export_private_key(alias_addr, password)
2282        pr = paymentrequest.make_unsigned_request(req)
2283        paymentrequest.sign_request_with_alias(pr, alias, alias_privkey)
2284        req.bip70 = pr.raw.hex()
2285        req['name'] = pr.pki_data
2286        req['sig'] = bh2u(pr.signature)
2287        self.receive_requests[key] = req
2288
2289    @classmethod
2290    def get_key_for_outgoing_invoice(cls, invoice: Invoice) -> str:
2291        """Return the key to use for this invoice in self.invoices."""
2292        if invoice.is_lightning():
2293            assert isinstance(invoice, LNInvoice)
2294            key = invoice.rhash
2295        else:
2296            assert isinstance(invoice, OnchainInvoice)
2297            key = invoice.id
2298        return key
2299
2300    def get_key_for_receive_request(self, req: Invoice, *, sanity_checks: bool = False) -> str:
2301        """Return the key to use for this invoice in self.receive_requests."""
2302        if not req.is_lightning():
2303            assert isinstance(req, OnchainInvoice)
2304            addr = req.get_address()
2305            if sanity_checks:
2306                if not bitcoin.is_address(addr):
2307                    raise Exception(_('Invalid Bitcoin address.'))
2308                if not self.is_mine(addr):
2309                    raise Exception(_('Address not in wallet.'))
2310            key = addr
2311        else:
2312            assert isinstance(req, LNInvoice)
2313            key = req.rhash
2314        return key
2315
2316    def add_payment_request(self, req: Invoice, *, write_to_disk: bool = True):
2317        key = self.get_key_for_receive_request(req, sanity_checks=True)
2318        message = req.message
2319        self.receive_requests[key] = req
2320        self.set_label(key, message)  # should be a default label
2321        if write_to_disk:
2322            self.save_db()
2323        return req
2324
2325    def delete_request(self, key):
2326        """ lightning or on-chain """
2327        if key in self.receive_requests:
2328            self.remove_payment_request(key)
2329        elif self.lnworker:
2330            self.lnworker.delete_payment(key)
2331
2332    def delete_invoice(self, key):
2333        """ lightning or on-chain """
2334        if key in self.invoices:
2335            self.invoices.pop(key)
2336        elif self.lnworker:
2337            self.lnworker.delete_payment(key)
2338
2339    def remove_payment_request(self, addr) -> bool:
2340        found = False
2341        if addr in self.receive_requests:
2342            found = True
2343            self.receive_requests.pop(addr)
2344            self.save_db()
2345        return found
2346
2347    def get_sorted_requests(self) -> List[Invoice]:
2348        """ sorted by timestamp """
2349        out = [self.get_request(x) for x in self.receive_requests.keys()]
2350        out = [x for x in out if x is not None]
2351        out.sort(key=lambda x: x.time)
2352        return out
2353
2354    def get_unpaid_requests(self):
2355        out = [self.get_request(x) for x in self.receive_requests.keys() if self.get_request_status(x) != PR_PAID]
2356        out = [x for x in out if x is not None]
2357        out.sort(key=lambda x: x.time)
2358        return out
2359
2360    @abstractmethod
2361    def get_fingerprint(self) -> str:
2362        """Returns a string that can be used to identify this wallet.
2363        Used e.g. by Labels plugin, and LN channel backups.
2364        Returns empty string "" for wallets that don't have an ID.
2365        """
2366        pass
2367
2368    def can_import_privkey(self):
2369        return False
2370
2371    def can_import_address(self):
2372        return False
2373
2374    def can_delete_address(self):
2375        return False
2376
2377    def has_password(self):
2378        return self.has_keystore_encryption() or self.has_storage_encryption()
2379
2380    def can_have_keystore_encryption(self):
2381        return self.keystore and self.keystore.may_have_password()
2382
2383    def get_available_storage_encryption_version(self) -> StorageEncryptionVersion:
2384        """Returns the type of storage encryption offered to the user.
2385
2386        A wallet file (storage) is either encrypted with this version
2387        or is stored in plaintext.
2388        """
2389        if isinstance(self.keystore, Hardware_KeyStore):
2390            return StorageEncryptionVersion.XPUB_PASSWORD
2391        else:
2392            return StorageEncryptionVersion.USER_PASSWORD
2393
2394    def has_keystore_encryption(self):
2395        """Returns whether encryption is enabled for the keystore.
2396
2397        If True, e.g. signing a transaction will require a password.
2398        """
2399        if self.can_have_keystore_encryption():
2400            return self.db.get('use_encryption', False)
2401        return False
2402
2403    def has_storage_encryption(self):
2404        """Returns whether encryption is enabled for the wallet file on disk."""
2405        return self.storage and self.storage.is_encrypted()
2406
2407    @classmethod
2408    def may_have_password(cls):
2409        return True
2410
2411    def check_password(self, password):
2412        if self.has_keystore_encryption():
2413            self.keystore.check_password(password)
2414        if self.has_storage_encryption():
2415            self.storage.check_password(password)
2416
2417    def update_password(self, old_pw, new_pw, *, encrypt_storage: bool = True):
2418        if old_pw is None and self.has_password():
2419            raise InvalidPassword()
2420        self.check_password(old_pw)
2421        if self.storage:
2422            if encrypt_storage:
2423                enc_version = self.get_available_storage_encryption_version()
2424            else:
2425                enc_version = StorageEncryptionVersion.PLAINTEXT
2426            self.storage.set_password(new_pw, enc_version)
2427        # make sure next storage.write() saves changes
2428        self.db.set_modified(True)
2429
2430        # note: Encrypting storage with a hw device is currently only
2431        #       allowed for non-multisig wallets. Further,
2432        #       Hardware_KeyStore.may_have_password() == False.
2433        #       If these were not the case,
2434        #       extra care would need to be taken when encrypting keystores.
2435        self._update_password_for_keystore(old_pw, new_pw)
2436        encrypt_keystore = self.can_have_keystore_encryption()
2437        self.db.set_keystore_encryption(bool(new_pw) and encrypt_keystore)
2438        self.save_db()
2439
2440    @abstractmethod
2441    def _update_password_for_keystore(self, old_pw: Optional[str], new_pw: Optional[str]) -> None:
2442        pass
2443
2444    def sign_message(self, address, message, password):
2445        index = self.get_address_index(address)
2446        return self.keystore.sign_message(index, message, password)
2447
2448    def decrypt_message(self, pubkey: str, message, password) -> bytes:
2449        addr = self.pubkeys_to_address([pubkey])
2450        index = self.get_address_index(addr)
2451        return self.keystore.decrypt_message(index, message, password)
2452
2453    @abstractmethod
2454    def pubkeys_to_address(self, pubkeys: Sequence[str]) -> Optional[str]:
2455        pass
2456
2457    def price_at_timestamp(self, txid, price_func):
2458        """Returns fiat price of bitcoin at the time tx got confirmed."""
2459        timestamp = self.get_tx_height(txid).timestamp
2460        return price_func(timestamp if timestamp else time.time())
2461
2462    def average_price(self, txid, price_func, ccy) -> Decimal:
2463        """ Average acquisition price of the inputs of a transaction """
2464        input_value = 0
2465        total_price = 0
2466        txi_addresses = self.db.get_txi_addresses(txid)
2467        if not txi_addresses:
2468            return Decimal('NaN')
2469        for addr in txi_addresses:
2470            d = self.db.get_txi_addr(txid, addr)
2471            for ser, v in d:
2472                input_value += v
2473                total_price += self.coin_price(ser.split(':')[0], price_func, ccy, v)
2474        return total_price / (input_value/Decimal(COIN))
2475
2476    def clear_coin_price_cache(self):
2477        self._coin_price_cache = {}
2478
2479    def coin_price(self, txid, price_func, ccy, txin_value) -> Decimal:
2480        """
2481        Acquisition price of a coin.
2482        This assumes that either all inputs are mine, or no input is mine.
2483        """
2484        if txin_value is None:
2485            return Decimal('NaN')
2486        cache_key = "{}:{}:{}".format(str(txid), str(ccy), str(txin_value))
2487        result = self._coin_price_cache.get(cache_key, None)
2488        if result is not None:
2489            return result
2490        if self.db.get_txi_addresses(txid):
2491            result = self.average_price(txid, price_func, ccy) * txin_value/Decimal(COIN)
2492            self._coin_price_cache[cache_key] = result
2493            return result
2494        else:
2495            fiat_value = self.get_fiat_value(txid, ccy)
2496            if fiat_value is not None:
2497                return fiat_value
2498            else:
2499                p = self.price_at_timestamp(txid, price_func)
2500                return p * txin_value/Decimal(COIN)
2501
2502    def is_billing_address(self, addr):
2503        # overridden for TrustedCoin wallets
2504        return False
2505
2506    @abstractmethod
2507    def is_watching_only(self) -> bool:
2508        pass
2509
2510    def get_keystore(self) -> Optional[KeyStore]:
2511        return self.keystore
2512
2513    def get_keystores(self) -> Sequence[KeyStore]:
2514        return [self.keystore] if self.keystore else []
2515
2516    @abstractmethod
2517    def save_keystore(self):
2518        pass
2519
2520    @abstractmethod
2521    def has_seed(self) -> bool:
2522        pass
2523
2524    @abstractmethod
2525    def get_all_known_addresses_beyond_gap_limit(self) -> Set[str]:
2526        pass
2527
2528    def create_transaction(self, outputs, *, fee=None, feerate=None, change_addr=None, domain_addr=None, domain_coins=None,
2529              unsigned=False, rbf=None, password=None, locktime=None):
2530        if fee is not None and feerate is not None:
2531            raise Exception("Cannot specify both 'fee' and 'feerate' at the same time!")
2532        coins = self.get_spendable_coins(domain_addr)
2533        if domain_coins is not None:
2534            coins = [coin for coin in coins if (coin.prevout.to_str() in domain_coins)]
2535        if feerate is not None:
2536            fee_per_kb = 1000 * Decimal(feerate)
2537            fee_estimator = partial(SimpleConfig.estimate_fee_for_feerate, fee_per_kb)
2538        else:
2539            fee_estimator = fee
2540        tx = self.make_unsigned_transaction(
2541            coins=coins,
2542            outputs=outputs,
2543            fee=fee_estimator,
2544            change_addr=change_addr)
2545        if locktime is not None:
2546            tx.locktime = locktime
2547        if rbf is None:
2548            rbf = bool(self.config.get('use_rbf', True))
2549        tx.set_rbf(rbf)
2550        if not unsigned:
2551            self.sign_transaction(tx, password)
2552        return tx
2553
2554    def get_warning_for_risk_of_burning_coins_as_fees(self, tx: 'PartialTransaction') -> Optional[str]:
2555        """Returns a warning message if there is risk of burning coins as fees if we sign.
2556        Note that if not all inputs are ismine, e.g. coinjoin, the risk is not just about fees.
2557
2558        Note:
2559            - legacy sighash does not commit to any input amounts
2560            - BIP-0143 sighash only commits to the *corresponding* input amount
2561            - BIP-taproot sighash commits to *all* input amounts
2562        """
2563        assert isinstance(tx, PartialTransaction)
2564        # if we have all full previous txs, we *know* all the input amounts -> fine
2565        if all([txin.utxo for txin in tx.inputs()]):
2566            return None
2567        # a single segwit input -> fine
2568        if len(tx.inputs()) == 1 and tx.inputs()[0].is_segwit() and tx.inputs()[0].witness_utxo:
2569            return None
2570        # coinjoin or similar
2571        if any([not self.is_mine(txin.address) for txin in tx.inputs()]):
2572            return (_("Warning") + ": "
2573                    + _("The input amounts could not be verified as the previous transactions are missing.\n"
2574                        "The amount of money being spent CANNOT be verified."))
2575        # some inputs are legacy
2576        if any([not txin.is_segwit() for txin in tx.inputs()]):
2577            return (_("Warning") + ": "
2578                    + _("The fee could not be verified. Signing non-segwit inputs is risky:\n"
2579                        "if this transaction was maliciously modified before you sign,\n"
2580                        "you might end up paying a higher mining fee than displayed."))
2581        # all inputs are segwit
2582        # https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-August/014843.html
2583        return (_("Warning") + ": "
2584                + _("If you received this transaction from an untrusted device, "
2585                    "do not accept to sign it more than once,\n"
2586                    "otherwise you could end up paying a different fee."))
2587
2588    def get_tx_fee_warning(
2589            self, *,
2590            invoice_amt: int,
2591            tx_size: int,
2592            fee: int) -> Optional[Tuple[bool, str, str]]:
2593
2594        feerate = Decimal(fee) / tx_size  # sat/byte
2595        fee_ratio = Decimal(fee) / invoice_amt if invoice_amt else 1
2596        long_warning = None
2597        short_warning = None
2598        allow_send = True
2599        if feerate < self.relayfee() / 1000:
2600            long_warning = (
2601                    _("This transaction requires a higher fee, or it will not be propagated by your current server.") + " "
2602                    + _("Try to raise your transaction fee, or use a server with a lower relay fee."))
2603            short_warning = _("below relay fee") + "!"
2604            allow_send = False
2605        elif fee_ratio >= FEE_RATIO_HIGH_WARNING:
2606            long_warning = (
2607                    _('Warning') + ': ' + _("The fee for this transaction seems unusually high.")
2608                    + f' ({fee_ratio*100:.2f}% of amount)')
2609            short_warning = _("high fee ratio") + "!"
2610        elif feerate > FEERATE_WARNING_HIGH_FEE / 1000:
2611            long_warning = (
2612                    _('Warning') + ': ' + _("The fee for this transaction seems unusually high.")
2613                    + f' (feerate: {feerate:.2f} sat/byte)')
2614            short_warning = _("high fee rate") + "!"
2615        if long_warning is None:
2616            return None
2617        else:
2618            return allow_send, long_warning, short_warning
2619
2620
2621class Simple_Wallet(Abstract_Wallet):
2622    # wallet with a single keystore
2623
2624    def is_watching_only(self):
2625        return self.keystore.is_watching_only()
2626
2627    def _update_password_for_keystore(self, old_pw, new_pw):
2628        if self.keystore and self.keystore.may_have_password():
2629            self.keystore.update_password(old_pw, new_pw)
2630            self.save_keystore()
2631
2632    def save_keystore(self):
2633        self.db.put('keystore', self.keystore.dump())
2634
2635    @abstractmethod
2636    def get_public_key(self, address: str) -> Optional[str]:
2637        pass
2638
2639    def get_public_keys(self, address: str) -> Sequence[str]:
2640        return [self.get_public_key(address)]
2641
2642    def get_redeem_script(self, address: str) -> Optional[str]:
2643        txin_type = self.get_txin_type(address)
2644        if txin_type in ('p2pkh', 'p2wpkh', 'p2pk'):
2645            return None
2646        if txin_type == 'p2wpkh-p2sh':
2647            pubkey = self.get_public_key(address)
2648            return bitcoin.p2wpkh_nested_script(pubkey)
2649        if txin_type == 'address':
2650            return None
2651        raise UnknownTxinType(f'unexpected txin_type {txin_type}')
2652
2653    def get_witness_script(self, address: str) -> Optional[str]:
2654        return None
2655
2656
2657class Imported_Wallet(Simple_Wallet):
2658    # wallet made of imported addresses
2659
2660    wallet_type = 'imported'
2661    txin_type = 'address'
2662
2663    def __init__(self, db, storage, *, config):
2664        Abstract_Wallet.__init__(self, db, storage, config=config)
2665        self.use_change = db.get('use_change', False)
2666
2667    def is_watching_only(self):
2668        return self.keystore is None
2669
2670    def can_import_privkey(self):
2671        return bool(self.keystore)
2672
2673    def load_keystore(self):
2674        self.keystore = load_keystore(self.db, 'keystore') if self.db.get('keystore') else None
2675
2676    def save_keystore(self):
2677        self.db.put('keystore', self.keystore.dump())
2678
2679    def can_import_address(self):
2680        return self.is_watching_only()
2681
2682    def can_delete_address(self):
2683        return True
2684
2685    def has_seed(self):
2686        return False
2687
2688    def is_deterministic(self):
2689        return False
2690
2691    def is_change(self, address):
2692        return False
2693
2694    def get_all_known_addresses_beyond_gap_limit(self) -> Set[str]:
2695        return set()
2696
2697    def get_fingerprint(self):
2698        return ''
2699
2700    def get_addresses(self):
2701        # note: overridden so that the history can be cleared
2702        return self.db.get_imported_addresses()
2703
2704    def get_receiving_addresses(self, **kwargs):
2705        return self.get_addresses()
2706
2707    def get_change_addresses(self, **kwargs):
2708        return self.get_addresses()
2709
2710    def import_addresses(self, addresses: List[str], *,
2711                         write_to_disk=True) -> Tuple[List[str], List[Tuple[str, str]]]:
2712        good_addr = []  # type: List[str]
2713        bad_addr = []  # type: List[Tuple[str, str]]
2714        for address in addresses:
2715            if not bitcoin.is_address(address):
2716                bad_addr.append((address, _('invalid address')))
2717                continue
2718            if self.db.has_imported_address(address):
2719                bad_addr.append((address, _('address already in wallet')))
2720                continue
2721            good_addr.append(address)
2722            self.db.add_imported_address(address, {})
2723            self.add_address(address)
2724        if write_to_disk:
2725            self.save_db()
2726        return good_addr, bad_addr
2727
2728    def import_address(self, address: str) -> str:
2729        good_addr, bad_addr = self.import_addresses([address])
2730        if good_addr and good_addr[0] == address:
2731            return address
2732        else:
2733            raise BitcoinException(str(bad_addr[0][1]))
2734
2735    def delete_address(self, address: str) -> None:
2736        if not self.db.has_imported_address(address):
2737            return
2738        if len(self.get_addresses()) <= 1:
2739            raise UserFacingException("cannot delete last remaining address from wallet")
2740        transactions_to_remove = set()  # only referred to by this address
2741        transactions_new = set()  # txs that are not only referred to by address
2742        with self.lock:
2743            for addr in self.db.get_history():
2744                details = self.get_address_history(addr)
2745                if addr == address:
2746                    for tx_hash, height in details:
2747                        transactions_to_remove.add(tx_hash)
2748                else:
2749                    for tx_hash, height in details:
2750                        transactions_new.add(tx_hash)
2751            transactions_to_remove -= transactions_new
2752            self.db.remove_addr_history(address)
2753            for tx_hash in transactions_to_remove:
2754                self.remove_transaction(tx_hash)
2755        self.set_label(address, None)
2756        self.remove_payment_request(address)
2757        self.set_frozen_state_of_addresses([address], False)
2758        pubkey = self.get_public_key(address)
2759        self.db.remove_imported_address(address)
2760        if pubkey:
2761            # delete key iff no other address uses it (e.g. p2pkh and p2wpkh for same key)
2762            for txin_type in bitcoin.WIF_SCRIPT_TYPES.keys():
2763                try:
2764                    addr2 = bitcoin.pubkey_to_address(txin_type, pubkey)
2765                except NotImplementedError:
2766                    pass
2767                else:
2768                    if self.db.has_imported_address(addr2):
2769                        break
2770            else:
2771                self.keystore.delete_imported_key(pubkey)
2772                self.save_keystore()
2773        self.save_db()
2774
2775    def get_change_addresses_for_new_transaction(self, *args, **kwargs) -> List[str]:
2776        # for an imported wallet, if all "change addresses" are already used,
2777        # it is probably better to send change back to the "from address", than to
2778        # send it to another random used address and link them together, hence
2779        # we force "allow_reusing_used_change_addrs=False"
2780        return super().get_change_addresses_for_new_transaction(
2781            *args,
2782            **{**kwargs, "allow_reusing_used_change_addrs": False},
2783        )
2784
2785    def calc_unused_change_addresses(self) -> Sequence[str]:
2786        with self.lock:
2787            unused_addrs = [addr for addr in self.get_change_addresses()
2788                            if not self.is_used(addr) and not self.is_address_reserved(addr)]
2789            return unused_addrs
2790
2791    def is_mine(self, address) -> bool:
2792        if not address: return False
2793        return self.db.has_imported_address(address)
2794
2795    def get_address_index(self, address) -> Optional[str]:
2796        # returns None if address is not mine
2797        return self.get_public_key(address)
2798
2799    def get_address_path_str(self, address):
2800        return None
2801
2802    def get_public_key(self, address) -> Optional[str]:
2803        x = self.db.get_imported_address(address)
2804        return x.get('pubkey') if x else None
2805
2806    def import_private_keys(self, keys: List[str], password: Optional[str], *,
2807                            write_to_disk=True) -> Tuple[List[str], List[Tuple[str, str]]]:
2808        good_addr = []  # type: List[str]
2809        bad_keys = []  # type: List[Tuple[str, str]]
2810        for key in keys:
2811            try:
2812                txin_type, pubkey = self.keystore.import_privkey(key, password)
2813            except Exception as e:
2814                bad_keys.append((key, _('invalid private key') + f': {e}'))
2815                continue
2816            if txin_type not in ('p2pkh', 'p2wpkh', 'p2wpkh-p2sh'):
2817                bad_keys.append((key, _('not implemented type') + f': {txin_type}'))
2818                continue
2819            addr = bitcoin.pubkey_to_address(txin_type, pubkey)
2820            good_addr.append(addr)
2821            self.db.add_imported_address(addr, {'type':txin_type, 'pubkey':pubkey})
2822            self.add_address(addr)
2823        self.save_keystore()
2824        if write_to_disk:
2825            self.save_db()
2826        return good_addr, bad_keys
2827
2828    def import_private_key(self, key: str, password: Optional[str]) -> str:
2829        good_addr, bad_keys = self.import_private_keys([key], password=password)
2830        if good_addr:
2831            return good_addr[0]
2832        else:
2833            raise BitcoinException(str(bad_keys[0][1]))
2834
2835    def get_txin_type(self, address):
2836        return self.db.get_imported_address(address).get('type', 'address')
2837
2838    @profiler
2839    def try_detecting_internal_addresses_corruption(self):
2840        # we check only a random sample, for performance
2841        addresses = self.get_addresses()
2842        addresses = random.sample(addresses, min(len(addresses), 10))
2843        for addr_found in addresses:
2844            self.check_address_for_corruption(addr_found)
2845
2846    def check_address_for_corruption(self, addr):
2847        if addr and self.is_mine(addr):
2848            pubkey = self.get_public_key(addr)
2849            if not pubkey:
2850                return
2851            txin_type = self.get_txin_type(addr)
2852            if txin_type == 'address':
2853                return
2854            if addr != bitcoin.pubkey_to_address(txin_type, pubkey):
2855                raise InternalAddressCorruption()
2856
2857    def _add_input_sig_info(self, txin, address, *, only_der_suffix):
2858        if not self.is_mine(address):
2859            return
2860        if txin.script_type in ('unknown', 'address'):
2861            return
2862        elif txin.script_type in ('p2pkh', 'p2wpkh', 'p2wpkh-p2sh'):
2863            pubkey = self.get_public_key(address)
2864            if not pubkey:
2865                return
2866            txin.pubkeys = [bfh(pubkey)]
2867        else:
2868            raise Exception(f'Unexpected script type: {txin.script_type}. '
2869                            f'Imported wallets are not implemented to handle this.')
2870
2871    def pubkeys_to_address(self, pubkeys):
2872        pubkey = pubkeys[0]
2873        # FIXME This is slow.
2874        #       Ideally we would re-derive the address from the pubkey and the txin_type,
2875        #       but we don't know the txin_type, and we only have an addr->txin_type map.
2876        #       so instead a linear search of reverse-lookups is done...
2877        for addr in self.db.get_imported_addresses():
2878            if self.db.get_imported_address(addr)['pubkey'] == pubkey:
2879                return addr
2880        return None
2881
2882    def decrypt_message(self, pubkey: str, message, password) -> bytes:
2883        # this is significantly faster than the implementation in the superclass
2884        return self.keystore.decrypt_message(pubkey, message, password)
2885
2886
2887class Deterministic_Wallet(Abstract_Wallet):
2888
2889    def __init__(self, db, storage, *, config):
2890        self._ephemeral_addr_to_addr_index = {}  # type: Dict[str, Sequence[int]]
2891        Abstract_Wallet.__init__(self, db, storage, config=config)
2892        self.gap_limit = db.get('gap_limit', 20)
2893        # generate addresses now. note that without libsecp this might block
2894        # for a few seconds!
2895        self.synchronize()
2896        # lightning_privkey2 is not deterministic (legacy wallets, bip39)
2897        ln_xprv = self.db.get('lightning_xprv') or self.db.get('lightning_privkey2')
2898        # lnworker can only be initialized once receiving addresses are available
2899        # therefore we instantiate lnworker in DeterministicWallet
2900        self.lnworker = LNWallet(self, ln_xprv) if ln_xprv else None
2901
2902    def has_seed(self):
2903        return self.keystore.has_seed()
2904
2905    def get_addresses(self):
2906        # note: overridden so that the history can be cleared.
2907        # addresses are ordered based on derivation
2908        out = self.get_receiving_addresses()
2909        out += self.get_change_addresses()
2910        return out
2911
2912    def get_receiving_addresses(self, *, slice_start=None, slice_stop=None):
2913        return self.db.get_receiving_addresses(slice_start=slice_start, slice_stop=slice_stop)
2914
2915    def get_change_addresses(self, *, slice_start=None, slice_stop=None):
2916        return self.db.get_change_addresses(slice_start=slice_start, slice_stop=slice_stop)
2917
2918    @profiler
2919    def try_detecting_internal_addresses_corruption(self):
2920        addresses_all = self.get_addresses()
2921        # sample 1: first few
2922        addresses_sample1 = addresses_all[:10]
2923        # sample2: a few more randomly selected
2924        addresses_rand = addresses_all[10:]
2925        addresses_sample2 = random.sample(addresses_rand, min(len(addresses_rand), 10))
2926        for addr_found in itertools.chain(addresses_sample1, addresses_sample2):
2927            self.check_address_for_corruption(addr_found)
2928
2929    def check_address_for_corruption(self, addr):
2930        if addr and self.is_mine(addr):
2931            if addr != self.derive_address(*self.get_address_index(addr)):
2932                raise InternalAddressCorruption()
2933
2934    def get_seed(self, password):
2935        return self.keystore.get_seed(password)
2936
2937    def change_gap_limit(self, value):
2938        '''This method is not called in the code, it is kept for console use'''
2939        value = int(value)
2940        if value >= self.min_acceptable_gap():
2941            self.gap_limit = value
2942            self.db.put('gap_limit', self.gap_limit)
2943            self.save_db()
2944            return True
2945        else:
2946            return False
2947
2948    def num_unused_trailing_addresses(self, addresses):
2949        k = 0
2950        for addr in addresses[::-1]:
2951            if self.db.get_addr_history(addr):
2952                break
2953            k += 1
2954        return k
2955
2956    def min_acceptable_gap(self) -> int:
2957        # fixme: this assumes wallet is synchronized
2958        n = 0
2959        nmax = 0
2960        addresses = self.get_receiving_addresses()
2961        k = self.num_unused_trailing_addresses(addresses)
2962        for addr in addresses[0:-k]:
2963            if self.address_is_old(addr):
2964                n = 0
2965            else:
2966                n += 1
2967                nmax = max(nmax, n)
2968        return nmax + 1
2969
2970    @abstractmethod
2971    def derive_pubkeys(self, c: int, i: int) -> Sequence[str]:
2972        pass
2973
2974    def derive_address(self, for_change: int, n: int) -> str:
2975        for_change = int(for_change)
2976        pubkeys = self.derive_pubkeys(for_change, n)
2977        return self.pubkeys_to_address(pubkeys)
2978
2979    def export_private_key_for_path(self, path: Union[Sequence[int], str], password: Optional[str]) -> str:
2980        if isinstance(path, str):
2981            path = convert_bip32_path_to_list_of_uint32(path)
2982        pk, compressed = self.keystore.get_private_key(path, password)
2983        txin_type = self.get_txin_type()  # assumes no mixed-scripts in wallet
2984        return bitcoin.serialize_privkey(pk, compressed, txin_type)
2985
2986    def get_public_keys_with_deriv_info(self, address: str):
2987        der_suffix = self.get_address_index(address)
2988        der_suffix = [int(x) for x in der_suffix]
2989        return {k.derive_pubkey(*der_suffix): (k, der_suffix)
2990                for k in self.get_keystores()}
2991
2992    def _add_input_sig_info(self, txin, address, *, only_der_suffix):
2993        self._add_txinout_derivation_info(txin, address, only_der_suffix=only_der_suffix)
2994
2995    def _add_txinout_derivation_info(self, txinout, address, *, only_der_suffix):
2996        if not self.is_mine(address):
2997            return
2998        pubkey_deriv_info = self.get_public_keys_with_deriv_info(address)
2999        txinout.pubkeys = sorted([pk for pk in list(pubkey_deriv_info)])
3000        for pubkey in pubkey_deriv_info:
3001            ks, der_suffix = pubkey_deriv_info[pubkey]
3002            fp_bytes, der_full = ks.get_fp_and_derivation_to_be_used_in_partial_tx(der_suffix,
3003                                                                                   only_der_suffix=only_der_suffix)
3004            txinout.bip32_paths[pubkey] = (fp_bytes, der_full)
3005
3006    def create_new_address(self, for_change: bool = False):
3007        assert type(for_change) is bool
3008        with self.lock:
3009            n = self.db.num_change_addresses() if for_change else self.db.num_receiving_addresses()
3010            address = self.derive_address(int(for_change), n)
3011            self.db.add_change_address(address) if for_change else self.db.add_receiving_address(address)
3012            self.add_address(address)
3013            if for_change:
3014                # note: if it's actually "old", it will get filtered later
3015                self._not_old_change_addresses.append(address)
3016            return address
3017
3018    def synchronize_sequence(self, for_change):
3019        limit = self.gap_limit_for_change if for_change else self.gap_limit
3020        while True:
3021            num_addr = self.db.num_change_addresses() if for_change else self.db.num_receiving_addresses()
3022            if num_addr < limit:
3023                self.create_new_address(for_change)
3024                continue
3025            if for_change:
3026                last_few_addresses = self.get_change_addresses(slice_start=-limit)
3027            else:
3028                last_few_addresses = self.get_receiving_addresses(slice_start=-limit)
3029            if any(map(self.address_is_old, last_few_addresses)):
3030                self.create_new_address(for_change)
3031            else:
3032                break
3033
3034    @AddressSynchronizer.with_local_height_cached
3035    def synchronize(self):
3036        with self.lock:
3037            self.synchronize_sequence(False)
3038            self.synchronize_sequence(True)
3039
3040    def get_all_known_addresses_beyond_gap_limit(self):
3041        # note that we don't stop at first large gap
3042        found = set()
3043
3044        def process_addresses(addrs, gap_limit):
3045            rolling_num_unused = 0
3046            for addr in addrs:
3047                if self.db.get_addr_history(addr):
3048                    rolling_num_unused = 0
3049                else:
3050                    if rolling_num_unused >= gap_limit:
3051                        found.add(addr)
3052                    rolling_num_unused += 1
3053
3054        process_addresses(self.get_receiving_addresses(), self.gap_limit)
3055        process_addresses(self.get_change_addresses(), self.gap_limit_for_change)
3056        return found
3057
3058    def get_address_index(self, address) -> Optional[Sequence[int]]:
3059        return self.db.get_address_index(address) or self._ephemeral_addr_to_addr_index.get(address)
3060
3061    def get_address_path_str(self, address):
3062        intpath = self.get_address_index(address)
3063        if intpath is None:
3064            return None
3065        return convert_bip32_intpath_to_strpath(intpath)
3066
3067    def _learn_derivation_path_for_address_from_txinout(self, txinout, address):
3068        for ks in self.get_keystores():
3069            pubkey, der_suffix = ks.find_my_pubkey_in_txinout(txinout, only_der_suffix=True)
3070            if der_suffix is not None:
3071                # note: we already know the pubkey belongs to the keystore,
3072                #       but the script template might be different
3073                if len(der_suffix) != 2: continue
3074                try:
3075                    my_address = self.derive_address(*der_suffix)
3076                except CannotDerivePubkey:
3077                    my_address = None
3078                if my_address == address:
3079                    self._ephemeral_addr_to_addr_index[address] = list(der_suffix)
3080                    return True
3081        return False
3082
3083    def get_master_public_keys(self):
3084        return [self.get_master_public_key()]
3085
3086    def get_fingerprint(self):
3087        return self.get_master_public_key()
3088
3089    def get_txin_type(self, address=None):
3090        return self.txin_type
3091
3092
3093class Simple_Deterministic_Wallet(Simple_Wallet, Deterministic_Wallet):
3094
3095    """ Deterministic Wallet with a single pubkey per address """
3096
3097    def __init__(self, db, storage, *, config):
3098        Deterministic_Wallet.__init__(self, db, storage, config=config)
3099
3100    def get_public_key(self, address):
3101        sequence = self.get_address_index(address)
3102        pubkeys = self.derive_pubkeys(*sequence)
3103        return pubkeys[0]
3104
3105    def load_keystore(self):
3106        self.keystore = load_keystore(self.db, 'keystore')
3107        try:
3108            xtype = bip32.xpub_type(self.keystore.xpub)
3109        except:
3110            xtype = 'standard'
3111        self.txin_type = 'p2pkh' if xtype == 'standard' else xtype
3112
3113    def get_master_public_key(self):
3114        return self.keystore.get_master_public_key()
3115
3116    def derive_pubkeys(self, c, i):
3117        return [self.keystore.derive_pubkey(c, i).hex()]
3118
3119
3120
3121
3122
3123
3124class Standard_Wallet(Simple_Deterministic_Wallet):
3125    wallet_type = 'standard'
3126
3127    def pubkeys_to_address(self, pubkeys):
3128        pubkey = pubkeys[0]
3129        return bitcoin.pubkey_to_address(self.txin_type, pubkey)
3130
3131
3132class Multisig_Wallet(Deterministic_Wallet):
3133    # generic m of n
3134
3135    def __init__(self, db, storage, *, config):
3136        self.wallet_type = db.get('wallet_type')
3137        self.m, self.n = multisig_type(self.wallet_type)
3138        Deterministic_Wallet.__init__(self, db, storage, config=config)
3139
3140    def get_public_keys(self, address):
3141        return [pk.hex() for pk in self.get_public_keys_with_deriv_info(address)]
3142
3143    def pubkeys_to_address(self, pubkeys):
3144        redeem_script = self.pubkeys_to_scriptcode(pubkeys)
3145        return bitcoin.redeem_script_to_address(self.txin_type, redeem_script)
3146
3147    def pubkeys_to_scriptcode(self, pubkeys: Sequence[str]) -> str:
3148        return transaction.multisig_script(sorted(pubkeys), self.m)
3149
3150    def get_redeem_script(self, address):
3151        txin_type = self.get_txin_type(address)
3152        pubkeys = self.get_public_keys(address)
3153        scriptcode = self.pubkeys_to_scriptcode(pubkeys)
3154        if txin_type == 'p2sh':
3155            return scriptcode
3156        elif txin_type == 'p2wsh-p2sh':
3157            return bitcoin.p2wsh_nested_script(scriptcode)
3158        elif txin_type == 'p2wsh':
3159            return None
3160        raise UnknownTxinType(f'unexpected txin_type {txin_type}')
3161
3162    def get_witness_script(self, address):
3163        txin_type = self.get_txin_type(address)
3164        pubkeys = self.get_public_keys(address)
3165        scriptcode = self.pubkeys_to_scriptcode(pubkeys)
3166        if txin_type == 'p2sh':
3167            return None
3168        elif txin_type in ('p2wsh-p2sh', 'p2wsh'):
3169            return scriptcode
3170        raise UnknownTxinType(f'unexpected txin_type {txin_type}')
3171
3172    def derive_pubkeys(self, c, i):
3173        return [k.derive_pubkey(c, i).hex() for k in self.get_keystores()]
3174
3175    def load_keystore(self):
3176        self.keystores = {}
3177        for i in range(self.n):
3178            name = 'x%d/'%(i+1)
3179            self.keystores[name] = load_keystore(self.db, name)
3180        self.keystore = self.keystores['x1/']
3181        xtype = bip32.xpub_type(self.keystore.xpub)
3182        self.txin_type = 'p2sh' if xtype == 'standard' else xtype
3183
3184    def save_keystore(self):
3185        for name, k in self.keystores.items():
3186            self.db.put(name, k.dump())
3187
3188    def get_keystore(self):
3189        return self.keystores.get('x1/')
3190
3191    def get_keystores(self):
3192        return [self.keystores[i] for i in sorted(self.keystores.keys())]
3193
3194    def can_have_keystore_encryption(self):
3195        return any([k.may_have_password() for k in self.get_keystores()])
3196
3197    def _update_password_for_keystore(self, old_pw, new_pw):
3198        for name, keystore in self.keystores.items():
3199            if keystore.may_have_password():
3200                keystore.update_password(old_pw, new_pw)
3201                self.db.put(name, keystore.dump())
3202
3203    def check_password(self, password):
3204        for name, keystore in self.keystores.items():
3205            if keystore.may_have_password():
3206                keystore.check_password(password)
3207        if self.has_storage_encryption():
3208            self.storage.check_password(password)
3209
3210    def get_available_storage_encryption_version(self):
3211        # multisig wallets are not offered hw device encryption
3212        return StorageEncryptionVersion.USER_PASSWORD
3213
3214    def has_seed(self):
3215        return self.keystore.has_seed()
3216
3217    def is_watching_only(self):
3218        return all([k.is_watching_only() for k in self.get_keystores()])
3219
3220    def get_master_public_key(self):
3221        return self.keystore.get_master_public_key()
3222
3223    def get_master_public_keys(self):
3224        return [k.get_master_public_key() for k in self.get_keystores()]
3225
3226    def get_fingerprint(self):
3227        return ''.join(sorted(self.get_master_public_keys()))
3228
3229
3230wallet_types = ['standard', 'multisig', 'imported']
3231
3232def register_wallet_type(category):
3233    wallet_types.append(category)
3234
3235wallet_constructors = {
3236    'standard': Standard_Wallet,
3237    'old': Standard_Wallet,
3238    'xpub': Standard_Wallet,
3239    'imported': Imported_Wallet
3240}
3241
3242def register_constructor(wallet_type, constructor):
3243    wallet_constructors[wallet_type] = constructor
3244
3245# former WalletFactory
3246class Wallet(object):
3247    """The main wallet "entry point".
3248    This class is actually a factory that will return a wallet of the correct
3249    type when passed a WalletStorage instance."""
3250
3251    def __new__(self, db: 'WalletDB', storage: Optional[WalletStorage], *, config: SimpleConfig):
3252        wallet_type = db.get('wallet_type')
3253        WalletClass = Wallet.wallet_class(wallet_type)
3254        wallet = WalletClass(db, storage, config=config)
3255        return wallet
3256
3257    @staticmethod
3258    def wallet_class(wallet_type):
3259        if multisig_type(wallet_type):
3260            return Multisig_Wallet
3261        if wallet_type in wallet_constructors:
3262            return wallet_constructors[wallet_type]
3263        raise WalletFileException("Unknown wallet type: " + str(wallet_type))
3264
3265
3266def create_new_wallet(*, path, config: SimpleConfig, passphrase=None, password=None,
3267                      encrypt_file=True, seed_type=None, gap_limit=None) -> dict:
3268    """Create a new wallet"""
3269    storage = WalletStorage(path)
3270    if storage.file_exists():
3271        raise Exception("Remove the existing wallet first!")
3272    db = WalletDB('', manual_upgrades=False)
3273
3274    seed = Mnemonic('en').make_seed(seed_type=seed_type)
3275    k = keystore.from_seed(seed, passphrase)
3276    db.put('keystore', k.dump())
3277    db.put('wallet_type', 'standard')
3278    if k.can_have_deterministic_lightning_xprv():
3279        db.put('lightning_xprv', k.get_lightning_xprv(None))
3280    if gap_limit is not None:
3281        db.put('gap_limit', gap_limit)
3282    wallet = Wallet(db, storage, config=config)
3283    wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file)
3284    wallet.synchronize()
3285    msg = "Please keep your seed in a safe place; if you lose it, you will not be able to restore your wallet."
3286    wallet.save_db()
3287    return {'seed': seed, 'wallet': wallet, 'msg': msg}
3288
3289
3290def restore_wallet_from_text(text, *, path, config: SimpleConfig,
3291                             passphrase=None, password=None, encrypt_file=True,
3292                             gap_limit=None) -> dict:
3293    """Restore a wallet from text. Text can be a seed phrase, a master
3294    public key, a master private key, a list of bitcoin addresses
3295    or bitcoin private keys."""
3296    storage = WalletStorage(path)
3297    if storage.file_exists():
3298        raise Exception("Remove the existing wallet first!")
3299    db = WalletDB('', manual_upgrades=False)
3300    text = text.strip()
3301    if keystore.is_address_list(text):
3302        wallet = Imported_Wallet(db, storage, config=config)
3303        addresses = text.split()
3304        good_inputs, bad_inputs = wallet.import_addresses(addresses, write_to_disk=False)
3305        # FIXME tell user about bad_inputs
3306        if not good_inputs:
3307            raise Exception("None of the given addresses can be imported")
3308    elif keystore.is_private_key_list(text, allow_spaces_inside_key=False):
3309        k = keystore.Imported_KeyStore({})
3310        db.put('keystore', k.dump())
3311        wallet = Imported_Wallet(db, storage, config=config)
3312        keys = keystore.get_private_keys(text, allow_spaces_inside_key=False)
3313        good_inputs, bad_inputs = wallet.import_private_keys(keys, None, write_to_disk=False)
3314        # FIXME tell user about bad_inputs
3315        if not good_inputs:
3316            raise Exception("None of the given privkeys can be imported")
3317    else:
3318        if keystore.is_master_key(text):
3319            k = keystore.from_master_key(text)
3320        elif keystore.is_seed(text):
3321            k = keystore.from_seed(text, passphrase)
3322            if k.can_have_deterministic_lightning_xprv():
3323                db.put('lightning_xprv', k.get_lightning_xprv(None))
3324        else:
3325            raise Exception("Seed or key not recognized")
3326        db.put('keystore', k.dump())
3327        db.put('wallet_type', 'standard')
3328        if gap_limit is not None:
3329            db.put('gap_limit', gap_limit)
3330        wallet = Wallet(db, storage, config=config)
3331    assert not storage.file_exists(), "file was created too soon! plaintext keys might have been written to disk"
3332    wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file)
3333    wallet.synchronize()
3334    msg = ("This wallet was restored offline. It may contain more addresses than displayed. "
3335           "Start a daemon and use load_wallet to sync its history.")
3336    wallet.save_db()
3337    return {'wallet': wallet, 'msg': msg}
3338
3339
3340def check_password_for_directory(config: SimpleConfig, old_password, new_password=None) -> Tuple[bool, bool]:
3341    """Checks password against all wallets, returns whether they can be unified and whether they are already.
3342    If new_password is not None, update all wallet passwords to new_password.
3343    """
3344    dirname = os.path.dirname(config.get_wallet_path())
3345    failed = []
3346    is_unified = True
3347    for filename in os.listdir(dirname):
3348        path = os.path.join(dirname, filename)
3349        if not os.path.isfile(path):
3350            continue
3351        basename = os.path.basename(path)
3352        storage = WalletStorage(path)
3353        if not storage.is_encrypted():
3354            is_unified = False
3355            # it is a bit wasteful load the wallet here, but that is fine
3356            # because we are progressively enforcing storage encryption.
3357            try:
3358                db = WalletDB(storage.read(), manual_upgrades=False)
3359                wallet = Wallet(db, storage, config=config)
3360            except:
3361                _logger.exception(f'failed to load {basename}:')
3362                failed.append(basename)
3363                continue
3364            if wallet.has_keystore_encryption():
3365                try:
3366                    wallet.check_password(old_password)
3367                except:
3368                    failed.append(basename)
3369                    continue
3370                if new_password:
3371                    wallet.update_password(old_password, new_password)
3372            else:
3373                if new_password:
3374                    wallet.update_password(None, new_password)
3375            continue
3376        if not storage.is_encrypted_with_user_pw():
3377            failed.append(basename)
3378            continue
3379        try:
3380            storage.check_password(old_password)
3381        except:
3382            failed.append(basename)
3383            continue
3384        try:
3385            db = WalletDB(storage.read(), manual_upgrades=False)
3386            wallet = Wallet(db, storage, config=config)
3387        except:
3388            _logger.exception(f'failed to load {basename}:')
3389            failed.append(basename)
3390            continue
3391        try:
3392            wallet.check_password(old_password)
3393        except:
3394            failed.append(basename)
3395            continue
3396        if new_password:
3397            wallet.update_password(old_password, new_password)
3398    can_be_unified = failed == []
3399    is_unified = can_be_unified and is_unified
3400    return can_be_unified, is_unified
3401
3402
3403def update_password_for_directory(config: SimpleConfig, old_password, new_password) -> bool:
3404    " returns whether password is unified "
3405    if new_password is None:
3406        # we opened a non-encrypted wallet
3407        return False
3408    can_be_unified, is_unified = check_password_for_directory(config, old_password, None)
3409    if not can_be_unified:
3410        return False
3411    if is_unified and old_password == new_password:
3412        return True
3413    check_password_for_directory(config, old_password, new_password)
3414    return True
3415