URI: 
       tmove more methods from wallet to address_synchronizer - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 0025073b24cf168e2ec29a00640f9280b18f2877
   DIR parent 780b2d067c1ade3575e9511e9e3c71a4a39f50c2
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Thu, 19 Jul 2018 10:15:22 +0200
       
       move more methods from wallet to address_synchronizer
       
       Diffstat:
         M electrum/address_synchronizer.py    |     255 ++++++++++++++++++++++++++++++-
         M electrum/wallet.py                  |     266 +------------------------------
       
       2 files changed, 255 insertions(+), 266 deletions(-)
       ---
   DIR diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py
       t@@ -25,6 +25,8 @@ import threading
        import itertools
        from collections import defaultdict
        
       +from . import bitcoin
       +from .bitcoin import COINBASE_MATURITY
        from .util import PrintError, profiler
        from .transaction import Transaction
        from .synchronizer import Synchronizer
       t@@ -66,9 +68,50 @@ class AddressSynchronizer(PrintError):
                self.up_to_date = False
                self.load_transactions()
                self.load_local_history()
       +        self.check_history()
                self.load_unverified_transactions()
                self.remove_local_transactions_we_dont_have()
        
       +    def is_mine(self, address):
       +        return address in self.history
       +
       +    def get_addresses(self):
       +        return sorted(self.history.keys())
       +
       +    def get_address_history(self, addr):
       +        h = []
       +        # we need self.transaction_lock but get_tx_height will take self.lock
       +        # so we need to take that too here, to enforce order of locks
       +        with self.lock, self.transaction_lock:
       +            related_txns = self._history_local.get(addr, set())
       +            for tx_hash in related_txns:
       +                tx_height = self.get_tx_height(tx_hash)[0]
       +                h.append((tx_hash, tx_height))
       +        return h
       +
       +    def get_txin_address(self, txi):
       +        addr = txi.get('address')
       +        if addr and addr != "(pubkey)":
       +            return addr
       +        prevout_hash = txi.get('prevout_hash')
       +        prevout_n = txi.get('prevout_n')
       +        dd = self.txo.get(prevout_hash, {})
       +        for addr, l in dd.items():
       +            for n, v, is_cb in l:
       +                if n == prevout_n:
       +                    return addr
       +        return None
       +
       +    def get_txout_address(self, txo):
       +        _type, x, v = txo
       +        if _type == TYPE_ADDRESS:
       +            addr = x
       +        elif _type == TYPE_PUBKEY:
       +            addr = bitcoin.public_key_to_p2pkh(bfh(x))
       +        else:
       +            addr = None
       +        return addr
       +
            def load_unverified_transactions(self):
                # review transactions that are in the history
                for addr, hist in self.history.items():
       t@@ -322,6 +365,26 @@ class AddressSynchronizer(PrintError):
                for txid in itertools.chain(self.txi, self.txo):
                    self._add_tx_to_local_history(txid)
        
       +    @profiler
       +    def check_history(self):
       +        save = False
       +        hist_addrs_mine = list(filter(lambda k: self.is_mine(k), self.history.keys()))
       +        hist_addrs_not_mine = list(filter(lambda k: not self.is_mine(k), self.history.keys()))
       +        for addr in hist_addrs_not_mine:
       +            self.history.pop(addr)
       +            save = True
       +        for addr in hist_addrs_mine:
       +            hist = self.history[addr]
       +            for tx_hash, tx_height in hist:
       +                if self.txi.get(tx_hash) or self.txo.get(tx_hash):
       +                    continue
       +                tx = self.transactions.get(tx_hash)
       +                if tx is not None:
       +                    self.add_transaction(tx_hash, tx, allow_unrelated=True)
       +                    save = True
       +        if save:
       +            self.save_transactions()
       +
            def remove_local_transactions_we_dont_have(self):
                txid_set = set(self.txi) | set(self.txo)
                for txid in txid_set:
       t@@ -362,10 +425,22 @@ class AddressSynchronizer(PrintError):
                        self.transactions = {}
                        self.save_transactions()
        
       +    def get_txpos(self, tx_hash):
       +        "return position, even if the tx is unverified"
       +        with self.lock:
       +            if tx_hash in self.verified_tx:
       +                height, timestamp, pos = self.verified_tx[tx_hash]
       +                return height, pos
       +            elif tx_hash in self.unverified_tx:
       +                height = self.unverified_tx[tx_hash]
       +                return (height, 0) if height > 0 else ((1e9 - height), 0)
       +            else:
       +                return (1e9+1, 0)
       +
            def get_history(self, domain=None):
                # get domain
                if domain is None:
       -            domain = self.get_addresses()
       +            domain = self.history.keys()
                domain = set(domain)
                # 1. Get the history of each address in the domain, maintain the
                #    delta of a tx as the sum of its deltas on domain addresses
       t@@ -492,3 +567,181 @@ class AddressSynchronizer(PrintError):
        
            def is_up_to_date(self):
                with self.lock: return self.up_to_date
       +
       +    def get_num_tx(self, address):
       +        """ return number of transactions where address is involved """
       +        return len(self.history.get(address, []))
       +
       +    def get_tx_delta(self, tx_hash, address):
       +        "effect of tx on address"
       +        delta = 0
       +        # substract the value of coins sent from address
       +        d = self.txi.get(tx_hash, {}).get(address, [])
       +        for n, v in d:
       +            delta -= v
       +        # add the value of the coins received at address
       +        d = self.txo.get(tx_hash, {}).get(address, [])
       +        for n, v, cb in d:
       +            delta += v
       +        return delta
       +
       +    def get_tx_value(self, txid):
       +        " effect of tx on the entire domain"
       +        delta = 0
       +        for addr, d in self.txi.get(txid, {}).items():
       +            for n, v in d:
       +                delta -= v
       +        for addr, d in self.txo.get(txid, {}).items():
       +            for n, v, cb in d:
       +                delta += v
       +        return delta
       +
       +    def get_wallet_delta(self, tx):
       +        """ effect of tx on wallet """
       +        is_relevant = False  # "related to wallet?"
       +        is_mine = False
       +        is_pruned = False
       +        is_partial = False
       +        v_in = v_out = v_out_mine = 0
       +        for txin in tx.inputs():
       +            addr = self.get_txin_address(txin)
       +            if self.is_mine(addr):
       +                is_mine = True
       +                is_relevant = True
       +                d = self.txo.get(txin['prevout_hash'], {}).get(addr, [])
       +                for n, v, cb in d:
       +                    if n == txin['prevout_n']:
       +                        value = v
       +                        break
       +                else:
       +                    value = None
       +                if value is None:
       +                    is_pruned = True
       +                else:
       +                    v_in += value
       +            else:
       +                is_partial = True
       +        if not is_mine:
       +            is_partial = False
       +        for addr, value in tx.get_outputs():
       +            v_out += value
       +            if self.is_mine(addr):
       +                v_out_mine += value
       +                is_relevant = True
       +        if is_pruned:
       +            # some inputs are mine:
       +            fee = None
       +            if is_mine:
       +                v = v_out_mine - v_out
       +            else:
       +                # no input is mine
       +                v = v_out_mine
       +        else:
       +            v = v_out_mine - v_in
       +            if is_partial:
       +                # some inputs are mine, but not all
       +                fee = None
       +            else:
       +                # all inputs are mine
       +                fee = v_in - v_out
       +        if not is_mine:
       +            fee = None
       +        return is_relevant, is_mine, v, fee
       +
       +    def get_addr_io(self, address):
       +        h = self.get_address_history(address)
       +        received = {}
       +        sent = {}
       +        for tx_hash, height in h:
       +            l = self.txo.get(tx_hash, {}).get(address, [])
       +            for n, v, is_cb in l:
       +                received[tx_hash + ':%d'%n] = (height, v, is_cb)
       +        for tx_hash, height in h:
       +            l = self.txi.get(tx_hash, {}).get(address, [])
       +            for txi, v in l:
       +                sent[txi] = height
       +        return received, sent
       +
       +    def get_addr_utxo(self, address):
       +        coins, spent = self.get_addr_io(address)
       +        for txi in spent:
       +            coins.pop(txi)
       +        out = {}
       +        for txo, v in coins.items():
       +            tx_height, value, is_cb = v
       +            prevout_hash, prevout_n = txo.split(':')
       +            x = {
       +                'address':address,
       +                'value':value,
       +                'prevout_n':int(prevout_n),
       +                'prevout_hash':prevout_hash,
       +                'height':tx_height,
       +                'coinbase':is_cb
       +            }
       +            out[txo] = x
       +        return out
       +
       +    # return the total amount ever received by an address
       +    def get_addr_received(self, address):
       +        received, sent = self.get_addr_io(address)
       +        return sum([v for height, v, is_cb in received.values()])
       +
       +    # return the balance of a bitcoin address: confirmed and matured, unconfirmed, unmatured
       +    def get_addr_balance(self, address):
       +        received, sent = self.get_addr_io(address)
       +        c = u = x = 0
       +        local_height = self.get_local_height()
       +        for txo, (tx_height, v, is_cb) in received.items():
       +            if is_cb and tx_height + COINBASE_MATURITY > local_height:
       +                x += v
       +            elif tx_height > 0:
       +                c += v
       +            else:
       +                u += v
       +            if txo in sent:
       +                if sent[txo] > 0:
       +                    c -= v
       +                else:
       +                    u -= v
       +        return c, u, x
       +
       +    def get_utxos(self, domain=None, excluded=None, mature=False, confirmed_only=False):
       +        coins = []
       +        if domain is None:
       +            domain = self.get_addresses()
       +        domain = set(domain)
       +        if excluded:
       +            domain = set(domain) - excluded
       +        for addr in domain:
       +            utxos = self.get_addr_utxo(addr)
       +            for x in utxos.values():
       +                if confirmed_only and x['height'] <= 0:
       +                    continue
       +                if mature and x['coinbase'] and x['height'] + COINBASE_MATURITY > self.get_local_height():
       +                    continue
       +                coins.append(x)
       +                continue
       +        return coins
       +
       +    def get_balance(self, domain=None):
       +        if domain is None:
       +            domain = self.get_addresses()
       +        domain = set(domain)
       +        cc = uu = xx = 0
       +        for addr in domain:
       +            c, u, x = self.get_addr_balance(addr)
       +            cc += c
       +            uu += u
       +            xx += x
       +        return cc, uu, xx
       +
       +    def is_used(self, address):
       +        h = self.history.get(address,[])
       +        if len(h) == 0:
       +            return False
       +        c, u, x = self.get_addr_balance(address)
       +        return c + u + x == 0
       +
       +    def is_empty(self, address):
       +        c, u, x = self.get_addr_balance(address)
       +        return c+u+x == 0
   DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
       t@@ -182,8 +182,6 @@ class Abstract_Wallet(AddressSynchronizer):
                self.load_addresses()
                self.test_addresses_sanity()
        
       -        self.check_history()
       -
                # save wallet type the first time
                if self.storage.get('wallet_type') is None:
                    self.storage.put('wallet_type', self.wallet_type)
       t@@ -203,26 +201,6 @@ class Abstract_Wallet(AddressSynchronizer):
            def get_master_public_key(self):
                return None
        
       -    @profiler
       -    def check_history(self):
       -        save = False
       -        hist_addrs_mine = list(filter(lambda k: self.is_mine(k), self.history.keys()))
       -        hist_addrs_not_mine = list(filter(lambda k: not self.is_mine(k), self.history.keys()))
       -        for addr in hist_addrs_not_mine:
       -            self.history.pop(addr)
       -            save = True
       -        for addr in hist_addrs_mine:
       -            hist = self.history[addr]
       -            for tx_hash, tx_height in hist:
       -                if self.txi.get(tx_hash) or self.txo.get(tx_hash):
       -                    continue
       -                tx = self.transactions.get(tx_hash)
       -                if tx is not None:
       -                    self.add_transaction(tx_hash, tx, allow_unrelated=True)
       -                    save = True
       -        if save:
       -            self.save_transactions()
       -
            def basename(self):
                return os.path.basename(self.storage.path)
        
       t@@ -290,9 +268,6 @@ class Abstract_Wallet(AddressSynchronizer):
                except:
                    return
        
       -    def is_mine(self, address):
       -        return address in self.get_addresses()
       -
            def is_change(self, address):
                if not self.is_mine(address):
                    return False
       t@@ -317,101 +292,9 @@ class Abstract_Wallet(AddressSynchronizer):
            def get_public_keys(self, address):
                return [self.get_public_key(address)]
        
       -    def get_txpos(self, tx_hash):
       -        "return position, even if the tx is unverified"
       -        with self.lock:
       -            if tx_hash in self.verified_tx:
       -                height, timestamp, pos = self.verified_tx[tx_hash]
       -                return height, pos
       -            elif tx_hash in self.unverified_tx:
       -                height = self.unverified_tx[tx_hash]
       -                return (height, 0) if height > 0 else ((1e9 - height), 0)
       -            else:
       -                return (1e9+1, 0)
       -
            def is_found(self):
                return self.history.values() != [[]] * len(self.history)
        
       -    def get_num_tx(self, address):
       -        """ return number of transactions where address is involved """
       -        return len(self.history.get(address, []))
       -
       -    def get_tx_delta(self, tx_hash, address):
       -        "effect of tx on address"
       -        delta = 0
       -        # substract the value of coins sent from address
       -        d = self.txi.get(tx_hash, {}).get(address, [])
       -        for n, v in d:
       -            delta -= v
       -        # add the value of the coins received at address
       -        d = self.txo.get(tx_hash, {}).get(address, [])
       -        for n, v, cb in d:
       -            delta += v
       -        return delta
       -
       -    def get_tx_value(self, txid):
       -        " effect of tx on the entire domain"
       -        delta = 0
       -        for addr, d in self.txi.get(txid, {}).items():
       -            for n, v in d:
       -                delta -= v
       -        for addr, d in self.txo.get(txid, {}).items():
       -            for n, v, cb in d:
       -                delta += v
       -        return delta
       -
       -    def get_wallet_delta(self, tx):
       -        """ effect of tx on wallet """
       -        is_relevant = False  # "related to wallet?"
       -        is_mine = False
       -        is_pruned = False
       -        is_partial = False
       -        v_in = v_out = v_out_mine = 0
       -        for txin in tx.inputs():
       -            addr = self.get_txin_address(txin)
       -            if self.is_mine(addr):
       -                is_mine = True
       -                is_relevant = True
       -                d = self.txo.get(txin['prevout_hash'], {}).get(addr, [])
       -                for n, v, cb in d:
       -                    if n == txin['prevout_n']:
       -                        value = v
       -                        break
       -                else:
       -                    value = None
       -                if value is None:
       -                    is_pruned = True
       -                else:
       -                    v_in += value
       -            else:
       -                is_partial = True
       -        if not is_mine:
       -            is_partial = False
       -        for addr, value in tx.get_outputs():
       -            v_out += value
       -            if self.is_mine(addr):
       -                v_out_mine += value
       -                is_relevant = True
       -        if is_pruned:
       -            # some inputs are mine:
       -            fee = None
       -            if is_mine:
       -                v = v_out_mine - v_out
       -            else:
       -                # no input is mine
       -                v = v_out_mine
       -        else:
       -            v = v_out_mine - v_in
       -            if is_partial:
       -                # some inputs are mine, but not all
       -                fee = None
       -            else:
       -                # all inputs are mine
       -                fee = v_in - v_out
       -        if not is_mine:
       -            fee = None
       -        return is_relevant, is_mine, v, fee
       -
            def get_tx_info(self, tx):
                is_relevant, is_mine, v, fee = self.get_wallet_delta(tx)
                exp_n = None
       t@@ -461,143 +344,16 @@ class Abstract_Wallet(AddressSynchronizer):
        
                return tx_hash, status, label, can_broadcast, can_bump, amount, fee, height, conf, timestamp, exp_n
        
       -    def get_addr_io(self, address):
       -        h = self.get_address_history(address)
       -        received = {}
       -        sent = {}
       -        for tx_hash, height in h:
       -            l = self.txo.get(tx_hash, {}).get(address, [])
       -            for n, v, is_cb in l:
       -                received[tx_hash + ':%d'%n] = (height, v, is_cb)
       -        for tx_hash, height in h:
       -            l = self.txi.get(tx_hash, {}).get(address, [])
       -            for txi, v in l:
       -                sent[txi] = height
       -        return received, sent
       -
       -    def get_addr_utxo(self, address):
       -        coins, spent = self.get_addr_io(address)
       -        for txi in spent:
       -            coins.pop(txi)
       -        out = {}
       -        for txo, v in coins.items():
       -            tx_height, value, is_cb = v
       -            prevout_hash, prevout_n = txo.split(':')
       -            x = {
       -                'address':address,
       -                'value':value,
       -                'prevout_n':int(prevout_n),
       -                'prevout_hash':prevout_hash,
       -                'height':tx_height,
       -                'coinbase':is_cb
       -            }
       -            out[txo] = x
       -        return out
       -
       -    # return the total amount ever received by an address
       -    def get_addr_received(self, address):
       -        received, sent = self.get_addr_io(address)
       -        return sum([v for height, v, is_cb in received.values()])
       -
       -    # return the balance of a bitcoin address: confirmed and matured, unconfirmed, unmatured
       -    def get_addr_balance(self, address):
       -        received, sent = self.get_addr_io(address)
       -        c = u = x = 0
       -        local_height = self.get_local_height()
       -        for txo, (tx_height, v, is_cb) in received.items():
       -            if is_cb and tx_height + COINBASE_MATURITY > local_height:
       -                x += v
       -            elif tx_height > 0:
       -                c += v
       -            else:
       -                u += v
       -            if txo in sent:
       -                if sent[txo] > 0:
       -                    c -= v
       -                else:
       -                    u -= v
       -        return c, u, x
       -
            def get_spendable_coins(self, domain, config):
                confirmed_only = config.get('confirmed_only', False)
       -        return self.get_utxos(domain, exclude_frozen=True, mature=True, confirmed_only=confirmed_only)
       -
       -    def get_utxos(self, domain = None, exclude_frozen = False, mature = False, confirmed_only = False):
       -        coins = []
       -        if domain is None:
       -            domain = self.get_addresses()
       -        domain = set(domain)
       -        if exclude_frozen:
       -            domain = set(domain) - self.frozen_addresses
       -        for addr in domain:
       -            utxos = self.get_addr_utxo(addr)
       -            for x in utxos.values():
       -                if confirmed_only and x['height'] <= 0:
       -                    continue
       -                if mature and x['coinbase'] and x['height'] + COINBASE_MATURITY > self.get_local_height():
       -                    continue
       -                coins.append(x)
       -                continue
       -        return coins
       +        return self.get_utxos(domain, excluded=self.frozen_addresses, mature=True, confirmed_only=confirmed_only)
        
            def dummy_address(self):
                return self.get_receiving_addresses()[0]
        
       -    def get_addresses(self):
       -        out = []
       -        out += self.get_receiving_addresses()
       -        out += self.get_change_addresses()
       -        return out
       -
            def get_frozen_balance(self):
                return self.get_balance(self.frozen_addresses)
        
       -    def get_balance(self, domain=None):
       -        if domain is None:
       -            domain = self.get_addresses()
       -        domain = set(domain)
       -        cc = uu = xx = 0
       -        for addr in domain:
       -            c, u, x = self.get_addr_balance(addr)
       -            cc += c
       -            uu += u
       -            xx += x
       -        return cc, uu, xx
       -
       -    def get_address_history(self, addr):
       -        h = []
       -        # we need self.transaction_lock but get_tx_height will take self.lock
       -        # so we need to take that too here, to enforce order of locks
       -        with self.lock, self.transaction_lock:
       -            related_txns = self._history_local.get(addr, set())
       -            for tx_hash in related_txns:
       -                tx_height = self.get_tx_height(tx_hash)[0]
       -                h.append((tx_hash, tx_height))
       -        return h
       -
       -    def get_txin_address(self, txi):
       -        addr = txi.get('address')
       -        if addr and addr != "(pubkey)":
       -            return addr
       -        prevout_hash = txi.get('prevout_hash')
       -        prevout_n = txi.get('prevout_n')
       -        dd = self.txo.get(prevout_hash, {})
       -        for addr, l in dd.items():
       -            for n, v, is_cb in l:
       -                if n == prevout_n:
       -                    return addr
       -        return None
       -
       -    def get_txout_address(self, txo):
       -        _type, x, v = txo
       -        if _type == TYPE_ADDRESS:
       -            addr = x
       -        elif _type == TYPE_PUBKEY:
       -            addr = bitcoin.public_key_to_p2pkh(bfh(x))
       -        else:
       -            addr = None
       -        return addr
       -
            def balance_at_timestamp(self, domain, target_timestamp):
                h = self.get_history(domain)
                for tx_hash, height, conf, timestamp, value, balance in h:
       t@@ -884,17 +640,6 @@ class Abstract_Wallet(AddressSynchronizer):
            def can_export(self):
                return not self.is_watching_only() and hasattr(self.keystore, 'get_private_key')
        
       -    def is_used(self, address):
       -        h = self.history.get(address,[])
       -        if len(h) == 0:
       -            return False
       -        c, u, x = self.get_addr_balance(address)
       -        return c + u + x == 0
       -
       -    def is_empty(self, address):
       -        c, u, x = self.get_addr_balance(address)
       -        return c+u+x == 0
       -
            def address_is_old(self, address, age_limit=2):
                age = -1
                h = self.history.get(address, [])
       t@@ -1458,15 +1203,9 @@ class Imported_Wallet(Simple_Wallet):
            def is_beyond_limit(self, address):
                return False
        
       -    def is_mine(self, address):
       -        return address in self.addresses
       -
            def get_fingerprint(self):
                return ''
        
       -    def get_addresses(self, include_change=False):
       -        return sorted(self.addresses.keys())
       -
            def get_receiving_addresses(self):
                return self.get_addresses()
        
       t@@ -1699,9 +1438,6 @@ class Deterministic_Wallet(Abstract_Wallet):
                        return False
                return True
        
       -    def is_mine(self, address):
       -        return address in self._addr_to_addr_index
       -
            def get_address_index(self, address):
                return self._addr_to_addr_index[address]