2017-09-27 05:30:09 +00:00
|
|
|
# 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
|
2017-09-27 21:15:34 +00:00
|
|
|
import modes
|
2017-09-28 02:06:10 +00:00
|
|
|
import splash
|
2017-09-27 05:30:09 +00:00
|
|
|
import header
|
|
|
|
import footer
|
|
|
|
import monitor
|
|
|
|
import peers
|
2017-09-27 20:15:26 +00:00
|
|
|
import block
|
2017-09-30 04:42:35 +00:00
|
|
|
import transaction
|
2017-10-01 01:13:01 +00:00
|
|
|
import net
|
2017-10-01 06:44:22 +00:00
|
|
|
import wallet
|
2017-09-27 05:30:09 +00:00
|
|
|
|
|
|
|
|
2017-09-27 21:15:34 +00:00
|
|
|
async def keypress_loop(window, callback, resize_callback):
|
|
|
|
async def handle_keypress(key):
|
2017-09-27 16:18:09 +00:00
|
|
|
if key == "KEY_RESIZE":
|
|
|
|
y, x = window.getmaxyx()
|
|
|
|
await resize_callback(y, x)
|
|
|
|
return
|
|
|
|
|
2017-09-27 21:17:32 +00:00
|
|
|
if len(key) == 1 and key.lower() == "q":
|
|
|
|
raise Exception("Quitting.")
|
|
|
|
|
2017-09-27 21:15:34 +00:00
|
|
|
key = await callback(key)
|
|
|
|
if key is not None:
|
2017-09-27 21:17:32 +00:00
|
|
|
# Unhandled key. Don't care.
|
|
|
|
pass
|
2017-09-27 05:30:09 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2017-09-27 21:15:34 +00:00
|
|
|
await handle_keypress(key)
|
2017-09-27 05:30:09 +00:00
|
|
|
|
|
|
|
|
2017-09-27 18:19:54 +00:00
|
|
|
async def poll_client(client, method, callback, sleeptime, params=None):
|
2017-09-27 05:30:09 +00:00
|
|
|
# Allow the rest of the program to start.
|
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
|
|
|
|
while True:
|
2017-09-27 18:19:54 +00:00
|
|
|
j = await client.request(method, params=params)
|
2017-09-27 05:30:09 +00:00
|
|
|
await callback(method, j)
|
|
|
|
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/"))
|
2017-09-28 02:06:10 +00:00
|
|
|
parser.add_argument("--no-splash",
|
|
|
|
help="whether to disable the splash screen [False]",
|
|
|
|
action='store_true',
|
|
|
|
dest="nosplash",
|
|
|
|
default=False)
|
2017-09-27 05:30:09 +00:00
|
|
|
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)
|
|
|
|
|
2017-09-28 02:06:10 +00:00
|
|
|
return client, args.nosplash
|
2017-09-27 05:30:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
def check_disablewallet(client):
|
|
|
|
""" Check if the wallet is enabled. """
|
|
|
|
|
|
|
|
# Ugly, a synchronous RPC request mechanism would be nice here.
|
|
|
|
x = asyncio.gather(client.request("getwalletinfo"))
|
|
|
|
loop = asyncio.get_event_loop()
|
|
|
|
loop.run_until_complete(x)
|
|
|
|
|
|
|
|
try:
|
|
|
|
x.result()[0]["result"]["walletname"]
|
|
|
|
except (KeyError, TypeError):
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2017-09-28 02:06:10 +00:00
|
|
|
def create_tasks(client, window, nosplash):
|
2017-09-27 05:30:09 +00:00
|
|
|
headerview = header.HeaderView()
|
|
|
|
footerview = footer.FooterView()
|
|
|
|
|
2017-09-28 02:06:10 +00:00
|
|
|
modehandler = modes.ModeHandler(
|
|
|
|
(headerview.on_mode_change, footerview.on_mode_change, ),
|
|
|
|
)
|
|
|
|
|
|
|
|
splashview = splash.SplashView(modehandler.set_mode)
|
2017-09-27 21:15:34 +00:00
|
|
|
|
2017-09-27 05:30:09 +00:00
|
|
|
monitorview = monitor.MonitorView(client)
|
|
|
|
peerview = peers.PeersView()
|
2017-09-27 20:15:26 +00:00
|
|
|
|
2017-09-30 04:42:35 +00:00
|
|
|
transactionstore = transaction.TransactionStore(client)
|
|
|
|
transactionview = transaction.TransactionView(transactionstore)
|
|
|
|
|
2017-09-27 20:15:26 +00:00
|
|
|
blockstore = block.BlockStore(client)
|
2017-09-30 04:42:35 +00:00
|
|
|
blockview = block.BlockView(
|
|
|
|
blockstore,
|
|
|
|
transactionview.set_txid,
|
|
|
|
modehandler.set_mode,
|
|
|
|
)
|
2017-09-27 20:15:26 +00:00
|
|
|
|
2017-10-01 01:13:01 +00:00
|
|
|
netview = net.NetView()
|
2017-10-01 06:44:22 +00:00
|
|
|
walletview = wallet.WalletView(
|
|
|
|
transactionview.set_txid,
|
|
|
|
modehandler.set_mode,
|
|
|
|
)
|
2017-10-01 01:13:01 +00:00
|
|
|
|
2017-09-27 21:15:34 +00:00
|
|
|
modehandler.add_callback("monitor", monitorview.on_mode_change)
|
|
|
|
modehandler.add_callback("peers", peerview.on_mode_change)
|
|
|
|
modehandler.add_callback("block", blockview.on_mode_change)
|
2017-09-30 04:42:35 +00:00
|
|
|
modehandler.add_callback("transaction", transactionview.on_mode_change)
|
2017-10-01 01:13:01 +00:00
|
|
|
modehandler.add_callback("net", netview.on_mode_change)
|
2017-10-01 06:44:22 +00:00
|
|
|
modehandler.add_callback("wallet", walletview.on_mode_change)
|
2017-09-27 20:15:26 +00:00
|
|
|
|
2017-09-27 22:21:40 +00:00
|
|
|
modehandler.add_keypress_handler("block", blockview.handle_keypress)
|
2017-09-30 04:42:35 +00:00
|
|
|
modehandler.add_keypress_handler("transaction", transactionview.handle_keypress)
|
2017-10-01 06:44:22 +00:00
|
|
|
modehandler.add_keypress_handler("wallet", walletview.handle_keypress)
|
2017-09-27 22:21:40 +00:00
|
|
|
|
2017-10-01 01:13:01 +00:00
|
|
|
async def on_nettotals(key, obj):
|
|
|
|
await headerview.on_nettotals(key, obj)
|
|
|
|
await netview.on_nettotals(key, obj)
|
|
|
|
|
2017-09-27 20:15:26 +00:00
|
|
|
async def on_bestblockhash(key, obj):
|
|
|
|
await monitorview.on_bestblockhash(key, obj)
|
|
|
|
await blockview.on_bestblockhash(key, obj)
|
2017-09-27 05:30:09 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2017-09-27 16:18:09 +00:00
|
|
|
async def on_window_resize(y, x):
|
|
|
|
interface.check_min_window_size(y, x)
|
|
|
|
|
2017-09-28 02:06:10 +00:00
|
|
|
await splashview.on_window_resize(y, x)
|
2017-09-27 16:18:09 +00:00
|
|
|
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)
|
2017-09-27 20:15:26 +00:00
|
|
|
await blockview.on_window_resize(y, x)
|
2017-09-30 04:42:35 +00:00
|
|
|
await transactionview.on_window_resize(y, x)
|
2017-10-01 01:13:01 +00:00
|
|
|
await netview.on_window_resize(y, x)
|
2017-10-01 06:44:22 +00:00
|
|
|
await walletview.on_window_resize(y, x)
|
2017-09-27 16:18:09 +00:00
|
|
|
|
|
|
|
ty, tx = window.getmaxyx()
|
2017-09-27 05:30:09 +00:00
|
|
|
tasks = [
|
|
|
|
poll_client(client, "getbestblockhash",
|
2017-09-27 20:15:26 +00:00
|
|
|
on_bestblockhash, 1.0),
|
2017-09-27 05:30:09 +00:00
|
|
|
poll_client(client, "getblockchaininfo",
|
|
|
|
headerview.on_blockchaininfo, 5.0),
|
|
|
|
poll_client(client, "getnetworkinfo",
|
|
|
|
headerview.on_networkinfo, 5.0),
|
|
|
|
poll_client(client, "getnettotals",
|
2017-10-01 01:13:01 +00:00
|
|
|
on_nettotals, 5.0),
|
2017-09-27 05:30:09 +00:00
|
|
|
poll_client(client, "getpeerinfo",
|
|
|
|
on_peerinfo, 5.0),
|
2017-09-27 18:09:16 +00:00
|
|
|
poll_client(client, "getmempoolinfo",
|
|
|
|
monitorview.on_mempoolinfo, 5.0),
|
2017-10-01 06:44:22 +00:00
|
|
|
poll_client(client, "listsinceblock",
|
|
|
|
walletview.on_sinceblock, 5.0),
|
2017-09-27 18:19:54 +00:00
|
|
|
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]),
|
2017-09-27 19:42:19 +00:00
|
|
|
# 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]),
|
2017-09-27 05:30:09 +00:00
|
|
|
tick(on_tick, 1.0),
|
2017-09-28 02:06:10 +00:00
|
|
|
keypress_loop(window, modehandler.handle_keypress, on_window_resize),
|
|
|
|
on_window_resize(ty, tx),
|
|
|
|
splashview.draw(nosplash),
|
2017-09-27 05:30:09 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
if not check_disablewallet(client):
|
|
|
|
tasks.append(
|
|
|
|
poll_client(client, "getwalletinfo", headerview.on_walletinfo, 1.0)
|
|
|
|
)
|
|
|
|
|
|
|
|
return tasks
|
|
|
|
|
|
|
|
|
|
|
|
def mainfn():
|
2017-09-28 02:06:10 +00:00
|
|
|
client, nosplash = initialize()
|
2017-09-27 05:30:09 +00:00
|
|
|
|
2017-09-27 18:09:16 +00:00
|
|
|
|
2017-09-27 05:30:09 +00:00
|
|
|
try:
|
|
|
|
window = interface.init_curses()
|
2017-09-27 18:09:16 +00:00
|
|
|
|
2017-09-28 02:06:10 +00:00
|
|
|
tasks = create_tasks(client, window, nosplash)
|
2017-09-27 05:30:09 +00:00
|
|
|
|
|
|
|
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()
|