[monitor] Prevent crashing during IBD (handle RPCErrors)

We also now ensure that the best block, coinbase etc are in sync with the
bestblockhash when we draw them.
master
Daniel Edgecumbe 7 years ago
parent 6fd0ce36de
commit df986b9b8d

@ -9,6 +9,7 @@ import asyncio
from decimal import Decimal
import view
from rpc import RPCError
class MonitorView(view.View):
@ -21,7 +22,7 @@ class MonitorView(view.View):
self._bestblockhash = None
self._bestblockheader = None # raw json blockheader
self._bestblock = None # raw json block
self._bestcoinbase = None # raw json tx
self._bestcoinbase = (None, None) # (blockhash, raw json tx)
self._mempoolinfo = None # raw mempoolinfo
self._estimatesmartfee = {} # blocks -> feerate/kB
self._dt = None
@ -32,83 +33,6 @@ class MonitorView(view.View):
async def _draw(self):
self._clear_init_pad()
if self._bestblockheader:
bbh = self._bestblockheader
self._pad.addstr(0, 1, "Height: {: 8d}".format(bbh["height"]))
self._pad.addstr(0, 36, bbh["hash"])
if self._bestblock:
bb = self._bestblock
self._pad.addstr(1, 1, "Size: {: 8d} bytes Weight: {: 8d} WU".format(
bb["size"],
bb["weight"]
))
self._pad.addstr(1, 64, "Block timestamp: {}".format(
datetime.datetime.utcfromtimestamp(bb["time"]),
))
if self._dt:
stampdelta = int(
(self._dt - datetime.datetime.utcfromtimestamp(bb["time"]))
.total_seconds())
if stampdelta > 3600*3: # probably syncing
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)"
self._pad.addstr(2, 64, "Age: {}".format(
stampdelta_string))
self._pad.addstr(2, 1, "Transactions: {} ({} bytes/tx, {} WU/tx)".format(
len(bb["tx"]),
bb["size"] // len(bb["tx"]),
bb["weight"] // len(bb["tx"]),
))
if self._bestcoinbase:
bcb = self._bestcoinbase
reward = sum(vout["value"] for vout in bcb["vout"])
# TODO: if chain is regtest, this is different
halvings = bb["height"] // 210000
block_subsidy = Decimal(50 * (0.5 ** halvings))
total_fees = Decimal(reward) - block_subsidy
self._pad.addstr(4, 1, "Block reward: {:.6f} BTC".format(
reward))
if len(bb["tx"]) > 1:
if reward > 0:
fee_pct = total_fees * 100 / Decimal(reward)
else:
fee_pct = 0
mbtc_per_tx = (total_fees / (len(bb["tx"]) - 1)) * 1000
# 80 bytes for the block header.
total_tx_size = bb["size"] - 80 - bcb["size"]
if total_tx_size > 0:
sat_per_kb = ((total_fees * 1024) / total_tx_size) * 100000000
else:
sat_per_kb = 0
self._pad.addstr(4, 34, "Fees: {: 8.6f} BTC ({: 6.2f}%, avg {: 6.2f} mBTC/tx, ~{: 7.0f} sat/kB)".format(total_fees, fee_pct, mbtc_per_tx, sat_per_kb))
self._pad.addstr(6, 1, "Diff: {:,}".format(
int(bb["difficulty"]),
))
self._pad.addstr(7, 1, "Chain work: 2**{:.6f}".format(
math.log(int(bb["chainwork"], 16), 2),
))
if self._mempoolinfo:
self._pad.addstr(9, 1, "Mempool transactions: {: 6d} ({: 5.2f} MiB)".format(
self._mempoolinfo["size"],
@ -125,6 +49,100 @@ class MonitorView(view.View):
if self._uptime:
self._pad.addstr(13, 1, "uptime: {}".format(datetime.timedelta(seconds=self._uptime)))
bbh = self._bestblockhash
if not bbh:
self._draw_pad_to_screen()
return
self._pad.addstr(0, 36, bbh)
bbhd = self._bestblockheader
if not bbhd or bbhd["hash"] != bbh:
self._draw_pad_to_screen()
return
self._pad.addstr(0, 1, "Height: {: 8d}".format(bbhd["height"]))
bb = self._bestblock
if not bb or bb["hash"] != bbh:
self._draw_pad_to_screen()
return
bb = self._bestblock
self._pad.addstr(1, 1, "Size: {: 8d} bytes Weight: {: 8d} WU".format(
bb["size"],
bb["weight"]
))
self._pad.addstr(1, 64, "Block timestamp: {}".format(
datetime.datetime.utcfromtimestamp(bb["time"]),
))
self._pad.addstr(2, 1, "Transactions: {} ({} bytes/tx, {} WU/tx)".format(
len(bb["tx"]),
bb["size"] // len(bb["tx"]),
bb["weight"] // len(bb["tx"]),
))
self._pad.addstr(6, 1, "Diff: {:,}".format(
int(bb["difficulty"]),
))
self._pad.addstr(7, 1, "Chain work: 2**{:.6f}".format(
math.log(int(bb["chainwork"], 16), 2),
))
if self._dt:
stampdelta = int(
(self._dt - datetime.datetime.utcfromtimestamp(bb["time"]))
.total_seconds())
if stampdelta > 3600*3: # probably syncing
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)"
self._pad.addstr(2, 64, "Age: {}".format(
stampdelta_string))
# TODO: check the coinbase is associated with this block.
(h, bcb) = self._bestcoinbase
if not bcb or h != bbh:
self._draw_pad_to_screen()
return
reward = sum(vout["value"] for vout in bcb["vout"])
# TODO: if chain is regtest, this is different
halvings = bb["height"] // 210000
block_subsidy = Decimal(50 * (0.5 ** halvings))
total_fees = Decimal(reward) - block_subsidy
self._pad.addstr(4, 1, "Block reward: {:.6f} BTC".format(
reward))
if len(bb["tx"]) > 1:
if reward > 0:
fee_pct = total_fees * 100 / Decimal(reward)
else:
fee_pct = 0
mbtc_per_tx = (total_fees / (len(bb["tx"]) - 1)) * 1000
# 80 bytes for the block header.
total_tx_size = bb["size"] - 80 - bcb["size"]
if total_tx_size > 0:
sat_per_kb = ((total_fees * 1024) / total_tx_size) * 100000000
else:
sat_per_kb = 0
self._pad.addstr(4, 34, "Fees: {: 8.6f} BTC ({: 6.2f}%, avg {: 6.2f} mBTC/tx, ~{: 7.0f} sat/kB)".format(total_fees, fee_pct, mbtc_per_tx, sat_per_kb))
self._draw_pad_to_screen()
async def _draw_if_visible(self):
@ -133,6 +151,26 @@ class MonitorView(view.View):
if self._visible:
await self._draw()
async def _request_bestblockhash_info(self, bestblockhash):
try:
j = await self._client.request("getblockheader", [bestblockhash])
self._bestblockheader = j["result"]
except RPCError:
return
try:
j = await self._client.request("getblock", [bestblockhash])
h = j["result"]["hash"]
self._bestblock = j["result"]
except (RPCError, KeyError):
return
try:
j = await self._client.request("getrawtransaction", [j["result"]["tx"][0], 1])
self._bestcoinbase = (h, j["result"])
except RPCError:
return
async def on_bestblockhash(self, key, obj):
try:
bestblockhash = obj["result"]
@ -144,15 +182,7 @@ class MonitorView(view.View):
if bestblockhash != self._bestblockhash:
draw = True
self._bestblockhash = bestblockhash
j = await self._client.request("getblockheader", [bestblockhash])
self._bestblockheader = j["result"]
j = await self._client.request("getblock", [bestblockhash])
self._bestblock = j["result"]
j = await self._client.request("getrawtransaction", [j["result"]["tx"][0], 1])
self._bestcoinbase = j["result"]
await self._request_bestblockhash_info(bestblockhash)
if draw:
await self._draw_if_visible()

Loading…
Cancel
Save