Los 23 weights de spec 16, certificados por Laurha en el santuario de la rp1 (origin-banco):
- Núcleo 3 aislado, governor performance a 2,4 GHz, CI parada, 45-49 °C.
- frame-omni-bencher 0.15.0 sobre el wasm 40cb5a1d de 3cd1068.
- 5 procesos por pallet; se cobra el MÁXIMO con scripts/bench/max-de-n.py, porque procesos
idénticos se separan hasta un 35 %.
- Evidencia: ~/laurha-work/banco/spec16-20261024-1631 en la rp1 (SHA256SUMS d930c5e1).
- Su perfil va en la cabecera de cada weights.rs.
Antes de cobrarlos:
- Mismos accesos a storage: el esqueleto de los 5 ficheros y sus reads/writes son idénticos a
los de main.
- Lo que Laurha pidió mirar (cast_vote, finalize_proposal y veto_proposal, mucho más altos que
en RV12) es esperable. Contra los interinos del Mac todos caen en la banda Pi/Mac de 2,5-3,9×:
es trabajo nuevo de RV15/RV16 en una CPU unas 3 veces más lenta.
- Destacan los tres que verifican Ed25519 en WASM (attest_asset, register_agent_did,
rotate_agent_key), con unas 6 veces: la criptografía pura sufre más en la Cortex-A76.
- Lo cobrado sube menos que la ejecución, entre +4 % (set_tier_profile) y +56 %
(rotate_agent_key), porque las lecturas y escrituras de storage no cambian.
- spec_version 16 → 17, porque cambian las fees. transaction_version no cambia: ninguna llamada
cambia su codificación.
Ensayado en la rp1 (aarch64) sobre un nodo efímero aparte, antes de la testnet de la casa, con
scripts/e2e/upgrade_en_vivo.py (nuevo):
- sudo(set_code) → CodeUpdated + Sudid Ok;
- spec 17 y LastRuntimeUpgrade sube;
- una firma con la spec vieja → «bad signature»; con la nueva, ExtrinsicSuccess;
- la finalidad pasa del bloque del upgrade.
Es la primera vez que el ejecutor ARM compila y corre un runtime nuevo.
Revisión adversarial (nivel 1 de la directiva), antes de tocar la red, con orden de tumbarlo:
no pudo. Los seis puntos aguantan:
- ningún storage ni codificación cambia, y transaction_version no debe subir;
- el más pesado (veto_proposal) cobra 1,9 ms, 344 veces por debajo del máximo por extrinsic;
- los weights.rs tienen la misma estructura y el impl () coincide;
- ninguna fee, subsidio ni test depende del valor de forma que se rompa.
scripts/rp1 (operación del nodo, medido en 23 h con los valores por defecto):
- 430 MB de WAL de RocksDB retenidos para 15 MB de datos, y el RSS subiendo de 200 a 340 MB:
--db-cache 128. Al reiniciar, 449 → 21 MB.
- El log, de 7 líneas por bloque a ~2 (txpool, sc_basic_authorship y aura en warn; ojo: las
líneas de autoría usan el target del módulo, no «basic-authorship»), de 16 MB/día a unos 3.
- Modo append, y medir.sh lo rota al pasar de 32 MB.
Co-Authored-By: Laurha <laurha@origin>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
119 lines
5.0 KiB
Python
119 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Aplica un runtime nuevo a una cadena VIVA por sudo(system.set_code), firmando como un wallet,
|
|
y comprueba que la cadena sigue sana. Es el upgrade de verdad, no el simulacro de
|
|
simulacro_set_code.py (ese usa un runtime marcado y la misma spec como control negativo).
|
|
|
|
ORIGIN_RPC=http://127.0.0.1:9944 python3 scripts/e2e/upgrade_en_vivo.py <runtime.wasm> <spec_esperada>
|
|
|
|
Comprueba, en este orden:
|
|
1. el sudo(set_code) entra y su bloque trae CodeUpdated + Sudid Ok;
|
|
2. el RPC anuncia la spec esperada y LastRuntimeUpgrade sube;
|
|
3. un remark firmado con la spec NUEVA entra (ExtrinsicSuccess) y uno con la VIEJA es «bad
|
|
signature»;
|
|
4. la finalidad avanza más allá del bloque del upgrade.
|
|
Se detiene en el primer fallo, con código de salida 1.
|
|
"""
|
|
import json, os, subprocess, sys, time, urllib.request
|
|
|
|
RPC = os.environ.get("ORIGIN_RPC", "http://127.0.0.1:9944")
|
|
RAIZ = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
TOOL = os.environ.get("ORIGIN_FIRMADOR", os.path.join(RAIZ, "target/release/examples/firmar_como_wallet"))
|
|
ALICE = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
|
|
EVENTS = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"
|
|
LAST_UPGRADE = "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8"
|
|
|
|
|
|
def rpc(m, p=None):
|
|
req = urllib.request.Request(RPC, json.dumps({"id": 1, "jsonrpc": "2.0", "method": m, "params": p or []}).encode(),
|
|
{"Content-Type": "application/json"})
|
|
with urllib.request.urlopen(req, timeout=120) as r:
|
|
return json.load(r)
|
|
|
|
|
|
def best():
|
|
return int(rpc("chain_getHeader")["result"]["number"], 16)
|
|
|
|
|
|
def finalizado():
|
|
return int(rpc("chain_getHeader", [rpc("chain_getFinalizedHead")["result"]])["result"]["number"], 16)
|
|
|
|
|
|
def firmar(llamada, nonce, genesis, v):
|
|
return subprocess.run([TOOL, "firmar", llamada, str(nonce), genesis, str(v["specVersion"]), str(v["transactionVersion"])],
|
|
capture_output=True, text=True, check=True).stdout.strip()
|
|
|
|
|
|
def eventos(n):
|
|
h = rpc("chain_getBlockHash", [n])["result"]
|
|
ev = rpc("state_getStorage", [EVENTS, h])["result"]
|
|
return subprocess.run([TOOL, "eventos", ev], capture_output=True, text=True).stdout if ev else ""
|
|
|
|
|
|
def buscar(n0, texto, espera=60):
|
|
t0 = time.time()
|
|
while time.time() - t0 < espera:
|
|
for n in range(n0 + 1, best() + 1):
|
|
e = eventos(n)
|
|
if texto in e:
|
|
return n, e
|
|
time.sleep(2)
|
|
return None, ""
|
|
|
|
|
|
def falla(msg):
|
|
print("❌", msg)
|
|
sys.exit(1)
|
|
|
|
|
|
def main(wasm, spec_esperada):
|
|
genesis = rpc("chain_getBlockHash", [0])["result"]
|
|
v = rpc("state_getRuntimeVersion")["result"]
|
|
print(f"antes: spec {v['specVersion']} · best #{best()} · finalizado #{finalizado()} · LastRuntimeUpgrade {rpc('state_getStorage', [LAST_UPGRADE])['result']}")
|
|
if v["specVersion"] >= spec_esperada:
|
|
falla(f"la cadena ya va en spec {v['specVersion']}")
|
|
|
|
n0 = best()
|
|
nonce = rpc("system_accountNextIndex", [ALICE])["result"]
|
|
r = rpc("author_submitExtrinsic", [firmar(f"set_code:{wasm}", nonce, genesis, v)])
|
|
if "error" in r:
|
|
falla(f"set_code rechazado: {r['error']}")
|
|
n, e = buscar(n0, "CodeUpdated")
|
|
if not n or "Sudid { sudo_result: Ok" not in e:
|
|
falla("no apareció CodeUpdated + Sudid Ok\n" + e)
|
|
print(f"1 ✅ bloque #{n}: CodeUpdated + Sudid Ok")
|
|
|
|
while best() < n + 1:
|
|
time.sleep(1)
|
|
v2 = rpc("state_getRuntimeVersion")["result"]
|
|
lru = rpc("state_getStorage", [LAST_UPGRADE])["result"]
|
|
if v2["specVersion"] != spec_esperada:
|
|
falla(f"el RPC anuncia spec {v2['specVersion']}, no {spec_esperada}")
|
|
print(f"2 ✅ spec {v2['specVersion']} · LastRuntimeUpgrade {lru}")
|
|
|
|
nonce = rpc("system_accountNextIndex", [ALICE])["result"]
|
|
viejo = rpc("author_submitExtrinsic", [firmar("remark", nonce, genesis, v)])
|
|
if "bad signature" not in json.dumps(viejo):
|
|
falla(f"una firma con la spec vieja NO fue rechazada: {viejo}")
|
|
m0 = best()
|
|
nuevo = rpc("author_submitExtrinsic", [firmar("remark", nonce, genesis, v2)])
|
|
if "error" in nuevo:
|
|
falla(f"la firma con la spec nueva fue rechazada: {nuevo['error']}")
|
|
m, e = buscar(m0, "ExtrinsicSuccess { dispatch_info: DispatchEventInfo { weight: Weight { ref_time", 60)
|
|
exitos = [l for l in e.splitlines() if "ExtrinsicSuccess" in l and "Normal" in l]
|
|
if not m or not exitos:
|
|
falla("el remark con la spec nueva no se incluyó con éxito")
|
|
print(f"3 ✅ spec vieja → bad signature · spec nueva → incluido en #{m}: {exitos[0][:110]}")
|
|
|
|
t0 = time.time()
|
|
while finalizado() <= n and time.time() - t0 < 120:
|
|
time.sleep(2)
|
|
if finalizado() <= n:
|
|
falla("la finalidad no pasó del bloque del upgrade en 2 min")
|
|
print(f"4 ✅ finalizado #{finalizado()} > bloque del upgrade #{n} · best #{best()}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) != 3:
|
|
sys.exit(__doc__)
|
|
main(os.path.abspath(sys.argv[1]), int(sys.argv[2]))
|