electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 81cc20039ea5d45204c00b75b283ea877029fbb0
parent 6958c0ccc3d97edfb7f8504f27270182cb3abcfa
Author: SomberNight <somber.night@protonmail.com>
Date:   Mon, 22 Oct 2018 16:41:25 +0200

more type annotations in core lib

Diffstat:
Melectrum/address_synchronizer.py | 10++++++++--
Melectrum/base_wizard.py | 13+++++++++----
Melectrum/blockchain.py | 5+++--
Melectrum/commands.py | 9+++++++--
Melectrum/daemon.py | 18+++++++++---------
Melectrum/exchange_rate.py | 4+++-
Melectrum/interface.py | 7+++++--
Melectrum/network.py | 4++--
Melectrum/plugin.py | 8+++++---
Melectrum/synchronizer.py | 10+++++++---
Melectrum/util.py | 12++++++++----
Melectrum/verifier.py | 8++++++--
Melectrum/wallet.py | 20++++++++++----------
Melectrum/websockets.py | 11++++++++---
14 files changed, 90 insertions(+), 49 deletions(-)

diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py @@ -34,6 +34,9 @@ from .synchronizer import Synchronizer from .verifier import SPV from .blockchain import hash_header from .i18n import _ +from .storage import WalletStorage +from .network import Network + TX_HEIGHT_LOCAL = -2 TX_HEIGHT_UNCONF_PARENT = -1 @@ -53,9 +56,9 @@ class AddressSynchronizer(PrintError): inherited by wallet """ - def __init__(self, storage): + def __init__(self, storage: WalletStorage): self.storage = storage - self.network = None + self.network = None # type: Network # verifier (SPV) and synchronizer are started in start_network self.synchronizer = None # type: Synchronizer self.verifier = None # type: SPV @@ -807,3 +810,6 @@ class AddressSynchronizer(PrintError): def is_empty(self, address): c, u, x = self.get_addr_balance(address) return c+u+x == 0 + + def synchronize(self): + pass diff --git a/electrum/base_wizard.py b/electrum/base_wizard.py @@ -31,10 +31,15 @@ from functools import partial from . import bitcoin from . import keystore from .keystore import bip44_derivation, purpose48_derivation -from .wallet import Imported_Wallet, Standard_Wallet, Multisig_Wallet, wallet_types, Wallet -from .storage import STO_EV_USER_PW, STO_EV_XPUB_PW, get_derivation_used_for_hw_device_encryption +from .wallet import (Imported_Wallet, Standard_Wallet, Multisig_Wallet, + wallet_types, Wallet, Abstract_Wallet) +from .storage import (WalletStorage, STO_EV_USER_PW, STO_EV_XPUB_PW, + get_derivation_used_for_hw_device_encryption) from .i18n import _ from .util import UserCancelled, InvalidPassword, WalletFileException +from .simple_config import SimpleConfig +from .plugin import Plugins + # hardware device setup purpose HWD_SETUP_NEW_WALLET, HWD_SETUP_DECRYPT_WALLET = range(0, 2) @@ -48,12 +53,12 @@ class GoBack(Exception): pass class BaseWizard(object): - def __init__(self, config, plugins, storage): + def __init__(self, config: SimpleConfig, plugins: Plugins, storage: WalletStorage): super(BaseWizard, self).__init__() self.config = config self.plugins = plugins self.storage = storage - self.wallet = None + self.wallet = None # type: Abstract_Wallet self.stack = [] self.plugin = None self.keystores = [] diff --git a/electrum/blockchain.py b/electrum/blockchain.py @@ -28,6 +28,7 @@ from . import util from .bitcoin import Hash, hash_encode, int_to_hex, rev_hex from . import constants from .util import bfh, bh2u +from .simple_config import SimpleConfig HEADER_SIZE = 80 # bytes @@ -77,7 +78,7 @@ blockchains = {} # type: Dict[int, Blockchain] blockchains_lock = threading.Lock() -def read_blockchains(config): +def read_blockchains(config: 'SimpleConfig') -> Dict[int, 'Blockchain']: blockchains[0] = Blockchain(config, 0, None) fdir = os.path.join(util.get_headers_dir(config), 'forks') util.make_dir(fdir) @@ -100,7 +101,7 @@ class Blockchain(util.PrintError): Manages blockchain headers and their verification """ - def __init__(self, config, forkpoint: int, parent_id: Optional[int]): + def __init__(self, config: SimpleConfig, forkpoint: int, parent_id: Optional[int]): self.config = config self.forkpoint = forkpoint self.checkpoints = constants.net.CHECKPOINTS diff --git a/electrum/commands.py b/electrum/commands.py @@ -32,6 +32,7 @@ import ast import base64 from functools import wraps from decimal import Decimal +from typing import Optional from .import util, ecc from .util import bfh, bh2u, format_satoshis, json_decode, print_error, json_encode @@ -43,8 +44,11 @@ from .paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED from .synchronizer import Notifier from .storage import WalletStorage from . import keystore -from .wallet import Wallet, Imported_Wallet +from .wallet import Wallet, Imported_Wallet, Abstract_Wallet from .mnemonic import Mnemonic +from .network import Network +from .simple_config import SimpleConfig + known_commands = {} @@ -95,7 +99,8 @@ def command(s): class Commands: - def __init__(self, config, wallet, network, callback = None): + def __init__(self, config: 'SimpleConfig', wallet: Abstract_Wallet, + network: Optional['Network'], callback=None): self.config = config self.wallet = wallet self.network = network diff --git a/electrum/daemon.py b/electrum/daemon.py @@ -29,7 +29,7 @@ import time import traceback import sys import threading -from typing import Dict +from typing import Dict, Optional, Tuple import jsonrpclib @@ -46,7 +46,7 @@ from .exchange_rate import FxThread from .plugin import run_hook -def get_lockfile(config): +def get_lockfile(config: SimpleConfig): return os.path.join(config.path, 'daemon') @@ -54,7 +54,7 @@ def remove_lockfile(lockfile): os.unlink(lockfile) -def get_fd_or_server(config): +def get_fd_or_server(config: SimpleConfig): '''Tries to create the lockfile, using O_EXCL to prevent races. If it succeeds it returns the FD. Otherwise try and connect to the server specified in the lockfile. @@ -73,7 +73,7 @@ def get_fd_or_server(config): remove_lockfile(lockfile) -def get_server(config): +def get_server(config: SimpleConfig) -> Optional[jsonrpclib.Server]: lockfile = get_lockfile(config) while True: create_time = None @@ -99,7 +99,7 @@ def get_server(config): time.sleep(1.0) -def get_rpc_credentials(config): +def get_rpc_credentials(config: SimpleConfig) -> Tuple[str, str]: rpc_user = config.get('rpcuser', None) rpc_password = config.get('rpcpassword', None) if rpc_user is None or rpc_password is None: @@ -121,7 +121,7 @@ def get_rpc_credentials(config): class Daemon(DaemonThread): - def __init__(self, config, fd=None, *, listen_jsonrpc=True): + def __init__(self, config: SimpleConfig, fd=None, *, listen_jsonrpc=True): DaemonThread.__init__(self) self.config = config if fd is None and listen_jsonrpc: @@ -142,7 +142,7 @@ class Daemon(DaemonThread): self.init_server(config, fd) self.start() - def init_server(self, config, fd): + def init_server(self, config: SimpleConfig, fd): host = config.get('rpchost', '127.0.0.1') port = config.get('rpcport', 0) rpc_user, rpc_password = get_rpc_credentials(config) @@ -230,7 +230,7 @@ class Daemon(DaemonThread): response = "Error: Electrum is running in daemon mode. Please stop the daemon first." return response - def load_wallet(self, path, password): + def load_wallet(self, path, password) -> Optional[Abstract_Wallet]: # wizard will be launched if we return if path in self.wallets: wallet = self.wallets[path] @@ -251,7 +251,7 @@ class Daemon(DaemonThread): self.wallets[path] = wallet return wallet - def add_wallet(self, wallet): + def add_wallet(self, wallet: Abstract_Wallet): path = wallet.storage.path self.wallets[path] = wallet diff --git a/electrum/exchange_rate.py b/electrum/exchange_rate.py @@ -17,6 +17,8 @@ from .i18n import _ from .util import PrintError, ThreadJob, make_dir, log_exceptions from .util import make_aiohttp_session from .network import Network +from .simple_config import SimpleConfig + # See https://en.wikipedia.org/wiki/ISO_4217 CCY_PRECISIONS = {'BHD': 3, 'BIF': 0, 'BYR': 0, 'CLF': 4, 'CLP': 0, @@ -434,7 +436,7 @@ def get_exchanges_by_ccy(history=True): class FxThread(ThreadJob): - def __init__(self, config, network): + def __init__(self, config: SimpleConfig, network: Network): self.config = config self.network = network if self.network: diff --git a/electrum/interface.py b/electrum/interface.py @@ -28,7 +28,7 @@ import ssl import sys import traceback import asyncio -from typing import Tuple, Union, List +from typing import Tuple, Union, List, TYPE_CHECKING from collections import defaultdict import aiorpcx @@ -43,6 +43,9 @@ from . import blockchain from .blockchain import Blockchain from . import constants +if TYPE_CHECKING: + from .network import Network + class NotificationSession(ClientSession): @@ -129,7 +132,7 @@ def serialize_server(host: str, port: Union[str, int], protocol: str) -> str: class Interface(PrintError): - def __init__(self, network, server, config_path, proxy): + def __init__(self, network: 'Network', server: str, config_path, proxy: dict): self.ready = asyncio.Future() self.got_disconnected = asyncio.Future() self.server = server diff --git a/electrum/network.py b/electrum/network.py @@ -164,12 +164,12 @@ class Network(PrintError): """ verbosity_filter = 'n' - def __init__(self, config=None): + def __init__(self, config: SimpleConfig=None): global INSTANCE INSTANCE = self if config is None: config = {} # Do not use mutables as default values! - self.config = SimpleConfig(config) if isinstance(config, dict) else config + self.config = SimpleConfig(config) if isinstance(config, dict) else config # type: SimpleConfig self.num_server = 10 if not self.config.get('oneserver') else 0 blockchain.blockchains = blockchain.read_blockchains(self.config) self.print_error("blockchains", list(blockchain.blockchains)) diff --git a/electrum/plugin.py b/electrum/plugin.py @@ -30,11 +30,13 @@ import pkgutil import time import threading -from .util import print_error from .i18n import _ -from .util import profiler, PrintError, DaemonThread, UserCancelled, ThreadJob +from .util import (profiler, PrintError, DaemonThread, UserCancelled, + ThreadJob, print_error) from . import bitcoin from . import plugins +from .simple_config import SimpleConfig + plugin_loaders = {} hook_names = set() @@ -45,7 +47,7 @@ class Plugins(DaemonThread): verbosity_filter = 'p' @profiler - def __init__(self, config, is_local, gui_name): + def __init__(self, config: SimpleConfig, is_local, gui_name): DaemonThread.__init__(self) self.setName('Plugins') self.pkgpath = os.path.dirname(plugins.__file__) diff --git a/electrum/synchronizer.py b/electrum/synchronizer.py @@ -24,7 +24,7 @@ # SOFTWARE. import asyncio import hashlib -from typing import Dict, List +from typing import Dict, List, TYPE_CHECKING from collections import defaultdict from aiorpcx import TaskGroup, run_in_thread @@ -33,6 +33,10 @@ from .transaction import Transaction from .util import bh2u, make_aiohttp_session, NetworkJobOnDefaultServer from .bitcoin import address_to_scripthash +if TYPE_CHECKING: + from .network import Network + from .address_synchronizer import AddressSynchronizer + def history_status(h): if not h: @@ -47,7 +51,7 @@ class SynchronizerBase(NetworkJobOnDefaultServer): """Subscribe over the network to a set of addresses, and monitor their statuses. Every time a status changes, run a coroutine provided by the subclass. """ - def __init__(self, network): + def __init__(self, network: 'Network'): self.asyncio_loop = network.asyncio_loop NetworkJobOnDefaultServer.__init__(self, network) @@ -112,7 +116,7 @@ class Synchronizer(SynchronizerBase): we don't have the full history of, and requests binary transaction data of any transactions the wallet doesn't have. ''' - def __init__(self, wallet): + def __init__(self, wallet: 'AddressSynchronizer'): self.wallet = wallet SynchronizerBase.__init__(self, wallet.network) diff --git a/electrum/util.py b/electrum/util.py @@ -23,7 +23,7 @@ import binascii import os, sys, re, json from collections import defaultdict -from typing import NamedTuple, Union +from typing import NamedTuple, Union, TYPE_CHECKING from datetime import datetime import decimal from decimal import Decimal @@ -46,6 +46,10 @@ from aiorpcx import TaskGroup from .i18n import _ +if TYPE_CHECKING: + from .network import Network + from .interface import Interface + def inv_dict(d): return {v: k for k, v in d.items()} @@ -923,10 +927,10 @@ class NetworkJobOnDefaultServer(PrintError): interface. Every time the main interface changes, the job is restarted, and some of its internals are reset. """ - def __init__(self, network): + def __init__(self, network: 'Network'): asyncio.set_event_loop(network.asyncio_loop) self.network = network - self.interface = None + self.interface = None # type: Interface self._restart_lock = asyncio.Lock() self._reset() asyncio.run_coroutine_threadsafe(self._restart(), network.asyncio_loop) @@ -938,7 +942,7 @@ class NetworkJobOnDefaultServer(PrintError): """ self.group = SilentTaskGroup() - async def _start(self, interface): + async def _start(self, interface: 'Interface'): self.interface = interface await interface.group.spawn(self._start_tasks) diff --git a/electrum/verifier.py b/electrum/verifier.py @@ -22,7 +22,7 @@ # SOFTWARE. import asyncio -from typing import Sequence, Optional +from typing import Sequence, Optional, TYPE_CHECKING import aiorpcx @@ -33,6 +33,10 @@ from .blockchain import hash_header from .interface import GracefulDisconnect from . import constants +if TYPE_CHECKING: + from .network import Network + from .address_synchronizer import AddressSynchronizer + class MerkleVerificationFailure(Exception): pass class MissingBlockHeader(MerkleVerificationFailure): pass @@ -43,7 +47,7 @@ class InnerNodeOfSpvProofIsValidTx(MerkleVerificationFailure): pass class SPV(NetworkJobOnDefaultServer): """ Simple Payment Verification """ - def __init__(self, network, wallet): + def __init__(self, network: 'Network', wallet: 'AddressSynchronizer'): self.wallet = wallet NetworkJobOnDefaultServer.__init__(self, network) diff --git a/electrum/wallet.py b/electrum/wallet.py @@ -48,7 +48,7 @@ from .util import (NotEnoughFunds, PrintError, UserCancelled, profiler, from .bitcoin import * from .version import * from .keystore import load_keystore, Hardware_KeyStore -from .storage import multisig_type, STO_EV_PLAINTEXT, STO_EV_USER_PW, STO_EV_XPUB_PW +from .storage import multisig_type, STO_EV_PLAINTEXT, STO_EV_USER_PW, STO_EV_XPUB_PW, WalletStorage from . import transaction, bitcoin, coinchooser, paymentrequest, contacts from .transaction import Transaction, TxOutput, TxOutputHwInfo from .plugin import run_hook @@ -57,6 +57,9 @@ from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL, from .paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED from .paymentrequest import InvoiceStore from .contacts import Contacts +from .network import Network +from .simple_config import SimpleConfig + TX_STATUS = [ _('Unconfirmed'), @@ -67,18 +70,18 @@ TX_STATUS = [ -def relayfee(network): +def relayfee(network: Network): from .simple_config import FEERATE_DEFAULT_RELAY MAX_RELAY_FEE = 50000 f = network.relay_fee if network and network.relay_fee else FEERATE_DEFAULT_RELAY return min(f, MAX_RELAY_FEE) -def dust_threshold(network): +def dust_threshold(network: Network): # Change <= dust threshold is added to the tx fee return 182 * 3 * relayfee(network) / 1000 -def append_utxos_to_inputs(inputs, network, pubkey, txin_type, imax): +def append_utxos_to_inputs(inputs, network: Network, pubkey, txin_type, imax): if txin_type != 'p2pk': address = bitcoin.pubkey_to_address(txin_type, pubkey) scripthash = bitcoin.address_to_scripthash(address) @@ -101,7 +104,7 @@ def append_utxos_to_inputs(inputs, network, pubkey, txin_type, imax): item['num_sig'] = 1 inputs.append(item) -def sweep_preparations(privkeys, network, imax=100): +def sweep_preparations(privkeys, network: Network, imax=100): def find_utxos_for_privkey(txin_type, privkey, compressed): pubkey = ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed) @@ -127,7 +130,7 @@ def sweep_preparations(privkeys, network, imax=100): return inputs, keypairs -def sweep(privkeys, network, config, recipient, fee=None, imax=100): +def sweep(privkeys, network: Network, config: SimpleConfig, recipient, fee=None, imax=100): inputs, keypairs = sweep_preparations(privkeys, network, imax) total = sum(i.get('value') for i in inputs) if fee is None: @@ -164,7 +167,7 @@ class Abstract_Wallet(AddressSynchronizer): gap_limit_for_change = 6 verbosity_filter = 'w' - def __init__(self, storage): + def __init__(self, storage: WalletStorage): AddressSynchronizer.__init__(self, storage) # saved fields @@ -220,9 +223,6 @@ class Abstract_Wallet(AddressSynchronizer): if not bitcoin.is_address(addrs[0]): raise WalletFileException('The addresses in this wallet are not bitcoin addresses.') - def synchronize(self): - pass - def calc_unused_change_addresses(self): with self.lock: if hasattr(self, '_unused_change_addresses'): diff --git a/electrum/websockets.py b/electrum/websockets.py @@ -27,7 +27,7 @@ import os import json from collections import defaultdict import asyncio -from typing import Dict, List, Tuple +from typing import Dict, List, Tuple, TYPE_CHECKING import traceback import sys @@ -40,6 +40,11 @@ from .util import PrintError from . import bitcoin from .synchronizer import SynchronizerBase +if TYPE_CHECKING: + from .network import Network + from .simple_config import SimpleConfig + + request_queue = asyncio.Queue() @@ -61,7 +66,7 @@ class ElectrumWebSocket(WebSocket, PrintError): class BalanceMonitor(SynchronizerBase): - def __init__(self, config, network): + def __init__(self, config: 'SimpleConfig', network: 'Network'): SynchronizerBase.__init__(self, network) self.config = config self.expected_payments = defaultdict(list) # type: Dict[str, List[Tuple[WebSocket, int]]] @@ -104,7 +109,7 @@ class BalanceMonitor(SynchronizerBase): class WebSocketServer(threading.Thread): - def __init__(self, config, network): + def __init__(self, config: 'SimpleConfig', network: 'Network'): threading.Thread.__init__(self) self.config = config self.network = network