fix(runtime): RV12 — el blob no sabia decir quien era y el genesis no construia
Tres defectos encontrados al montar el benchmarking real sobre el WASM. Ninguno era visible desde los tests nativos, y los tres bloqueaban frame-omni-bencher, que es el instrumento que mide los weights de mainnet. 1. LA SECCION runtime_version NO EXISTIA. Faltaba el atributo sp_version::runtime_version sobre VERSION. Esa custom section es la unica via para leer la version del runtime SIN ejecutarlo, que es de lo que depende set_code / forkless upgrade. wasm-builder abortaba con "Couldn't find the runtime_version section". El atributo exige literales, asi que la direccion queda INVERTIDA: VERSION es la fuente unica y las siete constantes publicas se derivan de sus campos. La deriva ya no es cuestion de disciplina, es imposible por construccion (contrafactual verificado: mover el literal pone rojo el test). 2. LOS PRESETS DE GENESIS NO EXISTIAN EN no_std. get_preset y preset_names devolvian None/vacio bajo un cfg(feature = "std") cuyo comentario afirmaba que los presets "solo se consultan en chain_spec generation, no en runtime execution". Falso: en el modelo runtime-driven chain spec el estado se construye DENTRO del WASM. omni-bencher fallaba con "The preset with name development is not available", y chain-spec-builder habria fallado igual. 3. NINGUN PRESET HABIA CONSTRUIDO UN GENESIS VALIDO JAMAS. Los tres poblaban aura.authorities Y session.keys a la vez; con pallet-session cableado, session instala las authorities de Aura via on_genesis_session y el build de Aura revienta con "Authorities are already initialized!". Sobrevivio toda la vida del crate porque todos los tests inspeccionaban el JSON del preset y ninguno llamaba a build_state: se miraba la caja por fuera. Ahora la abre los_tres_presets_construyen_estado (dev-dep sp-io), con contrafactual verificado: repoblar authorities lo pone rojo con el panic exacto. Tambien: el test que fijaba la version llevaba la version en el NOMBRE (runtime_version_v11_did_newtype) y su comentario quedaba mintiendo en cuanto la spec subia. Renombrado a spec_version_vigente_y_transaction_version_intacta. MEDIDO: WASM build exit 0; seccion runtime_version presente y decodificada (spec_name origin-chain, spec_version 12, tx 1); omni-bencher enumera los 19 benchmarks de los 5 pallets custom; 290 tests verde. RV11 -> RV12. TRANSACTION_VERSION intacto. Sin migracion: no hay cadena viva y ninguna transicion de estado cambia. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4bdd0409f9
commit
b7010db328
4 files changed
+143
-55
No files matched your search
@@ -106,6 +106,12 @@ log.workspace = true
|
||||
# dep rompía isolated WASM build canonical.
|
||||
serde_json.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
# Solo para `los_tres_presets_construyen_estado`: `TestExternalities` es el unico
|
||||
# sitio donde se puede EJECUTAR `build_state` y descubrir un genesis que panica.
|
||||
# Sin esto los presets se validaban leyendo su JSON, que es mirar la caja por fuera.
|
||||
sp-io = { workspace = true, default-features = true }
|
||||
|
||||
[build-dependencies]
|
||||
# Slice 2e v0.0.35 — Re-enabled substrate-wasm-builder for active investigation.
|
||||
# Slice 2c+2d findings: WASM_BUILD_STD=0 resuelve alloc/exchange_malloc pero
|
||||
|
||||
@@ -37,8 +37,6 @@
|
||||
//! Los bytes canonical son los **mismos exactos** que `sp_keyring::Sr25519Keyring::Alice.public()`
|
||||
//! retorna canonical — verified empíricamente contra Substrate canonical source.
|
||||
|
||||
#![cfg(feature = "std")]
|
||||
|
||||
use crate::{
|
||||
configs::{AuraId, EmissionEcosystemPalletId, GovernanceTreasuryPalletId, GrandpaId},
|
||||
opaque::SessionKeys,
|
||||
@@ -241,14 +239,22 @@ fn build_genesis(
|
||||
fees: FeesConfig {
|
||||
initial_profiles: initial_fee_profiles,
|
||||
},
|
||||
// VACIOS A PROPOSITO — con `pallet-session` (Index 6) cableado, las
|
||||
// authorities de Aura y Grandpa las instala la SESSION, no el genesis
|
||||
// de cada pallet. `initialize_authorities` corta con
|
||||
// `assert!(Authorities::get().is_empty(), "Authorities are already
|
||||
// initialized!")` en cuanto la lista NO esta vacia, y `SessionConfig`
|
||||
// ya la habia puesto: poblar los tres es un panic garantizado en
|
||||
// `build_state`. Se descubrio el 2026-09-09 al correr `omni-bencher`,
|
||||
// que construye el estado de verdad; ningun test lo hacia, asi que los
|
||||
// TRES presets llevaban desde su nacimiento sin construir un genesis
|
||||
// valido ni una sola vez. El test `los_tres_presets_construyen_estado`
|
||||
// existe para que eso no pueda repetirse.
|
||||
aura: AuraConfig {
|
||||
authorities: validators.iter().map(|(_, aura, _)| aura.clone()).collect(),
|
||||
authorities: vec![],
|
||||
},
|
||||
grandpa: GrandpaConfig {
|
||||
authorities: validators
|
||||
.iter()
|
||||
.map(|(_, _, grandpa)| (grandpa.clone(), 1))
|
||||
.collect(),
|
||||
authorities: vec![],
|
||||
_config: core::marker::PhantomData,
|
||||
},
|
||||
session: SessionConfig {
|
||||
@@ -461,6 +467,33 @@ pub fn preset_names() -> Vec<PresetId> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// LOS TRES PRESETS TIENEN QUE CONSTRUIR ESTADO DE VERDAD, no solo serializar.
|
||||
///
|
||||
/// Este test nace de un fallo que sobrevivio a toda la vida del crate: los
|
||||
/// tres presets poblaban `aura.authorities` Y `session.keys` a la vez, y
|
||||
/// `pallet_session::GenesisConfig::build` instala las authorities de Aura
|
||||
/// via `on_genesis_session`, con lo que el `build` de Aura reventaba con
|
||||
/// «Authorities are already initialized!». Ningun test lo veia porque todos
|
||||
/// inspeccionaban el JSON del preset — la caja por fuera — y `build_state`
|
||||
/// no se ejecutaba en ninguna parte. Lo destapo `frame-omni-bencher`, que
|
||||
/// construye el estado porque no le queda mas remedio.
|
||||
///
|
||||
/// Un preset que serializa pero no construye es un genesis que no existe.
|
||||
#[test]
|
||||
fn los_tres_presets_construyen_estado() {
|
||||
for id in preset_names() {
|
||||
let json = get_preset(&id)
|
||||
.unwrap_or_else(|| panic!("preset {id:?} sin JSON"));
|
||||
let mut ext = sp_io::TestExternalities::new_empty();
|
||||
ext.execute_with(|| {
|
||||
frame_support::genesis_builder_helper::build_state::<RuntimeGenesisConfig>(
|
||||
json.clone(),
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("preset {id:?} no construye estado: {e}"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bm_v2_allocations_sum_canonical_to_one_billion_ogc() {
|
||||
let total = ECOSYSTEM_ALLOCATION
|
||||
|
||||
@@ -47,7 +47,6 @@ use alloc::vec::Vec;
|
||||
pub mod configs;
|
||||
/// FIX-6 Stage 2b NPoS — pallet_staking config (infra dormida).
|
||||
pub mod staking;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod genesis_config_presets;
|
||||
pub mod opaque;
|
||||
|
||||
@@ -214,13 +213,27 @@ pub type TxExtension = (
|
||||
// Runtime metadata canonical (SPEC_NAME + version constants)
|
||||
// ============================================================
|
||||
|
||||
/// Extrae el `&'static str` de un `Cow::Borrowed` en contexto `const`.
|
||||
///
|
||||
/// Existe para que `SPEC_NAME`/`IMPL_NAME` se DERIVEN de [`VERSION`] en vez de
|
||||
/// repetir el literal: el atributo `#[sp_version::runtime_version]` obliga a que
|
||||
/// los campos de `VERSION` sean literales (ver la nota sobre esa const), y una
|
||||
/// segunda copia del literal es una divergencia esperando a ocurrir. `panic!` en
|
||||
/// `const fn` se evalua en compilacion: un `Cow::Owned` seria error de build.
|
||||
const fn borrowed_str(cow: &'static alloc::borrow::Cow<'static, str>) -> &'static str {
|
||||
match cow {
|
||||
alloc::borrow::Cow::Borrowed(s) => s,
|
||||
alloc::borrow::Cow::Owned(_) => panic!("VERSION debe usar Cow::Borrowed"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Canonical spec name per Substrate genesis canonical. Identifica el chain
|
||||
/// runtime canonical en bloques + storage proofs.
|
||||
pub const SPEC_NAME: &str = "origin-chain";
|
||||
pub const SPEC_NAME: &str = borrowed_str(&VERSION.spec_name);
|
||||
|
||||
/// Canonical implementation name canonical. Distingue diferentes node binaries
|
||||
/// que ejecutan el mismo spec (canonical: "origin-chain-charon").
|
||||
pub const IMPL_NAME: &str = "origin-chain-charon";
|
||||
pub const IMPL_NAME: &str = borrowed_str(&VERSION.impl_name);
|
||||
|
||||
/// Canonical runtime version canonical. Bumped cada forkless upgrade via
|
||||
/// `set_code` extrinsic governance canonical.
|
||||
@@ -284,19 +297,37 @@ pub const IMPL_NAME: &str = "origin-chain-charon";
|
||||
/// `DidToAccount` no requiere migración. El bump es por **higiene de metadata**:
|
||||
/// el `TypeInfo` de la clave de storage pasa a nombrarse `Did`, y los clientes
|
||||
/// que cachean metadata deben re-leerla. TRANSACTION_VERSION intacto.
|
||||
pub const RUNTIME_VERSION: u32 = 11;
|
||||
///
|
||||
/// **v12 (el blob por fin se declara, y el genesis por fin construye)**: tres
|
||||
/// defectos encontrados al montar el benchmarking real sobre el WASM, ninguno
|
||||
/// visible desde los tests nativos. (a) El blob **no emitia la custom section
|
||||
/// `runtime_version`** — faltaba `#[sp_version::runtime_version]` sobre `VERSION`.
|
||||
/// Esa seccion es la unica via para leer la version del runtime SIN ejecutarlo,
|
||||
/// que es de lo que depende `set_code` / forkless upgrade: el blob no sabia decir
|
||||
/// quien era. (b) `GenesisBuilder::get_preset`/`preset_names` devolvian `None`/
|
||||
/// vacio bajo `no_std` por un `#[cfg(feature = "std")]` cuyo comentario afirmaba
|
||||
/// que los presets «solo se consultan en chain_spec generation» — falso en el
|
||||
/// modelo runtime-driven chain spec, donde el estado se construye DENTRO del
|
||||
/// WASM. (c) Los tres presets poblaban `aura.authorities` **y** `session.keys`
|
||||
/// a la vez, y con `pallet-session` cableado eso es un panic garantizado
|
||||
/// («Authorities are already initialized!»): **ningun preset habia construido
|
||||
/// un genesis valido jamas**, porque ningun test llamaba a `build_state`. Ahora
|
||||
/// lo llama `los_tres_presets_construyen_estado`, con contrafactual verificado.
|
||||
/// Nada de esto cambia una transicion de estado de la cadena viva (no hay cadena
|
||||
/// viva): el bump es por higiene de blob y de metadata. TRANSACTION_VERSION intacto.
|
||||
pub const RUNTIME_VERSION: u32 = VERSION.spec_version;
|
||||
|
||||
/// Canonical authoring version per Substrate semver canonical.
|
||||
pub const AUTHORING_VERSION: u32 = 1;
|
||||
pub const AUTHORING_VERSION: u32 = VERSION.authoring_version;
|
||||
|
||||
/// Canonical impl version per Substrate semver canonical.
|
||||
pub const IMPL_VERSION: u32 = 1;
|
||||
pub const IMPL_VERSION: u32 = VERSION.impl_version;
|
||||
|
||||
/// Canonical transaction version per Substrate semver canonical.
|
||||
pub const TRANSACTION_VERSION: u32 = 1;
|
||||
pub const TRANSACTION_VERSION: u32 = VERSION.transaction_version;
|
||||
|
||||
/// Canonical state version: v1 per Substrate stable2509+ canonical.
|
||||
pub const STATE_VERSION: u8 = 1;
|
||||
pub const STATE_VERSION: u8 = VERSION.system_version;
|
||||
|
||||
// ============================================================
|
||||
// Runtime composition canonical (frame_support::runtime macro)
|
||||
@@ -641,35 +672,26 @@ impl_runtime_apis! {
|
||||
}
|
||||
|
||||
fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
|
||||
// Canonical Phase 1.7: 3 presets disponibles canonical (development +
|
||||
// local_testnet + mainnet_genesis). Cuando feature `std` activo
|
||||
// (chain_spec generation time), delegamos canonical al
|
||||
// `genesis_config_presets::get_preset`. En no_std (WASM runtime),
|
||||
// siempre return None canonical — los presets canonical solo se
|
||||
// consultan en chain_spec generation, no en runtime execution.
|
||||
#[cfg(feature = "std")]
|
||||
{
|
||||
frame_support::genesis_builder_helper::get_preset::<RuntimeGenesisConfig>(
|
||||
id,
|
||||
genesis_config_presets::get_preset,
|
||||
)
|
||||
}
|
||||
#[cfg(not(feature = "std"))]
|
||||
{
|
||||
let _ = id;
|
||||
None
|
||||
}
|
||||
// Canonical Phase 1.7: 3 presets (development + local_testnet +
|
||||
// mainnet_genesis), servidos TAMBIEN en no_std.
|
||||
//
|
||||
// Historia, porque el comentario anterior documentaba una suposicion
|
||||
// FALSA y costo un bloqueo: decia que los presets «solo se consultan
|
||||
// en chain_spec generation, no en runtime execution» y devolvia None
|
||||
// bajo no_std. Eso rompe el modelo runtime-driven chain spec entero:
|
||||
// `frame-omni-bencher` construye el estado de genesis llamando a este
|
||||
// metodo DENTRO del WASM, y `chain-spec-builder` genera el spec a
|
||||
// partir del blob solo. Con el gate puesto, ambos fallaban con «The
|
||||
// preset with name Some("development") is not available» — medido
|
||||
// 2026-09-09 al montar el benchmarking real.
|
||||
frame_support::genesis_builder_helper::get_preset::<RuntimeGenesisConfig>(
|
||||
id,
|
||||
genesis_config_presets::get_preset,
|
||||
)
|
||||
}
|
||||
|
||||
fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
|
||||
#[cfg(feature = "std")]
|
||||
{
|
||||
genesis_config_presets::preset_names()
|
||||
}
|
||||
#[cfg(not(feature = "std"))]
|
||||
{
|
||||
Vec::new()
|
||||
}
|
||||
genesis_config_presets::preset_names()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -915,15 +937,35 @@ mod benches {
|
||||
// ============================================================
|
||||
|
||||
/// Canonical `RuntimeVersion` per Substrate canonical. Consumed by `sp_api::Core::version()`.
|
||||
///
|
||||
/// # Por que los valores son literales y no las constantes con nombre
|
||||
///
|
||||
/// `#[sp_version::runtime_version]` emite la **custom section `runtime_version`**
|
||||
/// del blob WASM: la unica forma que tiene un nodo de leer la version del runtime
|
||||
/// SIN ejecutarlo (imprescindible para `set_code` / forkless upgrade). Su parser
|
||||
/// (`sp-version-proc-macro`, `ParseRuntimeVersion`) solo acepta literales en
|
||||
/// `spec_name`, `impl_name` y los cuatro `u32`/`u8`; una constante con nombre da
|
||||
/// «only numeric literals are supported here». Sin el atributo, `wasm-builder`
|
||||
/// aborta con «Couldn't find the `runtime_version` section».
|
||||
///
|
||||
/// Para que el literal no pueda divergir de la constante publica, la direccion
|
||||
/// esta INVERTIDA respecto de lo habitual: esta const es la fuente unica de
|
||||
/// verdad y `SPEC_NAME`, `RUNTIME_VERSION`, `AUTHORING_VERSION`, `IMPL_VERSION`,
|
||||
/// `TRANSACTION_VERSION` y `STATE_VERSION` se derivan de sus campos. Bumpear una
|
||||
/// version se hace AQUI, en un unico sitio.
|
||||
///
|
||||
/// `apis` lo ignora el macro a proposito: la seccion de runtime APIs la emite
|
||||
/// `impl_runtime_apis!` por su cuenta.
|
||||
#[sp_version::runtime_version]
|
||||
pub const VERSION: sp_version::RuntimeVersion = sp_version::RuntimeVersion {
|
||||
spec_name: alloc::borrow::Cow::Borrowed(SPEC_NAME),
|
||||
impl_name: alloc::borrow::Cow::Borrowed(IMPL_NAME),
|
||||
authoring_version: AUTHORING_VERSION,
|
||||
spec_version: RUNTIME_VERSION,
|
||||
impl_version: IMPL_VERSION,
|
||||
spec_name: alloc::borrow::Cow::Borrowed("origin-chain"),
|
||||
impl_name: alloc::borrow::Cow::Borrowed("origin-chain-charon"),
|
||||
authoring_version: 1,
|
||||
spec_version: 12,
|
||||
impl_version: 1,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: TRANSACTION_VERSION,
|
||||
system_version: STATE_VERSION,
|
||||
transaction_version: 1,
|
||||
system_version: 1,
|
||||
};
|
||||
|
||||
/// Canonical Executive type canonical (FRAME executive wrapper).
|
||||
@@ -942,14 +984,20 @@ use sp_api::impl_runtime_apis;
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Fija la version vigente y la invariante que la acompaña.
|
||||
///
|
||||
/// El test NO cuenta la historia de cada version — eso vive en el doc comment
|
||||
/// de [`RUNTIME_VERSION`], que es donde se lee. Aqui solo se pinchan dos cosas:
|
||||
/// que subir la spec es un acto deliberado (este assert se rompe y hay que
|
||||
/// venir a tocarlo), y que `TRANSACTION_VERSION` sigue en 1 — un bump suyo
|
||||
/// invalida cada extrinsic firmada en vuelo, y jamas puede colarse de rebote.
|
||||
///
|
||||
/// El nombre anterior clavaba una version concreta en el identificador
|
||||
/// (`runtime_version_v11_did_newtype`) y su comentario quedo mintiendo en el
|
||||
/// mismo commit en que la version subio a 12.
|
||||
#[test]
|
||||
fn runtime_version_v11_did_newtype() {
|
||||
// v11 = condición #3 de Mathi: `Did` pasa de alias de `AssetHash` a newtype
|
||||
// propio (cierra la confusión cross-namespace que el contexto KDF compartido
|
||||
// agravaba). NO cambia la transición de estado — mismos bytes, mismo encoding
|
||||
// SCALE ⇒ sin migración; el bump es higiene de metadata (TypeInfo).
|
||||
// (v10 = identidad unificada `origin/identity/v1`; v9 = weights DB-aware.)
|
||||
assert_eq!(RUNTIME_VERSION, 11);
|
||||
fn spec_version_vigente_y_transaction_version_intacta() {
|
||||
assert_eq!(RUNTIME_VERSION, 12);
|
||||
assert_eq!(TRANSACTION_VERSION, 1);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user