zcashd/contrib/metrics/supply_check/__init__.py

80 lines
3.3 KiB
Python

#!/usr/bin/env python3
import os
import progressbar
from slickrpc.rpc import Proxy
from .deltas_mainnet import MainnetSupplyDeltas
from .theoretical import Network, MAINNET
COIN=100000000
# Returns the theoretical supply, the total block rewards claimed,
# and the block at the given height.
#
# - `zcashd` a slickrpc.rpc.Proxy that can be used to access zcashd
# - `deltas` a SupplyDeltas object tracking the deltas observed
# - `height` the block height to consider
# - `flag` Either `1` or `2`. This flag will be provided to the
# getblock RPC call to indicate how much data should be returned.
# When `1` is provided, data for transactions in the block will
# be limited to transaction identifiers; when `2` is provided
# full transaction data will be returned for each transaction in
# the block.
def TheoreticalAndEmpirical(zcashd, deltas, height, flag):
theoreticalSupply = Network(MAINNET).SupplyAfterHeight(height)
block = zcashd.getblock(str(height), flag)
measuredSupply = block['chainSupply']['chainValueZat']
empiricalMaximum = measuredSupply + deltas.DeviationAtHeight(height)
return (theoreticalSupply, empiricalMaximum, block)
# Returns `True` if the theoretical supply matches the empirically
# determined supply after accounting for known deltas over the specified
# range, or `False` if it was not possible to determine a miner address
# for a block containing unclaimed fee and/or block reward amounts.
#
# - `startHeight` The block height at which to begin checking
# - `endHeight` The end of the block height range to check (inclusive)
def Bisect(bar, zcashd, deltas, startHeight, endHeight):
assert startHeight <= endHeight
bar.update(startHeight)
flag = 2 if startHeight == endHeight else 1
(theoretical, empirical, block) = TheoreticalAndEmpirical(zcashd, deltas, endHeight, flag)
if theoretical == empirical:
return True
elif startHeight == endHeight:
return deltas.SaveMismatch(block, theoretical, empirical)
else:
midpoint = (startHeight + endHeight) // 2
return (Bisect(bar, zcashd, deltas, startHeight, midpoint) and
Bisect(bar, zcashd, deltas, midpoint + 1, endHeight))
def main():
zcashd = Proxy('http://{rpc_user}:{rpc_pass}@{rpc_host}:{rpc_port}'.format(
rpc_user=os.environ['ZCASHD_RPC_USER'],
rpc_pass=os.environ['ZCASHD_RPC_PASS'],
rpc_host=os.environ['ZCASHD_RPC_HOST'],
rpc_port=os.environ['ZCASHD_RPC_PORT'],
))
latestHeight = zcashd.getblockchaininfo()['blocks']
deltas = MainnetSupplyDeltas(zcashd)
(theoretical, empirical, block) = TheoreticalAndEmpirical(zcashd, deltas, latestHeight, 1)
if theoretical != empirical:
with progressbar.ProgressBar(max_value = latestHeight, redirect_stdout = True) as bar:
try:
Bisect(bar, zcashd, deltas, 0, latestHeight)
except KeyboardInterrupt:
pass
deltas.PrintDeltas()
print("Block height: {}".format(latestHeight))
print("Chain total value: {} ZEC".format(block['chainSupply']['chainValueZat'] / COIN))
print("Theoretical maximum supply: {} ZEC".format(theoretical / COIN))
print("Blocks with unclaimed balance: {}".format(deltas.delta_count))
print("Unclaimed total: {} ZEC".format(deltas.delta_total / COIN))
if __name__ == "__main__":
main()