mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-02 18:25:21 +00:00
wizard: normalize bip32 derivation path
so that what gets put in storage is "canonical" (from now on... we could storage upgrade existing wallets but it's not critical)
This commit is contained in:
parent
85a7aa291e
commit
11733d6bc2
3 changed files with 49 additions and 4 deletions
|
@ -32,7 +32,7 @@ from typing import List, TYPE_CHECKING, Tuple, NamedTuple, Any
|
|||
from . import bitcoin
|
||||
from . import keystore
|
||||
from . import mnemonic
|
||||
from .bip32 import is_bip32_derivation, xpub_type
|
||||
from .bip32 import is_bip32_derivation, xpub_type, normalize_bip32_derivation
|
||||
from .keystore import bip44_derivation, purpose48_derivation
|
||||
from .wallet import (Imported_Wallet, Standard_Wallet, Multisig_Wallet,
|
||||
wallet_types, Wallet, Abstract_Wallet)
|
||||
|
@ -340,6 +340,7 @@ class BaseWizard(object):
|
|||
return
|
||||
if purpose == HWD_SETUP_NEW_WALLET:
|
||||
def f(derivation, script_type):
|
||||
derivation = normalize_bip32_derivation(derivation)
|
||||
self.run('on_hw_derivation', name, device_info, derivation, script_type)
|
||||
self.derivation_and_script_type_dialog(f)
|
||||
elif purpose == HWD_SETUP_DECRYPT_WALLET:
|
||||
|
@ -452,6 +453,7 @@ class BaseWizard(object):
|
|||
|
||||
def on_restore_bip39(self, seed, passphrase):
|
||||
def f(derivation, script_type):
|
||||
derivation = normalize_bip32_derivation(derivation)
|
||||
self.run('on_bip43', seed, passphrase, derivation, script_type)
|
||||
self.derivation_and_script_type_dialog(f)
|
||||
|
||||
|
|
|
@ -292,6 +292,8 @@ def convert_bip32_path_to_list_of_uint32(n: str) -> List[int]:
|
|||
x = x[:-1]
|
||||
prime = BIP32_PRIME
|
||||
if x.startswith('-'):
|
||||
if prime:
|
||||
raise ValueError(f"bip32 path child index is signalling hardened level in multiple ways")
|
||||
prime = BIP32_PRIME
|
||||
child_index = abs(int(x)) | prime
|
||||
if child_index > UINT32_MAX:
|
||||
|
@ -300,12 +302,36 @@ def convert_bip32_path_to_list_of_uint32(n: str) -> List[int]:
|
|||
return path
|
||||
|
||||
|
||||
def convert_bip32_intpath_to_strpath(path: List[int]) -> str:
|
||||
s = "m/"
|
||||
for child_index in path:
|
||||
if not isinstance(child_index, int):
|
||||
raise TypeError(f"bip32 path child index must be int: {child_index}")
|
||||
if not (0 <= child_index <= UINT32_MAX):
|
||||
raise ValueError(f"bip32 path child index out of range: {child_index}")
|
||||
prime = ""
|
||||
if child_index & BIP32_PRIME:
|
||||
prime = "'"
|
||||
child_index = child_index ^ BIP32_PRIME
|
||||
s += str(child_index) + prime + '/'
|
||||
# cut trailing "/"
|
||||
s = s[:-1]
|
||||
return s
|
||||
|
||||
|
||||
def is_bip32_derivation(s: str) -> bool:
|
||||
try:
|
||||
if not s.startswith('m/'):
|
||||
if not (s == 'm' or s.startswith('m/')):
|
||||
return False
|
||||
convert_bip32_path_to_list_of_uint32(s)
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def normalize_bip32_derivation(s: str) -> str:
|
||||
if not is_bip32_derivation(s):
|
||||
raise ValueError(f"invalid bip32 derivation: {s}")
|
||||
ints = convert_bip32_path_to_list_of_uint32(s)
|
||||
return convert_bip32_intpath_to_strpath(ints)
|
||||
|
|
|
@ -9,9 +9,10 @@ from electrum.bitcoin import (public_key_to_p2pkh, address_from_private_key,
|
|||
is_compressed_privkey, EncodeBase58Check,
|
||||
script_num_to_hex, push_script, add_number_to_script, int_to_hex,
|
||||
opcodes)
|
||||
from electrum.bip32 import (BIP32Node,
|
||||
from electrum.bip32 import (BIP32Node, convert_bip32_intpath_to_strpath,
|
||||
xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation,
|
||||
is_xpub, convert_bip32_path_to_list_of_uint32)
|
||||
is_xpub, convert_bip32_path_to_list_of_uint32,
|
||||
normalize_bip32_derivation)
|
||||
from electrum.crypto import sha256d, SUPPORTED_PW_HASH_VERSIONS
|
||||
from electrum import ecc, crypto, constants
|
||||
from electrum.ecc import number_to_string, string_to_number
|
||||
|
@ -463,18 +464,34 @@ class Test_xprv_xpub(SequentialTestCase):
|
|||
def test_is_bip32_derivation(self):
|
||||
self.assertTrue(is_bip32_derivation("m/0'/1"))
|
||||
self.assertTrue(is_bip32_derivation("m/0'/0'"))
|
||||
self.assertTrue(is_bip32_derivation("m/3'/-5/8h/"))
|
||||
self.assertTrue(is_bip32_derivation("m/44'/0'/0'/0/0"))
|
||||
self.assertTrue(is_bip32_derivation("m/49'/0'/0'/0/0"))
|
||||
self.assertTrue(is_bip32_derivation("m"))
|
||||
self.assertTrue(is_bip32_derivation("m/"))
|
||||
self.assertFalse(is_bip32_derivation("m5"))
|
||||
self.assertFalse(is_bip32_derivation("mmmmmm"))
|
||||
self.assertFalse(is_bip32_derivation("n/"))
|
||||
self.assertFalse(is_bip32_derivation(""))
|
||||
self.assertFalse(is_bip32_derivation("m/q8462"))
|
||||
self.assertFalse(is_bip32_derivation("m/-8h"))
|
||||
|
||||
def test_convert_bip32_path_to_list_of_uint32(self):
|
||||
self.assertEqual([0, 0x80000001, 0x80000001], convert_bip32_path_to_list_of_uint32("m/0/-1/1'"))
|
||||
self.assertEqual([], convert_bip32_path_to_list_of_uint32("m/"))
|
||||
self.assertEqual([2147483692, 2147488889, 221], convert_bip32_path_to_list_of_uint32("m/44'/5241h/221"))
|
||||
|
||||
def test_convert_bip32_intpath_to_strpath(self):
|
||||
self.assertEqual("m/0/1'/1'", convert_bip32_intpath_to_strpath([0, 0x80000001, 0x80000001]))
|
||||
self.assertEqual("m", convert_bip32_intpath_to_strpath([]))
|
||||
self.assertEqual("m/44'/5241'/221", convert_bip32_intpath_to_strpath([2147483692, 2147488889, 221]))
|
||||
|
||||
def test_normalize_bip32_derivation(self):
|
||||
self.assertEqual("m/0/1'/1'", normalize_bip32_derivation("m/0/1h/1'"))
|
||||
self.assertEqual("m", normalize_bip32_derivation("m////"))
|
||||
self.assertEqual("m/0/2/1'", normalize_bip32_derivation("m/0/2/-1/"))
|
||||
self.assertEqual("m/0/1'/1'/5'", normalize_bip32_derivation("m/0//-1/1'///5h"))
|
||||
|
||||
def test_xtype_from_derivation(self):
|
||||
self.assertEqual('standard', xtype_from_derivation("m/44'"))
|
||||
self.assertEqual('standard', xtype_from_derivation("m/44'/"))
|
||||
|
|
Loading…
Add table
Reference in a new issue