Browse Source

Merge pull request #5652 from SomberNight/20190922_config_no_longer_singleton

config: no longer singleton. it is passed to Wallet.__init__
dependabot/pip/contrib/deterministic-build/ecdsa-0.13.3
ThomasV 5 years ago
committed by GitHub
parent
commit
f3eeb8817e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      electrum/commands.py
  2. 2
      electrum/daemon.py
  3. 2
      electrum/gui/kivy/main_window.py
  4. 2
      electrum/gui/qt/__init__.py
  5. 52
      electrum/gui/qt/qrcodewidget.py
  6. 9
      electrum/gui/qt/qrtextedit.py
  7. 2
      electrum/gui/stdio.py
  8. 2
      electrum/gui/text.py
  9. 7
      electrum/lnchannel.py
  10. 46
      electrum/lnsweep.py
  11. 2
      electrum/lnworker.py
  12. 4
      electrum/plugins/trustedcoin/trustedcoin.py
  13. 6
      electrum/scripts/quick_start.py
  14. 37
      electrum/simple_config.py
  15. 1
      electrum/sql_db.py
  16. 22
      electrum/tests/__init__.py
  17. 10
      electrum/tests/test_bitcoin.py
  18. 12
      electrum/tests/test_blockchain.py
  19. 11
      electrum/tests/test_bolt11.py
  20. 26
      electrum/tests/test_commands.py
  21. 4
      electrum/tests/test_dnssec.py
  22. 15
      electrum/tests/test_lnchannel.py
  23. 8
      electrum/tests/test_lnhtlc.py
  24. 4
      electrum/tests/test_lnpeer.py
  25. 19
      electrum/tests/test_lnrouter.py
  26. 11
      electrum/tests/test_lntransport.py
  27. 7
      electrum/tests/test_lnutil.py
  28. 10
      electrum/tests/test_mnemonic.py
  29. 7
      electrum/tests/test_network.py
  30. 4
      electrum/tests/test_revealer.py
  31. 6
      electrum/tests/test_simple_config.py
  32. 8
      electrum/tests/test_storage_upgrade.py
  33. 6
      electrum/tests/test_transaction.py
  34. 4
      electrum/tests/test_util.py
  35. 24
      electrum/tests/test_wallet.py
  36. 248
      electrum/tests/test_wallet_vertical.py
  37. 7
      electrum/tests/test_x509.py
  38. 39
      electrum/wallet.py
  39. 2
      run_electrum

6
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'],

2
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

2
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)

2
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

52
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)

9
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 = ''

2
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

2
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

7
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 = {}

46
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)]

2
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

4
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()

6
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())

37
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

1
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")

22
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):

10
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"

12
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"

11
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',

26
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"),

4
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):

15
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()

8
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()

4
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):

19
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

11
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()

7
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 = [
{

10
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 = {

7
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):

4
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')

6
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()

8
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):

6
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):

4
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))

24
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)

248
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)

7
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()

39
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)

2
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

Loading…
Cancel
Save