with self.lock:
if value is not None:
self.data[key] = value
- else:
+ elif key in self.data:
self.data.pop(key)
if save:
self.write()
def get_action(self):
pass
+
+ def convert_imported_keys(self, password):
+ for k, v in self.imported_keys.items():
+ sec = pw_decode(v, password)
+ pubkey = public_key_from_private_key(sec)
+ address = public_key_to_bc_address(pubkey.decode('hex'))
+ assert address == k
+ self.import_key(sec, password)
+ self.imported_keys.pop(k)
+ self.storage.put('imported_keys', self.imported_keys)
+
+
def load_accounts(self):
self.accounts = {}
self.imported_keys = self.storage.get('imported_keys',{})
- if self.imported_keys:
- print_error("cannot load imported keys")
d = self.storage.get('accounts', {})
for k, v in d.items():
else:
return False
+ def has_imported_keys(self):
+ account = self.accounts.get(IMPORTED_ACCOUNT)
+ return account is not None
+
def import_key(self, sec, password):
try:
pubkey = public_key_from_private_key(sec)
secret = keys[0]
ec = regenerate_key(secret)
decrypted = ec.decrypt_message(message)
- return decrypted[0]
+ return decrypted
return [x[1] for x in coins]
- def choose_tx_inputs( self, amount, fixed_fee, num_outputs, domain = None ):
+ def choose_tx_inputs( self, amount, fixed_fee, num_outputs, domain = None, coins = None ):
""" todo: minimize tx size """
total = 0
fee = self.fee if fixed_fee is None else fixed_fee
- if domain is None:
- domain = self.addresses(True)
- for i in self.frozen_addresses:
- if i in domain: domain.remove(i)
+ if not coins:
+ if domain is None:
+ domain = self.addresses(True)
+ for i in self.frozen_addresses:
+ if i in domain: domain.remove(i)
+ coins = self.get_unspent_coins(domain)
- coins = self.get_unspent_coins(domain)
inputs = []
for item in coins:
return default_label
- def make_unsigned_transaction(self, outputs, fee=None, change_addr=None, domain=None ):
+ def make_unsigned_transaction(self, outputs, fee=None, change_addr=None, domain=None, coins=None ):
for address, x in outputs:
assert is_valid(address), "Address " + address + " is invalid!"
amount = sum( map(lambda x:x[1], outputs) )
- inputs, total, fee = self.choose_tx_inputs( amount, fee, len(outputs), domain )
+ inputs, total, fee = self.choose_tx_inputs( amount, fee, len(outputs), domain, coins )
if not inputs:
raise ValueError("Not enough funds")
for txin in inputs:
return Transaction.from_io(inputs, outputs)
- def mktx(self, outputs, password, fee=None, change_addr=None, domain= None ):
- tx = self.make_unsigned_transaction(outputs, fee, change_addr, domain)
+ def mktx(self, outputs, password, fee=None, change_addr=None, domain= None, coins = None ):
+ tx = self.make_unsigned_transaction(outputs, fee, change_addr, domain, coins)
keypairs = {}
self.add_keypairs_from_wallet(tx, keypairs, password)
if keypairs:
d[k] = v.dump()
self.storage.put('accounts', d, True)
+ def can_import(self):
+ return not self.is_watching_only()
+
+ def is_used(self, address):
+ h = self.history.get(address,[])
+ c, u = self.get_addr_balance(address)
+ return len(h), len(h) > 0 and c == -u
class Imported_Wallet(Abstract_Wallet):
a = self.accounts.get(IMPORTED_ACCOUNT)
if not a:
self.accounts[IMPORTED_ACCOUNT] = ImportedAccount({'imported':{}})
+ self.storage.put('wallet_type', 'imported', True)
def is_watching_only(self):
def check_password(self, password):
self.accounts[IMPORTED_ACCOUNT].get_private_key((0,0), self, password)
+ def is_used(self, address):
+ h = self.history.get(address,[])
+ return len(h), False
class Deterministic_Wallet(Abstract_Wallet):
def num_accounts(self):
- keys = self.accounts.keys()
+ keys = []
+ for k, v in self.accounts.items():
+ if type(v) != BIP32_Account:
+ continue
+ keys.append(k)
+
i = 0
while True:
account_id = self.account_id(i)
NewWallet.__init__(self, storage)
self.storage.put('wallet_type', '2of2', True)
+ def can_create_accounts(self):
+ return False
+
+ def can_import(self):
+ return False
+
def create_account(self):
xpub1 = self.master_public_keys.get("m/")
xpub2 = self.master_public_keys.get("cold/")
xpub1 = self.master_public_keys.get("m/")
xpub2 = self.master_public_keys.get("cold/")
xpub3 = self.master_public_keys.get("remote/")
- if xpub2 is None:
- return 'create_2of3_1'
+ # fixme: we use order of creation
+ if xpub2 and xpub1 is None:
+ return 'create_2fa_2'
if xpub1 is None:
+ return 'create_2of3_1'
+ if xpub2 is None or xpub3 is None:
return 'create_2of3_2'
- if xpub3 is None:
- return 'create_2of3_3'
def create_master_keys(self, password):
- seed = pw_decode(self.seed, password)
+ seed = self.get_seed(password)
mpk = OldAccount.mpk_from_seed(seed)
self.storage.put('master_public_key', mpk, True)
return seed
def check_password(self, password):
- seed = pw_decode(self.seed, password)
+ seed = self.get_seed(password)
self.accounts[0].check_seed(seed)
def get_mnemonic(self, password):
def add_keypairs_from_KeyID(self, tx, keypairs, password):
# first check the provided password
- seed = self.get_seed(password)
for txin in tx.inputs:
keyid = txin.get('KeyID')
if keyid:
account = self.accounts[0]
addr = account.get_address(for_change, num)
txin['address'] = addr # fixme: side effect
- pk = account.get_private_key(seed, (for_change, num))
- pubkey = public_key_from_private_key(pk)
- keypairs[pubkey] = pk
+ pk = account.get_private_key((for_change, num), self, password)
+ for sec in pk:
+ pubkey = public_key_from_private_key(sec)
+ keypairs[pubkey] = sec
if storage.get('wallet_type') == '2of3':
return Wallet_2of3(storage)
- if storage.file_exists and not storage.get('seed'):
- # wallet made of imported keys
+ if storage.get('wallet_type') == 'imported':
return Imported_Wallet(storage)
@classmethod
def is_address(self, text):
+ if not text:
+ return False
for x in text.split():
if not bitcoin.is_address(x):
return False
@classmethod
def is_private_key(self, text):
+ if not text:
+ return False
for x in text.split():
if not bitcoin.is_private_key(x):
return False