PCMARTINEAU / satoshit-nakamoto

/home/user
0 stars 0 forks source link

wallet #1

Open PCMARTINEAU opened 3 years ago

PCMARTINEAU commented 3 years ago

bitcoin-cli getwalletinfo-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256

60c93e3462c303eb080be7cf623f1a7684b37fd47a018ad3848bc23e13c84e1c bitcoin-0.20.1-aarch64-linux-gnu.tar.gz 55b577e0fb306fb429d4be6c9316607753e8543e5946b542d75d876a2f08654c bitcoin-0.20.1-arm-linux-gnueabihf.tar.gz b9024dde373ea7dad707363e07ec7e265383204127539ae0c234bff3a61da0d1 bitcoin-0.20.1-osx64.tar.gz c378d4e21109f09e8829f3591e015c66632dff2925a60b64d259be05a334c30b bitcoin-0.20.1-osx.dmg fa71cb52ee5e0459cbf5248cdec72df27995840c796f58b304607a1ed4c165af bitcoin-0.20.1-riscv64-linux-gnu.tar.gz 4bbd62fd6acfa5e9864ebf37a24a04bc2dcfe3e3222f056056288d854c53b978 bitcoin-0.20.1.tar.gz 930b96e774f5fe4795b9a3c0d4fd1da278d2b0777c9401dea3ba7453f8bbe14c bitcoin-0.20.1-win64-setup.exe e59fba67afce011d32b5d723a3a0be12da1b8a34f5d7966e504520c48d64716d bitcoin-0.20.1-win64.zip 376194f06596ecfa40331167c39bc70c355f960280bd2a645fdbf18f66527397 bitcoin-0.20.1-x86_64-linux-gnu.tar.gz -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux)

iQIcBAEBCAAGBQJfJU5lAAoJEJDIAZ42wulkAbAP/1pyFrxMrUFlNJdA8yCr0Dj5 5FBBGzFhVrAU+ouiJrPOf/X1HSM8haZ2TSsdI7Mg+S9wbMfFPBmeTIZNtdPkrEfB YWEWqkhYZZoSGxaOwQVyQmBTLyvlZcLM0Tuz5cVTcJ4ttFnrZBAcr251MVYRbJWx RxVb67GBgO7OOmxTfRIrgKLHAZo7zTdpPrSMhHEfcarf0A1r4xwUJCtNFRtBUQXi uacUessZCeZ3x+qoqRHdoeVJDXzshFzqVx7jLMMTKUgQ7HBiH+sWOwZi8VFeCI8u XgzPW6lv5Y1F5gOjfbHq+N+PggOBBukIKTHx9m6y60RSRXB08DwDnAldK900L+LD 8QUBNxzbCIKx65W80Nhb1GZxIWm89byvb1BR8LCk/P+8MBppp8x9zWfAdtOmnFXG pl34Q0en8dZvd+0tpNmEKcNHNVzVCF1hFmf9PPS6DW/AzFzYjVggsB1LKLbThjts XtdB5wcCEbFWSFsH3th/j7+gA1UkE6gnxq2lZdNQMV99KDZf6oP+XykKeq7xFIWG y2omYiRiFw9UkiUDNIhrE2wHA+VDCsOUwo2HM0dGJ/ZegdqLO5YjNP0nOi3Q9JP5 TeQF/DvkVTnU/ex0URkbkYY6SDod0f1KasFm1Ft3ygL9FXDncgYycYvAn7T7M3Wv QSEtaREvgcH90iLyHl90 =szhI -----END PGP SIGNATURE-----

PCMARTINEAU commented 3 years ago

puppets at play.png

PCMARTINEAU commented 3 years ago

add-apt-repository ppa:bitcoin/bitcoin

apt-get update

apt-get install libdb4.8-dev libdb4.8++-dev

mkdir /downloads

cd /downloads

git clone https://github.com/bitcoin/bitcoin.git

cd bitcoin

./autogen.sh

./configure

make

cd src

install -sv bitcoind bitcoin-cli /usr/local/bin/

mkdir ~/.bitcoin

vi $HOME/.bitcoin/bitcoin.conf

rpcuser=satoshi@nakamoto rpcpassword=01032009key

bitcoind -daemon

tail -f ~/.bitcoin/debug.log

bitcoin-cli stop

bitcoin-cli -getinfo

bitcoin-cli getblockchaininfo

bitcoin-cli getnetworkinfo

bitcoin-cli getwalletinfo

bitcoin-cli getpeerinfo

PCMARTINEAU commented 3 years ago

Electrum - lightweight Bitcoin client

Copyright (C) 2015 Thomas Voegtlin

#

Permission is hereby granted, free of charge, to any person

obtaining a copy of this software and associated documentation files

(the "Software"), to deal in the Software without restriction,

including without limitation the rights to use, copy, modify, merge,

publish, distribute, sublicense, and/or sell copies of the Software,

and to permit persons to whom the Software is furnished to do so,

subject to the following conditions:

#

The above copyright notice and this permission notice shall be

included in all copies or substantial portions of the Software.

#

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,

EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF

MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND

NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS

BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN

ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN

CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

SOFTWARE.

Wallet classes:

- Imported_Wallet: imported addresses or single keys, 0 or 1 keystore

- Standard_Wallet: one HD keystore, P2PKH-like scripts

- Multisig_Wallet: several HD keystores, M-of-N OP_CHECKMULTISIG scripts

import os import sys import random import time import json import copy import errno import traceback import operator import math from functools import partial from collections import defaultdict from numbers import Number from decimal import Decimal from typing import TYPE_CHECKING, List, Optional, Tuple, Union, NamedTuple, Sequence, Dict, Any, Set from abc import ABC, abstractmethod import itertools

from aiorpcx import TaskGroup

from .i18n import _ from .bip32 import BIP32Node, convert_bip32_intpath_to_strpath, convert_bip32_path_to_list_of_uint32 from .crypto import sha256 from . import util from .util import (NotEnoughFunds, UserCancelled, profiler, format_satoshis, format_fee_satoshis, NoDynamicFeeEstimates, WalletFileException, BitcoinException, MultipleSpendMaxTxOutputs, InvalidPassword, format_time, timestamp_to_datetime, Satoshis, Fiat, bfh, bh2u, TxMinedInfo, quantize_feerate, create_bip21_uri, OrderedDictWithIndex) from .util import get_backup_dir from .simple_config import SimpleConfig from .bitcoin import COIN, TYPE_ADDRESS from .bitcoin import is_address, address_to_script, is_minikey, relayfee, dust_threshold from .crypto import sha256d from . import keystore from .keystore import load_keystore, Hardware_KeyStore, KeyStore, KeyStoreWithMPK, AddressIndexGeneric from .util import multisig_type from .storage import StorageEncryptionVersion, WalletStorage from .wallet_db import WalletDB from . import transaction, bitcoin, coinchooser, paymentrequest, ecc, bip32 from .transaction import (Transaction, TxInput, UnknownTxinType, TxOutput, PartialTransaction, PartialTxInput, PartialTxOutput, TxOutpoint) from .plugin import run_hook from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL, TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE) from .invoices import Invoice, OnchainInvoice, LNInvoice from .invoices import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED, PR_INFLIGHT, PR_TYPE_ONCHAIN, PR_TYPE_LN from .contacts import Contacts from .interface import NetworkException from .mnemonic import Mnemonic from .logging import get_logger from .lnworker import LNWallet, LNBackups from .paymentrequest import PaymentRequest from .util import read_json_file, write_json_file, UserFacingException

if TYPE_CHECKING: from .network import Network

_logger = get_logger(name)

TXSTATUS = [ ('Unconfirmed'), ('Unconfirmed parent'), ('Not Verified'), _('Local'), ]

async def _append_utxos_to_inputs(*, inputs: List[PartialTxInput], network: 'Network', pubkey: str, txin_type: str, imax: int) -> None: if txin_type in ('p2pkh', 'p2wpkh', 'p2wpkh-p2sh'): address = bitcoin.pubkey_to_address(txin_type, pubkey) scripthash = bitcoin.address_to_scripthash(address) elif txin_type == 'p2pk': script = bitcoin.public_key_to_p2pk_script(pubkey) scripthash = bitcoin.script_to_scripthash(script) else: raise Exception(f'unexpected txin_type to sweep: {txin_type}')

async def append_single_utxo(item):
    prev_tx_raw = await network.get_transaction(item['tx_hash'])
    prev_tx = Transaction(prev_tx_raw)
    prev_txout = prev_tx.outputs()[item['tx_pos']]
    if scripthash != bitcoin.script_to_scripthash(prev_txout.scriptpubkey.hex()):
        raise Exception('scripthash mismatch when sweeping')
    prevout_str = item['tx_hash'] + ':%d' % item['tx_pos']
    prevout = TxOutpoint.from_str(prevout_str)
    txin = PartialTxInput(prevout=prevout)
    txin.utxo = prev_tx
    txin.block_height = int(item['height'])
    txin.script_type = txin_type
    txin.pubkeys = [bfh(pubkey)]
    txin.num_sig = 1
    if txin_type == 'p2wpkh-p2sh':
        txin.redeem_script = bfh(bitcoin.p2wpkh_nested_script(pubkey))
    inputs.append(txin)

u = await network.listunspent_for_scripthash(scripthash)
async with TaskGroup() as group:
    for item in u:
        if len(inputs) >= imax:
            break
        await group.spawn(append_single_utxo(item))

async def sweep_preparations(privkeys, network: 'Network', imax=100):

async def find_utxos_for_privkey(txin_type, privkey, compressed):
    pubkey = ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed)
    await _append_utxos_to_inputs(
        inputs=inputs,
        network=network,
        pubkey=pubkey,
        txin_type=txin_type,
        imax=imax)
    keypairs[pubkey] = privkey, compressed

inputs = []  # type: List[PartialTxInput]
keypairs = {}
async with TaskGroup() as group:
    for sec in privkeys:
        txin_type, privkey, compressed = bitcoin.deserialize_privkey(sec)
        await group.spawn(find_utxos_for_privkey(txin_type, privkey, compressed))
        # do other lookups to increase support coverage
        if is_minikey(sec):
            # minikeys don't have a compressed byte
            # we lookup both compressed and uncompressed pubkeys
            await group.spawn(find_utxos_for_privkey(txin_type, privkey, not compressed))
        elif txin_type == 'p2pkh':
            # WIF serialization does not distinguish p2pkh and p2pk
            # we also search for pay-to-pubkey outputs
            await group.spawn(find_utxos_for_privkey('p2pk', privkey, compressed))
if not inputs:
    raise UserFacingException(_('No inputs found.'))
return inputs, keypairs

def sweep(privkeys, *, network: 'Network', config: 'SimpleConfig', to_address: str, fee: int = None, imax=100, locktime=None, tx_version=None) -> PartialTransaction: inputs, keypairs = network.run_from_another_thread(sweep_preparations(privkeys, network, imax)) total = sum(txin.value_sats() for txin in inputs) if fee is None: outputs = [PartialTxOutput(scriptpubkey=bfh(bitcoin.address_to_script(to_address)), value=total)] tx = PartialTransaction.from_io(inputs, outputs) fee = config.estimate_fee(tx.estimatedsize()) if total - fee < 0: raise Exception(('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d'%(total, fee)) if total - fee < dustthreshold(network): raise Exception(('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d\nDust Threshold: %d'%(total, fee, dust_threshold(network)))

outputs = [PartialTxOutput(scriptpubkey=bfh(bitcoin.address_to_script(to_address)),
                           value=total - fee)]
if locktime is None:
    locktime = get_locktime_for_new_transaction(network)

tx = PartialTransaction.from_io(inputs, outputs, locktime=locktime, version=tx_version)
rbf = config.get('use_rbf', True)
if rbf:
    tx.set_rbf(True)
tx.sign(keypairs)
return tx

def get_locktime_for_new_transaction(network: 'Network') -> int:

if no network or not up to date, just set locktime to zero

if not network:
    return 0
chain = network.blockchain()
if chain.is_tip_stale():
    return 0
# discourage "fee sniping"
locktime = chain.height()
# sometimes pick locktime a bit further back, to help privacy
# of setups that need more time (offline/multisig/coinjoin/...)
if random.randint(0, 9) == 0:
    locktime = max(0, locktime - random.randint(0, 99))
return locktime

class CannotBumpFee(Exception): pass

class CannotDoubleSpendTx(Exception): pass

class InternalAddressCorruption(Exception): def str(self): return _("Wallet file corruption detected. " "Please restore your wallet from seed, and compare the addresses in both files")

class TxWalletDetails(NamedTuple): txid: Optional[str] status: str label: str can_broadcast: bool can_bump: bool can_dscancel: bool # whether user can double-spend to self can_save_as_local: bool amount: Optional[int] fee: Optional[int] tx_mined_status: TxMinedInfo mempool_depth_bytes: Optional[int] can_remove: bool # whether user should be allowed to delete tx is_lightning_funding_tx: bool

class Abstract_Wallet(AddressSynchronizer, ABC): """ Wallet classes are created to handle various address generation methods. Completion states (watching-only, single account, no seed, etc) are handled inside classes. """

LOGGING_SHORTCUT = 'w'
max_change_outputs = 3
gap_limit_for_change = 10

txin_type: str
wallet_type: str
lnworker: Optional['LNWallet']
lnbackups: Optional['LNBackups']

def __init__(self, db: WalletDB, storage: Optional[WalletStorage], *, config: SimpleConfig):
    if not db.is_ready_to_be_used_by_wallet():
        raise Exception("storage not ready to be used by Abstract_Wallet")

    self.config = config
    assert self.config is not None, "config must not be None"
    self.db = db
    self.storage = storage
    # load addresses needs to be called before constructor for sanity checks
    db.load_addresses(self.wallet_type)
    self.keystore = None  # type: Optional[KeyStore]  # will be set by load_keystore
    AddressSynchronizer.__init__(self, db)

    # saved fields
    self.use_change            = db.get('use_change', True)
    self.multiple_change       = db.get('multiple_change', False)
    self._labels                = db.get_dict('labels')
    self.frozen_addresses      = set(db.get('frozen_addresses', []))
    self.frozen_coins          = set(db.get('frozen_coins', []))  # set of txid:vout strings
    self.fiat_value            = db.get_dict('fiat_value')
    self.receive_requests      = db.get_dict('payment_requests')  # type: Dict[str, Invoice]
    self.invoices              = db.get_dict('invoices')  # type: Dict[str, Invoice]
    self._reserved_addresses   = set(db.get('reserved_addresses', []))

    self._prepare_onchain_invoice_paid_detection()
    self.calc_unused_change_addresses()
    # save wallet type the first time
    if self.db.get('wallet_type') is None:
        self.db.put('wallet_type', self.wallet_type)
    self.contacts = Contacts(self.db)
    self._coin_price_cache = {}

    self.lnworker = None
    # a wallet may have channel backups, regardless of lnworker activation
    self.lnbackups = LNBackups(self)

def save_db(self):
    if self.storage:
        self.db.write(self.storage)

def save_backup(self):
    backup_dir = get_backup_dir(self.config)
    if backup_dir is None:
        return
    new_db = WalletDB(self.db.dump(), manual_upgrades=False)

    if self.lnworker:
        channel_backups = new_db.get_dict('channel_backups')
        for chan_id, chan in self.lnworker.channels.items():
            channel_backups[chan_id.hex()] = self.lnworker.create_channel_backup(chan_id)
        new_db.put('channels', None)    new_db.put('lightning_privkey2', None)

    new_path = os.path.join(backup_dir, self.basename() + '.backup')
    new_storage = WalletStorage(new_path)
    new_storage._encryption_version = self.storage._encryption_version
    new_storage.pubkey = self.storage.pubkey
    new_db.set_modified(True)
    new_db.write(new_storage)
    return new_path

def has_lightning(self):
    return bool(self.lnworker)

def can_have_lightning(self):
    # we want static_remotekey to be a wallet address
    return self.txin_type == 'p2wpkh'

def init_lightning(self):
    assert self.can_have_lightning()
    if self.db.get('lightning_privkey2'):
        return
    # TODO derive this deterministically from wallet.keystore at keystore generation time
    # probably along a hardened path ( lnd-equivalent would be m/1017'/coinType'/ )
    seed = os.urandom(32)
    node = BIP32Node.from_rootseed(seed, xtype='standard')
    ln_xprv = node.to_xprv()
    self.db.put('lightning_privkey2', ln_xprv)

def stop(self):
    super().stop()
    if any([ks.is_requesting_to_be_rewritten_to_wallet_file for ks in self.get_keystores()]):
        self.save_keystore()
    if self.network:
        if self.lnworker:
            self.lnworker.stop()
            self.lnworker = None
        self.lnbackups.stop()
        self.lnbackups = None
    self.save_db()

def set_up_to_date(self, b):
    super().set_up_to_date(b)
    if b: self.save_db()

def clear_history(self):
    super().clear_history()
    self.save_db()

def start_network(self, network):
    AddressSynchronizer.start_network(self, network)
    if network:
        if self.lnworker:
            self.lnworker.start_network(network)
            # only start gossiping when we already have channels
            if self.db.get('channels'):
                self.network.start_gossip()
        self.lnbackups.start_network(network)

def load_and_cleanup(self):
    self.load_keystore()
    self.test_addresses_sanity()
    super().load_and_cleanup()

@abstractmethod
def load_keystore(self) -> None:
    pass

def diagnostic_name(self):
    return self.basename()

def __str__(self):
    return self.basename()

def get_master_public_key(self):
    return None

def get_master_public_keys(self):
    return []

def basename(self) -> str:
    return self.storage.basename() if self.storage else 'no name'

def test_addresses_sanity(self) -> None:
    addrs = self.get_receiving_addresses()
    if len(addrs) > 0:
        addr = str(addrs[0])
        if not bitcoin.is_address(addr):
            neutered_addr = addr[:5] + '..' + addr[-2:]
            raise WalletFileException(f'The addresses in this wallet are not bitcoin addresses.\n'
                                      f'e.g. {neutered_addr} (length: {len(addr)})')

def check_returned_address_for_corruption(func):
    def wrapper(self, *args, **kwargs):
        addr = func(self, *args, **kwargs)
        self.check_address_for_corruption(addr)
        return addr
    return wrapper

def calc_unused_change_addresses(self) -> Sequence[str]:
    """Returns a list of change addresses to choose from, for usage in e.g. new transactions.
    The caller should give priority to earlier ones in the list.
    """
    with self.lock:
        # We want a list of unused change addresses.
        # As a performance optimisation, to avoid checking all addresses every time,
        # we maintain a list of "not old" addresses ("old" addresses have deeply confirmed history),
        # and only check those.
        if not hasattr(self, '_not_old_change_addresses'):
            self._not_old_change_addresses = self.get_change_addresses()
        self._not_old_change_addresses = [addr for addr in self._not_old_change_addresses
                                          if not self.address_is_old(addr)]
        unused_addrs = [addr for addr in self._not_old_change_addresses
                        if not self.is_used(addr) and not self.is_address_reserved(addr)]
        return unused_addrs

def is_deterministic(self) -> bool:
    return self.keystore.is_deterministic()

def _set_label(self, key: str, value: Optional[str]) -> None:
    with self.lock:
        if value is None:
            self._labels.pop(key, None)
        else:
            self._labels[key] = value

def set_label(self, name: str, text: str = None) -> bool:
    if not name:
        return False
    changed = False
    with self.lock:
        old_text = self._labels.get(name)
        if text:
            text = text.replace("\n", " ")
            if old_text != text:
                self._labels[name] = text
                changed = True
        else:
            if old_text is not None:
                self._labels.pop(name)
                changed = True
    if changed:
        run_hook('set_label', self, name, text)
    return changed

def import_labels(self, path):
    data = read_json_file(path)
    for key, value in data.items():
        self.set_label(key, value)

def export_labels(self, path):
    write_json_file(path, self.get_all_labels())

def set_fiat_value(self, txid, ccy, text, fx, value_sat):
    if not self.db.get_transaction(txid):
        return
    # since fx is inserting the thousands separator,
    # and not util, also have fx remove it
    text = fx.remove_thousands_separator(text)
    def_fiat = self.default_fiat_value(txid, fx, value_sat)
    formatted = fx.ccy_amount_str(def_fiat, commas=False)
    def_fiat_rounded = Decimal(formatted)
    reset = not text
    if not reset:
        try:
            text_dec = Decimal(text)
            text_dec_rounded = Decimal(fx.ccy_amount_str(text_dec, commas=False))
            reset = text_dec_rounded == def_fiat_rounded
        except:
            # garbage. not resetting, but not saving either
            return False
    if reset:
        d = self.fiat_value.get(ccy, {})
        if d and txid in d:
            d.pop(txid)
        else:
            # avoid saving empty dict
            return True
    else:
        if ccy not in self.fiat_value:
            self.fiat_value[ccy] = {}
        self.fiat_value[ccy][txid] = text
    return reset

def get_fiat_value(self, txid, ccy):
    fiat_value = self.fiat_value.get(ccy, {}).get(txid)
    try:
        return Decimal(fiat_value)
    except:
        return

def is_mine(self, address) -> bool:
    if not address: return False
    return bool(self.get_address_index(address))

def is_change(self, address) -> bool:
    if not self.is_mine(address):
        return False
    return self.get_address_index(address)[0] == 1

@abstractmethod
def get_address_index(self, address: str) -> Optional[AddressIndexGeneric]:
    pass

@abstractmethod
def get_address_path_str(self, address: str) -> Optional[str]:
    """Returns derivation path str such as "m/0/5" to address,
    or None if not applicable.
    """
    pass

@abstractmethod
def get_redeem_script(self, address: str) -> Optional[str]:
    pass

@abstractmethod
def get_witness_script(self, address: str) -> Optional[str]:
    pass

@abstractmethod
def get_txin_type(self, address: str) -> str:
    """Return script type of wallet address."""
    pass

def export_private_key(self, address: str, password: Optional[str]) -> str:
    if self.is_watching_only():
        raise Exception(_("This is a watching-only wallet"))
    if not is_address(address):
        raise Exception(f"Invalid bitcoin address: {address}")
    if not self.is_mine(address):
        raise Exception(_('Address not in wallet.') + f' {address}')
    index = self.get_address_index(address)
    pk, compressed = self.keystore.get_private_key(index, password)
    txin_type = self.get_txin_type(address)
    serialized_privkey = bitcoin.serialize_privkey(pk, compressed, txin_type)
    return serialized_privkey

def export_private_key_for_path(self, path: Union[Sequence[int], str], password: Optional[str]) -> str:
    raise Exception("this wallet is not deterministic")

@abstractmethod
def get_public_keys(self, address: str) -> Sequence[str]:
    pass

def get_public_keys_with_deriv_info(self, address: str) -> Dict[bytes, Tuple[KeyStoreWithMPK, Sequence[int]]]:
    """Returns a map: pubkey -> (keystore, derivation_suffix)"""
    return {}

def get_tx_info(self, tx: Transaction) -> TxWalletDetails:
    tx_wallet_delta = self.get_wallet_delta(tx)
    is_relevant = tx_wallet_delta.is_relevant
    is_any_input_ismine = tx_wallet_delta.is_any_input_ismine
    fee = tx_wallet_delta.fee
    exp_n = None
    can_broadcast = False
    can_bump = False
    tx_hash = tx.txid()  # note: txid can be None! e.g. when called from GUI tx dialog
    is_lightning_funding_tx = False
    if self.has_lightning() and tx_hash is not None:
        is_lightning_funding_tx = any([chan.funding_outpoint.txid == tx_hash
                                       for chan in self.lnworker.channels.values()])
    tx_we_already_have_in_db = self.db.get_transaction(tx_hash)
    can_save_as_local = (is_relevant and tx.txid() is not None
                         and (tx_we_already_have_in_db is None or not tx_we_already_have_in_db.is_complete()))
    label = ''
    tx_mined_status = self.get_tx_height(tx_hash)
    can_remove = ((tx_mined_status.height in [TX_HEIGHT_FUTURE, TX_HEIGHT_LOCAL])
                  # otherwise 'height' is unreliable (typically LOCAL):
                  and is_relevant
                  # don't offer during common signing flow, e.g. when watch-only wallet starts creating a tx:
                  and bool(tx_we_already_have_in_db))
    can_dscancel = False
    if tx.is_complete():
        if tx_we_already_have_in_db:
            label = self.get_label_for_txid(tx_hash)
            if tx_mined_status.height > 0:
                if tx_mined_status.conf:
                    status = _("{} confirmations").format(tx_mined_status.conf)
                else:
                    status = _('Not verified')      elif tx_mined_status.height in (TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED):
                status = _('Unconfirmed')
                if fee is None:
                    fee = self.get_tx_fee(tx_hash)
                if fee and self.network and self.config.has_fee_mempool():
                    size = tx.estimated_size()
                    fee_per_byte = fee / size
                    exp_n = self.config.fee_to_depth(fee_per_byte)
                can_bump = is_any_input_ismine and not tx.is_final()
                can_dscancel = (is_any_input_ismine and not tx.is_final()
                                and not all([self.is_mine(txout.address) for txout in tx.outputs()]))
            else:
                status = _('Local')
                can_broadcast = self.network is not None
                can_bump = is_any_input_ismine and not tx.is_final()
        else:
            status = _("Signed")
            can_broadcast = self.network is not None
    else:
        assert isinstance(tx, PartialTransaction)
        s, r = tx.signature_count()
        status = _("Unsigned") if s == 0 else _('Partially signed') + ' (%d/%d)'%(s,r)

    if is_relevant:
        if tx_wallet_delta.is_all_input_ismine:
            assert fee is not None
            amount = tx_wallet_delta.delta + fee
        else:
            amount = tx_wallet_delta.delta
    else:
        amount = None

    if is_lightning_funding_tx:
        can_bump = False  # would change txid

    return TxWalletDetails(
        txid=tx_hash,
        status=status,
        label=label,
        can_broadcast=can_broadcast,
        can_bump=can_bump,
        can_dscancel=can_dscancel,
        can_save_as_local=can_save_as_local,
        amount=amount,
        fee=fee,
        tx_mined_status=tx_mined_status,
        mempool_depth_bytes=exp_n,
        can_remove=can_remove,
        is_lightning_funding_tx=is_lightning_funding_tx,
    )

def get_spendable_coins(self, domain, *, nonlocal_only=False) -> Sequence[PartialTxInput]:
    confirmed_only = self.config.get('confirmed_only', False)
    utxos = self.get_utxos(domain,
                           excluded_addresses=self.frozen_addresses,
                           mature_only=True,
                           confirmed_only=confirmed_only,
                           nonlocal_only=nonlocal_only)
    utxos = [utxo for utxo in utxos if not self.is_frozen_coin(utxo)]
    return utxos

@abstractmethod
def get_receiving_addresses(self, *, slice_start=None, slice_stop=None) -> Sequence[str]:
    pass

@abstractmethod
def get_change_addresses(self, *, slice_start=None, slice_stop=None) -> Sequence[str]:
    pass

def dummy_address(self):
    # first receiving address
    return self.get_receiving_addresses(slice_start=0, slice_stop=1)[0]

def get_frozen_balance(self):
    if not self.frozen_coins:  # shortcut
        return self.get_balance(self.frozen_addresses)
    c1, u1, x1 = self.get_balance()
    c2, u2, x2 = self.get_balance(excluded_addresses=self.frozen_addresses,
                                  excluded_coins=self.frozen_coins)
    return c1-c2, u1-u2, x1-x2

def balance_at_timestamp(self, domain, target_timestamp):
    # we assume that get_history returns items ordered by block height
    # we also assume that block timestamps are monotonic (which is false...!)
    h = self.get_history(domain=domain)
    balance = 0
    for hist_item in h:
        balance = hist_item.balance
        if hist_item.tx_mined_status.timestamp is None or hist_item.tx_mined_status.timestamp > target_timestamp:
            return balance - hist_item.delta
    # return last balance
    return balance

def get_onchain_history(self, *, domain=None):
    monotonic_timestamp = 0
    for hist_item in self.get_history(domain=domain):
        monotonic_timestamp = max(monotonic_timestamp, (hist_item.tx_mined_status.timestamp or 999_999_999_999))
        yield {
            'txid': hist_item.txid,
            'fee_sat': hist_item.fee,
            'height': hist_item.tx_mined_status.height,
            'confirmations': hist_item.tx_mined_status.conf,
            'timestamp': hist_item.tx_mined_status.timestamp,
            'monotonic_timestamp': monotonic_timestamp,
            'incoming': True if hist_item.delta>0 else False,
            'bc_value': Satoshis(hist_item.delta),
            'bc_balance': Satoshis(hist_item.balance),
            'date': timestamp_to_datetime(hist_item.tx_mined_status.timestamp),
            'label': self.get_label_for_txid(hist_item.txid),
            'txpos_in_block': hist_item.tx_mined_status.txpos,
        }

def create_invoice(self, *, outputs: List[PartialTxOutput], message, pr, URI) -> Invoice:
    if pr:
        return OnchainInvoice.from_bip70_payreq(pr)
    if '!' in (x.value for x in outputs):
        amount = '!'
    else:
        amount = sum(x.value for x in outputs)
    timestamp = None
    exp = None
    if URI:
        timestamp = URI.get('time')
        exp = URI.get('exp')
    timestamp = timestamp or int(time.time())
    exp = exp or 0
    invoice = OnchainInvoice(
        type=PR_TYPE_ONCHAIN,
        amount_sat=amount,
        outputs=outputs,
        message=message,
        id=bh2u(sha256(repr(outputs))[0:16]),
        time=timestamp,
        exp=exp,
        bip70=None,
        requestor=None,
    )
    return invoice

def save_invoice(self, invoice: Invoice) -> None:
    invoice_type = invoice.type
    if invoice_type == PR_TYPE_LN:
        assert isinstance(invoice, LNInvoice)
        key = invoice.rhash
    elif invoice_type == PR_TYPE_ONCHAIN:
        assert isinstance(invoice, OnchainInvoice)
        key = invoice.id
        if self.is_onchain_invoice_paid(invoice):
            self.logger.info("saving invoice... but it is already paid!")
        with self.transaction_lock:
            for txout in invoice.outputs:
                self._invoices_from_scriptpubkey_map[txout.scriptpubkey].add(key)
    else:
        raise Exception('Unsupported invoice type')
    self.invoices[key] = invoice
    self.save_db()

def clear_invoices(self):
    self.invoices = {}
    self.save_db()

def clear_requests(self):
    self.receive_requests = {}
    self.save_db()

def get_invoices(self):
    out = list(self.invoices.values())
    #out = list(filter(None, out)) filter out ln
    out.sort(key=lambda x:x.time)
    return out

def get_invoice(self, key):
    return self.invoices.get(key)

def import_requests(self, path):
    data = read_json_file(path)
    for x in data:
        req = Invoice.from_json(x)
        self.add_payment_request(req)

def export_requests(self, path):
    write_json_file(path, list(self.receive_requests.values()))

def import_invoices(self, path):
    data = read_json_file(path)
    for x in data:
        invoice = Invoice.from_json(x)
        self.save_invoice(invoice)

def export_invoices(self, path):
    write_json_file(path, list(self.invoices.values()))

def _get_relevant_invoice_keys_for_tx(self, tx: Transaction) -> Set[str]:
    relevant_invoice_keys = set()
    with self.transaction_lock:
        for txout in tx.outputs():
            for invoice_key in self._invoices_from_scriptpubkey_map.get(txout.scriptpubkey, set()):
                # note: the invoice might have been deleted since, so check now:
                if invoice_key in self.invoices:
                    relevant_invoice_keys.add(invoice_key)
    return relevant_invoice_keys

def get_relevant_invoices_for_tx(self, tx: Transaction) -> Sequence[OnchainInvoice]:
    invoice_keys = self._get_relevant_invoice_keys_for_tx(tx)
    invoices = [self.get_invoice(key) for key in invoice_keys]
    invoices = [inv for inv in invoices if inv]  # filter out None
    for inv in invoices:
        assert isinstance(inv, OnchainInvoice), f"unexpected type {type(inv)}"
    return invoices

def _prepare_onchain_invoice_paid_detection(self):
    # scriptpubkey -> list(invoice_keys)
    self._invoices_from_scriptpubkey_map = defaultdict(set)  # type: Dict[bytes, Set[str]]
    for invoice_key, invoice in self.invoices.items():
        if invoice.type == PR_TYPE_ONCHAIN:         assert isinstance(invoice, OnchainInvoice)
            for txout in invoice.outputs:
                self._invoices_from_scriptpubkey_map[txout.scriptpubkey].add(invoice_key)

def _is_onchain_invoice_paid(self, invoice: Invoice) -> Tuple[bool, Sequence[str]]:
    """Returns whether on-chain invoice is satisfied, and list of relevant TXIDs."""
    assert invoice.type == PR_TYPE_ONCHAIN
    assert isinstance(invoice, OnchainInvoice)
    invoice_amounts = defaultdict(int)  # type: Dict[bytes, int]  # scriptpubkey -> value_sats
    for txo in invoice.outputs:  # type: PartialTxOutput
        invoice_amounts[txo.scriptpubkey] += 1 if txo.value == '!' else txo.value
    relevant_txs = []
    with self.transaction_lock:
        for invoice_scriptpubkey, invoice_amt in invoice_amounts.items():
            scripthash = bitcoin.script_to_scripthash(invoice_scriptpubkey.hex())
            prevouts_and_values = self.db.get_prevouts_by_scripthash(scripthash)
            relevant_txs += [prevout.txid.hex() for prevout, v in prevouts_and_values]
            total_received = sum([v for prevout, v in prevouts_and_values])
            # check that there is at least one TXO, and that they pay enough.
            # note: "at least one TXO" check is needed for zero amount invoice (e.g. OP_RETURN)
            if len(prevouts_and_values) == 0:
                return False, []
            if total_received < invoice_amt:
                return False, []
    return True, relevant_txs

def is_onchain_invoice_paid(self, invoice: Invoice) -> bool:
    return self._is_onchain_invoice_paid(invoice)[0]

def _maybe_set_tx_label_based_on_invoices(self, tx: Transaction) -> bool:
    # note: this is not done in 'get_default_label' as that would require deserializing each tx
    tx_hash = tx.txid()
    labels = []
    for invoice in self.get_relevant_invoices_for_tx(tx):
        if invoice.message:
            labels.append(invoice.message)
    if labels and not self._labels.get(tx_hash, ''):
        self.set_label(tx_hash, "; ".join(labels))
    return bool(labels)

def add_transaction(self, tx, *, allow_unrelated=False):
    tx_was_added = super().add_transaction(tx, allow_unrelated=allow_unrelated)

    if tx_was_added:
        self._maybe_set_tx_label_based_on_invoices(tx)
    return tx_was_added

@profiler
def get_full_history(self, fx=None, *, onchain_domain=None, include_lightning=True):
    transactions_tmp = OrderedDictWithIndex()
    # add on-chain txns
    onchain_history = self.get_onchain_history(domain=onchain_domain)
    lnworker_history = self.lnworker.get_onchain_history() if self.lnworker and include_lightning else {}
    for tx_item in onchain_history:
        txid = tx_item['txid']
        transactions_tmp[txid] = tx_item
        # add lnworker info here
        if txid in lnworker_history:
            item = lnworker_history[txid]
            tx_item['group_id'] = item.get('group_id')  # for swaps
            tx_item['label'] = item['label']
            tx_item['type'] = item['type']
            ln_value = Decimal(item['amount_msat']) / 1000   # for channel open/close tx
            tx_item['ln_value'] = Satoshis(ln_value)
    # add lightning_transactions
    lightning_history = self.lnworker.get_lightning_history() if self.lnworker and include_lightning else {}
    for tx_item in lightning_history.values():
        txid = tx_item.get('txid')
        ln_value = Decimal(tx_item['amount_msat']) / 1000
        tx_item['lightning'] = True
        tx_item['ln_value'] = Satoshis(ln_value)
        key = tx_item.get('txid') or tx_item['payment_hash']
        transactions_tmp[key] = tx_item
    # sort on-chain and LN stuff into new dict, by timestamp
    # (we rely on this being a *stable* sort)
    transactions = OrderedDictWithIndex()
    for k, v in sorted(list(transactions_tmp.items()),
                       key=lambda x: x[1].get('monotonic_timestamp') or x[1].get('timestamp') or float('inf')):
        transactions[k] = v
    now = time.time()
    balance = 0
    for item in transactions.values():
        # add on-chain and lightning values
        value = Decimal(0)
        if item.get('bc_value'):
            value += item['bc_value'].value
        if item.get('ln_value'):
            value += item.get('ln_value').value
        # note: 'value' and 'balance' has msat precision (as LN has msat precision)
        item['value'] = Satoshis(value)
        balance += value
        item['balance'] = Satoshis(balance)
        if fx and fx.is_enabled() and fx.get_history_config():
            txid = item.get('txid')
            if not item.get('lightning') and txid:
                fiat_fields = self.get_tx_item_fiat(txid, value, fx, item['fee_sat'])
                item.update(fiat_fields)
            else:
                timestamp = item['timestamp'] or now
                fiat_value = value / Decimal(bitcoin.COIN) * fx.timestamp_rate(timestamp)
                item['fiat_value'] = Fiat(fiat_value, fx.ccy)
                item['fiat_default'] = True
    return transactions

@profiler
def get_detailed_history(self, from_timestamp=None, to_timestamp=None,
                         fx=None, show_addresses=False):
    # History with capital gains, using utxo pricing
    # FIXME: Lightning capital gains would requires FIFO
    out = []
    income = 0
    expenditures = 0
    capital_gains = Decimal(0)
    fiat_income = Decimal(0)
    fiat_expenditures = Decimal(0)
    now = time.time()
    for item in self.get_onchain_history():
        timestamp = item['timestamp']
        if from_timestamp and (timestamp or now) < from_timestamp:
            continue
        if to_timestamp and (timestamp or now) >= to_timestamp:
            continue
        tx_hash = item['txid']
        tx = self.db.get_transaction(tx_hash)
        tx_fee = item['fee_sat']
        item['fee'] = Satoshis(tx_fee) if tx_fee is not None else None
        if show_addresses:
            item['inputs'] = list(map(lambda x: x.to_json(), tx.inputs()))
            item['outputs'] = list(map(lambda x: {'address': x.get_ui_address_str(), 'value': Satoshis(x.value)},
                                       tx.outputs()))
        # fixme: use in and out values
        value = item['bc_value'].value
        if value < 0:
            expenditures += -value
        else:
            income += value
        # fiat computations
        if fx and fx.is_enabled() and fx.get_history_config():
            fiat_fields = self.get_tx_item_fiat(tx_hash, value, fx, tx_fee)
            fiat_value = fiat_fields['fiat_value'].value
            item.update(fiat_fields)
            if value < 0:
                capital_gains += fiat_fields['capital_gain'].value
                fiat_expenditures += -fiat_value
            else:
                fiat_income += fiat_value
        out.append(item)
    # add summary
    if out:
        b, v = out[0]['bc_balance'].value, out[0]['bc_value'].value
        start_balance = None if b is None or v is None else b - v
        end_balance = out[-1]['bc_balance'].value
        if from_timestamp is not None and to_timestamp is not None:
            start_date = timestamp_to_datetime(from_timestamp)
            end_date = timestamp_to_datetime(to_timestamp)
        else:
            start_date = None
            end_date = None
        summary = {
            'start_date': start_date,
            'end_date': end_date,
            'start_balance': Satoshis(start_balance),
            'end_balance': Satoshis(end_balance),
            'incoming': Satoshis(income),
            'outgoing': Satoshis(expenditures)
        }
        if fx and fx.is_enabled() and fx.get_history_config():
            unrealized = self.unrealized_gains(None, fx.timestamp_rate, fx.ccy)
            summary['fiat_currency'] = fx.ccy
            summary['fiat_capital_gains'] = Fiat(capital_gains, fx.ccy)
            summary['fiat_incoming'] = Fiat(fiat_income, fx.ccy)
            summary['fiat_outgoing'] = Fiat(fiat_expenditures, fx.ccy)
            summary['fiat_unrealized_gains'] = Fiat(unrealized, fx.ccy)
            summary['fiat_start_balance'] = Fiat(fx.historical_value(start_balance, start_date), fx.ccy)
            summary['fiat_end_balance'] = Fiat(fx.historical_value(end_balance, end_date), fx.ccy)
            summary['fiat_start_value'] = Fiat(fx.historical_value(COIN, start_date), fx.ccy)
            summary['fiat_end_value'] = Fiat(fx.historical_value(COIN, end_date), fx.ccy)
    else:
        summary = {}
    return {
        'transactions': out,
        'summary': summary
    }

def default_fiat_value(self, tx_hash, fx, value_sat):
    return value_sat / Decimal(COIN) * self.price_at_timestamp(tx_hash, fx.timestamp_rate)

def get_tx_item_fiat(self, tx_hash, value, fx, tx_fee):
    item = {}
    fiat_value = self.get_fiat_value(tx_hash, fx.ccy)
    fiat_default = fiat_value is None
    fiat_rate = self.price_at_timestamp(tx_hash, fx.timestamp_rate)
    fiat_value = fiat_value if fiat_value is not None else self.default_fiat_value(tx_hash, fx, value)
    fiat_fee = tx_fee / Decimal(COIN) * fiat_rate if tx_fee is not None else None
    item['fiat_currency'] = fx.ccy
    item['fiat_rate'] = Fiat(fiat_rate, fx.ccy)
    item['fiat_value'] = Fiat(fiat_value, fx.ccy)
    item['fiat_fee'] = Fiat(fiat_fee, fx.ccy) if fiat_fee else None
    item['fiat_default'] = fiat_default
    if value < 0:
        acquisition_price = - value / Decimal(COIN) * self.average_price(tx_hash, fx.timestamp_rate, fx.ccy)
        liquidation_price = - fiat_value
        item['acquisition_price'] = Fiat(acquisition_price, fx.ccy)
        cg = liquidation_price - acquisition_price
        item['capital_gain'] = Fiat(cg, fx.ccy)
    return item

def get_label(self, key: str) -> str:
    # key is typically: address / txid / LN-payment-hash-hex
    return self._labels.get(key) or ''

def get_label_for_txid(self, tx_hash: str) -> str:
    return self._labels.get(tx_hash) or self._get_default_label_for_txid(tx_hash)

def _get_default_label_for_txid(self, tx_hash: str) -> str:
    # if no inputs are ismine, concat labels of output addresses
    if not self.db.get_txi_addresses(tx_hash):
        labels = []
        for addr in self.db.get_txo_addresses(tx_hash):
            label = self._labels.get(addr)
            if label:
                labels.append(label)
        return ', '.join(labels)
    return ''

def get_all_labels(self) -> Dict[str, str]:
    with self.lock:
        return copy.copy(self._labels)

def get_tx_status(self, tx_hash, tx_mined_info: TxMinedInfo):
    extra = []
    height = tx_mined_info.height
    conf = tx_mined_info.conf
    timestamp = tx_mined_info.timestamp
    if height == TX_HEIGHT_FUTURE:
        assert conf < 0, conf
        num_blocks_remainining = -conf
        return 2, f'in {num_blocks_remainining} blocks'
    if conf == 0:
        tx = self.db.get_transaction(tx_hash)
        if not tx:
            return 2, 'unknown'
        is_final = tx and tx.is_final()
        if not is_final:
            extra.append('rbf')
        fee = self.get_tx_fee(tx_hash)
        if fee is not None:
            size = tx.estimated_size()            fee_per_byte = fee / size
            extra.append(format_fee_satoshis(fee_per_byte) + ' sat/b')
        if fee is not None and height in (TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED) \
           and self.config.has_fee_mempool():
            exp_n = self.config.fee_to_depth(fee_per_byte)
            if exp_n is not None:
                extra.append('%.2f MB'%(exp_n/1000000))
        if height == TX_HEIGHT_LOCAL:
            status = 3
        elif height == TX_HEIGHT_UNCONF_PARENT:
            status = 1
        elif height == TX_HEIGHT_UNCONFIRMED:
            status = 0
        else:
            status = 2  # not SPV verified
    else:
        status = 3 + min(conf, 6)
    time_str = format_time(timestamp) if timestamp else _("unknown")
    status_str = TX_STATUS[status] if status < 4 else time_str
    if extra:
        status_str += ' [%s]'%(', '.join(extra))
    return status, status_str

def relayfee(self):
    return relayfee(self.network)

def dust_threshold(self):
    return dust_threshold(self.network)

def get_unconfirmed_base_tx_for_batching(self) -> Optional[Transaction]:
    candidate = None
    for hist_item in self.get_history():
        # tx should not be mined yet
        if hist_item.tx_mined_status.conf > 0: continue
        # conservative future proofing of code: only allow known unconfirmed types
        if hist_item.tx_mined_status.height not in (TX_HEIGHT_UNCONFIRMED,
                                                    TX_HEIGHT_UNCONF_PARENT,
                                                    TX_HEIGHT_LOCAL):
            continue
        # tx should be "outgoing" from wallet
        if hist_item.delta >= 0:
            continue
        tx = self.db.get_transaction(hist_item.txid)
        if not tx:
            continue
        # is_mine outputs should not be spent yet
        # to avoid cancelling our own dependent transactions
        txid = tx.txid()
        if any([self.is_mine(o.address) and self.db.get_spent_outpoint(txid, output_idx)
                for output_idx, o in enumerate(tx.outputs())]):
            continue
        # all inputs should be is_mine
        if not all([self.is_mine(self.get_txin_address(txin)) for txin in tx.inputs()]):
            continue
        # prefer txns already in mempool (vs local)
        if hist_item.tx_mined_status.height == TX_HEIGHT_LOCAL:
            candidate = tx
            continue
        # tx must have opted-in for RBF
        if tx.is_final(): continue
        return tx
    return candidate

def get_change_addresses_for_new_transaction(
        self, preferred_change_addr=None, *, allow_reuse: bool = True,
) -> List[str]:
    change_addrs = []
    if preferred_change_addr:
        if isinstance(preferred_change_addr, (list, tuple)):
            change_addrs = list(preferred_change_addr)
        else:
            change_addrs = [preferred_change_addr]
    elif self.use_change:
        # Recalc and get unused change addresses
        addrs = self.calc_unused_change_addresses()
        # New change addresses are created only after a few
        # confirmations.
        if addrs:
            # if there are any unused, select all
            change_addrs = addrs
        else:
            # if there are none, take one randomly from the last few
            if not allow_reuse:
                return []
            addrs = self.get_change_addresses(slice_start=-self.gap_limit_for_change)
            change_addrs = [random.choice(addrs)] if addrs else []
    for addr in change_addrs:
        assert is_address(addr), f"not valid bitcoin address: {addr}"
        # note that change addresses are not necessarily ismine
        # in which case this is a no-op
        self.check_address_for_corruption(addr)
    max_change = self.max_change_outputs if self.multiple_change else 1
    return change_addrs[:max_change]

def get_single_change_address_for_new_transaction(
        self, preferred_change_addr=None, *, allow_reuse: bool = True,
) -> Optional[str]:
    addrs = self.get_change_addresses_for_new_transaction(
        preferred_change_addr=preferred_change_addr,
        allow_reuse=allow_reuse,
    )
    if addrs:
        return addrs[0]
    return None

@check_returned_address_for_corruption
def get_new_sweep_address_for_channel(self) -> str:
    # Recalc and get unused change addresses
    addrs = self.calc_unused_change_addresses()
    if addrs:
        selected_addr = addrs[0]
    else:
        # if there are none, take one randomly from the last few
        addrs = self.get_change_addresses(slice_start=-self.gap_limit_for_change)
        if addrs:
            selected_addr = random.choice(addrs)
        else:  # fallback for e.g. imported wallets
            selected_addr = self.get_receiving_address()
    assert is_address(selected_addr), f"not valid bitcoin address: {selected_addr}"
    return selected_addr

def make_unsigned_transaction(self, *, coins: Sequence[PartialTxInput],
                              outputs: List[PartialTxOutput], fee=None,
                              change_addr: str = None, is_sweep=False) -> PartialTransaction:

    if any([c.already_has_some_signatures() for c in coins]):
        raise Exception("Some inputs already contain signatures!")

    # prevent side-effect with '!'
    outputs = copy.deepcopy(outputs)

    # check outputs
    i_max = None
    for i, o in enumerate(outputs):
        if o.value == '!':
            if i_max is not None:
                raise MultipleSpendMaxTxOutputs()
            i_max = i

    if fee is None and self.config.fee_per_kb() is None:
        raise NoDynamicFeeEstimates()

    for item in coins:
        self.add_input_info(item)

    # Fee estimator
    if fee is None:
        fee_estimator = self.config.estimate_fee
    elif isinstance(fee, Number):
        fee_estimator = lambda size: fee
    elif callable(fee):
        fee_estimator = fee
    else:
        raise Exception(f'Invalid argument fee: {fee}')

    if i_max is None:
        # Let the coin chooser select the coins to spend
        coin_chooser = coinchooser.get_coin_chooser(self.config)
        # If there is an unconfirmed RBF tx, merge with it
        base_tx = self.get_unconfirmed_base_tx_for_batching()
        if self.config.get('batch_rbf', False) and base_tx:
            # make sure we don't try to spend change from the tx-to-be-replaced:
            coins = [c for c in coins if c.prevout.txid.hex() != base_tx.txid()]
            is_local = self.get_tx_height(base_tx.txid()).height == TX_HEIGHT_LOCAL
            base_tx = PartialTransaction.from_tx(base_tx)
            base_tx.add_info_from_wallet(self)
            base_tx_fee = base_tx.get_fee()
            relayfeerate = Decimal(self.relayfee()) / 1000
            original_fee_estimator = fee_estimator
            def fee_estimator(size: Union[int, float, Decimal]) -> int:
                size = Decimal(size)
                lower_bound = base_tx_fee + round(size * relayfeerate)
                lower_bound = lower_bound if not is_local else 0
                return int(max(lower_bound, original_fee_estimator(size)))
            txi = base_tx.inputs()
            txo = list(filter(lambda o: not self.is_change(o.address), base_tx.outputs()))
            old_change_addrs = [o.address for o in base_tx.outputs() if self.is_change(o.address)]
        else:
            txi = []
            txo = []
            old_change_addrs = []
        # change address. if empty, coin_chooser will set it
        change_addrs = self.get_change_addresses_for_new_transaction(change_addr or old_change_addrs)
        tx = coin_chooser.make_tx(coins=coins,
                                  inputs=txi,
                                  outputs=list(outputs) + txo,
                                  change_addrs=change_addrs,
                                  fee_estimator_vb=fee_estimator,
                                  dust_threshold=self.dust_threshold())
    else:
        # "spend max" branch
        # note: This *will* spend inputs with negative effective value (if there are any).
        #       Given as the user is spending "max", and so might be abandoning the wallet,
        #       try to include all UTXOs, otherwise leftover might remain in the UTXO set
        #       forever. see #5433
        # note: Actually it might be the case that not all UTXOs from the wallet are
        #       being spent if the user manually selected UTXOs.
        sendable = sum(map(lambda c: c.value_sats(), coins))
        outputs[i_max].value = 0
        tx = PartialTransaction.from_io(list(coins), list(outputs))
        fee = fee_estimator(tx.estimated_size())
        amount = sendable - tx.output_value() - fee
        if amount < 0:
            raise NotEnoughFunds()
        outputs[i_max].value = amount
        tx = PartialTransaction.from_io(list(coins), list(outputs))

    # Timelock tx to current height.
    tx.locktime = get_locktime_for_new_transaction(self.network)

    tx.add_info_from_wallet(self)
    run_hook('make_unsigned_transaction', self, tx)
    return tx

def mktx(self, *, outputs: List[PartialTxOutput], password=None, fee=None, change_addr=None,
         domain=None, rbf=False, nonlocal_only=False, tx_version=None, sign=True) -> PartialTransaction:
    coins = self.get_spendable_coins(domain, nonlocal_only=nonlocal_only)
    tx = self.make_unsigned_transaction(coins=coins,
                                        outputs=outputs,
                                        fee=fee,
                                        change_addr=change_addr)
    tx.set_rbf(rbf)
    if tx_version is not None:
        tx.version = tx_version
    if sign:
        self.sign_transaction(tx, password)
    return tx

def is_frozen_address(self, addr: str) -> bool:
    return addr in self.frozen_addresses

def is_frozen_coin(self, utxo: PartialTxInput) -> bool:
    prevout_str = utxo.prevout.to_str()
    return prevout_str in self.frozen_coins

def set_frozen_state_of_addresses(self, addrs, freeze: bool):
    """Set frozen state of the addresses to FREEZE, True or False"""
    if all(self.is_mine(addr) for addr in addrs):
        # FIXME take lock?
        if freeze:
            self.frozen_addresses |= set(addrs)
        else:
            self.frozen_addresses -= set(addrs)
        self.db.put('frozen_addresses', list(self.frozen_addresses))
        return True
    return False

def set_frozen_state_of_coins(self, utxos: Sequence[PartialTxInput], freeze: bool):
    """Set frozen state of the utxos to FREEZE, True or False"""
    utxos = {utxo.prevout.to_str() for utxo in utxos}
    # FIXME take lock?
    if freeze:
        self.frozen_coins |= set(utxos)
    else:
        self.frozen_coins -= set(utxos)
    self.db.put('frozen_coins', list(self.frozen_coins))

def is_address_reserved(self, addr: str) -> bool:
    # note: atm 'reserved' status is only taken into consideration for 'change addresses'
    return addr in self._reserved_addresses

def set_reserved_state_of_address(self, addr: str, *, reserved: bool) -> None:
    if not self.is_mine(addr):
        return
    with self.lock:
        if reserved:
            self._reserved_addresses.add(addr)
        else:
            self._reserved_addresses.discard(addr)
        self.db.put('reserved_addresses', list(self._reserved_addresses))

def can_export(self):
    return not self.is_watching_only() and hasattr(self.keystore, 'get_private_key')

def address_is_old(self, address: str, *, req_conf: int = 3) -> bool:
    """Returns whether address has any history that is deeply confirmed.
    Used for reorg-safe(ish) gap limit roll-forward.
    """
    max_conf = -1
    h = self.db.get_addr_history(address)
    needs_spv_check = not self.config.get("skipmerklecheck", False)
    for tx_hash, tx_height in h:
        if needs_spv_check:
            tx_age = self.get_tx_height(tx_hash).conf
        else:
            if tx_height <= 0:
                tx_age = 0
            else:
                tx_age = self.get_local_height() - tx_height + 1
        max_conf = max(max_conf, tx_age)
    return max_conf >= req_conf

def bump_fee(self, *, tx: Transaction, new_fee_rate: Union[int, float, Decimal],
             coins: Sequence[PartialTxInput] = None) -> PartialTransaction:
    """Increase the miner fee of 'tx'.
    'new_fee_rate' is the target min rate in sat/vbyte
    'coins' is a list of UTXOs we can choose from as potential new inputs to be added
    """
    if tx.is_final():
        raise CannotBumpFee(_('Cannot bump fee') + ': ' + _('transaction is final'))
    new_fee_rate = quantize_feerate(new_fee_rate)  # strip excess precision
    old_tx_size = tx.estimated_size()
    old_txid = tx.txid()
    assert old_txid
    old_fee = self.get_tx_fee(old_txid)
    if old_fee is None:
        raise CannotBumpFee(_('Cannot bump fee') + ': ' + _('current fee unknown'))
    old_fee_rate = old_fee / old_tx_size  # sat/vbyte
    if new_fee_rate <= old_fee_rate:
        raise CannotBumpFee(_('Cannot bump fee') + ': ' + _("The new fee rate needs to be higher than the old fee rate."))

    try:
        # method 1: keep all inputs, keep all not is_mine outputs,
        #           allow adding new inputs
        tx_new = self._bump_fee_through_coinchooser(
            tx=tx, new_fee_rate=new_fee_rate, coins=coins)
        method_used = 1
    except CannotBumpFee:
        # method 2: keep all inputs, no new inputs are added,
        #           allow decreasing and removing outputs (change is decreased first)
        # This is less "safe" as it might end up decreasing e.g. a payment to a merchant;
        # but e.g. if the user has sent "Max" previously, this is the only way to RBF.
        tx_new = self._bump_fee_through_decreasing_outputs(
            tx=tx, new_fee_rate=new_fee_rate)
        method_used = 2

    target_min_fee = new_fee_rate * tx_new.estimated_size()
    actual_fee = tx_new.get_fee()
    if actual_fee + 1 < target_min_fee:
        raise Exception(f"bump_fee fee target was not met (method: {method_used}). "
                        f"got {actual_fee}, expected >={target_min_fee}. "
                        f"target rate was {new_fee_rate}")

    tx_new.locktime = get_locktime_for_new_transaction(self.network)
    tx_new.add_info_from_wallet(self)
    return tx_new

def _bump_fee_through_coinchooser(self, *, tx: Transaction, new_fee_rate: Union[int, Decimal],             coins: Sequence[PartialTxInput] = None) -> PartialTransaction:
    tx = PartialTransaction.from_tx(tx)
    tx.add_info_from_wallet(self)
    old_inputs = list(tx.inputs())
    old_outputs = list(tx.outputs())
    # change address
    old_change_addrs = [o.address for o in old_outputs if self.is_change(o.address)]
    change_addrs = self.get_change_addresses_for_new_transaction(old_change_addrs)
    # which outputs to keep?
    if old_change_addrs:
        fixed_outputs = list(filter(lambda o: not self.is_change(o.address), old_outputs))
    else:
        if all(self.is_mine(o.address) for o in old_outputs):
            # all outputs are is_mine and none of them are change.
            # we bail out as it's unclear what the user would want!
            # the coinchooser bump fee method is probably not a good idea in this case
            raise CannotBumpFee(_('Cannot bump fee') + ': all outputs are non-change is_mine')
        old_not_is_mine = list(filter(lambda o: not self.is_mine(o.address), old_outputs))
        if old_not_is_mine:
            fixed_outputs = old_not_is_mine
        else:
            fixed_outputs = old_outputs
    if not fixed_outputs:
        raise CannotBumpFee(_('Cannot bump fee') + ': could not figure out which outputs to keep')

    if coins is None:
        coins = self.get_spendable_coins(None)
    # make sure we don't try to spend output from the tx-to-be-replaced:
    coins = [c for c in coins if c.prevout.txid.hex() != tx.txid()]
    for item in coins:
        self.add_input_info(item)
    def fee_estimator(size):
        return self.config.estimate_fee_for_feerate(fee_per_kb=new_fee_rate*1000, size=size)
    coin_chooser = coinchooser.get_coin_chooser(self.config)
    try:
        return coin_chooser.make_tx(coins=coins,
                                    inputs=old_inputs,
                                    outputs=fixed_outputs,
                                    change_addrs=change_addrs,
                                    fee_estimator_vb=fee_estimator,
                                    dust_threshold=self.dust_threshold())
    except NotEnoughFunds as e:
        raise CannotBumpFee(e)

def _bump_fee_through_decreasing_outputs(self, *, tx: Transaction,
                                         new_fee_rate: Union[int, Decimal]) -> PartialTransaction:
    tx = PartialTransaction.from_tx(tx)
    tx.add_info_from_wallet(self)
    inputs = tx.inputs()
    outputs = list(tx.outputs())

    # use own outputs
    s = list(filter(lambda o: self.is_mine(o.address), outputs))
    # ... unless there is none
    if not s:
        s = outputs
        x_fee = run_hook('get_tx_extra_fee', self, tx)
        if x_fee:
            x_fee_address, x_fee_amount = x_fee
            s = filter(lambda o: o.address != x_fee_address, s)
    if not s:
        raise CannotBumpFee(_('Cannot bump fee') + ': no outputs at all??')

    # prioritize low value outputs, to get rid of dust
    s = sorted(s, key=lambda o: o.value)
    for o in s:
        target_fee = int(round(tx.estimated_size() * new_fee_rate))
        delta = target_fee - tx.get_fee()
        i = outputs.index(o)
        if o.value - delta >= self.dust_threshold():
            new_output_value = o.value - delta
            assert isinstance(new_output_value, int)
            outputs[i].value = new_output_value
            delta = 0
            break
        else:
            del outputs[i]
            delta -= o.value
            # note: delta might be negative now, in which case
            # the value of the next output will be increased
    if delta > 0:
        raise CannotBumpFee(_('Cannot bump fee') + ': ' + _('could not find suitable outputs'))

    return PartialTransaction.from_io(inputs, outputs)

def cpfp(self, tx: Transaction, fee: int) -> Optional[PartialTransaction]:
    txid = tx.txid()
    for i, o in enumerate(tx.outputs()):
        address, value = o.address, o.value
        if self.is_mine(address):
            break
    else:
        return
    coins = self.get_addr_utxo(address)
    item = coins.get(TxOutpoint.from_str(txid+':%d'%i))
    if not item:
        return
    inputs = [item]
    out_address = (self.get_single_change_address_for_new_transaction(allow_reuse=False)
                   or self.get_unused_address()
                   or address)
    outputs = [PartialTxOutput.from_address_and_value(out_address, value - fee)]
    locktime = get_locktime_for_new_transaction(self.network)
    tx_new = PartialTransaction.from_io(inputs, outputs, locktime=locktime)
    tx_new.add_info_from_wallet(self)
    return tx_new

def dscancel(
        self, *, tx: Transaction, new_fee_rate: Union[int, float, Decimal]
) -> PartialTransaction:
    """Double-Spend-Cancel: cancel an unconfirmed tx by double-spending
    its inputs, paying ourselves.
    'new_fee_rate' is the target min rate in sat/vbyte
    """
    if tx.is_final():
        raise CannotDoubleSpendTx(_('Cannot cancel transaction') + ': ' + _('transaction is final'))
    new_fee_rate = quantize_feerate(new_fee_rate)  # strip excess precision
    old_tx_size = tx.estimated_size()
    old_txid = tx.txid()
    assert old_txid
    old_fee = self.get_tx_fee(old_txid)
    if old_fee is None:
        raise CannotDoubleSpendTx(_('Cannot cancel transaction') + ': ' + _('current fee unknown'))
    old_fee_rate = old_fee / old_tx_size  # sat/vbyte
    if new_fee_rate <= old_fee_rate:
        raise CannotDoubleSpendTx(_('Cannot cancel transaction') + ': ' + _("The new fee rate needs to be higher than the old fee rate."))

    tx = PartialTransaction.from_tx(tx)
    tx.add_info_from_wallet(self)

    # grab all ismine inputs
    inputs = [txin for txin in tx.inputs()
              if self.is_mine(self.get_txin_address(txin))]
    value = sum([txin.value_sats() for txin in tx.inputs()])
    # figure out output address
    old_change_addrs = [o.address for o in tx.outputs() if self.is_mine(o.address)]
    out_address = (self.get_single_change_address_for_new_transaction(old_change_addrs)
                   or self.get_receiving_address())

    locktime = get_locktime_for_new_transaction(self.network)

    outputs = [PartialTxOutput.from_address_and_value(out_address, value)]
    tx_new = PartialTransaction.from_io(inputs, outputs, locktime=locktime)
    new_tx_size = tx_new.estimated_size()
    new_fee = max(
        new_fee_rate * new_tx_size,
        old_fee + self.relayfee() * new_tx_size / Decimal(1000),  # BIP-125 rules 3 and 4
    )
    new_fee = int(math.ceil(new_fee))
    outputs = [PartialTxOutput.from_address_and_value(out_address, value - new_fee)]
    tx_new = PartialTransaction.from_io(inputs, outputs, locktime=locktime)
    tx_new.add_info_from_wallet(self)
    return tx_new

@abstractmethod
def _add_input_sig_info(self, txin: PartialTxInput, address: str, *, only_der_suffix: bool = True) -> None:
    pass

def _add_txinout_derivation_info(self, txinout: Union[PartialTxInput, PartialTxOutput],
                                 address: str, *, only_der_suffix: bool = True) -> None:
    pass  # implemented by subclasses

def _add_input_utxo_info(self, txin: PartialTxInput, address: str) -> None:
    if txin.utxo is None:
        # note: for hw wallets, for legacy inputs, ignore_network_issues used to be False
        txin.utxo = self.get_input_tx(txin.prevout.txid.hex(), ignore_network_issues=True)
    txin.ensure_there_is_only_one_utxo()

def _learn_derivation_path_for_address_from_txinout(self, txinout: Union[PartialTxInput, PartialTxOutput],
                                                    address: str) -> bool:
    """Tries to learn the derivation path for an address (potentially beyond gap limit)
    using data available in given txin/txout.
    Returns whether the address was found to be is_mine.
    """
    return False  # implemented by subclasses

def add_input_info(self, txin: PartialTxInput, *, only_der_suffix: bool = True) -> None:
    address = self.get_txin_address(txin)
    if not self.is_mine(address):
        is_mine = self._learn_derivation_path_for_address_from_txinout(txin, address)
        if not is_mine:
            return
    # set script_type first, as later checks might rely on it:
    txin.script_type = self.get_txin_type(address)
    self._add_input_utxo_info(txin, address)
    txin.num_sig = self.m if isinstance(self, Multisig_Wallet) else 1
    if txin.redeem_script is None:
        try:
            redeem_script_hex = self.get_redeem_script(address)
            txin.redeem_script = bfh(redeem_script_hex) if redeem_script_hex else None
        except UnknownTxinType:
            pass
    if txin.witness_script is None:
        try:
            witness_script_hex = self.get_witness_script(address)
            txin.witness_script = bfh(witness_script_hex) if witness_script_hex else None
        except UnknownTxinType:
            pass
    self._add_input_sig_info(txin, address, only_der_suffix=only_der_suffix)

def can_sign(self, tx: Transaction) -> bool:
    if not isinstance(tx, PartialTransaction):
        return False
    if tx.is_complete():
        return False
    # add info to inputs if we can; otherwise we might return a false negative:
    tx.add_info_from_wallet(self)
    for txin in tx.inputs():
        # note: is_mine check needed to avoid false positives.
        #       just because keystore could sign, txin does not necessarily belong to wallet.
        #       Example: we have p2pkh-like addresses and txin is a multisig that involves our pubkey.
        if not self.is_mine(txin.address):
            continue
        for k in self.get_keystores():
            if k.can_sign_txin(txin):
                return True
    return False

def get_input_tx(self, tx_hash, *, ignore_network_issues=False) -> Optional[Transaction]:
    # First look up an input transaction in the wallet where it
    # will likely be.  If co-signing a transaction it may not have
    # all the input txs, in which case we ask the network.
    tx = self.db.get_transaction(tx_hash)
    if not tx and self.network and self.network.has_internet_connection():
        try:
            raw_tx = self.network.run_from_another_thread(
                self.network.get_transaction(tx_hash, timeout=10))
        except NetworkException as e:
            self.logger.info(f'got network error getting input txn. err: {repr(e)}. txid: {tx_hash}. '
                             f'if you are intentionally offline, consider using the --offline flag')
            if not ignore_network_issues:
                raise e
        else:
            tx = Transaction(raw_tx)
    return tx

def add_output_info(self, txout: PartialTxOutput, *, only_der_suffix: bool = True) -> None:
    address = txout.address
    if not self.is_mine(address):
        is_mine = self._learn_derivation_path_for_address_from_txinout(txout, address)
        if not is_mine:
            return
    txout.script_type = self.get_txin_type(address)
    txout.is_mine = True
    txout.is_change = self.is_change(address)
    if isinstance(self, Multisig_Wallet):
        txout.num_sig = self.m
    self._add_txinout_derivation_info(txout, address, only_der_suffix=only_der_suffix)
    if txout.redeem_script is None:
        try:
            redeem_script_hex = self.get_redeem_script(address)
            txout.redeem_script = bfh(redeem_script_hex) if redeem_script_hex else None
        except UnknownTxinType:
            pass
    if txout.witness_script is None:
        try:
            witness_script_hex = self.get_witness_script(address)
            txout.witness_script = bfh(witness_script_hex) if witness_script_hex else None
        except UnknownTxinType:
            pass

def sign_transaction(self, tx: Transaction, password) -> Optional[PartialTransaction]:
    if self.is_watching_only():
        return
    if not isinstance(tx, PartialTransaction):
        return
    # add info to a temporary tx copy; including xpubs
    # and full derivation paths as hw keystores might want them
    tmp_tx = copy.deepcopy(tx)
    tmp_tx.add_info_from_wallet(self, include_xpubs_and_full_paths=True)
    # sign. start with ready keystores.
    for k in sorted(self.get_keystores(), key=lambda ks: ks.ready_to_sign(), reverse=True):
        try:
            if k.can_sign(tmp_tx):
                k.sign_transaction(tmp_tx, password)
        except UserCancelled:
            continue
    # remove sensitive info; then copy back details from temporary tx
    tmp_tx.remove_xpubs_and_bip32_paths()
    tx.combine_with_other_psbt(tmp_tx)
    tx.add_info_from_wallet(self, include_xpubs_and_full_paths=False)
    return tx

def try_detecting_internal_addresses_corruption(self) -> None:
    pass

def check_address_for_corruption(self, addr: str) -> None:
    pass

def get_unused_addresses(self) -> Sequence[str]:
    domain = self.get_receiving_addresses()
    # TODO we should index receive_requests by id
    in_use_by_request = [k for k in self.receive_requests.keys()
                         if self.get_request_status(k) != PR_EXPIRED]
    in_use_by_request = set(in_use_by_request)
    return [addr for addr in domain if not self.is_used(addr)
            and addr not in in_use_by_request]

@check_returned_address_for_corruption
def get_unused_address(self) -> Optional[str]:
    """Get an unused receiving address, if there is one.
    Note: there might NOT be one available!
    """
    addrs = self.get_unused_addresses()
    if addrs:
        return addrs[0]

@check_returned_address_for_corruption
def get_receiving_address(self) -> str:
    """Get a receiving address. Guaranteed to always return an address."""
    unused_addr = self.get_unused_address()
    if unused_addr:
        return unused_addr
    domain = self.get_receiving_addresses()
    if not domain:
        raise Exception("no receiving addresses in wallet?!")
    choice = domain[0]
    for addr in domain:
        if not self.is_used(addr): if addr not in self.receive_requests.keys():
                return addr
            else:
                choice = addr
    return choice

def create_new_address(self, for_change: bool = False):
    raise Exception("this wallet cannot generate new addresses")

def import_address(self, address: str) -> str:
    raise Exception("this wallet cannot import addresses")

def import_addresses(self, addresses: List[str], *,
                     write_to_disk=True) -> Tuple[List[str], List[Tuple[str, str]]]:
    raise Exception("this wallet cannot import addresses")

def delete_address(self, address: str) -> None:
    raise Exception("this wallet cannot delete addresses")

def get_payment_status(self, address, amount):
    received, sent = self.get_addr_io(address)
    l = []
    for txo, x in received.items():
        h, v, is_cb = x
        txid, n = txo.split(':')
        conf = self.get_tx_height(txid).conf
        l.append((conf, v))
    vsum = 0
    for conf, v in reversed(sorted(l)):
        vsum += v
        if vsum >= amount:
            return True, conf
    return False, None

def get_request_URI(self, req: OnchainInvoice) -> str:
    addr = req.get_address()
    message = self.get_label(addr)
    amount = req.amount_sat
    extra_query_params = {}
    if req.time:
        extra_query_params['time'] = str(int(req.time))
    if req.exp:
        extra_query_params['exp'] = str(int(req.exp))
    #if req.get('name') and req.get('sig'):
    #    sig = bfh(req.get('sig'))
    #    sig = bitcoin.base_encode(sig, base=58)
    #    extra_query_params['name'] = req['name']
    #    extra_query_params['sig'] = sig
    uri = create_bip21_uri(addr, amount, message, extra_query_params=extra_query_params)
    return str(uri)

def check_expired_status(self, r: Invoice, status):
    if r.is_lightning() and r.exp == 0:
        status = PR_EXPIRED  # for BOLT-11 invoices, exp==0 means 0 seconds
    if status == PR_UNPAID and r.exp > 0 and r.time + r.exp < time.time():
        status = PR_EXPIRED
    return status

def get_invoice_status(self, invoice: Invoice):
    if invoice.is_lightning():
        status = self.lnworker.get_invoice_status(invoice) if self.lnworker else PR_UNKNOWN
    else:
        status = PR_PAID if self.is_onchain_invoice_paid(invoice) else PR_UNPAID
    return self.check_expired_status(invoice, status)

def get_request_status(self, key):
    r = self.get_request(key)
    if r is None:
        return PR_UNKNOWN
    if r.is_lightning():
        assert isinstance(r, LNInvoice)
        status = self.lnworker.get_payment_status(bfh(r.rhash)) if self.lnworker else PR_UNKNOWN
    else:
        assert isinstance(r, OnchainInvoice)
        paid, conf = self.get_payment_status(r.get_address(), r.get_amount_sat())
        status = PR_PAID if paid else PR_UNPAID
    return self.check_expired_status(r, status)

def get_request(self, key):
    return self.receive_requests.get(key)

def get_formatted_request(self, key):
    x = self.receive_requests.get(key)
    if x:
        return self.export_request(x)

def export_request(self, x: Invoice) -> Dict[str, Any]:
    if x.is_lightning():
        assert isinstance(x, LNInvoice)
        key = x.rhash
    else:
        assert isinstance(x, OnchainInvoice)
        key = x.get_address()
    status = self.get_request_status(key)
    status_str = x.get_status_str(status)
    is_lightning = x.is_lightning()
    d = {
        'is_lightning': is_lightning,
        'amount_BTC': format_satoshis(x.get_amount_sat()),
        'message': x.message,
        'timestamp': x.time,
        'expiration': x.exp,
        'status': status,
        'status_str': status_str,
    }
    if is_lightning:
        assert isinstance(x, LNInvoice)
        d['rhash'] = x.rhash
        d['invoice'] = x.invoice
        d['amount_msat'] = x.get_amount_msat()
        if self.lnworker and status == PR_UNPAID:
            d['can_receive'] = self.lnworker.can_receive_invoice(x)
    else:
        assert isinstance(x, OnchainInvoice)
        amount_sat = x.get_amount_sat()
        addr = x.get_address()
        paid, conf = self.get_payment_status(addr, amount_sat)
        d['amount_sat'] = amount_sat
        d['address'] = addr
        d['URI'] = self.get_request_URI(x)
        if conf is not None:
            d['confirmations'] = conf
    # add URL if we are running a payserver
    payserver = self.config.get_netaddress('payserver_address')
    if payserver:
        root = self.config.get('payserver_root', '/r')
        use_ssl = bool(self.config.get('ssl_keyfile'))
        protocol = 'https' if use_ssl else 'http'
        base = '%s://%s:%d'%(protocol, payserver.host, payserver.port)
        d['view_url'] = base + root + '/pay?id=' + key
        if use_ssl and 'URI' in d:
            request_url = base + '/bip70/' + key + '.bip70'
            d['bip70_url'] = request_url
    return d

def export_invoice(self, x: Invoice) -> Dict[str, Any]:
    status = self.get_invoice_status(x)
    status_str = x.get_status_str(status)
    is_lightning = x.is_lightning()
    d = {
        'is_lightning': is_lightning,
        'amount_BTC': format_satoshis(x.get_amount_sat()),
        'message': x.message,
        'timestamp': x.time,
        'expiration': x.exp,
        'status': status,
        'status_str': status_str,
    }
    if is_lightning:
        assert isinstance(x, LNInvoice)
        d['invoice'] = x.invoice
        d['amount_msat'] = x.get_amount_msat()
        if self.lnworker and status == PR_UNPAID:
            d['can_pay'] = self.lnworker.can_pay_invoice(x)
    else:
        assert isinstance(x, OnchainInvoice)
        amount_sat = x.get_amount_sat()
        assert isinstance(amount_sat, (int, str, type(None)))
        d['amount_sat'] = amount_sat
        d['outputs'] = [y.to_legacy_tuple() for y in x.outputs]
        if x.bip70:
            d['bip70'] = x.bip70
            d['requestor'] = x.requestor
    return d

def receive_tx_callback(self, tx_hash, tx, tx_height):
    super().receive_tx_callback(tx_hash, tx, tx_height)
    for txo in tx.outputs():
        addr = self.get_txout_address(txo)
        if addr in self.receive_requests:
            status = self.get_request_status(addr)
            util.trigger_callback('request_status', self, addr, status)

def make_payment_request(self, address, amount_sat, message, expiration):
    # TODO maybe merge with wallet.create_invoice()...
    #      note that they use incompatible "id"
    amount_sat = amount_sat or 0
    timestamp = int(time.time())
    _id = bh2u(sha256d(address + "%d"%timestamp))[0:10]
    expiration = expiration or 0
    return OnchainInvoice(
        type=PR_TYPE_ONCHAIN,
        outputs=[(TYPE_ADDRESS, address, amount_sat)],
        message=message,
        time=timestamp,
        amount_sat=amount_sat,
        exp=expiration,
        id=_id,
        bip70=None,
        requestor=None,
    )

def sign_payment_request(self, key, alias, alias_addr, password):  # FIXME this is broken
    req = self.receive_requests.get(key)
    assert isinstance(req, OnchainInvoice)
    alias_privkey = self.export_private_key(alias_addr, password)
    pr = paymentrequest.make_unsigned_request(req)
    paymentrequest.sign_request_with_alias(pr, alias, alias_privkey)
    req.bip70 = pr.raw.hex()
    req['name'] = pr.pki_data
    req['sig'] = bh2u(pr.signature)
    self.receive_requests[key] = req

def add_payment_request(self, req: Invoice):
    if not req.is_lightning():
        assert isinstance(req, OnchainInvoice)
        addr = req.get_address()
        if not bitcoin.is_address(addr):
            raise Exception(_('Invalid Bitcoin address.'))
        if not self.is_mine(addr):
            raise Exception(_('Address not in wallet.'))
        key = addr
    else:
        assert isinstance(req, LNInvoice)
        key = req.rhash
    message = req.message
    self.receive_requests[key] = req
    self.set_label(key, message) # should be a default label
    return req

def delete_request(self, key):
    """ lightning or on-chain """
    if key in self.receive_requests:
        self.remove_payment_request(key)
    elif self.lnworker:
        self.lnworker.delete_payment(key)

def delete_invoice(self, key):
    """ lightning or on-chain """
    if key in self.invoices:
        self.invoices.pop(key)
    elif self.lnworker:
        self.lnworker.delete_payment(key)

def remove_payment_request(self, addr):
    if addr not in self.receive_requests:
        return False
    self.receive_requests.pop(addr)
    return True

def get_sorted_requests(self) -> List[Invoice]:
    """ sorted by timestamp """
    out = [self.get_request(x) for x in self.receive_requests.keys()]
    out = [x for x in out if x is not None]
    out.sort(key=lambda x: x.time)
    return out

@abstractmethod
def get_fingerprint(self) -> str:
    """Returns a string that can be used to identify this wallet.
    Used e.g. by Labels plugin, and LN channel backups.
    Returns empty string "" for wallets that don't have an ID.
    """
    pass

def can_import_privkey(self):
    return False

def can_import_address(self):
    return False

def can_delete_address(self):
    return False

def has_password(self):
    return self.has_keystore_encryption() or self.has_storage_encryption()

def can_have_keystore_encryption(self):
    return self.keystore and self.keystore.may_have_password()

def get_available_storage_encryption_version(self) -> StorageEncryptionVersion:
    """Returns the type of storage encryption offered to the user.

    A wallet file (storage) is either encrypted with this version
    or is stored in plaintext.
    """
    if isinstance(self.keystore, Hardware_KeyStore):
        return StorageEncryptionVersion.XPUB_PASSWORD
    else:
        return StorageEncryptionVersion.USER_PASSWORD

def has_keystore_encryption(self):
    """Returns whether encryption is enabled for the keystore.

    If True, e.g. signing a transaction will require a password.
    """
    if self.can_have_keystore_encryption():
        return self.db.get('use_encryption', False)
    return False

def has_storage_encryption(self):
    """Returns whether encryption is enabled for the wallet file on disk."""
    return self.storage and self.storage.is_encrypted()

@classmethod
def may_have_password(cls):
    return True

def check_password(self, password):
    if self.has_keystore_encryption():
        self.keystore.check_password(password)
    if self.has_storage_encryption():
        self.storage.check_password(password)

def update_password(self, old_pw, new_pw, *, encrypt_storage: bool = True):
    if old_pw is None and self.has_password():
        raise InvalidPassword()
    self.check_password(old_pw)
    if self.storage:
        if encrypt_storage:
            enc_version = self.get_available_storage_encryption_version()
        else:
            enc_version = StorageEncryptionVersion.PLAINTEXT
        self.storage.set_password(new_pw, enc_version)
    # make sure next storage.write() saves changes
    self.db.set_modified(True)

    # note: Encrypting storage with a hw device is currently only
    #       allowed for non-multisig wallets. Further,
    #       Hardware_KeyStore.may_have_password() == False.
    #       If these were not the case,
    #       extra care would need to be taken when encrypting keystores.
    self._update_password_for_keystore(old_pw, new_pw)
    encrypt_keystore = self.can_have_keystore_encryption()
    self.db.set_keystore_encryption(bool(new_pw) and encrypt_keystore)
    self.save_db()

@abstractmethod
def _update_password_for_keystore(self, old_pw: Optional[str], new_pw: Optional[str]) -> None:
    pass

def sign_message(self, address, message, password):
    index = self.get_address_index(address)
    return self.keystore.sign_message(index, message, password)

def decrypt_message(self, pubkey: str, message, password) -> bytes:
    addr = self.pubkeys_to_address([pubkey])
    index = self.get_address_index(addr)
    return self.keystore.decrypt_message(index, message, password)

@abstractmethod
def pubkeys_to_address(self, pubkeys: Sequence[str]) -> Optional[str]:
    pass

def price_at_timestamp(self, txid, price_func):
    """Returns fiat price of bitcoin at the time tx got confirmed."""
    timestamp = self.get_tx_height(txid).timestamp
    return price_func(timestamp if timestamp else time.time())

def unrealized_gains(self, domain, price_func, ccy):
    coins = self.get_utxos(domain)
    now = time.time()
    p = price_func(now)
    ap = sum(self.coin_price(coin.prevout.txid.hex(), price_func, ccy, self.get_txin_value(coin)) for coin in coins)
    lp = sum([coin.value_sats() for coin in coins]) * p / Decimal(COIN)
    return lp - ap

def average_price(self, txid, price_func, ccy) -> Decimal:
    """ Average acquisition price of the inputs of a transaction """
    input_value = 0
    total_price = 0
    txi_addresses = self.db.get_txi_addresses(txid)
    if not txi_addresses:
        return Decimal('NaN')
    for addr in txi_addresses:
        d = self.db.get_txi_addr(txid, addr)
        for ser, v in d:
            input_value += v
            total_price += self.coin_price(ser.split(':')[0], price_func, ccy, v)
    return total_price / (input_value/Decimal(COIN))

def clear_coin_price_cache(self):
    self._coin_price_cache = {}

def coin_price(self, txid, price_func, ccy, txin_value) -> Decimal:
    """
    Acquisition price of a coin.
    This assumes that either all inputs are mine, or no input is mine.
    """
    if txin_value is None:
        return Decimal('NaN')
    cache_key = "{}:{}:{}".format(str(txid), str(ccy), str(txin_value))
    result = self._coin_price_cache.get(cache_key, None)
    if result is not None:
        return result
    if self.db.get_txi_addresses(txid):
        result = self.average_price(txid, price_func, ccy) * txin_value/Decimal(COIN)
        self._coin_price_cache[cache_key] = result
        return result
    else:
        fiat_value = self.get_fiat_value(txid, ccy)
        if fiat_value is not None:
            return fiat_value
        else:
            p = self.price_at_timestamp(txid, price_func)
            return p * txin_value/Decimal(COIN)

def is_billing_address(self, addr):
    # overridden for TrustedCoin wallets
    return False

@abstractmethod
def is_watching_only(self) -> bool:
    pass

def get_keystore(self) -> Optional[KeyStore]:
    return self.keystore

def get_keystores(self) -> Sequence[KeyStore]:
    return [self.keystore] if self.keystore else []

@abstractmethod
def save_keystore(self):
    pass

@abstractmethod
def has_seed(self) -> bool:
    pass

@abstractmethod
def get_all_known_addresses_beyond_gap_limit(self) -> Set[str]:
    pass

def create_transaction(self, outputs, *, fee=None, feerate=None, change_addr=None, domain_addr=None, domain_coins=None,
          unsigned=False, rbf=None, password=None, locktime=None):
    if fee is not None and feerate is not None:
        raise Exception("Cannot specify both 'fee' and 'feerate' at the same time!")
    coins = self.get_spendable_coins(domain_addr)
    if domain_coins is not None:
        coins = [coin for coin in coins if (coin.prevout.to_str() in domain_coins)]
    if feerate is not None:
        fee_per_kb = 1000 * Decimal(feerate)
        fee_estimator = partial(SimpleConfig.estimate_fee_for_feerate, fee_per_kb)
    else:
        fee_estimator = fee
    tx = self.make_unsigned_transaction(
        coins=coins,
        outputs=outputs,
        fee=fee_estimator,
        change_addr=change_addr)
    if locktime is not None:
        tx.locktime = locktime
    if rbf is None:
        rbf = self.config.get('use_rbf', True)
    if rbf:
        tx.set_rbf(True)
    if not unsigned:
        self.sign_transaction(tx, password)
    return tx

def get_warning_for_risk_of_burning_coins_as_fees(self, tx: 'PartialTransaction') -> Optional[str]:
    """Returns a warning message if there is risk of burning coins as fees if we sign.
    Note that if not all inputs are ismine, e.g. coinjoin, the risk is not just about fees.

    Note:
        - legacy sighash does not commit to any input amounts
        - BIP-0143 sighash only commits to the *corresponding* input amount
        - BIP-taproot sighash commits to *all* input amounts
    """
    assert isinstance(tx, PartialTransaction)
    # if we have all full previous txs, we *know* all the input amounts -> fine
    if all([txin.utxo for txin in tx.inputs()]):
        return None
    # a single segwit input -> fine
    if len(tx.inputs()) == 1 and tx.inputs()[0].is_segwit() and tx.inputs()[0].witness_utxo:
        return None
    # coinjoin or similar
    if any([not self.is_mine(txin.address) for txin in tx.inputs()]):
        return (_("Warning") + ": "
                + _("The input amounts could not be verified as the previous transactions are missing.\n"
                    "The amount of money being spent CANNOT be verified."))
    # some inputs are legacy
    if any([not txin.is_segwit() for txin in tx.inputs()]):
        return (_("Warning") + ": "
                + _("The fee could not be verified. Signing non-segwit inputs is risky:\n"
                    "if this transaction was maliciously modified before you sign,\n"
                    "you might end up paying a higher mining fee than displayed."))
    # all inputs are segwit
    # https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-August/014843.html
    return (_("Warning") + ": "
            + _("If you received this transaction from an untrusted device, "
                "do not accept to sign it more than once,\n"
                "otherwise you could end up paying a different fee."))

class Simple_Wallet(Abstract_Wallet):

wallet with a single keystore

def is_watching_only(self):
    return self.keystore.is_watching_only()

def _update_password_for_keystore(self, old_pw, new_pw):
    if self.keystore and self.keystore.may_have_password():
        self.keystore.update_password(old_pw, new_pw)
        self.save_keystore()

def save_keystore(self):
    self.db.put('keystore', self.keystore.dump())

@abstractmethod
def get_public_key(self, address: str) -> Optional[str]:
    pass

def get_public_keys(self, address: str) -> Sequence[str]:
    return [self.get_public_key(address)]

def get_redeem_script(self, address: str) -> Optional[str]:
    txin_type = self.get_txin_type(address)
    if txin_type in ('p2pkh', 'p2wpkh', 'p2pk'):
        return None
    if txin_type == 'p2wpkh-p2sh':
        pubkey = self.get_public_key(address)
        return bitcoin.p2wpkh_nested_script(pubkey)
    if txin_type == 'address':
        return None
    raise UnknownTxinType(f'unexpected txin_type {txin_type}')

def get_witness_script(self, address: str) -> Optional[str]:
    return None

class Imported_Wallet(Simple_Wallet):

wallet made of imported addresses

wallet_type = 'imported'
txin_type = 'address'

def __init__(self, db, storage, *, config):
    Abstract_Wallet.__init__(self, db, storage, config=config)

def is_watching_only(self):
    return self.keystore is None

def can_import_privkey(self):
    return bool(self.keystore)

def load_keystore(self):
    self.keystore = load_keystore(self.db, 'keystore') if self.db.get('keystore') else None

def save_keystore(self):
    self.db.put('keystore', self.keystore.dump())

def can_import_address(self):
    return self.is_watching_only()

def can_delete_address(self):
    return True

def has_seed(self):
    return False

def is_deterministic(self):
    return False

def is_change(self, address):
    return False

def get_all_known_addresses_beyond_gap_limit(self) -> Set[str]:
    return set()

def get_fingerprint(self):
    return ''

def get_addresses(self):
    # note: overridden so that the history can be cleared
    return self.db.get_imported_addresses()

def get_receiving_addresses(self, **kwargs):
    return self.get_addresses()

def get_change_addresses(self, **kwargs):
    return []

def import_addresses(self, addresses: List[str], *,
                     write_to_disk=True) -> Tuple[List[str], List[Tuple[str, str]]]:
    good_addr = []  # type: List[str]
    bad_addr = []  # type: List[Tuple[str, str]]
    for address in addresses:
        if not bitcoin.is_address(address):
            bad_addr.append((address, _('invalid address')))
            continue
        if self.db.has_imported_address(address):
            bad_addr.append((address, _('address already in wallet')))
            continue
        good_addr.append(address)
        self.db.add_imported_address(address, {})
        self.add_address(address)
    if write_to_disk:
        self.save_db()
    return good_addr, bad_addr

def import_address(self, address: str) -> str:
    good_addr, bad_addr = self.import_addresses([address])
    if good_addr and good_addr[0] == address:
        return address
    else:
        raise BitcoinException(str(bad_addr[0][1]))

def delete_address(self, address: str) -> None:
    if not self.db.has_imported_address(address):
        return
    if len(self.get_addresses()) <= 1:
        raise UserFacingException("cannot delete last remaining address from wallet")
    transactions_to_remove = set()  # only referred to by this address
    transactions_new = set()  # txs that are not only referred to by address
    with self.lock:
        for addr in self.db.get_history():
            details = self.get_address_history(addr)
            if addr == address:
                for tx_hash, height in details:
                    transactions_to_remove.add(tx_hash)
            else:
                for tx_hash, height in details:
                    transactions_new.add(tx_hash)
        transactions_to_remove -= transactions_new
        self.db.remove_addr_history(address)
        for tx_hash in transactions_to_remove:
            self.remove_transaction(tx_hash)
    self.set_label(address, None)
    self.remove_payment_request(address)
    self.set_frozen_state_of_addresses([address], False)
    pubkey = self.get_public_key(address)
    self.db.remove_imported_address(address)
    if pubkey:
        # delete key iff no other address uses it (e.g. p2pkh and p2wpkh for same key)
        for txin_type in bitcoin.WIF_SCRIPT_TYPES.keys():
            try:
                addr2 = bitcoin.pubkey_to_address(txin_type, pubkey)
            except NotImplementedError:
                pass
            else:
                if self.db.has_imported_address(addr2):
                    break
        else:
            self.keystore.delete_imported_key(pubkey)
            self.save_keystore()
    self.save_db()

def is_mine(self, address) -> bool:
    if not address: return False
    return self.db.has_imported_address(address)

def get_address_index(self, address) -> Optional[str]:
    # returns None if address is not mine
    return self.get_public_key(address)

def get_address_path_str(self, address):
    return None

def get_public_key(self, address) -> Optional[str]:
    x = self.db.get_imported_address(address)
    return x.get('pubkey') if x else None

def import_private_keys(self, keys: List[str], password: Optional[str], *,
                        write_to_disk=True) -> Tuple[List[str], List[Tuple[str, str]]]:
    good_addr = []  # type: List[str]
    bad_keys = []  # type: List[Tuple[str, str]]
    for key in keys:
        try:
            txin_type, pubkey = self.keystore.import_privkey(key, password)
        except Exception as e:
            bad_keys.append((key, _('invalid private key') + f': {e}'))
            continue
        if txin_type not in ('p2pkh', 'p2wpkh', 'p2wpkh-p2sh'):
            bad_keys.append((key, _('not implemented type') + f': {txin_type}'))
            continue
        addr = bitcoin.pubkey_to_address(txin_type, pubkey)
        good_addr.append(addr)
        self.db.add_imported_address(addr, {'type':txin_type, 'pubkey':pubkey})
        self.add_address(addr)
    self.save_keystore()
    if write_to_disk:
        self.save_db()
    return good_addr, bad_keys

def import_private_key(self, key: str, password: Optional[str]) -> str:
    good_addr, bad_keys = self.import_private_keys([key], password=password)
    if good_addr:
        return good_addr[0]
    else:
        raise BitcoinException(str(bad_keys[0][1]))

def get_txin_type(self, address):
    return self.db.get_imported_address(address).get('type', 'address')

def _add_input_sig_info(self, txin, address, *, only_der_suffix=True):
    if not self.is_mine(address):
        return
    if txin.script_type in ('unknown', 'address'):
        return
    elif txin.script_type in ('p2pkh', 'p2wpkh', 'p2wpkh-p2sh'):
        pubkey = self.get_public_key(address)
        if not pubkey:
            return
        txin.pubkeys = [bfh(pubkey)]
    else:
        raise Exception(f'Unexpected script type: {txin.script_type}. '
                        f'Imported wallets are not implemented to handle this.')

def pubkeys_to_address(self, pubkeys):
    pubkey = pubkeys[0]
    for addr in self.db.get_imported_addresses():  # FIXME slow...
        if self.db.get_imported_address(addr)['pubkey'] == pubkey:
            return addr
    return None

def decrypt_message(self, pubkey: str, message, password) -> bytes:
    # this is significantly faster than the implementation in the superclass
    return self.keystore.decrypt_message(pubkey, message, password)

class Deterministic_Wallet(Abstract_Wallet):

def __init__(self, db, storage, *, config):
    self._ephemeral_addr_to_addr_index = {}  # type: Dict[str, Sequence[int]]
    Abstract_Wallet.__init__(self, db, storage, config=config)
    self.gap_limit = db.get('gap_limit', 20)
    # generate addresses now. note that without libsecp this might block
    # for a few seconds!
    self.synchronize()

    # create lightning keys
    if self.can_have_lightning():
        self.init_lightning()
    ln_xprv = self.db.get('lightning_privkey2')
    # lnworker can only be initialized once receiving addresses are available
    # therefore we instantiate lnworker in DeterministicWallet
    self.lnworker = LNWallet(self, ln_xprv) if ln_xprv else None

def has_seed(self):
    return self.keystore.has_seed()

def get_addresses(self):
    # note: overridden so that the history can be cleared.
    # addresses are ordered based on derivation
    out = self.get_receiving_addresses()
    out += self.get_change_addresses()
    return out

def get_receiving_addresses(self, *, slice_start=None, slice_stop=None):
    return self.db.get_receiving_addresses(slice_start=slice_start, slice_stop=slice_stop)

def get_change_addresses(self, *, slice_start=None, slice_stop=None):
    return self.db.get_change_addresses(slice_start=slice_start, slice_stop=slice_stop)

@profiler
def try_detecting_internal_addresses_corruption(self):
    addresses_all = self.get_addresses()
    # sample 1: first few
    addresses_sample1 = addresses_all[:10]
    # sample2: a few more randomly selected
    addresses_rand = addresses_all[10:]
    addresses_sample2 = random.sample(addresses_rand, min(len(addresses_rand), 10))
    for addr_found in itertools.chain(addresses_sample1, addresses_sample2):
        self.check_address_for_corruption(addr_found)

def check_address_for_corruption(self, addr):
    if addr and self.is_mine(addr):
        if addr != self.derive_address(*self.get_address_index(addr)):
            raise InternalAddressCorruption()

def get_seed(self, password):
    return self.keystore.get_seed(password)

def change_gap_limit(self, value):
    '''This method is not called in the code, it is kept for console use'''
    value = int(value)
    if value >= self.min_acceptable_gap():
        self.gap_limit = value
        self.db.put('gap_limit', self.gap_limit)
        self.save_db()
        return True
    else:
        return False

def num_unused_trailing_addresses(self, addresses):
    k = 0
    for addr in addresses[::-1]:
        if self.db.get_addr_history(addr):
            break
        k += 1
    return k

def min_acceptable_gap(self) -> int:
    # fixme: this assumes wallet is synchronized
    n = 0
    nmax = 0
    addresses = self.get_receiving_addresses()
    k = self.num_unused_trailing_addresses(addresses)
    for addr in addresses[0:-k]:
        if self.address_is_old(addr):
            n = 0
        else:
            n += 1
            nmax = max(nmax, n)
    return nmax + 1

@abstractmethod
def derive_pubkeys(self, c: int, i: int) -> Sequence[str]:
    pass

def derive_address(self, for_change: int, n: int) -> str:
    for_change = int(for_change)
    pubkeys = self.derive_pubkeys(for_change, n)
    return self.pubkeys_to_address(pubkeys)

def export_private_key_for_path(self, path: Union[Sequence[int], str], password: Optional[str]) -> str:
    if isinstance(path, str):
        path = convert_bip32_path_to_list_of_uint32(path)
    pk, compressed = self.keystore.get_private_key(path, password)
    txin_type = self.get_txin_type()  # assumes no mixed-scripts in wallet
    return bitcoin.serialize_privkey(pk, compressed, txin_type)

def get_public_keys_with_deriv_info(self, address: str):
    der_suffix = self.get_address_index(address)
    der_suffix = [int(x) for x in der_suffix]
    return {k.derive_pubkey(*der_suffix): (k, der_suffix)
            for k in self.get_keystores()}

def _add_input_sig_info(self, txin, address, *, only_der_suffix=True):
    self._add_txinout_derivation_info(txin, address, only_der_suffix=only_der_suffix)

def _add_txinout_derivation_info(self, txinout, address, *, only_der_suffix=True):
    if not self.is_mine(address):
        return
    pubkey_deriv_info = self.get_public_keys_with_deriv_info(address)
    txinout.pubkeys = sorted([pk for pk in list(pubkey_deriv_info)])
    for pubkey in pubkey_deriv_info:
        ks, der_suffix = pubkey_deriv_info[pubkey]
        fp_bytes, der_full = ks.get_fp_and_derivation_to_be_used_in_partial_tx(der_suffix,
                                                                               only_der_suffix=only_der_suffix)
        txinout.bip32_paths[pubkey] = (fp_bytes, der_full)

def create_new_address(self, for_change: bool = False):
    assert type(for_change) is bool
    with self.lock:
        n = self.db.num_change_addresses() if for_change else self.db.num_receiving_addresses()
        address = self.derive_address(int(for_change), n)
        self.db.add_change_address(address) if for_change else self.db.add_receiving_address(address)
        self.add_address(address)
        if for_change:
            # note: if it's actually "old", it will get filtered later
            self._not_old_change_addresses.append(address)
        return address

def synchronize_sequence(self, for_change):
    limit = self.gap_limit_for_change if for_change else self.gap_limit
    while True:
        num_addr = self.db.num_change_addresses() if for_change else self.db.num_receiving_addresses()
        if num_addr < limit:
            self.create_new_address(for_change)
            continue
        if for_change:
            last_few_addresses = self.get_change_addresses(slice_start=-limit)
        else:
            last_few_addresses = self.get_receiving_addresses(slice_start=-limit)
        if any(map(self.address_is_old, last_few_addresses)):
            self.create_new_address(for_change)
        else:
            break

@AddressSynchronizer.with_local_height_cached
def synchronize(self):
    with self.lock:
        self.synchronize_sequence(False)
        self.synchronize_sequence(True)

def get_all_known_addresses_beyond_gap_limit(self):
    # note that we don't stop at first large gap
    found = set()

    def process_addresses(addrs, gap_limit):
        rolling_num_unused = 0
        for addr in addrs:
            if self.db.get_addr_history(addr):
                rolling_num_unused = 0
            else:
                if rolling_num_unused >= gap_limit:
                    found.add(addr)
                rolling_num_unused += 1

    process_addresses(self.get_receiving_addresses(), self.gap_limit)
    process_addresses(self.get_change_addresses(), self.gap_limit_for_change)
    return found

def get_address_index(self, address) -> Optional[Sequence[int]]:
    return self.db.get_address_index(address) or self._ephemeral_addr_to_addr_index.get(address)

def get_address_path_str(self, address):
    intpath = self.get_address_index(address)
    if intpath is None:
        return None
    return convert_bip32_intpath_to_strpath(intpath)

def _learn_derivation_path_for_address_from_txinout(self, txinout, address):
    for ks in self.get_keystores():
        pubkey, der_suffix = ks.find_my_pubkey_in_txinout(txinout, only_der_suffix=True)
        if der_suffix is not None:
            # note: we already know the pubkey belongs to the keystore,
            #       but the script template might be different
            if len(der_suffix) != 2: continue
            my_address = self.derive_address(*der_suffix)
            if my_address == address:
                self._ephemeral_addr_to_addr_index[address] = list(der_suffix)
                return True
    return False

def get_master_public_keys(self):
    return [self.get_master_public_key()]

def get_fingerprint(self):
    return self.get_master_public_key()

def get_txin_type(self, address=None):
    return self.txin_type

class Simple_Deterministic_Wallet(Simple_Wallet, Deterministic_Wallet):

""" Deterministic Wallet with a single pubkey per address """

def __init__(self, db, storage, *, config):
    Deterministic_Wallet.__init__(self, db, storage, config=config)

def get_public_key(self, address):
    sequence = self.get_address_index(address)
    pubkeys = self.derive_pubkeys(*sequence)
    return pubkeys[0]

def load_keystore(self):
    self.keystore = load_keystore(self.db, 'keystore')
    try:
        xtype = bip32.xpub_type(self.keystore.xpub)
    except:
        xtype = 'standard'
    self.txin_type = 'p2pkh' if xtype == 'standard' else xtype

def get_master_public_key(self):
    return self.keystore.get_master_public_key()

def derive_pubkeys(self, c, i):
    return [self.keystore.derive_pubkey(c, i).hex()]

class Standard_Wallet(Simple_Deterministic_Wallet): wallet_type = 'standard'

def pubkeys_to_address(self, pubkeys):
    pubkey = pubkeys[0]
    return bitcoin.pubkey_to_address(self.txin_type, pubkey)

class Multisig_Wallet(Deterministic_Wallet):

generic m of n

def __init__(self, db, storage, *, config):
    self.wallet_type = db.get('wallet_type')
    self.m, self.n = multisig_type(self.wallet_type)
    Deterministic_Wallet.__init__(self, db, storage, config=config)

def get_public_keys(self, address):
    return [pk.hex() for pk in self.get_public_keys_with_deriv_info(address)]

def pubkeys_to_address(self, pubkeys):
    redeem_script = self.pubkeys_to_scriptcode(pubkeys)
    return bitcoin.redeem_script_to_address(self.txin_type, redeem_script)

def pubkeys_to_scriptcode(self, pubkeys: Sequence[str]) -> str:
    return transaction.multisig_script(sorted(pubkeys), self.m)

def get_redeem_script(self, address):
    txin_type = self.get_txin_type(address)
    pubkeys = self.get_public_keys(address)
    scriptcode = self.pubkeys_to_scriptcode(pubkeys)
    if txin_type == 'p2sh':
        return scriptcode
    elif txin_type == 'p2wsh-p2sh':
        return bitcoin.p2wsh_nested_script(scriptcode)
    elif txin_type == 'p2wsh':
        return None
    raise UnknownTxinType(f'unexpected txin_type {txin_type}')

def get_witness_script(self, address):
    txin_type = self.get_txin_type(address)
    pubkeys = self.get_public_keys(address)
    scriptcode = self.pubkeys_to_scriptcode(pubkeys)
    if txin_type == 'p2sh':
        return None
    elif txin_type in ('p2wsh-p2sh', 'p2wsh'):
        return scriptcode
    raise UnknownTxinType(f'unexpected txin_type {txin_type}')

def derive_pubkeys(self, c, i):
    return [k.derive_pubkey(c, i).hex() for k in self.get_keystores()]

def load_keystore(self):
    self.keystores = {}
    for i in range(self.n):
        name = 'x%d/'%(i+1)
        self.keystores[name] = load_keystore(self.db, name)
    self.keystore = self.keystores['x1/']
    xtype = bip32.xpub_type(self.keystore.xpub)
    self.txin_type = 'p2sh' if xtype == 'standard' else xtype

def save_keystore(self):
    for name, k in self.keystores.items():
        self.db.put(name, k.dump())

def get_keystore(self):
    return self.keystores.get('x1/')

def get_keystores(self):
    return [self.keystores[i] for i in sorted(self.keystores.keys())]

def can_have_keystore_encryption(self):
    return any([k.may_have_password() for k in self.get_keystores()])

def _update_password_for_keystore(self, old_pw, new_pw):
    for name, keystore in self.keystores.items():
        if keystore.may_have_password():
            keystore.update_password(old_pw, new_pw)
            self.db.put(name, keystore.dump())

def check_password(self, password):
    for name, keystore in self.keystores.items():
        if keystore.may_have_password():
            keystore.check_password(password)
    if self.has_storage_encryption():
        self.storage.check_password(password)

def get_available_storage_encryption_version(self):
    # multisig wallets are not offered hw device encryption
    return StorageEncryptionVersion.USER_PASSWORD

def has_seed(self):
    return self.keystore.has_seed()

def is_watching_only(self):
    return all([k.is_watching_only() for k in self.get_keystores()])
def get_master_public_key(self):
    return self.keystore.get_master_public_key()

def get_master_public_keys(self):
    return [k.get_master_public_key() for k in self.get_keystores()]

def get_fingerprint(self):
    return ''.join(sorted(self.get_master_public_keys()))

wallet_types = ['standard', 'multisig', 'imported']

def register_wallet_type(category): wallet_types.append(category)

wallet_constructors = { 'standard': Standard_Wallet, 'old': Standard_Wallet, 'xpub': Standard_Wallet, 'imported': Imported_Wallet }

def register_constructor(wallet_type, constructor): wallet_constructors[wallet_type] = constructor

former WalletFactory

class Wallet(object): """The main wallet "entry point". This class is actually a factory that will return a wallet of the correct type when passed a WalletStorage instance."""

def __new__(self, db: 'WalletDB', storage: Optional[WalletStorage], *, config: SimpleConfig):
    wallet_type = db.get('wallet_type')
    WalletClass = Wallet.wallet_class(wallet_type)
    wallet = WalletClass(db, storage, config=config)
    return wallet

@staticmethod
def wallet_class(wallet_type):
    if multisig_type(wallet_type):
        return Multisig_Wallet
    if wallet_type in wallet_constructors:
        return wallet_constructors[wallet_type]
    raise WalletFileException("Unknown wallet type: " + str(wallet_type))

def create_new_wallet(*, path, config: SimpleConfig, passphrase=None, password=None, encrypt_file=True, seed_type=None, gap_limit=None) -> dict: """Create a new wallet""" storage = WalletStorage(path) if storage.file_exists(): raise Exception("Remove the existing wallet first!") db = WalletDB('', manual_upgrades=False)

seed = Mnemonic('en').make_seed(seed_type)
k = keystore.from_seed(seed, passphrase)
db.put('keystore', k.dump())
db.put('wallet_type', 'standard')
if gap_limit is not None:
    db.put('gap_limit', gap_limit)
wallet = Wallet(db, storage, config=config)
wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file)
wallet.synchronize()
msg = "Please keep your seed in a safe place; if you lose it, you will not be able to restore your wallet."
wallet.save_db()
return {'seed': seed, 'wallet': wallet, 'msg': msg}

def restore_wallet_from_text(text, *, path, config: SimpleConfig, passphrase=None, password=None, encrypt_file=True, gap_limit=None) -> dict: """Restore a wallet from text. Text can be a seed phrase, a master public key, a master private key, a list of bitcoin addresses or bitcoin private keys.""" storage = WalletStorage(path) if storage.file_exists(): raise Exception("Remove the existing wallet first!") db = WalletDB('', manual_upgrades=False) text = text.strip() if keystore.is_address_list(text): wallet = Imported_Wallet(db, storage, config=config) addresses = text.split() good_inputs, bad_inputs = wallet.import_addresses(addresses, write_to_disk=False)

FIXME tell user about bad_inputs

    if not good_inputs:
        raise Exception("None of the given addresses can be imported")
elif keystore.is_private_key_list(text, allow_spaces_inside_key=False):
    k = keystore.Imported_KeyStore({})
    db.put('keystore', k.dump())
    wallet = Imported_Wallet(db, storage, config=config)
    keys = keystore.get_private_keys(text, allow_spaces_inside_key=False)
    good_inputs, bad_inputs = wallet.import_private_keys(keys, None, write_to_disk=False)
    # FIXME tell user about bad_inputs
    if not good_inputs:
        raise Exception("None of the given privkeys can be imported")
else:
    if keystore.is_master_key(text):
        k = keystore.from_master_key(text)
    elif keystore.is_seed(text):
        k = keystore.from_seed(text, passphrase)
    else:
        raise Exception("Seed or key not recognized")
    db.put('keystore', k.dump())
    db.put('wallet_type', 'standard')
    if gap_limit is not None:
        db.put('gap_limit', gap_limit)
    wallet = Wallet(db, storage, config=config)

assert not storage.file_exists(), "file was created too soon! plaintext keys might have been written to disk"
wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file)
wallet.synchronize()
msg = ("This wallet was restored offline. It may contain more addresses than displayed. "
       "Start a daemon and use load_wallet to sync its history.")

wallet.save_db()
return {'wallet': wallet, 'msg': msg}
PCMARTINEAU commented 3 years ago

<!DOCTYPE html>

GitHub: Where the world builds software · GitHub

Where the world
builds software

Millions of developers and companies build, ship, and maintain their software on GitHub—the largest and most advanced development platform in the world.

56+ million

Developers

3+ million

Organizations

100+ million

Repositories

72%

Fortune 50

Glowing universe

Build like the best with GitHub Enterprise

Take collaboration to the next level with security and administrative features built for teams.

Contact Sales
Futuristic city scape

Give your code a
home in the cloud

  • Record or rewind any change to your code to keep you and your team in sync. Host it all for free with unlimited public and private repositories.

    Sign up for GitHub
The resulting GitHub repository page from pushing
File icon
octocat-classifier
File icon
assets
File icon
tests
File icon
LICENSE
File icon
README.md
File icon
index.js
File icon
package.json
File icon
README.md
File icon
app.js
File icon
index.html
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          
              # Octocat Classifier :octopus: :cat: :mag:

              ![](https://img.shields.io/badge/build-passing-brightgreen) ![](https://img.shields.io/badge/coverage-90%25-green) ![](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen)

              As the name suggests, Octocat Classifier is used to determine whether a given image contains an Octocat. It is trained with images from the [Octodex](1), images shared with [#MyOctocat on Twitter](2), and [photographs of laptops with :octocat: stickers on them]().

              ## Installation
              ```
              git clone https://github.com/jasonetco/octocat-classifier
              ```
            
           ~/octocat-classifier gh repo create octocat-classifier
           Created repository jasonetco/octocat-classifier on GitHub
           Added remote https://github.com/jasonetco/octocat-classifier.git

           ~/octocat-classifier git push origin main
        
Light glowing behind the editor
  • Build on what's
    been built

    Write less code thanks to the world's largest software package registry. Find the best community-approved projects to accelerate your work, then share it with the world with npm and GitHub Packages.

Ready player two. Scale your team to any size in the cloud.

Create a new organization for free
Timeline of a pull request on GitHub
  • Better code starts with pull requests—conversations around your code where you can experiment, squash bugs, and build new features.

    Pull request description on GitHub
  • Code review is built in. Pull requests cover the entire review flow: propose changes, browse code, ask for input, make a suggestion, and sign off in one place.

    Pull request comment on GitHub
  • Know when your pull request is ready to merge when everything’s green. Reviews approved? Check. Tests passing? Check check. No conflicts? Ship it already.

    Merge pull request on GitHub
iPhone notch

Keep work moving. Review or merge code, manage notifications, browse repositories, and more with GitHub for mobile.

Available for iOS and Android

  • Apple App Store
  • Google Play Store
Viewing change log in GitHub Desktop
••• bash
                      ~ gh pr status
                      Relevant pull requests in cli/cli

                      Current branch
                      There is no pull request associated with [main]

                      Created by you
                      You have no open pull requests

                      Requesting a code review from you
                      #1401 Correctly handle and set empty fields... 
                      [octocat:emptyBody]
                      ✓ Checks passing
                      #1357 Added confirmation steps for risk... 
                      [octocat:confirmations]
                      x 1/3 checks failing
                      ~ 
                    

Work however you want. Put a GUI on it with GitHub Desktop or stay in the command line with GitHub CLI.

Available for macOS, Windows, and Linux*

  • macOS
  • Windows
  • Linux

* GitHub CLI is available on macOS, Windows, and Linux
* GitHub Desktop is available on macOS and Windows

Instant dev environments with Codespaces

Learn more about GitHub Codespaces

The future of code is in the cloud, not your local copy. Codespaces gives you a complete, configurable dev environment on top of a powerful VM in minutes.

Visual Studio Code, in your browser, full stop. Codespaces brings the world’s most popular desktop editor to every repo. Code, build, test, use the terminal, and open pull requests from anywhere.

Customize to your heart’s desire. Add your favorite VS Code extensions, create a devcontainer config file, install new themes, and tweak your settings.

VS Code running in the browser with your project's code and development environment running. A terminal panel is visiable in the editor. VS Code running in the browser with your project's code and development environment running. A terminal panel is visiable in the editor. VS Code running in the browser with your project's code and development environment running. A terminal panel is visiable in the editor.
Light glowing behind the editor

Automate anything with GitHub Actions

Learn more about GitHub Actions

Setup CI/CD, enhance your DevOps, and script your entire workflow with GitHub Actions. Kick off automated workflows with GitHub events like push, issue creation, merge, and release.

GitHub Actions web editor editing workflow file showcasing autocomplete

5,000+
Actions

Write your own, or import Actions from the open source community, all within our world-class editor. Feeling stuck? Browse the Actions developer docs as you code.

Explore the Actions Marketplace
The Actions Marketplace is available inline alongside the GitHub Actions workflow editor
The Actions Marketplace is available inline alongside the GitHub Actions workflow editor
The GitHub Actions workflow editor

You can have it all. Run actions in any language or operating system, on Linux, macOS, Windows, ARM, and containers. Or all at once with matrix builds.

  • Linux
  • macOS
  • Windows
  • ARM
Actions build workflow
Actions build workflow
Actions build workflow
Actions build workflow
Actions publish workflow
Actions workflow canvas
Large representation of the indicator that a workflow is successfully completed
Large representation of the indicator that a workflow is running

With 70 million jobs run per month you’re in good company with Actions, the number one CI service on the world’s largest developer platform.

Speaking of automation, Dependabot keeps your projects up to date with automated pull requests that update all your dependencies. Just review and merge to keep your software secure.

Learn more about Dependabot
Automated pull request that updates a dependency in your repository
Merge pull request on GitHub

Find and fix vulnerabilities before you merge

Learn more about advanced security
  • Secure your code as you write it. CodeQL’s code scanning automatically reviews every change to your codebase and identifies known vulnerabilities before they ever reach production.

    Step 3
    Step 2
    Step 1
    Code injection alert
  • Keep your secrets. We automatically scan repositories for OAuth tokens, API keys, personal tokens, and more. If we find one, we’ll notify you and the partner that issued it to invalidate the secret.

    An alert of found secret committed to the repository List of exposed secrets found in the repository

Found a vulnerability? Our security advisory remediation tools help developers identify and disclose them responsibly so maintainers can patch them in dedicated, private workspaces.

Distributed security alerts

The home for
all developers—
including you

  • Beta

    GitHub Discussions is dedicated space for your community to come together, ask and answer questions, and have open-ended conversations.

    Learn how to get started with Discussions
    Technical question from the Gatsby community
    Answered checkmark Floating checkmark Floating checkmark Floating checkmark
    Answer to the technical question from the Gatsby community
  • PCMARTINEAU commented 3 years ago

    Tracking Number:3687461942CU to authenticate this certificate,visit the following site,enter this number, and there follow the instructions displayed.

    https://services.sunbiz.org/Filings/CertificateOfStatus/CertificateAuthentication

    PCMARTINEAU commented 3 years ago

    -----BEGIN PGP PUBLIC KEY BLOCK-----

    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 =NmIc -----END PGP PUBLIC KEY BLOCK-----