|
#!/usr/bin/python |
|
# -*- coding: utf-8 -*- |
|
|
|
import json |
|
import time |
|
import subprocess |
|
import sys |
|
from prometheus_client import start_http_server, Gauge, Counter |
|
|
|
# Create Prometheus metrics to track zcashd stats. |
|
ZCASH_BLOCKS = Gauge('zcash_blocks', 'Block height') |
|
ZCASH_DIFFICULTY = Gauge('zcash_difficulty', 'Difficulty') |
|
ZCASH_PEERS = Gauge('zcash_peers', 'Number of peers') |
|
ZCASH_SOLS = Gauge('zcash_sols', 'Estimated network solutions per second') |
|
|
|
ZCASH_ERRORS = Counter('zcash_errors', 'Number of errors detected') |
|
|
|
ZCASH_MEMPOOL_BYTES = Gauge('zcash_mempool_bytes', 'Size of mempool in bytes') |
|
ZCASH_MEMPOOL_SIZE = Gauge('zcash_mempool_size', 'Number of unconfirmed transactions in mempool') |
|
|
|
ZCASH_LATEST_BLOCK_SIZE = Gauge('zcash_latest_block_size', 'Size of latest block in bytes') |
|
ZCASH_LATEST_BLOCK_TXS = Gauge('zcash_latest_block_txs', 'Number of transactions in latest block') |
|
|
|
ZCASH_CHAINFORK_LOCATION = Gauge('zcash_chainfork_location', 'Block height of chain fork') |
|
ZCASH_CHAINFORK_SIZE = Gauge('zcash_chainfork_size', 'Length of chain fork') |
|
|
|
ZCASH_TOTAL_BYTES_RECV = Gauge('zcash_total_bytes_recv', 'Total bytes received') |
|
ZCASH_TOTAL_BYTES_SENT = Gauge('zcash_total_bytes_sent', 'Total bytes sent') |
|
|
|
ZCASH_LATEST_BLOCK_INPUTS = Gauge('zcash_latest_block_inputs', 'Number of inputs in transactions of latest block') |
|
ZCASH_LATEST_BLOCK_OUTPUTS = Gauge('zcash_latest_block_outputs', 'Number of outputs in transactions of latest block') |
|
ZCASH_LATEST_BLOCK_JOINSPLITS = Gauge('zcash_latest_block_joinsplits', 'Number of joinsplits in transactions of latest block') |
|
|
|
ZCASH_NUM_TRANSPARENT_TX = Gauge('zcash_num_transparent_tx', 'Number of fully transparent transactions in latest block') |
|
ZCASH_NUM_SHIELDED_TX = Gauge('zcash_num_shielded_tx', 'Number of fully shielded transactions in latest block') |
|
ZCASH_NUM_MIXED_TX = Gauge('zcash_num_mixed_tx', 'Number of mixed transactions in latest block') |
|
|
|
def find_zcash_cli(): |
|
if sys.version_info[0] < 3: |
|
from whichcraft import which |
|
if sys.version_info[0] >= 3: |
|
from shutil import which |
|
return which('zcash-cli') |
|
|
|
ZCASH_CLI_PATH = str(find_zcash_cli()) |
|
|
|
def zcash(cmd): |
|
zcash = subprocess.Popen([ZCASH_CLI_PATH, cmd], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) |
|
output = zcash.communicate()[0] |
|
return json.loads(output.decode('utf-8')) |
|
|
|
|
|
def zcashcli(cmd): |
|
zcash = subprocess.Popen([ZCASH_CLI_PATH, cmd], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) |
|
output = zcash.communicate()[0] |
|
return output.decode('utf-8') |
|
|
|
|
|
def get_block(block_height): |
|
try: |
|
block = subprocess.check_output([ZCASH_CLI_PATH, 'getblock', block_height]) |
|
except Exception as e: |
|
print(e) |
|
print('Error: Can\'t retrieve block number ' + block_height + ' from zcashd.') |
|
return None |
|
return json.loads(block.decode('utf-8')) |
|
|
|
|
|
def get_raw_tx(txid): |
|
try: |
|
rawtx = subprocess.check_output([ZCASH_CLI_PATH, 'getrawtransaction', txid, '1']) |
|
except Exception as e: |
|
print(e) |
|
print('Error: Can\'t retrieve raw transaction ' + txid + ' from zcashd.') |
|
return None |
|
return json.loads(rawtx.decode('utf-8')) |
|
|
|
|
|
def main(): |
|
# Start up the server to expose the metrics. |
|
start_http_server(9000) |
|
while True: |
|
info = zcash('getinfo') |
|
chaintips = zcash('getchaintips') |
|
mempool = zcash('getmempoolinfo') |
|
nettotals = zcash('getnettotals') |
|
latest_block = get_block(str(info['blocks'])) |
|
solutions = int(zcashcli('getnetworkhashps')) |
|
|
|
ZCASH_BLOCKS.set(info['blocks']) |
|
ZCASH_PEERS.set(info['connections']) |
|
ZCASH_DIFFICULTY.set(info['difficulty']) |
|
ZCASH_SOLS.set(solutions) |
|
|
|
if info['errors']: |
|
ZCASH_ERRORS.inc() |
|
|
|
ZCASH_MEMPOOL_BYTES.set(mempool['bytes']) |
|
ZCASH_MEMPOOL_SIZE.set(mempool['size']) |
|
|
|
ZCASH_TOTAL_BYTES_RECV.set(nettotals['totalbytesrecv']) |
|
ZCASH_TOTAL_BYTES_SENT.set(nettotals['totalbytessent']) |
|
|
|
ZCASH_LATEST_BLOCK_SIZE.set(latest_block['size']) |
|
ZCASH_LATEST_BLOCK_TXS.set(len(latest_block['tx'])) |
|
|
|
inputs, outputs, joinsplits = 0, 0, 0 |
|
fullyshielded, fullytransparent, mixed = 0, 0, 0 |
|
# counting transaction inputs and outputs requires txindex=1 |
|
# to be enabled, which may also necessitate reindex=1 in zcash.conf |
|
if latest_block is not None: |
|
for tx in latest_block['tx']: |
|
if get_raw_tx(tx) is not None: |
|
rawtx = get_raw_tx(tx) |
|
i = len(rawtx['vin']) |
|
inputs += i |
|
o = len(rawtx['vout']) |
|
outputs += o |
|
j = len(rawtx['vjoinsplit']) |
|
joinsplits += j |
|
if j == 0: |
|
fullytransparent += 1 |
|
elif i + o == 0: |
|
fullyshielded += 1 |
|
else: |
|
mixed += 1 |
|
|
|
ZCASH_LATEST_BLOCK_INPUTS.set(inputs) |
|
ZCASH_LATEST_BLOCK_OUTPUTS.set(outputs) |
|
ZCASH_LATEST_BLOCK_JOINSPLITS.set(joinsplits) |
|
|
|
ZCASH_NUM_TRANSPARENT_TX.set(fullytransparent) |
|
ZCASH_NUM_SHIELDED_TX.set(fullyshielded) |
|
ZCASH_NUM_MIXED_TX.set(mixed) |
|
|
|
for chaintip in chaintips: |
|
if chaintip['height'] > (info['blocks'] - 1000): |
|
if chaintip['branchlen'] >= 2: |
|
ZCASH_CHAINFORK_LOCATION.set(chaintip['height']) |
|
ZCASH_CHAINFORK_SIZE.set(chaintip['branchlen']) |
|
break |
|
|
|
time.sleep(120) |
|
|
|
|
|
if __name__ == '__main__': |
|
main() |