from util import print_msg, print_error, format_satoshis
from bitcoin import *
from account import *
-from transaction import Transaction
+from transaction import Transaction, is_extended_pubkey
from plugins import run_hook
import bitcoin
from synchronizer import WalletSynchronizer
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)
return self.accounts[account_id].get_pubkeys(sequence)
- def add_keypairs_from_wallet(self, tx, keypairs, password):
+ def add_keypairs(self, tx, keypairs, password):
+ # first check the provided password
+ seed = self.get_seed(password)
+
for txin in tx.inputs:
+ x_pubkeys = txin['x_pubkeys']
address = txin['address']
- if not self.is_mine(address):
- continue
- private_keys = self.get_private_key(address, password)
- for sec in private_keys:
- pubkey = public_key_from_private_key(sec)
- keypairs[ pubkey ] = sec
+ if self.is_mine(address):
+ private_keys = self.get_private_key(address, password)
+ for sec in private_keys:
+ pubkey = public_key_from_private_key(sec)
+ keypairs[ pubkey ] = sec
- def add_keypairs_from_KeyID(self, tx, keypairs, password):
- # first check the provided password
- seed = self.get_seed(password)
+ else:
- for txin in tx.inputs:
- keyid = txin.get('KeyID')
- if keyid:
- roots = []
- for s in keyid.split('&'):
- m = re.match("bip32\((.*),(/\d+/\d+)\)", s)
- if not m: continue
- xpub = m.group(1)
- sequence = m.group(2)
- root = self.find_root_by_master_key(xpub)
- if not root: continue
- sequence = map(lambda x:int(x), sequence.strip('/').split('/'))
- root = root + '%d'%sequence[0]
- sequence = sequence[1:]
- roots.append((root,sequence))
-
- account_id = " & ".join( map(lambda x:x[0], roots) )
- account = self.accounts.get(account_id)
- if not account: continue
- addr = account.get_address(*sequence)
- txin['address'] = addr # fixme: side effect
- pk = self.get_private_key(addr, password)
- for sec in pk:
- pubkey = public_key_from_private_key(sec)
- keypairs[pubkey] = sec
+ from account import BIP32_Account, OldAccount
+ for x_pubkey in x_pubkeys:
+ if not is_extended_pubkey(x_pubkey):
+ continue
+ if x_pubkey[0:2] == 'ff':
+ xpub, sequence = BIP32_Account.parse_xpubkey(x_pubkey)
+ elif x_pubkey[0:2] == 'fe':
+ xpub, sequence = OldAccount.parse_xpubkey(x_pubkey)
+ # look for account that can sign
+ for k, account in self.accounts.items():
+ if xpub in account.get_master_pubkeys():
+ break
+ else:
+ continue
+
+ addr = account.get_address(*sequence)
+ assert txin['address'] == addr
+ pk = self.get_private_key(addr, password)
+ for sec in pk:
+ pubkey = public_key_from_private_key(sec)
+ keypairs[pubkey] = sec
- def signrawtransaction(self, tx, input_info, private_keys, password):
- # check that the password is correct
- seed = self.get_seed(password)
- # if input_info is not known, build it using wallet UTXOs
- if not input_info:
- input_info = []
- unspent_coins = self.get_unspent_coins()
- for txin in tx.inputs:
- for item in unspent_coins:
- if txin['prevout_hash'] == item['prevout_hash'] and txin['prevout_n'] == item['prevout_n']:
- info = { 'address':item['address'], 'scriptPubKey':item['scriptPubKey'] }
- self.add_input_info(info)
- input_info.append(info)
- break
- else:
- print_error( "input not in UTXOs" )
- input_info.append(None)
- # add input_info to the transaction
- print_error("input_info", input_info)
- tx.add_input_info(input_info)
+ def signrawtransaction(self, tx, private_keys, password):
+
+ # check that the password is correct
+ seed = self.get_seed(password)
# build a list of public/private keys
keypairs = {}
pubkey = public_key_from_private_key(sec)
keypairs[ pubkey ] = sec
- # add private_keys from KeyID
- self.add_keypairs_from_KeyID(tx, keypairs, password)
- # add private keys from wallet
- self.add_keypairs_from_wallet(tx, keypairs, password)
+ # add private_keys
+ self.add_keypairs(tx, keypairs, password)
+
# sign the transaction
self.sign_transaction(tx, keypairs, password)
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)
+ self.add_keypairs(tx, keypairs, password)
if keypairs:
self.sign_transaction(tx, keypairs, password)
return tx
address = txin['address']
account_id, sequence = self.get_address_index(address)
account = self.accounts[account_id]
- txin['KeyID'] = account.get_keyID(sequence)
redeemScript = account.redeem_script(sequence)
+ txin['x_pubkeys'] = account.get_xpubkeys(sequence)
+ txin['pubkeys'] = pubkeys = account.get_pubkeys(sequence)
+ txin['signatures'] = [None] * len(pubkeys)
+
if redeemScript:
txin['redeemScript'] = redeemScript
+ txin['num_sig'] = 2
else:
txin['redeemPubkey'] = account.get_pubkey(*sequence)
+ txin['num_sig'] = 1
def sign_transaction(self, tx, keypairs, password):
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):
s = self.get_seed(password)
return ' '.join(mnemonic.mn_encode(s))
-
- 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:
- m = re.match("old\(([0-9a-f]+),(\d+),(\d+)", keyid)
- if not m: continue
- mpk = m.group(1)
- if mpk != self.storage.get('master_public_key'): continue
- for_change = int(m.group(2))
- num = int(m.group(3))
- 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
-
-
-
def check_pending_accounts(self):
pass