diff -uNr a/gbw-node/command/gbw-node b/gbw-node/command/gbw-node --- a/gbw-node/command/gbw-node b6ca6ebd3c45dcada137d82aba3d87c5717caae34fabcd0a13affa1dc93eb0ae9474d9c138443e7dcc9de9ecf607e4171db0b4a36fb8b37d87b66be7828b2d47 +++ b/gbw-node/command/gbw-node 8265c25fc3dd65a66078c2bc3a8e7111afa94e76b679de94d245c9da1a2cb3484e72d3bcfbd2889e7fb06c9c99448175d31b841bed91f5b5c5755f7afbbdde17 @@ -306,7 +306,7 @@ def a2b_base58(data): digits = [base58_inverse[ord(b)] for b in data] if None in digits: - raise BadDigit + raise BadDigit(data[digits.index(None)], data) leading_zeros = 0 for digit in digits: @@ -326,12 +326,12 @@ return ''.join(chr(b) for b in reversed(data_bytes)) -def a2b_base58check(data): - data = a2b_base58(data) - payload = data[:-4] - check = data[-4:] +def a2b_base58check(a): + b = a2b_base58(a) + payload = b[:-4] + check = b[-4:] if check != sha256d(payload)[:4]: - raise BadChecksum + raise BadChecksum(a) return payload class BadAddressLength(ValueError): @@ -343,9 +343,9 @@ def parse_address(a): b = a2b_base58check(a) if len(b) != 21: - raise BadAddressLength + raise BadAddressLength(len(b), a) if b[0] != '\x00': - raise BadAddressVersion(ord(b[0])) + raise BadAddressVersion(ord(b[0]), a) return b[1:] def format_address(b): @@ -474,7 +474,7 @@ die('tag not found: %r' % name) return i -def cmd_scan(argv): +def cmd_scan(): ''' scan @@ -497,7 +497,7 @@ db.execute('UPDATE state SET scan_height = ?', (height,)) db.commit() -def cmd_reset(argv): +def cmd_reset(): ''' reset @@ -506,16 +506,16 @@ db.execute('UPDATE state SET scan_height = -1') db.commit() -def cmd_tags(argv): +def cmd_tags(addr=None): ''' tags [ADDRESS] List tags for the given ADDRESS (or all tags). ''' - if len(argv) > 0: - addr_id = get_address_id(parse_address(argv.pop(0))) + if addr: + addr_id = get_address_id(parse_address(addr)) if addr_id is None: - die('address not found: %r' % name) + die('address not found: %r' % addr) r = db.execute('SELECT name FROM tag \ JOIN address_tag ON tag.tag_id=address_tag.tag_id \ WHERE address_id=? ORDER BY name', (addr_id,)) @@ -524,14 +524,14 @@ for name, in r: stdout.write(name + '\n') -def cmd_addresses(argv): +def cmd_addresses(tag=None): ''' addresses [TAG] List addresses with the given TAG (or all watched addresses). ''' - if len(argv) > 0: - tag_id = require_tag(argv.pop(0)) + if tag: + tag_id = require_tag(tag) r = db.execute('SELECT address FROM address \ JOIN address_tag ON address.address_id=address_tag.address_id \ WHERE tag_id=? ORDER BY address', (tag_id,)) @@ -540,14 +540,14 @@ for a, in r: stdout.write(format_address(str(a)) + '\n') -def cmd_unspent_outs(argv): +def cmd_unspent_outs(tag=None): ''' unspent-outs [TAG] Display the unspent outputs table for addresses with the given TAG (or all watched addresses), as required by the offline wallet, ordered by age. ''' - if len(argv) > 0: - tag_id = require_tag(argv.pop(0)) + if tag: + tag_id = require_tag(tag) r = db.execute('SELECT address, value, hash, output.n, block_height, tx.n FROM output \ JOIN address ON output.address_id = address.address_id \ JOIN tx ON output.tx_id = tx.tx_id \ @@ -563,14 +563,14 @@ for a, v, hash, n_out, height, n_tx in r: stdout.write('%s %s %s %s #blk %s tx %s\n' % (format_address(str(a)), format_coin(v), b2lx(hash), n_out, height, n_tx)) -def cmd_balance(argv): +def cmd_balance(tag=None): ''' balance [TAG] Display confirmed balance of addresses with the given TAG (or all watched addresses). ''' - if len(argv) > 0: - tag_id = require_tag(argv.pop(0)) + if tag: + tag_id = require_tag(tag) r = db.execute('SELECT COALESCE(SUM(value),0) FROM output \ JOIN address_tag ON output.address_id = address_tag.address_id \ WHERE spent IS NULL AND tag_id=?', (tag_id,)) @@ -579,7 +579,7 @@ bal, = r.fetchone() stdout.write('%s\n' % format_coin(bal)) -def cmd_register(argv): +def cmd_register(tag=None): ''' register [TAG] @@ -591,8 +591,8 @@ - total withdrawals (spent outputs) - running balance ''' - if len(argv) > 0: - tag_id = require_tag(argv.pop(0)) + if tag: + tag_id = require_tag(tag) outs = db.execute('SELECT block_height, tx.n, COALESCE(SUM(value),0) FROM tx \ JOIN output ON output.tx_id = tx.tx_id \ JOIN address_tag ON output.address_id = address_tag.address_id \ @@ -651,18 +651,17 @@ i += 1 o += 1 -def cmd_watch(argv): +def cmd_watch(tag=None): ''' watch [TAG] - Import a set of addresses to watch linewise from stdin, optionally named by the given TAG. Addresses can be associated with multiple tags using multiple watch commands. + Import a set of addresses to watch linewise from stdin (i.e. one address per line of input), optionally named by the single given TAG. Addresses can be associated with multiple tags using multiple watch commands. ''' tag_id = None - if len(argv) > 0: - name = argv.pop(0) - if '\n' in name: + if tag: + if '\n' in tag: die('newline not allowed in tag name') - tag_id = insert_or_get_tag_id(name) + tag_id = insert_or_get_tag_id(tag) while True: l = stdin.readline() if len(l) == 0: @@ -676,11 +675,11 @@ pass db.commit() -def cmd_push(argv): +def cmd_push(): ''' push - Import raw hex transactions linewise from stdin and send to bitcoind. + Import raw hex transactions linewise from stdin (i.e. one rawtx per line of input) and send to bitcoind. ''' while True: line = stdin.readline() @@ -689,27 +688,26 @@ tx_hex = line.rstrip('\n') stdout.write('txid %s\n' % rpc('sendrawtransaction', tx_hex)) -def cmd_unlock_wallet(argv): +def cmd_unlock_wallet(timeout=60): ''' unlock-wallet [TIMEOUT] Read encryption passphrase from the terminal and unlock the bitcoind internal wallet for TIMEOUT seconds (default 60). ''' - timeout = int(argv.pop(0)) if len(argv) > 0 else 60 + timeout = int(timeout) r = rpc('walletpassphrase', getpass('Passphrase: '), timeout) if r is not None: stdout.write('%r\n' % r) else: stdout.write('Cached for %s seconds\n' % timeout) -def cmd_help(argv): +def cmd_help(name=None): ''' help [COMMAND] Display help for a given command or list all commands. ''' - if len(argv) > 0: - name = argv.pop(0) + if name: name, func = get_command(name) doc = getdoc(func) if doc is None: @@ -756,7 +754,7 @@ db.execute('PRAGMA wal_autocheckpoint=10000') # in pages (4k) if len(argv) < 2: die('missing command', help=True) - get_command(argv[1])[1](argv[2:]) + get_command(argv[1])[1](*argv[2:]) if __name__ == '__main__': main() diff -uNr a/gbw-node/manifest b/gbw-node/manifest --- a/gbw-node/manifest e0072723f44e0dd43f913c26331ca48460d0c0cad577376e57eeb2479a551a3417b905c119915e5f19d60224f79ecdc51752f893ab861d6e4b7b15c19d1361c4 +++ b/gbw-node/manifest dbfe25de2dd81d46762679b185ec93b7ef0f71c395089b3e6e5278c1c8af6482f79a35c8d9400bb471f8ef480294af82ec9003333b3ce95401cca4d692d131f0 @@ -1,2 +1,3 @@ 711740 gbw-node_subdir_genesis jfw Online database frontend and schema for gbw, the Gales Bitcoin Wallet. Reissued to follow various conventions: top-level project subdir, lowercase manifest filename, README at project level. (File renaming only; pending content changes are to follow.) 711740 gbw-node_usrbin jfw As seen also with gscm, change command symlink from /command/gbw-node to /usr/bin/gbw-node, and correct the install script for the case of replacing an existing version. Update README and bump version to reflect the packaging changes. +783265 gbw-node_error_reporting jfw Fix undefined reference on reporting a bad address in cmd_tags. More generally, simplify arglist processing and fix the silent ignoring of excess CLI arguments, by having Python unpack the arglist automatically into named parameters of each command handler function. Report more context for address decoding errors. Clarify help text regarding input format for watch and push commands.