From 5a93bf054ea14f9c8dee8aad1f61eb37bd0317b4 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 28 Nov 2018 16:24:18 +0100 Subject: [PATCH 1/9] 2fa segwit (from ghost43's PR) --- electrum/base_wizard.py | 26 +++++---- electrum/bitcoin.py | 6 ++ electrum/plugins/trustedcoin/trustedcoin.py | 64 +++++++++++++++------ electrum/tests/test_wallet_vertical.py | 3 +- electrum/version.py | 9 ++- 5 files changed, 74 insertions(+), 34 deletions(-) diff --git a/electrum/base_wizard.py b/electrum/base_wizard.py index 7efd82297..838e6c4a6 100644 --- a/electrum/base_wizard.py +++ b/electrum/base_wizard.py @@ -417,7 +417,7 @@ class BaseWizard(object): self.passphrase_dialog(run_next=f, is_restoring=True) if is_ext else f('') elif self.seed_type == 'old': self.run('create_keystore', seed, '') - elif self.seed_type == '2fa': + elif bitcoin.is_any_2fa_seed_type(self.seed_type): self.load_2fa() self.run('on_restore_seed', seed, is_ext) else: @@ -540,18 +540,20 @@ class BaseWizard(object): def show_xpub_and_add_cosigners(self, xpub): self.show_xpub_dialog(xpub=xpub, run_next=lambda x: self.run('choose_keystore')) - def choose_seed_type(self): + def choose_seed_type(self, message=None, choices=None): title = _('Choose Seed type') - message = ' '.join([ - _("The type of addresses used by your wallet will depend on your seed."), - _("Segwit wallets use bech32 addresses, defined in BIP173."), - _("Please note that websites and other wallets may not support these addresses yet."), - _("Thus, you might want to keep using a non-segwit wallet in order to be able to receive bitcoins during the transition period.") - ]) - choices = [ - ('create_segwit_seed', _('Segwit')), - ('create_standard_seed', _('Legacy')), - ] + if message is None: + message = ' '.join([ + _("The type of addresses used by your wallet will depend on your seed."), + _("Segwit wallets use bech32 addresses, defined in BIP173."), + _("Please note that websites and other wallets may not support these addresses yet."), + _("Thus, you might want to keep using a non-segwit wallet in order to be able to receive bitcoins during the transition period.") + ]) + if choices is None: + choices = [ + ('create_segwit_seed', _('Segwit')), + ('create_standard_seed', _('Legacy')), + ] self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run) def create_segwit_seed(self): self.create_seed('segwit') diff --git a/electrum/bitcoin.py b/electrum/bitcoin.py index 194ccfa78..ee1b6c0fb 100644 --- a/electrum/bitcoin.py +++ b/electrum/bitcoin.py @@ -207,6 +207,8 @@ def seed_type(x: str) -> str: return 'segwit' elif is_new_seed(x, version.SEED_PREFIX_2FA): return '2fa' + elif is_new_seed(x, version.SEED_PREFIX_2FA_SW): + return '2fa_segwit' return '' @@ -214,6 +216,10 @@ def is_seed(x: str) -> bool: return bool(seed_type(x)) +def is_any_2fa_seed_type(seed_type): + return seed_type in ['2fa', '2fa_segwit'] + + ############ functions from pywallet ##################### def hash160_to_b58_address(h160: bytes, addrtype: int) -> str: diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index 03a7ca832..b3b2d0a99 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -34,9 +34,9 @@ from urllib.parse import quote from aiohttp import ClientResponse from electrum import ecc, constants, keystore, version, bip32 -from electrum.bitcoin import TYPE_ADDRESS, is_new_seed, public_key_to_p2pkh +from electrum.bitcoin import TYPE_ADDRESS, is_new_seed, public_key_to_p2pkh, seed_type, is_any_2fa_seed_type from electrum.bip32 import (deserialize_xpub, deserialize_xprv, bip32_private_key, CKD_pub, - serialize_xpub, bip32_root, bip32_private_derivation) + serialize_xpub, bip32_root, bip32_private_derivation, xpub_type) from electrum.crypto import sha256 from electrum.transaction import TxOutput from electrum.mnemonic import Mnemonic @@ -47,12 +47,20 @@ from electrum.util import NotEnoughFunds from electrum.storage import STO_EV_USER_PW from electrum.network import Network -# signing_xpub is hardcoded so that the wallet can be restored from seed, without TrustedCoin's server -def get_signing_xpub(): - if constants.net.TESTNET: - return "tpubD6NzVbkrYhZ4XdmyJQcCPjQfg6RXVUzGFhPjZ7uvRC8JLcS7Hw1i7UTpyhp9grHpak4TyK2hzBJrujDVLXQ6qB5tNpVx9rC6ixijUXadnmY" +def get_signing_xpub(xtype): + if xtype == 'standard': + if constants.net.TESTNET: + return "tpubD6NzVbkrYhZ4XdmyJQcCPjQfg6RXVUzGFhPjZ7uvRC8JLcS7Hw1i7UTpyhp9grHpak4TyK2hzBJrujDVLXQ6qB5tNpVx9rC6ixijUXadnmY" + else: + return "xpub661MyMwAqRbcGnMkaTx2594P9EDuiEqMq25PM2aeG6UmwzaohgA6uDmNsvSUV8ubqwA3Wpste1hg69XHgjUuCD5HLcEp2QPzyV1HMrPppsL" + elif xtype == 'p2wsh': + # TODO these are temp xpubs + if constants.net.TESTNET: + return "Vpub5fcdcgEwTJmbmqAktuK8Kyq92fMf7sWkcP6oqAii2tG47dNbfkGEGUbfS9NuZaRywLkHE6EmUksrqo32ZL3ouLN1HTar6oRiHpDzKMAF1tf" + else: + return "Zpub6xwgqLvc42wXB1wEELTdALD9iXwStMUkGqBgxkJFYumaL2dWgNvUkjEDWyDFZD3fZuDWDzd1KQJ4NwVHS7hs6H6QkpNYSShfNiUZsgMdtNg" else: - return "xpub661MyMwAqRbcGnMkaTx2594P9EDuiEqMq25PM2aeG6UmwzaohgA6uDmNsvSUV8ubqwA3Wpste1hg69XHgjUuCD5HLcEp2QPzyV1HMrPppsL" + raise NotImplementedError('xtype: {}'.format(xtype)) def get_billing_xpub(): if constants.net.TESTNET: @@ -60,7 +68,6 @@ def get_billing_xpub(): else: return "xpub6DTBdtBB8qUmH5c77v8qVGVoYk7WjJNpGvutqjLasNG1mbux6KsojaLrYf2sRhXAVU4NaFuHhbD9SvVPRt1MB1MaMooRuhHcAZH1yhQ1qDU" -SEED_PREFIX = version.SEED_PREFIX_2FA DISCLAIMER = [ _("Two-factor authentication is a service provided by TrustedCoin. " @@ -377,7 +384,8 @@ class TrustedCoinPlugin(BasePlugin): @staticmethod def is_valid_seed(seed): - return is_new_seed(seed, SEED_PREFIX) + t = seed_type(seed) + return is_any_2fa_seed_type(t) def is_available(self): return True @@ -449,8 +457,10 @@ class TrustedCoinPlugin(BasePlugin): t.start() return t - def make_seed(self): - return Mnemonic('english').make_seed(seed_type='2fa', num_bits=128) + def make_seed(self, seed_type): + if not is_any_2fa_seed_type(seed_type): + raise BaseException('unexpected seed type: {}'.format(seed_type)) + return Mnemonic('english').make_seed(seed_type=seed_type, num_bits=128) @hook def do_clear(self, window): @@ -465,25 +475,41 @@ class TrustedCoinPlugin(BasePlugin): title = _('Create or restore') message = _('Do you want to create a new seed, or to restore a wallet using an existing seed?') choices = [ - ('create_seed', _('Create a new seed')), + ('choose_seed_type', _('Create a new seed')), ('restore_wallet', _('I already have a seed')), ] wizard.choice_dialog(title=title, message=message, choices=choices, run_next=wizard.run) - def create_seed(self, wizard): - seed = self.make_seed() + def choose_seed_type(self, wizard): + choices = [ + ('create_2fa_seed', _('Standard 2FA')), + ('create_2fa_segwit_seed', _('Segwit 2FA')), + ] + wizard.choose_seed_type(choices=choices) + + def create_2fa_seed(self, wizard): self.create_seed(wizard, '2fa') + def create_2fa_segwit_seed(self, wizard): self.create_seed(wizard, '2fa_segwit') + + def create_seed(self, wizard, seed_type): + seed = self.make_seed(seed_type) f = lambda x: wizard.request_passphrase(seed, x) wizard.show_seed_dialog(run_next=f, seed_text=seed) @classmethod def get_xkeys(self, seed, passphrase, derivation): + t = seed_type(seed) + assert is_any_2fa_seed_type(t) + xtype = 'standard' if t == '2fa' else 'p2wsh' bip32_seed = Mnemonic.mnemonic_to_seed(seed, passphrase) - xprv, xpub = bip32_root(bip32_seed, 'standard') + xprv, xpub = bip32_root(bip32_seed, xtype) xprv, xpub = bip32_private_derivation(xprv, "m/", derivation) return xprv, xpub @classmethod def xkeys_from_seed(self, seed, passphrase): + t = seed_type(seed) + if not is_any_2fa_seed_type(t): + raise BaseException('unexpected seed type: {}'.format(t)) words = seed.split() n = len(words) # old version use long seed phrases @@ -495,7 +521,7 @@ class TrustedCoinPlugin(BasePlugin): raise Exception('old 2fa seed cannot have passphrase') xprv1, xpub1 = self.get_xkeys(' '.join(words[0:12]), '', "m/") xprv2, xpub2 = self.get_xkeys(' '.join(words[12:]), '', "m/") - elif n==12: + elif not t == '2fa' or n == 12: xprv1, xpub1 = self.get_xkeys(seed, passphrase, "m/0'/") xprv2, xpub2 = self.get_xkeys(seed, passphrase, "m/1'/") else: @@ -561,7 +587,8 @@ class TrustedCoinPlugin(BasePlugin): storage.put('x1/', k1.dump()) storage.put('x2/', k2.dump()) long_user_id, short_id = get_user_id(storage) - xpub3 = make_xpub(get_signing_xpub(), long_user_id) + xtype = xpub_type(xpub1) + xpub3 = make_xpub(get_signing_xpub(xtype), long_user_id) k3 = keystore.from_xpub(xpub3) storage.put('x3/', k3.dump()) @@ -578,7 +605,8 @@ class TrustedCoinPlugin(BasePlugin): xpub2 = wizard.storage.get('x2/')['xpub'] # Generate third key deterministically. long_user_id, short_id = get_user_id(wizard.storage) - xpub3 = make_xpub(get_signing_xpub(), long_user_id) + xtype = xpub_type(xpub1) + xpub3 = make_xpub(get_signing_xpub(xtype), long_user_id) # secret must be sent by the server try: r = server.create(xpub1, xpub2, email) diff --git a/electrum/tests/test_wallet_vertical.py b/electrum/tests/test_wallet_vertical.py index 9bc1a6e89..0acc2776c 100644 --- a/electrum/tests/test_wallet_vertical.py +++ b/electrum/tests/test_wallet_vertical.py @@ -178,7 +178,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): long_user_id, short_id = trustedcoin.get_user_id( {'x1/': {'xpub': xpub1}, 'x2/': {'xpub': xpub2}}) - xpub3 = trustedcoin.make_xpub(trustedcoin.get_signing_xpub(), long_user_id) + xtype = bitcoin.xpub_type(xpub1) + xpub3 = trustedcoin.make_xpub(trustedcoin.get_signing_xpub(xtype), long_user_id) ks3 = keystore.from_xpub(xpub3) WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks3) self.assertTrue(isinstance(ks3, keystore.BIP32_KeyStore)) diff --git a/electrum/version.py b/electrum/version.py index 5866941f3..31ecebe5c 100644 --- a/electrum/version.py +++ b/electrum/version.py @@ -4,9 +4,10 @@ APK_VERSION = '3.3.0.0' # read by buildozer.spec PROTOCOL_VERSION = '1.4' # protocol version requested # The hash of the mnemonic seed must begin with this -SEED_PREFIX = '01' # Standard wallet -SEED_PREFIX_2FA = '101' # Two-factor authentication -SEED_PREFIX_SW = '100' # Segwit wallet +SEED_PREFIX = '01' # Standard wallet +SEED_PREFIX_SW = '100' # Segwit wallet +SEED_PREFIX_2FA = '101' # Two-factor authentication +SEED_PREFIX_2FA_SW = '102' # Two-factor auth, using segwit def seed_prefix(seed_type): @@ -16,3 +17,5 @@ def seed_prefix(seed_type): return SEED_PREFIX_SW elif seed_type == '2fa': return SEED_PREFIX_2FA + elif seed_type == '2fa_segwit': + return SEED_PREFIX_2FA_SW From df59a43300add969692f2c0f62c3398f159a0519 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 28 Nov 2018 17:01:04 +0100 Subject: [PATCH 2/9] fix test --- electrum/tests/test_wallet_vertical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/electrum/tests/test_wallet_vertical.py b/electrum/tests/test_wallet_vertical.py index 0acc2776c..87213318c 100644 --- a/electrum/tests/test_wallet_vertical.py +++ b/electrum/tests/test_wallet_vertical.py @@ -4,7 +4,7 @@ import tempfile from typing import Sequence import asyncio -from electrum import storage, bitcoin, keystore +from electrum import storage, bitcoin, keystore, bip32 from electrum import Transaction from electrum import SimpleConfig from electrum.address_synchronizer import TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT @@ -178,7 +178,7 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase): long_user_id, short_id = trustedcoin.get_user_id( {'x1/': {'xpub': xpub1}, 'x2/': {'xpub': xpub2}}) - xtype = bitcoin.xpub_type(xpub1) + xtype = bip32.xpub_type(xpub1) xpub3 = trustedcoin.make_xpub(trustedcoin.get_signing_xpub(xtype), long_user_id) ks3 = keystore.from_xpub(xpub3) WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks3) From eeea4fcb319acd35708d7cc395796708dfd8f9f0 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Wed, 28 Nov 2018 17:24:32 +0100 Subject: [PATCH 3/9] rename 2fa non-segwit type to "legacy 2fa" and make segwit the default --- electrum/plugins/trustedcoin/trustedcoin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index b3b2d0a99..adf3f413e 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -482,8 +482,8 @@ class TrustedCoinPlugin(BasePlugin): def choose_seed_type(self, wizard): choices = [ - ('create_2fa_seed', _('Standard 2FA')), ('create_2fa_segwit_seed', _('Segwit 2FA')), + ('create_2fa_seed', _('Legacy 2FA')), ] wizard.choose_seed_type(choices=choices) From 7b90d6944354847762b1668af24ad50850eb59e9 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Wed, 28 Nov 2018 19:00:41 +0100 Subject: [PATCH 4/9] trustedcoin: p2wpkh billing addresses --- electrum/plugins/trustedcoin/qt.py | 12 ---- electrum/plugins/trustedcoin/trustedcoin.py | 62 +++++++++++++-------- electrum/storage.py | 12 +++- 3 files changed, 49 insertions(+), 37 deletions(-) diff --git a/electrum/plugins/trustedcoin/qt.py b/electrum/plugins/trustedcoin/qt.py index da4ce84cd..c84a2bbb0 100644 --- a/electrum/plugins/trustedcoin/qt.py +++ b/electrum/plugins/trustedcoin/qt.py @@ -195,18 +195,6 @@ class Plugin(TrustedCoinPlugin): vbox.addLayout(Buttons(CloseButton(d))) d.exec_() - def on_buy(self, window, k, v, d): - d.close() - if window.pluginsdialog: - window.pluginsdialog.close() - wallet = window.wallet - uri = "bitcoin:" + wallet.billing_info['billing_address'] + "?message=TrustedCoin %d Prepaid Transactions&amount="%k + str(Decimal(v)/100000000) - wallet.is_billing = True - window.pay_to_URI(uri) - window.payto_e.setFrozen(True) - window.message_e.setFrozen(True) - window.amount_e.setFrozen(True) - def go_online_dialog(self, wizard): msg = [ _("Your wallet file is: {}.").format(os.path.abspath(wizard.storage.path)), diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index adf3f413e..d93bf3541 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -28,13 +28,15 @@ import json import base64 import time import hashlib +from collections import defaultdict +from typing import Dict from urllib.parse import urljoin from urllib.parse import quote from aiohttp import ClientResponse -from electrum import ecc, constants, keystore, version, bip32 -from electrum.bitcoin import TYPE_ADDRESS, is_new_seed, public_key_to_p2pkh, seed_type, is_any_2fa_seed_type +from electrum import ecc, constants, keystore, version, bip32, bitcoin +from electrum.bitcoin import TYPE_ADDRESS, is_new_seed, seed_type, is_any_2fa_seed_type from electrum.bip32 import (deserialize_xpub, deserialize_xprv, bip32_private_key, CKD_pub, serialize_xpub, bip32_root, bip32_private_derivation, xpub_type) from electrum.crypto import sha256 @@ -244,14 +246,18 @@ class Wallet_2fa(Multisig_Wallet): self.is_billing = False self.billing_info = None self._load_billing_addresses() + self.plugin = None # type: TrustedCoinPlugin def _load_billing_addresses(self): billing_addresses = self.storage.get('trustedcoin_billing_addresses', {}) - self._billing_addresses = {} # index -> addr - # convert keys from str to int - for index, addr in list(billing_addresses.items()): - self._billing_addresses[int(index)] = addr - self._billing_addresses_set = set(self._billing_addresses.values()) # set of addrs + self._billing_addresses = defaultdict(dict) # type: Dict[str, Dict[int, str]] # addr_type -> index -> addr + self._billing_addresses_set = set() # set of addrs + for addr_type, d in list(billing_addresses.items()): + self._billing_addresses[addr_type] = {} + # convert keys from str to int + for index, addr in d.items(): + self._billing_addresses[addr_type][int(index)] = addr + self._billing_addresses_set.add(addr) def can_sign_without_server(self): return not self.keystores['x2/'].is_watching_only() @@ -291,7 +297,7 @@ class Wallet_2fa(Multisig_Wallet): self, coins, o, config, fixed_fee, change_addr) fee = self.extra_fee(config) if not is_sweep else 0 if fee: - address = self.billing_info['billing_address'] + address = self.billing_info['billing_address_segwit'] fee_output = TxOutput(TYPE_ADDRESS, address, fee) try: tx = mk_tx(outputs + [fee_output]) @@ -322,8 +328,9 @@ class Wallet_2fa(Multisig_Wallet): self.billing_info = None self.plugin.start_request_thread(self) - def add_new_billing_address(self, billing_index: int, address: str): - saved_addr = self._billing_addresses.get(billing_index) + def add_new_billing_address(self, billing_index: int, address: str, addr_type: str): + billing_addresses_of_this_type = self._billing_addresses[addr_type] + saved_addr = billing_addresses_of_this_type.get(billing_index) if saved_addr is not None: if saved_addr == address: return # already saved this address @@ -332,15 +339,16 @@ class Wallet_2fa(Multisig_Wallet): 'for index {}, already saved {}, now got {}' .format(billing_index, saved_addr, address)) # do we have all prior indices? (are we synced?) - largest_index_we_have = max(self._billing_addresses) if self._billing_addresses else -1 + largest_index_we_have = max(billing_addresses_of_this_type) if billing_addresses_of_this_type else -1 if largest_index_we_have + 1 < billing_index: # need to sync for i in range(largest_index_we_have + 1, billing_index): - addr = make_billing_address(self, i) - self._billing_addresses[i] = addr + addr = make_billing_address(self, i, addr_type=addr_type) + billing_addresses_of_this_type[i] = addr self._billing_addresses_set.add(addr) # save this address; and persist to disk - self._billing_addresses[billing_index] = address + billing_addresses_of_this_type[billing_index] = address self._billing_addresses_set.add(address) + self._billing_addresses[addr_type] = billing_addresses_of_this_type self.storage.put('trustedcoin_billing_addresses', self._billing_addresses) # FIXME this often runs in a daemon thread, where storage.write will fail self.storage.write() @@ -365,12 +373,17 @@ def make_xpub(xpub, s): cK2, c2 = bip32._CKD_pub(cK, c, s) return serialize_xpub(version, c2, cK2) -def make_billing_address(wallet, num): +def make_billing_address(wallet, num, addr_type): long_id, short_id = wallet.get_user_id() xpub = make_xpub(get_billing_xpub(), long_id) version, _, _, _, c, cK = deserialize_xpub(xpub) cK, c = CKD_pub(cK, c, num) - return public_key_to_p2pkh(cK) + if addr_type == 'legacy': + return bitcoin.public_key_to_p2pkh(cK) + elif addr_type == 'segwit': + return bitcoin.public_key_to_p2wpkh(cK) + else: + raise ValueError(f'unexpected billing type: {addr_type}') class TrustedCoinPlugin(BasePlugin): @@ -428,7 +441,7 @@ class TrustedCoinPlugin(BasePlugin): return f @finish_requesting - def request_billing_info(self, wallet): + def request_billing_info(self, wallet: 'Wallet_2fa'): if wallet.can_sign_without_server(): return self.print_error("request billing info") @@ -438,11 +451,16 @@ class TrustedCoinPlugin(BasePlugin): self.print_error('cannot connect to TrustedCoin server: {}'.format(repr(e))) return billing_index = billing_info['billing_index'] - billing_address = make_billing_address(wallet, billing_index) - if billing_address != billing_info['billing_address']: - raise Exception('unexpected trustedcoin billing address: expected {}, received {}' - .format(billing_address, billing_info['billing_address'])) - wallet.add_new_billing_address(billing_index, billing_address) + # add segwit billing address; this will be used for actual billing + billing_address = make_billing_address(wallet, billing_index, addr_type='segwit') + if billing_address != billing_info['billing_address_segwit']: + raise Exception(f'unexpected trustedcoin billing address: ' + f'calculated {billing_address}, received {billing_info["billing_address_segwit"]}') + wallet.add_new_billing_address(billing_index, billing_address, addr_type='segwit') + # also add legacy billing address; only used for detecting past payments in GUI + billing_address = make_billing_address(wallet, billing_index, addr_type='legacy') + wallet.add_new_billing_address(billing_index, billing_address, addr_type='legacy') + wallet.billing_info = billing_info wallet.price_per_tx = dict(billing_info['price_per_tx']) wallet.price_per_tx.pop(1, None) diff --git a/electrum/storage.py b/electrum/storage.py index d526000e2..b88e83136 100644 --- a/electrum/storage.py +++ b/electrum/storage.py @@ -44,7 +44,7 @@ from .keystore import bip44_derivation OLD_SEED_VERSION = 4 # electrum versions < 2.0 NEW_SEED_VERSION = 11 # electrum versions >= 2.0 -FINAL_SEED_VERSION = 18 # electrum >= 2.7 will set this to prevent +FINAL_SEED_VERSION = 19 # electrum >= 2.7 will set this to prevent # old versions from overwriting new format @@ -354,6 +354,7 @@ class WalletStorage(JsonDB): self.convert_version_16() self.convert_version_17() self.convert_version_18() + self.convert_version_19() self.put('seed_version', FINAL_SEED_VERSION) # just to be sure self.write() @@ -572,11 +573,16 @@ class WalletStorage(JsonDB): # delete verified_tx3 as its structure changed if not self._is_upgrade_method_needed(17, 17): return - self.put('verified_tx3', None) - self.put('seed_version', 18) + def convert_version_19(self): + # delete trustedcoin_billing_addresses + if not self._is_upgrade_method_needed(18, 18): + return + self.put('trustedcoin_billing_addresses', None) + self.put('seed_version', 19) + def convert_imported(self): if not self._is_upgrade_method_needed(0, 13): return From 852f2a0d65de2b5d34083fa0cdd031fec257714e Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sun, 9 Dec 2018 06:17:33 +0100 Subject: [PATCH 5/9] trustedcoin: do not require wallet file upgrade --- electrum/plugins/trustedcoin/trustedcoin.py | 10 +++++++--- electrum/storage.py | 10 +--------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index d93bf3541..1a989ca09 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -249,8 +249,11 @@ class Wallet_2fa(Multisig_Wallet): self.plugin = None # type: TrustedCoinPlugin def _load_billing_addresses(self): - billing_addresses = self.storage.get('trustedcoin_billing_addresses', {}) - self._billing_addresses = defaultdict(dict) # type: Dict[str, Dict[int, str]] # addr_type -> index -> addr + # type: Dict[str, Dict[int, str]] # addr_type -> index -> addr + self._billing_addresses = { + 'legacy': self.storage.get('trustedcoin_billing_addresses', {}), + 'segwit': self.storage.get('trustedcoin_billing_addresses_segwit', {}) + } self._billing_addresses_set = set() # set of addrs for addr_type, d in list(billing_addresses.items()): self._billing_addresses[addr_type] = {} @@ -349,7 +352,8 @@ class Wallet_2fa(Multisig_Wallet): billing_addresses_of_this_type[billing_index] = address self._billing_addresses_set.add(address) self._billing_addresses[addr_type] = billing_addresses_of_this_type - self.storage.put('trustedcoin_billing_addresses', self._billing_addresses) + self.storage.put('trustedcoin_billing_addresses', self._billing_addresses['legacy']) + self.storage.put('trustedcoin_billing_addresses_segwit', self._billing_addresses['segwit']) # FIXME this often runs in a daemon thread, where storage.write will fail self.storage.write() diff --git a/electrum/storage.py b/electrum/storage.py index b88e83136..7dcce567c 100644 --- a/electrum/storage.py +++ b/electrum/storage.py @@ -44,7 +44,7 @@ from .keystore import bip44_derivation OLD_SEED_VERSION = 4 # electrum versions < 2.0 NEW_SEED_VERSION = 11 # electrum versions >= 2.0 -FINAL_SEED_VERSION = 19 # electrum >= 2.7 will set this to prevent +FINAL_SEED_VERSION = 18 # electrum >= 2.7 will set this to prevent # old versions from overwriting new format @@ -354,7 +354,6 @@ class WalletStorage(JsonDB): self.convert_version_16() self.convert_version_17() self.convert_version_18() - self.convert_version_19() self.put('seed_version', FINAL_SEED_VERSION) # just to be sure self.write() @@ -576,13 +575,6 @@ class WalletStorage(JsonDB): self.put('verified_tx3', None) self.put('seed_version', 18) - def convert_version_19(self): - # delete trustedcoin_billing_addresses - if not self._is_upgrade_method_needed(18, 18): - return - self.put('trustedcoin_billing_addresses', None) - self.put('seed_version', 19) - def convert_imported(self): if not self._is_upgrade_method_needed(0, 13): return From 84519752c381a688b612cc3bfbd7987908eba7a4 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Sun, 9 Dec 2018 06:33:32 +0100 Subject: [PATCH 6/9] trustedcoin: fix prev. remove temp xpubs. --- electrum/plugins/trustedcoin/trustedcoin.py | 24 ++++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index 1a989ca09..5c309b932 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -50,19 +50,17 @@ from electrum.storage import STO_EV_USER_PW from electrum.network import Network def get_signing_xpub(xtype): - if xtype == 'standard': - if constants.net.TESTNET: - return "tpubD6NzVbkrYhZ4XdmyJQcCPjQfg6RXVUzGFhPjZ7uvRC8JLcS7Hw1i7UTpyhp9grHpak4TyK2hzBJrujDVLXQ6qB5tNpVx9rC6ixijUXadnmY" - else: - return "xpub661MyMwAqRbcGnMkaTx2594P9EDuiEqMq25PM2aeG6UmwzaohgA6uDmNsvSUV8ubqwA3Wpste1hg69XHgjUuCD5HLcEp2QPzyV1HMrPppsL" - elif xtype == 'p2wsh': - # TODO these are temp xpubs - if constants.net.TESTNET: - return "Vpub5fcdcgEwTJmbmqAktuK8Kyq92fMf7sWkcP6oqAii2tG47dNbfkGEGUbfS9NuZaRywLkHE6EmUksrqo32ZL3ouLN1HTar6oRiHpDzKMAF1tf" - else: - return "Zpub6xwgqLvc42wXB1wEELTdALD9iXwStMUkGqBgxkJFYumaL2dWgNvUkjEDWyDFZD3fZuDWDzd1KQJ4NwVHS7hs6H6QkpNYSShfNiUZsgMdtNg" + if not constants.net.TESTNET: + xpub = "xpub661MyMwAqRbcGnMkaTx2594P9EDuiEqMq25PM2aeG6UmwzaohgA6uDmNsvSUV8ubqwA3Wpste1hg69XHgjUuCD5HLcEp2QPzyV1HMrPppsL" else: + xpub = "tpubD6NzVbkrYhZ4XdmyJQcCPjQfg6RXVUzGFhPjZ7uvRC8JLcS7Hw1i7UTpyhp9grHpak4TyK2hzBJrujDVLXQ6qB5tNpVx9rC6ixijUXadnmY" + if xtype not in ('standard', 'p2wsh'): raise NotImplementedError('xtype: {}'.format(xtype)) + if xtype == 'standard': + return xpub + _, depth, fingerprint, child_number, c, cK = bip32.deserialize_xpub(xpub) + xpub = bip32.serialize_xpub(xtype, c, cK, depth, fingerprint, child_number) + return xpub def get_billing_xpub(): if constants.net.TESTNET: @@ -249,11 +247,11 @@ class Wallet_2fa(Multisig_Wallet): self.plugin = None # type: TrustedCoinPlugin def _load_billing_addresses(self): - # type: Dict[str, Dict[int, str]] # addr_type -> index -> addr - self._billing_addresses = { + billing_addresses = { 'legacy': self.storage.get('trustedcoin_billing_addresses', {}), 'segwit': self.storage.get('trustedcoin_billing_addresses_segwit', {}) } + self._billing_addresses = {} # type: Dict[str, Dict[int, str]] # addr_type -> index -> addr self._billing_addresses_set = set() # set of addrs for addr_type, d in list(billing_addresses.items()): self._billing_addresses[addr_type] = {} From 040b5b3f88aacaee033b96dbee584aa59d4572fa Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 11 Dec 2018 09:59:39 +0100 Subject: [PATCH 7/9] trustedcoin: fix get_xkeys --- electrum/plugins/trustedcoin/trustedcoin.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index 5c309b932..92c14cb0b 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -516,8 +516,7 @@ class TrustedCoinPlugin(BasePlugin): wizard.show_seed_dialog(run_next=f, seed_text=seed) @classmethod - def get_xkeys(self, seed, passphrase, derivation): - t = seed_type(seed) + def get_xkeys(self, seed, t, passphrase, derivation): assert is_any_2fa_seed_type(t) xtype = 'standard' if t == '2fa' else 'p2wsh' bip32_seed = Mnemonic.mnemonic_to_seed(seed, passphrase) @@ -539,11 +538,11 @@ class TrustedCoinPlugin(BasePlugin): # the probability of it being < 20 words is about 2^(-(256+12-19*11)) = 2^(-59) if passphrase != '': raise Exception('old 2fa seed cannot have passphrase') - xprv1, xpub1 = self.get_xkeys(' '.join(words[0:12]), '', "m/") - xprv2, xpub2 = self.get_xkeys(' '.join(words[12:]), '', "m/") + xprv1, xpub1 = self.get_xkeys(' '.join(words[0:12]), t, '', "m/") + xprv2, xpub2 = self.get_xkeys(' '.join(words[12:]), t, '', "m/") elif not t == '2fa' or n == 12: - xprv1, xpub1 = self.get_xkeys(seed, passphrase, "m/0'/") - xprv2, xpub2 = self.get_xkeys(seed, passphrase, "m/1'/") + xprv1, xpub1 = self.get_xkeys(seed, t, passphrase, "m/0'/") + xprv2, xpub2 = self.get_xkeys(seed, t, passphrase, "m/1'/") else: raise Exception('unrecognized seed length: {} words'.format(n)) return xprv1, xpub1, xprv2, xpub2 From 467e40b555bad9f5bc01952c9fcadc3df4aa0fe4 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 11 Dec 2018 11:46:31 +0100 Subject: [PATCH 8/9] trustedcoin: serialize using PARTIAL_TXN_HEADER_MAGIC --- electrum/plugins/trustedcoin/trustedcoin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index 92c14cb0b..9bb9491ff 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -319,7 +319,7 @@ class Wallet_2fa(Multisig_Wallet): return otp = int(otp) long_user_id, short_id = self.get_user_id() - raw_tx = tx.serialize_to_network() + raw_tx = tx.serialize() r = server.sign(short_id, raw_tx, otp) if r: raw_tx = r.get('transaction') From 502a4819b6a831152357fd017921b4a857dcfa50 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 11 Dec 2018 13:08:10 +0100 Subject: [PATCH 9/9] trustedcoin: do not set wallet.plugin in constructor --- electrum/plugins/trustedcoin/trustedcoin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index 9bb9491ff..86887c96c 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -244,7 +244,6 @@ class Wallet_2fa(Multisig_Wallet): self.is_billing = False self.billing_info = None self._load_billing_addresses() - self.plugin = None # type: TrustedCoinPlugin def _load_billing_addresses(self): billing_addresses = {