build: automatic block height update

This commit is contained in:
scgbckbone 2026-01-14 20:16:25 +01:00 committed by doc-hex
parent 9ff3f5c447
commit 2981d15933
6 changed files with 110 additions and 6 deletions

9
shared/block_height.py Normal file
View File

@ -0,0 +1,9 @@
# (c) Copyright 2026 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
# AUTO-generated.
#
# Updated: 2026-06-19 14:13:23 UTC
BLOCK_HEIGHT = 932301
# EOF

View File

@ -8,6 +8,7 @@ from ubinascii import hexlify as b2a_hex
from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2TR from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2TR
from public_constants import AF_P2SH, AF_P2WSH, AF_P2WPKH_P2SH, AF_P2WSH_P2SH from public_constants import AF_P2SH, AF_P2WSH, AF_P2WPKH_P2SH, AF_P2WSH_P2SH
from public_constants import AFC_PUBKEY, AFC_SEGWIT, AFC_BECH32, AFC_SCRIPT from public_constants import AFC_PUBKEY, AFC_SEGWIT, AFC_BECH32, AFC_SCRIPT
from block_height import BLOCK_HEIGHT
from serializations import hash160, ser_compact_size, disassemble from serializations import hash160, ser_compact_size, disassemble
from ucollections import namedtuple from ucollections import namedtuple
from opcodes import OP_RETURN, OP_1, OP_16 from opcodes import OP_RETURN, OP_1, OP_16
@ -297,7 +298,7 @@ class BitcoinMain(ChainsBase):
# see <https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp#L140> # see <https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp#L140>
ctype = 'BTC' ctype = 'BTC'
name = 'Bitcoin Mainnet' name = 'Bitcoin Mainnet'
ccc_min_block = 939464 # Mar 5/2026 ccc_min_block = BLOCK_HEIGHT
slip132 = { slip132 = {
AF_CLASSIC: Slip132Version(0x0488B21E, 0x0488ADE4, 'x'), AF_CLASSIC: Slip132Version(0x0488B21E, 0x0488ADE4, 'x'),

View File

@ -6,6 +6,7 @@ freeze_as_mpy('', [
'address_explorer.py', 'address_explorer.py',
'auth.py', 'auth.py',
'backups.py', 'backups.py',
'block_height.py',
'callgate.py', 'callgate.py',
'ccc.py', 'ccc.py',
'chains.py', 'chains.py',

View File

@ -0,0 +1,81 @@
#!/usr/bin/env python3
#
# (c) Copyright 2026 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
# Capture current (mainnet) block height for SSSP/CCC features
#
import sys, time, datetime
import urllib.request
FILE_NAME = "../shared/block_height.py"
def _get_block_height(url):
with urllib.request.urlopen(url) as response:
height_data = response.read().decode().strip()
return int(height_data)
def get_block_height(url):
try:
return _get_block_height(url)
except:
time.sleep(2)
return _get_block_height(url)
def parse_block_height_file():
with open(FILE_NAME, "r") as f:
for l in f.readlines():
if l.startswith("BLOCK_HEIGHT ="):
return int(l.split("=")[-1].strip())
return None
def write_block_height_file(block_height):
now = datetime.datetime.now(datetime.timezone.utc)
with open(FILE_NAME, "wt") as f:
f.write('''\
# (c) Copyright 2026 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
# AUTO-generated.
#
# As of %s UTC
BLOCK_HEIGHT = %d
# EOF
''' % (now.strftime("%Y-%m-%d %H:%M:%S"), block_height))
def main():
current_height = None
for _ in range(2):
bh_a = get_block_height("https://mempool.space/api/blocks/tip/height")
bh_b = get_block_height("https://blockstream.info/api/blocks/tip/height")
if bh_a == bh_b:
current_height = bh_a
break
time.sleep(5)
if current_height is None:
raise RuntimeError("Could not get current block height")
file_block_height = parse_block_height_file()
if file_block_height is None:
raise RuntimeError("Could not parse block height from file")
if current_height > file_block_height:
write_block_height_file(current_height)
sys.exit(1)
else:
sys.exit(0)
if __name__ == "__main__":
main()
# EOF

View File

@ -5,14 +5,14 @@
# Capture build time and version number into a number used as the timestamp on # Capture build time and version number into a number used as the timestamp on
# all created files for that Coldcard version. # all created files for that Coldcard version.
# #
import os, sys, time, datetime import os, sys, datetime
out_fname, version = sys.argv[1:] out_fname, version = sys.argv[1:]
assert out_fname.endswith('.c'), out_fname assert out_fname.endswith('.c'), out_fname
if os.path.exists(out_fname): if os.path.exists(out_fname):
# to help deterministic builds, don't replace the file from git if verison # is right # to help deterministic builds, don't replace the file from git if version # is right
with open(out_fname, 'rt') as fd: with open(out_fname, 'rt') as fd:
if ('// version: %s\n' % version) in fd.read(): if ('// version: %s\n' % version) in fd.read():
print("==> %s already version %s; not changing it" % (out_fname, version)) print("==> %s already version %s; not changing it" % (out_fname, version))
@ -22,7 +22,7 @@ if os.path.exists(out_fname):
today = datetime.date.today() today = datetime.date.today()
value = ((today.year - 1980) << 25) | (today.month << 21) | (today.day << 16) value = ((today.year - 1980) << 25) | (today.month << 21) | (today.day << 16)
# only 2second resolution for times, so can only support minor verion up to x.x.5 and hard to see # only 2second resolution for times, so can only support minor version up to x.x.5 and hard to see
# anyway, let's omit ... worst case, use the date instead # anyway, let's omit ... worst case, use the date instead
ver = ''.join(v for v in version if v in '0123456789.') # strip letter codes from end ver = ''.join(v for v in version if v in '0123456789.') # strip letter codes from end
h, m, _ = [int(x) for x in ver.split('b')[0].split('.')] h, m, _ = [int(x) for x in ver.split('b')[0].split('.')]

View File

@ -82,6 +82,18 @@ $(BOARD)/file_time.c: make_filetime.py *-Makefile shared.mk
./make_filetime.py $(BOARD)/file_time.c $(VERSION_STRING) ./make_filetime.py $(BOARD)/file_time.c $(VERSION_STRING)
cp $(BOARD)/file_time.c . cp $(BOARD)/file_time.c .
.PHONY: block_height
block_height:
@python3 make_block_height.py; \
if [ $$? -eq 0 ]; then \
echo "Block Height file already up-to-date."; \
else \
echo "Block Height file updated."; \
git commit -m "update block height" ../shared/block_height.py; \
fi
# Make a factory release: using key #1 # Make a factory release: using key #1
# - when executed in a repro w/o the required key, it defaults to key zero # - when executed in a repro w/o the required key, it defaults to key zero
# - and that's what happens inside the Docker build # - and that's what happens inside the Docker build
@ -91,7 +103,7 @@ production.bin: firmware-signed.bin Makefile
SUBMAKE = $(MAKE) -f $(PARENT_MKFILE) SUBMAKE = $(MAKE) -f $(PARENT_MKFILE)
.PHONY: release .PHONY: release
release: submods-match code-committed release: submods-match code-committed block_height
$(SUBMAKE) clean $(SUBMAKE) clean
$(SUBMAKE) repro $(SUBMAKE) repro
test -f built/production.bin test -f built/production.bin
@ -118,7 +130,7 @@ rc1:
rc2: RC2_TIMESTAMP = $(shell date "+%F_%H%M") rc2: RC2_TIMESTAMP = $(shell date "+%F_%H%M")
rc2: RC2_FNAME = ./RC2-$(RC2_TIMESTAMP)-$(HW_MODEL)-coldcard.dfu rc2: RC2_FNAME = ./RC2-$(RC2_TIMESTAMP)-$(HW_MODEL)-coldcard.dfu
rc2: RC2_FNAME_FACT = ./RC2-$(RC2_TIMESTAMP)-$(HW_MODEL)-factory.dfu rc2: RC2_FNAME_FACT = ./RC2-$(RC2_TIMESTAMP)-$(HW_MODEL)-factory.dfu
rc2: submods-match code-committed rc2: submods-match code-committed block_height
$(SUBMAKE) clean $(SUBMAKE) clean
$(SUBMAKE) repro $(SUBMAKE) repro
test -f built/production.bin test -f built/production.bin