feat(bench): maquinaria FRAME v2 de benchmarking para los 5 pallets custom (RV 8→9)

Los 19 extrinsics de provenance/fees/agent-identity/emission/governance pasan de
weights placeholder `from_parts(10_000, 0)` (proof_size=0, sin contar reads/writes
de DB — vector de DoS real) a weights INTERIM hand-derived DB-aware vía el trait
`WeightInfo`. Cierra el mispricing de DoS en TODOS los extrinsics del chain.

Cada pallet gana:
- weights.rs: trait WeightInfo + SubstrateWeight<T> (DB-aware vía T::DbWeight) + impl ()
- benchmarking.rs: benchmarks FRAME v2, VERIFICADOS por impl_benchmark_test_suite!
  (19 benchmarks ejecutan+pasan en los mocks — cargo test --features runtime-benchmarks)
- Config: `type WeightInfo` + swap de los `#[pallet::weight]`

Runtime:
- define_benchmarks! con los 5 pallets + runtime API Benchmark (inerte sin la feature)
- WeightInfo cableado a SubstrateWeight<Runtime> en cada Config
- RUNTIME_VERSION 8→9 (weights = consenso-relevante: fee + llenado de bloque;
  las firmas de los extrinsics NO cambian ⇒ TRANSACTION_VERSION intacto)

3 bugs latentes cerrados (territorio virgen — el runtime NUNCA se compiló con
runtime-benchmarks; las dimensiones nuevas cazan lo que no se había visto):
- pallet-grandpa/try-runtime mal ubicado en la feature runtime-benchmarks (copy-paste
  erróneo: features ortogonales; NUNCA activaba los benchmarks de grandpa)
- feature runtime-benchmarks incompleta → E0046 en OriginFeeAdapter (métodos
  benchmark-gated de OnChargeTransaction) + pallet-staking
  (StakingUnchecked::migrate_to_direct_staker); ahora lista TODOS los pallets como
  un runtime de producción Substrate
- pallet-revive/runtime-benchmarks omitido a propósito (fixtures RISC-V/PolkaVM
  indisponibles; ReviveApi deferred, no benchmarkeado)

emission: mock extraído del `mod tests` inline a `pub mod mock` (reuso del test-suite,
sin duplicar el scaffold; los 15 tests existentes + el benchmark verdes).

GATED (documentado en el §Estado de cada weights.rs): la generación de números
CANÓNICOS por CLI requiere el node sc-service o omni-bencher+WASM (ambos bloqueados
hace sesiones). Los weights interinos son DB-aware/DoS-safe y regenerables de forma
mecánica cuando aterrice el HW de referencia (Pi5/VPS) — benchmarking.rs es la fuente
de verdad de la FORMA.

Verificado: 5 test-suites de pallet verdes (con y sin runtime-benchmarks) + runtime
compila en ambos modos + workspace sin regresión + 40 tests de runtime (incl. v9).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
davidherranandClaude Opus 4.8 committed 2026-07-07 22:48:26 -05:00
1 parent 0d56e0bcbf
commit c7fc6a88fc
29 files changed
+1206 -121

No files matched your search

@@ -0,0 +1,131 @@
//! # Benchmarks para `pallet-origin-provenance` (FRAME v2)
//!
//! Fuente de verdad de la **forma** del weight de cada extrinsic. Los números
//! canónicos se generan con `benchmark pallet` sobre HW de referencia (gated por
//! el node sc-service / omni-bencher+WASM, ver `weights.rs` §Estado); mientras
//! tanto, `SubstrateWeight` lleva números conservadores DB-aware.
//!
//! **Verificación sin HW**: `impl_benchmark_test_suite!` ejecuta cada benchmark
//! una vez dentro del mock runtime (`cargo test -p origin-chain-provenance
//! --features runtime-benchmarks`), probando que el harness corre y que el
//! `verify` post-estado se cumple — el mismo camino que el CLI recorrerá luego.
//!
//! ## `attest_asset` sin firmar en no_std
//!
//! El benchmark no puede firmar Ed25519 (corre en no_std/wasm). Reusa los **golden
//! vectors ASP-3** (payload postcard + firma constantes, keypair seed `[7u8;32]`,
//! genesis `0xAB×32`) que ya prueban el fee-path en `tests_integration.rs`. El
//! setup siembra `block_hash(0) = 0xAB×32` para que el dominio de la firma
//! coincida, exactamente como `mock::new_test_ext_with_genesis`.
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use frame_benchmarking::v2::*;
use frame_support::pallet_prelude::Decode;
use frame_support::BoundedVec;
// `sp-runtime` es dev-dep (no dep normal) → se accede vía el re-export de frame_support.
use frame_support::sp_runtime::traits::Zero;
use frame_system::pallet_prelude::BlockNumberFor;
use frame_system::RawOrigin;
use alloc::vec::Vec;
/// Genesis de los golden vectors ASP-3 (`0xAB×32`). El pallet lee `block_hash(0)`
/// para el dominio de la firma; el setup lo siembra con este valor.
const GOLDEN_GENESIS: [u8; 32] = [0xABu8; 32];
/// Pubkey del validador golden (seed `[7u8;32]`).
const GOLDEN_PUBKEY: [u8; 32] = [
0xea, 0x4a, 0x6c, 0x63, 0xe2, 0x9c, 0x52, 0x0a, 0xbe, 0xf5, 0x50, 0x7b, 0x13, 0x2e, 0xc5, 0xf9,
0x95, 0x47, 0x76, 0xae, 0xbe, 0xbe, 0x7b, 0x92, 0x42, 0x1e, 0xea, 0x69, 0x14, 0x46, 0xd2, 0x2c,
];
/// PAYLOAD1: asset `[1×32]`, atelier level 3, Passed/Info → ANCLA (golden ASP-3).
const PAYLOAD1: &str = "05312e302e300101010101010101010101010101010101010101010101010101010101010101146174656c6965725f63616e6f6e6963616c5f763103ea4a6c63e29c520abef5507b132ec5f9954776aebebe7b92421eea691446d22cd285d8cc0400002a";
/// SIG1: firma Ed25519 de PAYLOAD1 (dominio-separada con genesis `0xAB×32`).
const SIG1: &str = "e155043ad90df787d73e8ba4c3ee961427ecb228637ade77e19bd9b57bb727dcdf3c247462aa8e6facf5697887f3e08f96af1c5f2fdcab10fcfbd2daabe73204";
/// Asset id anclado por PAYLOAD1 (para el `verify` post-estado).
const PAYLOAD1_ASSET_ID: [u8; 32] = [1u8; 32];
fn hexval(c: u8) -> u8 {
match c {
b'0'..=b'9' => c - b'0',
b'a'..=b'f' => c - b'a' + 10,
b'A'..=b'F' => c - b'A' + 10,
_ => 0,
}
}
fn unhex(s: &str) -> Vec<u8> {
let bytes = s.as_bytes();
let mut out = Vec::with_capacity(bytes.len() / 2);
let mut i = 0;
while i + 1 < bytes.len() {
out.push((hexval(bytes[i]) << 4) | hexval(bytes[i + 1]));
i += 2;
}
out
}
fn to_sig(s: &str) -> [u8; 64] {
let v = unhex(s);
let mut out = [0u8; 64];
out.copy_from_slice(&v[..64]);
out
}
/// Siembra `block_hash(0) = 0xAB×32` para que la firma golden verifique.
/// Asume `T::Hash = H256` (mock + runtime real), que es lo que exigen los vectores.
fn seed_golden_genesis<T: Config>() {
let genesis: T::Hash =
Decode::decode(&mut &GOLDEN_GENESIS[..]).expect("H256 decodes from 32 bytes");
frame_system::BlockHash::<T>::insert(BlockNumberFor::<T>::zero(), genesis);
}
#[benchmarks]
mod benchmarks {
use super::*;
#[benchmark]
fn attest_asset() {
let submitter: T::AccountId = whitelisted_caller();
seed_golden_genesis::<T>();
// El validador golden debe estar registrado a nivel >= 3 (FIX-4 gate).
Pallet::<T>::register_validator(RawOrigin::Root.into(), GOLDEN_PUBKEY, 3)
.expect("Root registra el validador golden");
let payload: BoundedVec<u8, MaxPayloadLen> =
BoundedVec::try_from(unhex(PAYLOAD1)).expect("payload dentro de la cota");
let signature = to_sig(SIG1);
#[extrinsic_call]
attest_asset(RawOrigin::Signed(submitter), payload, signature);
// Post-estado: la attestation quedó anclada bajo su asset_id BLAKE3.
assert!(Attestations::<T>::contains_key(AssetHash::new(PAYLOAD1_ASSET_ID)));
}
#[benchmark]
fn register_validator() {
let pubkey = GOLDEN_PUBKEY;
#[extrinsic_call]
register_validator(RawOrigin::Root, pubkey, 3u8);
assert_eq!(AuthorizedValidators::<T>::get(pubkey), Some(3u8));
}
#[benchmark]
fn revoke_validator() {
let pubkey = GOLDEN_PUBKEY;
AuthorizedValidators::<T>::insert(pubkey, 3u8);
#[extrinsic_call]
revoke_validator(RawOrigin::Root, pubkey);
assert!(AuthorizedValidators::<T>::get(pubkey).is_none());
}
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
}