Standing fleet: weight_check reads vote-key shares from the hub's inspect and maps boxes by key-hash (tested: two boxes = 1.2 percent of 2,000 blocks)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
f0a7a9249a
commit
1dda56b491
1 changed files with 19 additions and 18 deletions
|
|
@ -91,27 +91,28 @@ def rerent(label):
|
|||
return new
|
||||
# ---- the 10 percent rule (20:00Z): never remove more than 10 percent of the live devnet's 30-day weight in any hour ----
|
||||
WEIGHT_LOG = os.path.join(ROOT, "weight-removals.jsonl")
|
||||
def weight_shares(window_blocks=30*86400):
|
||||
"""Blue blocks per payout label over the window, from the hub's node (igneum-miner payouts); returns {label: share}."""
|
||||
def weight_shares(blocks=2000):
|
||||
"""Vote-key-hash shares over the last `blocks` blocks of the live devnet, from the hub's node (igneum-miner inspect:
|
||||
one line per block with voteKeyHash=<64 hex>); the hub is pruned, so the 30-day window is approximated by the largest
|
||||
read the RPC serves (2,000 blocks is about 33 minutes at 1 block/s; pass more when the RPC allows). Returns
|
||||
({key_hash: share}, blocks_read)."""
|
||||
hub = next((v for v in Registry.load().values() if v.get("hub") and v.get("state") != "destroyed"), None)
|
||||
if not hub: raise SshError("no hub box")
|
||||
b = Box(hub["ssh_host"], hub["ssh_port"], "hub-1", None, None, hub.get("provider"))
|
||||
rc, out, err = b.run("/opt/igneum/pkg/bin/igneum-miner payouts grpc://127.0.0.1:26610 2>/dev/null | tail -400", 120)
|
||||
rows = {}
|
||||
for ln in out.splitlines():
|
||||
parts = ln.split()
|
||||
nums = [p for p in parts if p.isdigit()]
|
||||
if len(parts) >= 2 and nums:
|
||||
rows[parts[0]] = rows.get(parts[0], 0) + int(nums[-1])
|
||||
tot = sum(rows.values()) or 1
|
||||
return {k: v / tot for k, v in rows.items()}, out[-600:]
|
||||
rc, out, err = b.run(f"timeout 170 /opt/igneum/pkg/bin/igneum-miner inspect {blocks} grpc://127.0.0.1:26610 2>&1 | grep -oE 'voteKeyHash=[0-9a-f]{{64}}'", 190)
|
||||
keys = [ln.split("=", 1)[1] for ln in out.split() if ln.startswith("voteKeyHash=")]
|
||||
n = len(keys) or 1; rows = {}
|
||||
for k in keys: rows[k] = rows.get(k, 0) + 1
|
||||
return {k: v / n for k, v in rows.items()}, len(keys)
|
||||
def vote_key_hash(label):
|
||||
"""The box's vote key hash: its miner votes with the key its payout label derives (igneum-miner key-hash <label>)."""
|
||||
b = Box.from_registry(label); rc, out, err = b.run(f"/opt/igneum/pkg/bin/igneum-miner key-hash {label} 2>/dev/null | grep -oE '[0-9a-f]{{64}}' | head -1", 30)
|
||||
return out.strip()
|
||||
def weight_check(labels, limit=0.10, hours=1.0):
|
||||
"""Refuses when the labels' weight plus what was removed in the last `hours` exceeds `limit`. Records an allowed removal."""
|
||||
shares, raw = weight_shares()
|
||||
reg = Registry.load(); keys = {}
|
||||
for l in labels:
|
||||
iid, b = Registry.find(l); keys[l] = (b.get("wallet") or "").lower()
|
||||
want = sum(shares.get(k, 0) for k in keys.values()) + sum(shares.get(l, 0) for l in labels)
|
||||
shares, nblocks = weight_shares()
|
||||
keys = {l: vote_key_hash(l) for l in labels}
|
||||
want = sum(shares.get(k, 0) for k in keys.values() if k)
|
||||
since = time.time() - hours * 3600; recent = 0.0
|
||||
if os.path.exists(WEIGHT_LOG):
|
||||
for ln in open(WEIGHT_LOG):
|
||||
|
|
@ -119,7 +120,7 @@ def weight_check(labels, limit=0.10, hours=1.0):
|
|||
except Exception: continue
|
||||
if j.get("ts", 0) >= since: recent += float(j.get("share", 0))
|
||||
ok = (want + recent) <= limit
|
||||
verdict = {"labels": labels, "share": round(want, 4), "removed_last_hour": round(recent, 4), "limit": limit, "ok": ok, "t": now()}
|
||||
verdict = {"labels": labels, "keys": {l: k[:16] for l, k in keys.items()}, "share": round(want, 4), "removed_last_hour": round(recent, 4), "limit": limit, "ok": ok, "blocks_read": nblocks, "t": now()}
|
||||
if ok:
|
||||
with open(WEIGHT_LOG, "a") as f: f.write(json.dumps({"ts": time.time(), "labels": labels, "share": want}) + "\n")
|
||||
return verdict
|
||||
|
|
@ -152,4 +153,4 @@ if __name__ == "__main__":
|
|||
elif a[0] == "weight_check":
|
||||
v = weight_check(a[1:]); print(json.dumps(v)); sys.exit(0 if v["ok"] else 1)
|
||||
elif a[0] == "weights":
|
||||
sh, raw = weight_shares(); print(json.dumps(dict(sorted(sh.items(), key=lambda kv: -kv[1])[:40]), indent=1))
|
||||
sh, n = weight_shares(); print("blocks read", n, "voters", len(sh)); print(json.dumps({k[:16]: round(v, 4) for k, v in sorted(sh.items(), key=lambda kv: -kv[1])[:40]}, indent=1))
|
||||
|
|
|
|||
Loading…
Reference in a new issue