electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 92a158b910b54a4e411eda1272ccc09d15553772
parent bf98b547e127d851005a45c716537b493ee05c93
Author: ThomasV <thomasv@gitorious>
Date:   Sat, 30 May 2015 18:49:58 +0200

proper handling of arg_types. add more options for listaddresses

Diffstat:
Melectrum | 32++------------------------------
Mlib/commands.py | 92++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mlib/wallet.py | 11+++++++++--
3 files changed, 72 insertions(+), 63 deletions(-)

diff --git a/electrum b/electrum @@ -181,6 +181,8 @@ def run_cmdline(config): # arguments passed to function args = map(lambda x: config.get(x), map(lambda x: x[0], cmd.params)) + # options + args += map(lambda x: config.get(x), cmd.options) # instanciate wallet for command-line storage = WalletStorage(config.get_wallet_path()) @@ -297,36 +299,6 @@ def run_cmdline(config): # See if they specificed a key on the cmd line, if not prompt args.append(prompt_password('Enter PrivateKey (will not echo):', False)) - elif cmd.name == 'createmultisig': - args = [int(args[0]), json.loads(args[1])] - - elif cmd.name == 'createrawtransaction': - args = [json.loads(args[0]), json.loads(args[2])] - - elif cmd.name == 'listaddresses': - args = [config.get('show_all'), config.get('show_labels')] - - elif cmd.name == 'make_seed': - args = [int(config.get('nbits')), long(config.get('entropy')), config.get('language')] - - elif cmd.name in ['payto', 'mktx']: - domain = [config.get('from_addr')] if config.get('from_addr') else None - args = [args[0], Decimal(args[1]), Decimal(config.get('tx_fee')) if config.get('tx_fee') else None, config.get('change_addr'), domain] - - elif cmd.name in ['paytomany', 'mksendmanytx']: - domain = [config.get('from_addr')] if config.get('from_addr') else None - args = [args[0], Decimal(config.get('tx_fee')) if config.get('tx_fee') else None, config.get('change_addr'), domain] - - elif cmd.name == 'help': - if len(args) < 1: - print_help(parser) - sys.exit(1) - - if cmd.name == 'check_seed': - args.append(long(config.get('entropy'))) - args.append(config.get('language')) - - # run the command if cmd.name == 'deseed': if not wallet.seed: diff --git a/lib/commands.py b/lib/commands.py @@ -21,6 +21,7 @@ import datetime import time import copy import argparse +import json from util import print_msg, format_satoshis, print_stderr from util import StoreDict @@ -57,8 +58,8 @@ def register_command(*args): # options register_command('listcontacts', 0, 0, 0, [], [], 'Show your list of contacts', '') register_command('create', 0, 1, 0, [], [], 'Create a new wallet', '') -register_command('createmultisig', 0, 1, 0, [('num','number'), ('pubkeys', 'Public keys (json)')], [], 'Create multisig address', '') -register_command('createrawtx', 0, 1, 0, [('inputs', ''), ('outputs', '')], [], 'Create an unsigned transaction.', 'The syntax is similar to bitcoind.') +register_command('createmultisig', 0, 1, 0, [('num', 'number'), ('pubkeys', 'Public keys (json)')], [], 'Create multisig address', '') +register_command('createrawtx', 0, 1, 0, [('inputs', 'json'), ('outputs', 'json')], [], 'Create an unsigned transaction.', 'The syntax is similar to bitcoind.') register_command('deseed', 0, 1, 0, [], [], 'Remove seed from wallet.', 'This creates a seedless, watching-only wallet.') register_command('decoderawtx', 0, 0, 0, [('tx', 'Serialized transaction')], [], 'Decode raw transaction.', '') register_command('getprivatekeys', 0, 1, 1, [('address', 'Bitcoin address')], [], 'Get the private keys of a wallet address.', '') @@ -77,7 +78,8 @@ register_command('help', 0, 0, 0, [], [], 'Print help on a command register_command('history', 1, 1, 0, [], [], 'Wallet history', 'Returns the transaction history of your wallet') register_command('importprivkey', 0, 1, 1, [('privkey', 'Private key')], [], 'Import a private key', '') register_command('ismine', 0, 1, 0, [('address', 'Bitcoin address')], [], 'Check if address is in wallet', 'Return true if and only if address is in wallet') -register_command('listaddresses', 0, 1, 0, [], ['show_all', 'show_labels'], 'List wallet addresses', 'Returns your list of addresses.') +register_command('listaddresses', 0, 1, 0, [], ['show_all', 'show_labels', 'frozen', 'unused', 'funded', 'show_balance'], + 'List wallet addresses', 'Returns your list of addresses.') register_command('listunspent', 1, 1, 0, [], [], 'List unspent outputs', 'Returns the list of unspent transaction outputs in your wallet.') register_command('getaddressunspent', 1, 0, 0, [('address', 'Bitcoin address')], [], 'Returns the list of unspent inputs for an address.', '') register_command('mktx', 0, 1, 1, [('recipient', 'Bitcoin address'), ('amount', 'Amount in BTC')], @@ -106,7 +108,8 @@ register_command('decrypt', 0, 1, 1, [('pubkey', 'public key'), ('mes register_command('getmerkle', 1, 0, 0, [('txid', 'Transaction ID'), ('height', 'Block height')], [], 'Get Merkle branch of a transaction included in a block', '') register_command('getproof', 1, 0, 0, [('address', '')], [], 'Get Merkle branch of an address in the UTXO set', '') register_command('getutxoaddress', 1, 0, 0, [('txid', 'Transction ID'), ('pos', 'Position')], [], 'Get the address of an unspent transaction output', '') -register_command('sweep', 1, 0, 0, [('privkey', 'Private key'), ('address', 'Destination address')], ['tx_fee'], 'Sweep a private key.', '') +register_command('sweep', 1, 0, 0, [('privkey', 'Private key'), ('address', 'Destination address')], ['tx_fee'], + 'Sweep private key.', 'Returns a transaction that sends all UTXOs to the destination address. The transactoin is not broadcasted.') register_command('make_seed', 0, 0, 0, [], ['nbits', 'entropy', 'language'], 'Create a seed.', '') register_command('check_seed', 0, 0, 0, [('seed', 'Seed phrase')], ['entropy', 'language'], 'Check that a seed was generated with external entropy.', '') @@ -115,19 +118,33 @@ register_command('check_seed', 0, 0, 0, [('seed', 'Seed phrase')], ['ent command_options = { 'password': ("-W", "--password", None, "Password"), 'concealed': ("-C", "--concealed", False, "Don't echo seed to console when restoring"), - 'show_all': ("-a", "--all", False, "Show all addresses"), + 'show_all': ("-a", "--all", False, "Include change addresses"), + 'frozen': (None, "--frozen", False, "Show only frozen addresses"), + 'unused': (None, "--unused", False, "Show only unused addresses"), + 'funded': (None, "--funded", False, "Show only funded addresses"), + 'show_balance':("-b", "--balance", False, "Show the balances of listed addresses"), 'show_labels': ("-l", "--labels", False, "Show the labels of listed addresses"), 'tx_fee': ("-f", "--fee", None, "Transaction fee"), 'from_addr': ("-F", "--fromaddr", None, "Source address. If it isn't in the wallet, it will ask for the private key unless supplied in the format public_key:private_key. It's not saved in the wallet."), 'change_addr': ("-c", "--changeaddr", None, "Change address. Default is a spare address, or the source address if it's not in the wallet"), - 'nbits': (None, "--nbits", "128", "Number of bits of entropy"), - 'entropy': (None, "--entropy", "1", "Custom entropy"), + 'nbits': (None, "--nbits", 128, "Number of bits of entropy"), + 'entropy': (None, "--entropy", 1, "Custom entropy"), 'language': ("-L", "--lang", None, "Default language for wordlist"), 'gap_limit': ("-G", "--gap", None, "Gap limit"), 'mpk': (None, "--mpk", None, "Restore from master public key"), } +arg_types = { + 'num':int, + 'nbits':int, + 'entropy':long, + 'pubkeys':json.loads, + 'inputs': json.loads, + 'outputs':json.loads, + 'tx_fee':lambda x: (Decimal(x) if x is not None else None) +} + def set_default_subparser(self, name, args=None): """see http://stackoverflow.com/questions/5176691/argparse-how-to-specify-a-default-subcommand""" @@ -197,9 +214,15 @@ def get_parser(run_gui, run_daemon, run_cmdline): a, b, default, help = command_options[optname] action = "store_true" if type(default) is bool else 'store' args = (a, b) if a else (b,) - p.add_argument(*args, dest=optname, action=action, default=default, help=help) + if action == 'store': + _type = arg_types.get(optname, str) + p.add_argument(*args, dest=optname, action=action, default=default, help=help, type=_type) + else: + p.add_argument(*args, dest=optname, action=action, default=default, help=help) + for param, h in cmd.params: - p.add_argument(param, help=h) + _type = arg_types.get(param, str) + p.add_argument(param, help=h, type=_type) # 'gui' is the default command parser.set_default_subparser('gui') return parser @@ -244,7 +267,7 @@ class Commands: return l def getaddressunspent(self, addr): - return self.network.synchronous_get([ ('blockchain.address.listunspent',[addr]) ])[0] + return self.network.synchronous_get([('blockchain.address.listunspent',[addr])])[0] def getutxoaddress(self, txid, num): r = self.network.synchronous_get([ ('blockchain.utxo.get_address',[txid, num]) ]) @@ -290,7 +313,7 @@ class Commands: return self.network.synchronous_get([('blockchain.transaction.broadcast', [str(tx)])])[0] def createmultisig(self, num, pubkeys): - assert isinstance(pubkeys, list) + assert isinstance(pubkeys, list), (type(num), type(pubkeys)) redeem_script = Transaction.multisig_script(pubkeys, num) address = hash_160_to_bc_address(hash_160(redeem_script.decode('hex')), 5) return {'address':address, 'redeemScript':redeem_script} @@ -421,10 +444,6 @@ class Commands: if fee is not None: fee = int(100000000*fee) return self.wallet.mktx(final_outputs, self.password, fee , change_addr, domain) - def mktx(self, to_address, amount, fee = None, change_addr = None, domain = None): - tx = self._mktx([(to_address, amount)], fee, change_addr, domain) - return tx - def _read_csv(self, csvpath): import csv outputs = [] @@ -437,17 +456,25 @@ class Commands: outputs.append((address, amount)) return outputs - def mktx_csv(self, path, fee = None, change_addr = None, domain = None): + def mktx(self, to_address, amount, fee = None, change_addr = None, from_addr = None): + domain = [from_addr] if from_addr else None + tx = self._mktx([(to_address, amount)], fee, change_addr, domain) + return tx + + def mktx_csv(self, path, fee = None, change_addr = None, from_addr = None): + domain = [from_addr] if from_addr else None outputs = self._read_csv(path) tx = self._mktx(outputs, fee, change_addr, domain) return tx - def payto(self, to_address, amount, fee = None, change_addr = None, domain = None): + def payto(self, to_address, amount, fee = None, change_addr = None, from_addr = None): + domain = [from_addr] if from_addr else None tx = self._mktx([(to_address, amount)], fee, change_addr, domain) r, h = self.wallet.sendtx( tx ) return h - def payto_csv(self, path, fee = None, change_addr = None, domain = None): + def payto_csv(self, path, fee = None, change_addr = None, from_addr = None): + domain = [from_addr] if from_addr else None outputs = self._read_csv(path) tx = self._mktx(outputs, fee, change_addr, domain) r, h = self.wallet.sendtx( tx ) @@ -483,19 +510,23 @@ class Commands: results[key] = value return results - def listaddresses(self, show_all = False, show_label = False): + def listaddresses(self, show_change=False, show_label=False, frozen=False, unused=False, funded=False, show_balance=False): out = [] for addr in self.wallet.addresses(True): - if show_all or not self.wallet.is_change(addr): - if show_label: - item = { 'address': addr } - if show_label: - label = self.wallet.labels.get(addr,'') - if label: - item['label'] = label - else: - item = addr - out.append( item ) + if frozen and not self.wallet.is_frozen(addr): + continue + if not show_change and self.wallet.is_change(addr): + continue + if unused and self.wallet.is_used(addr): + continue + if funded and self.wallet.is_empty(addr): + continue + item = addr + if show_balance: + item += ", "+ format_satoshis(sum(self.wallet.get_addr_balance(addr))) + if show_label: + item += ', ' + self.wallet.labels.get(addr,'') + out.append(item) return out def getrawtransaction(self, tx_hash): @@ -503,8 +534,7 @@ class Commands: tx = self.wallet.transactions.get(tx_hash) if tx: return tx - - raw = self.network.synchronous_get([ ('blockchain.transaction.get',[tx_hash]) ])[0] + raw = self.network.synchronous_get([('blockchain.transaction.get',[tx_hash])])[0] if raw: return Transaction(raw) else: diff --git a/lib/wallet.py b/lib/wallet.py @@ -1013,8 +1013,11 @@ class Abstract_Wallet(object): self.use_encryption = (new_password != None) self.storage.put('use_encryption', self.use_encryption,True) + def is_frozen(self, addr): + return addr in self.frozen_addresses + def freeze(self,addr): - if self.is_mine(addr) and addr not in self.frozen_addresses: + if self.is_mine(addr) and self.is_frozen(addr): self.frozen_addresses.append(addr) self.storage.put('frozen_addresses', self.frozen_addresses, True) return True @@ -1022,7 +1025,7 @@ class Abstract_Wallet(object): return False def unfreeze(self,addr): - if self.is_mine(addr) and addr in self.frozen_addresses: + if self.is_mine(addr) and self.is_frozen(addr): self.frozen_addresses.remove(addr) self.storage.put('frozen_addresses', self.frozen_addresses, True) return True @@ -1148,6 +1151,10 @@ class Abstract_Wallet(object): c, u, x = self.get_addr_balance(address) return len(h), len(h) > 0 and c == -u + def is_empty(self, address): + c, u, x = self.get_addr_balance(address) + return c+u+x == 0 + def address_is_old(self, address, age_limit=2): age = -1 h = self.history.get(address, [])