80 lines
3.3 KiB
Python
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()
|