Wobei das stimmt nicht. 20M wird bei Blockhöhe 940.000 gekreuzt und damit viel früher.
940.000 - 939723 = 277 / 144 = 1,92 Tage
Ups, ja stimmt… Mein Fehler. Danke Dir!
![]()
Ich werfe einfach mal ein, dass über die Jahre nicht in jedem Block auch tatsächlich die theoretische Subsidy ausbezahlt wurde ;-)
https://www.blocktrainer.de/blog/ungewoehnliche-bitcoin-bloecke
Okay, jetzt musste ich aber mal nachzählen (lassen).
Laut meiner Blockchain (Summe aller Coinbases) ergibt sich:
Tip height: 939745
Mined BTC: 19999206.25000000
und damit sind es noch
20 000 000 - 19 999 206,25 = 793,75 BTC
793,75 / 3,125 = 254 Blöcke
254 / 144 = 1,76 Tage
Hier das Python-Skript (falls Interesse besteht):
Spoiler
#!/usr/bin/env python3
import argparse
import concurrent.futures as cf
import decimal
import itertools
import json
import math
import os
import sys
import time
from pathlib import Path
from typing import Any, Iterable, List
import requests
SATS = decimal.Decimal("100000000")
D = decimal.Decimal
class BitcoinRPC:
def __init__(self, url: str, cookiefile: str, timeout: int = 120):
self.url = url
self.timeout = timeout
self.session = requests.Session()
user, password = self._read_cookie(cookiefile)
self.session.auth = (user, password)
self.session.headers.update({"content-type": "application/json"})
def _read_cookie(self, cookiefile: str):
with open(cookiefile, "r") as f:
data = f.read().strip()
user, password = data.split(":")
return user, password
def batch_call(self, calls: List[dict[str, Any]]) -> list[Any]:
resp = self.session.post(
self.url,
data=json.dumps(calls),
timeout=self.timeout,
)
resp.raise_for_status()
results = resp.json()
by_id = {}
for item in results:
if item.get("error"):
raise RuntimeError(item["error"])
by_id[item["id"]] = item["result"]
return [by_id[c["id"]] for c in calls]
def call(self, method: str, params=None):
if params is None:
params = []
payload = [{
"jsonrpc": "2.0",
"id": 1,
"method": method,
"params": params,
}]
return self.batch_call(payload)[0]
def chunked(seq: Iterable[Any], size: int):
it = iter(seq)
while True:
chunk = list(itertools.islice(it, size))
if not chunk:
return
yield chunk
def make_calls(method, params_list, start_id=0):
calls = []
for i, params in enumerate(params_list, start=start_id):
calls.append({
"jsonrpc": "2.0",
"id": i,
"method": method,
"params": params,
})
return calls
def fetch_block_hashes(rpc, heights, batch_size):
out = []
next_id = 0
for hs in chunked(heights, batch_size):
calls = make_calls("getblockhash", [[h] for h in hs], start_id=next_id)
next_id += len(calls)
out.extend(rpc.batch_call(calls))
return out
def sum_subsidies_for_hashes(rpc, hashes, batch_size):
total = 0
next_id = 0
for part in chunked(hashes, batch_size):
calls = make_calls("getblockstats", [[h, ["subsidy"]] for h in part], start_id=next_id)
next_id += len(calls)
results = rpc.batch_call(calls)
total += sum(int(r["subsidy"]) for r in results)
return total
def worker_sum_subsidies(args):
url, cookiefile, timeout, heights, batch_size = args
rpc = BitcoinRPC(url, cookiefile, timeout)
hashes = fetch_block_hashes(rpc, heights, batch_size)
return sum_subsidies_for_hashes(rpc, hashes, batch_size)
def satoshis_to_btc(sats):
return f"{D(sats) / SATS:.8f}"
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--rpc-url",
default="http://127.0.0.1:8332",
)
parser.add_argument(
"--cookiefile",
default=os.path.expanduser("~/.bitcoin/.cookie"),
)
parser.add_argument("--timeout", type=int, default=120)
parser.add_argument("--workers", type=int, default=max(4, (os.cpu_count() or 8)))
parser.add_argument("--batch-size", type=int, default=500)
args = parser.parse_args()
if not Path(args.cookiefile).exists():
print("Cookie file not found:", args.cookiefile)
sys.exit(1)
rpc = BitcoinRPC(args.rpc_url, args.cookiefile, args.timeout)
start = time.time()
tip = rpc.call("getblockcount")
heights = list(range(tip + 1))
workers = min(args.workers, math.ceil(len(heights) / args.batch_size))
chunk_size = math.ceil(len(heights) / workers)
jobs = [heights[i:i + chunk_size] for i in range(0, len(heights), chunk_size)]
total_sats = 0
with cf.ThreadPoolExecutor(max_workers=workers) as ex:
futures = [
ex.submit(
worker_sum_subsidies,
(args.rpc_url, args.cookiefile, args.timeout, job, args.batch_size),
)
for job in jobs
]
for fut in cf.as_completed(futures):
total_sats += fut.result()
elapsed = time.time() - start
print("Tip height:", tip)
print("Mined BTC:", satoshis_to_btc(total_sats))
print("Satoshis:", total_sats)
print("Elapsed:", round(elapsed, 2), "seconds")
if __name__ == "__main__":
main()
Da hast du jetzt also die fehlenden/fehlerhaften Coinbases nicht mit gezählt?
Weil laut deiner zählweise kommst du ja auch auf den „theoretischen Block“ 939999.
Aber eigentlich müsste es ja ein paar Blöcke später erst soweit sein.
Oder habe ich auch einen Denkfehler?
Du hast recht. Auch das stimmt nicht. Habe nochmal den Block 501726 per bitcoin-cli getblockstats 501726 ausgelesen. Auch dort wird die Subsidy mit "subsidy": 1250000000 angegeben, obwohl sie nicht „ausgezahlt“ wurde. War wohl zu einfach gedacht.
Wie kann Dein Supply höher sein, als meines?
Oh… blockheight Unterschied trotz refresh
Aber die rechnen ja falsch, das macht keinen Sinn… die echten 20M sind einige Blöcke später…
Coinmarketcap…
19,999,953 BTC
Stand: 14:39
Jetzt fehlen dann nur noch die nicht ausgezahlten Subsidy…
Schon komisch bei newhedge.
20mio btc + 1000050 verbleibende btc, würde ja bedeuten, dass wir mehr als 21 mio btc hätten ![]()
Aber ich frage mich gerade beim erneuten Lesens des BT Artikels… Die Rewards wurden ja erzeugt, aber nur nicht an die Miner-Adressen ausgegeben. Also auch, wenn diese niemals in Umlauf kommen, sind sie dennoch vorhanden.
von daher ![]()
Schätze die runden irgendwo unzulässig. Maximal 20 999 999, 9769 BTC könnten es sein, und davon müssen die nicht ausgezahlten Block-Subsidy noch abgezogen werden.
Im Prinzip auch nichts anderes als in’s Nirvana schicken… haste auch wieder Recht.
Ach, es ist eh erst offiziell, wenn hier jemand das passende „Wunderkerzen-Meme“ gepostet hat! ![]()
Der Timechain Calendar zeigt schon 20M an ![]()
Hmmm ne, genau genommen nicht? Die werden doch erst durch die Auszahlung erzeugt…
Schau dir mal das Beispiel von Block 501726 an z.B.
Da wurden eben keine BTC erzeugt.
Ja, eine Coinbase braucht immer einen Input, aber im Fall einer Coinbase ist das ja ein „Null-Input“. Und erzeugt werden die Coins dann aber eigentlich erst durch die Auszahlung.
Was meint Ihr, ich tue mir echt schwer, die richtige Antwort festzulegen.
Am Ende hat keiner die richtige Blockhöhe bestimmt & das Datum wurde auch nicht richtig vorhergesagt.
Meint Ihr, dass der Kommentar vom @Trek als Lösung festgehalten werden sollte?
Je nach Antworten würde ich morgen um diese Uhrzeit dann die “Lösung“ markieren.
Kannst Du im Prinzip machen, wie Du lustig bist.
Was @renna sagt, ist shon richtig. Also müsste man um es genau zu wissen ein Skript bauen, was die Blöcke mit Auszahlung < Subsidy sucht, so dass wir die Differenz bestimmen können.
Etwa 230 BTC (inkl. Genesis) gelten aktuell als Ausnahmen sagt ChatGPT.
Und liefert ein Skript (was ich noch nicht getestet habe):
from bitcoinrpc.authproxy import AuthServiceProxy
rpc = AuthServiceProxy("http://user:password@127.0.0.1:8332")
height = rpc.getblockcount()
supply = 0
for h in range(height + 1):
blockhash = rpc.getblockhash(h)
block = rpc.getblock(blockhash, 2)
coinbase = block["tx"][0]
for vout in coinbase["vout"]:
supply += vout["value"]
print("Total issued BTC:", supply)


