electrum

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

commit f3eeb8817e847cf5b1f8fd7db92b8486937e6da9
parent d6c7dee547c844ecac6a5879721ae767efe4b9bd
Author: ThomasV <thomasv@electrum.org>
Date:   Sun, 22 Sep 2019 23:56:08 +0200

Merge pull request #5652 from SomberNight/20190922_config_no_longer_singleton

config: no longer singleton. it is passed to Wallet.__init__
Diffstat:
Melectrum/commands.py | 6++++--
Melectrum/daemon.py | 2+-
Melectrum/gui/kivy/main_window.py | 2+-
Melectrum/gui/qt/__init__.py | 2+-
Melectrum/gui/qt/qrcodewidget.py | 52+++++++++++++++++++++++++++-------------------------
Melectrum/gui/qt/qrtextedit.py | 9++++++---
Melectrum/gui/stdio.py | 2+-
Melectrum/gui/text.py | 2+-
Melectrum/lnchannel.py | 7+++++--
Melectrum/lnsweep.py | 46++++++++++++++++++++++++++++------------------
Melectrum/lnworker.py | 2+-
Melectrum/plugins/trustedcoin/trustedcoin.py | 4++--
Melectrum/scripts/quick_start.py | 6+++---
Melectrum/simple_config.py | 37++++++-------------------------------
Melectrum/sql_db.py | 1+
Melectrum/tests/__init__.py | 22++++++++++++++++------
Melectrum/tests/test_bitcoin.py | 10+++++-----
Melectrum/tests/test_blockchain.py | 12++++--------
Melectrum/tests/test_bolt11.py | 11++++++++---
Melectrum/tests/test_commands.py | 26++++++++++++++++----------
Melectrum/tests/test_dnssec.py | 4++--
Melectrum/tests/test_lnchannel.py | 15++++++++++-----
Melectrum/tests/test_lnhtlc.py | 8++++++--
Melectrum/tests/test_lnpeer.py | 4++--
Melectrum/tests/test_lnrouter.py | 19++++++-------------
Melectrum/tests/test_lntransport.py | 11++++++++---
Melectrum/tests/test_lnutil.py | 7++++++-
Melectrum/tests/test_mnemonic.py | 10+++++-----
Melectrum/tests/test_network.py | 7+++++--
Melectrum/tests/test_revealer.py | 4++--
Melectrum/tests/test_simple_config.py | 6+++---
Melectrum/tests/test_storage_upgrade.py | 8++++----
Melectrum/tests/test_transaction.py | 6+++---
Melectrum/tests/test_util.py | 4++--
Melectrum/tests/test_wallet.py | 24++++++++++++++----------
Melectrum/tests/test_wallet_vertical.py | 248++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Melectrum/tests/test_x509.py | 7++++++-
Melectrum/wallet.py | 39+++++++++++++++++++--------------------
Mrun_electrum | 2+-
39 files changed, 374 insertions(+), 320 deletions(-)

diff --git a/electrum/commands.py b/electrum/commands.py @@ -216,7 +216,8 @@ class Commands: passphrase=passphrase, password=password, encrypt_file=encrypt_file, - seed_type=seed_type) + seed_type=seed_type, + config=self.config) return { 'seed': d['seed'], 'path': d['wallet'].storage.path, @@ -235,7 +236,8 @@ class Commands: path=wallet_path, passphrase=passphrase, password=password, - encrypt_file=encrypt_file) + encrypt_file=encrypt_file, + config=self.config) return { 'path': d['wallet'].storage.path, 'msg': d['msg'], diff --git a/electrum/daemon.py b/electrum/daemon.py @@ -386,7 +386,7 @@ class Daemon(Logger): return if storage.get_action(): return - wallet = Wallet(storage) + wallet = Wallet(storage, config=self.config) wallet.start_network(self.network) self._wallets[path] = wallet self.wallet = wallet diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py @@ -576,7 +576,7 @@ class ElectrumWindow(App): def on_wizard_complete(self, wizard, storage): if storage: - wallet = Wallet(storage) + wallet = Wallet(storage, config=self.electrum_config) wallet.start_network(self.daemon.network) self.daemon.add_wallet(wallet) self.load_wallet(wallet) diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py @@ -308,7 +308,7 @@ class ElectrumGui(Logger): # return if wallet creation is not complete if storage is None or storage.get_action(): return - wallet = Wallet(storage) + wallet = Wallet(storage, config=self.config) wallet.start_network(self.daemon.network) self.daemon.add_wallet(wallet) return wallet diff --git a/electrum/gui/qt/qrcodewidget.py b/electrum/gui/qt/qrcodewidget.py @@ -1,17 +1,16 @@ -import os import qrcode from PyQt5.QtGui import QColor, QPen import PyQt5.QtGui as QtGui from PyQt5.QtCore import Qt from PyQt5.QtWidgets import ( - QApplication, QVBoxLayout, QTextEdit, QHBoxLayout, QPushButton, QWidget) + QApplication, QVBoxLayout, QTextEdit, QHBoxLayout, QPushButton, QWidget, + QFileDialog, +) -import electrum from electrum.i18n import _ -from electrum.simple_config import SimpleConfig -from .util import WindowModalDialog +from .util import WindowModalDialog, get_parent_main_window class QRCodeWidget(QWidget): @@ -107,27 +106,30 @@ class QRDialog(WindowModalDialog): hbox = QHBoxLayout() hbox.addStretch(1) - config = SimpleConfig.get_instance() - if config: - filename = os.path.join(config.path, "qrcode.png") - - def print_qr(): - p = qrw.grab() # FIXME also grabs neutral colored padding - p.save(filename, 'png') - self.show_message(_("QR code saved to file") + " " + filename) - - def copy_to_clipboard(): - p = qrw.grab() - QApplication.clipboard().setPixmap(p) - self.show_message(_("QR code copied to clipboard")) - - b = QPushButton(_("Copy")) - hbox.addWidget(b) - b.clicked.connect(copy_to_clipboard) + def print_qr(): + main_window = get_parent_main_window(self) + if main_window: + filename = main_window.getSaveFileName(_("Select where to save file"), "qrcode.png") + else: + filename, __ = QFileDialog.getSaveFileName(self, _("Select where to save file"), "qrcode.png") + if not filename: + return + p = qrw.grab() # FIXME also grabs neutral colored padding + p.save(filename, 'png') + self.show_message(_("QR code saved to file") + " " + filename) + + def copy_to_clipboard(): + p = qrw.grab() + QApplication.clipboard().setPixmap(p) + self.show_message(_("QR code copied to clipboard")) + + b = QPushButton(_("Copy")) + hbox.addWidget(b) + b.clicked.connect(copy_to_clipboard) - b = QPushButton(_("Save")) - hbox.addWidget(b) - b.clicked.connect(print_qr) + b = QPushButton(_("Save")) + hbox.addWidget(b) + b.clicked.connect(print_qr) b = QPushButton(_("Close")) hbox.addWidget(b) diff --git a/electrum/gui/qt/qrtextedit.py b/electrum/gui/qt/qrtextedit.py @@ -4,7 +4,7 @@ from electrum.i18n import _ from electrum.plugin import run_hook from electrum.simple_config import SimpleConfig -from .util import ButtonsTextEdit, MessageBoxMixin, ColorScheme +from .util import ButtonsTextEdit, MessageBoxMixin, ColorScheme, get_parent_main_window class ShowQRTextEdit(ButtonsTextEdit): @@ -23,7 +23,7 @@ class ShowQRTextEdit(ButtonsTextEdit): s = str(self.toPlainText()) except: s = self.toPlainText() - QRDialog(s).exec_() + QRDialog(s, parent=self).exec_() def contextMenuEvent(self, e): m = self.createStandardContextMenu() @@ -56,8 +56,11 @@ class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin): def qr_input(self): from electrum import qrscanner + main_window = get_parent_main_window(self) + assert main_window + config = main_window.config try: - data = qrscanner.scan_barcode(SimpleConfig.get_instance().get_video_device()) + data = qrscanner.scan_barcode(config.get_video_device()) except BaseException as e: self.show_error(repr(e)) data = '' diff --git a/electrum/gui/stdio.py b/electrum/gui/stdio.py @@ -39,7 +39,7 @@ class ElectrumGui: self.str_amount = "" self.str_fee = "" - self.wallet = Wallet(storage) + self.wallet = Wallet(storage, config=config) self.wallet.start_network(self.network) self.contacts = self.wallet.contacts diff --git a/electrum/gui/text.py b/electrum/gui/text.py @@ -33,7 +33,7 @@ class ElectrumGui: if storage.is_encrypted(): password = getpass.getpass('Password:', stream=None) storage.decrypt(password) - self.wallet = Wallet(storage) + self.wallet = Wallet(storage, config=config) self.wallet.start_network(self.network) self.contacts = self.wallet.contacts diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py @@ -27,7 +27,7 @@ from collections import namedtuple, defaultdict import binascii import json from enum import Enum, auto -from typing import Optional, Dict, List, Tuple, NamedTuple, Set, Callable, Iterable, Sequence +from typing import Optional, Dict, List, Tuple, NamedTuple, Set, Callable, Iterable, Sequence, TYPE_CHECKING import time from . import ecc @@ -51,6 +51,9 @@ from .lnsweep import create_sweeptxs_for_our_ctx, create_sweeptxs_for_their_ctx from .lnsweep import create_sweeptx_for_their_revoked_htlc, SweepInfo from .lnhtlc import HTLCManager +if TYPE_CHECKING: + from .lnworker import LNWallet + class ChannelJsonEncoder(json.JSONEncoder): def default(self, o): @@ -110,7 +113,7 @@ class Channel(Logger): def __init__(self, state, *, sweep_address=None, name=None, lnworker=None, initial_feerate=None): self.name = name Logger.__init__(self) - self.lnworker = lnworker + self.lnworker = lnworker # type: Optional[LNWallet] self.sweep_address = sweep_address assert 'local_state' not in state self.config = {} diff --git a/electrum/lnsweep.py b/electrum/lnsweep.py @@ -16,7 +16,7 @@ from .lnutil import (make_commitment_output_to_remote_address, make_commitment_o RevocationStore, extract_ctn_from_tx_and_chan, UnableToDeriveSecret, SENT, RECEIVED, map_htlcs_to_ctx_output_idxs, Direction) from .transaction import Transaction, TxOutput, construct_witness -from .simple_config import estimate_fee +from .simple_config import SimpleConfig from .logging import get_logger if TYPE_CHECKING: @@ -61,7 +61,8 @@ def create_sweeptxs_for_watchtower(chan: 'Channel', ctx: Transaction, per_commit output_idx=output_idx, witness_script=witness_script, privkey=other_revocation_privkey, - is_revocation=True) + is_revocation=True, + config=chan.lnworker.config) if sweep_tx: txs.append(sweep_tx) # HTLCs @@ -80,7 +81,8 @@ def create_sweeptxs_for_watchtower(chan: 'Channel', ctx: Transaction, per_commit htlctx_witness_script=htlc_tx_witness_script, sweep_address=sweep_address, privkey=other_revocation_privkey, - is_revocation=True) + is_revocation=True, + config=chan.lnworker.config) ctn = extract_ctn_from_tx_and_chan(ctx, chan) htlc_to_ctx_output_idx_map = map_htlcs_to_ctx_output_idxs(chan=chan, @@ -121,7 +123,8 @@ def create_sweeptx_for_their_revoked_ctx(chan: 'Channel', ctx: Transaction, per_ output_idx=output_idx, witness_script=witness_script, privkey=other_revocation_privkey, - is_revocation=True) + is_revocation=True, + config=chan.lnworker.config) return sweep_tx return None @@ -155,7 +158,8 @@ def create_sweeptx_for_their_revoked_htlc(chan: 'Channel', ctx: Transaction, htl output_idx=0, witness_script=witness_script, privkey=other_revocation_privkey, - is_revocation=True) + is_revocation=True, + config=chan.lnworker.config) return SweepInfo(name='redeem_htlc2', csv_delay=0, @@ -213,7 +217,8 @@ def create_sweeptxs_for_our_ctx(*, chan: 'Channel', ctx: Transaction, witness_script=to_local_witness_script, privkey=our_localdelayed_privkey.get_secret_bytes(), is_revocation=False, - to_self_delay=to_self_delay) + to_self_delay=to_self_delay, + config=chan.lnworker.config) prevout = ctx.txid() + ':%d'%output_idx txs[prevout] = SweepInfo(name='our_ctx_to_local', csv_delay=to_self_delay, @@ -246,7 +251,8 @@ def create_sweeptxs_for_our_ctx(*, chan: 'Channel', ctx: Transaction, htlctx_witness_script=htlctx_witness_script, sweep_address=sweep_address, privkey=our_localdelayed_privkey.get_secret_bytes(), - is_revocation=False) + is_revocation=False, + config=chan.lnworker.config) # side effect txs[htlc_tx.prevout(0)] = SweepInfo(name='first-stage-htlc', csv_delay=0, @@ -352,7 +358,8 @@ def create_sweeptxs_for_their_ctx(*, chan: 'Channel', ctx: Transaction, sweep_address=sweep_address, ctx=ctx, output_idx=output_idx, - our_payment_privkey=our_payment_privkey) + our_payment_privkey=our_payment_privkey, + config=chan.lnworker.config) txs[prevout] = SweepInfo(name='their_ctx_to_remote', csv_delay=0, cltv_expiry=0, @@ -386,7 +393,8 @@ def create_sweeptxs_for_their_ctx(*, chan: 'Channel', ctx: Transaction, output_idx=ctx_output_idx, privkey=our_revocation_privkey if is_revocation else our_htlc_privkey.get_secret_bytes(), is_revocation=is_revocation, - cltv_expiry=cltv_expiry) + cltv_expiry=cltv_expiry, + config=chan.lnworker.config) txs[prevout] = SweepInfo(name=f'their_ctx_htlc_{ctx_output_idx}', csv_delay=0, cltv_expiry=cltv_expiry, @@ -432,7 +440,7 @@ def create_htlctx_that_spends_from_our_ctx(chan: 'Channel', our_pcp: bytes, def create_sweeptx_their_ctx_htlc(ctx: Transaction, witness_script: bytes, sweep_address: str, preimage: Optional[bytes], output_idx: int, privkey: bytes, is_revocation: bool, - cltv_expiry: int) -> Optional[Transaction]: + cltv_expiry: int, config: SimpleConfig) -> Optional[Transaction]: assert type(cltv_expiry) is int preimage = preimage or b'' # preimage is required iff (not is_revocation and htlc is offered) val = ctx.outputs()[output_idx].value @@ -448,7 +456,7 @@ def create_sweeptx_their_ctx_htlc(ctx: Transaction, witness_script: bytes, sweep 'preimage_script': bh2u(witness_script), }] tx_size_bytes = 200 # TODO (depends on offered/received and is_revocation) - fee = estimate_fee(tx_size_bytes) + fee = config.estimate_fee(tx_size_bytes, allow_fallback_to_static_rates=True) outvalue = val - fee if outvalue <= dust_threshold(): return None sweep_outputs = [TxOutput(TYPE_ADDRESS, sweep_address, outvalue)] @@ -465,7 +473,8 @@ def create_sweeptx_their_ctx_htlc(ctx: Transaction, witness_script: bytes, sweep def create_sweeptx_their_ctx_to_remote(sweep_address: str, ctx: Transaction, output_idx: int, - our_payment_privkey: ecc.ECPrivkey) -> Optional[Transaction]: + our_payment_privkey: ecc.ECPrivkey, + config: SimpleConfig) -> Optional[Transaction]: our_payment_pubkey = our_payment_privkey.get_public_key_hex(compressed=True) val = ctx.outputs()[output_idx].value sweep_inputs = [{ @@ -479,7 +488,7 @@ def create_sweeptx_their_ctx_to_remote(sweep_address: str, ctx: Transaction, out 'signatures': [None], }] tx_size_bytes = 110 # approx size of p2wpkh->p2wpkh - fee = estimate_fee(tx_size_bytes) + fee = config.estimate_fee(tx_size_bytes, allow_fallback_to_static_rates=True) outvalue = val - fee if outvalue <= dust_threshold(): return None sweep_outputs = [TxOutput(TYPE_ADDRESS, sweep_address, outvalue)] @@ -491,8 +500,8 @@ def create_sweeptx_their_ctx_to_remote(sweep_address: str, ctx: Transaction, out return sweep_tx -def create_sweeptx_ctx_to_local(sweep_address: str, ctx: Transaction, output_idx: int, witness_script: str, - privkey: bytes, is_revocation: bool, +def create_sweeptx_ctx_to_local(*, sweep_address: str, ctx: Transaction, output_idx: int, witness_script: str, + privkey: bytes, is_revocation: bool, config: SimpleConfig, to_self_delay: int=None) -> Optional[Transaction]: """Create a txn that sweeps the 'to_local' output of a commitment transaction into our wallet. @@ -516,7 +525,7 @@ def create_sweeptx_ctx_to_local(sweep_address: str, ctx: Transaction, output_idx assert isinstance(to_self_delay, int) sweep_inputs[0]['sequence'] = to_self_delay tx_size_bytes = 121 # approx size of to_local -> p2wpkh - fee = estimate_fee(tx_size_bytes) + fee = config.estimate_fee(tx_size_bytes, allow_fallback_to_static_rates=True) outvalue = val - fee if outvalue <= dust_threshold(): return None @@ -530,7 +539,8 @@ def create_sweeptx_ctx_to_local(sweep_address: str, ctx: Transaction, output_idx def create_sweeptx_that_spends_htlctx_that_spends_htlc_in_ctx(*, htlc_tx: Transaction, htlctx_witness_script: bytes, sweep_address: str, - privkey: bytes, is_revocation: bool, to_self_delay: int) -> Optional[Transaction]: + privkey: bytes, is_revocation: bool, to_self_delay: int, + config: SimpleConfig) -> Optional[Transaction]: val = htlc_tx.outputs()[0].value sweep_inputs = [{ 'scriptSig': '', @@ -547,7 +557,7 @@ def create_sweeptx_that_spends_htlctx_that_spends_htlc_in_ctx(*, assert isinstance(to_self_delay, int) sweep_inputs[0]['sequence'] = to_self_delay tx_size_bytes = 200 # TODO - fee = estimate_fee(tx_size_bytes) + fee = config.estimate_fee(tx_size_bytes, allow_fallback_to_static_rates=True) outvalue = val - fee if outvalue <= dust_threshold(): return None sweep_outputs = [TxOutput(TYPE_ADDRESS, sweep_address, outvalue)] diff --git a/electrum/lnworker.py b/electrum/lnworker.py @@ -312,6 +312,7 @@ class LNWallet(LNWorker): Logger.__init__(self) self.wallet = wallet self.storage = wallet.storage + self.config = wallet.config xprv = self.storage.get('lightning_privkey2') if xprv is None: # TODO derive this deterministically from wallet.keystore at keystore generation time @@ -384,7 +385,6 @@ class LNWallet(LNWorker): await watchtower.add_sweep_tx(outpoint, ctn, tx.prevout(0), str(tx)) def start_network(self, network: 'Network'): - self.config = network.config self.lnwatcher = LNWatcher(network) self.lnwatcher.start_network(network) self.network = network diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py @@ -261,9 +261,9 @@ class Wallet_2fa(Multisig_Wallet): wallet_type = '2fa' - def __init__(self, storage): + def __init__(self, storage, *, config): self.m, self.n = 2, 3 - Deterministic_Wallet.__init__(self, storage) + Deterministic_Wallet.__init__(self, storage, config=config) self.is_billing = False self.billing_info = None self._load_billing_addresses() diff --git a/electrum/scripts/quick_start.py b/electrum/scripts/quick_start.py @@ -18,15 +18,15 @@ assert network.asyncio_loop.is_running() wallet_dir = os.path.dirname(config.get_wallet_path()) wallet_path = os.path.join(wallet_dir, "test_wallet") if not os.path.exists(wallet_path): - create_new_wallet(path=wallet_path, segwit=True) + create_new_wallet(path=wallet_path, config=config) # open wallet storage = WalletStorage(wallet_path) -wallet = Wallet(storage) +wallet = Wallet(storage, config=config) wallet.start_network(network) # you can use ~CLI commands by accessing command_runner -command_runner = Commands(config, wallet=None, network=network) +command_runner = Commands(config=config, daemon=daemon, network=network) command_runner.wallet = wallet print("balance", command_runner.getbalance()) print("addr", command_runner.getunusedaddress()) diff --git a/electrum/simple_config.py b/electrum/simple_config.py @@ -36,27 +36,9 @@ FEERATE_REGTEST_HARDCODED = 180000 # for eclair compat _logger = get_logger(__name__) -def estimate_fee(tx_size_bytes: int) -> int: - def use_fallback_feerate(): - fee_per_kb = FEERATE_FALLBACK_STATIC_FEE - fee = SimpleConfig.estimate_fee_for_feerate(fee_per_kb, tx_size_bytes) - return fee - - global _INSTANCE - if not _INSTANCE: - return use_fallback_feerate() - try: - return _INSTANCE.estimate_fee(tx_size_bytes) - except NoDynamicFeeEstimates: - return use_fallback_feerate() - FINAL_CONFIG_VERSION = 3 -_INSTANCE = None -_ENFORCE_SIMPLECONFIG_SINGLETON = True # disabled in tests - - class SimpleConfig(Logger): """ The SimpleConfig class is responsible for handling operations involving @@ -70,13 +52,6 @@ class SimpleConfig(Logger): def __init__(self, options=None, read_user_config_function=None, read_user_dir_function=None): - # note: To be honest, singletons are bad design... :/ - # However currently we somewhat rely on config being one. - global _INSTANCE - if _ENFORCE_SIMPLECONFIG_SINGLETON: - assert _INSTANCE is None, "SimpleConfig is a singleton!" - _INSTANCE = self - if options is None: options = {} @@ -122,10 +97,6 @@ class SimpleConfig(Logger): if self.requires_upgrade(): self.upgrade() - @staticmethod - def get_instance() -> Optional["SimpleConfig"]: - return _INSTANCE - def electrum_path(self): # Read electrum_path from command line # Otherwise use the user's default data directory. @@ -549,10 +520,14 @@ class SimpleConfig(Logger): fee_per_kb = self.fee_per_kb() return fee_per_kb / 1000 if fee_per_kb is not None else None - def estimate_fee(self, size: Union[int, float, Decimal]) -> int: + def estimate_fee(self, size: Union[int, float, Decimal], *, + allow_fallback_to_static_rates: bool = False) -> int: fee_per_kb = self.fee_per_kb() if fee_per_kb is None: - raise NoDynamicFeeEstimates() + if allow_fallback_to_static_rates: + fee_per_kb = FEERATE_FALLBACK_STATIC_FEE + else: + raise NoDynamicFeeEstimates() return self.estimate_fee_for_feerate(fee_per_kb, size) @classmethod diff --git a/electrum/sql_db.py b/electrum/sql_db.py @@ -54,4 +54,5 @@ class SqlDB(Logger): self.conn.commit() # write self.conn.commit() + self.conn.close() self.logger.info("SQL thread terminated") diff --git a/electrum/tests/__init__.py b/electrum/tests/__init__.py @@ -1,8 +1,9 @@ import unittest import threading +import tempfile +import shutil from electrum import constants -from electrum import simple_config # Set this locally to make the test suite run faster. @@ -12,10 +13,7 @@ from electrum import simple_config FAST_TESTS = False -simple_config._ENFORCE_SIMPLECONFIG_SINGLETON = False - - -# some unit tests are modifying globals; sorry. +# some unit tests are modifying globals... class SequentialTestCase(unittest.TestCase): test_lock = threading.Lock() @@ -29,7 +27,19 @@ class SequentialTestCase(unittest.TestCase): self.test_lock.release() -class TestCaseForTestnet(SequentialTestCase): +class ElectrumTestCase(SequentialTestCase): + """Base class for our unit tests.""" + + def setUp(self): + super().setUpClass() + self.electrum_path = tempfile.mkdtemp() + + def tearDown(self): + super().tearDownClass() + shutil.rmtree(self.electrum_path) + + +class TestCaseForTestnet(ElectrumTestCase): @classmethod def setUpClass(cls): diff --git a/electrum/tests/test_bitcoin.py b/electrum/tests/test_bitcoin.py @@ -22,7 +22,7 @@ from electrum.keystore import xtype_from_derivation from electrum import ecc_fast -from . import SequentialTestCase +from . import ElectrumTestCase from . import TestCaseForTestnet from . import FAST_TESTS @@ -84,7 +84,7 @@ def needs_test_with_all_aes_implementations(func): return run_test -class Test_bitcoin(SequentialTestCase): +class Test_bitcoin(ElectrumTestCase): def test_libsecp256k1_is_available(self): # we want the unit testing framework to test with libsecp256k1 available. @@ -393,7 +393,7 @@ class Test_bitcoin_testnet(TestCaseForTestnet): self.assertEqual(address_to_script('2NE4ZdmxFmUgwu5wtfoN2gVniyMgRDYq1kk'), 'a914e4567743d378957cd2ee7072da74b1203c1a7a0b87') -class Test_xprv_xpub(SequentialTestCase): +class Test_xprv_xpub(ElectrumTestCase): xprv_xpub = ( # Taken from test vectors in https://en.bitcoin.it/wiki/BIP_0032_TestVectors @@ -585,7 +585,7 @@ class Test_xprv_xpub_testnet(TestCaseForTestnet): self.assertTrue(xkey_b58.startswith(xpub_headers_b58[xtype])) -class Test_keyImport(SequentialTestCase): +class Test_keyImport(ElectrumTestCase): priv_pub_addr = ( {'priv': 'KzMFjMC2MPadjvX5Cd7b8AKKjjpBSoRKUTpoAtN6B3J9ezWYyXS6', @@ -751,7 +751,7 @@ class Test_keyImport(SequentialTestCase): raise_on_error=True) -class TestBaseEncode(SequentialTestCase): +class TestBaseEncode(ElectrumTestCase): def test_base43(self): tx_hex = "020000000001021cd0e96f9ca202e017ca3465e3c13373c0df3a4cdd91c1fd02ea42a1a65d2a410000000000fdffffff757da7cf8322e5063785e2d8ada74702d2648fa2add2d533ba83c52eb110df690200000000fdffffff02d07e010000000000160014b544c86eaf95e3bb3b6d2cabb12ab40fc59cad9ca086010000000000232102ce0d066fbfcf150a5a1bbc4f312cd2eb080e8d8a47e5f2ce1a63b23215e54fb5ac02483045022100a9856bf10a950810abceeabc9a86e6ba533e130686e3d7863971b9377e7c658a0220288a69ef2b958a7c2ecfa376841d4a13817ed24fa9a0e0a6b9cb48e6439794c701210324e291735f83ff8de47301b12034950b80fa4724926a34d67e413d8ff8817c53024830450221008f885978f7af746679200ed55fe2e86c1303620824721f95cc41eb7965a3dfcf02207872082ac4a3c433d41a203e6d685a459e70e551904904711626ac899238c20a0121023d4c9deae1aacf3f822dd97a28deaec7d4e4ff97be746d124a63d20e582f5b290a971600" diff --git a/electrum/tests/test_blockchain.py b/electrum/tests/test_blockchain.py @@ -7,10 +7,10 @@ from electrum.simple_config import SimpleConfig from electrum.blockchain import Blockchain, deserialize_header, hash_header from electrum.util import bh2u, bfh, make_dir -from . import SequentialTestCase +from . import ElectrumTestCase -class TestBlockchain(SequentialTestCase): +class TestBlockchain(ElectrumTestCase): HEADERS = { 'A': deserialize_header(bfh("0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f2002000000"), 0), @@ -57,15 +57,11 @@ class TestBlockchain(SequentialTestCase): def setUp(self): super().setUp() - self.data_dir = tempfile.mkdtemp() + self.data_dir = self.electrum_path make_dir(os.path.join(self.data_dir, 'forks')) self.config = SimpleConfig({'electrum_path': self.data_dir}) blockchain.blockchains = {} - def tearDown(self): - super().tearDown() - shutil.rmtree(self.data_dir) - def _append_header(self, chain: Blockchain, header: dict): self.assertTrue(chain.can_connect(header)) chain.save_header(header) @@ -341,7 +337,7 @@ class TestBlockchain(SequentialTestCase): self.assertTrue(all([b.can_connect(b.read_header(i), False) for i in range(b.height())])) -class TestVerifyHeader(SequentialTestCase): +class TestVerifyHeader(ElectrumTestCase): # Data for Bitcoin block header #100. valid_header = "0100000095194b8567fe2e8bbda931afd01a7acd399b9325cb54683e64129bcd00000000660802c98f18fd34fd16d61c63cf447568370124ac5f3be626c2e1c3c9f0052d19a76949ffff001d33f3c25d" diff --git a/electrum/tests/test_bolt11.py b/electrum/tests/test_bolt11.py @@ -1,17 +1,22 @@ from hashlib import sha256 -from electrum.lnaddr import shorten_amount, unshorten_amount, LnAddr, lnencode, lndecode, u5_to_bitarray, bitarray_to_u5 from decimal import Decimal from binascii import unhexlify, hexlify -from electrum.segwit_addr import bech32_encode, bech32_decode import pprint import unittest +from electrum.lnaddr import shorten_amount, unshorten_amount, LnAddr, lnencode, lndecode, u5_to_bitarray, bitarray_to_u5 +from electrum.segwit_addr import bech32_encode, bech32_decode + +from . import ElectrumTestCase + + RHASH=unhexlify('0001020304050607080900010203040506070809000102030405060708090102') CONVERSION_RATE=1200 PRIVKEY=unhexlify('e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734') PUBKEY=unhexlify('03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad') -class TestBolt11(unittest.TestCase): + +class TestBolt11(ElectrumTestCase): def test_shorten_amount(self): tests = { Decimal(10)/10**12: '10p', diff --git a/electrum/tests/test_commands.py b/electrum/tests/test_commands.py @@ -6,15 +6,17 @@ from electrum.util import create_and_start_event_loop from electrum.commands import Commands, eval_bool from electrum import storage from electrum.wallet import restore_wallet_from_text +from electrum.simple_config import SimpleConfig -from . import TestCaseForTestnet +from . import TestCaseForTestnet, ElectrumTestCase -class TestCommands(unittest.TestCase): +class TestCommands(ElectrumTestCase): def setUp(self): super().setUp() self.asyncio_loop, self._stop_loop, self._loop_thread = create_and_start_event_loop() + self.config = SimpleConfig({'electrum_path': self.electrum_path}) def tearDown(self): super().tearDown() @@ -56,7 +58,7 @@ class TestCommands(unittest.TestCase): self.assertTrue(eval_bool("1")) def test_convert_xkey(self): - cmds = Commands(config=None) + cmds = Commands(config=self.config) xpubs = { ("xpub6CCWFbvCbqF92kGwm9nV7t7RvVoQUKaq5USMdyVP6jvv1NgN52KAX6NNYCeE8Ca7JQC4K5tZcnQrubQcjJ6iixfPs4pwAQJAQgTt6hBjg11", "standard"), ("ypub6X2mZGb7kWnct3U4bWa7KyCw6TwrQwaKzaxaRNPGUkJo4UVbKgUj9A2WZQbp87E2i3Js4ZV85SmQnt2BSzWjXCLzjQXMkK7egQXXVHT4eKn", "p2wpkh-p2sh"), @@ -78,8 +80,9 @@ class TestCommands(unittest.TestCase): @mock.patch.object(storage.WalletStorage, '_write') def test_encrypt_decrypt(self, mock_write): wallet = restore_wallet_from_text('p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN', - path='if_this_exists_mocking_failed_648151893')['wallet'] - cmds = Commands(config=None) + path='if_this_exists_mocking_failed_648151893', + config=self.config)['wallet'] + cmds = Commands(config=self.config) cleartext = "asdasd this is the message" pubkey = "021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da" ciphertext = cmds._run('encrypt', (pubkey, cleartext)) @@ -88,8 +91,9 @@ class TestCommands(unittest.TestCase): @mock.patch.object(storage.WalletStorage, '_write') def test_export_private_key_imported(self, mock_write): wallet = restore_wallet_from_text('p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL', - path='if_this_exists_mocking_failed_648151893')['wallet'] - cmds = Commands(config=None) + path='if_this_exists_mocking_failed_648151893', + config=self.config)['wallet'] + cmds = Commands(config=self.config) # single address tests with self.assertRaises(Exception): cmds._run('getprivatekeys', ("asdasd",), wallet=wallet) # invalid addr, though might raise "not in wallet" @@ -107,8 +111,9 @@ class TestCommands(unittest.TestCase): def test_export_private_key_deterministic(self, mock_write): wallet = restore_wallet_from_text('bitter grass shiver impose acquire brush forget axis eager alone wine silver', gap_limit=2, - path='if_this_exists_mocking_failed_648151893')['wallet'] - cmds = Commands(config=None) + path='if_this_exists_mocking_failed_648151893', + config=self.config)['wallet'] + cmds = Commands(config=self.config) # single address tests with self.assertRaises(Exception): cmds._run('getprivatekeys', ("asdasd",), wallet=wallet) # invalid addr, though might raise "not in wallet" @@ -128,6 +133,7 @@ class TestCommandsTestnet(TestCaseForTestnet): def setUp(self): super().setUp() self.asyncio_loop, self._stop_loop, self._loop_thread = create_and_start_event_loop() + self.config = SimpleConfig({'electrum_path': self.electrum_path}) def tearDown(self): super().tearDown() @@ -135,7 +141,7 @@ class TestCommandsTestnet(TestCaseForTestnet): self._loop_thread.join(timeout=1) def test_convert_xkey(self): - cmds = Commands(config=None) + cmds = Commands(config=self.config) xpubs = { ("tpubD8p5qNfjczgTGbh9qgNxsbFgyhv8GgfVkmp3L88qtRm5ibUYiDVCrn6WYfnGey5XVVw6Bc5QNQUZW5B4jFQsHjmaenvkFUgWtKtgj5AdPm9", "standard"), ("upub59wfQ8qJTg6ZSuvwtR313Qdp8gP8TSBwTof5dPQ3QVsYp1N9t29Rr9TGF1pj8kAXUg3mKbmrTKasA2qmBJKb1bGUzB6ApDZpVC7LoHhyvBo", "p2wpkh-p2sh"), diff --git a/electrum/tests/test_dnssec.py b/electrum/tests/test_dnssec.py @@ -2,11 +2,11 @@ import dns from electrum import dnssec -from . import SequentialTestCase +from . import ElectrumTestCase from .test_bitcoin import needs_test_with_all_ecc_implementations -class TestDnsSec(SequentialTestCase): +class TestDnsSec(ElectrumTestCase): @needs_test_with_all_ecc_implementations def test_python_validate_rrsig_ecdsa(self): diff --git a/electrum/tests/test_lnchannel.py b/electrum/tests/test_lnchannel.py @@ -35,6 +35,9 @@ from electrum.lnutil import FeeUpdate from electrum.ecc import sig_string_from_der_sig from electrum.logging import console_stderr_handler +from . import ElectrumTestCase + + one_bitcoin_in_msat = bitcoin.COIN * 1000 def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator, local_amount, remote_amount, privkeys, other_pubkeys, seed, cur, nex, other_node_id, l_dust, r_dust, l_csv, r_csv): @@ -160,7 +163,7 @@ def create_test_channels(feerate=6000, local=None, remote=None): return alice, bob -class TestFee(unittest.TestCase): +class TestFee(ElectrumTestCase): """ test https://github.com/lightningnetwork/lightning-rfc/blob/e0c436bd7a3ed6a028e1cb472908224658a14eca/03-transactions.md#requirements-2 @@ -169,7 +172,7 @@ class TestFee(unittest.TestCase): alice_channel, bob_channel = create_test_channels(253, 10000000000, 5000000000) self.assertIn(9999817, [x[2] for x in alice_channel.get_latest_commitment(LOCAL).outputs()]) -class TestChannel(unittest.TestCase): +class TestChannel(ElectrumTestCase): maxDiff = 999 def assertOutputExistsByValue(self, tx, amt_sat): @@ -181,9 +184,11 @@ class TestChannel(unittest.TestCase): @classmethod def setUpClass(cls): + super().setUpClass() console_stderr_handler.setLevel(logging.DEBUG) def setUp(self): + super().setUp() # Create a test channel which will be used for the duration of this # unittest. The channel will be funded evenly with Alice having 5 BTC, # and Bob having 5 BTC. @@ -607,7 +612,7 @@ class TestChannel(unittest.TestCase): self.assertIn('Not enough local balance', cm.exception.args[0]) -class TestAvailableToSpend(unittest.TestCase): +class TestAvailableToSpend(ElectrumTestCase): def test_DesyncHTLCs(self): alice_channel, bob_channel = create_test_channels() @@ -645,7 +650,7 @@ class TestAvailableToSpend(unittest.TestCase): force_state_transition(alice_channel, bob_channel) alice_channel.add_htlc(htlc_dict) -class TestChanReserve(unittest.TestCase): +class TestChanReserve(ElectrumTestCase): def setUp(self): alice_channel, bob_channel = create_test_channels() alice_min_reserve = int(.5 * one_bitcoin_in_msat // 1000) @@ -778,7 +783,7 @@ class TestChanReserve(unittest.TestCase): self.assertEqual(self.alice_channel.available_to_spend(REMOTE), amt2) self.assertEqual(self.bob_channel.available_to_spend(LOCAL), amt2) -class TestDust(unittest.TestCase): +class TestDust(ElectrumTestCase): def test_DustLimit(self): alice_channel, bob_channel = create_test_channels() diff --git a/electrum/tests/test_lnhtlc.py b/electrum/tests/test_lnhtlc.py @@ -1,14 +1,18 @@ from pprint import pprint import unittest +from typing import NamedTuple + from electrum.lnutil import RECEIVED, LOCAL, REMOTE, SENT, HTLCOwner, Direction from electrum.lnhtlc import HTLCManager -from typing import NamedTuple + +from . import ElectrumTestCase + class H(NamedTuple): owner : str htlc_id : int -class TestHTLCManager(unittest.TestCase): +class TestHTLCManager(ElectrumTestCase): def test_adding_htlcs_race(self): A = HTLCManager() B = HTLCManager() diff --git a/electrum/tests/test_lnpeer.py b/electrum/tests/test_lnpeer.py @@ -24,7 +24,7 @@ from electrum.logging import console_stderr_handler from electrum.lnworker import InvoiceInfo, RECEIVED, PR_UNPAID from .test_lnchannel import create_test_channels -from . import SequentialTestCase +from . import ElectrumTestCase def keypair(): priv = ECPrivkey.generate_random_key().get_secret_bytes() @@ -173,7 +173,7 @@ def transport_pair(name1, name2): t2.other_mock_transport = t1 return t1, t2 -class TestPeer(SequentialTestCase): +class TestPeer(ElectrumTestCase): @classmethod def setUpClass(cls): diff --git a/electrum/tests/test_lnrouter.py b/electrum/tests/test_lnrouter.py @@ -29,25 +29,15 @@ class Test_LNRouter(TestCaseForTestnet): # assert witness_bytes == b"", witness_bytes # return res - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.electrum_path = tempfile.mkdtemp() - cls.config = SimpleConfig({'electrum_path': cls.electrum_path}) - def setUp(self): super().setUp() self.asyncio_loop, self._stop_loop, self._loop_thread = create_and_start_event_loop() - - @classmethod - def tearDownClass(cls): - super().tearDownClass() - shutil.rmtree(cls.electrum_path) + self.config = SimpleConfig({'electrum_path': self.electrum_path}) def tearDown(self): - super().tearDown() self.asyncio_loop.call_soon_threadsafe(self._stop_loop.set_result, 1) self._loop_thread.join(timeout=1) + super().tearDown() def test_find_path_for_payment(self): class fake_network: @@ -115,7 +105,10 @@ class Test_LNRouter(TestCaseForTestnet): self.assertEqual(route[0].node_id, start_node) self.assertEqual(route[0].short_channel_id, bfh('0000000000000003')) - + # need to duplicate tear_down here, as we also need to wait for the sql thread to stop + self.asyncio_loop.call_soon_threadsafe(self._stop_loop.set_result, 1) + self._loop_thread.join(timeout=1) + cdb.sql_thread.join(timeout=1) def test_new_onion_packet(self): # test vector from bolt-04 diff --git a/electrum/tests/test_lntransport.py b/electrum/tests/test_lntransport.py @@ -1,10 +1,14 @@ -from electrum.ecc import ECPrivkey import asyncio + +from electrum.ecc import ECPrivkey from electrum.lnutil import LNPeerAddr from electrum.lntransport import LNResponderTransport, LNTransport -from unittest import TestCase -class TestLNTransport(TestCase): +from . import ElectrumTestCase + + +class TestLNTransport(ElectrumTestCase): + def test_responder(self): # local static ls_priv=bytes.fromhex('2121212121212121212121212121212121212121212121212121212121212121') @@ -32,6 +36,7 @@ class TestLNTransport(TestCase): return bytes.fromhex('00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba') transport = LNResponderTransport(ls_priv, Reader(), Writer()) asyncio.get_event_loop().run_until_complete(transport.handshake(epriv=e_priv)) + def test_loop(self): l = asyncio.get_event_loop() responder_shaked = asyncio.Event() diff --git a/electrum/tests/test_lnutil.py b/electrum/tests/test_lnutil.py @@ -1,5 +1,6 @@ import unittest import json + from electrum import bitcoin from electrum.lnutil import (RevocationStore, get_per_commitment_secret_from_seed, make_offered_htlc, make_received_htlc, make_commitment, make_htlc_tx_witness, make_htlc_tx_output, @@ -10,6 +11,9 @@ from electrum.lnutil import (RevocationStore, get_per_commitment_secret_from_see from electrum.util import bh2u, bfh from electrum.transaction import Transaction +from . import ElectrumTestCase + + funding_tx_id = '8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be' funding_output_index = 0 funding_amount_satoshi = 10000000 @@ -30,7 +34,8 @@ local_delayedpubkey = bytes.fromhex('03fd5960528dc152014952efdb702a88f71e3c1653b local_revocation_pubkey = bytes.fromhex('0212a140cd0c6539d07cd08dfe09984dec3251ea808b892efeac3ede9402bf2b19') # funding wscript = 5221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae -class TestLNUtil(unittest.TestCase): + +class TestLNUtil(ElectrumTestCase): def test_shachain_store(self): tests = [ { diff --git a/electrum/tests/test_mnemonic.py b/electrum/tests/test_mnemonic.py @@ -7,7 +7,7 @@ from electrum.util import bh2u, bfh from electrum.mnemonic import is_new_seed, is_old_seed, seed_type from electrum.version import SEED_PREFIX_SW, SEED_PREFIX -from . import SequentialTestCase +from . import ElectrumTestCase from .test_wallet_vertical import UNICODE_HORROR, UNICODE_HORROR_HEX @@ -95,7 +95,7 @@ SEED_TEST_CASES = { } -class Test_NewMnemonic(SequentialTestCase): +class Test_NewMnemonic(ElectrumTestCase): def test_mnemonic_to_seed_basic(self): # note: not a valid electrum seed @@ -125,7 +125,7 @@ class Test_NewMnemonic(SequentialTestCase): self.assertEqual(m.mnemonic_encode(i), seed) -class Test_OldMnemonic(SequentialTestCase): +class Test_OldMnemonic(ElectrumTestCase): def test(self): seed = '8edad31a95e7d59f8837667510d75a4d' @@ -135,7 +135,7 @@ class Test_OldMnemonic(SequentialTestCase): self.assertEqual(old_mnemonic.mn_decode(result), seed) -class Test_BIP39Checksum(SequentialTestCase): +class Test_BIP39Checksum(ElectrumTestCase): def test(self): mnemonic = u'gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog' @@ -144,7 +144,7 @@ class Test_BIP39Checksum(SequentialTestCase): self.assertTrue(is_checksum_valid) -class Test_seeds(SequentialTestCase): +class Test_seeds(ElectrumTestCase): """ Test old and new seeds. """ mnemonics = { diff --git a/electrum/tests/test_network.py b/electrum/tests/test_network.py @@ -9,6 +9,8 @@ from electrum.interface import Interface from electrum.crypto import sha256 from electrum.util import bh2u +from . import ElectrumTestCase + class MockTaskGroup: async def spawn(self, x): return @@ -36,7 +38,7 @@ class MockInterface(Interface): assert assert_mode in item['mock'], (assert_mode, item) return item -class TestNetwork(unittest.TestCase): +class TestNetwork(ElectrumTestCase): @classmethod def setUpClass(cls): @@ -49,7 +51,8 @@ class TestNetwork(unittest.TestCase): constants.set_mainnet() def setUp(self): - self.config = SimpleConfig({'electrum_path': tempfile.mkdtemp(prefix="test_network")}) + super().setUp() + self.config = SimpleConfig({'electrum_path': self.electrum_path}) self.interface = MockInterface(self.config) def test_fork_noconflict(self): diff --git a/electrum/tests/test_revealer.py b/electrum/tests/test_revealer.py @@ -1,9 +1,9 @@ from electrum.plugins.revealer.revealer import RevealerPlugin -from . import SequentialTestCase +from . import ElectrumTestCase -class TestRevealer(SequentialTestCase): +class TestRevealer(ElectrumTestCase): def test_version_0_noisemap(self): versioned_seed = RevealerPlugin.get_versioned_seed_from_user_input('03b0c557d6d0d4308a3393851d78bd8c7861') diff --git a/electrum/tests/test_simple_config.py b/electrum/tests/test_simple_config.py @@ -7,10 +7,10 @@ import shutil from io import StringIO from electrum.simple_config import (SimpleConfig, read_user_config) -from . import SequentialTestCase +from . import ElectrumTestCase -class Test_SimpleConfig(SequentialTestCase): +class Test_SimpleConfig(ElectrumTestCase): def setUp(self): super(Test_SimpleConfig, self).setUp() @@ -147,7 +147,7 @@ class Test_SimpleConfig(SequentialTestCase): self.assertEqual(36495000, config.fee_to_depth(0.5)) -class TestUserConfig(SequentialTestCase): +class TestUserConfig(ElectrumTestCase): def setUp(self): super(TestUserConfig, self).setUp() diff --git a/electrum/tests/test_storage_upgrade.py b/electrum/tests/test_storage_upgrade.py @@ -278,8 +278,8 @@ class TestStorageUpgrade(WalletTestCase): from electrum.plugin import Plugins from electrum.simple_config import SimpleConfig - cls.electrum_path = tempfile.mkdtemp() - config = SimpleConfig({'electrum_path': cls.electrum_path}) + cls.__electrum_path = tempfile.mkdtemp() + config = SimpleConfig({'electrum_path': cls.__electrum_path}) gui_name = 'cmdline' # TODO it's probably wasteful to load all plugins... only need Trezor @@ -288,7 +288,7 @@ class TestStorageUpgrade(WalletTestCase): @classmethod def tearDownClass(cls): super().tearDownClass() - shutil.rmtree(cls.electrum_path) + shutil.rmtree(cls.__electrum_path) def _upgrade_storage(self, wallet_json, accounts=1): if accounts == 1: @@ -326,7 +326,7 @@ class TestStorageUpgrade(WalletTestCase): def _sanity_check_upgraded_storage(self, storage): self.assertFalse(storage.requires_split()) self.assertFalse(storage.requires_upgrade()) - w = Wallet(storage) + w = Wallet(storage, config=self.config) @staticmethod def _load_storage_from_json_string(*, wallet_json, path, manual_upgrades): diff --git a/electrum/tests/test_transaction.py b/electrum/tests/test_transaction.py @@ -4,7 +4,7 @@ from electrum.bitcoin import TYPE_ADDRESS from electrum.keystore import xpubkey_to_address from electrum.util import bh2u, bfh -from . import SequentialTestCase, TestCaseForTestnet +from . import ElectrumTestCase, TestCaseForTestnet from .test_bitcoin import needs_test_with_all_ecc_implementations unsigned_blob = '45505446ff0001000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65bf38633b424eb4031000000005701ff4c53ff0488b21e03ef2afea18000000089689bff23e1e7fb2f161daa37270a97a3d8c2e537584b2d304ecb47b86d21fc021b010d3bd425f8cf2e04824bfdf1f1f5ff1d51fadd9a41f9e3fb8dd3403b1bfe00000000ffffffff0140420f00000000001976a914230ac37834073a42146f11ef8414ae929feaafc388ac00000000' @@ -14,7 +14,7 @@ signed_segwit_blob = "01000000000101b66d722484f2db63e827ebf41d02684fed0c6550e850 signed_blob_signatures = ['3046022100a82bbc57a0136751e5433f41cf000b3f1a99c6744775e76ec764fb78c54ee100022100f9e80b7de89de861dc6fb0c1429d5da72c2b6b2ee2406bc9bfb1beedd729d98501', ] -class TestBCDataStream(SequentialTestCase): +class TestBCDataStream(ElectrumTestCase): def test_compact_size(self): s = transaction.BCDataStream() @@ -55,7 +55,7 @@ class TestBCDataStream(SequentialTestCase): self.assertEqual(s.read_bytes(4), b'r') self.assertEqual(s.read_bytes(1), b'') -class TestTransaction(SequentialTestCase): +class TestTransaction(ElectrumTestCase): @needs_test_with_all_ecc_implementations def test_tx_unsigned(self): diff --git a/electrum/tests/test_util.py b/electrum/tests/test_util.py @@ -3,10 +3,10 @@ from decimal import Decimal from electrum.util import (format_satoshis, format_fee_satoshis, parse_URI, is_hash256_str, chunks, is_ip_address, list_enabled_bits) -from . import SequentialTestCase +from . import ElectrumTestCase -class TestUtil(SequentialTestCase): +class TestUtil(ElectrumTestCase): def test_format_satoshis(self): self.assertEqual("0.00001234", format_satoshis(1234)) diff --git a/electrum/tests/test_wallet.py b/electrum/tests/test_wallet.py @@ -15,8 +15,9 @@ from electrum.exchange_rate import ExchangeBase, FxThread from electrum.util import TxMinedInfo from electrum.bitcoin import COIN from electrum.json_db import JsonDB +from electrum.simple_config import SimpleConfig -from . import SequentialTestCase +from . import ElectrumTestCase class FakeSynchronizer(object): @@ -28,11 +29,12 @@ class FakeSynchronizer(object): self.store.append(address) -class WalletTestCase(SequentialTestCase): +class WalletTestCase(ElectrumTestCase): def setUp(self): super(WalletTestCase, self).setUp() self.user_dir = tempfile.mkdtemp() + self.config = SimpleConfig({'electrum_path': self.user_dir}) self.wallet_path = os.path.join(self.user_dir, "somewallet") @@ -114,7 +116,7 @@ class FakeWallet: txid = 'abc' ccy = 'TEST' -class TestFiat(SequentialTestCase): +class TestFiat(ElectrumTestCase): def setUp(self): super().setUp() self.value_sat = COIN @@ -156,7 +158,8 @@ class TestCreateRestoreWallet(WalletTestCase): passphrase=passphrase, password=password, encrypt_file=encrypt_file, - gap_limit=1) + gap_limit=1, + config=self.config) wallet = d['wallet'] # type: Standard_Wallet wallet.check_password(password) self.assertEqual(passphrase, wallet.keystore.get_passphrase(password)) @@ -173,7 +176,8 @@ class TestCreateRestoreWallet(WalletTestCase): passphrase=passphrase, password=password, encrypt_file=encrypt_file, - gap_limit=1) + gap_limit=1, + config=self.config) wallet = d['wallet'] # type: Standard_Wallet self.assertEqual(passphrase, wallet.keystore.get_passphrase(password)) self.assertEqual(text, wallet.keystore.get_seed(password)) @@ -182,28 +186,28 @@ class TestCreateRestoreWallet(WalletTestCase): def test_restore_wallet_from_text_xpub(self): text = 'zpub6nydoME6CFdJtMpzHW5BNoPz6i6XbeT9qfz72wsRqGdgGEYeivso6xjfw8cGcCyHwF7BNW4LDuHF35XrZsovBLWMF4qXSjmhTXYiHbWqGLt' - d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1) + d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config) wallet = d['wallet'] # type: Standard_Wallet self.assertEqual(text, wallet.keystore.get_master_public_key()) self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', wallet.get_receiving_addresses()[0]) def test_restore_wallet_from_text_xkey_that_is_also_a_valid_electrum_seed_by_chance(self): text = 'yprvAJBpuoF4FKpK92ofzQ7ge6VJMtorow3maAGPvPGj38ggr2xd1xCrC9ojUVEf9jhW5L9SPu6fU2U3o64cLrRQ83zaQGNa6YP3ajZS6hHNPXj' - d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1) + d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config) wallet = d['wallet'] # type: Standard_Wallet self.assertEqual(text, wallet.keystore.get_master_private_key(password=None)) self.assertEqual('3Pa4hfP3LFWqa2nfphYaF7PZfdJYNusAnp', wallet.get_receiving_addresses()[0]) def test_restore_wallet_from_text_xprv(self): text = 'zprvAZzHPqhCMt51fskXBUYB1fTFYgG3CBjJUT4WEZTpGw6hPSDWBPZYZARC5sE9xAcX8NeWvvucFws8vZxEa65RosKAhy7r5MsmKTxr3hmNmea' - d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1) + d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config) wallet = d['wallet'] # type: Standard_Wallet self.assertEqual(text, wallet.keystore.get_master_private_key(password=None)) self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', wallet.get_receiving_addresses()[0]) def test_restore_wallet_from_text_addresses(self): text = 'bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw bc1qnp78h78vp92pwdwq5xvh8eprlga5q8gu66960c' - d = restore_wallet_from_text(text, path=self.wallet_path) + d = restore_wallet_from_text(text, path=self.wallet_path, config=self.config) wallet = d['wallet'] # type: Imported_Wallet self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', wallet.get_receiving_addresses()[0]) self.assertEqual(2, len(wallet.get_receiving_addresses())) @@ -213,7 +217,7 @@ class TestCreateRestoreWallet(WalletTestCase): def test_restore_wallet_from_text_privkeys(self): text = 'p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL p2wpkh:L24GxnN7NNUAfCXA6hFzB1jt59fYAAiFZMcLaJ2ZSawGpM3uqhb1' - d = restore_wallet_from_text(text, path=self.wallet_path) + d = restore_wallet_from_text(text, path=self.wallet_path, config=self.config) wallet = d['wallet'] # type: Imported_Wallet addr0 = wallet.get_receiving_addresses()[0] self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', addr0) diff --git a/electrum/tests/test_wallet_vertical.py b/electrum/tests/test_wallet_vertical.py @@ -17,7 +17,7 @@ from electrum.mnemonic import seed_type from electrum.plugins.trustedcoin import trustedcoin from . import TestCaseForTestnet -from . import SequentialTestCase +from . import ElectrumTestCase from .test_bitcoin import needs_test_with_all_ecc_implementations @@ -45,25 +45,26 @@ class WalletIntegrityHelper: test_obj.assertFalse(ks.has_seed()) @classmethod - def create_standard_wallet(cls, ks, gap_limit=None): + def create_standard_wallet(cls, ks, *, config: SimpleConfig, gap_limit=None): store = storage.WalletStorage('if_this_exists_mocking_failed_648151893') store.put('keystore', ks.dump()) store.put('gap_limit', gap_limit or cls.gap_limit) - w = Standard_Wallet(store) + w = Standard_Wallet(store, config=config) w.synchronize() return w @classmethod - def create_imported_wallet(cls, privkeys=False): + def create_imported_wallet(cls, *, config: SimpleConfig, privkeys: bool): store = storage.WalletStorage('if_this_exists_mocking_failed_648151893') if privkeys: k = keystore.Imported_KeyStore({}) store.put('keystore', k.dump()) - w = Imported_Wallet(store) + w = Imported_Wallet(store, config=config) return w @classmethod - def create_multisig_wallet(cls, keystores: Sequence, multisig_type: str, gap_limit=None): + def create_multisig_wallet(cls, keystores: Sequence, multisig_type: str, *, + config: SimpleConfig, gap_limit=None): """Creates a multisig wallet.""" store = storage.WalletStorage('if_this_exists_mocking_failed_648151893') for i, ks in enumerate(keystores): @@ -71,12 +72,16 @@ class WalletIntegrityHelper: store.put('x%d/' % cosigner_index, ks.dump()) store.put('wallet_type', multisig_type) store.put('gap_limit', gap_limit or cls.gap_limit) - w = Multisig_Wallet(store) + w = Multisig_Wallet(store, config=config) w.synchronize() return w -class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): +class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase): + + def setUp(self): + super().setUp() + self.config = SimpleConfig({'electrum_path': self.electrum_path}) @needs_test_with_all_ecc_implementations @mock.patch.object(storage.WalletStorage, '_write') @@ -92,7 +97,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): self.assertEqual(ks.xprv, 'xprv9s21ZrQH143K32jECVM729vWgGq4mUDJCk1ozqAStTphzQtCTuoFmFafNoG1g55iCnBTXUzz3zWnDb5CVLGiFvmaZjuazHDL8a81cPQ8KL6') self.assertEqual(ks.xpub, 'xpub661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52CwBdDWroaZf8U') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(w.txin_type, 'p2pkh') self.assertEqual(w.get_receiving_addresses()[0], '1NNkttn1YvVGdqBW4PR6zvc3Zx3H5owKRf') @@ -112,7 +117,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): self.assertEqual(ks.xprv, 'zprvAZswDvNeJeha8qZ8g7efN3FXYVJLaEUsE9TW6qXDEbVe74AZ75c2sZFZXPNFzxnhChDQ89oC8C5AjWwHmH1HeRKE1c4kKBQAmjUDdKDUZw2') self.assertEqual(ks.xpub, 'zpub6nsHdRuY92FsMKdbn9BfjBCG6X8pyhCibNP6uDvpnw2cyrVhecvHRMa3Ne8kdJZxjxgwnpbHLkcR4bfnhHy6auHPJyDTQ3kianeuVLdkCYQ') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(w.txin_type, 'p2wpkh') self.assertEqual(w.get_receiving_addresses()[0], 'bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af') @@ -132,7 +137,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): self.assertEqual(ks.xprv, 'zprvAZDmEQiCLUcZXPfrBXoksCD2R6RMAzAre7SUyBotibisy9c7vGhLYvHaP3d9rYU12DKAWdZfscPNA7qEPgTkCDqX5sE93ryAJAQvkDbfLxU') self.assertEqual(ks.xpub, 'zpub6nD7dvF6ArArjskKHZLmEL9ky8FqaSti1LN5maDWGwFrqwwGTp1b6ic4EHwciFNaYDmCXcQYxXSiF9BjcLCMPcaYkVN2nQD6QjYQ8vpSR3Z') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(w.txin_type, 'p2wpkh') self.assertEqual(w.get_receiving_addresses()[0], 'bc1qx94dutas7ysn2my645cyttujrms5d9p57f6aam') @@ -151,7 +156,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): self.assertEqual(ks.mpk, 'e9d4b7866dd1e91c862aebf62a49548c7dbf7bcc6e4b7b8c9da820c7737968df9c09d5a3e271dc814a29981f81b3faaf2737b551ef5dcc6189cf0f8252c442b3') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(w.txin_type, 'p2pkh') self.assertEqual(w.get_receiving_addresses()[0], '1FJEEB8ihPMbzs2SkLmr37dHyRFzakqUmo') @@ -186,7 +191,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks3) self.assertTrue(isinstance(ks3, keystore.BIP32_KeyStore)) - w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2, ks3], '2of3') + w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2, ks3], '2of3', config=self.config) self.assertEqual(w.txin_type, 'p2sh') self.assertEqual(w.get_receiving_addresses()[0], '35L8XmCDoEBKeaWRjvmZvoZvhp8BXMMMPV') @@ -221,7 +226,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks3) self.assertTrue(isinstance(ks3, keystore.BIP32_KeyStore)) - w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2, ks3], '2of3') + w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2, ks3], '2of3', config=self.config) self.assertEqual(w.txin_type, 'p2wsh') self.assertEqual(w.get_receiving_addresses()[0], 'bc1qpmufh0zjp5prfsrk2yskcy82sa26srqkd97j0457andc6m0gh5asw7kqd2') @@ -240,7 +245,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): self.assertEqual(ks.xprv, 'xprv9zGLcNEb3cHUKizLVBz6RYeE9bEZAVPjH2pD1DEzCnPcsemWc3d3xTao8sfhfUmDLMq6e3RcEMEvJG1Et8dvfL8DV4h7mwm9J6AJsW9WXQD') self.assertEqual(ks.xpub, 'xpub6DFh1smUsyqmYD4obDX6ngaxhd53Zx7aeFjoobebm7vbkT6f9awJWFuGzBT9FQJEWFBL7UyhMXtYzRcwDuVbcxtv9Ce2W9eMm4KXLdvdbjv') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(w.txin_type, 'p2pkh') self.assertEqual(w.get_receiving_addresses()[0], '16j7Dqk3Z9DdTdBtHcCVLaNQy9MTgywUUo') @@ -259,7 +264,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): self.assertEqual(ks.xprv, 'xprv9z8izheguGnLopSqkY7GcGFrP2Gu6rzBvvHo6uB9B8DWJhsows6WDZAsbBTaP3ncP2AVbTQphyEQkahrB9s1L7ihZtfz5WGQPMbXwsUtSik') self.assertEqual(ks.xpub, 'xpub6D85QDBajeLe2JXJrZeGyQCaw47PWKi3J9DPuHakjTkVBWCxVQQkmMVMSSfnw39tj9FntbozpRtb1AJ8ubjeVSBhyK4M5mzdvsXZzKPwodT') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(w.txin_type, 'p2pkh') self.assertEqual(w.get_receiving_addresses()[0], '1F88g2naBMhDB7pYFttPWGQgryba3hPevM') @@ -278,7 +283,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): self.assertEqual(ks.xprv, 'yprvAJEYHeNEPcyBoQYM7sGCxDiNCTX65u4ANgZuSGTrKN5YCC9MP84SBayrgaMyZV7zvkHrr3HVPTK853s2SPk4EttPazBZBmz6QfDkXeE8Zr7') self.assertEqual(ks.xpub, 'ypub6XDth9u8DzXV1tcpDtoDKMf6kVMaVMn1juVWEesTshcX4zUVvfNgjPJLXrD9N7AdTLnbHFL64KmBn3SNaTe69iZYbYCqLCCNPZKbLz9niQ4') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(w.txin_type, 'p2wpkh-p2sh') self.assertEqual(w.get_receiving_addresses()[0], '35ohQTdNykjkF1Mn9nAVEFjupyAtsPAK1W') @@ -298,7 +303,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): self.assertEqual(ks.xprv, 'zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE') self.assertEqual(ks.xpub, 'zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(w.txin_type, 'p2wpkh') self.assertEqual(w.get_receiving_addresses()[0], 'bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu') @@ -321,7 +326,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) - w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2], '2of2') + w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2], '2of2', config=self.config) self.assertEqual(w.txin_type, 'p2sh') self.assertEqual(w.get_receiving_addresses()[0], '32ji3QkAgXNz6oFoRfakyD3ys1XXiERQYN') @@ -344,7 +349,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) - w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2], '2of2') + w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2], '2of2', config=self.config) self.assertEqual(w.txin_type, 'p2wsh') self.assertEqual(w.get_receiving_addresses()[0], 'bc1qvzezdcv6vs5h45ugkavp896e0nde5c5lg5h0fwe2xyfhnpkxq6gq7pnwlc') @@ -367,7 +372,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) - w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2], '2of2') + w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2], '2of2', config=self.config) self.assertEqual(w.txin_type, 'p2sh') self.assertEqual(w.get_receiving_addresses()[0], '3JPTQ2nitVxXBJ1yhMeDwH6q417UifE3bN') @@ -389,7 +394,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) - w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2], '2of2') + w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2], '2of2', config=self.config) self.assertEqual(w.txin_type, 'p2wsh-p2sh') self.assertEqual(w.get_receiving_addresses()[0], '35LeC45QgCVeRor1tJD6LiDgPbybBXisns') @@ -414,42 +419,42 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): self.assertEqual('02bf27f41683d84183e4e930e66d64fc8af5508b4b5bf3c473c505e4dbddaeed80', ks.derive_pubkey(1, 0)) ks = create_keystore_from_bip32seed(xtype='standard') # p2pkh - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(ks.xprv, 'xprv9s21ZrQH143K3nyWMZVjzGL4KKAE1zahmhTHuV5pdw4eK3o3igC5QywgQG7UTRe6TGBniPDpPFWzXMeMUFbBj8uYsfXGjyMmF54wdNt8QBm') self.assertEqual(ks.xpub, 'xpub661MyMwAqRbcGH3yTb2kMQGnsLziRTJZ8vNthsVSCGbdBr8CGDWKxnGAFYgyKTzBtwvPPmfVAWJuFmxRXjSbUTg87wDkWQ5GmzpfUcN9t8Z') self.assertEqual(w.get_receiving_addresses()[0], '19fWEVaXqgJFFn7JYNr6ouxyjZy3uK7CdK') self.assertEqual(w.get_change_addresses()[0], '1EEX7da31qndYyeKdbM665w1ze5gbkkAZZ') ks = create_keystore_from_bip32seed(xtype='p2wpkh-p2sh') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(ks.xprv, 'yprvABrGsX5C9janu6AdBvHNCMRZVHJfxcaCgoyWgsyi1wSXN9cGyLMe33bpRU54TLJ1ruJbTrpNqusYQeFvBx1CXNb9k1DhKtBFWo8b1sLbXhN') self.assertEqual(ks.xpub, 'ypub6QqdH2c5z7967aF6HwpNZVNJ3K9AN5J442u7VGPKaGyWEwwRWsftaqvJGkeZKNe7Jb3C9FG3dAfT94ZzFRrcGhMizGvB6Jtm3itJsEFhxMC') self.assertEqual(w.get_receiving_addresses()[0], '34SAT5gGF5UaBhhSZ8qEuuxYvZ2cm7Zi23') self.assertEqual(w.get_change_addresses()[0], '38unULZaetSGSKvDx7Krukh8zm8NQnxGiA') ks = create_keystore_from_bip32seed(xtype='p2wpkh') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(ks.xprv, 'zprvAWgYBBk7JR8GkPMk2H4zQSX4fFT7uEZhbvVjUGsbPwpQRFRWDzXCf7FxSg2eTEwwGYRQDLQwJaE6HvsUueRDKcGkcLv7unzjnXCEQVWhrF9') self.assertEqual(ks.xpub, 'zpub6jftahH18ngZxsSD8JbzmaToDHHcJhHYy9RLGfHCxHMPJ3kemXqTCuaSHxc9KHJ2iE9ztirc5q212MBYy8Gd4w3KrccbgDiFKSwxFpYKEH6') self.assertEqual(w.get_receiving_addresses()[0], 'bc1qtuynwzd0d6wptvyqmc6ehkm70zcamxpshyzu5e') self.assertEqual(w.get_change_addresses()[0], 'bc1qjy5zunxh6hjysele86qqywfa437z4xwmleq8wk') ks = create_keystore_from_bip32seed(xtype='standard') # p2sh - w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1') + w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1', config=self.config) self.assertEqual(ks.xprv, 'xprv9s21ZrQH143K3nyWMZVjzGL4KKAE1zahmhTHuV5pdw4eK3o3igC5QywgQG7UTRe6TGBniPDpPFWzXMeMUFbBj8uYsfXGjyMmF54wdNt8QBm') self.assertEqual(ks.xpub, 'xpub661MyMwAqRbcGH3yTb2kMQGnsLziRTJZ8vNthsVSCGbdBr8CGDWKxnGAFYgyKTzBtwvPPmfVAWJuFmxRXjSbUTg87wDkWQ5GmzpfUcN9t8Z') self.assertEqual(w.get_receiving_addresses()[0], '3F4nm8Vunb7mxVvqhUP238PYge2hpU5qYv') self.assertEqual(w.get_change_addresses()[0], '3N8jvKGmxzVHENn6B4zTdZt3N9bmRKjj96') ks = create_keystore_from_bip32seed(xtype='p2wsh-p2sh') - w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1') + w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1', config=self.config) self.assertEqual(ks.xprv, 'YprvANkMzkodih9AKfL18akM2RmND5LwAyFo15dBc9FFPiGvzLBBjjjv8ATkEB2Y1mWv6NNaLSpVj8G3XosgVBA9frhpaUL6jHeFQXQTbqVPcv2') self.assertEqual(ks.xpub, 'Ypub6bjiQGLXZ4hTY9QUEcHMPZi6m7BRaRyeNJYnQXerx3ous8WLHH4AfxnE5Tc2sos1Y47B1qGAWP3xGEBkYf1ZRBUPpk2aViMkwTABT6qoiBb') self.assertEqual(w.get_receiving_addresses()[0], '3L1BxLLASGKE3DR1ruraWm3hZshGCKqcJx') self.assertEqual(w.get_change_addresses()[0], '3NDGcbZVXTpaQWRhiuVPpXsNt4g2JiCX4E') ks = create_keystore_from_bip32seed(xtype='p2wsh') - w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1') + w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1', config=self.config) self.assertEqual(ks.xprv, 'ZprvAhadJRUYsNgeAxX7xwXyEWrsP3VP7bFHvC9QPY98miep3RzQzPuUkE7tFNz81gAqW1VP5vR4BncbR6VFCsaAU6PRSp2XKCTjgFU6zRpk6Xp') self.assertEqual(ks.xpub, 'Zpub6vZyhw1ShkEwPSbb4y4ybeobw5KsX3y9HR51BvYkL4BnvEKZXwDjJ2SN6fZcsiWvwhDymJriy3QW9WoKGMRaDR9zh5j15dBFDBDpqjK1ekQ') self.assertEqual(w.get_receiving_addresses()[0], 'bc1q84x0yrztvcjg88qef4d6978zccxulcmc9y88xcg4ghjdau999x7q7zv2qe') @@ -458,6 +463,10 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): class TestWalletKeystoreAddressIntegrityForTestnet(TestCaseForTestnet): + def setUp(self): + super().setUp() + self.config = SimpleConfig({'electrum_path': self.electrum_path}) + @mock.patch.object(storage.WalletStorage, '_write') def test_bip39_multisig_seed_p2sh_segwit_testnet(self, mock_write): # bip39 seed: finish seminar arrange erosion sunny coil insane together pretty lunch lunch rose @@ -473,7 +482,7 @@ class TestWalletKeystoreAddressIntegrityForTestnet(TestCaseForTestnet): WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) - w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2], '2of2') + w = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2], '2of2', config=self.config) self.assertEqual(w.txin_type, 'p2wsh-p2sh') self.assertEqual(w.get_receiving_addresses()[0], '2MzsfTfTGomPRne6TkctMmoDj6LwmVkDrMt') @@ -498,42 +507,42 @@ class TestWalletKeystoreAddressIntegrityForTestnet(TestCaseForTestnet): self.assertEqual('02bf27f41683d84183e4e930e66d64fc8af5508b4b5bf3c473c505e4dbddaeed80', ks.derive_pubkey(1, 0)) ks = create_keystore_from_bip32seed(xtype='standard') # p2pkh - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(ks.xprv, 'tprv8ZgxMBicQKsPecD328MF9ux3dSaSFWci7FNQmuWH7uZ86eY8i3XpvjK8KSH8To2QphiZiUqaYc6nzDC6bTw8YCB9QJjaQL5pAApN4z7vh2B') self.assertEqual(ks.xpub, 'tpubD6NzVbkrYhZ4Y5Epun1qZKcACU6NQqocgYyC4RYaYBMWw8nuLSMR7DvzVamkqxwRgrTJ1MBMhc8wwxT2vbHqMu8RBXy4BvjWMxR5EdZroxE') self.assertEqual(w.get_receiving_addresses()[0], 'mpBTXYfWehjW2tavFwpUdqBJbZZkup13k2') self.assertEqual(w.get_change_addresses()[0], 'mtkUQgf1psDtL67wMAKTv19LrdgPWy6GDQ') ks = create_keystore_from_bip32seed(xtype='p2wpkh-p2sh') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(ks.xprv, 'uprv8tXDerPXZ1QsVuQ9rV8sN13YoQitC8cD2MtdZJQAVuw19kMMxhhPYnyGLeEiThgLELqNTxS91GTLsVofKAM9LRrkGeRzzEuJRtt1Tcostr7') self.assertEqual(ks.xpub, 'upub57Wa4MvRPNyAiPUcxWfsj8zHMSZNbbL4PapEMgon4FTz2YgWWF1e6bHkBvpDKk2Rg2Zy9LsonXFFbv7jNeCZ5kdKWv8UkfcoxpdjJrZuBX6') self.assertEqual(w.get_receiving_addresses()[0], '2MuzNWpcHrXyvPVKzEGT7Xrwp8uEnXXjWnK') self.assertEqual(w.get_change_addresses()[0], '2MzTzY5VcGLwce7YmdEwjXhgQD7LYEKLJTm') ks = create_keystore_from_bip32seed(xtype='p2wpkh') - w = WalletIntegrityHelper.create_standard_wallet(ks) + w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) self.assertEqual(ks.xprv, 'vprv9DMUxX4ShgxMMCbGgqvVa693yNsL8kbhwUQrLhJ3svJtCrAbDMrxArdQMrCJTcLFdyxBDS2hTvotknRE2rmA8fYM8z8Ra9inhcwerEsG6Ev') self.assertEqual(ks.xpub, 'vpub5SLqN2bLY4WeZgfjnsTVwE5nXQhpYDKZJhLT95hfSFqs5eVjkuBCiewtD8moKegM5fgmtpUNFBboVCjJ6LcZszJvPFpuLaSJEYhNhUAnrCS') self.assertEqual(w.get_receiving_addresses()[0], 'tb1qtuynwzd0d6wptvyqmc6ehkm70zcamxpsaze002') self.assertEqual(w.get_change_addresses()[0], 'tb1qjy5zunxh6hjysele86qqywfa437z4xwm4lm549') ks = create_keystore_from_bip32seed(xtype='standard') # p2sh - w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1') + w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1', config=self.config) self.assertEqual(ks.xprv, 'tprv8ZgxMBicQKsPecD328MF9ux3dSaSFWci7FNQmuWH7uZ86eY8i3XpvjK8KSH8To2QphiZiUqaYc6nzDC6bTw8YCB9QJjaQL5pAApN4z7vh2B') self.assertEqual(ks.xpub, 'tpubD6NzVbkrYhZ4Y5Epun1qZKcACU6NQqocgYyC4RYaYBMWw8nuLSMR7DvzVamkqxwRgrTJ1MBMhc8wwxT2vbHqMu8RBXy4BvjWMxR5EdZroxE') self.assertEqual(w.get_receiving_addresses()[0], '2N6czpsRwQ3d8AHZPNbztf5NotzEsaZmVQ8') self.assertEqual(w.get_change_addresses()[0], '2NDgwz4CoaSzdSAQdrCcLFWsJaVowCNgiPA') ks = create_keystore_from_bip32seed(xtype='p2wsh-p2sh') - w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1') + w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1', config=self.config) self.assertEqual(ks.xprv, 'Uprv95RJn67y7xyEvUZXo9brC5PMXCm9QVHoLdYJUZfhsgmQmvvGj75fduqC9MCC28uETouMLYSFtUqqzfRRcPW6UuyR77YQPeNJKd9t3XutF8b') self.assertEqual(ks.xpub, 'Upub5JQfBberxLXY8xdzuB8rZDL65Ebdox1ehrTuGx5KS2JPejFRGePvBi9fzdmgtBFKuVdx1vsvfjdkj5jVfsMWEEjzMPEtA55orYubtrCZmRr') self.assertEqual(w.get_receiving_addresses()[0], '2NBZQ25GC3ipaF13ZY3UT8i2xnDuS17pJqx') self.assertEqual(w.get_change_addresses()[0], '2NDmUgLVX8vKvcJ4FQ37GSUre6QtBzKkb6k') ks = create_keystore_from_bip32seed(xtype='p2wsh') - w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1') + w = WalletIntegrityHelper.create_multisig_wallet([ks], '1of1', config=self.config) self.assertEqual(ks.xprv, 'Vprv16YtLrHXxePM6noKqtFtMtmUgBE9bEpF3fPLmpvuPksssLostujtdHBwqhEeVuzESz22UY8hyPx9ed684SQpCmUKSVhpxPFbvVNY7qnviNR') self.assertEqual(ks.xpub, 'Vpub5dEvVGKn7251zFq7jXvUmJRbFCk5ka19cxz84LyCp2gGhq4eXJZUomop1qjGt5uFK8kkmQUV8PzJcNM4PZmX2URbDiwJjyuJ8GyFHRrEmmG') self.assertEqual(w.get_receiving_addresses()[0], 'tb1q84x0yrztvcjg88qef4d6978zccxulcmc9y88xcg4ghjdau999x7qf2696k') @@ -542,20 +551,13 @@ class TestWalletKeystoreAddressIntegrityForTestnet(TestCaseForTestnet): class TestWalletSending(TestCaseForTestnet): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.electrum_path = tempfile.mkdtemp() - cls.config = SimpleConfig({'electrum_path': cls.electrum_path}) - - @classmethod - def tearDownClass(cls): - super().tearDownClass() - shutil.rmtree(cls.electrum_path) + def setUp(self): + super().setUp() + self.config = SimpleConfig({'electrum_path': self.electrum_path}) def create_standard_wallet_from_seed(self, seed_words): ks = keystore.from_seed(seed_words, '', False) - return WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=2) + return WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=2, config=self.config) @needs_test_with_all_ecc_implementations @mock.patch.object(storage.WalletStorage, '_write') @@ -623,7 +625,8 @@ class TestWalletSending(TestCaseForTestnet): keystore.from_xpub('tpubD6NzVbkrYhZ4YTPEgwk4zzr8wyo7pXGmbbVUnfYNtx6SgAMF5q3LN3Kch58P9hxGNsTmP7Dn49nnrmpE6upoRb1Xojg12FGLuLHkVpVtS44'), keystore.from_xpub('tpubD6NzVbkrYhZ4XJzYkhsCbDCcZRmDAKSD7bXi9mdCni7acVt45fxbTVZyU6jRGh29ULKTjoapkfFsSJvQHitcVKbQgzgkkYsAmaovcro7Mhf') ], - '2of3', gap_limit=2 + '2of3', gap_limit=2, + config=self.config ) wallet1b = WalletIntegrityHelper.create_multisig_wallet( [ @@ -631,7 +634,8 @@ class TestWalletSending(TestCaseForTestnet): keystore.from_xpub('tpubD6NzVbkrYhZ4YTPEgwk4zzr8wyo7pXGmbbVUnfYNtx6SgAMF5q3LN3Kch58P9hxGNsTmP7Dn49nnrmpE6upoRb1Xojg12FGLuLHkVpVtS44'), keystore.from_xpub('tpubD6NzVbkrYhZ4YARFMEZPckrqJkw59GZD1PXtQnw14ukvWDofR7Z1HMeSCxfYEZVvg4VdZ8zGok5VxHwdrLqew5cMdQntWc5mT7mh1CSgrnX') ], - '2of3', gap_limit=2 + '2of3', gap_limit=2, + config=self.config ) # ^ third seed: ghost into match ivory badge robot record tackle radar elbow traffic loud wallet2 = self.create_standard_wallet_from_seed('powerful random nobody notice nothing important anyway look away hidden message over') @@ -699,7 +703,8 @@ class TestWalletSending(TestCaseForTestnet): keystore.from_xpub('Vpub5fcdcgEwTJmbmqAktuK8Kyq92fMf7sWkcP6oqAii2tG47dNbfkGEGUbfS9NuZaRywLkHE6EmUksrqo32ZL3ouLN1HTar6oRiHpDzKMAF1tf'), keystore.from_xpub('Vpub5fjkKyYnvSS4wBuakWTkNvZDaBM2vQ1MeXWq368VJHNr2eT8efqhpmZ6UUkb7s2dwCXv2Vuggjdhk4vZVyiAQTwUftvff73XcUGq2NQmWra') ], - '2of3', gap_limit=2 + '2of3', gap_limit=2, + config=self.config ) wallet1b = WalletIntegrityHelper.create_multisig_wallet( [ @@ -707,7 +712,8 @@ class TestWalletSending(TestCaseForTestnet): keystore.from_xpub('Vpub5fjkKyYnvSS4wBuakWTkNvZDaBM2vQ1MeXWq368VJHNr2eT8efqhpmZ6UUkb7s2dwCXv2Vuggjdhk4vZVyiAQTwUftvff73XcUGq2NQmWra'), keystore.from_xpub('Vpub5gSKXzxK7FeKQedu2q1z9oJWxqvX72AArW3HSWpEhc8othDH8xMDu28gr7gf17sp492BuJod8Tn7anjvJrKpETwqnQqX7CS8fcYyUtedEMk') ], - '2of3', gap_limit=2 + '2of3', gap_limit=2, + config=self.config ) # ^ third seed: hedgehog sunset update estate number jungle amount piano friend donate upper wool wallet2a = WalletIntegrityHelper.create_multisig_wallet( @@ -716,7 +722,8 @@ class TestWalletSending(TestCaseForTestnet): keystore.from_xprv('Uprv9CvELvByqm8k2dpecJVjgLMX1z5DufEjY4fBC5YvdGF5WjGCa7GVJJ2fYni1tyuF7Hw83E6W2ZBjAhaFLZv2ri3rEsubkCd5avg4EHKoDBN'), keystore.from_xpub('Upub5Qb8ik4Cnu8g97KLXKgVXHqY6tH8emQvqtBncjSKsyfTZuorPtTZgX7ovKKZHuuVGBVd1MTTBkWez1XXt2weN1sWBz6SfgRPQYEkNgz81QF') ], - '2of2', gap_limit=2 + '2of2', gap_limit=2, + config=self.config ) wallet2b = WalletIntegrityHelper.create_multisig_wallet( [ @@ -724,7 +731,8 @@ class TestWalletSending(TestCaseForTestnet): keystore.from_xprv('Uprv9BbnKEXJxXaNvdEsRJ9VA9toYrSeFJh5UfGBpM2iKe8Uh7UhrM9K8ioL53s8gvCoGfirHHaqpABDAE7VUNw8LNU1DMJKVoWyeNKu9XcDC19'), keystore.from_xpub('Upub5RuakRisg8h3F7u7iL2k3UJFa1uiK7xauHamzTxYBbn4PXbM7eajr6M9Q2VCr6cVGhfhqWQqxnABvtSATuVM1xzxk4nA189jJwzaMn1QX7V') ], - '2of2', gap_limit=2 + '2of2', gap_limit=2, + config=self.config ) # bootstrap wallet1 @@ -798,13 +806,15 @@ class TestWalletSending(TestCaseForTestnet): keystore.from_seed('phone guilt ancient scan defy gasp off rotate approve ill word exchange', '', True), keystore.from_xpub('tpubD6NzVbkrYhZ4YPZ3ntVjqSCxiUUv2jikrUBU73Q3iJ7Y8iR41oYf991L5fanv7ciHjbjokdK2bjYqg1BzEUDxucU9qM5WRdBiY738wmgLP4') ], - '1of2', gap_limit=2 + '1of2', gap_limit=2, + config=self.config ) # ^ second seed: kingdom now gift initial age right velvet exotic harbor enforce kingdom kick wallet2 = WalletIntegrityHelper.create_standard_wallet( # bip39: uniform tank success logic lesson awesome stove elegant regular desert drip device, der: m/49'/1'/0' keystore.from_xprv('uprv91HGbrNZTK4x8u22nbdYGzEuWPxjaHMREUi7CNhY64KsG5ZGnVM99uCa16EMSfrnaPTFxjbRdBZ2WiBkokoM8anzAy3Vpc52o88WPkitnxi'), - gap_limit=2 + gap_limit=2, + config=self.config ) # bootstrap wallet1 @@ -1306,27 +1316,22 @@ class TestWalletSending(TestCaseForTestnet): class TestWalletOfflineSigning(TestCaseForTestnet): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.electrum_path = tempfile.mkdtemp() - cls.config = SimpleConfig({'electrum_path': cls.electrum_path}) - - @classmethod - def tearDownClass(cls): - super().tearDownClass() - shutil.rmtree(cls.electrum_path) + def setUp(self): + super().setUp() + self.config = SimpleConfig({'electrum_path': self.electrum_path}) @needs_test_with_all_ecc_implementations @mock.patch.object(storage.WalletStorage, '_write') def test_sending_offline_old_electrum_seed_online_mpk(self, mock_write): wallet_offline = WalletIntegrityHelper.create_standard_wallet( keystore.from_seed('alone body father children lead goodbye phone twist exist grass kick join', '', False), - gap_limit=4 + gap_limit=4, + config=self.config ) wallet_online = WalletIntegrityHelper.create_standard_wallet( keystore.from_master_key('cd805ed20aec61c7a8b409c121c6ba60a9221f46d20edbc2be83ebd91460e97937cd7d782e77c1cb08364c6bc1c98bc040fdad53f22f29f7d3a85c8e51f9c875'), - gap_limit=4 + gap_limit=4, + config=self.config ) # bootstrap wallet_online @@ -1365,11 +1370,13 @@ class TestWalletOfflineSigning(TestCaseForTestnet): wallet_offline = WalletIntegrityHelper.create_standard_wallet( # bip39: "qwe", der: m/44'/1'/0' keystore.from_xprv('tprv8gfKwjuAaqtHgqxMh1tosAQ28XvBMkcY5NeFRA3pZMpz6MR4H4YZ3MJM4fvNPnRKeXR1Td2vQGgjorNXfo94WvT5CYDsPAqjHxSn436G1Eu'), - gap_limit=4 + gap_limit=4, + config=self.config ) wallet_online = WalletIntegrityHelper.create_standard_wallet( keystore.from_xpub('tpubDDMN69wQjDZxaJz9afZQGa48hZS7X5oSegF2hg67yddNvqfpuTN9DqvDEp7YyVf7AzXnqBqHdLhzTAStHvsoMDDb8WoJQzNrcHgDJHVYgQF'), - gap_limit=4 + gap_limit=4, + config=self.config ) # bootstrap wallet_online @@ -1406,11 +1413,13 @@ class TestWalletOfflineSigning(TestCaseForTestnet): wallet_offline = WalletIntegrityHelper.create_standard_wallet( # bip39: "qwe", der: m/49'/1'/0' keystore.from_xprv('uprv8zHHrMQMQ26utWwNJ5MK2SXpB9hbmy7pbPaneii69xT8cZTyFpxQFxkknGWKP8dxBTZhzy7yP6cCnLrRCQjzJDk3G61SjZpxhFQuB2NR8a5'), - gap_limit=4 + gap_limit=4, + config=self.config ) wallet_online = WalletIntegrityHelper.create_standard_wallet( keystore.from_xpub('upub5DGeFrwFEPfD711qQ6tKPaUYjBY6BRqfxcWPT77hiHz7VMo7oNGeom5EdXoKXEazePyoN3ueJMqHBfp3MwmsaD8k9dFHoa8KGeVXev7Pbg2'), - gap_limit=4 + gap_limit=4, + config=self.config ) # bootstrap wallet_online @@ -1448,11 +1457,13 @@ class TestWalletOfflineSigning(TestCaseForTestnet): wallet_offline = WalletIntegrityHelper.create_standard_wallet( # bip39: "qwe", der: m/84'/1'/0' keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'), - gap_limit=4 + gap_limit=4, + config=self.config ) wallet_online = WalletIntegrityHelper.create_standard_wallet( keystore.from_xpub('vpub5Y941QgusZGvuD5nXTpUvVWohm8q41uftcRNronjRWs9jB2iVr4BbxqbRfAoQjWHgJtDCQEXChgfsPbEuBnidtkFztZSD3zDKTrtwXa2LCa'), - gap_limit=4 + gap_limit=4, + config=self.config ) # bootstrap wallet_online @@ -1487,9 +1498,9 @@ class TestWalletOfflineSigning(TestCaseForTestnet): @needs_test_with_all_ecc_implementations @mock.patch.object(storage.WalletStorage, '_write') def test_sending_offline_wif_online_addr_p2pkh(self, mock_write): # compressed pubkey - wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True) + wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config) wallet_offline.import_private_key('p2pkh:cQDxbmQfwRV3vP1mdnVHq37nJekHLsuD3wdSQseBRA2ct4MFk5Pq', password=None) - wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False) + wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online.import_address('mg2jk6S5WGDhUPA8mLSxDLWpUoQnX1zzoG') # bootstrap wallet_online @@ -1522,9 +1533,9 @@ class TestWalletOfflineSigning(TestCaseForTestnet): @needs_test_with_all_ecc_implementations @mock.patch.object(storage.WalletStorage, '_write') def test_sending_offline_wif_online_addr_p2wpkh_p2sh(self, mock_write): - wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True) + wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config) wallet_offline.import_private_key('p2wpkh-p2sh:cU9hVzhpvfn91u2zTVn8uqF2ymS7ucYH8V5TmsTDmuyMHgRk9WsJ', password=None) - wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False) + wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online.import_address('2NA2JbUVK7HGWUCK5RXSVNHrkgUYF8d9zV8') # bootstrap wallet_online @@ -1557,9 +1568,9 @@ class TestWalletOfflineSigning(TestCaseForTestnet): @needs_test_with_all_ecc_implementations @mock.patch.object(storage.WalletStorage, '_write') def test_sending_offline_wif_online_addr_p2wpkh(self, mock_write): - wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True) + wallet_offline = WalletIntegrityHelper.create_imported_wallet(privkeys=True, config=self.config) wallet_offline.import_private_key('p2wpkh:cPuQzcNEgbeYZ5at9VdGkCwkPA9r34gvEVJjuoz384rTfYpahfe7', password=None) - wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False) + wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online.import_address('tb1qm2eh4787lwanrzr6pf0ekf5c7jnmghm2y9k529') # bootstrap wallet_online @@ -1595,9 +1606,10 @@ class TestWalletOfflineSigning(TestCaseForTestnet): wallet_offline = WalletIntegrityHelper.create_standard_wallet( # bip39: "qwe", der: m/44'/1'/0' keystore.from_xprv('tprv8gfKwjuAaqtHgqxMh1tosAQ28XvBMkcY5NeFRA3pZMpz6MR4H4YZ3MJM4fvNPnRKeXR1Td2vQGgjorNXfo94WvT5CYDsPAqjHxSn436G1Eu'), - gap_limit=4 + gap_limit=4, + config=self.config ) - wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False) + wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online.import_address('mg2jk6S5WGDhUPA8mLSxDLWpUoQnX1zzoG') # bootstrap wallet_online @@ -1633,9 +1645,10 @@ class TestWalletOfflineSigning(TestCaseForTestnet): wallet_offline = WalletIntegrityHelper.create_standard_wallet( # bip39: "qwe", der: m/49'/1'/0' keystore.from_xprv('uprv8zHHrMQMQ26utWwNJ5MK2SXpB9hbmy7pbPaneii69xT8cZTyFpxQFxkknGWKP8dxBTZhzy7yP6cCnLrRCQjzJDk3G61SjZpxhFQuB2NR8a5'), - gap_limit=4 + gap_limit=4, + config=self.config ) - wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False) + wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online.import_address('2NA2JbUVK7HGWUCK5RXSVNHrkgUYF8d9zV8') # bootstrap wallet_online @@ -1671,9 +1684,10 @@ class TestWalletOfflineSigning(TestCaseForTestnet): wallet_offline = WalletIntegrityHelper.create_standard_wallet( # bip39: "qwe", der: m/84'/1'/0' keystore.from_xprv('vprv9K9hbuA23Bidgj1KRSHUZMa59jJLeZBpXPVn4RP7sBLArNhZxJjw4AX7aQmVTErDt4YFC11ptMLjbwxgrsH8GLQ1cx77KggWeVPeDBjr9xM'), - gap_limit=4 + gap_limit=4, + config=self.config ) - wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False) + wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online.import_address('tb1qm2eh4787lwanrzr6pf0ekf5c7jnmghm2y9k529') # bootstrap wallet_online @@ -1713,7 +1727,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet): keystore.from_xpub('tpubD6NzVbkrYhZ4YTPEgwk4zzr8wyo7pXGmbbVUnfYNtx6SgAMF5q3LN3Kch58P9hxGNsTmP7Dn49nnrmpE6upoRb1Xojg12FGLuLHkVpVtS44'), keystore.from_xpub('tpubD6NzVbkrYhZ4XJzYkhsCbDCcZRmDAKSD7bXi9mdCni7acVt45fxbTVZyU6jRGh29ULKTjoapkfFsSJvQHitcVKbQgzgkkYsAmaovcro7Mhf') ], - '2of3', gap_limit=2 + '2of3', gap_limit=2, + config=self.config ) wallet_offline2 = WalletIntegrityHelper.create_multisig_wallet( [ @@ -1721,9 +1736,10 @@ class TestWalletOfflineSigning(TestCaseForTestnet): keystore.from_xpub('tpubD6NzVbkrYhZ4YTPEgwk4zzr8wyo7pXGmbbVUnfYNtx6SgAMF5q3LN3Kch58P9hxGNsTmP7Dn49nnrmpE6upoRb1Xojg12FGLuLHkVpVtS44'), keystore.from_xpub('tpubD6NzVbkrYhZ4YARFMEZPckrqJkw59GZD1PXtQnw14ukvWDofR7Z1HMeSCxfYEZVvg4VdZ8zGok5VxHwdrLqew5cMdQntWc5mT7mh1CSgrnX') ], - '2of3', gap_limit=2 + '2of3', gap_limit=2, + config=self.config ) - wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False) + wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online.import_address('2N4z38eTKcWTZnfugCCfRyXtXWMLnn8HDfw') # bootstrap wallet_online @@ -1771,7 +1787,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet): keystore.from_xprv('Uprv9CvELvByqm8k2dpecJVjgLMX1z5DufEjY4fBC5YvdGF5WjGCa7GVJJ2fYni1tyuF7Hw83E6W2ZBjAhaFLZv2ri3rEsubkCd5avg4EHKoDBN'), keystore.from_xpub('Upub5Qb8ik4Cnu8g97KLXKgVXHqY6tH8emQvqtBncjSKsyfTZuorPtTZgX7ovKKZHuuVGBVd1MTTBkWez1XXt2weN1sWBz6SfgRPQYEkNgz81QF') ], - '2of2', gap_limit=2 + '2of2', gap_limit=2, + config=self.config ) wallet_offline2 = WalletIntegrityHelper.create_multisig_wallet( [ @@ -1779,9 +1796,10 @@ class TestWalletOfflineSigning(TestCaseForTestnet): keystore.from_xprv('Uprv9BbnKEXJxXaNvdEsRJ9VA9toYrSeFJh5UfGBpM2iKe8Uh7UhrM9K8ioL53s8gvCoGfirHHaqpABDAE7VUNw8LNU1DMJKVoWyeNKu9XcDC19'), keystore.from_xpub('Upub5RuakRisg8h3F7u7iL2k3UJFa1uiK7xauHamzTxYBbn4PXbM7eajr6M9Q2VCr6cVGhfhqWQqxnABvtSATuVM1xzxk4nA189jJwzaMn1QX7V') ], - '2of2', gap_limit=2 + '2of2', gap_limit=2, + config=self.config ) - wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False) + wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online.import_address('2MsHQRm1pNi6VsmXYRxYMcCTdPu7Xa1RyFe') # bootstrap wallet_online @@ -1830,7 +1848,8 @@ class TestWalletOfflineSigning(TestCaseForTestnet): keystore.from_xpub('Vpub5fcdcgEwTJmbmqAktuK8Kyq92fMf7sWkcP6oqAii2tG47dNbfkGEGUbfS9NuZaRywLkHE6EmUksrqo32ZL3ouLN1HTar6oRiHpDzKMAF1tf'), keystore.from_xpub('Vpub5fjkKyYnvSS4wBuakWTkNvZDaBM2vQ1MeXWq368VJHNr2eT8efqhpmZ6UUkb7s2dwCXv2Vuggjdhk4vZVyiAQTwUftvff73XcUGq2NQmWra') ], - '2of3', gap_limit=2 + '2of3', gap_limit=2, + config=self.config ) wallet_offline2 = WalletIntegrityHelper.create_multisig_wallet( [ @@ -1838,10 +1857,11 @@ class TestWalletOfflineSigning(TestCaseForTestnet): keystore.from_xpub('Vpub5fjkKyYnvSS4wBuakWTkNvZDaBM2vQ1MeXWq368VJHNr2eT8efqhpmZ6UUkb7s2dwCXv2Vuggjdhk4vZVyiAQTwUftvff73XcUGq2NQmWra'), keystore.from_xpub('Vpub5gSKXzxK7FeKQedu2q1z9oJWxqvX72AArW3HSWpEhc8othDH8xMDu28gr7gf17sp492BuJod8Tn7anjvJrKpETwqnQqX7CS8fcYyUtedEMk') ], - '2of3', gap_limit=2 + '2of3', gap_limit=2, + config=self.config ) # ^ third seed: hedgehog sunset update estate number jungle amount piano friend donate upper wool - wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False) + wallet_online = WalletIntegrityHelper.create_imported_wallet(privkeys=False, config=self.config) wallet_online.import_address('tb1q83p6eqxkuvq4eumcha46crpzg4nj84s9p0hnynkxg8nhvfzqcc7q4erju6') # bootstrap wallet_online @@ -1905,11 +1925,14 @@ class TestWalletHistory_SimpleRandomOrder(TestCaseForTestnet): } txid_list = sorted(list(transactions)) - @classmethod - def create_old_wallet(cls): + def setUp(self): + super().setUp() + self.config = SimpleConfig({'electrum_path': self.electrum_path}) + + def create_old_wallet(self): ks = keystore.from_old_mpk('e9d4b7866dd1e91c862aebf62a49548c7dbf7bcc6e4b7b8c9da820c7737968df9c09d5a3e271dc814a29981f81b3faaf2737b551ef5dcc6189cf0f8252c442b3') # seed words: powerful random nobody notice nothing important anyway look away hidden message over - w = WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=20) + w = WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=20, config=self.config) # some txns are beyond gap limit: w.create_new_address(for_change=True) return w @@ -1949,28 +1972,17 @@ class TestWalletHistory_EvilGapLimit(TestCaseForTestnet): "268fce617aaaa4847835c2212b984d7b7741fdab65de22813288341819bc5656": "010000000001014f1bdc64da8056d08f79db7f5348d1de55946e57aa7c8279499c703889b6e0fd0100000000fdffffff0260e316000000000016001445e9879cf7cd5b4a15df7ddcaf5c6dca0e1508bacc242600000000001600141bc12094a4475dcfbf24f9920dafddf9104ca95b02483045022100ae3618912f341fefee11b67e0047c47c88c4fa031561c3fafe993259dd14d846022056fa0a5b5d8a65942fa68bcc2f848fd71fa455ba42bc2d421b67eb49ba62aa4e01210394d8f4f06c2ea9c569eb050c897737a7315e7f2104d9b536b49968cc89a1f11033181400", } - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.electrum_path = tempfile.mkdtemp() - cls.config = SimpleConfig({ - 'electrum_path': cls.electrum_path, + def setUp(self): + super().setUp() + self.config = SimpleConfig({ + 'electrum_path': self.electrum_path, 'skipmerklecheck': True, # needed for Synchronizer to generate new addresses without SPV }) - @classmethod - def tearDownClass(cls): - super().tearDownClass() - shutil.rmtree(cls.electrum_path) - # horrible hack. create a new config to ensure custom settings - # don't get persisted in the "singleton" config: - SimpleConfig({'electrum_path': cls.electrum_path}) - - @classmethod - def create_wallet(cls): + def create_wallet(self): ks = keystore.from_xpub('vpub5Vhmk4dEJKanDTTw6immKXa3thw45u3gbd1rPYjREB6viP13sVTWcH6kvbR2YeLtGjradr6SFLVt9PxWDBSrvw1Dc1nmd3oko3m24CQbfaJ') # seed words: nephew work weather maze pyramid employ check permit garment scene kiwi smooth - w = WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=20) + w = WalletIntegrityHelper.create_standard_wallet(ks, gap_limit=20, config=self.config) return w @mock.patch.object(storage.WalletStorage, '_write') @@ -2017,11 +2029,16 @@ class TestWalletHistory_DoubleSpend(TestCaseForTestnet): "2c9aa33d9c8ec649f9bfb84af027a5414b760be5231fe9eca4a95b9eb3f8a017": "020000000001012516fade5b5938336a11815d02787ba1580b3189432aa11b150527f8409084a30100000000fdffffff01d2410f00000000001600147880a7c79744b908a5f6d6235f2eb46c174c84f002483045022100974d27c872f09115e57c6acb674cd4da6d0b26656ad967ddb2678ff409714b9502206d91b49cf778ced6ca9e40b4094fb57b86c86fac09ce46ce53aea4afa68ff311012102254b5b20ed21c3bba75ec2a9ff230257d13a2493f6b7da066d8195dcdd484310788d1700", } + def setUp(self): + super().setUp() + self.config = SimpleConfig({'electrum_path': self.electrum_path}) + @mock.patch.object(storage.WalletStorage, '_write') def test_restoring_wallet_without_manual_delete(self, mock_write): w = restore_wallet_from_text("small rapid pattern language comic denial donate extend tide fever burden barrel", path='if_this_exists_mocking_failed_648151893', - gap_limit=5)['wallet'] + gap_limit=5, + config=self.config)['wallet'] for txid in self.transactions: tx = Transaction(self.transactions[txid]) w.add_transaction(tx.txid(), tx) @@ -2034,7 +2051,8 @@ class TestWalletHistory_DoubleSpend(TestCaseForTestnet): def test_restoring_wallet_with_manual_delete(self, mock_write): w = restore_wallet_from_text("small rapid pattern language comic denial donate extend tide fever burden barrel", path='if_this_exists_mocking_failed_648151893', - gap_limit=5)['wallet'] + gap_limit=5, + config=self.config)['wallet'] # txn A is an external incoming txn funding the wallet txA = Transaction(self.transactions["a3849040f82705151ba12a4389310b58a17b78025d81116a3338595bdefa1625"]) w.add_transaction(txA.txid(), txA) diff --git a/electrum/tests/test_x509.py b/electrum/tests/test_x509.py @@ -1,6 +1,11 @@ import unittest + from electrum.x509 import X509 -class TestX509(unittest.TestCase): + +from . import ElectrumTestCase + + +class TestX509(ElectrumTestCase): def test_generalizedtime(self): full = X509(b'0\x82\x05F0\x82\x03.\x02\t\x00\xfeV\xd6\xb5?\xb1j\xe40\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000d1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x130\x11\x06\x03U\x04\x08\x0c\nCalifornia1!0\x1f\x06\x03U\x04\n\x0c\x18Internet Widgits Pty Ltd1\x1d0\x1b\x06\x03U\x04\x03\x0c\x14testnet.qtornado.com0 \x17\r180206010225Z\x18\x0f21180113010225Z0d1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x130\x11\x06\x03U\x04\x08\x0c\nCalifornia1!0\x1f\x06\x03U\x04\n\x0c\x18Internet Widgits Pty Ltd1\x1d0\x1b\x06\x03U\x04\x03\x0c\x14testnet.qtornado.com0\x82\x02"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x02\x0f\x000\x82\x02\n\x02\x82\x02\x01\x00\xc2B\xe0\xa8\xd9$M\xbc)Wx\x0cv\x00\xc0\xfa2Ew:\xce\xa7\xcb\xc8\r?\xea\xc5R(\xc7\xc3Y\xe7zq=\xcd\x8d\xe3\x86\x9ecSI\xc7\x84\xf2~\x91\xd4\x19\xc2;\x97\xe81e\xf2\xeb\xf1\xadw\xa3p\x88A*-\r\xb6Yt\x98R\xe8\x8a\xf9\xb5>"F\xac\x19%\xc8~\x1d\xac\x93A\xffk\xce\xdb\xfc9\x05\xa0\xad\xf9V\x0f0\xa2b\xd0@\xe4\xf1\xb1\xe8\xb1\x10[&\xa1\xff\x13\xcfQ\xb7\x805\xef\xe7tL\xe5|\x08W\x8c\xd72\x9d\'\xeb\x92)3N\x01M\x06\xa9\xdc\xe4\'\x13\x90x\xd8\x830\x97\xa8\xcc2d \xfa\x91\x04\xd0\x1b\xe7\xaa t\x87\xba]\xb5w\x05(\xba\x07\xc2X$~?L\xc5\x03\xb2\xdeQ\xf3\xf3\xdab\xd9\x92\xd9\x86^:\x93\xc9\x86~\xd1\x94\xd4\x80\x9c\xff0\xc6m\xf4\xf0\xd6\x18\x96l\x1d\x0c\xe8\x15 \x8c\x89\xcb\xa4*\xd9\xefg\x844\x81\xb3\xce\xa1\x8a|\xf9h\xc3\xe1!\xfeZ`\xb71\x97Kj\x0b"\xd3\x98T\r\xd9\xbb<r\x0c\xd5Q\xd0L\x02\xcb\x19\x19\xd6\xdf$\xcej\xa8l\xbd\x81\x803\x95\x0e\x907&\x81J\x88\xaf\xa23\xb4q\x96\x08\xa9]}\xb8Rs\x89{\x04\x88/\xc1m\x8c\xe8\\X\x95 \x1cj\xf2(t\xd7\xef\x10-r\xb6\x17L\xce_\x1bf\xc0c\x18\x83\x99\xdf\xd5\xad\x88\xcd \xae\x07 \xed\xb6\xfc[\x9a/f\x92\xce^\x9c\xd9\x064\xb4\xcc\x1d,d\x99\xee\x9a4\xbe\xde0\x92\x8f/keq\x94\x9frf1\xda\xadM_\x11C\x19\x01\xf0\xe0I\x84W\xf9\xaa\xd3\x12ex\x89"\xbfQ\x1f\xbdU\xa0\x92\xa3\x9d\xdb?\x86\x82\x0b\x1e\xe0\x8aSq\xce%\xea4\xfb\x82\x92\x0f\xcf\xaa\xe2\r\xedd\xba\xff\x85\xa2+\xb0x9\xba\'\xd3\xf5\xd6\xfa\xb43\x0b\xd4\xf4\xca\xa5\xb1\xe4[\xe7\xf7\xc3\xd3\xdd\x85)\xac5E\x17\xae\x03fCC(\x06\x1cU\xedM\x90r\xe87\x8d}\xf1i\xfdO\x83\x05\x83\x83y\xd9f,\xe1\xba\xf0\\y\x8d\x08`\xb1\x02\x03\x01\x00\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x02\x01\x00,.\x12jC3\x9fdF\x15\x16\xea*1\x0b[\xfa-\xcf\x80\x17\xf0\xfa\xf4\x96C\xff\xf9\xe9\xa2N\xda\xf1&6\x9ecV~\xea[\x07\xc1R\x03\x95\xd4\x84B\xe2r\x92\xad<mp\xf1\xcb\xb3\x8b\xbf \x08\x12\x1e6\xe3\xad\xbd1\x81\xbe\xaex\x002\xb6\xf9\xa0\xf6\xb7E^"\r\xa0w\x08\x14\xe7\x84\x03q2\x9c\xac\xce>\xc6\x0b\x81\x81k\x0e\xd01\x16\x91\xe4A\x8c\x1a\xe9W\xd4=<\xd4m_\xd4m\xa4H\x14\xc0\xae\x12\xab\x808\xf1\xf9_\xbb\xfb\xd0U\x0e\\\xd3.?\xa36\xe1hstU"\x17P\xcb>\x83\x9c\xaa\x9b\xb7\xe5\xb4\xb5W\xdc\xc1\xee\x91K\x12\xc2\xe1U\xaf\xf7I`\x83\x91\x0c\xc0\xcb\x15\x13!V\xa9\xc1\xca\x1b\x80\xff\xd8\x1f\xd8_+\x83\xcd\xcb%\xd6\xb7\xdc\x8a2\xa8Q\x1f\xbb.\xdf\x05\xb7hD\xab\xea\xe9\xfb.\xdd\x93\xd1\xf0\xb8r\xb9t.\xab\xf6]\xac\xc9U9\x87\x9e\xe36 \x87\xe7eo\x98\xac\xf4\x87\x8e\xf4\xa86\xd3\xcapy\xee\xa0]\xdbA\xb9\x00\xe9_R\xc8\xf7\xca\x13\xc6\xb1Z|c\xe8v\xa24\xac?k\xf1\xc4\x97\x18\x07\xbaU\xc9\xf5? \x95\x8f\x11\xa7\xc9\x8eY\x9c\xdfnx?\x88\xba\x90\xef\x94WU\xb5\xcf\x0b"\xe8\xfe\xa6.\x0cr-\xaf3\x8a\xe6v\xf9\xb91\x87\x91\xc6\xb1\xe9\xb9UP\xf5\x14\xb7\x99\x80\xc0\xc5}\x9a~\x7f\x06\x1e\xb8\x05\xd5\xa2LXO\\73i\x82\xcd\xc6#\xb7\xa4q\xd7\xd4y\xb1d\xaf\xa8\t\x9e1K\xd94\xaf7\x08\x8c);\xd2\xed\x91\xc6\xed\x83\x90\r\xef\x85\xf0\xfeJi\x02;\xf0\x0b\x03\xe7\xc1\x84\xd45\xaeP\xc2Lp\x1akb\xcaP\xe9\xfc\xc1\xc8VPQu\x85\x92l\x12\xb99{\x91\xd0\xa6d\n\xde\xf85\x93e\xfa\\\xf9cKx8\x84"s\xb8\xe52~\x97\x05\xc3\xf6\x1c\xca\x0b\xda\x8b\x90\xfeu5,\x94,\x99\xf9\x9a\xf3T\x8dAZ\xc7\xe9\x95-\x98\xf2\xbaL\x89\xc0?\xba1\xb5\\t|RY_\xc6\xabr\xe8') full.check_date() diff --git a/electrum/wallet.py b/electrum/wallet.py @@ -208,10 +208,12 @@ class Abstract_Wallet(AddressSynchronizer): max_change_outputs = 3 gap_limit_for_change = 6 - def __init__(self, storage: WalletStorage): + def __init__(self, storage: WalletStorage, *, config: SimpleConfig): if not storage.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.storage = storage # load addresses needs to be called before constructor for sanity checks self.storage.db.load_addresses(self.wallet_type) @@ -242,9 +244,6 @@ class Abstract_Wallet(AddressSynchronizer): self.contacts = Contacts(self.storage) self._coin_price_cache = {} - # TODO config should be passed as a param instead? SimpleConfig should not be a singleton. - self.config = SimpleConfig.get_instance() - assert self.config is not None, "config must not be None" self.lnworker = LNWallet(self) if self.config.get('lightning') else None def stop_threads(self): @@ -1643,8 +1642,8 @@ class Imported_Wallet(Simple_Wallet): wallet_type = 'imported' txin_type = 'address' - def __init__(self, storage): - Abstract_Wallet.__init__(self, storage) + def __init__(self, storage, *, config): + Abstract_Wallet.__init__(self, storage, config=config) def is_watching_only(self): return self.keystore is None @@ -1831,8 +1830,8 @@ class Imported_Wallet(Simple_Wallet): class Deterministic_Wallet(Abstract_Wallet): - def __init__(self, storage): - Abstract_Wallet.__init__(self, storage) + def __init__(self, storage, *, config): + Abstract_Wallet.__init__(self, storage, config=config) self.gap_limit = storage.get('gap_limit', 20) # generate addresses now. note that without libsecp this might block # for a few seconds! @@ -1982,8 +1981,8 @@ class Simple_Deterministic_Wallet(Simple_Wallet, Deterministic_Wallet): """ Deterministic Wallet with a single pubkey per address """ - def __init__(self, storage): - Deterministic_Wallet.__init__(self, storage) + def __init__(self, storage, *, config): + Deterministic_Wallet.__init__(self, storage, config=config) def get_public_key(self, address): sequence = self.get_address_index(address) @@ -2030,10 +2029,10 @@ class Multisig_Wallet(Deterministic_Wallet): # generic m of n gap_limit = 20 - def __init__(self, storage): + def __init__(self, storage, *, config): self.wallet_type = storage.get('wallet_type') self.m, self.n = multisig_type(self.wallet_type) - Deterministic_Wallet.__init__(self, storage) + Deterministic_Wallet.__init__(self, storage, config=config) def get_pubkeys(self, c, i): return self.derive_pubkeys(c, i) @@ -2149,10 +2148,10 @@ class Wallet(object): This class is actually a factory that will return a wallet of the correct type when passed a WalletStorage instance.""" - def __new__(self, storage): + def __new__(self, storage: WalletStorage, *, config: SimpleConfig): wallet_type = storage.get('wallet_type') WalletClass = Wallet.wallet_class(wallet_type) - wallet = WalletClass(storage) + wallet = WalletClass(storage, config=config) return wallet @staticmethod @@ -2164,7 +2163,7 @@ class Wallet(object): raise WalletFileException("Unknown wallet type: " + str(wallet_type)) -def create_new_wallet(*, path, passphrase=None, password=None, +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) @@ -2177,7 +2176,7 @@ def create_new_wallet(*, path, passphrase=None, password=None, storage.put('wallet_type', 'standard') if gap_limit is not None: storage.put('gap_limit', gap_limit) - wallet = Wallet(storage) + wallet = Wallet(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." @@ -2186,7 +2185,7 @@ def create_new_wallet(*, path, passphrase=None, password=None, return {'seed': seed, 'wallet': wallet, 'msg': msg} -def restore_wallet_from_text(text, *, path, +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 @@ -2198,7 +2197,7 @@ def restore_wallet_from_text(text, *, path, text = text.strip() if keystore.is_address_list(text): - wallet = Imported_Wallet(storage) + wallet = Imported_Wallet(storage, config=config) addresses = text.split() good_inputs, bad_inputs = wallet.import_addresses(addresses, write_to_disk=False) # FIXME tell user about bad_inputs @@ -2207,7 +2206,7 @@ def restore_wallet_from_text(text, *, path, elif keystore.is_private_key_list(text, allow_spaces_inside_key=False): k = keystore.Imported_KeyStore({}) storage.put('keystore', k.dump()) - wallet = Imported_Wallet(storage) + wallet = Imported_Wallet(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 @@ -2224,7 +2223,7 @@ def restore_wallet_from_text(text, *, path, storage.put('wallet_type', 'standard') if gap_limit is not None: storage.put('gap_limit', gap_limit) - wallet = Wallet(storage) + wallet = Wallet(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) diff --git a/run_electrum b/run_electrum @@ -211,7 +211,7 @@ async def run_offline_command(config, config_options, plugins): password = get_password_for_hw_device_encrypted_storage(plugins) config_options['password'] = password storage.decrypt(password) - wallet = Wallet(storage) + wallet = Wallet(storage, config=config) config_options['wallet'] = wallet else: wallet = None