You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bitcoind-ncurses2/main.py

262 lines
7.9 KiB
Python

# Copyright (c) 2014-2017 esotericnonsense (Daniel Edgecumbe)
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://opensource.org/licenses/mit-license.php
import argparse
import os
import asyncio
import datetime
import rpc
import interface
import modes
import splash
import header
import footer
import monitor
import peers
import block
import transaction
import net
import wallet
import console
async def keypress_loop(window, callback, resize_callback):
async def handle_keypress(key):
if key == "KEY_RESIZE":
y, x = window.getmaxyx()
await resize_callback(y, x)
return
if len(key) == 1 and key.lower() == "q":
raise Exception("Quitting.")
key = await callback(key)
if key is not None:
# Unhandled key. Don't care.
pass
while True:
# This is basically spinning which is really annoying.
# TODO: find a way of having async blocking getch/getkey.
try:
key = window.getkey()
except Exception:
await asyncio.sleep(0.05)
continue
await handle_keypress(key)
async def poll_client(client, method, callback, sleeptime, params=None):
# Allow the rest of the program to start.
await asyncio.sleep(0.1)
while True:
try:
d = await client.request(method, params=params)
except (rpc.RPCContentError, rpc.RPCTimeoutError):
# TODO: back off?
await asyncio.sleep(sleeptime)
continue
await callback(method, d)
await asyncio.sleep(sleeptime)
async def tick(callback, sleeptime):
# Allow the rest of the program to start.
await asyncio.sleep(0.1)
while True:
dt = datetime.datetime.utcnow()
await callback(dt)
await asyncio.sleep(sleeptime)
def initialize():
# parse commandline arguments
parser = argparse.ArgumentParser()
parser.add_argument("--datadir",
help="path to bitcoin datadir [~/.bitcoin/]",
default=os.path.expanduser("~/.bitcoin/"))
parser.add_argument("--no-splash",
help="whether to disable the splash screen [False]",
action='store_true',
dest="nosplash",
default=False)
args = parser.parse_args()
url = rpc.get_url_from_datadir(args.datadir)
auth = rpc.get_auth_from_datadir(args.datadir)
client = rpc.BitcoinRPCClient(url, auth)
return client, args.nosplash
def wallet_enabled(client):
""" Check if the wallet is enabled. """
async def check_getwalletinfo(client):
try:
d = await client.request("getwalletinfo")
except (rpc.RPCContentError, rpc.RPCTimeoutError):
# TODO: try again on timeout?
return False
try:
d["result"]["walletname"]
except (KeyError, TypeError):
return False
return True
# Ugly, a synchronous RPC request mechanism would be nice here.
check = asyncio.gather(check_getwalletinfo(client))
loop = asyncio.get_event_loop()
loop.run_until_complete(check)
if check.result():
return True
return False
def create_tasks(client, window, nosplash):
headerview = header.HeaderView()
footerview = footer.FooterView()
modehandler = modes.ModeHandler(
(headerview.on_mode_change, footerview.on_mode_change, ),
)
splashview = splash.SplashView(modehandler.set_mode)
monitorview = monitor.MonitorView(client)
peerview = peers.PeersView()
transactionstore = transaction.TransactionStore(client)
transactionview = transaction.TransactionView(transactionstore)
blockstore = block.BlockStore(client)
blockview = block.BlockView(
blockstore,
transactionview.set_txid,
modehandler.set_mode,
)
netview = net.NetView()
walletview = wallet.WalletView(
transactionview.set_txid,
modehandler.set_mode,
)
consoleview = console.ConsoleView(client)
modehandler.add_callback("monitor", monitorview.on_mode_change)
modehandler.add_callback("peers", peerview.on_mode_change)
modehandler.add_callback("block", blockview.on_mode_change)
modehandler.add_callback("transaction", transactionview.on_mode_change)
modehandler.add_callback("net", netview.on_mode_change)
modehandler.add_callback("wallet", walletview.on_mode_change)
modehandler.add_callback("console", consoleview.on_mode_change)
modehandler.add_keypress_handler("block", blockview.handle_keypress)
modehandler.add_keypress_handler("transaction", transactionview.handle_keypress)
modehandler.add_keypress_handler("wallet", walletview.handle_keypress)
modehandler.add_keypress_handler("console", consoleview.handle_keypress)
async def on_nettotals(key, obj):
await headerview.on_nettotals(key, obj)
await netview.on_nettotals(key, obj)
async def on_bestblockhash(key, obj):
await monitorview.on_bestblockhash(key, obj)
await blockview.on_bestblockhash(key, obj)
async def on_peerinfo(key, obj):
await headerview.on_peerinfo(key, obj)
await peerview.on_peerinfo(key, obj)
async def on_tick(dt):
await footerview.on_tick(dt)
await monitorview.on_tick(dt)
async def on_window_resize(y, x):
interface.check_min_window_size(y, x)
await splashview.on_window_resize(y, x)
await headerview.on_window_resize(y, x)
await footerview.on_window_resize(y, x)
await monitorview.on_window_resize(y, x)
await peerview.on_window_resize(y, x)
await blockview.on_window_resize(y, x)
await transactionview.on_window_resize(y, x)
await netview.on_window_resize(y, x)
await walletview.on_window_resize(y, x)
await consoleview.on_window_resize(y, x)
ty, tx = window.getmaxyx()
tasks = [
poll_client(client, "getbestblockhash",
on_bestblockhash, 1.0),
poll_client(client, "getblockchaininfo",
headerview.on_blockchaininfo, 5.0),
poll_client(client, "getnetworkinfo",
headerview.on_networkinfo, 5.0),
poll_client(client, "getnettotals",
on_nettotals, 5.0),
poll_client(client, "getpeerinfo",
on_peerinfo, 5.0),
poll_client(client, "getmempoolinfo",
monitorview.on_mempoolinfo, 5.0),
poll_client(client, "listsinceblock",
walletview.on_sinceblock, 5.0),
poll_client(client, "estimatesmartfee",
monitorview.on_estimatesmartfee, 15.0, params=[2]),
poll_client(client, "estimatesmartfee",
monitorview.on_estimatesmartfee, 15.0, params=[5]),
poll_client(client, "estimatesmartfee",
monitorview.on_estimatesmartfee, 15.0, params=[10]),
# This is a bit lazy because we could just do it once and calculate it.
poll_client(client, "uptime",
monitorview.on_uptime, 5.0, params=[10]),
tick(on_tick, 1.0),
keypress_loop(window, modehandler.handle_keypress, on_window_resize),
on_window_resize(ty, tx),
splashview.draw(nosplash),
]
if wallet_enabled(client):
tasks.append(
poll_client(client, "getwalletinfo", headerview.on_walletinfo, 1.0)
)
return tasks
def mainfn():
client, nosplash = initialize()
try:
window = interface.init_curses()
tasks = create_tasks(client, window, nosplash)
loop = asyncio.get_event_loop()
t = asyncio.gather(*tasks)
loop.run_until_complete(t)
finally:
try:
loop.close()
except BaseException:
pass
interface.end_curses()
if __name__ == "__main__":
mainfn()