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:
1 parent
0d56e0bcbf
commit
c7fc6a88fc
29 files changed
+1206
-121
No files matched your search
@@ -34,6 +34,9 @@ sp-io = { workspace = true }
|
||||
parity-scale-codec = { workspace = true }
|
||||
scale-info = { workspace = true }
|
||||
|
||||
# Benchmarking canonical (FRAME v2) — optional, solo activo bajo runtime-benchmarks.
|
||||
frame-benchmarking = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# Mock runtime canonical para integration tests v0.0.26 — Pattern v0.0.18/v0.0.22 mirror.
|
||||
sp-core = { workspace = true, default-features = true }
|
||||
@@ -52,8 +55,11 @@ std = [
|
||||
"sp-runtime/std",
|
||||
"parity-scale-codec/std",
|
||||
"scale-info/std",
|
||||
"frame-benchmarking?/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"dep:frame-benchmarking",
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
//! # Benchmarks para `pallet-agent-identity` (FRAME v2)
|
||||
//!
|
||||
//! Fuente de verdad de la forma del weight de `register_agent_did`. Verificado por
|
||||
//! `impl_benchmark_test_suite!` en el mock (`cargo test -p origin-chain-agent-identity
|
||||
//! --features runtime-benchmarks`). Números canónicos gated por HW/omni-bencher
|
||||
//! (ver `weights.rs` §Estado).
|
||||
//!
|
||||
//! ## Sin firmar en no_std (mismo patrón que provenance)
|
||||
//!
|
||||
//! `register_agent_did` verifica una firma Ed25519 de binding sobre
|
||||
//! `AGENT_DID_REGISTER_DOMAIN || who.encode()`. El benchmark no puede firmar en
|
||||
//! no_std, así que usa un **golden vector determinista**: caller fijo `[1u8;32]`
|
||||
//! (= `alice_account` del mock), keypair seed `[0x11;32]` (= `alice_pair`), y la
|
||||
//! firma precomputada de ese par sobre el mensaje de ESE caller. Como el mensaje
|
||||
//! depende de `who`, el caller DEBE ser exactamente `[1u8;32]`.
|
||||
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
|
||||
use super::*;
|
||||
use frame_benchmarking::v2::*;
|
||||
use frame_support::pallet_prelude::Decode;
|
||||
use frame_system::RawOrigin;
|
||||
// `AgentKind` llega vía `super::*` (re-exportado en el crate root).
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Caller golden: `alice_account` del mock = `AccountId32::new([1u8;32])`.
|
||||
const CALLER: [u8; 32] = [1u8; 32];
|
||||
/// Pubkey golden: `alice_pair` (seed `[0x11;32]`).
|
||||
const BENCH_PUBKEY: &str = "d04ab232742bb4ab3a1368bd4615e4e6d0224ab71a016baf8520a332c9778737";
|
||||
/// Firma golden: `alice_pair` sobre `AGENT_DID_REGISTER_DOMAIN || [1u8;32].encode()`.
|
||||
const BENCH_SIG: &str = "9f593fc1941f09b36b41f9001a9da9ecde506edd08627a35e571c0c9994c2dccbd9e44b371751fdbed85f7bc0ce0c011653b2e5304ac96da5a948c63557c0b04";
|
||||
|
||||
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_32(s: &str) -> [u8; 32] {
|
||||
let v = unhex(s);
|
||||
let mut out = [0u8; 32];
|
||||
out.copy_from_slice(&v[..32]);
|
||||
out
|
||||
}
|
||||
|
||||
fn to_64(s: &str) -> [u8; 64] {
|
||||
let v = unhex(s);
|
||||
let mut out = [0u8; 64];
|
||||
out.copy_from_slice(&v[..64]);
|
||||
out
|
||||
}
|
||||
|
||||
#[benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn register_agent_did() {
|
||||
// Caller EXACTO `[1u8;32]` para que el mensaje firmado coincida.
|
||||
// Asume `T::AccountId = AccountId32` (mock + runtime real).
|
||||
let who: T::AccountId =
|
||||
Decode::decode(&mut &CALLER[..]).expect("AccountId32 decodes from 32 bytes");
|
||||
let pubkey = to_32(BENCH_PUBKEY);
|
||||
let signature = to_64(BENCH_SIG);
|
||||
let co_authors: Vec<[u8; 32]> = Vec::new();
|
||||
|
||||
#[extrinsic_call]
|
||||
register_agent_did(
|
||||
RawOrigin::Signed(who.clone()),
|
||||
pubkey,
|
||||
AgentKind::Human,
|
||||
co_authors,
|
||||
signature,
|
||||
);
|
||||
|
||||
// Post-estado: la identidad quedó registrada para el caller.
|
||||
assert!(Identities::<T>::contains_key(&who));
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
}
|
||||
@@ -52,6 +52,14 @@
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
/// Weights canonical (INTERIM hand-derived DB-aware — ver `weights.rs` §Estado).
|
||||
pub mod weights;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
/// Benchmarks canonical (FRAME v2) — solo bajo `runtime-benchmarks`.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
|
||||
/// Re-export canonical types desde origin-chain-types para consumers.
|
||||
pub use origin_chain_types::{AgentIdentity, AgentKind, Did};
|
||||
|
||||
@@ -127,6 +135,8 @@ pub mod pallet {
|
||||
use origin_chain_types::{AccountId, AgentChain, AgentIdentity, AgentKind, Did};
|
||||
use sp_core::ed25519;
|
||||
use sp_runtime::traits::SaturatedConversion;
|
||||
// Trait de weights en scope para resolver `T::WeightInfo::<method>()`.
|
||||
use crate::weights::WeightInfo;
|
||||
|
||||
/// Pallet configuration trait canonical.
|
||||
///
|
||||
@@ -142,6 +152,9 @@ pub mod pallet {
|
||||
/// Runtime event type canonical wired into `RuntimeEvent` aggregate.
|
||||
type RuntimeEvent: From<Event<Self>>
|
||||
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
|
||||
/// Weight information canonical para los extrinsics de este pallet.
|
||||
type WeightInfo: crate::weights::WeightInfo;
|
||||
}
|
||||
|
||||
/// Pallet definition canonical.
|
||||
@@ -248,7 +261,7 @@ pub mod pallet {
|
||||
/// 7. Insert storage canonical: `Identities` + `DidToAccount`
|
||||
/// 8. Emit `AgentRegistered` + `AgentBound` (if cross-species) canonical
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::register_agent_did())]
|
||||
pub fn register_agent_did(
|
||||
origin: OriginFor<T>,
|
||||
public_key: [u8; 32],
|
||||
|
||||
@@ -29,6 +29,7 @@ impl frame_system::Config for Test {
|
||||
|
||||
impl origin_chain_agent_identity::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
/// Test externalities builder canonical (pattern v0.0.18/v0.0.22 mirror).
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
//! # Weights para `pallet-agent-identity`
|
||||
//!
|
||||
//! ## Estado: INTERIM hand-derived (DB-aware, DoS-safe) — pendiente regeneración HW
|
||||
//!
|
||||
//! Ver `origin-chain-provenance/src/weights.rs` §Estado para el racional completo.
|
||||
//! Números conservadores DB-aware vía `T::DbWeight` (cierran el DoS de proof_size=0
|
||||
//! del placeholder); `benchmarking.rs` es la fuente de verdad de la forma.
|
||||
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use frame_support::{traits::Get, weights::constants::RocksDbWeight, weights::Weight};
|
||||
|
||||
/// Trait de weights para los extrinsics de `pallet-agent-identity`.
|
||||
pub trait WeightInfo {
|
||||
/// Peso de `register_agent_did` — 2-3 reads + verify Ed25519 + derive_did + 2 writes.
|
||||
fn register_agent_did() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights parametrizados por el `DbWeight` del runtime.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
fn register_agent_did() -> Weight {
|
||||
// Compute: Ed25519 verify (dominante) + derive_did (BLAKE3) + decode +
|
||||
// build BoundedVec agent_chain. Storage: contains_key(Identities) +
|
||||
// contains_key(DidToAccount) + block_number = 3 reads; insert(Identities)
|
||||
// + insert(DidToAccount) = 2 writes.
|
||||
Weight::from_parts(50_000_000, 0)
|
||||
.saturating_add(T::DbWeight::get().reads(3_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(2_u64))
|
||||
}
|
||||
}
|
||||
|
||||
/// Impl no-op para mocks / tests.
|
||||
impl WeightInfo for () {
|
||||
fn register_agent_did() -> Weight {
|
||||
Weight::from_parts(50_000_000, 0)
|
||||
.saturating_add(RocksDbWeight::get().reads(3_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(2_u64))
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,9 @@ sp-runtime = { workspace = true }
|
||||
parity-scale-codec = { workspace = true }
|
||||
scale-info = { workspace = true }
|
||||
|
||||
# Benchmarking canonical (FRAME v2) — optional, solo activo bajo runtime-benchmarks.
|
||||
frame-benchmarking = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# Mock runtime canonical — pallet_balances para testear el minteo real.
|
||||
pallet-balances = { workspace = true, default-features = true }
|
||||
@@ -36,8 +39,11 @@ std = [
|
||||
"sp-runtime/std",
|
||||
"parity-scale-codec/std",
|
||||
"scale-info/std",
|
||||
"frame-benchmarking?/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"dep:frame-benchmarking",
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
//! # Benchmarks para `pallet-origin-emission` (FRAME v2)
|
||||
//!
|
||||
//! Fuente de verdad de la forma del weight de `set_emission_paused` (kill-switch
|
||||
//! de governance). Verificado por `impl_benchmark_test_suite!` en el mock
|
||||
//! (`cargo test -p origin-chain-emission --features runtime-benchmarks`). Números
|
||||
//! canónicos gated por HW/omni-bencher (ver `weights.rs` §Estado).
|
||||
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
|
||||
use super::*;
|
||||
use frame_benchmarking::v2::*;
|
||||
use frame_system::RawOrigin;
|
||||
|
||||
#[benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn set_emission_paused() {
|
||||
// `EmissionPaused` arranca en `false` (default); pausar (`true`) es un
|
||||
// cambio real → pasa el guard anti-no-op del extrinsic.
|
||||
#[extrinsic_call]
|
||||
set_emission_paused(RawOrigin::Root, true);
|
||||
|
||||
// Post-estado: la emisión quedó pausada.
|
||||
assert!(EmissionPaused::<T>::get());
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
}
|
||||
@@ -51,6 +51,18 @@
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
/// Weights canonical (INTERIM hand-derived DB-aware — ver `weights.rs` §Estado).
|
||||
pub mod weights;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
/// Benchmarks canonical (FRAME v2) — solo bajo `runtime-benchmarks`.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
|
||||
/// Mock runtime canonical (cfg test) — extraído para reuso del benchmark test-suite.
|
||||
#[cfg(test)]
|
||||
pub mod mock;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[frame_support::pallet]
|
||||
@@ -62,6 +74,8 @@ pub mod pallet {
|
||||
use frame_support::PalletId;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use sp_runtime::traits::AccountIdConversion;
|
||||
// Trait de weights en scope para resolver `T::WeightInfo::<method>()`.
|
||||
use crate::weights::WeightInfo;
|
||||
|
||||
/// StorageVersion en código (FIX-2 lifecycle): arranca en 0 (genesis).
|
||||
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
|
||||
@@ -109,6 +123,9 @@ pub mod pallet {
|
||||
/// PalletId del subsidy pool (proof-of-equity, Inmutable #16 anti-clasista).
|
||||
#[pallet::constant]
|
||||
type SubsidyPalletId: Get<PalletId>;
|
||||
|
||||
/// Weight information canonical para los extrinsics de este pallet.
|
||||
type WeightInfo: crate::weights::WeightInfo;
|
||||
}
|
||||
|
||||
/// Pallet definition.
|
||||
@@ -291,7 +308,7 @@ pub mod pallet {
|
||||
/// todo cambio de governance quede como evento observable on-chain.
|
||||
/// `TotalEmitted` se preserva — pausar NO reescribe el histórico.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
|
||||
#[pallet::weight(T::WeightInfo::set_emission_paused())]
|
||||
pub fn set_emission_paused(origin: OriginFor<T>, paused: bool) -> DispatchResult {
|
||||
frame_system::ensure_root(origin)?;
|
||||
ensure!(
|
||||
@@ -326,100 +343,14 @@ pub mod pallet {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate as origin_chain_emission;
|
||||
// Mock extraído a `crate::mock` (ver mock.rs) — reusado por el benchmark
|
||||
// test-suite. El glob trae Test/System/Balances/Emission/RuntimeOrigin +
|
||||
// helpers (new_test_ext, author, ecosystem_account, …).
|
||||
use crate::mock::*;
|
||||
use frame_support::traits::fungible::Inspect;
|
||||
use frame_support::traits::{FindAuthor, Hooks};
|
||||
use frame_support::{
|
||||
assert_noop, assert_ok, construct_runtime, derive_impl, parameter_types, PalletId,
|
||||
};
|
||||
use sp_runtime::traits::AccountIdConversion;
|
||||
use sp_runtime::{AccountId32, BuildStorage, DispatchError};
|
||||
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
construct_runtime!(
|
||||
pub enum Test {
|
||||
System: frame_system,
|
||||
Balances: pallet_balances,
|
||||
Emission: origin_chain_emission,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||
impl frame_system::Config for Test {
|
||||
type Block = Block;
|
||||
type AccountId = AccountId32;
|
||||
type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
|
||||
type AccountData = pallet_balances::AccountData<u128>;
|
||||
}
|
||||
|
||||
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_balances::Config for Test {
|
||||
type AccountStore = System;
|
||||
type Balance = u128;
|
||||
}
|
||||
|
||||
/// FindAuthor de prueba: devuelve siempre ALICE como autor del bloque.
|
||||
pub struct MockFindAuthor;
|
||||
impl FindAuthor<AccountId32> for MockFindAuthor {
|
||||
fn find_author<'a, I>(_digests: I) -> Option<AccountId32>
|
||||
where
|
||||
I: 'a + IntoIterator<Item = (sp_runtime::ConsensusEngineId, &'a [u8])>,
|
||||
{
|
||||
Some(AccountId32::new([1u8; 32]))
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockRewardCfg: u128 = 1_000;
|
||||
pub const AuthorShareBpsCfg: u16 = 7_000; // 70%
|
||||
pub const TreasuryShareBpsCfg: u16 = 2_000; // 20% (subsidy = 10% residual)
|
||||
pub const EcosystemPid: PalletId = PalletId(*b"og/ecosy");
|
||||
pub const TreasuryPid: PalletId = PalletId(*b"og/gvtry");
|
||||
pub const SubsidyPid: PalletId = PalletId(*b"og/subsy");
|
||||
}
|
||||
|
||||
impl crate::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Currency = Balances;
|
||||
type FindAuthor = MockFindAuthor;
|
||||
type BlockReward = BlockRewardCfg;
|
||||
type AuthorShareBps = AuthorShareBpsCfg;
|
||||
type TreasuryShareBps = TreasuryShareBpsCfg;
|
||||
type EcosystemPalletId = EcosystemPid;
|
||||
type TreasuryPalletId = TreasuryPid;
|
||||
type SubsidyPalletId = SubsidyPid;
|
||||
}
|
||||
|
||||
fn ecosystem_account() -> AccountId32 {
|
||||
EcosystemPid::get().into_account_truncating()
|
||||
}
|
||||
|
||||
/// Fondea el pool Ecosystem con `pool_balance` en génesis (la fuente finita
|
||||
/// de la que se libera; sin esto, transfer no mueve nada — techo duro).
|
||||
fn new_test_ext_with_pool(pool_balance: u128) -> sp_io::TestExternalities {
|
||||
let mut storage = frame_system::GenesisConfig::<Test>::default()
|
||||
.build_storage()
|
||||
.expect("genesis builds");
|
||||
pallet_balances::GenesisConfig::<Test> {
|
||||
balances: vec![(ecosystem_account(), pool_balance)],
|
||||
..Default::default()
|
||||
}
|
||||
.assimilate_storage(&mut storage)
|
||||
.expect("balances genesis");
|
||||
let mut ext: sp_io::TestExternalities = storage.into();
|
||||
ext.execute_with(|| System::set_block_number(1));
|
||||
ext
|
||||
}
|
||||
|
||||
/// Default: pool amplio (1M) — cubre miles de bloques sin agotarse.
|
||||
fn new_test_ext() -> sp_io::TestExternalities {
|
||||
new_test_ext_with_pool(1_000_000)
|
||||
}
|
||||
|
||||
fn author() -> AccountId32 {
|
||||
AccountId32::new([1u8; 32])
|
||||
}
|
||||
use frame_support::traits::Hooks;
|
||||
use frame_support::{assert_noop, assert_ok};
|
||||
use sp_runtime::{AccountId32, DispatchError};
|
||||
|
||||
#[test]
|
||||
fn block_reward_released_and_split_correctly() {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
//! # Mock runtime para tests + benchmark test-suite de `pallet-origin-emission`
|
||||
//!
|
||||
//! Extraído del inline `mod tests` (v0.0.x) a `pub mod mock` para que
|
||||
//! `impl_benchmark_test_suite!` (benchmarking.rs) pueda reusar `Test` +
|
||||
//! `new_test_ext` — mismo patrón que provenance/fees/agent-identity. Los tests de
|
||||
//! `lib.rs` importan de aquí (`use crate::mock::*`).
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use crate as origin_chain_emission;
|
||||
use frame_support::traits::FindAuthor;
|
||||
use frame_support::{construct_runtime, derive_impl, parameter_types, PalletId};
|
||||
use sp_runtime::traits::AccountIdConversion;
|
||||
use sp_runtime::{AccountId32, BuildStorage};
|
||||
|
||||
/// Mock block type.
|
||||
pub type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
construct_runtime!(
|
||||
pub enum Test {
|
||||
System: frame_system,
|
||||
Balances: pallet_balances,
|
||||
Emission: origin_chain_emission,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||
impl frame_system::Config for Test {
|
||||
type Block = Block;
|
||||
type AccountId = AccountId32;
|
||||
type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
|
||||
type AccountData = pallet_balances::AccountData<u128>;
|
||||
}
|
||||
|
||||
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_balances::Config for Test {
|
||||
type AccountStore = System;
|
||||
type Balance = u128;
|
||||
}
|
||||
|
||||
/// FindAuthor de prueba: devuelve siempre ALICE como autor del bloque.
|
||||
pub struct MockFindAuthor;
|
||||
impl FindAuthor<AccountId32> for MockFindAuthor {
|
||||
fn find_author<'a, I>(_digests: I) -> Option<AccountId32>
|
||||
where
|
||||
I: 'a + IntoIterator<Item = (sp_runtime::ConsensusEngineId, &'a [u8])>,
|
||||
{
|
||||
Some(AccountId32::new([1u8; 32]))
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockRewardCfg: u128 = 1_000;
|
||||
pub const AuthorShareBpsCfg: u16 = 7_000; // 70%
|
||||
pub const TreasuryShareBpsCfg: u16 = 2_000; // 20% (subsidy = 10% residual)
|
||||
pub const EcosystemPid: PalletId = PalletId(*b"og/ecosy");
|
||||
pub const TreasuryPid: PalletId = PalletId(*b"og/gvtry");
|
||||
pub const SubsidyPid: PalletId = PalletId(*b"og/subsy");
|
||||
}
|
||||
|
||||
impl crate::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Currency = Balances;
|
||||
type FindAuthor = MockFindAuthor;
|
||||
type BlockReward = BlockRewardCfg;
|
||||
type AuthorShareBps = AuthorShareBpsCfg;
|
||||
type TreasuryShareBps = TreasuryShareBpsCfg;
|
||||
type EcosystemPalletId = EcosystemPid;
|
||||
type TreasuryPalletId = TreasuryPid;
|
||||
type SubsidyPalletId = SubsidyPid;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
/// Cuenta del pool Ecosystem (fuente finita de la emisión).
|
||||
pub fn ecosystem_account() -> AccountId32 {
|
||||
EcosystemPid::get().into_account_truncating()
|
||||
}
|
||||
|
||||
/// Fondea el pool Ecosystem con `pool_balance` en génesis (la fuente finita
|
||||
/// de la que se libera; sin esto, transfer no mueve nada — techo duro).
|
||||
pub fn new_test_ext_with_pool(pool_balance: u128) -> sp_io::TestExternalities {
|
||||
let mut storage = frame_system::GenesisConfig::<Test>::default()
|
||||
.build_storage()
|
||||
.expect("genesis builds");
|
||||
pallet_balances::GenesisConfig::<Test> {
|
||||
balances: vec![(ecosystem_account(), pool_balance)],
|
||||
..Default::default()
|
||||
}
|
||||
.assimilate_storage(&mut storage)
|
||||
.expect("balances genesis");
|
||||
let mut ext: sp_io::TestExternalities = storage.into();
|
||||
ext.execute_with(|| System::set_block_number(1));
|
||||
ext
|
||||
}
|
||||
|
||||
/// Default: pool amplio (1M) — cubre miles de bloques sin agotarse.
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
new_test_ext_with_pool(1_000_000)
|
||||
}
|
||||
|
||||
/// Autor del bloque (ALICE) usado por los tests de split.
|
||||
pub fn author() -> AccountId32 {
|
||||
AccountId32::new([1u8; 32])
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//! # Weights para `pallet-origin-emission`
|
||||
//!
|
||||
//! ## Estado: INTERIM hand-derived (DB-aware) — pendiente regeneración HW
|
||||
//!
|
||||
//! Ver `origin-chain-provenance/src/weights.rs` §Estado. Nota: `set_emission_paused`
|
||||
//! ya era DB-aware antes de esta migración (usaba `T::DbWeight::get().reads_writes(1,1)`
|
||||
//! directo — el ÚNICO extrinsic del chain que nunca fue DoS-prone). Aquí se mueve
|
||||
//! ese mismo cálculo detrás del trait `WeightInfo` por consistencia con los demás
|
||||
//! pallets + para exponerlo a `benchmark pallet`. `benchmarking.rs` es la fuente
|
||||
//! de verdad de la forma.
|
||||
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use frame_support::{traits::Get, weights::constants::RocksDbWeight, weights::Weight};
|
||||
|
||||
/// Trait de weights para los extrinsics de `pallet-origin-emission`.
|
||||
pub trait WeightInfo {
|
||||
/// Peso de `set_emission_paused` (Root) — 1 read (guard no-op) + 1 write.
|
||||
fn set_emission_paused() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights parametrizados por el `DbWeight` del runtime.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
fn set_emission_paused() -> Weight {
|
||||
// ensure_root + get(EmissionPaused) [guard no-op] + put(EmissionPaused).
|
||||
T::DbWeight::get().reads_writes(1, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Impl no-op para mocks / tests.
|
||||
impl WeightInfo for () {
|
||||
fn set_emission_paused() -> Weight {
|
||||
RocksDbWeight::get().reads_writes(1, 1)
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,9 @@ scale-info = { workspace = true }
|
||||
pallet-transaction-payment = { workspace = true }
|
||||
sp-runtime = { workspace = true }
|
||||
|
||||
# Benchmarking canonical (FRAME v2) — optional, solo activo bajo runtime-benchmarks.
|
||||
frame-benchmarking = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# Mock runtime canonical para integration tests v0.0.22 — Pattern v0.0.18 mirror.
|
||||
# FIX-6 Stage 2a: + pallet-balances para los tests del OriginFeeAdapter
|
||||
@@ -48,8 +51,11 @@ std = [
|
||||
"scale-info/std",
|
||||
"pallet-transaction-payment/std",
|
||||
"sp-runtime/std",
|
||||
"frame-benchmarking?/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"dep:frame-benchmarking",
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"pallet-transaction-payment/runtime-benchmarks",
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
//! # Benchmarks para `pallet-origin-fees` (FRAME v2)
|
||||
//!
|
||||
//! Fuente de verdad de la forma del weight de `set_tier_profile`. Verificado por
|
||||
//! `impl_benchmark_test_suite!` en el mock (`cargo test -p origin-chain-fees
|
||||
//! --features runtime-benchmarks`). Números canónicos gated por HW/omni-bencher
|
||||
//! (ver `weights.rs` §Estado).
|
||||
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
|
||||
use super::*;
|
||||
use frame_benchmarking::v2::*;
|
||||
use frame_system::RawOrigin;
|
||||
|
||||
#[benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn set_tier_profile() {
|
||||
// Root asigna el TierProfile de una cuenta (insumo del fee unificado #16).
|
||||
let who: T::AccountId = whitelisted_caller();
|
||||
|
||||
#[extrinsic_call]
|
||||
set_tier_profile(
|
||||
RawOrigin::Root,
|
||||
who.clone(),
|
||||
HardwareTier::Desktop,
|
||||
SubscriptionTier::Free,
|
||||
);
|
||||
|
||||
// Post-estado: el perfil quedó explícitamente asentado para la cuenta.
|
||||
assert!(TierProfiles::<T>::contains_key(&who));
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
}
|
||||
@@ -47,6 +47,14 @@
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
/// Weights canonical (INTERIM hand-derived DB-aware — ver `weights.rs` §Estado).
|
||||
pub mod weights;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
/// Benchmarks canonical (FRAME v2) — solo bajo `runtime-benchmarks`.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
|
||||
/// Vendor copy temporal Mathi `OitFeeSchedule` (cross-workspace boundary).
|
||||
pub mod fee_schedule;
|
||||
|
||||
@@ -121,6 +129,8 @@ pub mod pallet {
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use origin_chain_types::{ChainAction, HardwareTier, SubscriptionTier};
|
||||
// Trait de weights en scope para resolver `T::WeightInfo::<method>()`.
|
||||
use crate::weights::WeightInfo;
|
||||
|
||||
/// Pallet configuration trait canonical.
|
||||
///
|
||||
@@ -140,6 +150,9 @@ pub mod pallet {
|
||||
/// misma cuenta determinística.
|
||||
#[pallet::constant]
|
||||
type SubsidyPalletId: Get<frame_support::PalletId>;
|
||||
|
||||
/// Weight information canonical para los extrinsics de este pallet.
|
||||
type WeightInfo: crate::weights::WeightInfo;
|
||||
}
|
||||
|
||||
/// Pallet definition canonical.
|
||||
@@ -329,7 +342,7 @@ pub mod pallet {
|
||||
/// FIX-3/FIX-4), este dispatchable migra a verificación de attestation
|
||||
/// firmada en vez de Root fiat.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::set_tier_profile())]
|
||||
pub fn set_tier_profile(
|
||||
origin: OriginFor<T>,
|
||||
who: T::AccountId,
|
||||
|
||||
@@ -87,6 +87,7 @@ parameter_types! {
|
||||
impl origin_chain_fees::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type SubsidyPalletId = MockSubsidyPalletId;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
/// Test externalities builder canonical (pattern v0.0.18 mirror).
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
//! # Weights para `pallet-origin-fees`
|
||||
//!
|
||||
//! ## Estado: INTERIM hand-derived (DB-aware, DoS-safe) — pendiente regeneración HW
|
||||
//!
|
||||
//! Ver `origin-chain-provenance/src/weights.rs` §Estado para el racional completo.
|
||||
//! Resumen: números conservadores derivados a mano (no autogenerados por CLI —
|
||||
//! gated por sc-service/omni-bencher+WASM), pero **DB-aware** vía `T::DbWeight`
|
||||
//! → pueblan ref_time + proof_size por read/write real, cerrando el DoS de los
|
||||
//! placeholders `Weight::from_parts(10_000, 0)` (proof_size = 0, sin contar DB).
|
||||
//! `benchmarking.rs` es la fuente de verdad de la forma.
|
||||
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use frame_support::{traits::Get, weights::constants::RocksDbWeight, weights::Weight};
|
||||
|
||||
/// Trait de weights para los extrinsics de `pallet-origin-fees`.
|
||||
pub trait WeightInfo {
|
||||
/// Peso de `set_tier_profile` (Root) — 1 write (TierProfiles).
|
||||
fn set_tier_profile() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights parametrizados por el `DbWeight` del runtime.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
fn set_tier_profile() -> Weight {
|
||||
// ensure_root (in-memory) + insert(TierProfiles) = 1 write.
|
||||
Weight::from_parts(8_000_000, 0).saturating_add(T::DbWeight::get().writes(1_u64))
|
||||
}
|
||||
}
|
||||
|
||||
/// Impl no-op para mocks / tests.
|
||||
impl WeightInfo for () {
|
||||
fn set_tier_profile() -> Weight {
|
||||
Weight::from_parts(8_000_000, 0).saturating_add(RocksDbWeight::get().writes(1_u64))
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,9 @@ sp-runtime = { workspace = true }
|
||||
parity-scale-codec = { workspace = true }
|
||||
scale-info = { workspace = true }
|
||||
|
||||
# Benchmarking canonical (FRAME v2) — optional, solo activo bajo runtime-benchmarks.
|
||||
frame-benchmarking = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# Mock runtime canonical para integration tests v0.0.30 — Pattern v0.0.26 mirror.
|
||||
sp-core = { workspace = true, default-features = true }
|
||||
@@ -47,8 +50,11 @@ std = [
|
||||
"sp-runtime/std",
|
||||
"parity-scale-codec/std",
|
||||
"scale-info/std",
|
||||
"frame-benchmarking?/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"dep:frame-benchmarking",
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
//! # Benchmarks para `pallet-origin-governance` (FRAME v2)
|
||||
//!
|
||||
//! Fuente de verdad de la forma del weight de los 13 extrinsics. Verificado por
|
||||
//! `impl_benchmark_test_suite!` en el mock (`cargo test -p origin-chain-governance
|
||||
//! --features runtime-benchmarks`). Números canónicos gated por HW/omni-bencher
|
||||
//! (ver `weights.rs` §Estado).
|
||||
//!
|
||||
//! Los preconditions (proposal en un estado, membresía de council, endosos) se
|
||||
//! siembran por storage directo en el setup — patrón estándar de benchmarking.
|
||||
//! Los thresholds con council_size=1 son 1 (veto y emergency), así que un solo
|
||||
//! miembro/endoso alcanza el umbral.
|
||||
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
|
||||
use super::*;
|
||||
use frame_benchmarking::v2::*;
|
||||
use frame_support::traits::fungible::{Inspect, Mutate};
|
||||
use frame_system::RawOrigin;
|
||||
use origin_chain_types::AccountId;
|
||||
|
||||
/// Fondea `who` con holgura para bonds + treasury deposits.
|
||||
fn fund<T: Config>(who: &T::AccountId) {
|
||||
let _ = T::Currency::mint_into(who, MIN_PROPOSAL_STAKE.saturating_mul(100));
|
||||
}
|
||||
|
||||
/// Siembra una `Proposal` en el estado/end_block dados (bypass del extrinsic).
|
||||
fn seed_proposal<T: Config>(id: ProposalId, status: ProposalStatus, end_block: u64) {
|
||||
let proposal = Proposal {
|
||||
id,
|
||||
proposer: AccountId::new([9u8; 32]),
|
||||
title_hash: [1u8; 32],
|
||||
body_hash: [2u8; 32],
|
||||
stake: MIN_PROPOSAL_STAKE,
|
||||
start_block: 0,
|
||||
end_block,
|
||||
status,
|
||||
yes_weight: 0,
|
||||
nay_weight: 0,
|
||||
abstain_weight: 0,
|
||||
};
|
||||
Proposals::<T>::insert(id, proposal);
|
||||
}
|
||||
|
||||
/// Añade `who` al council (size=1) por storage directo.
|
||||
fn seed_council<T: Config>(who: &T::AccountId) {
|
||||
let member = CouncilMember {
|
||||
account: AccountId::new([0u8; 32]),
|
||||
joined_at_block: 0,
|
||||
role: CouncilRole::Member,
|
||||
};
|
||||
Council::<T>::insert(who, member);
|
||||
CouncilSize::<T>::put(1u32);
|
||||
}
|
||||
|
||||
const EM_TITLE: [u8; 32] = [3u8; 32];
|
||||
const EM_BODY: [u8; 32] = [4u8; 32];
|
||||
|
||||
#[benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn submit_proposal() {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
fund::<T>(&caller);
|
||||
|
||||
#[extrinsic_call]
|
||||
submit_proposal(RawOrigin::Signed(caller), [1u8; 32], [2u8; 32], 1_000u64, MIN_PROPOSAL_STAKE);
|
||||
|
||||
assert_eq!(Proposals::<T>::iter().count(), 1);
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn cast_vote() {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
let id = ProposalId([7u8; 32]);
|
||||
seed_proposal::<T>(id, ProposalStatus::Voting, 1_000_000u64);
|
||||
|
||||
#[extrinsic_call]
|
||||
cast_vote(RawOrigin::Signed(caller.clone()), id, VoteStance::Aye, 100u128);
|
||||
|
||||
assert!(Votes::<T>::contains_key(id, &caller));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn finalize_proposal() {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
let id = ProposalId([7u8; 32]);
|
||||
seed_proposal::<T>(id, ProposalStatus::Voting, 0u64);
|
||||
// current_block debe ser > end_block(0) → avanza a 2.
|
||||
frame_system::Pallet::<T>::set_block_number(2u32.into());
|
||||
|
||||
#[extrinsic_call]
|
||||
finalize_proposal(RawOrigin::Signed(caller), id);
|
||||
|
||||
assert!(!matches!(
|
||||
Proposals::<T>::get(id).unwrap().status,
|
||||
ProposalStatus::Voting
|
||||
));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn add_council_member() {
|
||||
let acct: T::AccountId = account("member", 0, 0);
|
||||
|
||||
#[extrinsic_call]
|
||||
add_council_member(RawOrigin::Root, acct.clone(), CouncilRole::Member);
|
||||
|
||||
assert!(Council::<T>::contains_key(&acct));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn remove_council_member() {
|
||||
let acct: T::AccountId = account("member", 0, 0);
|
||||
seed_council::<T>(&acct);
|
||||
|
||||
#[extrinsic_call]
|
||||
remove_council_member(RawOrigin::Root, acct.clone());
|
||||
|
||||
assert!(!Council::<T>::contains_key(&acct));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn veto_proposal() {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
seed_council::<T>(&caller);
|
||||
let id = ProposalId([7u8; 32]);
|
||||
seed_proposal::<T>(id, ProposalStatus::Voting, 1_000_000u64);
|
||||
|
||||
#[extrinsic_call]
|
||||
veto_proposal(RawOrigin::Signed(caller.clone()), id);
|
||||
|
||||
assert!(VetoVotes::<T>::contains_key(id, &caller));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn mark_as_advisor() {
|
||||
let acct: T::AccountId = account("advisor", 0, 0);
|
||||
|
||||
#[extrinsic_call]
|
||||
mark_as_advisor(RawOrigin::Root, acct.clone());
|
||||
|
||||
assert!(AdvisorFlags::<T>::contains_key(&acct));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn unmark_as_advisor() {
|
||||
let acct: T::AccountId = account("advisor", 0, 0);
|
||||
AdvisorFlags::<T>::insert(&acct, ());
|
||||
|
||||
#[extrinsic_call]
|
||||
unmark_as_advisor(RawOrigin::Root, acct.clone());
|
||||
|
||||
assert!(!AdvisorFlags::<T>::contains_key(&acct));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn override_council_veto() {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
let id = ProposalId([7u8; 32]);
|
||||
seed_proposal::<T>(id, ProposalStatus::Rejected, 0u64);
|
||||
VetoRejected::<T>::insert(id, ());
|
||||
|
||||
#[extrinsic_call]
|
||||
override_council_veto(RawOrigin::Signed(caller.clone()), id, 100u128);
|
||||
|
||||
assert!(OverrideVotes::<T>::contains_key(id, &caller));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn submit_emergency_proposal() {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
fund::<T>(&caller);
|
||||
seed_council::<T>(&caller);
|
||||
// council_size=1 ⇒ emergency threshold=1; un endoso VIVO del caller basta.
|
||||
let content_id = crate::emergency_content_id(&EM_TITLE, &EM_BODY);
|
||||
EmergencyEndorsements::<T>::insert(content_id, &caller, ());
|
||||
|
||||
#[extrinsic_call]
|
||||
submit_emergency_proposal(RawOrigin::Signed(caller), EM_TITLE, EM_BODY, MIN_PROPOSAL_STAKE);
|
||||
|
||||
assert!(Proposals::<T>::iter().count() >= 1);
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn treasury_disbursement() {
|
||||
let id = ProposalId([7u8; 32]);
|
||||
seed_proposal::<T>(id, ProposalStatus::Approved, 0u64);
|
||||
let recipient: T::AccountId = account("recipient", 0, 0);
|
||||
// Fondea la tesorería para que el transfer real tenga de dónde salir.
|
||||
let _ = T::Currency::mint_into(
|
||||
&Pallet::<T>::treasury_account(),
|
||||
MIN_PROPOSAL_STAKE.saturating_mul(10),
|
||||
);
|
||||
|
||||
#[extrinsic_call]
|
||||
treasury_disbursement(RawOrigin::Root, id, recipient, MIN_PROPOSAL_STAKE);
|
||||
|
||||
assert!(Disbursed::<T>::contains_key(id));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn treasury_deposit() {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
fund::<T>(&caller);
|
||||
|
||||
#[extrinsic_call]
|
||||
treasury_deposit(RawOrigin::Signed(caller), MIN_PROPOSAL_STAKE);
|
||||
|
||||
assert!(T::Currency::balance(&Pallet::<T>::treasury_account()) >= MIN_PROPOSAL_STAKE);
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn endorse_emergency_proposal() {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
seed_council::<T>(&caller);
|
||||
|
||||
#[extrinsic_call]
|
||||
endorse_emergency_proposal(RawOrigin::Signed(caller.clone()), EM_TITLE, EM_BODY);
|
||||
|
||||
let content_id = crate::emergency_content_id(&EM_TITLE, &EM_BODY);
|
||||
assert!(EmergencyEndorsements::<T>::contains_key(content_id, &caller));
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
}
|
||||
@@ -40,6 +40,14 @@
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
/// Weights canonical (INTERIM hand-derived DB-aware — ver `weights.rs` §Estado).
|
||||
pub mod weights;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
/// Benchmarks canonical (FRAME v2) — solo bajo `runtime-benchmarks`.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
|
||||
/// Re-export canonical types desde origin-chain-types para consumers.
|
||||
pub use origin_chain_types::{ProposalId, Vote, VoteStance};
|
||||
|
||||
@@ -124,6 +132,8 @@ pub mod pallet {
|
||||
use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
use sp_runtime::traits::{AccountIdConversion, SaturatedConversion};
|
||||
// Trait de weights en scope para resolver `T::WeightInfo::<method>()`.
|
||||
use crate::weights::WeightInfo;
|
||||
|
||||
/// ProposalStatus canonical — proposal lifecycle stages canonical.
|
||||
#[derive(
|
||||
@@ -246,6 +256,9 @@ pub mod pallet {
|
||||
/// tesorería no depende de una llave humana custodiable.
|
||||
#[pallet::constant]
|
||||
type TreasuryPalletId: Get<PalletId>;
|
||||
|
||||
/// Weight information canonical para los extrinsics de este pallet.
|
||||
type WeightInfo: crate::weights::WeightInfo;
|
||||
}
|
||||
|
||||
/// Pallet definition canonical.
|
||||
@@ -623,7 +636,7 @@ pub mod pallet {
|
||||
/// 7-step canonical flow: ensure_signed + validate_stake + derive_id +
|
||||
/// collision_check + build + insert + emit.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::submit_proposal())]
|
||||
pub fn submit_proposal(
|
||||
origin: OriginFor<T>,
|
||||
title_hash: [u8; 32],
|
||||
@@ -683,7 +696,7 @@ pub mod pallet {
|
||||
|
||||
/// CHN-4 5.4 canonical: `cast_vote` real dispatchable canonical.
|
||||
#[pallet::call_index(1)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::cast_vote())]
|
||||
pub fn cast_vote(
|
||||
origin: OriginFor<T>,
|
||||
proposal_id: ProposalId,
|
||||
@@ -746,7 +759,7 @@ pub mod pallet {
|
||||
|
||||
/// CHN-4 canonical: `finalize_proposal` real dispatchable canonical.
|
||||
#[pallet::call_index(2)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::finalize_proposal())]
|
||||
pub fn finalize_proposal(
|
||||
origin: OriginFor<T>,
|
||||
proposal_id: ProposalId,
|
||||
@@ -799,7 +812,7 @@ pub mod pallet {
|
||||
/// 6-step canonical flow: ensure_root + size_check + no_duplicate + build +
|
||||
/// insert + emit.
|
||||
#[pallet::call_index(3)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::add_council_member())]
|
||||
pub fn add_council_member(
|
||||
origin: OriginFor<T>,
|
||||
account: T::AccountId,
|
||||
@@ -843,7 +856,7 @@ pub mod pallet {
|
||||
///
|
||||
/// 5-step canonical flow: ensure_root + validate_exists + remove + decrement + emit.
|
||||
#[pallet::call_index(4)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::remove_council_member())]
|
||||
pub fn remove_council_member(
|
||||
origin: OriginFor<T>,
|
||||
account: T::AccountId,
|
||||
@@ -880,7 +893,7 @@ pub mod pallet {
|
||||
/// 7. Check threshold canonical: if count >= required → flip status to Rejected
|
||||
/// + emit ProposalVetoed canonical; else emit CouncilVetoVoteCast canonical
|
||||
#[pallet::call_index(5)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::veto_proposal())]
|
||||
pub fn veto_proposal(
|
||||
origin: OriginFor<T>,
|
||||
proposal_id: ProposalId,
|
||||
@@ -964,7 +977,7 @@ pub mod pallet {
|
||||
/// Sentinel). Advisor canonical NO puede votar — humans-only vote canonical
|
||||
/// non-negotiable per CHN-4 + Inmutable #41.
|
||||
#[pallet::call_index(6)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::mark_as_advisor())]
|
||||
pub fn mark_as_advisor(
|
||||
origin: OriginFor<T>,
|
||||
account: T::AccountId,
|
||||
@@ -981,7 +994,7 @@ pub mod pallet {
|
||||
|
||||
/// CHN-4 5.6 v0.0.34 canonical: `unmark_as_advisor` Phase 5.6 dispatchable.
|
||||
#[pallet::call_index(7)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::unmark_as_advisor())]
|
||||
pub fn unmark_as_advisor(
|
||||
origin: OriginFor<T>,
|
||||
account: T::AccountId,
|
||||
@@ -1012,7 +1025,7 @@ pub mod pallet {
|
||||
/// 6. Insert vote + increment total stake
|
||||
/// 7. Check threshold canonical 4/5: if >= → flip to Approved + emit CouncilVetoOverridden
|
||||
#[pallet::call_index(8)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::override_council_veto())]
|
||||
pub fn override_council_veto(
|
||||
origin: OriginFor<T>,
|
||||
proposal_id: ProposalId,
|
||||
@@ -1084,7 +1097,7 @@ pub mod pallet {
|
||||
/// canonical 5/7 council pre-approval flow (gather signatures via separate
|
||||
/// dispatchable y luego promote canonical).
|
||||
#[pallet::call_index(9)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::submit_emergency_proposal())]
|
||||
pub fn submit_emergency_proposal(
|
||||
origin: OriginFor<T>,
|
||||
title_hash: [u8; 32],
|
||||
@@ -1179,7 +1192,7 @@ pub mod pallet {
|
||||
/// 5. (Phase 5.9+ refinement canonical: transfer via pallet_balances integration)
|
||||
/// 6. Emit TreasuryDisbursed canonical
|
||||
#[pallet::call_index(10)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::treasury_disbursement())]
|
||||
pub fn treasury_disbursement(
|
||||
origin: OriginFor<T>,
|
||||
proposal_id: ProposalId,
|
||||
@@ -1233,7 +1246,7 @@ pub mod pallet {
|
||||
/// canonical (BM V2 250M OGC Foundation allocation). Real Phase 5.9+ refinement
|
||||
/// canonical: replace canonical via Genesis storage canonical config.
|
||||
#[pallet::call_index(11)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::treasury_deposit())]
|
||||
pub fn treasury_deposit(origin: OriginFor<T>, amount: u128) -> DispatchResult {
|
||||
// FIX-6: donación REAL a la tesorería — el caller transfiere `amount`
|
||||
// de su propio balance a la cuenta-tesorería. Cualquiera puede fondear
|
||||
@@ -1251,7 +1264,7 @@ pub mod pallet {
|
||||
/// reemplaza el gate 5/7 TAUTOLÓGICO anterior (que dejaba a UN solo miembro
|
||||
/// fast-trackear a 48h).
|
||||
#[pallet::call_index(12)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::endorse_emergency_proposal())]
|
||||
pub fn endorse_emergency_proposal(
|
||||
origin: OriginFor<T>,
|
||||
title_hash: [u8; 32],
|
||||
|
||||
@@ -43,6 +43,7 @@ impl origin_chain_governance::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Currency = Balances;
|
||||
type TreasuryPalletId = TestTreasuryPalletId;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
/// Balance inicial de cada cuenta de test. Audit 2026-07-04: debe ser
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
//! # Weights para `pallet-origin-governance`
|
||||
//!
|
||||
//! ## Estado: INTERIM hand-derived (DB-aware, DoS-safe) — pendiente regeneración HW
|
||||
//!
|
||||
//! Ver `origin-chain-provenance/src/weights.rs` §Estado para el racional completo.
|
||||
//! Los 13 extrinsics tenían `Weight::from_parts(10_000, 0)` (proof_size=0, sin
|
||||
//! contar DB) → DoS. Aquí cada uno se cuesta con `T::DbWeight` sobre los reads/writes
|
||||
//! REALES contados del código (incluyendo los `iter_prefix`/`clear_prefix` acotados
|
||||
//! por el council ≤ 7). `benchmarking.rs` es la fuente de verdad de la forma.
|
||||
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use frame_support::{traits::Get, weights::constants::RocksDbWeight, weights::Weight};
|
||||
|
||||
/// Compute floor conservador para la lógica de cada extrinsic (hashing blake2,
|
||||
/// aritmética de umbrales, build de structs). Los que hashean content/proposal id
|
||||
/// (submit_*, endorse) llevan un poco más vía el propio conteo de reads.
|
||||
const COMPUTE: u64 = 12_000_000;
|
||||
|
||||
/// Trait de weights para los 13 extrinsics de `pallet-origin-governance`.
|
||||
pub trait WeightInfo {
|
||||
/// `submit_proposal` — contains_key + bond transfer + 2 inserts.
|
||||
fn submit_proposal() -> Weight;
|
||||
/// `cast_vote` — advisor/proposal/vote reads + 2 inserts.
|
||||
fn cast_vote() -> Weight;
|
||||
/// `finalize_proposal` — proposal read/write + release bond.
|
||||
fn finalize_proposal() -> Weight;
|
||||
/// `add_council_member` — size/dup reads + 2 writes.
|
||||
fn add_council_member() -> Weight;
|
||||
/// `remove_council_member` — exists read + remove + size write.
|
||||
fn remove_council_member() -> Weight;
|
||||
/// `veto_proposal` — council iter (≤7) + threshold flip path.
|
||||
fn veto_proposal() -> Weight;
|
||||
/// `mark_as_advisor` — 1 read + 1 write.
|
||||
fn mark_as_advisor() -> Weight;
|
||||
/// `unmark_as_advisor` — 1 read + 1 write.
|
||||
fn unmark_as_advisor() -> Weight;
|
||||
/// `override_council_veto` — proposal/veto/advisor reads + vote inserts.
|
||||
fn override_council_veto() -> Weight;
|
||||
/// `submit_emergency_proposal` — endorsement iter (≤7) + bond + clear_prefix.
|
||||
fn submit_emergency_proposal() -> Weight;
|
||||
/// `treasury_disbursement` — proposal read + single-use + transfer.
|
||||
fn treasury_disbursement() -> Weight;
|
||||
/// `treasury_deposit` — transfer only.
|
||||
fn treasury_deposit() -> Weight;
|
||||
/// `endorse_emergency_proposal` — council iter (≤7) + insert.
|
||||
fn endorse_emergency_proposal() -> Weight;
|
||||
}
|
||||
|
||||
macro_rules! w {
|
||||
($r:expr, $w:expr) => {
|
||||
Weight::from_parts(COMPUTE, 0)
|
||||
.saturating_add(T::DbWeight::get().reads($r as u64))
|
||||
.saturating_add(T::DbWeight::get().writes($w as u64))
|
||||
};
|
||||
}
|
||||
|
||||
/// Weights parametrizados por el `DbWeight` del runtime.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
fn submit_proposal() -> Weight { w!(3, 4) }
|
||||
fn cast_vote() -> Weight { w!(4, 2) }
|
||||
fn finalize_proposal() -> Weight { w!(3, 4) }
|
||||
fn add_council_member() -> Weight { w!(3, 2) }
|
||||
fn remove_council_member() -> Weight { w!(2, 2) }
|
||||
fn veto_proposal() -> Weight { w!(10, 4) }
|
||||
fn mark_as_advisor() -> Weight { w!(1, 1) }
|
||||
fn unmark_as_advisor() -> Weight { w!(1, 1) }
|
||||
fn override_council_veto() -> Weight { w!(5, 3) }
|
||||
fn submit_emergency_proposal() -> Weight { w!(12, 11) }
|
||||
fn treasury_disbursement() -> Weight { w!(4, 3) }
|
||||
fn treasury_deposit() -> Weight { w!(2, 2) }
|
||||
fn endorse_emergency_proposal() -> Weight { w!(10, 1) }
|
||||
}
|
||||
|
||||
macro_rules! wnop {
|
||||
($r:expr, $w:expr) => {
|
||||
Weight::from_parts(COMPUTE, 0)
|
||||
.saturating_add(RocksDbWeight::get().reads($r as u64))
|
||||
.saturating_add(RocksDbWeight::get().writes($w as u64))
|
||||
};
|
||||
}
|
||||
|
||||
/// Impl no-op para mocks / tests.
|
||||
impl WeightInfo for () {
|
||||
fn submit_proposal() -> Weight { wnop!(3, 4) }
|
||||
fn cast_vote() -> Weight { wnop!(4, 2) }
|
||||
fn finalize_proposal() -> Weight { wnop!(3, 4) }
|
||||
fn add_council_member() -> Weight { wnop!(3, 2) }
|
||||
fn remove_council_member() -> Weight { wnop!(2, 2) }
|
||||
fn veto_proposal() -> Weight { wnop!(10, 4) }
|
||||
fn mark_as_advisor() -> Weight { wnop!(1, 1) }
|
||||
fn unmark_as_advisor() -> Weight { wnop!(1, 1) }
|
||||
fn override_council_veto() -> Weight { wnop!(5, 3) }
|
||||
fn submit_emergency_proposal() -> Weight { wnop!(12, 11) }
|
||||
fn treasury_disbursement() -> Weight { wnop!(4, 3) }
|
||||
fn treasury_deposit() -> Weight { wnop!(2, 2) }
|
||||
fn endorse_emergency_proposal() -> Weight { wnop!(10, 1) }
|
||||
}
|
||||
@@ -33,6 +33,9 @@ sp-io = { workspace = true }
|
||||
parity-scale-codec = { workspace = true }
|
||||
scale-info = { workspace = true }
|
||||
|
||||
# Benchmarking canonical (FRAME v2) — optional, solo activo bajo runtime-benchmarks.
|
||||
frame-benchmarking = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# Mock runtime canonical para integration tests — PROV-3 E2E flow + golden
|
||||
# vectors ASP-3. ed25519-dalek solo aqui (fixtures); el runtime usa sp_io.
|
||||
@@ -52,8 +55,11 @@ std = [
|
||||
"sp-io/std",
|
||||
"parity-scale-codec/std",
|
||||
"scale-info/std",
|
||||
"frame-benchmarking?/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"dep:frame-benchmarking",
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -63,6 +63,10 @@
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
/// Weights canonical (INTERIM hand-derived DB-aware — ver `weights.rs` §Estado).
|
||||
pub mod weights;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
/// Re-export canonical types desde origin-chain-types para consumers.
|
||||
pub use origin_chain_types::{
|
||||
AssetAttestation, AssetHash, OitIdentity, SeverityTier, ValidationAttestation,
|
||||
@@ -107,6 +111,8 @@ pub mod pallet {
|
||||
use origin_chain_types::{
|
||||
attestation_signed_message, decode_signing_payload, AssetAttestation, AssetHash,
|
||||
};
|
||||
// Trait de weights en scope para resolver `T::WeightInfo::<method>()`.
|
||||
use crate::weights::WeightInfo;
|
||||
|
||||
/// Cota DoS del `signing_payload` postcard recibido (el real ~115 bytes;
|
||||
/// 512 es holgado para schema/identifier futuros sin permitir flooding).
|
||||
@@ -121,6 +127,11 @@ pub mod pallet {
|
||||
type RuntimeEvent: From<Event<Self>>
|
||||
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
|
||||
/// Weight information canonical para los extrinsics de este pallet.
|
||||
/// El runtime lo cablea a `weights::SubstrateWeight<Runtime>`; los mocks
|
||||
/// usan `()`. Ver `weights.rs` + `benchmarking.rs`.
|
||||
type WeightInfo: crate::weights::WeightInfo;
|
||||
|
||||
// Future canonical (post OCAFB ratify):
|
||||
// type OitFeeSchedule: arkhe_amis_server::OitFeeSchedule;
|
||||
}
|
||||
@@ -281,7 +292,7 @@ pub mod pallet {
|
||||
/// con el `genesis_hash` de ESTA cadena (cierra replay cross-chain).
|
||||
/// 7. Insert `AssetAttestation` (creator = validador, block real) + evento.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(Weight::from_parts(20_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::attest_asset())]
|
||||
pub fn attest_asset(
|
||||
origin: OriginFor<T>,
|
||||
signing_payload: BoundedVec<u8, MaxPayloadLen>,
|
||||
@@ -353,7 +364,7 @@ pub mod pallet {
|
||||
/// validadores que governance habilita pueden anclar. Sunset a
|
||||
/// attestation-driven cuando el registro OIT de Mathi aterrice.
|
||||
#[pallet::call_index(1)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::register_validator())]
|
||||
pub fn register_validator(
|
||||
origin: OriginFor<T>,
|
||||
public_key: [u8; 32],
|
||||
@@ -367,7 +378,7 @@ pub mod pallet {
|
||||
|
||||
/// FIX-4: revoca un validador — ya no puede anclar. **Root-only.**
|
||||
#[pallet::call_index(2)]
|
||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||
#[pallet::weight(T::WeightInfo::revoke_validator())]
|
||||
pub fn revoke_validator(
|
||||
origin: OriginFor<T>,
|
||||
public_key: [u8; 32],
|
||||
@@ -382,6 +393,11 @@ pub mod pallet {
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
/// Benchmarks canonical (FRAME v2) — solo bajo la feature `runtime-benchmarks`.
|
||||
/// Verificados por `impl_benchmark_test_suite!` en el mock (ver `benchmarking.rs`).
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
|
||||
// v0.0.18 — Mock runtime canonical + integration tests E2E PROV-3 flow.
|
||||
// Per audit canonical sesión 3+ (`AUDIT_CHARON_SESION_3_STATE_V1.md`),
|
||||
// own fixtures basados en vendor copy ValidationAttestation real existente
|
||||
|
||||
@@ -33,6 +33,7 @@ impl frame_system::Config for Test {
|
||||
|
||||
impl origin_chain_provenance::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
/// Genesis hash de los golden vectors ASP-3 (`/tmp/golden-asp3`, GENESIS=0xAB×32).
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
//! # 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.
|
||||
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use frame_support::{traits::Get, weights::constants::RocksDbWeight, weights::Weight};
|
||||
|
||||
/// Trait de weights para los extrinsics de `pallet-origin-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;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
/// Impl no-op para mocks / tests — usa `RocksDbWeight` directo (no requiere `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))
|
||||
}
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user