box-prover.py: the export directory owns its disk (20 GB and 7-day caps, export deleted on record accepted or paid, no export under 10 percent free)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
a99617bd9f
commit
86c9854863
1 changed files with 27 additions and 3 deletions
|
|
@ -27,7 +27,29 @@ HOST = f"{FLOOR}/bin/igneum-prove-host"; EXPORT = f"{FLOOR}/bin/igneum-prove-exp
|
|||
EVM = "http://127.0.0.1:26790"; GRPC = "grpc://127.0.0.1:26610"; CHAIN = os.environ.get("CHAIN_NAME", "igneum-devnet") # Devnet 2 signs for igneum-devnet-2
|
||||
LABEL = os.environ.get("LABEL", "box"); WALLET = os.environ.get("WALLET", "0x" + "19" * 20)
|
||||
THRESHOLD = os.environ.get("THRESHOLD", ""); MINER = os.environ.get("MINER", "keep"); RUN_HOURS = float(os.environ.get("RUN_HOURS", "9"))
|
||||
EXPORT_FROM = int(os.environ.get("EXPORT_FROM", "27276")) # the devnet's exec restart block (ov13.json exec_restart_number) when the node does not report one
|
||||
EXPORT_FROM = int(os.environ.get("EXPORT_FROM", "27276"))
|
||||
# the export directory's own rule (6 October 2026, 20:4xZ, after the hub's node died on a full disk): a size cap and an age cap
|
||||
# on OUT/segs, each segment's export deleted the moment its record is accepted or paid, and a disk-free check before each
|
||||
# export that skips with a logged line under 10 percent free. Defaults fit a 100 GB box for a week.
|
||||
SEGS_CAP_GB = float(os.environ.get("SEGS_CAP_GB", "20")); SEGS_MAX_AGE_H = float(os.environ.get("SEGS_MAX_AGE_H", str(7 * 24))); DISK_MIN_FREE_PCT = float(os.environ.get("DISK_MIN_FREE_PCT", "10"))
|
||||
import shutil
|
||||
def seg_dir_size(d):
|
||||
return sum(os.path.getsize(os.path.join(r, f)) for r, _, fs in os.walk(d) for f in fs if os.path.exists(os.path.join(r, f)))
|
||||
def drop_export(first, why):
|
||||
d = f"{OUT}/segs/seg-{first}"
|
||||
if os.path.isdir(d): sz = seg_dir_size(d); shutil.rmtree(d, ignore_errors=True); say(f"RESULT export_dropped {stamp()} segment {first} ({sz/1e6:.0f} MB): {why}")
|
||||
def prune_exports():
|
||||
root = f"{OUT}/segs"
|
||||
if not os.path.isdir(root): return
|
||||
entries = sorted((os.path.join(root, n) for n in os.listdir(root)), key=lambda x: os.path.getmtime(x))
|
||||
now_ = time.time(); total = sum(seg_dir_size(e) if os.path.isdir(e) else os.path.getsize(e) for e in entries)
|
||||
for e in entries:
|
||||
age_h = (now_ - os.path.getmtime(e)) / 3600
|
||||
if age_h > SEGS_MAX_AGE_H or total > SEGS_CAP_GB * 1e9:
|
||||
sz = seg_dir_size(e) if os.path.isdir(e) else os.path.getsize(e); shutil.rmtree(e, ignore_errors=True) if os.path.isdir(e) else os.remove(e); total -= sz
|
||||
say(f"RESULT export_pruned {stamp()} {os.path.basename(e)} ({sz/1e6:.0f} MB, {age_h:.1f} h old): dir {total/1e9:.1f} GB against the {SEGS_CAP_GB:.0f} GB cap, {SEGS_MAX_AGE_H:.0f} h age cap")
|
||||
def disk_free_pct(path="/"):
|
||||
st = os.statvfs(path); return 100.0 * st.f_bavail / max(st.f_blocks, 1) # the devnet's exec restart block (ov13.json exec_restart_number) when the node does not report one
|
||||
MINE = f"{F}/card{CARD}/mine" if CARD else f"{F}/mine"
|
||||
os.makedirs(f"{OUT}/segs", exist_ok=True); os.makedirs(f"{MINE}/packs", exist_ok=True)
|
||||
DEV = os.environ.get("IGNEUM_CUDA_DEVICE", "0")
|
||||
|
|
@ -131,7 +153,7 @@ while (time.time() - t_run0) / 3600 < RUN_HOURS:
|
|||
for first in list(submitted):
|
||||
rec = rpc("igneum_getSegmentRecords", [hex(first)])
|
||||
if rec and rec.get("paid"):
|
||||
wei = hexi(rec["paid"]["wei"]); state["paid"] += 1; state["paid_wei"] += wei
|
||||
wei = hexi(rec["paid"]["wei"]); state["paid"] += 1; state["paid_wei"] += wei; drop_export(first, "record paid")
|
||||
say(f"RESULT paid {stamp()} segment {first}..{submitted[first]['last']} wei={wei} ({wei/1e18:.4f} IGN) carrier={hexi(rec['paid'].get('carrierNumber'))} after {int(time.time()-submitted[first]['at'])} s")
|
||||
for s in state["segments"]:
|
||||
if s["first"] == first: s["paid_wei"] = wei; s["paid_at"] = stamp()
|
||||
|
|
@ -144,7 +166,7 @@ while (time.time() - t_run0) / 3600 < RUN_HOURS:
|
|||
if tip > h["deadline"]: say(f"RESULT held_expired {stamp()} segment {first} deadline passed"); del held[first]; continue
|
||||
rr = submit("igneum_submitSegmentRecord", h["record"], h["proof_file"])
|
||||
if rr and rr.get("accepted"):
|
||||
state["submitted"] += 1; submitted[first] = {"last": h["last"], "at": time.time(), "deadline": h["deadline"]}; say(f"RESULT submitted {stamp()} segment {first}..{h['last']} record accepted on retry (held {int(time.time()-h['since'])} s)"); del held[first]
|
||||
state["submitted"] += 1; submitted[first] = {"last": h["last"], "at": time.time(), "deadline": h["deadline"]}; drop_export(first, "record accepted"); say(f"RESULT submitted {stamp()} segment {first}..{h['last']} record accepted on retry (held {int(time.time()-h['since'])} s)"); del held[first]
|
||||
state["held"] = len(held)
|
||||
cands, start, n, tip, entries = candidates(st)
|
||||
save_state()
|
||||
|
|
@ -171,6 +193,8 @@ while (time.time() - t_run0) / 3600 < RUN_HOURS:
|
|||
first, last = picked["first"], picked["last"]; attempted.add(first); state["claimed"] += 1
|
||||
say(f"RESULT claim {stamp()} segment {first}..{last} ({len(picked['shards'])} shards, {'continuing' if prev_file else 'fresh'}) margin={picked['margin']} tip={tip} candidates={len(cands)} rank_by=fnv")
|
||||
seg = {"first": first, "last": last, "claimed_at": stamp(), "shards": len(picked["shards"]), "fresh": prev_file is None}; state["segments"].append(seg)
|
||||
prune_exports(); free = disk_free_pct()
|
||||
if free < DISK_MIN_FREE_PCT: say(f"RESULT skip {stamp()} segment {first}: disk {free:.1f}% free is under the {DISK_MIN_FREE_PCT:.0f}% floor, no export"); time.sleep(60); continue
|
||||
d = f"{OUT}/segs/seg-{first}"; os.makedirs(d, exist_ok=True); t_seg0 = time.time()
|
||||
# export
|
||||
# a node whose EVM restarted at a chain block (0.3.13's exec restart rule) exports from that block, not genesis: the
|
||||
|
|
|
|||
Loading…
Reference in a new issue