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:
1 parent
7f68c5b656
commit
defd3fa580
12 files changed
+1146
-20
No files matched your search
@@ -99,6 +99,19 @@ impl frame_system::Config for Runtime {
|
||||
type AccountData = pallet_balances::AccountData<Balance>;
|
||||
type SS58Prefix = SS58Prefix;
|
||||
type MaxConsumers = ConstU32<16>;
|
||||
/// **El coste de leer y escribir storage.** Faltaba, y por eso valía CERO.
|
||||
///
|
||||
/// `SolochainDefaultConfig` trae `type DbWeight = ();`, y este `impl` lo heredaba sin
|
||||
/// declararlo: `RuntimeDbWeight { read: 0, write: 0 }`. Todo `.reads(n)` y
|
||||
/// `.writes(n)` de los `weights.rs` de la cadena se multiplicaba por cero, así que la
|
||||
/// E/S de storage no se cobraba en fees ni contaba para llenar el bloque. Lo midió la
|
||||
/// revisión adversarial de RV14 con una sonda sobre el runtime real.
|
||||
///
|
||||
/// ⚠️ Esto corrige también lo que RV13 afirmaba haber arreglado. RV13 midió los 19
|
||||
/// extrinsics y subió sus reads/writes, pero con `DbWeight = ()` esa parte no tuvo
|
||||
/// ningún efecto en el runtime. Lo que RV13 sí arregló de verdad es el `proof_size`
|
||||
/// y el `ref_time` de ejecución, que no pasan por `DbWeight`.
|
||||
type DbWeight = frame_support::weights::constants::RocksDbWeight;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
@@ -340,6 +340,33 @@ pub const IMPL_NAME: &str = borrowed_str(&VERSION.impl_name);
|
||||
/// correr `scripts/bench/regen-weights.sh` en el Pi 5 con governor y aislamiento
|
||||
/// declarados (peticion abierta a Laurha, `request:66f500d54e28a80c`).
|
||||
/// TRANSACTION_VERSION intacto: ninguna firma de extrinsic cambia.
|
||||
///
|
||||
/// **v14 (una identidad sobrevive a perder su llave)**: nace `rotate_agent_key`
|
||||
/// (call_index 1 de `origin-chain-agent-identity`) y la runtime API `AgentKeyApi`.
|
||||
/// Hasta hoy el DID quedaba atado de hecho a su llave de nacimiento: no se
|
||||
/// re-derivaba, pero tampoco había forma de pasar la autoridad a otra llave, así que
|
||||
/// perder o filtrar la llave obligaba a registrarse otra vez con OTRO DID y perder
|
||||
/// credenciales, créditos e historial. Ahora el DID es el mismo de por vida y lo que
|
||||
/// rota es qué llave manda sobre él. Exige dos firmas —la vigente autoriza, la nueva
|
||||
/// prueba control— y una llave retirada queda quemada para siempre. Es la
|
||||
/// dependencia de dos hilos: el DID del niño (Marihan) y los créditos de referido
|
||||
/// indexados por DID (Codex). Nuevo storage (`CurrentKey`, `KeyToDid`,
|
||||
/// `RotationNonce`) + call aditiva en índice nuevo ⇒ spec bump; TRANSACTION_VERSION
|
||||
/// intacto. Sin migración: una identidad sin entrada en `CurrentKey` resuelve a su
|
||||
/// llave de nacimiento, que ya vivía en `creator`.
|
||||
///
|
||||
/// **Y lo que la revisión adversarial encontró antes de publicar** (5 lentes, 18
|
||||
/// hallazgos, 17 confirmados, cada arreglo con su contrafactual visto en rojo):
|
||||
/// - **`DbWeight = ()` en todo el runtime** — heredado sin declarar de
|
||||
/// `SolochainDefaultConfig`. La E/S de storage valía CERO en fees y en llenado de
|
||||
/// bloque para TODOS los extrinsics. Ahora `RocksDbWeight`. Esto invalida en parte lo
|
||||
/// que dice la nota de v13: los reads/writes que midió no tenían efecto en el runtime;
|
||||
/// el `proof_size` y el `ref_time` de ejecución sí.
|
||||
/// - **Llaves de orden pequeño**: el verificador del host (ZIP215) las acepta, y con la
|
||||
/// firma trivial `R = identidad, s = 0` la «prueba de control» se superaba sin llave
|
||||
/// privada, en rotación Y en registro. Ahora `is_strong_ed25519_key` las rechaza.
|
||||
/// - **El registro no consultaba `KeyToDid`**: una llave rotada fundaba otro DID.
|
||||
/// - **Replay entre cadenas**: el mensaje de rotación no llevaba génesis. Ahora sí.
|
||||
pub const RUNTIME_VERSION: u32 = VERSION.spec_version;
|
||||
|
||||
/// Canonical authoring version per Substrate semver canonical.
|
||||
@@ -783,6 +810,12 @@ impl_runtime_apis! {
|
||||
//
|
||||
// Phase 2.7 COMPLETE canonical post-v0.0.27 🎉
|
||||
|
||||
impl origin_chain_agent_identity::AgentKeyApi<Block> for Runtime {
|
||||
fn current_key(did: origin_chain_types::Did) -> Option<[u8; 32]> {
|
||||
AgentIdentity::current_key_of(&did)
|
||||
}
|
||||
}
|
||||
|
||||
impl origin_chain_agent_identity::AgentIdentityApi<Block> for Runtime {
|
||||
fn query_identity(
|
||||
account: origin_chain_types::AccountId,
|
||||
@@ -986,7 +1019,7 @@ pub const VERSION: sp_version::RuntimeVersion = sp_version::RuntimeVersion {
|
||||
spec_name: alloc::borrow::Cow::Borrowed("origin-chain"),
|
||||
impl_name: alloc::borrow::Cow::Borrowed("origin-chain-charon"),
|
||||
authoring_version: 1,
|
||||
spec_version: 13,
|
||||
spec_version: 14,
|
||||
impl_version: 1,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 1,
|
||||
@@ -1009,6 +1042,38 @@ use sp_api::impl_runtime_apis;
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// El storage cuesta: leer y escribir no puede ser gratis en el runtime.
|
||||
///
|
||||
/// Nace de un defecto que sobrevivió a toda la vida de la cadena: `frame_system`
|
||||
/// heredaba `DbWeight = ()` y cada `.reads(n)`/`.writes(n)` de los pesos valía cero.
|
||||
/// Las 19 mediciones de RV13 subieron esos números sin cambiar nada en la práctica.
|
||||
#[test]
|
||||
fn leer_y_escribir_storage_no_es_gratis() {
|
||||
use frame_support::traits::Get;
|
||||
// Tipo anotado a propósito: con `DbWeight = ()` la llamada sin anotar ni compila
|
||||
// (ambigüedad de `Get<T>`), y un test que solo reprueba por un error de inferencia
|
||||
// protege por accidente. Anotado, compila en los dos casos y reprueba la ASERCIÓN.
|
||||
let db: frame_support::weights::RuntimeDbWeight =
|
||||
<Runtime as frame_system::Config>::DbWeight::get();
|
||||
assert!(
|
||||
db.read > 0,
|
||||
"DbWeight.read = 0: las lecturas de storage salen gratis"
|
||||
);
|
||||
assert!(
|
||||
db.write > 0,
|
||||
"DbWeight.write = 0: las escrituras de storage salen gratis"
|
||||
);
|
||||
// Y llega de verdad a un peso real de la cadena.
|
||||
let rot = <origin_chain_agent_identity::weights::SubstrateWeight<Runtime>
|
||||
as origin_chain_agent_identity::weights::WeightInfo>::rotate_agent_key();
|
||||
let base = <() as origin_chain_agent_identity::weights::WeightInfo>::rotate_agent_key();
|
||||
assert_eq!(
|
||||
rot.ref_time(),
|
||||
base.ref_time(),
|
||||
"el runtime debe usar RocksDbWeight, igual que el impl ()"
|
||||
);
|
||||
}
|
||||
|
||||
/// Fija la version vigente y la invariante que la acompaña.
|
||||
///
|
||||
/// El test NO cuenta la historia de cada version — eso vive en el doc comment
|
||||
@@ -1022,7 +1087,7 @@ mod tests {
|
||||
/// mismo commit en que la version subio a 12.
|
||||
#[test]
|
||||
fn spec_version_vigente_y_transaction_version_intacta() {
|
||||
assert_eq!(RUNTIME_VERSION, 13);
|
||||
assert_eq!(RUNTIME_VERSION, 14);
|
||||
assert_eq!(TRANSACTION_VERSION, 1);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user