def draw_window(state, window, rpc_queue = None, do_clear = True): if do_clear: window.clear() window.refresh() win_header = curses.newwin(3, g.x, 0, 0) if 'peerinfo' in state: win_header.addstr(0, 1, "Connected peers: " + str(len(state['peerinfo'])), curses.A_BOLD) g.addstr_rjust(win_header, 0, "(UP/DOWN: scroll, P: refresh)", curses.A_BOLD, 1) win_header.addstr(2, 1, " Node IP Version", curses.A_BOLD + curses.color_pair(5)) header = "Recv MB Sent MB Time Height" win_header.addstr(2, (g.x - len(header) - 1 if g.x <= 100 else 99 - len(header)), header, curses.A_BOLD + curses.color_pair(5)) draw_peers(state) else: if rpc_queue.qsize() > 0: g.addstr_cjust(win_header, 0, "...waiting for peer information being processed...", curses.A_BOLD + curses.color_pair(3)) else: win_header.addstr(0, 1, "no peer information loaded.", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'P' to refresh", curses.A_BOLD) win_header.refresh() footer.draw_window(state, rpc_queue)
def draw_window(state, window): window.clear() window.refresh() win_header = curses.newwin(2, 76, 0, 0) unit = 'DRK' if 'testnet' in state: if state['testnet']: unit = 'tDRK' if 'wallet' in state: if 'balance' in state: balance_string = "balance: " + "%0.8f" % state['balance'] + " " + unit if 'unconfirmedbalance' in state: if state['unconfirmedbalance'] != 0: balance_string += " (+" + "%0.8f" % state['unconfirmedbalance'] + " unconf)" window.addstr(0, 1, balance_string, curses.A_BOLD) window.addstr(0, 63, "(W: refresh)", curses.A_BOLD) draw_transactions(state) else: win_header.addstr(0, 1, "no wallet information loaded. -disablewallet, perhaps?", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'W' to refresh", curses.A_BOLD) win_header.refresh() footer.draw_window(state)
def change_mode(block_viewer, state, window, mode, poller): try: g.modes.index(mode) except ValueError: return False state['mode'] = mode block_viewer._mode = mode if mode == 'monitor': monitor.draw_window(state, window) elif mode == 'tx': tx.draw_window(state, window) elif mode == 'peers': peers.draw_window(state, window) elif mode == 'wallet': wallet.draw_window(state, window) elif mode == 'block': block_viewer.draw() elif mode == 'console': console.draw_window(state, window) elif mode == 'net': net.draw_window(state, window) elif mode == 'forks': forks.draw_window(state, window) footer.draw_window(state) poller.set_mode(mode)
def draw_window(state, window, rpc_queue=None): window.clear() window.refresh() win_header = curses.newwin(2, 76, 0, 0) unit = g.coin_unit if 'testnet' in state: if state['testnet']: unit = g.coin_unit_test if 'wallet' in state: if 'balance' in state: balance_string = "balance: " + "%0.8f" % state['balance'] + " " + unit if 'unconfirmedbalance' in state: if state['unconfirmedbalance'] != 0: balance_string += " (+" + "%0.8f" % state['unconfirmedbalance'] + " unconf)" window.addstr(0, 1, balance_string, curses.A_BOLD) if state['wallet']['mode'] == 'tx': window.addstr(0, 44, "(W: refresh, A: list addresses)", curses.A_BOLD) draw_transactions(state) else: window.addstr(0, 41, "(A: refresh, W: list transactions)", curses.A_BOLD) draw_addresses(state) else: if rpc_queue.qsize() > 0: win_header.addstr(0, 1, "...waiting for wallet information being processed...", curses.A_BOLD + curses.color_pair(3)) else: win_header.addstr(0, 1, "no wallet information loaded. -disablewallet, perhaps?", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'W' to refresh", curses.A_BOLD) win_header.refresh() footer.draw_window(state, rpc_queue)
def consolecommand(s, state, window): state['console']['cbuffer'].append(s['consolecommand']) state['console']['rbuffer'].append(s['consoleresponse']) state['console']['offset'] = 0 if state['mode'] == "console": console.draw_window(state, window) footer.draw_window(state)
def draw_window(state, window): window.clear() window.refresh() win_header = curses.newwin(2, 76, 0, 0) unit = 'BTC' if 'testnet' in state: if state['testnet']: unit = 'TNC' if 'wallet' in state: if 'balance' in state: balance_string = "balance: " + "%0.8f" % state['balance'] + " " + unit if 'unconfirmedbalance' in state: if state['unconfirmedbalance'] != 0: balance_string += " (+" + "%0.8f" % state['unconfirmedbalance'] + " unconf)" window.addstr(0, 1, balance_string, curses.A_BOLD) window.addstr(0, 63, "(W: refresh)", curses.A_BOLD) draw_transactions(state) else: win_header.addstr(0, 1, "no wallet information loaded. -disablewallet, perhaps?", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'W' to refresh", curses.A_BOLD) win_header.refresh() footer.draw_window(state)
def block_seek_back_one(state, window, rpc_queue): if state['mode'] == "block": if 'blocks' in state: if (state['blocks']['browse_height']) > 0: if state['blocks']['loaded'] == 1: state['blocks']['loaded'] = 0 state['blocks']['browse_height'] -= 1 state['blocks']['cursor'] = 0 state['blocks']['offset'] = 0 if str(state['blocks']['browse_height']) in state['blocks']: block.draw_window(state, window, rpc_queue) else: s = {'getblockhash': state['blocks']['browse_height']} rpc_queue.put(s) footer.draw_window(state, rpc_queue) elif state['mode'] == "tx": if 'txid_history' in state: if len(state['txid_history']) > 1: s = {'txid': state['txid_history'][len(state['txid_history']) - 2]} del state['txid_history'][len(state['txid_history']) - 1] del state['txid_history'][len(state['txid_history']) - 1] rpc_queue.put(s) footer.draw_window(state, rpc_queue) state['mode'] = 'tx'
def load_transaction(state, window, rpc_queue): # TODO: some sort of indicator that a transaction is loading if state['mode'] == 'tx': if 'tx' in state: if 'txid' in state['tx']['vin'][ state['tx']['cursor'] ]: if state['tx']['loaded']: state['tx']['loaded'] = 0 s = {'txid': state['tx']['vin'][ state['tx']['cursor'] ]['txid']} rpc_queue.put(s) footer.draw_window(state, rpc_queue) elif state['mode'] == "block": if 'blocks' in state: if state['blocks']['browse_height'] > 0: # block 0 is not indexed height = str(state['blocks']['browse_height']) if height in state['blocks']: blockdata = state['blocks'][height] s = {'txid': blockdata['tx'][ state['blocks']['cursor'] ]} rpc_queue.put(s) state['mode'] = 'tx' change_mode(state, window, state['mode'], rpc_queue) elif state['mode'] == "wallet": if 'wallet' in state: if 'transactions' in state['wallet']: s = {'txid': state['wallet']['transactions'][ state['wallet']['cursor'] ]['txid']} rpc_queue.put(s) state['mode'] = 'tx' change_mode(state, window, state['mode'], rpc_queue)
def draw_window(state, window): window.clear() window.refresh() win_header = curses.newwin(5, 75, 0, 0) if 'browse_height' in state['blocks']: height = str(state['blocks']['browse_height']) if height in state['blocks']: blockdata = state['blocks'][height] win_header.addstr(0, 1, "height: " + height.zfill(6) + " (J/K: browse, HOME/END: quicker, L: latest, G: seek)", curses.A_BOLD) win_header.addstr(1, 1, "hash: " + blockdata['hash'], curses.A_BOLD) win_header.addstr(2, 1, "root: " + blockdata['merkleroot'], curses.A_BOLD) win_header.addstr(3, 1, str(blockdata['size']) + " bytes (" + str(blockdata['size']/1024) + " KB) ", curses.A_BOLD) win_header.addstr(3, 26, "diff: " + "{:,d}".format(int(blockdata['difficulty'])), curses.A_BOLD) win_header.addstr(3, 52, time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(blockdata['time'])), curses.A_BOLD) win_header.addstr(4, 51, ("v" + str(blockdata['version'])).rjust(20), curses.A_BOLD) draw_transactions(state) state['blocks']['loaded'] = 1 else: win_header.addstr(0, 1, "no block information loaded", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'G' to enter a block hash, height, or timestamp", curses.A_BOLD) else: win_header.addstr(0, 1, "no block information loaded", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'G' to enter a block hash, height, or timestamp", curses.A_BOLD) win_header.refresh() footer.draw_window(state)
def draw_window(state, window): window.clear() window.refresh() win_header = curses.newwin(3, 75, 0, 0) if "peerinfo" in state: win_header.addstr( 0, 1, "connected peers: " + str(len(state["peerinfo"])).ljust(10) + " (UP/DOWN: scroll, P: refresh)", curses.A_BOLD, ) win_header.addstr( 2, 1, " Node IP Version Recv Sent Time Height", curses.A_BOLD + curses.color_pair(5), ) draw_peers(state) else: win_header.addstr(0, 1, "no peer information loaded", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'P' to refresh", curses.A_BOLD) win_header.refresh() footer.draw_window(state)
def draw_window(state, window): window.clear() window.refresh() win_header = curses.newwin(3, 75, 0, 0) if 'chaintips' in state: win_header.addstr( 0, 1, "chain tips: " + str(len(state['chaintips'])).ljust(10) + " (UP/DOWN: scroll, F: refresh)", curses.A_BOLD) win_header.addstr( 1, 1, "key: Active/Invalid/HeadersOnly/ValidFork/ValidHeaders", curses.A_BOLD) win_header.addstr(2, 1, "height length status 0-prefix hash", curses.A_BOLD + curses.color_pair(5)) draw_tips(state) else: win_header.addstr(0, 1, "no chain tip information loaded", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'F' to refresh", curses.A_BOLD) win_header.addstr( 2, 1, "(note that bitcoind 0.9.3 and older do not support this feature)", curses.A_BOLD) win_header.refresh() footer.draw_window(state)
def draw_window(state, window, rpc_queue=None): window.clear() window.refresh() win_header = curses.newwin(1, g.x, 0, 0) if 'mempool' in state: if 'num_transactions' in state['mempool']: height = str(state['mempool']['num_transactions']) if height > 0: win_header.addstr(0, 1, "Transactions in mempool: " + "{:,d}".format(int(height)), curses.A_BOLD) draw_transactions(state) else: win_header.addstr(0, 1, "No transactions in mempool - yet...", curses.A_BOLD) else: if rpc_queue.qsize() > 0: g.addstr_cjust(win_header, 0, "...waiting for mempool information being processed...", curses.A_BOLD + curses.color_pair(3)) else: win_header.addstr(0, 1, "no mempool information loaded.", curses.A_BOLD + curses.color_pair(3)) else: win_header.addstr(0, 1, "no mempool information loaded.", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(0, 32, "press 'M' to load mempool data.", curses.A_BOLD) win_header.refresh() footer.draw_window(state, rpc_queue)
def txid(s, state, window): if s['size'] < 0: if 'tx' in state: state.pop('tx') if state['mode'] == 'tx': tx.draw_window(state, window) footer.draw_window(state) return False state['tx'] = { 'txid': s['txid'], 'vin': [], 'vout_string': [], 'cursor': 0, 'offset': 0, 'out_offset': 0, 'loaded': 1, 'mode': 'inputs', 'size': s['size'], } for vin in s['vin']: if 'coinbase' in vin: state['tx']['vin'].append({'coinbase': vin['coinbase']}) elif 'txid' in vin: if 'prev_tx' in vin: state['tx']['vin'].append({'txid': vin['txid'], 'vout': vin['vout'], 'prev_tx': vin['prev_tx']}) else: state['tx']['vin'].append({'txid': vin['txid'], 'vout': vin['vout']}) state['tx']['total_outputs'] = 0 for vout in s['vout']: if 'value' in vout: if vout['scriptPubKey']['type'] == "pubkeyhash": buffer_string = "% 14.8f" % vout['value'] + ": " + vout['scriptPubKey']['addresses'][0] else: buffer_string = "% 14.8f" % vout['value'] + ": " + vout['scriptPubKey']['asm'] if 'confirmations' in s: if 'spent' in vout: if vout['spent'] == 'confirmed': buffer_string += " [SPENT]" elif vout['spent'] == 'unconfirmed': buffer_string += " [UNCONFIRMED SPEND]" else: buffer_string += " [UNSPENT]" state['tx']['total_outputs'] += vout['value'] state['tx']['vout_string'].extend(textwrap.wrap(buffer_string,70)) # change this to scale with window ? if 'total_inputs' in s: state['tx']['total_inputs'] = s['total_inputs'] if 'confirmations' in s: state['tx']['confirmations'] = s['confirmations'] if state['mode'] == 'tx': tx.draw_window(state, window) footer.draw_window(state)
def getnetworkhashps(s, state, window): blocks = s['getnetworkhashps']['blocks'] state['networkhashps'][blocks] = s['getnetworkhashps']['value'] if state['mode'] == "splash" and blocks == 2016: # initialization complete state['mode'] = "monitor" monitor.draw_window(state, window) footer.draw_window(state)
def draw_input_box(state, rpc_queue): entered_command = getstr.getstr(state['x'], state['y']-2, 1) # w, y, x if entered_command == "": pass else: s = {'consolecommand': entered_command} rpc_queue.put(s) footer.draw_window(state, rpc_queue)
def go_to_latest_block(state, window, rpc_queue): if state['mode'] == "block": if 'mininginfo' in state: if state['mininginfo']['blocks'] not in state['blocks']: s = {'getblockhash': state['mininginfo']['blocks']} rpc_queue.put(s) footer.draw_window(state, rpc_queue) else: state['blocks']['browse_height'] = state['mininginfo']['blocks'] block.draw_window(state, window, rpc_queue)
def getblock(s, state, window): height = s['getblock']['height'] state['blocks'][str(height)] = s['getblock'] if state['mode'] == "monitor": monitor.draw_window(state, window) footer.draw_window(state) """
def listsinceblock(s, state, window): state['wallet'] = s['listsinceblock'] state['wallet']['cursor'] = 0 state['wallet']['offset'] = 0 state['wallet']['view_string'] = [] state['wallet']['transactions'].sort(key=lambda entry: entry['category'], reverse=True) # add cumulative balance field to transactiosn once ordered by time state['wallet']['transactions'].sort(key=lambda entry: entry['time']) state['wallet']['transactions'].sort(key=lambda entry: entry['confirmations'], reverse=True) cumulative_balance = 0 nonce = 0 # ensures a definitive ordering of transactions for cumulative balance for entry in state['wallet']['transactions']: entry['nonce'] = nonce nonce += 1 if 'amount' in entry: if 'fee' in entry: cumulative_balance += entry['fee'] cumulative_balance += entry['amount'] entry['cumulative_balance'] = cumulative_balance state['wallet']['transactions'].sort(key=lambda entry: entry['nonce'], reverse=True) unit = 'BTC' if 'testnet' in state: if state['testnet']: unit = 'TNC' for entry in state['wallet']['transactions']: if 'txid' in entry: entry_time = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(entry['time'])) output_string = entry_time + " %8d" % entry['confirmations'] + " conf" delta = entry['amount'] if 'fee' in entry: delta += entry['fee'] # this fails if not all inputs owned by wallet; could be 'too negative' output_string += "% 17.8f" % delta + unit output_string += " " + "% 17.8f" % entry['cumulative_balance'] + unit state['wallet']['view_string'].append(output_string) output_string = entry['txid'].rjust(74) state['wallet']['view_string'].append(output_string) if 'address' in entry: # TODO: more sanity checking here output_string = " " + entry['category'].ljust(15) + entry['address'] else: output_string = " unknown transaction type" state['wallet']['view_string'].append(output_string) state['wallet']['view_string'].append("") if state['mode'] == "wallet": wallet.draw_window(state, window) footer.draw_window(state)
def draw_input_box(state, rpc_queue): entered_command = getstr.getstr( state['x'], state['y'] - 2, 1, '', state['console']['cbuffer'][0::1]).strip() # w, y, x if entered_command == "": pass else: s = {'consolecommand': entered_command} rpc_queue.put(s) footer.draw_window(state, rpc_queue)
def draw_window(state, window): # TODO: add transaction locktime, add sequence to inputs window.clear() window.refresh() win_header = curses.newwin(3, 75, 0, 0) unit = 'BTC' if 'testnet' in state: if state['testnet']: unit = 'TNC' if 'tx' in state: win_header.addstr(0, 1, "txid: " + state['tx']['txid'], curses.A_BOLD) win_header.addstr( 1, 1, str(state['tx']['size']) + " bytes (" + str(state['tx']['size'] / 1024) + " KB) ", curses.A_BOLD) if 'total_outputs' in state['tx']: output_string = "%.8f" % state['tx']['total_outputs'] + " " + unit if 'total_inputs' in state['tx']: if state['tx']['total_inputs'] == 'coinbase': fee = 0 output_string += " (coinbase)" else: # Verbose mode only fee = state['tx']['total_inputs'] - state['tx'][ 'total_outputs'] output_string += " + " + "%.8f" % fee + " " + unit + " fee" else: output_string += " + unknown fee" win_header.addstr(1, 26, output_string.rjust(45), curses.A_BOLD) if 'confirmations' in state['tx']: win_header.addstr(2, 1, str(state['tx']['confirmations']) + " conf", curses.A_BOLD) else: win_header.addstr(2, 1, "unconfirmed", curses.A_BOLD) win_header.addstr(2, 56, "(G: enter txid)", curses.A_BOLD) draw_inputs(state) draw_outputs(state) else: win_header.addstr(0, 1, "no transaction loaded", curses.A_BOLD + curses.color_pair(3)) win_header.addstr( 1, 1, "if you have entered one, consider running bitcoind with -txindex", curses.A_BOLD) win_header.addstr(2, 1, "press 'G' to enter a txid", curses.A_BOLD) win_header.refresh() footer.draw_window(state)
def draw_window(state, window, rpc_queue): window.clear() window.refresh() win_header = curses.newwin(1, 76, 0, 0) win_header.addstr(0, 1, "G: enter command".rjust(72), curses.A_BOLD + curses.color_pair(5)) win_header.refresh() if len(state['console']['rbuffer']): draw_buffer(state) footer.draw_window(state, rpc_queue)
def draw_window(state, window, rpc_queue): # TODO: add transaction locktime, add sequence to inputs window.clear() window.refresh() win_header = curses.newwin(3, 75, 0, 0) unit = g.coin_unit if 'testnet' in state: if state['testnet']: unit = g.coin_unit_test if 'tx' in state: win_header.addstr(0, 1, "txid: " + state['tx']['txid'], curses.A_BOLD) win_header.addstr(1, 1, str(state['tx']['size']) + " bytes (" + str(state['tx']['size']/1024) + " KB) ", curses.A_BOLD) if 'total_outputs' in state['tx']: output_string = "%.8f" % state['tx']['total_outputs'] + " " + unit if 'total_inputs' in state['tx']: if state['tx']['total_inputs'] == 'coinbase': fee = 0 output_string += " (coinbase)" else: # Verbose mode only try: if int(state['tx']['total_inputs']) == 0: fee = 0 else: fee = state['tx']['total_inputs'] - state['tx']['total_outputs'] except: fee = state['tx']['total_inputs'] - state['tx']['total_outputs'] output_string += " + " + "%.8f" % fee + " " + unit + " fee" else: output_string += " + unknown fee" win_header.addstr(1, 26, output_string.rjust(45), curses.A_BOLD) if 'confirmations' in state['tx']: win_header.addstr(2, 1, str(state['tx']['confirmations']) + " conf", curses.A_BOLD) else: win_header.addstr(2, 1, "unconfirmed", curses.A_BOLD) win_header.addstr(2, 56, "(G: enter txid)", curses.A_BOLD) draw_inputs(state) draw_outputs(state) else: if rpc_queue.qsize() > 0: win_header.addstr(0, 1, "...waiting for transaction information being processed...", curses.A_BOLD + curses.color_pair(3)) else: win_header.addstr(0, 1, "no transaction loaded or no transaction information found", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "if you have entered one, consider running " + g.rpc_deamon + " with -txindex", curses.A_BOLD) win_header.addstr(2, 1, "press 'G' to enter a txid", curses.A_BOLD) win_header.refresh() footer.draw_window(state, rpc_queue)
def draw_window(state, window): window.clear() window.refresh() win_header = curses.newwin(5, 75, 0, 0) if 'browse_height' in state['blocks']: height = str(state['blocks']['browse_height']) if height in state['blocks']: blockdata = state['blocks'][height] win_header.addstr( 0, 1, "height: " + height.zfill(6) + " (J/K: browse, HOME/END: quicker, L: latest, G: seek)", curses.A_BOLD) win_header.addstr(1, 1, "hash: " + blockdata['hash'], curses.A_BOLD) win_header.addstr(2, 1, "root: " + blockdata['merkleroot'], curses.A_BOLD) win_header.addstr( 3, 1, str(blockdata['size']) + " bytes (" + str(blockdata['size'] / 1024) + " KB) ", curses.A_BOLD) win_header.addstr( 3, 26, "diff: " + "{:,d}".format(int(blockdata['difficulty'])), curses.A_BOLD) win_header.addstr( 3, 52, time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(blockdata['time'])), curses.A_BOLD) win_header.addstr(4, 51, ("v" + str(blockdata['version'])).rjust(20), curses.A_BOLD) draw_transactions(state) state['blocks']['loaded'] = 1 else: win_header.addstr(0, 1, "no block information loaded", curses.A_BOLD + curses.color_pair(3)) win_header.addstr( 1, 1, "press 'G' to enter a block hash, height, or timestamp", curses.A_BOLD) else: win_header.addstr(0, 1, "no block information loaded", curses.A_BOLD + curses.color_pair(3)) win_header.addstr( 1, 1, "press 'G' to enter a block hash, height, or timestamp", curses.A_BOLD) win_header.refresh() footer.draw_window(state)
def draw_window(state, window, rpc_queue): window.clear() window.refresh() win_header = curses.newwin(1, g.x, 0, 0) g.addstr_rjust(win_header, 0, "(G: enter command, E: erase output)", curses.A_BOLD + curses.color_pair(5), 1) win_header.refresh() if len(state['console']['rbuffer']): draw_buffer(state) footer.draw_window(state, rpc_queue)
def getnettotals(s, state, window): state['totalbytesrecv'] = s['getnettotals']['totalbytesrecv'] state['totalbytessent'] = s['getnettotals']['totalbytessent'] state['history']['getnettotals'].append(s['getnettotals']) # ensure getnettotals history does not fill RAM eventually, 300 items is enough if len(state['history']['getnettotals']) > 500: state['history']['getnettotals'] = state['history']['getnettotals'][-300:] if state['mode'] == 'net': net.draw_window(state, window) footer.draw_window(state)
def draw_window(state, window): window.clear() window.refresh() win_header = curses.newwin(1, 76, 0, 0) win_header.addstr(0, 1, "G: enter command".rjust(72), curses.A_BOLD + curses.color_pair(5)) win_header.refresh() if len(state['console']['rbuffer']): draw_buffer(state) footer.draw_window(state)
def draw_window(state, window): window.clear() window.refresh() win_header = curses.newwin(3, 75, 0, 0) if 'mnodes' in state: win_header.addstr(0, 1, "masternode information successfully loaded", curses.A_BOLD + curses.color_pair(2)) else: win_header.addstr(0, 1, "under construction", curses.A_BOLD + curses.color_pair(3)) win_header.refresh() footer.draw_window(state)
def toggle_verbose_mode(state, window, rpc_queue): if state['mode'] == 'tx': if 'tx' in state: if 'txid' in state['tx']: if state['tx']['loaded']: state['tx']['loaded'] = 0 if 'total_inputs' not in state['tx']: s = {'txid': state['tx']['txid'], 'verbose': 1} else: s = {'txid': state['tx']['txid']} rpc_queue.put(s) footer.draw_window(state, rpc_queue)
def block_seek_forward_thousand(state, window, rpc_queue): if state['mode'] == "block": if 'blocks' in state: if (state['blocks']['browse_height']) < state['mininginfo']['blocks'] - 999: if state['blocks']['loaded'] == 1: state['blocks']['loaded'] = 0 state['blocks']['browse_height'] += 1000 state['blocks']['cursor'] = 0 state['blocks']['offset'] = 0 if str(state['blocks']['browse_height']) in state['blocks']: block.draw_window(state, window, rpc_queue) else: s = {'getblockhash': state['blocks']['browse_height']} rpc_queue.put(s) footer.draw_window(state, rpc_queue)
def block_seek_back_one(state, window, rpc_queue): if state['mode'] == "block": if 'blocks' in state: if (state['blocks']['browse_height']) > 0: if state['blocks']['loaded'] == 1: state['blocks']['loaded'] = 0 state['blocks']['browse_height'] -= 1 state['blocks']['cursor'] = 0 state['blocks']['offset'] = 0 if str(state['blocks']['browse_height']) in state['blocks']: block.draw_window(state, window, rpc_queue) else: s = {'getblockhash': state['blocks']['browse_height']} rpc_queue.put(s) footer.draw_window(state, rpc_queue)
def draw_window(state, window): window.clear() window.refresh() win_header = curses.newwin(3, 75, 0, 0) if 'peerinfo' in state: win_header.addstr(0, 1, "connected peers: " + str(len(state['peerinfo'])).ljust(10) + " (UP/DOWN: scroll, P: refresh)", curses.A_BOLD) win_header.addstr(2, 1, " Node IP Version Recv Sent Time Height", curses.A_BOLD + curses.color_pair(5)) draw_peers(state) else: win_header.addstr(0, 1, "no peer information loaded", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'P' to refresh", curses.A_BOLD) win_header.refresh() footer.draw_window(state)
def draw_window(state, window): # TODO: add transaction locktime, add sequence to inputs window.clear() window.refresh() win_header = curses.newwin(3, 75, 0, 0) unit = 'BTC' if 'testnet' in state: if state['testnet']: unit = 'TNC' if 'tx' in state: win_header.addstr(0, 1, "txid: " + state['tx']['txid'], curses.A_BOLD) win_header.addstr(1, 1, str(state['tx']['size']) + " bytes (" + str(state['tx']['size']/1024) + " KB) ", curses.A_BOLD) if 'total_outputs' in state['tx']: output_string = "%.8f" % state['tx']['total_outputs'] + " " + unit if 'total_inputs' in state['tx']: if state['tx']['total_inputs'] == 'coinbase': fee = 0 output_string += " (coinbase)" else: # Verbose mode only fee = state['tx']['total_inputs'] - state['tx']['total_outputs'] output_string += " + " + "%.8f" % fee + " " + unit + " fee" else: output_string += " + unknown fee" win_header.addstr(1, 26, output_string.rjust(45), curses.A_BOLD) if 'confirmations' in state['tx']: win_header.addstr(2, 1, str(state['tx']['confirmations']) + " conf", curses.A_BOLD) else: win_header.addstr(2, 1, "unconfirmed", curses.A_BOLD) win_header.addstr(2, 56, "(G: enter txid)", curses.A_BOLD) draw_inputs(state) draw_outputs(state) else: win_header.addstr(0, 1, "no transaction loaded", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "if you have entered one, consider running bitcoind with -txindex", curses.A_BOLD) win_header.addstr(2, 1, "press 'G' to enter a txid", curses.A_BOLD) win_header.refresh() footer.draw_window(state)
def draw_window(state, window): window.clear() window.refresh() win_header = curses.newwin(3, 75, 0, 0) if 'chaintips' in state: win_header.addstr(0, 1, "chain tips: " + str(len(state['chaintips'])).ljust(10) + " (UP/DOWN: scroll, F: refresh)", curses.A_BOLD) win_header.addstr(1, 1, "key: Active/Invalid/HeadersOnly/ValidFork/ValidHeaders", curses.A_BOLD) win_header.addstr(2, 1, "height length status 0-prefix hash", curses.A_BOLD + curses.color_pair(5)) draw_tips(state) else: win_header.addstr(0, 1, "no chain tip information loaded", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'F' to refresh", curses.A_BOLD) win_header.addstr(2, 1, "(note that bitcoind 0.9.3 and older do not support this feature)", curses.A_BOLD) win_header.refresh() footer.draw_window(state)
def toggle_verbose_mode(state, window, rpc_queue): if state['mode'] == 'tx': if 'tx' in state: if 'txid' in state['tx']: if state['tx']['loaded']: state['tx']['loaded'] = 0 if 'total_inputs' not in state['tx']: s = {'txid': state['tx']['txid'], 'verbose': 1, 'notrack':1} else: s = {'txid': state['tx']['txid'], 'notrack':1} rpc_queue.put(s) footer.draw_window(state, rpc_queue) elif state['mode'] == "wallet": if 'wallet' in state: if 'transactions' in state['wallet']: rpc_queue.put('wallet_toggle_tx') footer.draw_window(state, rpc_queue)
def loop(block_viewer, state, window, interface_queue, rpcc, poller): iterations = 0 while 1: check_window_size(interface_queue, state, window, 12, 75) # min_y, min_x error_message = process.queue(state, window, interface_queue) if error_message: return error_message # ends if stop command sent by rpc gevent.sleep(0.0001) # TODO: Can we kill this? if state['mode'] == "monitor": if not iterations % 20: monitor.draw_window(state, window) footer.draw_window(state) if hotkey.check(block_viewer, state, window, rpcc, poller): # poll for user input break # returns 1 when quit key is pressed iterations += 1 return False
def load_transaction(state, window, rpc_queue): # TODO: some sort of indicator that a transaction is loading if state['mode'] == 'tx': if 'tx' in state: if 'txid' in state['tx']['vin'][ state['tx']['cursor'] ]: if state['tx']['loaded']: state['tx']['loaded'] = 0 s = {'txid': state['tx']['vin'][ state['tx']['cursor'] ]['txid']} rpc_queue.put(s) footer.draw_window(state, rpc_queue) elif state['mode'] == "mempool": if 'mempool' in state: if 'transactions' in state['mempool']: s = {'txid': state['mempool']['transactions'][ int(state['mempool']['cursor']) ]} rpc_queue.put(s) state['mode'] = 'tx' change_mode(state, window, state['mode'], rpc_queue) elif state['mode'] == "block": if 'blocks' in state: if state['blocks']['browse_height'] > 0: # block 0 is not indexed height = str(state['blocks']['browse_height']) if height in state['blocks']: blockdata = state['blocks'][height] s = {'txid': blockdata['tx'][ state['blocks']['cursor'] ]} rpc_queue.put(s) state['mode'] = 'tx' change_mode(state, window, state['mode'], rpc_queue) elif state['mode'] == "wallet": if 'wallet' in state: if 'transactions' in state['wallet']: s = {'txid': state['wallet']['transactions'][ state['wallet']['cursor'] ]['txid']} rpc_queue.put(s) state['mode'] = 'tx' change_mode(state, window, state['mode'], rpc_queue)
def draw_window(state, window, rpc_queue=None): window.clear() window.refresh() win_header = curses.newwin(4, g.x, 0, 0) if 'chaintips' in state: win_header.addstr(0, 1, "chain tips: " + str(len(state['chaintips'])), curses.A_BOLD) g.addstr_rjust(win_header, 0, "(UP/DOWN: scroll, F: refresh)", curses.A_BOLD) win_header.addstr( 1, 1, "key: Active/Invalid/HeadersOnly/ValidFork/ValidHeaders", curses.A_BOLD) win_header.addstr(3, 1, "height, length, status, 0-prefix hash", curses.A_BOLD + curses.color_pair(5)) draw_tips(state) else: if rpc_queue.qsize() > 0: g.addstr_cjust( win_header, 0, "...waiting for chain tip information being processed...", curses.A_BOLD + curses.color_pair(3)) else: win_header.addstr(0, 1, "no chain tip information loaded.", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'F' to refresh", curses.A_BOLD) if g.coin_unit == 'BTC': win_header.addstr( 2, 1, "(note that bitcoind 0.9.3 and older do not support this feature)", curses.A_BOLD) win_header.refresh() footer.draw_window(state, rpc_queue)
def getpeerinfo(s, state, window): state['peerinfo'] = s['getpeerinfo'] state['peerinfo_offset'] = 0 if state['mode'] == "peers": peers.draw_window(state, window) footer.draw_window(state)
def getchaintips(s, state, window): state['chaintips'] = s['getchaintips'] state['chaintips_offset'] = 0 if state['mode'] == 'forks': forks.draw_window(state, window) footer.draw_window(state)
def draw_window(state, old_window): old_window.clear() old_window.refresh() window_height = state['y'] - 1 window_width = state['x'] window = curses.newwin(window_height, window_width, 0, 0) history = state['history']['getnettotals'] sent_deltas = [] recv_deltas = [] index = 1 while index < len(history): timedelta = history[index]['timemillis'] - history[index-1]['timemillis'] recvdelta = history[index]['totalbytesrecv'] - history[index-1]['totalbytesrecv'] sentdelta = history[index]['totalbytessent'] - history[index-1]['totalbytessent'] recv_deltas.append(recvdelta*1000 / timedelta) sent_deltas.append(sentdelta*1000 / timedelta) index += 1 if sent_deltas: chart_height = window_height - 2 plot_height = chart_height / 2 chart_width = window_width - 11 if len(sent_deltas) > chart_width: sent_deltas = sent_deltas[-chart_width:] recv_deltas = recv_deltas[-chart_width:] color_sent = curses.color_pair(2) color_recv = curses.color_pair(1) max_sent = max(sent_deltas) max_recv = max(recv_deltas) max_total = max(max_sent, max_recv) if max_total > 0: if max_sent > 0: height = int(math.ceil((1.0 * plot_height * max_sent) / max_total)) window.addstr(plot_height-height, 1, ("%0.1f" % (max_sent*1.0/1024)).rjust(6) + "K", curses.A_BOLD) if max_recv > 0: height = int(math.ceil((1.0 * plot_height * max_recv) / max_total)) window.addstr(plot_height+height, 1, ("%0.1f" % (max_recv*1.0/1024)).rjust(6) + "K", curses.A_BOLD) index = 0 while index < len(sent_deltas): if index < chart_width: height = int(math.ceil((1.0 * plot_height * sent_deltas[index]) / max_total)) for y in xrange(0, height): window.addch(plot_height-1-y, index+10, " ", color_sent + curses.A_REVERSE) height = int(math.ceil((1.0 * plot_height * recv_deltas[index]) / max_total)) for y in xrange(0, height): window.addch(plot_height+1+y, index+10, " ", color_recv + curses.A_REVERSE) index += 1 recv_string = "Down: " + ("%0.1f" % (recv_deltas[-1]*1.0/1024)).rjust(7) + "KB/s" sent_string = "Up: " + ("%0.1f" % (sent_deltas[-1]*1.0/1024)).rjust(7) + "KB/s" total_string = "Total: " + ("%0.1f" % ((sent_deltas[-1] + recv_deltas[-1])*1.0/1024)).rjust(7) + "KB/s" window.addstr(chart_height+1, window_width-1-18, total_string, curses.A_BOLD) window.addstr(chart_height+1, window_width-1-38, sent_string, curses.A_BOLD + color_sent) window.addstr(chart_height+1, window_width-1-58, recv_string, curses.A_BOLD + color_recv) window.refresh() footer.draw_window(state)
def draw_window(state, old_window, rpc_queue=None): old_window.clear() old_window.refresh() window_height = state['y'] - 1 window_width = state['x'] window = curses.newwin(window_height, window_width, 0, 0) history = state['history']['getnettotals'] sent_deltas = [] recv_deltas = [] index = 1 while index < len(history): timedelta = history[index]['timemillis'] - history[index-1]['timemillis'] recvdelta = history[index]['totalbytesrecv'] - history[index-1]['totalbytesrecv'] sentdelta = history[index]['totalbytessent'] - history[index-1]['totalbytessent'] recv_deltas.append(recvdelta*1000 / timedelta) sent_deltas.append(sentdelta*1000 / timedelta) index += 1 if sent_deltas: chart_height = window_height - 2 plot_height = chart_height / 2 chart_width = window_width - 11 if len(sent_deltas) > chart_width: sent_deltas = sent_deltas[-chart_width:] recv_deltas = recv_deltas[-chart_width:] color_sent = curses.color_pair(2) color_recv = curses.color_pair(1) max_sent = max(sent_deltas) max_recv = max(recv_deltas) max_total = max(max_sent, max_recv) if max_total > 0: if max_sent > 0: height = int(math.ceil((1.0 * plot_height * max_sent) / max_total)) window.addstr(plot_height-height, 1, ("%0.1f" % (max_sent*1.0/1024)).rjust(6) + "K", curses.A_BOLD) if max_recv > 0: height = int(math.ceil((1.0 * plot_height * max_recv) / max_total)) window.addstr(plot_height+height, 1, ("%0.1f" % (max_recv*1.0/1024)).rjust(6) + "K", curses.A_BOLD) index = 0 while index < len(sent_deltas): if index < chart_width: height = int(math.ceil((1.0 * plot_height * sent_deltas[index]) / max_total)) for y in xrange(0, height): window.addch(plot_height-1-y, index+10, " ", color_sent + curses.A_REVERSE) height = int(math.ceil((1.0 * plot_height * recv_deltas[index]) / max_total)) for y in xrange(0, height): window.addch(plot_height+1+y, index+10, " ", color_recv + curses.A_REVERSE) index += 1 recv_string = "Down: " + ("%0.1f" % (recv_deltas[-1]*1.0/1024)).rjust(7) + "KB/s" sent_string = "Up: " + ("%0.1f" % (sent_deltas[-1]*1.0/1024)).rjust(7) + "KB/s" total_string = "Total: " + ("%0.1f" % ((sent_deltas[-1] + recv_deltas[-1])*1.0/1024)).rjust(7) + "KB/s" # window.addstr(chart_height+1, window_width-1-18, total_string, curses.A_BOLD) window.addstr(chart_height+1, 1, total_string, curses.A_BOLD) window.addstr(chart_height+1, 21, sent_string, curses.A_BOLD + color_sent) window.addstr(chart_height+1, 41, recv_string, curses.A_BOLD + color_recv) window.refresh() footer.draw_window(state, rpc_queue)
def draw_window(state, old_window): # TODO: only draw parts that actually changed old_window.clear() old_window.refresh() window = curses.newwin(19, 76, 0, 0) if 'version' in state: if state['testnet'] == 1: color = curses.color_pair(2) window.addstr(1, 1, "bitcoind v" + state['version'] + " (testnet)", color + curses.A_BOLD) unit = 'TNC' else: color = curses.color_pair(1) window.addstr(1, 1, "bitcoind v" + state['version'] + " ", color + curses.A_BOLD) unit = 'BTC' window.addstr(0, 1, "bitcoind-ncurses " + g.version, color + curses.A_BOLD) if 'peers' in state: if state['peers'] > 0: color = 0 else: color = curses.color_pair(3) window.addstr(0, 32, str(state['peers']) + " peers ", color + curses.A_BOLD) if 'balance' in state: balance_string = "%0.8f" % state['balance'] + " " + unit if 'unconfirmedbalance' in state: if state['unconfirmedbalance'] != 0: balance_string += " (+" + "%0.8f" % state[ 'unconfirmedbalance'] + " unconf)" window.addstr(1, 32, balance_string, curses.A_BOLD) if 'mininginfo' in state: height = str(state['mininginfo']['blocks']) if height in state['blocks']: blockdata = state['blocks'][str(height)] if 'new' in blockdata: window.attrset(curses.A_REVERSE + curses.color_pair(5) + curses.A_BOLD) blockdata.pop('new') window.addstr(3, 1, height.zfill(6) + ": " + str(blockdata['hash'])) window.addstr( 4, 1, str(blockdata['size']) + " bytes (" + str(blockdata['size'] / 1024) + " KB) ") tx_count = len(blockdata['tx']) bytes_per_tx = blockdata['size'] / tx_count window.addstr( 5, 1, "Transactions: " + str(tx_count) + " (" + str(bytes_per_tx) + " bytes/tx)") if 'coinbase_amount' in blockdata: if state['mininginfo']['blocks'] < 210000: block_subsidy = 50 elif state['mininginfo']['blocks'] < 420000: block_subsidy = 25 if block_subsidy: # this will fail after block 420,000. TODO: stop being lazy and do it properly coinbase_amount = blockdata['coinbase_amount'] total_fees = coinbase_amount - block_subsidy # assumption, mostly correct if coinbase_amount > 0: fee_percentage = "%0.2f" % ( (total_fees / coinbase_amount) * 100) coinbase_amount_str = "%0.8f" % coinbase_amount window.addstr( 7, 1, "Total block reward: " + coinbase_amount_str + " " + unit + " (" + fee_percentage + "% fees)") if tx_count > 1: tx_count -= 1 # the coinbase can't pay a fee fees_per_tx = (total_fees / tx_count) * 1000 fees_per_kb = ( (total_fees * 1024) / blockdata['size']) * 1000 total_fees_str = "%0.8f" % total_fees + " " + unit fees_per_tx = "%0.5f" % fees_per_tx + " m" + unit + "/tx" fees_per_kb = "%0.5f" % fees_per_kb + " m" + unit + "/KB" window.addstr( 8, 1, "Fees: " + total_fees_str + " (avg " + fees_per_tx + ", ~" + fees_per_kb + ")") window.addstr( 4, 37, "Block timestamp: " + time.strftime( "%Y-%m-%d %H:%M:%S", time.gmtime(blockdata['time']))) if state['lastblocktime'] == 0: recvdelta_string = " " else: recvdelta = int(time.time() - state['lastblocktime']) m, s = divmod(recvdelta, 60) h, m = divmod(m, 60) recvdelta_string = "{:02d}:{:02d}:{:02d}".format(h, m, s) stampdelta = int(time.time() - blockdata['time']) if stampdelta > 3600 * 3: # probably syncing if it's three hours old stampdelta_string = " (syncing)" elif stampdelta > 0: m, s = divmod(stampdelta, 60) h, m = divmod(m, 60) d, h = divmod(h, 24) stampdelta_string = "({:d}d {:02d}:{:02d}:{:02d} by stamp)".format( d, h, m, s) else: stampdelta_string = " (stamp in future)" window.addstr(5, 37, "Age: " + recvdelta_string + " " + stampdelta_string) if 'chainwork' in blockdata: log2_chainwork = math.log(int(blockdata['chainwork'], 16), 2) window.addstr(14, 1, "Chain work: 2**" + "%0.6f" % log2_chainwork) diff = int(state['mininginfo']['difficulty']) window.addstr(10, 1, "Diff: " + "{:,d}".format(diff)) for block_avg in state['networkhashps']: index = 10 if block_avg == 'diff': pass elif block_avg == 2016: index += 1 elif block_avg == 144: index += 2 else: break rate = state['networkhashps'][block_avg] if block_avg != 'diff': nextdiff = (rate * 600) / (2**32) if state['testnet'] == 1: nextdiff *= 2 # testnet has 1200 est. block interval, not 600 window.addstr( index, 1, "Est (" + str(block_avg).rjust(4) + "): ~" + "{:,d}".format(nextdiff)) if rate > 10**18: rate /= 10**18 suffix = " EH/s" elif rate > 10**12: rate /= 10**12 suffix = " TH/s" else: rate /= 10**6 suffix = " MH/s" rate_string = "{:,d}".format(rate) + suffix window.addstr( index, 37, "Hashrate (" + str(block_avg).rjust(4) + "): " + rate_string.rjust(13)) index += 1 pooledtx = state['mininginfo']['pooledtx'] window.addstr(14, 37, "Mempool transactions: " + "% 5d" % pooledtx) if 'totalbytesrecv' in state: recvmb = "%.2f" % (state['totalbytesrecv'] * 1.0 / 1048576) sentmb = "%.2f" % (state['totalbytessent'] * 1.0 / 1048576) recvsent_string = "D/U: " + recvmb + " / " + sentmb + " MB" window.addstr(0, 43, recvsent_string.rjust(30), curses.A_BOLD) if 'estimatefee' in state: string = "estimatefee:" for item in state['estimatefee']: if item['value'] > 0: string += " (" + str(item['blocks']) + ")" + "%4.2f" % ( item['value'] * 1000) + "m" + unit if len(string) > 12: window.addstr(15, 37, string) if 'mininginfo' in state: errors = state['mininginfo']['errors'] if len(errors): if state['y'] < 20: y = state['y'] - 3 else: y = 17 window.addstr( y, 1, errors[:72], curses.color_pair(5) + curses.A_BOLD + curses.A_REVERSE) window.addstr( y + 1, 1, errors[72:142].rjust(72), curses.color_pair(5) + curses.A_BOLD + curses.A_REVERSE) window.refresh() footer.draw_window(state)
def draw_new_address_window(state, window, rpc_queue): color = curses.color_pair(1) if g.testnet: color = curses.color_pair(2) UI = getstr.UserInput(window, "new receiving address mode") abort = False error_string = "" UI.addline("Please enter an address label (optional):", curses.A_BOLD) try: new_account = str(UI.getstr(128)).strip() except: abort = True if not abort: UI.addline("Do you want to add a [r]eceiving or a [w]atch-only address? [r/w]", curses.A_BOLD) try: new_type = str(UI.getstr(1)) except: error_string = "Invalid address type entered. " abort = True if new_type == 'r': state['wallet']['y'] = UI._y + 2 state['wallet']['newlabel'] = new_account rpc_queue.put({'getnewaddress': new_account}) UI.addmessageline("Creating new receiving address" + (" for account " + "'{}'".format(new_account) if len(new_account) else "") + "...", color + curses.A_BOLD) elif new_type == 'w': UI.addline("Please enter watch-only address:", curses.A_BOLD) new_address = "" try: new_address = UI.getstr(35).strip() except: abort = True if not check_address(new_address): error_string = "Invalid address entered. " abort = True if not abort: UI.addline("Do you want to rescan the entire blockchain for matching transactions? [y/N]", curses.A_BOLD) try: new_rescan = str(UI.getstr(1)) except: abort = True if new_rescan in ['y', 'Y']: new_rescan = True elif new_rescan in ['n', 'N']: new_rescan = False else: abort = True if not abort: state['wallet']['newlabel'] = new_account state['wallet']['newaddress'] = new_address s = {'importaddress': {'account': new_account, 'address': new_address, 'rescan': bool(new_rescan)}} rpc_queue.put(s) UI.addmessageline("Importing watch-only address" + (" for account " + "'{}'".format(new_account) if len(new_account) else "") + "...", color + curses.A_BOLD) else: abort = True if abort: UI.addmessageline(error_string + "Aborting.", color + curses.A_BOLD) state['wallet']['mode'] = 'addresses' rpc_queue.put('listreceivedbyaddress') UI.clear() footer.draw_window(state, rpc_queue, True)
def draw_window(state, window, rpc_queue): # TODO: add transaction locktime, add sequence to inputs window.clear() window.refresh() win_header = curses.newwin(3, g.x, 0, 0) unit = g.coin_unit if g.testnet: unit = g.coin_unit_test if 'tx' in state: win_header.addstr(0, 1, "txid: " + state['tx']['txid'], curses.A_BOLD) win_header.addstr( 1, 1, str(state['tx']['size']) + " bytes (" + "{:.2f}".format(float(state['tx']['size']) / 1024.0) + " KB) ", curses.A_BOLD) if 'total_outputs' in state['tx']: output_string = "%.8f" % state['tx']['total_outputs'] + " " + unit if 'total_inputs' in state['tx']: if state['tx']['total_inputs'] == 'coinbase': fee = float(0) output_string += " (coinbase)" else: # Verbose mode only try: if float(state['tx']['total_inputs']) == 0: fee = float(0) else: fee = float(state['tx']['total_inputs']) - float( state['tx']['total_outputs']) except: fee = float(state['tx']['total_inputs']) - float( state['tx']['total_outputs']) output_string += " + " + "%.8f" % fee + " " + unit + " fee" else: output_string += " + unknown fee" win_header.addstr(1, 26, output_string.rjust(45), curses.A_BOLD) if 'time' in state['tx']: output_string = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(state['tx']['time'])) else: output_string = "" if 'confirmations' in state['tx']: output_string += ("" if not len(output_string) else " / ") + "{:,d}".format( int(state['tx']['confirmations'])) + " conf" else: output_string += ("" if not len(output_string) else " / ") + "unconfirmed" win_header.addstr(2, 1, output_string[:g.x - 2], curses.A_BOLD) history_height = 0 if not 'txid_history' in state else len( state['txid_history']) - 1 if history_height <= 0: history_msg = "" else: if g.x > 79: history_msg = ", J: browse back" else: history_msg = ", J: go back" g.addstr_rjust(win_header, 2, "(V: verbose, G: enter txid" + history_msg + ")", curses.A_BOLD, 1) draw_inputs(state) draw_outputs(state) else: if rpc_queue.qsize() > 0: g.addstr_cjust( win_header, 0, "...waiting for transaction information being processed...", curses.A_BOLD + curses.color_pair(3)) else: win_header.addstr( 0, 1, "no transaction loaded or no transaction information found.", curses.A_BOLD + curses.color_pair(3)) win_header.addstr( 1, 1, "if you have entered one, consider running " + g.rpc_deamon + " with -txindex", curses.A_BOLD) win_header.addstr(2, 1, "press 'G' to enter a txid", curses.A_BOLD) win_header.refresh() footer.draw_window(state, rpc_queue)
def draw_window(state, old_window): # TODO: only draw parts that actually changed old_window.clear() old_window.refresh() window = curses.newwin(19, 76, 0, 0) if 'version' in state: if state['testnet'] == 1: color = curses.color_pair(2) window.addstr(1, 1, "bitcoind v" + state['version'] + " (testnet)", color + curses.A_BOLD) unit = 'TNC' else: color = curses.color_pair(1) window.addstr(1, 1, "bitcoind v" + state['version'] + " ", color + curses.A_BOLD) unit = 'BTC' window.addstr(0, 1, "bitcoind-ncurses " + g.version, color + curses.A_BOLD) if 'peers' in state: if state['peers'] > 0: color = 0 else: color = curses.color_pair(3) window.addstr(0, 32, str(state['peers']) + " peers ", color + curses.A_BOLD) if 'balance' in state: balance_string = "%0.8f" % state['balance'] + " " + unit if 'unconfirmedbalance' in state: if state['unconfirmedbalance'] != 0: balance_string += " (+" + "%0.8f" % state['unconfirmedbalance'] + " unconf)" window.addstr(1, 32, balance_string, curses.A_BOLD) if 'mininginfo' in state: height = str(state['mininginfo']['blocks']) if height in state['blocks']: blockdata = state['blocks'][str(height)] if 'new' in blockdata: window.attrset(curses.A_REVERSE + curses.color_pair(5) + curses.A_BOLD) blockdata.pop('new') window.addstr(3, 1, height.zfill(6) + ": " + str(blockdata['hash'])) window.addstr(4, 1, str(blockdata['size']) + " bytes (" + str(blockdata['size']/1024) + " KB) ") tx_count = len(blockdata['tx']) bytes_per_tx = blockdata['size'] / tx_count window.addstr(5, 1, "Transactions: " + str(tx_count) + " (" + str(bytes_per_tx) + " bytes/tx)") if 'coinbase_amount' in blockdata: if state['mininginfo']['blocks'] < 210000: block_subsidy = 50 elif state['mininginfo']['blocks'] < 420000: block_subsidy = 25 if block_subsidy: # this will fail after block 420,000. TODO: stop being lazy and do it properly coinbase_amount = blockdata['coinbase_amount'] total_fees = coinbase_amount - block_subsidy # assumption, mostly correct if coinbase_amount > 0: fee_percentage = "%0.2f" % ((total_fees / coinbase_amount) * 100) coinbase_amount_str = "%0.8f" % coinbase_amount window.addstr(7, 1, "Total block reward: " + coinbase_amount_str + " " + unit + " (" + fee_percentage + "% fees)") if tx_count > 1: tx_count -= 1 # the coinbase can't pay a fee fees_per_tx = (total_fees / tx_count) * 1000 fees_per_kb = ((total_fees * 1024) / blockdata['size']) * 1000 total_fees_str = "%0.8f" % total_fees + " " + unit fees_per_tx = "%0.5f" % fees_per_tx + " m" + unit + "/tx" fees_per_kb = "%0.5f" % fees_per_kb + " m" + unit + "/KB" window.addstr(8, 1, "Fees: " + total_fees_str + " (avg " + fees_per_tx + ", ~" + fees_per_kb + ")") window.addstr(4, 37, "Block timestamp: " + time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(blockdata['time']))) if state['lastblocktime'] == 0: recvdelta_string = " " else: recvdelta = int(time.time() - state['lastblocktime']) m, s = divmod(recvdelta, 60) h, m = divmod(m, 60) recvdelta_string = "{:02d}:{:02d}:{:02d}".format(h,m,s) stampdelta = int(time.time() - blockdata['time']) if stampdelta > 3600*3: # probably syncing if it's three hours old stampdelta_string = " (syncing)" elif stampdelta > 0: m, s = divmod(stampdelta, 60) h, m = divmod(m, 60) d, h = divmod(h, 24) stampdelta_string = "({:d}d {:02d}:{:02d}:{:02d} by stamp)".format(d,h,m,s) else: stampdelta_string = " (stamp in future)" window.addstr(5, 37, "Age: " + recvdelta_string + " " + stampdelta_string) if 'chainwork' in blockdata: log2_chainwork = math.log(int(blockdata['chainwork'], 16), 2) window.addstr(14, 1, "Chain work: 2**" + "%0.6f" % log2_chainwork) diff = int(state['mininginfo']['difficulty']) window.addstr(10, 1, "Diff: " + "{:,d}".format(diff)) for block_avg in state['networkhashps']: index = 10 if block_avg == 'diff': pass elif block_avg == 2016: index += 1 elif block_avg == 144: index += 2 else: break rate = state['networkhashps'][block_avg] if block_avg != 'diff': nextdiff = (rate*600)/(2**32) if state['testnet'] == 1: nextdiff *= 2 # testnet has 1200 est. block interval, not 600 window.addstr(index, 1, "Est (" + str(block_avg).rjust(4) + "): ~" + "{:,}".format(nextdiff)) if rate > 10**18: rate /= 10**18 suffix = " EH/s" elif rate > 10**12: rate /= 10**12 suffix = " TH/s" else: rate /= 10**6 suffix = " MH/s" rate_string = "{:,}".format(rate) + suffix window.addstr(index, 37, "Hashrate (" + str(block_avg).rjust(4) + "): " + rate_string.rjust(13)) index += 1 pooledtx = state['mininginfo']['pooledtx'] window.addstr(14, 37, "Mempool transactions: " + "% 5d" % pooledtx) if 'totalbytesrecv' in state: recvmb = "%.2f" % (state['totalbytesrecv']*1.0/1048576) sentmb = "%.2f" % (state['totalbytessent']*1.0/1048576) recvsent_string = "D/U: " + recvmb + " / " + sentmb + " MB" window.addstr(0, 43, recvsent_string.rjust(30), curses.A_BOLD) if 'estimatefee' in state: string = "estimatefee:" for item in state['estimatefee']: if item['value'] > 0: string += " (" + str(item['blocks']) + ")" + "%4.2f" % (item['value']*1000) + "m" + unit if len(string) > 12: window.addstr(15, 37, string) if 'mininginfo' in state: errors = state['mininginfo']['errors'] if len(errors): if state['y'] < 20: y = state['y'] - 3 else: y = 17 window.addstr(y, 1, errors[:72], curses.color_pair(5) + curses.A_BOLD + curses.A_REVERSE) window.addstr(y+1, 1, errors[72:142].rjust(72), curses.color_pair(5) + curses.A_BOLD + curses.A_REVERSE) window.refresh() footer.draw_window(state)
def draw_window(state, window, rpc_queue=None, do_clear = True): if do_clear: window.clear() window.refresh() win_header = curses.newwin(3, g.x, 0, 0) unit = g.coin_unit if 'testnet' in state: if state['testnet']: unit = g.coin_unit_test if 'wallet' in state or 'walletinfo' in state: if 'balance' in state: balance_string = "Balance: " + "%0.8f" % state['balance'] + " " + unit if 'unconfirmedbalance' in state: if state['unconfirmedbalance'] != 0: balance_string += " (+" + "%0.8f" % state['unconfirmedbalance'] + " unconf)" if 'unconfirmed_balance' in state: if state['unconfirmed_balance'] != 0: balance_string += " (+" + "%0.8f" % state['unconfirmed_balance'] + " unconf)" window.addstr(0, 1, balance_string, curses.A_BOLD) if 'walletinfo' in state: if 'paytxfee' in state['walletinfo']: fee_string = "Fee: " + "%0.8f" % state['walletinfo']['paytxfee'] + " " + unit + " per kB" window.addstr(1, 1, fee_string, curses.A_BOLD) fee_string = "Wallet status: " if 'unlocked_until' in state['walletinfo']: if state['walletinfo']['unlocked_until'] == 0: fee_string += 'locked' window.addstr(2, 1, fee_string, curses.A_BOLD) else: try: fee_string += 'unlocked until ' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(state['walletinfo']['unlocked_until'])) except ValueError: fee_string += 'unlocked!!! (' + str(state['walletinfo']['unlocked_until'])+ ')' window.addstr(2, 1, fee_string, curses.A_BOLD + curses.A_REVERSE) else: fee_string += 'not encrypted!' window.addstr(2, 1, fee_string, curses.A_BOLD + curses.A_REVERSE) if state['wallet']['mode'] == 'tx': g.addstr_rjust(window, 0, "(W: refresh, A: list addresses, R: new address)", curses.A_BOLD, 1) g.addstr_rjust(window, 1, "(X: set tx fee, S: send " + unit + ")", curses.A_BOLD, 1) g.addstr_rjust(window, 2, "(E: export all txs, L: export wallet.dat)", curses.A_BOLD, 1) draw_transactions(state) elif state['wallet']['mode'] == 'settxfee': draw_fee_input_window(state, window, rpc_queue) elif state['wallet']['mode'] == 'newaddress': draw_new_address_window(state, window, rpc_queue) elif state['wallet']['mode'] == 'sendtoaddress': draw_send_coins_window(state, window, rpc_queue) elif state['wallet']['mode'] == 'backupwallet': draw_backup_wallet_window(state, window, rpc_queue) elif state['wallet']['mode'] == 'exporttx': draw_exporttx_window(state, window, rpc_queue) else: g.addstr_rjust(window, 0, "(W: refresh, A: list addresses, R: new address)", curses.A_BOLD, 1) g.addstr_rjust(window, 1, "(X: set tx fee, S: send " + unit + ")", curses.A_BOLD, 1) g.addstr_rjust(window, 2, "(E: export all txs, L: export wallet.dat)", curses.A_BOLD, 1) draw_addresses(state) else: if rpc_queue.qsize() > 0: g.addstr_cjust(win_header, 0, "...waiting for wallet information being processed...", curses.A_BOLD + curses.color_pair(3)) else: win_header.addstr(0, 1, "no wallet information loaded.", curses.A_BOLD + curses.color_pair(3)) win_header.addstr(1, 1, "press 'W' to refresh", curses.A_BOLD) win_header.refresh() footer.draw_window(state, rpc_queue)
def draw_window(state, window, rpc_queue=None): window.clear() window.refresh() win_header = curses.newwin(5, g.x, 0, 0) if 'browse_height' in state['blocks']: height = str(state['blocks']['browse_height']) if height in state['blocks']: blockdata = state['blocks'][height] win_header.addstr(0, 1, "Height: " + "{:,d}".format(int(height)), curses.A_BOLD) g.addstr_rjust( win_header, 0, "(J/K: browse, HOME/END: quicker, L: latest, G: seek)", curses.A_BOLD, 1) win_header.addstr(1, 1, "Hash: " + blockdata['hash'], curses.A_BOLD) win_header.addstr(2, 1, "Root: " + blockdata['merkleroot'], curses.A_BOLD) win_header.addstr( 3, 1, str("{:,d}".format(int(blockdata['size']))) + " bytes (" + str(blockdata['size'] / 1024) + " KB) ", curses.A_BOLD) g.addstr_cjust( win_header, 3, "Diff: " + "{:,d}".format(int(blockdata['difficulty'])), curses.A_BOLD, 0, 4, 2) g.addstr_rjust( win_header, 3, time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(blockdata['time'])), curses.A_BOLD, 1) version_bits = "{0:032b}".format(int(blockdata['version'])) version_bits = version_bits[0:7] + " " + version_bits[ 8:15] + " " + version_bits[16:23] + " " + version_bits[24:] win_header.addstr( 4, 1, "Version: " + str(blockdata['version']) + " (" + version_bits + ")", curses.A_BOLD) draw_transactions(state) state['blocks']['loaded'] = 1 else: if rpc_queue.qsize() > 0: g.addstr_cjust( win_header, 0, "...waiting for block information being processed...", curses.A_BOLD + curses.color_pair(3)) else: win_header.addstr(0, 1, "no block information loaded.", curses.A_BOLD + curses.color_pair(3)) win_header.addstr( 1, 1, "press 'G' to enter a block hash, height, or timestamp", curses.A_BOLD) else: if rpc_queue.qsize() > 0: g.addstr_cjust( win_header, 0, "...waiting for block information being processed...", curses.A_BOLD + curses.color_pair(3)) else: win_header.addstr(0, 1, "no block information loaded.", curses.A_BOLD + curses.color_pair(3)) win_header.addstr( 1, 1, "press 'G' to enter a block hash, height, or timestamp", curses.A_BOLD) win_header.refresh() footer.draw_window(state, rpc_queue)