|
@ -43,9 +43,6 @@ class KeyStore(PrintError): |
|
|
def has_seed(self): |
|
|
def has_seed(self): |
|
|
return False |
|
|
return False |
|
|
|
|
|
|
|
|
def has_password(self): |
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
def is_watching_only(self): |
|
|
def is_watching_only(self): |
|
|
return False |
|
|
return False |
|
|
|
|
|
|
|
@ -57,10 +54,6 @@ class Software_KeyStore(KeyStore): |
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
def __init__(self): |
|
|
KeyStore.__init__(self) |
|
|
KeyStore.__init__(self) |
|
|
self.use_encryption = False |
|
|
|
|
|
|
|
|
|
|
|
def has_password(self): |
|
|
|
|
|
return self.use_encryption |
|
|
|
|
|
|
|
|
|
|
|
def sign_message(self, sequence, message, password): |
|
|
def sign_message(self, sequence, message, password): |
|
|
sec = self.get_private_key(sequence, password) |
|
|
sec = self.get_private_key(sequence, password) |
|
@ -79,9 +72,11 @@ class Software_KeyStore(KeyStore): |
|
|
class Imported_KeyStore(Software_KeyStore): |
|
|
class Imported_KeyStore(Software_KeyStore): |
|
|
# keystore for imported private keys |
|
|
# keystore for imported private keys |
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
def __init__(self, d): |
|
|
Software_KeyStore.__init__(self) |
|
|
Software_KeyStore.__init__(self) |
|
|
self.keypairs = {} |
|
|
self.keypairs = d.get('keypairs', {}) |
|
|
|
|
|
self.receiving_pubkeys = self.keypairs.keys() |
|
|
|
|
|
self.change_pubkeys = [] |
|
|
|
|
|
|
|
|
def is_deterministic(self): |
|
|
def is_deterministic(self): |
|
|
return False |
|
|
return False |
|
@ -92,16 +87,11 @@ class Imported_KeyStore(Software_KeyStore): |
|
|
def get_master_public_key(self): |
|
|
def get_master_public_key(self): |
|
|
return None |
|
|
return None |
|
|
|
|
|
|
|
|
def load(self, storage, name): |
|
|
def dump(self): |
|
|
self.keypairs = storage.get('keypairs', {}) |
|
|
return { |
|
|
self.use_encryption = storage.get('use_encryption', False) |
|
|
'type': 'imported', |
|
|
self.receiving_pubkeys = self.keypairs.keys() |
|
|
'keypairs': self.keypairs, |
|
|
self.change_pubkeys = [] |
|
|
} |
|
|
|
|
|
|
|
|
def save(self, storage, root_name): |
|
|
|
|
|
storage.put('key_type', 'imported') |
|
|
|
|
|
storage.put('keypairs', self.keypairs) |
|
|
|
|
|
storage.put('use_encryption', self.use_encryption) |
|
|
|
|
|
|
|
|
|
|
|
def can_import(self): |
|
|
def can_import(self): |
|
|
return True |
|
|
return True |
|
@ -148,25 +138,21 @@ class Imported_KeyStore(Software_KeyStore): |
|
|
b = pw_decode(v, old_password) |
|
|
b = pw_decode(v, old_password) |
|
|
c = pw_encode(b, new_password) |
|
|
c = pw_encode(b, new_password) |
|
|
self.keypairs[k] = b |
|
|
self.keypairs[k] = b |
|
|
self.use_encryption = (new_password is not None) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Deterministic_KeyStore(Software_KeyStore): |
|
|
class Deterministic_KeyStore(Software_KeyStore): |
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
def __init__(self, d): |
|
|
Software_KeyStore.__init__(self) |
|
|
Software_KeyStore.__init__(self) |
|
|
self.seed = '' |
|
|
self.seed = d.get('seed', '') |
|
|
|
|
|
|
|
|
def is_deterministic(self): |
|
|
def is_deterministic(self): |
|
|
return True |
|
|
return True |
|
|
|
|
|
|
|
|
def load(self, storage, name): |
|
|
def dump(self): |
|
|
self.seed = storage.get('seed', '') |
|
|
return { |
|
|
self.use_encryption = storage.get('use_encryption', False) |
|
|
'seed': self.seed, |
|
|
|
|
|
} |
|
|
def save(self, storage, name): |
|
|
|
|
|
storage.put('seed', self.seed) |
|
|
|
|
|
storage.put('use_encryption', self.use_encryption) |
|
|
|
|
|
|
|
|
|
|
|
def has_seed(self): |
|
|
def has_seed(self): |
|
|
return self.seed != '' |
|
|
return self.seed != '' |
|
@ -180,7 +166,6 @@ class Deterministic_KeyStore(Software_KeyStore): |
|
|
self.seed_version, self.seed = self.format_seed(seed) |
|
|
self.seed_version, self.seed = self.format_seed(seed) |
|
|
if password: |
|
|
if password: |
|
|
self.seed = pw_encode(self.seed, password) |
|
|
self.seed = pw_encode(self.seed, password) |
|
|
self.use_encryption = (password is not None) |
|
|
|
|
|
|
|
|
|
|
|
def get_seed(self, password): |
|
|
def get_seed(self, password): |
|
|
return pw_decode(self.seed, password).encode('utf8') |
|
|
return pw_decode(self.seed, password).encode('utf8') |
|
@ -235,27 +220,21 @@ class Xpub: |
|
|
|
|
|
|
|
|
class BIP32_KeyStore(Deterministic_KeyStore, Xpub): |
|
|
class BIP32_KeyStore(Deterministic_KeyStore, Xpub): |
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
def __init__(self, d): |
|
|
Xpub.__init__(self) |
|
|
Xpub.__init__(self) |
|
|
Deterministic_KeyStore.__init__(self) |
|
|
Deterministic_KeyStore.__init__(self, d) |
|
|
self.xprv = None |
|
|
self.xpub = d.get('xpub') |
|
|
|
|
|
self.xprv = d.get('xprv') |
|
|
|
|
|
|
|
|
def format_seed(self, seed): |
|
|
def format_seed(self, seed): |
|
|
return NEW_SEED_VERSION, ' '.join(seed.split()) |
|
|
return NEW_SEED_VERSION, ' '.join(seed.split()) |
|
|
|
|
|
|
|
|
def load(self, storage, name): |
|
|
def dump(self): |
|
|
Deterministic_KeyStore.load(self, storage, name) |
|
|
d = Deterministic_KeyStore.dump(self) |
|
|
self.xpub = storage.get('master_public_keys', {}).get(name) |
|
|
d['type'] = 'bip32' |
|
|
self.xprv = storage.get('master_private_keys', {}).get(name) |
|
|
d['xpub'] = self.xpub |
|
|
|
|
|
d['xprv'] = self.xprv |
|
|
def save(self, storage, name): |
|
|
return d |
|
|
Deterministic_KeyStore.save(self, storage, name) |
|
|
|
|
|
d = storage.get('master_public_keys', {}) |
|
|
|
|
|
d[name] = self.xpub |
|
|
|
|
|
storage.put('master_public_keys', d) |
|
|
|
|
|
d = storage.get('master_private_keys', {}) |
|
|
|
|
|
d[name] = self.xprv |
|
|
|
|
|
storage.put('master_private_keys', d) |
|
|
|
|
|
|
|
|
|
|
|
def add_master_private_key(self, xprv, password): |
|
|
def add_master_private_key(self, xprv, password): |
|
|
self.xprv = pw_encode(xprv, password) |
|
|
self.xprv = pw_encode(xprv, password) |
|
@ -279,7 +258,6 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub): |
|
|
if self.xprv is not None: |
|
|
if self.xprv is not None: |
|
|
b = pw_decode(self.xprv, old_password) |
|
|
b = pw_decode(self.xprv, old_password) |
|
|
self.xprv = pw_encode(b, new_password) |
|
|
self.xprv = pw_encode(b, new_password) |
|
|
self.use_encryption = (new_password is not None) |
|
|
|
|
|
|
|
|
|
|
|
def is_watching_only(self): |
|
|
def is_watching_only(self): |
|
|
return self.xprv is None |
|
|
return self.xprv is None |
|
@ -340,18 +318,14 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub): |
|
|
|
|
|
|
|
|
class Old_KeyStore(Deterministic_KeyStore): |
|
|
class Old_KeyStore(Deterministic_KeyStore): |
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
def __init__(self, d): |
|
|
Deterministic_KeyStore.__init__(self) |
|
|
Deterministic_KeyStore.__init__(self, d) |
|
|
self.mpk = None |
|
|
self.mpk = d.get('mpk').decode('hex') |
|
|
|
|
|
|
|
|
def load(self, storage, name): |
|
|
|
|
|
Deterministic_KeyStore.load(self, storage, name) |
|
|
|
|
|
self.mpk = storage.get('master_public_key').decode('hex') |
|
|
|
|
|
|
|
|
|
|
|
def save(self, storage, name): |
|
|
def dump(self): |
|
|
Deterministic_KeyStore.save(self, storage, name) |
|
|
d = Deterministic_KeyStore.dump(self) |
|
|
storage.put('wallet_type', 'old') |
|
|
d['mpk'] = self.mpk.encode('hex') |
|
|
storage.put('master_public_key', self.mpk.encode('hex')) |
|
|
return d |
|
|
|
|
|
|
|
|
def add_seed(self, seed, password): |
|
|
def add_seed(self, seed, password): |
|
|
Deterministic_KeyStore.add_seed(self, seed, password) |
|
|
Deterministic_KeyStore.add_seed(self, seed, password) |
|
@ -473,7 +447,6 @@ class Old_KeyStore(Deterministic_KeyStore): |
|
|
if self.has_seed(): |
|
|
if self.has_seed(): |
|
|
decoded = self.get_seed(old_password) |
|
|
decoded = self.get_seed(old_password) |
|
|
self.seed = pw_encode(decoded, new_password) |
|
|
self.seed = pw_encode(decoded, new_password) |
|
|
self.use_encryption = (new_password is not None) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Hardware_KeyStore(KeyStore, Xpub): |
|
|
class Hardware_KeyStore(KeyStore, Xpub): |
|
@ -485,24 +458,26 @@ class Hardware_KeyStore(KeyStore, Xpub): |
|
|
#restore_wallet_class = BIP32_RD_Wallet |
|
|
#restore_wallet_class = BIP32_RD_Wallet |
|
|
max_change_outputs = 1 |
|
|
max_change_outputs = 1 |
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
def __init__(self, d): |
|
|
Xpub.__init__(self) |
|
|
Xpub.__init__(self) |
|
|
KeyStore.__init__(self) |
|
|
KeyStore.__init__(self) |
|
|
# Errors and other user interaction is done through the wallet's |
|
|
# Errors and other user interaction is done through the wallet's |
|
|
# handler. The handler is per-window and preserved across |
|
|
# handler. The handler is per-window and preserved across |
|
|
# device reconnects |
|
|
# device reconnects |
|
|
|
|
|
self.xpub = d.get('xpub') |
|
|
|
|
|
self.derivation = d.get('derivation') |
|
|
self.handler = None |
|
|
self.handler = None |
|
|
|
|
|
|
|
|
def is_deterministic(self): |
|
|
def is_deterministic(self): |
|
|
return True |
|
|
return True |
|
|
|
|
|
|
|
|
def load(self, storage, name): |
|
|
def dump(self): |
|
|
self.xpub = storage.get('master_public_keys', {}).get(name) |
|
|
return { |
|
|
|
|
|
'type': 'hardware', |
|
|
def save(self, storage, name): |
|
|
'hw_type': self.hw_type, |
|
|
d = storage.get('master_public_keys', {}) |
|
|
'xpub': self.xpub, |
|
|
d[name] = self.xpub |
|
|
'derivation':self.derivation, |
|
|
storage.put('master_public_keys', d) |
|
|
} |
|
|
|
|
|
|
|
|
def unpaired(self): |
|
|
def unpaired(self): |
|
|
'''A device paired with the wallet was diconnected. This can be |
|
|
'''A device paired with the wallet was diconnected. This can be |
|
@ -572,37 +547,37 @@ def xpubkey_to_address(x_pubkey): |
|
|
return pubkey, address |
|
|
return pubkey, address |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
keystores = [] |
|
|
hw_keystores = {} |
|
|
|
|
|
|
|
|
|
|
|
def register_keystore(hw_type, constructor): |
|
|
|
|
|
hw_keystores[hw_type] = constructor |
|
|
|
|
|
|
|
|
|
|
|
def hardware_keystore(hw_type, d): |
|
|
|
|
|
if hw_type in hw_keystores: |
|
|
|
|
|
constructor = hw_keystores[hw_type] |
|
|
|
|
|
return constructor(d) |
|
|
|
|
|
raise BaseException('unknown hardware type', hw_type) |
|
|
|
|
|
|
|
|
def load_keystore(storage, name): |
|
|
def load_keystore(storage, name): |
|
|
w = storage.get('wallet_type') |
|
|
w = storage.get('wallet_type', 'standard') |
|
|
t = storage.get('key_type', 'seed') |
|
|
d = storage.get(name, {}) |
|
|
seed_version = storage.get_seed_version() |
|
|
t = d.get('type') |
|
|
if seed_version == OLD_SEED_VERSION or w == 'old': |
|
|
if not t: |
|
|
k = Old_KeyStore() |
|
|
raise BaseException('wallet format requires update') |
|
|
|
|
|
if t == 'old': |
|
|
|
|
|
k = Old_KeyStore(d) |
|
|
elif t == 'imported': |
|
|
elif t == 'imported': |
|
|
k = Imported_KeyStore() |
|
|
k = Imported_KeyStore(d) |
|
|
elif name and name not in [ 'x/', 'x1/' ]: |
|
|
elif t == 'bip32': |
|
|
k = BIP32_KeyStore() |
|
|
k = BIP32_KeyStore(d) |
|
|
elif t in ['seed', 'hw_seed']: |
|
|
|
|
|
k = BIP32_KeyStore() |
|
|
|
|
|
elif t == 'hardware': |
|
|
elif t == 'hardware': |
|
|
hw_type = storage.get('hardware_type') |
|
|
hw_type = d.get('hw_type') |
|
|
for cat, _type, constructor in keystores: |
|
|
k = hardware_keystore(hw_type, d) |
|
|
if cat == 'hardware' and _type == hw_type: |
|
|
|
|
|
k = constructor() |
|
|
|
|
|
break |
|
|
|
|
|
else: |
|
|
|
|
|
raise BaseException('unknown hardware type') |
|
|
|
|
|
else: |
|
|
else: |
|
|
raise BaseException('unknown wallet type', t) |
|
|
raise BaseException('unknown wallet type', t) |
|
|
k.load(storage, name) |
|
|
|
|
|
return k |
|
|
return k |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def register_keystore(category, type, constructor): |
|
|
|
|
|
keystores.append((category, type, constructor)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_old_mpk(mpk): |
|
|
def is_old_mpk(mpk): |
|
|
try: |
|
|
try: |
|
@ -649,35 +624,35 @@ def bip44_derivation(account_id): |
|
|
|
|
|
|
|
|
def from_seed(seed, password): |
|
|
def from_seed(seed, password): |
|
|
if is_old_seed(seed): |
|
|
if is_old_seed(seed): |
|
|
keystore = Old_KeyStore() |
|
|
keystore = Old_KeyStore({}) |
|
|
keystore.add_seed(seed, password) |
|
|
keystore.add_seed(seed, password) |
|
|
elif is_new_seed(seed): |
|
|
elif is_new_seed(seed): |
|
|
keystore = BIP32_KeyStore() |
|
|
keystore = BIP32_KeyStore({}) |
|
|
keystore.add_seed(seed, password) |
|
|
keystore.add_seed(seed, password) |
|
|
bip32_seed = Mnemonic.mnemonic_to_seed(seed, '') |
|
|
bip32_seed = Mnemonic.mnemonic_to_seed(seed, '') |
|
|
keystore.add_xprv_from_seed(bip32_seed, "m/", password) |
|
|
keystore.add_xprv_from_seed(bip32_seed, "m/", password) |
|
|
return keystore |
|
|
return keystore |
|
|
|
|
|
|
|
|
def from_private_key_list(text, password): |
|
|
def from_private_key_list(text, password): |
|
|
keystore = Imported_KeyStore() |
|
|
keystore = Imported_KeyStore({}) |
|
|
for x in text.split(): |
|
|
for x in text.split(): |
|
|
keystore.import_key(x, None) |
|
|
keystore.import_key(x, None) |
|
|
keystore.update_password(None, password) |
|
|
keystore.update_password(None, password) |
|
|
return keystore |
|
|
return keystore |
|
|
|
|
|
|
|
|
def from_old_mpk(mpk): |
|
|
def from_old_mpk(mpk): |
|
|
keystore = Old_KeyStore() |
|
|
keystore = Old_KeyStore({}) |
|
|
keystore.add_master_public_key(mpk) |
|
|
keystore.add_master_public_key(mpk) |
|
|
return keystore |
|
|
return keystore |
|
|
|
|
|
|
|
|
def from_xpub(xpub): |
|
|
def from_xpub(xpub): |
|
|
keystore = BIP32_KeyStore() |
|
|
keystore = BIP32_KeyStore({}) |
|
|
keystore.add_master_public_key(xpub) |
|
|
keystore.add_master_public_key(xpub) |
|
|
return keystore |
|
|
return keystore |
|
|
|
|
|
|
|
|
def from_xprv(xprv, password): |
|
|
def from_xprv(xprv, password): |
|
|
xpub = bitcoin.xpub_from_xprv(xprv) |
|
|
xpub = bitcoin.xpub_from_xprv(xprv) |
|
|
keystore = BIP32_KeyStore() |
|
|
keystore = BIP32_KeyStore({}) |
|
|
keystore.add_master_private_key(xprv, password) |
|
|
keystore.add_master_private_key(xprv, password) |
|
|
keystore.add_master_public_key(xpub) |
|
|
keystore.add_master_public_key(xpub) |
|
|
return keystore |
|
|
return keystore |
|
|