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

+37 -1
View File
@@ -32,6 +32,10 @@ frame-support.workspace = true
frame-executive.workspace = true
frame-metadata-hash-extension.workspace = true
# Benchmarking canonical (FRAME v2) — optional, solo activo bajo runtime-benchmarks.
# Provee el runtime API `Benchmark` + los macros define/list/add_benchmarks.
frame-benchmarking = { workspace = true, optional = true }
# FRAME pallets Phase 1.3-1.5 (per SBS-4 ratified)
pallet-balances.workspace = true
pallet-timestamp.workspace = true
@@ -154,13 +158,45 @@ std = [
"pallet-transaction-payment-rpc-runtime-api/std",
"parity-scale-codec/std",
"scale-info/std",
"frame-benchmarking?/std",
# Slice 2c canonical deferred — substrate-wasm-builder bloqueado WASM build
]
runtime-benchmarks = [
"dep:frame-benchmarking",
"frame-benchmarking/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"pallet-transaction-payment/runtime-benchmarks",
"pallet-grandpa/try-runtime",
# Pallets upstream wired restantes. Necesarios para ALINEAR sus impls de traits
# benchmark-gated (p.ej. StakingUnchecked::migrate_to_direct_staker de
# pallet-staking, gated en sp-staking/runtime-benchmarks) con los traits upstream.
# La feature del runtime nunca fue comprehensiva — producción Substrate lista TODO.
"pallet-staking/runtime-benchmarks",
"frame-election-provider-support/runtime-benchmarks",
"sp-staking/runtime-benchmarks",
"pallet-session/runtime-benchmarks",
"pallet-sudo/runtime-benchmarks",
"pallet-grandpa/runtime-benchmarks",
# NOTA: `pallet-revive/runtime-benchmarks` OMITIDO a propósito — arrastra
# `pallet-revive-fixtures`, cuyo build script requiere el toolchain PolkaVM
# RISC-V (rv32/rv64) que no está disponible aquí y NO es benchmarkeado (ReviveApi
# deferred, ver impl_runtime_apis §SCAFFOLD). Su gating queda OFF de forma
# consistente → sin misalignment de traits. Re-activar cuando el toolchain PolkaVM
# esté en el reference HW y se quieran weights de contratos.
# Pallets custom Charon. TODOS deben propagar runtime-benchmarks para que sus
# impls de traits benchmark-gated (p.ej. OnChargeTransaction del OriginFeeAdapter
# en fees) alineen con los traits upstream. Provenance ya tiene maquinaria FRAME
# v2 (ver `mod benches`); los demás la reciben en el fan-out.
"origin-chain-provenance/runtime-benchmarks",
"origin-chain-fees/runtime-benchmarks",
"origin-chain-emission/runtime-benchmarks",
"origin-chain-agent-identity/runtime-benchmarks",
"origin-chain-governance/runtime-benchmarks",
# BUGFIX: antes decía `pallet-grandpa/try-runtime` — activar try-runtime desde
# la feature de benchmarks era un copy-paste erróneo (features ortogonales;
# metía código try-runtime en builds de benchmark y NUNCA activaba los
# benchmarks de grandpa). Grandpa no está en `define_benchmarks!`, así que la
# corrección es removerlo, no re-apuntarlo.
]
@@ -427,6 +427,9 @@ impl pallet_session::Config for Runtime {
impl origin_chain_provenance::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
// Weights INTERIM hand-derived DB-aware (cierra el DoS de proof_size=0 de los
// placeholders). Regenerable vía `benchmark pallet` cuando aterrice HW/omni-bencher.
type WeightInfo = origin_chain_provenance::weights::SubstrateWeight<Runtime>;
}
// ============================================================
@@ -449,6 +452,8 @@ impl origin_chain_fees::Config for Runtime {
/// — pool-alimentación y pool-consumo apuntan a la misma cuenta og/subsy
/// (jamás re-declarar el literal: un typo divergiría las cuentas).
type SubsidyPalletId = EmissionSubsidyPalletId;
// Weights INTERIM hand-derived DB-aware (ver weights.rs). Regenerable en HW.
type WeightInfo = origin_chain_fees::weights::SubstrateWeight<Runtime>;
}
// ============================================================
@@ -466,6 +471,8 @@ impl origin_chain_fees::Config for Runtime {
impl origin_chain_agent_identity::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
// Weights INTERIM hand-derived DB-aware (ver weights.rs). Regenerable en HW.
type WeightInfo = origin_chain_agent_identity::weights::SubstrateWeight<Runtime>;
}
// ============================================================
@@ -495,6 +502,8 @@ impl origin_chain_governance::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = crate::Balances;
type TreasuryPalletId = GovernanceTreasuryPalletId;
// Weights INTERIM hand-derived DB-aware (ver weights.rs). Regenerable en HW.
type WeightInfo = origin_chain_governance::weights::SubstrateWeight<Runtime>;
}
// ============================================================
@@ -544,6 +553,8 @@ impl origin_chain_emission::Config for Runtime {
// Reusa el MISMO vault de governance (og/gvtry) para el treasury.
type TreasuryPalletId = GovernanceTreasuryPalletId;
type SubsidyPalletId = EmissionSubsidyPalletId;
// Weights INTERIM hand-derived DB-aware (ver weights.rs). Regenerable en HW.
type WeightInfo = origin_chain_emission::weights::SubstrateWeight<Runtime>;
}
// ============================================================
+82 -8
View File
@@ -258,7 +258,15 @@ pub const IMPL_NAME: &str = "origin-chain-charon";
/// 5/7 de emergencia real: nueva dispatchable `endorse_emergency_proposal`
/// (call_index 12) + gate real (antes tautológico). Nuevas storage/errors/evento
/// + 1 call aditiva ⇒ spec bump; TRANSACTION_VERSION intacto (call_index nuevo).
pub const RUNTIME_VERSION: u32 = 8;
///
/// **v9 (benchmarking real machinery)**: los 19 extrinsics de los 5 pallets custom
/// pasan de weights placeholder `from_parts(10_000, 0)` (proof_size=0, sin contar
/// DB — DoS) a weights INTERIM hand-derived DB-aware vía `WeightInfo`. Los weights
/// son consenso-relevantes (afectan fee y llenado de bloque) ⇒ spec bump obligatorio.
/// Las FIRMAS de los extrinsics NO cambian ⇒ TRANSACTION_VERSION intacto. La feature
/// `runtime-benchmarks` gana `define_benchmarks!` + runtime API `Benchmark` (inerte
/// sin la feature) + fix del bug `pallet-grandpa/try-runtime` en la feature.
pub const RUNTIME_VERSION: u32 = 9;
/// Canonical authoring version per Substrate semver canonical.
pub const AUTHORING_VERSION: u32 = 1;
@@ -763,6 +771,52 @@ impl_runtime_apis! {
}
}
// ============================================================
// frame_benchmarking::Benchmark — runtime API para `benchmark pallet`
// ============================================================
//
// Solo bajo la feature `runtime-benchmarks`. Expone la maquinaria FRAME v2 de
// los pallets custom (ver `benches` mod + `define_benchmarks!`). La GENERACIÓN
// de números canónicos por CLI está gated por el node sc-service o
// omni-bencher+WASM (ambos bloqueados hace sesiones, ver `main.rs` §BLOQUEO y
// el §Estado de cada `weights.rs`); mientras tanto los `SubstrateWeight` llevan
// números conservadores DB-aware. Este impl compila y queda listo para que el
// loop de medición sea mecánico cuando la infra aterrice.
#[cfg(feature = "runtime-benchmarks")]
impl frame_benchmarking::Benchmark<Block> for Runtime {
fn benchmark_metadata(
extra: bool,
) -> (
alloc::vec::Vec<frame_benchmarking::BenchmarkList>,
alloc::vec::Vec<frame_support::traits::StorageInfo>,
) {
use frame_support::traits::StorageInfoTrait;
let mut list = alloc::vec::Vec::<frame_benchmarking::BenchmarkList>::new();
list_benchmarks!(list, extra);
let storage_info = AllPalletsWithSystem::storage_info();
(list, storage_info)
}
fn dispatch_benchmark(
config: frame_benchmarking::BenchmarkConfig,
) -> Result<alloc::vec::Vec<frame_benchmarking::BenchmarkBatch>, alloc::string::String>
{
use frame_support::traits::TrackedStorageKey;
// Whitelist vacía: cada benchmark siembra su propio estado (validador
// golden, etc.) y no depende de claves pre-existentes.
let whitelist: alloc::vec::Vec<TrackedStorageKey> = alloc::vec::Vec::new();
let mut batches = alloc::vec::Vec::<frame_benchmarking::BenchmarkBatch>::new();
let params = (&config, &whitelist);
add_benchmarks!(params, batches);
Ok(batches)
}
}
// ============================================================
// Phase 3 Slice 3.1 SCAFFOLD — ReviveApi exposure DEFERRED
// ============================================================
@@ -818,6 +872,26 @@ impl_runtime_apis! {
*/
}
// ============================================================
// Registro de benchmarks canonical (frame_benchmarking::define_benchmarks!)
// ============================================================
//
// Lista los pallets custom cuya maquinaria FRAME v2 se expone vía el runtime API
// `Benchmark`. Genera los macros `list_benchmarks!` + `add_benchmarks!` (ambos
// `#[macro_export]` → crate-global). Cada `[crate_path, PalletAlias]` mapea al
// alias del `#[runtime]` de arriba. Los pallets upstream (balances/timestamp/…)
// se benchmarkean con sus propios weights ya generados; aquí solo van los míos.
#[cfg(feature = "runtime-benchmarks")]
mod benches {
frame_benchmarking::define_benchmarks!(
[origin_chain_provenance, Provenance]
[origin_chain_fees, Fees]
[origin_chain_agent_identity, AgentIdentity]
[origin_chain_emission, Emission]
[origin_chain_governance, Governance]
);
}
// ============================================================
// Runtime metadata canonical (RuntimeVersion + Executive)
// ============================================================
@@ -851,13 +925,13 @@ mod tests {
use super::*;
#[test]
fn runtime_version_v8_governance_hardening() {
// v8 = hardening de governance (audit ultra 2026-07-04 + decisiones A/B):
// correctitud (override/treasury/veto) + bond de propuesta (A) + flujo 5/7
// de emergencia real con endorse_emergency_proposal (B, call_index 12).
// Call aditiva + storage/metadata nuevos ⇒ spec bump; los extrinsics
// EXISTENTES no cambian formato ⇒ TRANSACTION_VERSION intacto.
assert_eq!(RUNTIME_VERSION, 8);
fn runtime_version_v9_benchmarking_weights() {
// v9 = benchmarking real machinery: los 19 extrinsics de los 5 pallets
// custom pasan de weights placeholder (proof_size=0, DoS) a weights INTERIM
// hand-derived DB-aware vía WeightInfo. Los weights son consenso-relevantes
// (fee + llenado de bloque) ⇒ spec bump. Las FIRMAS de los extrinsics NO
// cambian ⇒ TRANSACTION_VERSION intacto.
assert_eq!(RUNTIME_VERSION, 9);
assert_eq!(TRANSACTION_VERSION, 1);
}