Browse Source

Merge pull request #4880 from spesmilo/2fa_segwit

2fa segwit (from ghost43's PR)
3.3.3.1
ThomasV 6 years ago
committed by GitHub
parent
commit
dd848304e6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 26
      electrum/base_wizard.py
  2. 6
      electrum/bitcoin.py
  3. 12
      electrum/plugins/trustedcoin/qt.py
  4. 140
      electrum/plugins/trustedcoin/trustedcoin.py
  5. 2
      electrum/storage.py
  6. 5
      electrum/tests/test_wallet_vertical.py
  7. 9
      electrum/version.py

26
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('') self.passphrase_dialog(run_next=f, is_restoring=True) if is_ext else f('')
elif self.seed_type == 'old': elif self.seed_type == 'old':
self.run('create_keystore', seed, '') self.run('create_keystore', seed, '')
elif self.seed_type == '2fa': elif bitcoin.is_any_2fa_seed_type(self.seed_type):
self.load_2fa() self.load_2fa()
self.run('on_restore_seed', seed, is_ext) self.run('on_restore_seed', seed, is_ext)
else: else:
@ -540,18 +540,20 @@ class BaseWizard(object):
def show_xpub_and_add_cosigners(self, xpub): def show_xpub_and_add_cosigners(self, xpub):
self.show_xpub_dialog(xpub=xpub, run_next=lambda x: self.run('choose_keystore')) 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') title = _('Choose Seed type')
message = ' '.join([ if message is None:
_("The type of addresses used by your wallet will depend on your seed."), message = ' '.join([
_("Segwit wallets use bech32 addresses, defined in BIP173."), _("The type of addresses used by your wallet will depend on your seed."),
_("Please note that websites and other wallets may not support these addresses yet."), _("Segwit wallets use bech32 addresses, defined in BIP173."),
_("Thus, you might want to keep using a non-segwit wallet in order to be able to receive bitcoins during the transition period.") _("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')), if choices is None:
('create_standard_seed', _('Legacy')), choices = [
] ('create_segwit_seed', _('Segwit')),
('create_standard_seed', _('Legacy')),
]
self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run) self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run)
def create_segwit_seed(self): self.create_seed('segwit') def create_segwit_seed(self): self.create_seed('segwit')

6
electrum/bitcoin.py

@ -207,6 +207,8 @@ def seed_type(x: str) -> str:
return 'segwit' return 'segwit'
elif is_new_seed(x, version.SEED_PREFIX_2FA): elif is_new_seed(x, version.SEED_PREFIX_2FA):
return '2fa' return '2fa'
elif is_new_seed(x, version.SEED_PREFIX_2FA_SW):
return '2fa_segwit'
return '' return ''
@ -214,6 +216,10 @@ def is_seed(x: str) -> bool:
return bool(seed_type(x)) return bool(seed_type(x))
def is_any_2fa_seed_type(seed_type):
return seed_type in ['2fa', '2fa_segwit']
############ functions from pywallet ##################### ############ functions from pywallet #####################
def hash160_to_b58_address(h160: bytes, addrtype: int) -> str: def hash160_to_b58_address(h160: bytes, addrtype: int) -> str:

12
electrum/plugins/trustedcoin/qt.py

@ -195,18 +195,6 @@ class Plugin(TrustedCoinPlugin):
vbox.addLayout(Buttons(CloseButton(d))) vbox.addLayout(Buttons(CloseButton(d)))
d.exec_() 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): def go_online_dialog(self, wizard):
msg = [ msg = [
_("Your wallet file is: {}.").format(os.path.abspath(wizard.storage.path)), _("Your wallet file is: {}.").format(os.path.abspath(wizard.storage.path)),

140
electrum/plugins/trustedcoin/trustedcoin.py

@ -28,15 +28,17 @@ import json
import base64 import base64
import time import time
import hashlib import hashlib
from collections import defaultdict
from typing import Dict
from urllib.parse import urljoin from urllib.parse import urljoin
from urllib.parse import quote from urllib.parse import quote
from aiohttp import ClientResponse from aiohttp import ClientResponse
from electrum import ecc, constants, keystore, version, bip32 from electrum import ecc, constants, keystore, version, bip32, bitcoin
from electrum.bitcoin import TYPE_ADDRESS, is_new_seed, public_key_to_p2pkh 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, 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.crypto import sha256
from electrum.transaction import TxOutput from electrum.transaction import TxOutput
from electrum.mnemonic import Mnemonic from electrum.mnemonic import Mnemonic
@ -47,12 +49,18 @@ from electrum.util import NotEnoughFunds
from electrum.storage import STO_EV_USER_PW from electrum.storage import STO_EV_USER_PW
from electrum.network import Network 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(xtype):
def get_signing_xpub(): if not constants.net.TESTNET:
if constants.net.TESTNET: xpub = "xpub661MyMwAqRbcGnMkaTx2594P9EDuiEqMq25PM2aeG6UmwzaohgA6uDmNsvSUV8ubqwA3Wpste1hg69XHgjUuCD5HLcEp2QPzyV1HMrPppsL"
return "tpubD6NzVbkrYhZ4XdmyJQcCPjQfg6RXVUzGFhPjZ7uvRC8JLcS7Hw1i7UTpyhp9grHpak4TyK2hzBJrujDVLXQ6qB5tNpVx9rC6ixijUXadnmY"
else: else:
return "xpub661MyMwAqRbcGnMkaTx2594P9EDuiEqMq25PM2aeG6UmwzaohgA6uDmNsvSUV8ubqwA3Wpste1hg69XHgjUuCD5HLcEp2QPzyV1HMrPppsL" 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(): def get_billing_xpub():
if constants.net.TESTNET: if constants.net.TESTNET:
@ -60,7 +68,6 @@ def get_billing_xpub():
else: else:
return "xpub6DTBdtBB8qUmH5c77v8qVGVoYk7WjJNpGvutqjLasNG1mbux6KsojaLrYf2sRhXAVU4NaFuHhbD9SvVPRt1MB1MaMooRuhHcAZH1yhQ1qDU" return "xpub6DTBdtBB8qUmH5c77v8qVGVoYk7WjJNpGvutqjLasNG1mbux6KsojaLrYf2sRhXAVU4NaFuHhbD9SvVPRt1MB1MaMooRuhHcAZH1yhQ1qDU"
SEED_PREFIX = version.SEED_PREFIX_2FA
DISCLAIMER = [ DISCLAIMER = [
_("Two-factor authentication is a service provided by TrustedCoin. " _("Two-factor authentication is a service provided by TrustedCoin. "
@ -239,12 +246,18 @@ class Wallet_2fa(Multisig_Wallet):
self._load_billing_addresses() self._load_billing_addresses()
def _load_billing_addresses(self): def _load_billing_addresses(self):
billing_addresses = self.storage.get('trustedcoin_billing_addresses', {}) billing_addresses = {
self._billing_addresses = {} # index -> addr 'legacy': self.storage.get('trustedcoin_billing_addresses', {}),
# convert keys from str to int 'segwit': self.storage.get('trustedcoin_billing_addresses_segwit', {})
for index, addr in list(billing_addresses.items()): }
self._billing_addresses[int(index)] = addr self._billing_addresses = {} # type: Dict[str, Dict[int, str]] # addr_type -> index -> addr
self._billing_addresses_set = set(self._billing_addresses.values()) # set of addrs 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): def can_sign_without_server(self):
return not self.keystores['x2/'].is_watching_only() return not self.keystores['x2/'].is_watching_only()
@ -284,7 +297,7 @@ class Wallet_2fa(Multisig_Wallet):
self, coins, o, config, fixed_fee, change_addr) self, coins, o, config, fixed_fee, change_addr)
fee = self.extra_fee(config) if not is_sweep else 0 fee = self.extra_fee(config) if not is_sweep else 0
if fee: if fee:
address = self.billing_info['billing_address'] address = self.billing_info['billing_address_segwit']
fee_output = TxOutput(TYPE_ADDRESS, address, fee) fee_output = TxOutput(TYPE_ADDRESS, address, fee)
try: try:
tx = mk_tx(outputs + [fee_output]) tx = mk_tx(outputs + [fee_output])
@ -305,7 +318,7 @@ class Wallet_2fa(Multisig_Wallet):
return return
otp = int(otp) otp = int(otp)
long_user_id, short_id = self.get_user_id() 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) r = server.sign(short_id, raw_tx, otp)
if r: if r:
raw_tx = r.get('transaction') raw_tx = r.get('transaction')
@ -315,8 +328,9 @@ class Wallet_2fa(Multisig_Wallet):
self.billing_info = None self.billing_info = None
self.plugin.start_request_thread(self) self.plugin.start_request_thread(self)
def add_new_billing_address(self, billing_index: int, address: str): def add_new_billing_address(self, billing_index: int, address: str, addr_type: str):
saved_addr = self._billing_addresses.get(billing_index) 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 is not None:
if saved_addr == address: if saved_addr == address:
return # already saved this address return # already saved this address
@ -325,16 +339,18 @@ class Wallet_2fa(Multisig_Wallet):
'for index {}, already saved {}, now got {}' 'for index {}, already saved {}, now got {}'
.format(billing_index, saved_addr, address)) .format(billing_index, saved_addr, address))
# do we have all prior indices? (are we synced?) # 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 if largest_index_we_have + 1 < billing_index: # need to sync
for i in range(largest_index_we_have + 1, billing_index): for i in range(largest_index_we_have + 1, billing_index):
addr = make_billing_address(self, i) addr = make_billing_address(self, i, addr_type=addr_type)
self._billing_addresses[i] = addr billing_addresses_of_this_type[i] = addr
self._billing_addresses_set.add(addr) self._billing_addresses_set.add(addr)
# save this address; and persist to disk # 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_set.add(address)
self.storage.put('trustedcoin_billing_addresses', self._billing_addresses) self._billing_addresses[addr_type] = billing_addresses_of_this_type
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 # FIXME this often runs in a daemon thread, where storage.write will fail
self.storage.write() self.storage.write()
@ -358,12 +374,17 @@ def make_xpub(xpub, s):
cK2, c2 = bip32._CKD_pub(cK, c, s) cK2, c2 = bip32._CKD_pub(cK, c, s)
return serialize_xpub(version, c2, cK2) 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() long_id, short_id = wallet.get_user_id()
xpub = make_xpub(get_billing_xpub(), long_id) xpub = make_xpub(get_billing_xpub(), long_id)
version, _, _, _, c, cK = deserialize_xpub(xpub) version, _, _, _, c, cK = deserialize_xpub(xpub)
cK, c = CKD_pub(cK, c, num) 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): class TrustedCoinPlugin(BasePlugin):
@ -377,7 +398,8 @@ class TrustedCoinPlugin(BasePlugin):
@staticmethod @staticmethod
def is_valid_seed(seed): 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): def is_available(self):
return True return True
@ -420,7 +442,7 @@ class TrustedCoinPlugin(BasePlugin):
return f return f
@finish_requesting @finish_requesting
def request_billing_info(self, wallet): def request_billing_info(self, wallet: 'Wallet_2fa'):
if wallet.can_sign_without_server(): if wallet.can_sign_without_server():
return return
self.print_error("request billing info") self.print_error("request billing info")
@ -430,11 +452,16 @@ class TrustedCoinPlugin(BasePlugin):
self.print_error('cannot connect to TrustedCoin server: {}'.format(repr(e))) self.print_error('cannot connect to TrustedCoin server: {}'.format(repr(e)))
return return
billing_index = billing_info['billing_index'] billing_index = billing_info['billing_index']
billing_address = make_billing_address(wallet, billing_index) # add segwit billing address; this will be used for actual billing
if billing_address != billing_info['billing_address']: billing_address = make_billing_address(wallet, billing_index, addr_type='segwit')
raise Exception('unexpected trustedcoin billing address: expected {}, received {}' if billing_address != billing_info['billing_address_segwit']:
.format(billing_address, billing_info['billing_address'])) raise Exception(f'unexpected trustedcoin billing address: '
wallet.add_new_billing_address(billing_index, 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.billing_info = billing_info
wallet.price_per_tx = dict(billing_info['price_per_tx']) wallet.price_per_tx = dict(billing_info['price_per_tx'])
wallet.price_per_tx.pop(1, None) wallet.price_per_tx.pop(1, None)
@ -449,8 +476,10 @@ class TrustedCoinPlugin(BasePlugin):
t.start() t.start()
return t return t
def make_seed(self): def make_seed(self, seed_type):
return Mnemonic('english').make_seed(seed_type='2fa', num_bits=128) 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 @hook
def do_clear(self, window): def do_clear(self, window):
@ -465,25 +494,40 @@ class TrustedCoinPlugin(BasePlugin):
title = _('Create or restore') title = _('Create or restore')
message = _('Do you want to create a new seed, or to restore a wallet using an existing seed?') message = _('Do you want to create a new seed, or to restore a wallet using an existing seed?')
choices = [ choices = [
('create_seed', _('Create a new seed')), ('choose_seed_type', _('Create a new seed')),
('restore_wallet', _('I already have a seed')), ('restore_wallet', _('I already have a seed')),
] ]
wizard.choice_dialog(title=title, message=message, choices=choices, run_next=wizard.run) wizard.choice_dialog(title=title, message=message, choices=choices, run_next=wizard.run)
def create_seed(self, wizard): def choose_seed_type(self, wizard):
seed = self.make_seed() choices = [
('create_2fa_segwit_seed', _('Segwit 2FA')),
('create_2fa_seed', _('Legacy 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) f = lambda x: wizard.request_passphrase(seed, x)
wizard.show_seed_dialog(run_next=f, seed_text=seed) wizard.show_seed_dialog(run_next=f, seed_text=seed)
@classmethod @classmethod
def get_xkeys(self, seed, passphrase, derivation): 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) 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) xprv, xpub = bip32_private_derivation(xprv, "m/", derivation)
return xprv, xpub return xprv, xpub
@classmethod @classmethod
def xkeys_from_seed(self, seed, passphrase): 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() words = seed.split()
n = len(words) n = len(words)
# old version use long seed phrases # old version use long seed phrases
@ -493,11 +537,11 @@ class TrustedCoinPlugin(BasePlugin):
# the probability of it being < 20 words is about 2^(-(256+12-19*11)) = 2^(-59) # the probability of it being < 20 words is about 2^(-(256+12-19*11)) = 2^(-59)
if passphrase != '': if passphrase != '':
raise Exception('old 2fa seed cannot have passphrase') raise Exception('old 2fa seed cannot have passphrase')
xprv1, xpub1 = self.get_xkeys(' '.join(words[0:12]), '', "m/") xprv1, xpub1 = self.get_xkeys(' '.join(words[0:12]), t, '', "m/")
xprv2, xpub2 = self.get_xkeys(' '.join(words[12:]), '', "m/") xprv2, xpub2 = self.get_xkeys(' '.join(words[12:]), t, '', "m/")
elif n==12: elif not t == '2fa' or n == 12:
xprv1, xpub1 = self.get_xkeys(seed, passphrase, "m/0'/") xprv1, xpub1 = self.get_xkeys(seed, t, passphrase, "m/0'/")
xprv2, xpub2 = self.get_xkeys(seed, passphrase, "m/1'/") xprv2, xpub2 = self.get_xkeys(seed, t, passphrase, "m/1'/")
else: else:
raise Exception('unrecognized seed length: {} words'.format(n)) raise Exception('unrecognized seed length: {} words'.format(n))
return xprv1, xpub1, xprv2, xpub2 return xprv1, xpub1, xprv2, xpub2
@ -561,7 +605,8 @@ class TrustedCoinPlugin(BasePlugin):
storage.put('x1/', k1.dump()) storage.put('x1/', k1.dump())
storage.put('x2/', k2.dump()) storage.put('x2/', k2.dump())
long_user_id, short_id = get_user_id(storage) 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) k3 = keystore.from_xpub(xpub3)
storage.put('x3/', k3.dump()) storage.put('x3/', k3.dump())
@ -578,7 +623,8 @@ class TrustedCoinPlugin(BasePlugin):
xpub2 = wizard.storage.get('x2/')['xpub'] xpub2 = wizard.storage.get('x2/')['xpub']
# Generate third key deterministically. # Generate third key deterministically.
long_user_id, short_id = get_user_id(wizard.storage) 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 # secret must be sent by the server
try: try:
r = server.create(xpub1, xpub2, email) r = server.create(xpub1, xpub2, email)

2
electrum/storage.py

@ -572,9 +572,7 @@ class WalletStorage(JsonDB):
# delete verified_tx3 as its structure changed # delete verified_tx3 as its structure changed
if not self._is_upgrade_method_needed(17, 17): if not self._is_upgrade_method_needed(17, 17):
return return
self.put('verified_tx3', None) self.put('verified_tx3', None)
self.put('seed_version', 18) self.put('seed_version', 18)
def convert_imported(self): def convert_imported(self):

5
electrum/tests/test_wallet_vertical.py

@ -4,7 +4,7 @@ import tempfile
from typing import Sequence from typing import Sequence
import asyncio import asyncio
from electrum import storage, bitcoin, keystore from electrum import storage, bitcoin, keystore, bip32
from electrum import Transaction from electrum import Transaction
from electrum import SimpleConfig from electrum import SimpleConfig
from electrum.address_synchronizer import TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT from electrum.address_synchronizer import TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT
@ -178,7 +178,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(SequentialTestCase):
long_user_id, short_id = trustedcoin.get_user_id( long_user_id, short_id = trustedcoin.get_user_id(
{'x1/': {'xpub': xpub1}, {'x1/': {'xpub': xpub1},
'x2/': {'xpub': xpub2}}) 'x2/': {'xpub': xpub2}})
xpub3 = trustedcoin.make_xpub(trustedcoin.get_signing_xpub(), long_user_id) xtype = bip32.xpub_type(xpub1)
xpub3 = trustedcoin.make_xpub(trustedcoin.get_signing_xpub(xtype), long_user_id)
ks3 = keystore.from_xpub(xpub3) ks3 = keystore.from_xpub(xpub3)
WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks3) WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks3)
self.assertTrue(isinstance(ks3, keystore.BIP32_KeyStore)) self.assertTrue(isinstance(ks3, keystore.BIP32_KeyStore))

9
electrum/version.py

@ -4,9 +4,10 @@ APK_VERSION = '3.3.0.0' # read by buildozer.spec
PROTOCOL_VERSION = '1.4' # protocol version requested PROTOCOL_VERSION = '1.4' # protocol version requested
# The hash of the mnemonic seed must begin with this # The hash of the mnemonic seed must begin with this
SEED_PREFIX = '01' # Standard wallet SEED_PREFIX = '01' # Standard wallet
SEED_PREFIX_2FA = '101' # Two-factor authentication SEED_PREFIX_SW = '100' # Segwit 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): def seed_prefix(seed_type):
@ -16,3 +17,5 @@ def seed_prefix(seed_type):
return SEED_PREFIX_SW return SEED_PREFIX_SW
elif seed_type == '2fa': elif seed_type == '2fa':
return SEED_PREFIX_2FA return SEED_PREFIX_2FA
elif seed_type == '2fa_segwit':
return SEED_PREFIX_2FA_SW

Loading…
Cancel
Save