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>
This commit is contained in:
CharonandClaude Opus 5 committed 2026-09-16 15:45:26 -05:00
1 parent 7f68c5b656
commit defd3fa580
12 files changed
+1146 -20

No files matched your search

@@ -14,7 +14,7 @@
//! procedimiento completo y el hardware al que hay que creerle).
//!
//! GENERADO POR SUBSTRATE BENCHMARK CLI VERSION 51.0.0
//! FECHA: 2026-09-09, STEPS: `50`, REPEAT: `20`
//! FECHA: 2026-09-16, STEPS: `50`, REPEAT: `20`
//! HOSTNAME: `MacBookProM3Max.local`, CPU: `<UNKNOWN>`
//! WASM-EXECUTION: `Compiled`, DB CACHE: 1024
@@ -24,7 +24,7 @@
// benchmark
// pallet
// --runtime
// /Users/davidherran/originmetaverse/artifacts/origin-chain/origin_chain_runtime_rv12_bench.compact.compressed.wasm
// target/release/wbuild/origin-chain-runtime/origin_chain_runtime.compact.compressed.wasm
// --genesis-builder-preset
// development
// --pallet
@@ -36,11 +36,11 @@
// --repeat
// 20
// --template
// /Users/davidherran/originmetaverse/external/arkhe-origin-chain/scripts/bench/origin-weights-template.hbs
// scripts/bench/origin-weights-template.hbs
// --header
// /Users/davidherran/originmetaverse/external/arkhe-origin-chain/scripts/bench/HEADER.txt
// scripts/bench/HEADER.txt
// --output
// /Users/davidherran/originmetaverse/external/arkhe-origin-chain/crates/origin-chain-agent-identity/src/weights.rs
// crates/origin-chain-agent-identity/src/weights.rs
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
@@ -54,6 +54,8 @@ use frame_support::{traits::Get, weights::constants::RocksDbWeight, weights::Wei
pub trait WeightInfo {
/// Peso de `register_agent_did` — MEDIDO sobre el WASM, no estimado a mano.
fn register_agent_did() -> Weight;
/// Peso de `rotate_agent_key` — MEDIDO sobre el WASM, no estimado a mano.
fn rotate_agent_key() -> Weight;
}
/// Weights parametrizados por el `DbWeight` real del runtime.
@@ -63,6 +65,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// Proof: `AgentIdentity::Identities` (`max_values`: None, `max_size`: Some(475), added: 2950, mode: `MaxEncodedLen`)
/// Storage: `AgentIdentity::DidToAccount` (r:1 w:1)
/// Proof: `AgentIdentity::DidToAccount` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`)
/// Storage: `AgentIdentity::KeyToDid` (r:1 w:1)
/// Proof: `AgentIdentity::KeyToDid` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`)
/// Storage: `System::Number` (r:1 w:0)
/// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
/// Storage: `System::ExecutionPhase` (r:1 w:0)
@@ -75,21 +79,57 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof size en bytes:
// Medido: `25`
// Estimado: `3940`
// Tiempo minimo de ejecucion: 41_000_000 picosegundos.
Weight::from_parts(43_000_000, 0)
// Tiempo minimo de ejecucion: 54_000_000 picosegundos.
Weight::from_parts(55_000_000, 0)
.saturating_add(Weight::from_parts(0, 3940))
.saturating_add(T::DbWeight::get().reads(6_u64))
.saturating_add(T::DbWeight::get().writes(4_u64))
.saturating_add(T::DbWeight::get().reads(7_u64))
.saturating_add(T::DbWeight::get().writes(5_u64))
}
/// Storage: `AgentIdentity::Identities` (r:1 w:0)
/// Proof: `AgentIdentity::Identities` (`max_values`: None, `max_size`: Some(475), added: 2950, mode: `MaxEncodedLen`)
/// Storage: `AgentIdentity::CurrentKey` (r:1 w:1)
/// Proof: `AgentIdentity::CurrentKey` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`)
/// Storage: `AgentIdentity::KeyToDid` (r:2 w:1)
/// Proof: `AgentIdentity::KeyToDid` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`)
/// Storage: `AgentIdentity::DidToAccount` (r:1 w:0)
/// Proof: `AgentIdentity::DidToAccount` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`)
/// Storage: `AgentIdentity::RotationNonce` (r:1 w:1)
/// Proof: `AgentIdentity::RotationNonce` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`)
/// Storage: `System::BlockHash` (r:1 w:0)
/// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
/// Storage: `System::Number` (r:1 w:0)
/// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
/// Storage: `System::ExecutionPhase` (r:1 w:0)
/// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`)
/// Storage: `System::EventCount` (r:1 w:1)
/// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
/// Storage: `System::Events` (r:1 w:1)
/// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
fn rotate_agent_key() -> Weight {
// Proof size en bytes:
// Medido: `434`
// Estimado: `6100`
// Tiempo minimo de ejecucion: 93_000_000 picosegundos.
Weight::from_parts(97_000_000, 0)
.saturating_add(Weight::from_parts(0, 6100))
.saturating_add(T::DbWeight::get().reads(11_u64))
.saturating_add(T::DbWeight::get().writes(5_u64))
}
}
/// Impl para mocks y tests — `RocksDbWeight` directo, sin necesitar `T`.
impl WeightInfo for () {
fn register_agent_did() -> Weight {
Weight::from_parts(43_000_000, 0)
Weight::from_parts(55_000_000, 0)
.saturating_add(Weight::from_parts(0, 3940))
.saturating_add(RocksDbWeight::get().reads(6_u64))
.saturating_add(RocksDbWeight::get().writes(4_u64))
.saturating_add(RocksDbWeight::get().reads(7_u64))
.saturating_add(RocksDbWeight::get().writes(5_u64))
}
fn rotate_agent_key() -> Weight {
Weight::from_parts(97_000_000, 0)
.saturating_add(Weight::from_parts(0, 6100))
.saturating_add(RocksDbWeight::get().reads(11_u64))
.saturating_add(RocksDbWeight::get().writes(5_u64))
}
}
@@ -119,6 +159,7 @@ mod guard {
fn ningun_weight_declara_proof_size_cero() {
let casos: &[(&str, Weight)] = &[
("register_agent_did", <() as WeightInfo>::register_agent_did()),
("rotate_agent_key", <() as WeightInfo>::rotate_agent_key()),
];
for (nombre, w) in casos {
assert!(