mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-31 01:11:35 +00:00
2fa segwit (from ghost43's PR)
This commit is contained in:
parent
0ec7005f90
commit
5a93bf054e
5 changed files with 74 additions and 34 deletions
|
@ -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')
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue