Files
arkhe-origin-chain/crates/origin-chain-provenance/src/benchmarking.rs
T
davidherranandClaude Opus 4.8 c7fc6a88fc 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>
2026-07-07 22:48:26 -05:00

132 lines
5.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! # 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);
}