feat(weights): RV13 — los 19 estaban subdeclarados, los 19

Primera medicion real de los weights con frame-omni-bencher sobre el
propio blob WASM. El resultado no es un ajuste fino: los 19 extrinsics
de los 5 pallets custom declaraban MENOS de lo que cuestan.

EL PATRON SISTEMATICO. Se costeo el storage del pallet y se olvido el
que cuesta emitir un evento — System::Events, EventCount, ExecutionPhase
y Number son reads y writes reales en cada extrinsic que deposita
evento. Ejemplos medidos: attest_asset declaraba 4 reads / 1 write y
cuesta 7/3; submit_proposal declaraba 12 us y cuesta 62; register_agent_did
declaraba 3/2 y cuesta 6/4.

EL PEOR, Y ES UN ERROR MIO DOCUMENTADO AL REVES. proof_size valia 0 en
los 19. La cabecera de los interinos afirmaba que T::DbWeight "puebla
tanto ref_time como proof_size ... cerrando el hueco". Es falso y se
comprueba en la fuente: sp_weights::RuntimeDbWeight solo tiene los
campos read y write, y reads(r) devuelve from_parts(read * r, 0). Los
interinos arreglaron reads/writes y dejaron el proof_size exactamente
donde estaba. Un proof_size en cero miente al block-builder sobre el
peso PoV: es el vector de DoS que la nota decia haber cerrado. La
afirmacion sobrevivio mas de un mes sin que ninguna lectura la
desmintiera; la desmintio una medicion.

Cada fichero generado lleva ahora un guard GENERADO desde la lista real
de benchmarks que reprueba cualquier proof_size = 0 o ref_time = 0.
Verificado que sabe reprobar: contra los weights anteriores se pone rojo
con "`attest_asset` declara proof_size = 0".

ADVERTENCIA QUE VA EN EL CODIGO, NO SOLO AQUI: son numeros de un
MacBook Pro M3 Max, NO del banco de referencia BM V2. La forma
(reads/writes/proof_size) no depende del hardware y es correcta; el
ref_time si, y en un Pi 5 sera mayor. Un caso lo enseña solo:
attest_asset BAJA de 50 a 44 us, y es el extrinsic dominado por el
verify Ed25519, el mas sensible al silicio. Antes de mainnet hay que
regenerar en el Pi 5 con governor y aislamiento declarados — peticion
abierta a Laurha (request:66f500d54e28a80c) con el blob, el comando y
la version exacta del bencher.

RV12 -> RV13 (weights = consenso). TRANSACTION_VERSION intacto.
295 tests verde.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
davidherranandClaude Opus 5 committed 2026-09-09 11:06:39 -05:00
1 parent b72650974d
commit 38e5602d78
7 files changed
+1084 -245

No files matched your search

+178 -71
View File
@@ -1,87 +1,194 @@
//! # Weights para `pallet-origin-provenance`
//!
//! ## Estado: INTERIM hand-derived (DB-aware, DoS-safe) — pendiente regeneración HW
//!
//! Estos weights son **derivados a mano de forma conservadora**, NO autogenerados
//! por `frame-benchmarking-cli` sobre hardware de referencia todavía. La razón es
//! infraestructural, no de diseño: generar los números canónicos requiere ejecutar
//! el loop `benchmark pallet`, que necesita **o** el node `sc-service` real
//! (bloqueado por el mismatch de API sc-*/sp-consensus del tag stable2509-3, ver
//! `main.rs` §BLOQUEO) **o** `frame-omni-bencher` sobre el `WASM_BINARY` (gated tras
//! la feature `wasm-build`). Cuando cualquiera de los dos aterrice (VPS / HW de
//! referencia Pi5), regenerar es mecánico: los benchmarks de `benchmarking.rs` son
//! la **fuente de verdad de la forma** y sobrescribirán estos números.
//!
//! ## Por qué esto ya es una mejora de seguridad REAL
//!
//! Los placeholders anteriores eran `Weight::from_parts(10_000, 0)`: **proof_size = 0
//! y sin contabilizar reads/writes de DB**. Eso es un vector de DoS — un atacante
//! llama extrinsics que tocan varias entradas de storage pagando casi nada, y el
//! `proof_size = 0` miente al block-builder sobre el peso PoV. Estos weights usan
//! `T::DbWeight` (RocksDbWeight en el runtime), que puebla **tanto ref_time como
//! proof_size** por cada read/write real contado del código — cerrando el hueco.
//!
//! El componente `from_parts(N, 0)` fijo cubre el compute (decode postcard, verify
//! Ed25519, hashing) con holgura conservadora; el `DbWeight` cubre el storage.
// SPDX-License-Identifier: Apache-2.0
// Origin Chain — Pilar 4 · Charon
//
// ATENCION AL LEER ESTOS NUMEROS: un weight es una LATENCIA, y una latencia sin
// el hardware y el perfil de potencia que la produjeron no se puede reproducir.
// La cabecera dice HOSTNAME y CPU: creele a eso y a nada mas. Un weight medido
// en un portatil y cobrado en un Pi 5 es mispricing estructural, solo que
// invisible. Los numeros CANONICOS de mainnet salen del banco de referencia
// (Pi 5, governor y aislamiento declarados), no de la maquina de quien compila.
//! Weights AUTOGENERADOS para `origin_chain_provenance` — NO editar a mano.
//!
//! Regenerar: `scripts/bench/regen-weights.sh` (ver ese script para el
//! 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`
//! HOSTNAME: `MacBookProM3Max.local`, CPU: `<UNKNOWN>`
//! WASM-EXECUTION: `Compiled`, DB CACHE: 1024
// Comando ejecutado:
// /Users/davidherran/.cargo/bin/frame-omni-bencher
// v1
// benchmark
// pallet
// --runtime
// /Users/davidherran/originmetaverse/artifacts/origin-chain/origin_chain_runtime_rv12_bench.compact.compressed.wasm
// --genesis-builder-preset
// development
// --pallet
// origin_chain_provenance
// --extrinsic
//
// --steps
// 50
// --repeat
// 20
// --template
// /Users/davidherran/originmetaverse/external/arkhe-origin-chain/scripts/bench/origin-weights-template.hbs
// --header
// /Users/davidherran/originmetaverse/external/arkhe-origin-chain/scripts/bench/HEADER.txt
// --output
// /Users/davidherran/originmetaverse/external/arkhe-origin-chain/crates/origin-chain-provenance/src/weights.rs
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(clippy::unnecessary_cast)]
#![allow(missing_docs)]
use core::marker::PhantomData;
use frame_support::{traits::Get, weights::constants::RocksDbWeight, weights::Weight};
/// Trait de weights para los extrinsics de `pallet-origin-provenance`.
/// Trait de weights para los extrinsics de `origin_chain_provenance`.
pub trait WeightInfo {
/// Peso de `attest_asset` — decode postcard + 3-4 reads + verify Ed25519 + 1 write.
fn attest_asset() -> Weight;
/// Peso de `register_validator` (Root) — 1 write.
fn register_validator() -> Weight;
/// Peso de `revoke_validator` (Root) — 1 write (remove).
fn revoke_validator() -> Weight;
/// Peso de `attest_asset` — MEDIDO sobre el WASM, no estimado a mano.
fn attest_asset() -> Weight;
/// Peso de `register_validator` — MEDIDO sobre el WASM, no estimado a mano.
fn register_validator() -> Weight;
/// Peso de `revoke_validator` — MEDIDO sobre el WASM, no estimado a mano.
fn revoke_validator() -> Weight;
}
/// Weights para `pallet-origin-provenance` parametrizados por el `DbWeight` del runtime.
///
/// Se usa en el runtime como `SubstrateWeight<Runtime>` → los reads/writes se cuestan
/// con el `RocksDbWeight` real de la cadena, no con constantes hardcodeadas.
/// Weights parametrizados por el `DbWeight` real del runtime.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
fn attest_asset() -> Weight {
// Compute conservador: decode postcard (~115 bytes) + Ed25519 verify
// (dominante, ~50µs en HW de referencia) + BLAKE-hashing del dominio.
// Storage: contains_key(Attestations) + get(AuthorizedValidators) +
// block_hash(0) + block_number = 4 reads; insert(Attestations) = 1 write.
Weight::from_parts(50_000_000, 0)
.saturating_add(T::DbWeight::get().reads(4_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
fn register_validator() -> Weight {
// ensure_root (in-memory) + insert(AuthorizedValidators) = 1 write.
Weight::from_parts(8_000_000, 0).saturating_add(T::DbWeight::get().writes(1_u64))
}
fn revoke_validator() -> Weight {
// ensure_root (in-memory) + remove(AuthorizedValidators) = 1 write.
Weight::from_parts(8_000_000, 0).saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: `Provenance::Attestations` (r:1 w:1)
/// Proof: `Provenance::Attestations` (`max_values`: None, `max_size`: Some(505), added: 2980, mode: `MaxEncodedLen`)
/// Storage: `Provenance::AuthorizedValidators` (r:1 w:0)
/// Proof: `Provenance::AuthorizedValidators` (`max_values`: None, `max_size`: Some(49), added: 2524, 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 attest_asset() -> Weight {
// Proof size en bytes:
// Medido: `153`
// Estimado: `3970`
// Tiempo minimo de ejecucion: 43_000_000 picosegundos.
Weight::from_parts(44_000_000, 0)
.saturating_add(Weight::from_parts(0, 3970))
.saturating_add(T::DbWeight::get().reads(7_u64))
.saturating_add(T::DbWeight::get().writes(3_u64))
}
/// 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`)
/// Storage: `Provenance::AuthorizedValidators` (r:0 w:1)
/// Proof: `Provenance::AuthorizedValidators` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`)
fn register_validator() -> Weight {
// Proof size en bytes:
// Medido: `19`
// Estimado: `1504`
// Tiempo minimo de ejecucion: 6_000_000 picosegundos.
Weight::from_parts(7_000_000, 0)
.saturating_add(Weight::from_parts(0, 1504))
.saturating_add(T::DbWeight::get().reads(4_u64))
.saturating_add(T::DbWeight::get().writes(3_u64))
}
/// 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`)
/// Storage: `Provenance::AuthorizedValidators` (r:0 w:1)
/// Proof: `Provenance::AuthorizedValidators` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`)
fn revoke_validator() -> Weight {
// Proof size en bytes:
// Medido: `19`
// Estimado: `1504`
// Tiempo minimo de ejecucion: 6_000_000 picosegundos.
Weight::from_parts(7_000_000, 0)
.saturating_add(Weight::from_parts(0, 1504))
.saturating_add(T::DbWeight::get().reads(4_u64))
.saturating_add(T::DbWeight::get().writes(3_u64))
}
}
/// Impl no-op para mocks / tests — usa `RocksDbWeight` directo (no requiere `T`).
/// Impl para mocks y tests — `RocksDbWeight` directo, sin necesitar `T`.
impl WeightInfo for () {
fn attest_asset() -> Weight {
Weight::from_parts(50_000_000, 0)
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
fn register_validator() -> Weight {
Weight::from_parts(8_000_000, 0).saturating_add(RocksDbWeight::get().writes(1_u64))
}
fn revoke_validator() -> Weight {
Weight::from_parts(8_000_000, 0).saturating_add(RocksDbWeight::get().writes(1_u64))
}
fn attest_asset() -> Weight {
Weight::from_parts(44_000_000, 0)
.saturating_add(Weight::from_parts(0, 3970))
.saturating_add(RocksDbWeight::get().reads(7_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}
fn register_validator() -> Weight {
Weight::from_parts(7_000_000, 0)
.saturating_add(Weight::from_parts(0, 1504))
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}
fn revoke_validator() -> Weight {
Weight::from_parts(7_000_000, 0)
.saturating_add(Weight::from_parts(0, 1504))
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}
}
#[cfg(test)]
mod guard {
use super::*;
/// NINGUN EXTRINSIC PUEDE DECLARAR `proof_size = 0`.
///
/// El `proof_size` es lo que el block-builder usa para no pasarse del limite
/// de PoV. Un cero no dice «este extrinsic no toca storage»: dice «miento
/// sobre lo que va a ocupar la prueba», y con eso entra en un bloque mucho
/// mas de lo que cabe.
///
/// El test nace de un error propio y de la peor clase: la cabecera de los
/// weights interinos afirmaba que `T::DbWeight` «puebla tanto ref_time como
/// proof_size … cerrando el hueco». Es falso y se comprueba en la fuente —
/// `sp_weights::RuntimeDbWeight` solo tiene los campos `read` y `write`, y
/// `reads(r)` devuelve `Weight::from_parts(read * r, 0)`. Los interinos
/// arreglaron reads/writes y dejaron el proof_size donde estaba: en 0. La
/// afirmacion de haberlo cerrado sobrevivio mas de un mes sin que ninguna
/// lectura la desmintiera; la desmintio una medicion.
///
/// Esta lista la GENERA la plantilla a partir de los benchmarks reales, asi
/// que no puede quedarse corta cuando se anada un extrinsic nuevo.
#[test]
fn ningun_weight_declara_proof_size_cero() {
let casos: &[(&str, Weight)] = &[
("attest_asset", <() as WeightInfo>::attest_asset()),
("register_validator", <() as WeightInfo>::register_validator()),
("revoke_validator", <() as WeightInfo>::revoke_validator()),
];
for (nombre, w) in casos {
assert!(
w.proof_size() > 0,
"`{nombre}` declara proof_size = 0: miente al block-builder sobre el peso PoV"
);
assert!(
w.ref_time() > 0,
"`{nombre}` declara ref_time = 0: el extrinsic saldria gratis"
);
}
}
}