Add script for verifying block rewards and fees not claimed by miners.
Co-authored-by: Jack Grigg <jack@z.cash> Co-authored-by: Daira Hopwood <daira@jacaranda.org>
This commit is contained in:
parent
2fd52ada51
commit
810c191216
|
@ -130,3 +130,5 @@ src/fuzzing/*/output
|
||||||
src/fuzz.cpp
|
src/fuzz.cpp
|
||||||
|
|
||||||
.updatecheck-token
|
.updatecheck-token
|
||||||
|
.env
|
||||||
|
poetry.lock
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
[tool.poetry]
|
||||||
|
name = "zcash-metrics"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Zcash Metrics"
|
||||||
|
authors = [
|
||||||
|
"Jack Grigg <jack@electriccoin.co>",
|
||||||
|
"Daira Hopwood <daira@jacaranda.org>",
|
||||||
|
"Kris Nuttycombe <kris@nutty.land>",
|
||||||
|
]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
readme = "README.md"
|
||||||
|
homepage = "https://github.com/zcash/zcash/"
|
||||||
|
repository = "https://github.com/zcash/zcash/"
|
||||||
|
documentation = "https://github.com/zcash/zcash/"
|
||||||
|
classifiers = [
|
||||||
|
"Private :: Do Not Upload",
|
||||||
|
]
|
||||||
|
packages = [
|
||||||
|
{ include = "supply_check" }
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.7"
|
||||||
|
slick-bitcoinrpc = "0.1.4"
|
||||||
|
progressbar2 = "4.2.0"
|
||||||
|
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
supply-check = "supply_check:main"
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
#!/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()
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,104 @@
|
||||||
|
fr_addrs = set([
|
||||||
|
# FR addresses
|
||||||
|
"t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd",
|
||||||
|
"t3cL9AucCajm3HXDhb5jBnJK2vapVoXsop3",
|
||||||
|
"t3fqvkzrrNaMcamkQMwAyHRjfDdM2xQvDTR",
|
||||||
|
"t3TgZ9ZT2CTSK44AnUPi6qeNaHa2eC7pUyF",
|
||||||
|
"t3SpkcPQPfuRYHsP5vz3Pv86PgKo5m9KVmx",
|
||||||
|
"t3Xt4oQMRPagwbpQqkgAViQgtST4VoSWR6S",
|
||||||
|
"t3ayBkZ4w6kKXynwoHZFUSSgXRKtogTXNgb",
|
||||||
|
"t3adJBQuaa21u7NxbR8YMzp3km3TbSZ4MGB",
|
||||||
|
"t3K4aLYagSSBySdrfAGGeUd5H9z5Qvz88t2",
|
||||||
|
"t3RYnsc5nhEvKiva3ZPhfRSk7eyh1CrA6Rk",
|
||||||
|
"t3Ut4KUq2ZSMTPNE67pBU5LqYCi2q36KpXQ",
|
||||||
|
"t3ZnCNAvgu6CSyHm1vWtrx3aiN98dSAGpnD",
|
||||||
|
"t3fB9cB3eSYim64BS9xfwAHQUKLgQQroBDG",
|
||||||
|
"t3cwZfKNNj2vXMAHBQeewm6pXhKFdhk18kD",
|
||||||
|
"t3YcoujXfspWy7rbNUsGKxFEWZqNstGpeG4",
|
||||||
|
"t3bLvCLigc6rbNrUTS5NwkgyVrZcZumTRa4",
|
||||||
|
"t3VvHWa7r3oy67YtU4LZKGCWa2J6eGHvShi",
|
||||||
|
"t3eF9X6X2dSo7MCvTjfZEzwWrVzquxRLNeY",
|
||||||
|
"t3esCNwwmcyc8i9qQfyTbYhTqmYXZ9AwK3X",
|
||||||
|
"t3M4jN7hYE2e27yLsuQPPjuVek81WV3VbBj",
|
||||||
|
"t3gGWxdC67CYNoBbPjNvrrWLAWxPqZLxrVY",
|
||||||
|
"t3LTWeoxeWPbmdkUD3NWBquk4WkazhFBmvU",
|
||||||
|
"t3P5KKX97gXYFSaSjJPiruQEX84yF5z3Tjq",
|
||||||
|
"t3f3T3nCWsEpzmD35VK62JgQfFig74dV8C9",
|
||||||
|
"t3Rqonuzz7afkF7156ZA4vi4iimRSEn41hj",
|
||||||
|
"t3fJZ5jYsyxDtvNrWBeoMbvJaQCj4JJgbgX",
|
||||||
|
"t3Pnbg7XjP7FGPBUuz75H65aczphHgkpoJW",
|
||||||
|
"t3WeKQDxCijL5X7rwFem1MTL9ZwVJkUFhpF",
|
||||||
|
"t3Y9FNi26J7UtAUC4moaETLbMo8KS1Be6ME",
|
||||||
|
"t3aNRLLsL2y8xcjPheZZwFy3Pcv7CsTwBec",
|
||||||
|
"t3gQDEavk5VzAAHK8TrQu2BWDLxEiF1unBm",
|
||||||
|
"t3Rbykhx1TUFrgXrmBYrAJe2STxRKFL7G9r",
|
||||||
|
"t3aaW4aTdP7a8d1VTE1Bod2yhbeggHgMajR",
|
||||||
|
"t3YEiAa6uEjXwFL2v5ztU1fn3yKgzMQqNyo",
|
||||||
|
"t3g1yUUwt2PbmDvMDevTCPWUcbDatL2iQGP",
|
||||||
|
"t3dPWnep6YqGPuY1CecgbeZrY9iUwH8Yd4z",
|
||||||
|
"t3QRZXHDPh2hwU46iQs2776kRuuWfwFp4dV",
|
||||||
|
"t3enhACRxi1ZD7e8ePomVGKn7wp7N9fFJ3r",
|
||||||
|
"t3PkLgT71TnF112nSwBToXsD77yNbx2gJJY",
|
||||||
|
"t3LQtHUDoe7ZhhvddRv4vnaoNAhCr2f4oFN",
|
||||||
|
"t3fNcdBUbycvbCtsD2n9q3LuxG7jVPvFB8L",
|
||||||
|
"t3dKojUU2EMjs28nHV84TvkVEUDu1M1FaEx",
|
||||||
|
"t3aKH6NiWN1ofGd8c19rZiqgYpkJ3n679ME",
|
||||||
|
"t3MEXDF9Wsi63KwpPuQdD6by32Mw2bNTbEa",
|
||||||
|
"t3WDhPfik343yNmPTqtkZAoQZeqA83K7Y3f",
|
||||||
|
"t3PSn5TbMMAEw7Eu36DYctFezRzpX1hzf3M",
|
||||||
|
"t3R3Y5vnBLrEn8L6wFjPjBLnxSUQsKnmFpv",
|
||||||
|
"t3Pcm737EsVkGTbhsu2NekKtJeG92mvYyoN",
|
||||||
|
# ECC funding stream addresses
|
||||||
|
"t3LmX1cxWPPPqL4TZHx42HU3U5ghbFjRiif",
|
||||||
|
"t3Toxk1vJQ6UjWQ42tUJz2rV2feUWkpbTDs",
|
||||||
|
"t3ZBdBe4iokmsjdhMuwkxEdqMCFN16YxKe6",
|
||||||
|
"t3ZuaJziLM8xZ32rjDUzVjVtyYdDSz8GLWB",
|
||||||
|
"t3bAtYWa4bi8VrtvqySxnbr5uqcG9czQGTZ",
|
||||||
|
"t3dktADfb5Rmxncpe1HS5BRS5Gcj7MZWYBi",
|
||||||
|
"t3hgskquvKKoCtvxw86yN7q8bzwRxNgUZmc",
|
||||||
|
"t3R1VrLzwcxAZzkX4mX3KGbWpNsgtYtMntj",
|
||||||
|
"t3ff6fhemqPMVujD3AQurxRxTdvS1pPSaa2",
|
||||||
|
"t3cEUQFG3KYnFG6qYhPxSNgGi3HDjUPwC3J",
|
||||||
|
"t3WR9F5U4QvUFqqx9zFmwT6xFqduqRRXnaa",
|
||||||
|
"t3PYc1LWngrdUrJJbHkYPCKvJuvJjcm85Ch",
|
||||||
|
"t3bgkjiUeatWNkhxY3cWyLbTxKksAfk561R",
|
||||||
|
"t3Z5rrR8zahxUpZ8itmCKhMSfxiKjUp5Dk5",
|
||||||
|
"t3PU1j7YW3fJ67jUbkGhSRto8qK2qXCUiW3",
|
||||||
|
"t3S3yaT7EwNLaFZCamfsxxKwamQW2aRGEkh",
|
||||||
|
"t3eutXKJ9tEaPSxZpmowhzKhPfJvmtwTEZK",
|
||||||
|
"t3gbTb7brxLdVVghSPSd3ycGxzHbUpukeDm",
|
||||||
|
"t3UCKW2LrHFqPMQFEbZn6FpjqnhAAbfpMYR",
|
||||||
|
"t3NyHsrnYbqaySoQqEQRyTWkjvM2PLkU7Uu",
|
||||||
|
"t3QEFL6acxuZwiXtW3YvV6njDVGjJ1qeaRo",
|
||||||
|
"t3PdBRr2S1XTDzrV8bnZkXF3SJcrzHWe1wj",
|
||||||
|
"t3ZWyRPpWRo23pKxTLtWsnfEKeq9T4XPxKM",
|
||||||
|
"t3he6QytKCTydhpztykFsSsb9PmBT5JBZLi",
|
||||||
|
"t3VWxWDsLb2TURNEP6tA1ZSeQzUmPKFNxRY",
|
||||||
|
"t3NmWLvZkbciNAipauzsFRMxoZGqmtJksbz",
|
||||||
|
"t3cKr4YxVPvPBG1mCvzaoTTdBNokohsRJ8n",
|
||||||
|
"t3T3smGZn6BoSFXWWXa1RaoQdcyaFjMfuYK",
|
||||||
|
"t3gkDUe9Gm4GGpjMk86TiJZqhztBVMiUSSA",
|
||||||
|
"t3eretuBeBXFHe5jAqeSpUS1cpxVh51fAeb",
|
||||||
|
"t3dN8g9zi2UGJdixGe9txeSxeofLS9t3yFQ",
|
||||||
|
"t3S799pq9sYBFwccRecoTJ3SvQXRHPrHqvx",
|
||||||
|
"t3fhYnv1S5dXwau7GED3c1XErzt4n4vDxmf",
|
||||||
|
"t3cmE3vsBc5xfDJKXXZdpydCPSdZqt6AcNi",
|
||||||
|
"t3h5fPdjJVHaH4HwynYDM5BB3J7uQaoUwKi",
|
||||||
|
"t3Ma35c68BgRX8sdLDJ6WR1PCrKiWHG4Da9",
|
||||||
|
"t3LokMKPL1J8rkJZvVpfuH7dLu6oUWqZKQK",
|
||||||
|
"t3WFFGbEbhJWnASZxVLw2iTJBZfJGGX73mM",
|
||||||
|
"t3L8GLEsUn4QHNaRYcX3EGyXmQ8kjpT1zTa",
|
||||||
|
"t3PgfByBhaBSkH8uq4nYJ9ZBX4NhGCJBVYm",
|
||||||
|
"t3WecsqKDhWXD4JAgBVcnaCC2itzyNZhJrv",
|
||||||
|
"t3ZG9cSfopnsMQupKW5v9sTotjcP5P6RTbn",
|
||||||
|
"t3hC1Ywb5zDwUYYV8LwhvF5rZ6m49jxXSG5",
|
||||||
|
"t3VgMqDL15ZcyQDeqBsBW3W6rzfftrWP2yB",
|
||||||
|
"t3LC94Y6BwLoDtBoK2NuewaEbnko1zvR9rm",
|
||||||
|
"t3cWCUZJR3GtALaTcatrrpNJ3MGbMFVLRwQ",
|
||||||
|
"t3YYF4rPLVxDcF9hHFsXyc5Yq1TFfbojCY6",
|
||||||
|
"t3XHAGxRP2FNfhAjxGjxbrQPYtQQjc3RCQD",
|
||||||
|
# ZF funding stream addresses
|
||||||
|
"t3dvVE3SQEi7kqNzwrfNePxZ1d4hUyztBA1",
|
||||||
|
# MG funding stream addresses
|
||||||
|
"t3XyYW8yBFRuMnfvm5KLGFbEVz25kckZXym",
|
||||||
|
])
|
|
@ -0,0 +1,73 @@
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
class SupplyDeltas:
|
||||||
|
def __init__(self, zcashd, fr_addrs, miner_deltas, flush_interval = 500):
|
||||||
|
self.zcashd = zcashd
|
||||||
|
self.fr_addrs = fr_addrs
|
||||||
|
self.miner_deltas = miner_deltas
|
||||||
|
|
||||||
|
self.delta_count = 0
|
||||||
|
self.delta_total = 0
|
||||||
|
self.delta_cache = []
|
||||||
|
self.flush_interval = flush_interval
|
||||||
|
|
||||||
|
deltas_flat = [pair for deltas in miner_deltas.values() for pair in deltas]
|
||||||
|
for (deltaHeight, delta) in sorted(deltas_flat):
|
||||||
|
self.AddSupplyDelta(deltaHeight, delta)
|
||||||
|
|
||||||
|
def AddSupplyDelta(self, deltaHeight, delta):
|
||||||
|
assert len(self.delta_cache) == 0 or deltaHeight > self.delta_cache[-1][0]
|
||||||
|
|
||||||
|
self.delta_count += 1
|
||||||
|
self.delta_total += delta
|
||||||
|
self.delta_cache.append((deltaHeight, self.delta_total))
|
||||||
|
|
||||||
|
def DeviationAtHeight(self, height):
|
||||||
|
# search for the cached entry at the maximum deltaHeight <= height
|
||||||
|
def binary_search(start, end, max_found):
|
||||||
|
if start >= end:
|
||||||
|
if max_found:
|
||||||
|
return max_found[1]
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
mid = (start + end) // 2
|
||||||
|
item = self.delta_cache[mid]
|
||||||
|
if item[0] <= height:
|
||||||
|
return binary_search(mid+1, end, item)
|
||||||
|
else:
|
||||||
|
return binary_search(start, mid, max_found)
|
||||||
|
|
||||||
|
return binary_search(0, len(self.delta_cache), None)
|
||||||
|
|
||||||
|
def SaveMismatch(self, block, theoretical, empirical):
|
||||||
|
height = block['height']
|
||||||
|
coinbase_tx = block['tx'][0]
|
||||||
|
delta = theoretical - empirical
|
||||||
|
|
||||||
|
print('Mismatch at height {}: {} != {} ({}) miner_deltas: {}'.format(
|
||||||
|
height,
|
||||||
|
theoretical,
|
||||||
|
empirical,
|
||||||
|
delta,
|
||||||
|
len(self.miner_deltas),
|
||||||
|
))
|
||||||
|
|
||||||
|
# if delta ever goes negative, we will halt
|
||||||
|
if delta >= 0:
|
||||||
|
miner_addrs = set([addr for out in coinbase_tx['vout'] for addr in out['scriptPubKey'].get('addresses', [])]) - self.fr_addrs
|
||||||
|
if len(miner_addrs) > 0:
|
||||||
|
self.miner_deltas.setdefault(",".join(sorted(miner_addrs)), []).append((height, delta))
|
||||||
|
self.AddSupplyDelta(height, delta)
|
||||||
|
if self.delta_count % 500 == 0:
|
||||||
|
with open("delta_cache.{}.out".format(self.delta_count), 'w', encoding="utf8") as f:
|
||||||
|
pprint.pprint(self.miner_deltas, stream = f, indent = 4)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
pprint.pprint(coinbase_tx['vout'], indent = 4)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def PrintDeltas(self):
|
||||||
|
with open("delta_cache.out", 'w', encoding="utf8") as f:
|
||||||
|
pprint.pprint(self.miner_deltas, stream = f, indent = 4)
|
|
@ -0,0 +1,67 @@
|
||||||
|
def exact_div(x, y):
|
||||||
|
assert x % y == 0
|
||||||
|
return x // y
|
||||||
|
|
||||||
|
# floor(u/x + v/y)
|
||||||
|
def div2(u, x, v, y):
|
||||||
|
return (u*y + v*x) // (x*y)
|
||||||
|
|
||||||
|
TESTNET = 0
|
||||||
|
MAINNET = 1
|
||||||
|
|
||||||
|
class Network:
|
||||||
|
# <https://zips.z.cash/protocol/protocol.pdf#constants>
|
||||||
|
def __init__(self, network):
|
||||||
|
self.BlossomActivationHeight = 653600 if network == MAINNET else 584000
|
||||||
|
|
||||||
|
SlowStartInterval = 20000
|
||||||
|
MaxBlockSubsidy = 1250000000 # 12.5 ZEC
|
||||||
|
PreBlossomHalvingInterval = 840000
|
||||||
|
PreBlossomPoWTargetSpacing = 150
|
||||||
|
PostBlossomPoWTargetSpacing = 75
|
||||||
|
|
||||||
|
# <https://zips.z.cash/protocol/protocol.pdf#diffadjustment>
|
||||||
|
def IsBlossomActivated(self, height):
|
||||||
|
return height >= self.BlossomActivationHeight
|
||||||
|
|
||||||
|
BlossomPoWTargetSpacingRatio = exact_div(PreBlossomPoWTargetSpacing, PostBlossomPoWTargetSpacing)
|
||||||
|
|
||||||
|
# no need for floor since this is necessarily an integer
|
||||||
|
PostBlossomHalvingInterval = PreBlossomHalvingInterval * BlossomPoWTargetSpacingRatio
|
||||||
|
|
||||||
|
# <https://zips.z.cash/protocol/protocol.pdf#subsidies>
|
||||||
|
SlowStartShift = exact_div(SlowStartInterval, 2)
|
||||||
|
|
||||||
|
SlowStartRate = exact_div(MaxBlockSubsidy, SlowStartInterval)
|
||||||
|
|
||||||
|
SupplyCache = []
|
||||||
|
|
||||||
|
def Halving(self, height):
|
||||||
|
if height < self.SlowStartShift:
|
||||||
|
return 0
|
||||||
|
elif not self.IsBlossomActivated(height):
|
||||||
|
return (height - self.SlowStartShift) // self.PreBlossomHalvingInterval
|
||||||
|
else:
|
||||||
|
return div2(self.BlossomActivationHeight - self.SlowStartShift, self.PreBlossomHalvingInterval,
|
||||||
|
height - self.BlossomActivationHeight, self.PostBlossomHalvingInterval)
|
||||||
|
|
||||||
|
def BlockSubsidy(self, height):
|
||||||
|
if height < self.SlowStartShift:
|
||||||
|
return self.SlowStartRate * height
|
||||||
|
elif self.SlowStartShift <= height and height < self.SlowStartInterval:
|
||||||
|
return self.SlowStartRate * (height + 1)
|
||||||
|
if self.SlowStartInterval <= height and not self.IsBlossomActivated(height):
|
||||||
|
return self.MaxBlockSubsidy // (1 << self.Halving(height))
|
||||||
|
else:
|
||||||
|
return self.MaxBlockSubsidy // (self.BlossomPoWTargetSpacingRatio << self.Halving(height))
|
||||||
|
|
||||||
|
def SupplyAfterHeight(self, height):
|
||||||
|
cacheLen = len(self.SupplyCache)
|
||||||
|
if cacheLen > height:
|
||||||
|
return self.SupplyCache[height]
|
||||||
|
else:
|
||||||
|
cur = 0 if cacheLen == 0 else self.SupplyCache[-1]
|
||||||
|
for h in range(cacheLen, height + 1):
|
||||||
|
cur += self.BlockSubsidy(h)
|
||||||
|
self.SupplyCache.append(cur)
|
||||||
|
return self.SupplyCache[-1]
|
Loading…
Reference in New Issue