ba5d1d8e85cbc11127eac0f2572a4e3504bb9abc
15
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ba5d1d8e85 |
docs(readme): la cadena se abre al mundo, con su historia, su estado real y cómo comprobarla
David, 2-nov-2026: abrir el código de la cadena «tal cual, con historia», en la organización
pública de la Fundación (Origin/arkhe-origin-chain). Pidió un README que muestre la seriedad
del trabajo y su historia, para que un agente de IA que lo audite no lo tome por un repositorio
de ayer, y que dé los créditos a la casa, a Gavin Wood y a Polkadot.
- Qué es y en qué estado está: la testnet en la Raspberry Pi 5, el spec 20 en vivo desde el
bloque 81022 y la mainnet que todavía no existe. Sin venta, sin precio y sin exchange.
- La historia desde el 2026-05-19, con los commits de cada hito.
- Cómo trabajamos: contrafactuales, revisión adversaria y E2E, y pesos medidos y certificados.
- Compruébalo tú: comandos con su resultado esperado (365 tests en verde en
|
||
|
|
6055faff1b |
feat(pesos): spec 20, los 17 pesos del gobierno salen de los certificados de Laurha
Ya no queda ningun smoke de Charon. weights.rs se ENSAMBLA desde los tres certificados del banco de referencia (la Pi 5) con scripts/bench/ensamblar-pesos.py: - Cada funcion cobra el maximo, coeficiente a coeficiente, de todos los certificados que midieron su mismo codigo (mismo esqueleto de lecturas, escrituras y proof que el del ultimo, que es el del codigo que corre). - Con --comprobar dice si el fichero es exactamente ese ensamblado: probado en rojo cambiando a mano un peso. - Los tres certificados quedan en scripts/bench/certificados/ (sha256 18b8fcd6, 1e15d04e y 6f5cfec1), con su perfil y su evidencia en la cabecera. Por que el maximo de todos y no solo el ultimo: «El maximo de 5 de un dia no es el de otro» (Laurha). La revision adversaria de spec 20 encontro que cobrar solo el certificado vigente de cada funcion dejaba treasury_disbursement en 136 us, cuando el certificado del 2-nov, sobre el wasm que corre, mide 155 us, y otros siete por debajo de lo medido. Con la regla, ningun peso baja respecto a spec 19; los que suben, entre el +1 % y el +33 % de ejecucion. Probado: - 365 tests verdes. - Ensayo en la rp1 sobre una cadena con el codigo vivo de spec 19. Contrafactual: con estos pesos, el E2E reprueba contra spec 19, porque la cadena cobra los viejos. Tras el upgrade a 20 pasa entero, y lo cobrado es weights.rs + extensiones. Ademas: configs.rs ya no dice «Weights INTERIM hand-derived» en los cinco pallets, que cobran pesos certificados desde spec 17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
158060bfe4 |
feat(gobierno): spec 19, el quorum es el 10 % de lo que puede votar
Decision de David, 2026-10-29: «Si, del 10 % de lo que puede votar». Que puede votar: - Es la emision vigente menos el saldo que nadie puede votar ni recuperar: el pool de emision, el de subsidios y el vault de gobierno (cuentas_sin_llave en configs.rs). - Los bonds (og/props) y los depositos de pallet-revive NO se restan, porque vuelven enteros a una cuenta con llave. La revision adversaria de spec 19 lo demostro con sonda: restarlos dejaba bajar el quorum un 14 % en el genesis de mainnet, aparcando un bond en una propuesta de relleno y recuperandolo despues. Efecto: - En mainnet el quorum pasa de ~100M a ~35,1M OGC (10 % de 351M). - En la testnet de la casa apenas cambia. - Las propuestas abiertas conservan el quorum que guardaron al crearse. Pruebas: - quorums_del_runtime clasifica cada cuenta de pallet por su nombre. - aparcar_un_bond_no_baja_el_quorum reproduce la sonda con la configuracion real. - toda_cuenta_de_pallet_con_saldo_esta_clasificada vigila las cuentas modl del genesis. - 10 contrafactuales en rojo. - El E2E (paso 2b) descubre las cuentas de pallet recorriendo el storage, no copia la lista, y exige el quorum exacto. La comision se quema despues de despachar, y eso se mide en el BurnedDebt de la propia transaccion. Pesos: submit_proposal, submit_treasury_proposal y veto_proposal leen 3 cuentas mas. Medidos en la Pi: 5 procesos limpios, el nucleo calentado 3 s antes de cada uno, a 2,4 GHz. Operacion de la rp1, arrancar.sh: - en cada encendido desde el 26-oct el nodo moria al segundo porque la red no estaba; ahora espera a la ruta por defecto; - un cerrojo flock impide dos instancias; - el pid vivo se reconoce por su linea de comandos. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b6d05eaf45 |
feat(gobierno): spec 18 (RV17), quorum del voto ordinario y pago del tesoro atado a la propuesta
Gobierno (origin-chain-governance): - El voto ordinario exige un quorum de Aye, el 10 % de PILAR_4 §6.6. Se fija AL CREAR la propuesta (VoteQuorumOf) y se consume al decidirla: un set_code no mueve la meta de un voto abierto. Cuenta solo el Aye: con el Nay, un voto en contra podria aprobar; con la abstencion, el mismo OGC inflaria el quorum de muchas propuestas. - Un override del veto tiene que alcanzar ese quorum y superar el Nay original, las dos condiciones del voto ordinario. Sin ellas, vetar abarataba una aprobacion (dos bloqueantes de las revisiones adversariales). - submit_treasury_proposal (call 16) fija destinatario y monto (TreasuryActions), y treasury_disbursement solo paga exactamente eso, una vez. Rechaza montos bajo el deposito existencial y como destinatario la propia tesoreria o la cuenta-bond. - Sin entrada en VoteQuorumOf no hay quorum: la emergencia (que nunca lleva accion de tesoro) y lo creado antes de RV17, que se abrio y voto sin quorum. Runtime: los dos quorums son el 10 % de la emision VIGENTE, redondeado hacia arriba y nunca por debajo del bond (DiezPorCientoDeLaEmision). Con 100M fijos, en la testnet de la casa (100M emitidos, todos de Alice) nada se habria aprobado nunca; en mainnet sigue siendo ~100M. Spec 18, transaction_version 1: todo lo nuevo es aditivo, sin migracion. Node: pasa a opaque::Block. Decodificaba los extrinsics con tipos nativos y rechazaba en el pool la llamada que anadia el set_code (1040, el mismo error que una llamada inexistente). Los bytes y los hashes no cambian: el binario nuevo sincronizo desde cero los 39 273 bloques de la testnet de la casa con los mismos hashes en 8 alturas. El RPC sirve los extrinsics en hex. Pesos: seis extrinsics cambiaron de codigo y se midieron en la Pi 5 (smoke declarado: 5 procesos limpios, sin CI en ningun segundo, maximo de los 5). Los otros once siguen siendo los certificados por Laurha. Se descarto una medida contaminada: su muestreador no veia los tests de la CI. Pruebas: - 363 tests verdes en el workspace. - 24 contrafactuales, los 24 en rojo. - Ensayo en la rp1 sobre una cadena con el codigo vivo de spec 17: upgrade a 18 y E2E firmando como un wallet (scripts/e2e/rv17_tesoro.py). - Ejemplo firmar_como_wallet con tesoro, pagar, votar, depositar y finalizar; los eventos llevan su fase. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
abe680e853 |
docs(vectores): la firma OIT de las invitaciones V1, con los casos que deben fallar
Para Irhis y el servicio de invitaciones de originmetaverse.com (CONTRATO_INVITACIONES_V1 §3.1, origin-metaverse-web d5f5f5e). Ella escribe sus tests antes que el servicio. - Firma sobre etiqueta || 0x00 || JCS(cuerpo). - 4 vectores que deben aceptarse y 11 que deben rechazarse: nonce repetido, ts fuera de ventana, etiqueta cruzada, llave de cabecera distinta, S no canonico, cuerpo cambiado, tres llaves de orden pequeno con firma forjada sin llave privada, y clave JSON duplicada. - Ed25519 en Python puro (referencia del RFC 8032), comprobado con los TEST 1 y 2 del RFC y con OpenSSL byte a byte. Salida determinista. Medido: OpenSSL 3.6.4 ACEPTA las tres firmas forjadas (F8-F10). La verificacion sola no basta; hace falta la comprobacion de llave fuerte, como is_strong_ed25519_key en la cadena (RV14). Contrafactual del generador: sin la comprobacion de orden pequeno, la de S < L o la etiqueta, se niega a emitir. Las comprobaciones de codificacion canonica se solapan con la de orden pequeno en F10 y ningun vector puede separarlas (ver LEEME). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e7b9c0c8bf |
feat(runtime): spec 17 — los pesos se cobran como en la Pi 5, medidos por Laurha en el banco de referencia
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
|
||
|
|
22bab19932 |
feat(rp1): la cadena corre en la Pi 5 de la casa — binario aarch64, despliegue sin root y medida continua
David, 24-oct: «lo que podamos hacer en la rp1 hagamoslo no esperemos la VPS porfavor». Y ese
mismo día, por Ergon: la rp1 aloja el servidor de arranque de la red.
scripts/rp1/:
- construir-aarch64.sh + Dockerfile.aarch64: origin-chain-node release, con el WASM real, para
linux/aarch64 en el Mac.
- Imagen bookworm (glibc 2.36; la rp1 tiene 2.41).
- Target en un volumen de Docker: la VM no ve el disco externo de compilación si se montó
después de arrancar Docker, y reiniciarla tiraría los contenedores de otras hermanas.
- Copia el binario con docker cp y avisa de cómo borrar el volumen.
- desplegar.sh: copia el binario con su sha256 (y deja el anterior para volver atrás), copia
arrancar.sh y medir.sh, e instala tres líneas en el crontab del usuario: @reboot, vigilante
cada 5 min y medida cada 5 min. Sin root y sin systemd.
- arrancar.sh: idempotente.
- taskset -c 0-2: el nodo nunca toca el núcleo 3, el santuario de origin-banco de Laurha.
- nice 10 + ionice 7: la CI de la casa va primero.
- RPC sólo en 127.0.0.1; P2P en el 30333.
- Cadena dev con Alice: es la testnet de la casa, con llaves públicas de desarrollo; ningún
valor real.
- medir.sh: una fila CSV cada 5 min con best, finalizado, pares, RSS, CPU, MB de datos, load,
temperatura y estado de la CI.
scripts/bench/max-de-n.py: funde N weights.rs del mismo pallet cobrando el MÁXIMO de cada
coeficiente.
- Por qué: Laurha midió el 24-oct en su santuario que 8 procesos idénticos de frame-omni-bencher
separan el base weight hasta un 66 %.
- Se niega si los esqueletos difieren (otra lista de extrinsics o de storage) o si los conteos
de reads/writes no coinciden. Avisa si N < 5.
- Controles: tres copias idénticas salen idénticas byte a byte; el máximo cae sólo donde se
subió; un conteo de reads distinto se rechaza; un extrinsic renombrado se rechaza.
firmar_como_wallet: modo cuenta "<frase>" [//derivación], que da la llave sr25519 y la SS58 que
verá un wallet Substrate. Son los vectores para la migración de la wallet Origin! (Irhis).
fix(node): build-spec emite el chain spec REAL cuando el binario lleva el WASM.
- Antes, aunque el binario llevara el WASM real, emitía siempre el de mayo, con :code = «0x»
simulado, y --raw no sacaba el génesis en crudo.
- Ahora usa el mismo spec con el que arranca run (node_chain_spec), y --raw lleva el génesis
entero.
- Sin WASM sigue el simulado, pero avisa de que no sirve para unirse a una red. Mainnet sigue
cerrada por la compuerta L-3.
- Por qué importa, medido hoy: el WASM del runtime NO es reproducible entre máquinas. El mismo
commit compilado en el Mac y en Linux da génesis distintos (0xe80c… frente a 0x0926…), y el
nodo del Mac baneó al de la rp1 con «Genesis mismatch». El génesis tiene que fijarlo un chain
spec que se reparte, no quien compila.
- arrancar.sh usa ~/origin-chain/casa.json cuando existe (exportado en la propia rp1). Así un
binario nuevo nunca crea otro génesis sobre los mismos datos.
Medido en vivo el 24-oct, todo por RPC firmado como un wallet:
- Nodo en la rp1 (Pi 5, Debian 13): bloques cada 6 s y finalidad a 2-3 bloques. RSS ~200 MB y
afinidad 0-2 con nice 10.
- El vigilante de cron lo resucita con el mismo peer id
(12D3KooWDWm5p3Z8Y5DddRki6M6nM1A2xUVQzPqNU1DW53uB6BVi) y sin regenerar la llave.
- E2E en la rp1: remark → ExtrinsicSuccess; finalize_override → ProposalNotFound; uphold0 →
ZeroWeightWindowVote.
- casa.json exportado en la rp1 = estado del bloque 0 de la cadena viva: 46 de 46 claves
idénticas. El nodo reinició sobre él con el mismo génesis y siguió desde el #155.
- Red de dos máquinas por la LAN: el nodo del Mac, con casa.json y la rp1 como bootnode,
sincronizó (mismo best, mismo finalizado). Una transacción enviada al nodo del Mac la incluyó
la rp1 en el bloque #167, con el mismo hash blake2-256 que devolvió author_submitExtrinsic.
Defectos encontrados en mis propios scripts antes de dejarlos publicados:
- desplegar.sh instalaba un crontab VACÍO: con set -e, el grep -v sobre un crontab vacío
abortaba la subshell antes de los echo.
- medir.sh dejaba best/finalizado en NA: set -u con $2 sin definir, y stderr se iba con cron.
- max-de-n.py reescribía 16416 como 16_416 (la prueba de identidad lo cazó).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
3cd1068893 |
feat(e2e): simulacro de set_code 16→17 — la cadena ya se actualiza sin fork, y sin 11e532e no podía
Ensayo en vivo el 2026-10-12, nodo --dev con WASM real, todo firmado por RPC como un wallet:
- A, control negativo: set_code con la MISMA spec 16 → Sudid Err(System, 1) =
SpecVersionNeedsToIncrease.
- B, el upgrade: sudo(set_code) con spec 17 → CodeUpdated + Sudid Ok.
- El RPC anuncia 17 y LastRuntimeUpgrade pasa de {16,"origin-chain"} a {17,"origin-chain"}.
- La migración del simulacro dejó su marca (0x11000000): on_runtime_upgrade corrió.
- C, un wallet tras el upgrade: firmado con spec 16 → «bad signature»; con spec 17 →
ExtrinsicSuccess.
- La finalidad siguió: el bloque del upgrade (#5) quedó finalizado.
Contrafactual, nodo sin type Version (el runtime de antes de
|
||
|
|
ce91aa4572 |
feat(runtime): firmar_como_wallet — el E2E por RPC firma como un wallet, no solo mira bloques
Herramienta (example del runtime) con la que se midió el defecto de
|
||
|
|
11e532e251 |
fix(runtime): frame_system no sabía su propia versión — ningún wallet podía firmar
Medido en el E2E de RV16 por el canal de un wallet: Alice firmando como polkadot.js (versión de state_getRuntimeVersion, hash del génesis, era inmortal) recibía del nodo vivo «Transaction has a bad signature». Causa: SolochainDefaultConfig trae type Version = (), es decir RuntimeVersion::default() (nombre vacío, spec 0, tx 0), y el impl de frame_system lo heredaba sin declararlo — el mismo agujero que DbWeight en RV14. El RPC anunciaba spec 16 / tx 1; CheckSpecVersion y CheckTxVersion reconstruían el payload con 0 / 0. Rompía tres cosas: 1. Ningún wallet podía firmar un extrinsic (BadProof). 2. set_code habría fallado con InvalidSpecName («origin-chain» contra «»): sin upgrades. 3. Las migraciones no corrían nunca: Executive::runtime_upgraded compara 0 contra 0. Nadie lo vio en 16 specs porque ningún test firmaba por el canal del consumidor: los del pallet llaman a las funciones directamente, y los E2E anteriores solo miraban bloques. Arreglo: type Version = Version, con Version = crate::VERSION. Tests (contrafactual: sin la línea, los dos reprueban en la ASERCIÓN — spec_name "" y BadProof): - frame_system_conoce_la_version_que_anuncia_el_rpc - una_firma_de_wallet_estandar_pasa_el_check_del_runtime: firma como un wallet sobre el estado del preset development y pasa por el check real de la firma. E2E después del arreglo, nodo --dev con WASM real: dos extrinsics firmados por //Alice entran por author_submitExtrinsic, se incluyen en el bloque #3 (finalizado) y fallan con los errores que tocaban: finalize_override → ProposalNotFound, uphold_council_veto de peso 0 → ZeroWeightWindowVote. La metadata viva trae las llamadas, el storage, MinOverrideQuorum = 1e26 (100M OGC) y los errores nuevos. Tests: runtime 46, workspace 350. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
3771a15165 |
feat(governance): RV16 — el override del veto se decide al cerrar la ventana, con 4/5 de quien participa
Cierra L-11 (override real del veto). David aprobó el 4/5 el 2026-10-01: «exigir una mayoría calificada de 4/5 en el consejo para anular un veto de emergencia es el cerrojo adecuado antes de retirar la llave Sudo». Qué cambia - La ventana de override tiene dos lados: revertir (override_council_veto) y sostener (uphold_council_veto, nuevo). Quien votó Nay puede sostener el veto; antes no tenía voz. - Nada se decide al cruzar un umbral. finalize_override (nuevo) decide UNA vez, al cerrar la ventana: se revierte si override >= ceil(4/5 * max(quórum, participación)). - El quórum se fija al vetar: max(Aye + Nay del voto original, MinOverrideQuorum). Abstain no cuenta. Así el bond que el veto devuelve no basta para revertirlo y un upgrade no lo mueve. - MinOverrideQuorum en el runtime = 100M OGC (el 10% de PILAR_4 §6.6); integrity_test exige que supere MIN_PROPOSAL_STAKE. - El periodo de voto ordinario dura al menos 14 días (MIN = DEFAULT) y el veto solo cabe dentro de él. Un voto de peso 0 en la ventana se rechaza. - spec_version 16. Pesos de uphold_council_veto y finalize_override medidos (frame-omni-bencher 0.15.0, 50 steps x 20 repeats); los demás re-medidos en el mismo banco. La revisión adversarial (nivel 1 de la directiva) tumbó la primera versión - Bloqueante: la regla de quórum no era monótona y votar «sostener» podía REVERTIR el veto. Ahora sostener nunca ayuda a revertir (test con la frontera exacta 2250/2251). - El mínimo de 48 h regalaba la vía de emergencia a cualquiera; ahora 14 días. - Las abstenciones inflaban el quórum; el quórum podía cambiar con un upgrade; la spec no subía. - 18 contrafactuales (mutaciones V1..V18), todos en rojo sin su pieza. Divergencia que queda abierta (D1 del roadmap V2): PILAR_4 §6.6 ratificó «override por 2/3» y el código implementa 4/5, que es lo que David confirmó. La constitución del DAO lo fija. Tests: gobernanza 78 (94 con runtime-benchmarks), runtime 44. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
a694cbd9f0 |
feat(governance): RV15 — el peso de un voto sale del saldo, y la revision adversaria tumbo la primera version
Cierra L-10. cast_vote y override_council_veto sumaban el stake_weight que
DECLARABA el votante (hueco medido por Palace el 2026-09-06; test en rojo
publicado en
|
||
|
|
d37a278ba6 |
test(governance): el hueco del peso de voto ya tiene donde vivir — test en rojo con control positivo (RV15)
Palace lo midio el 2026-09-06 y escribio que no era explotable porque el nodo
no arrancaba. Desde
|
||
|
|
63918f365b |
feat(node): Origin Chain produce su primer bloque — el bloqueo arquitectural era un diagnostico falso
El node nunca habia producido un bloque: service.rs y rpc.rs estaban fuera del build desde v0.0.49 por un «bloqueo arquitectural» que afirmaba que las crates publicadas sc-consensus-aura 0.52.0 y sp-consensus-aura 0.45.0 no tenian standalone::slot_duration_at ni from_timestamp_and_slot_duration. Las dos estan ahi, en crates.io. La nota guio la decision durante meses. Causa real, medida con cargo check (362 errores) y cargo tree: el workspace mezclaba dos releases de polkadot-sdk. Las sc-* del cliente arrastraban sp-runtime 43 y las frame-* del runtime sp-runtime 44: 36 crates del nucleo duplicadas y errores de la forma «Block no satisface Block». Cura: alinear diez versiones del Cargo.toml (sc-cli 0.55, sc-service 0.54, sc-network 0.53, sc-consensus 0.52, sc-consensus-aura 0.53, sc-consensus-grandpa 0.38, sc-offchain 48, sp-blockchain 42, substrate-frame-rpc-system 47, sc-telemetry 30.0.1). El lock solo PIERDE la generacion vieja (1210 -> 1123 entradas); ninguna crate que use el runtime cambia de version. rpc.rs: el client pasa a ser el FullClient concreto. Nuestro node trabaja sobre el Block completo del runtime, que lleva dentro ChainAccountId, una proyeccion (<<MultiSignature as Verify>::Signer as IdentifyAccount>::AccountId) que rustc no normaliza dentro de un where-clause generico: 6 errores de «un bound declarado en la firma no se satisface». Con el generico fallaba, con el concreto compila. main.rs: subcomando run (sc_cli::RunCmd), que arranca new_full con litep2p o libp2p. chain_spec.rs: node_chain_spec construye un GenericChainSpec con el WASM real del runtime (build-spec sigue con su JSON). run --chain mainnet se RECHAZA: sus validadores son placeholder sin llave (gate L-3) y un node con ese genesis no produciria nunca, en silencio. Test con contrafactual: sin el brazo mainnet el id cae en la rama de ruta y falla como «fichero no encontrado»; el test exige la razon, y en rojo lo dijo asi. Medido (build con --features wasm-build, run --dev --tmp): - bloque #1 importado 2026-09-23 20:04:30, uno cada ~6 s (Aura); - GRANDPA finaliza: #7 finalizado con best #9 a los 60 s; - RPC: chain_getHeader, chain_getFinalizedHead, system_accountNextIndex (Alice = 0) y system_properties (OGC, 18 decimales, ss58 42) responden; - «Essential task txpool-background failed» aparece SOLO al SIGINT (0 ocurrencias a los 50 s con RPC servido; 1 al segundo del SIGINT). - cargo test --workspace: 318 passed, 0 failed (igual que RV14) + 1 nuevo. macOS 27: el build necesita DEVELOPER_DIR=/Library/Developer/CommandLineTools mientras la licencia de Xcode 27 no este aceptada (exit 69 en cc y git). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
defd3fa580 |
feat(identity): RV14 — una identidad sobrevive a perder su llave, y la revision encontro que el storage salia gratis
QUE NACE. rotate_agent_key (call_index 1 de agent-identity) y la runtime API AgentKeyApi::current_key. Hasta hoy un DID quedaba atado de hecho a su llave de nacimiento: no se re-derivaba, pero tampoco habia forma de pasar la autoridad a otra llave. Perder o filtrar la llave obligaba a registrarse otra vez con OTRO DID y perder credenciales, creditos e historial. Ahora el DID es de por vida y lo que rota es que llave manda sobre el. Es la dependencia de dos hilos abiertos: el DID del niño (Marihan) y los creditos de referido indexados por DID (Codex). Diseño: CurrentKey[did] (ausente = manda la de nacimiento, que ya vivia en creator; sin migracion), KeyToDid[llave] (una llave manda sobre un solo DID en toda la vida de la cadena; las retiradas quedan quemadas) y RotationNonce[did]. Dos firmas sobre DOMAIN || genesis || did || vigente || nueva || nonce || who: la vigente autoriza, la nueva prueba control. LO QUE LA REVISION ADVERSARIAL ENCONTRO ANTES DE PUBLICAR. Cinco lentes, 18 hallazgos, 17 confirmados. Todos arreglados aqui y cada arreglo con su contrafactual visto en ROJO: 1. DbWeight = () EN TODO EL RUNTIME. frame_system heredaba el valor por defecto de SolochainDefaultConfig sin declararlo. Cada reads(n)/writes(n) de TODOS los weights.rs valia cero: la E/S de storage no se cobraba en fees ni llenaba el bloque. Ahora RocksDbWeight, con un test que reprueba si vuelve a cero. ESTO CORRIGE LO QUE YO DIJE EN RV13: los reads/writes que medi alli no tenian efecto en el runtime. Lo que RV13 si arreglo es el proof_size y el ref_time de ejecucion, que no pasan por DbWeight. 2. LLAVES DE ORDEN PEQUEÑO. El verificador del host (ed25519-zebra, ZIP215) las acepta. Con el punto identidad como llave y la firma trivial R=identidad s=0, la «prueba de control» se superaba sin llave privada, en rotacion Y en registro: el DID quedaba bajo una llave con la que cualquiera firma en su nombre. Para un DID de un niño en Ethos eso es suplantacion. Ahora is_strong_ed25519_key descomprime el punto, exige codificacion canonica y rechaza orden pequeño (curve25519-dalek, que ya iba dentro del WASM). 3. EL REGISTRO ESCRIBIA KeyToDid PERO NO LO CONSULTABA. Una llave a la que otro DID ya habia rotado fundaba un DID nuevo y pisaba el indice. Hallado cinco veces por separado. Va detras de la colision de DID para no cambiar el error documentado de scenario_12. 4. REPLAY ENTRE CADENAS: el mensaje no llevaba genesis. Ahora si, mismo patron que provenance. Y menores: el evento nuevo va al final del enum (insertarlo en medio corria el indice SCALE de AgentUpdated); la insercion redundante de la llave vieja ya no pisa entradas; la doc dice que perder la CUENTA tambien bloquea la rotacion, no solo perder la llave; el comentario de creator decia «la cuenta que registro» y el codigo guarda la llave de nacimiento. DOS TESTS MIOS PASABAN POR LA RAZON EQUIVOCADA, y lo digo. El de «llave retirada quemada» volvia a la de nacimiento, que atrapa otra clausula. El del ladron firmaba con la llave vigente nueva, algo que un ladron real no tiene; con el pallet roto seguia verde en su mitad de ataque. Reescritos, y ahora el rojo sale de la mitad correcta (verificado por linea del panic). LO QUE QUEDA ABIERTO Y NO ES DE ESTE COMMIT: tras rotar, derive(llave_vigente) ya no es el DID. OCF y Palace hoy DERIVAN el identificador; para un usuario que roto tienen que RESOLVERLO contra KeyToDid. Es una enmienda al DC de unificacion con sus firmantes. La recuperacion por guardianes va aparte. Y KeyToDid crece sin deposito; se reevalua ahora que las escrituras cuestan de verdad. Medido sobre el WASM real (bencher 0.15.0, Mac, no canonico): rotate 97 us, 11 lecturas, 5 escrituras, proof 6100. 318 tests verde. RV13 -> RV14. TRANSACTION_VERSION intacto: call nueva en indice nuevo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |