Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
32229a9
check zk root on import
illuzen Jul 2, 2026
497352d
Only inject the outer arguments if the inner attribute does not speci…
illuzen Jul 2, 2026
2bc24d8
max dev accounts
illuzen Jul 2, 2026
b2ecef9
Bound RemovePallet/RemoveStorage deletion to prevent upgrade-time cha…
illuzen Jul 2, 2026
b80a5f9
Add bounded move_prefix_bounded to stage attacker-growable prefix moves
illuzen Jul 2, 2026
74ab733
Check issuance headroom in Balanced::deposit before crediting account
illuzen Jul 2, 2026
8ef8b60
Reject nonce increments that overflow instead of wrapping to zero
illuzen Jul 2, 2026
3db1953
Allow ensure_frozen/decrease_frozen on permitted over-freezes without…
illuzen Jul 2, 2026
ca4a4bd
Use decode_all for Callback::curry and typed NFT attribute reads
illuzen Jul 2, 2026
555c575
Fix ration denominator overflow and reject invalid SplitTwoWays confi…
illuzen Jul 2, 2026
5f0f0b9
Propagate actual transfer debit and burn approved-transfer reaping dust
illuzen Jul 2, 2026
a15a98e
fix tests
illuzen Jul 2, 2026
f146867
fmt
illuzen Jul 2, 2026
a0dd58e
Tolerate bounded key remainder in RemovePallet/RemoveStorage try-runt…
illuzen Jul 2, 2026
2f10123
Mirror issuance headroom check in fungibles::Balanced::deposit
illuzen Jul 3, 2026
fd5858e
Document that the deposit headroom check is best-effort against live …
illuzen Jul 3, 2026
55479e3
Return the requested amount on self-transfer paths
illuzen Jul 3, 2026
56088be
Pin the expected panic message in the bad ZK tree root import test
illuzen Jul 3, 2026
a0cccb2
Deduplicate key-counting helpers in migrations.rs
illuzen Jul 3, 2026
f8432d1
Lower MAX_DEV_ACCOUNTS from 10,000 to 1,000
illuzen Jul 3, 2026
3ec6808
fmt
illuzen Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion frame/executive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ frame-support.workspace = true
frame-system.workspace = true
frame-try-runtime = { optional = true, workspace = true }
log.workspace = true
qp-header.workspace = true
scale-info = { features = ["derive"], workspace = true }
sp-core.workspace = true
sp-io.workspace = true
Expand All @@ -31,7 +32,6 @@ sp-tracing.workspace = true
array-bytes = { workspace = true, default-features = true }
pallet-balances = { workspace = true, default-features = true }
pallet-transaction-payment = { workspace = true, default-features = true }
qp-header = { workspace = true, default-features = true }
sp-inherents = { workspace = true, default-features = true }
sp-version = { workspace = true, default-features = true }

Expand All @@ -44,6 +44,7 @@ std = [
"frame-system/std",
"frame-try-runtime/std",
"log/std",
"qp-header/std",
"scale-info/std",
"sp-core/std",
"sp-io/std",
Expand Down
17 changes: 17 additions & 0 deletions frame/executive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ use frame_support::{
MAX_EXTRINSIC_DEPTH,
};
use frame_system::pallet_prelude::BlockNumberFor;
use qp_header::ZkTreeRootProvider;
use sp_runtime::{
generic::Digest,
traits::{
Expand Down Expand Up @@ -396,6 +397,15 @@ where
header.state_root() == storage_root,
"Storage root must match that calculated."
);

// The ZK tree root is derived from storage, so it is only expected to match
// when the state itself is expected to match.
let zk_tree_root = new_header.zk_tree_root();
header.zk_tree_root().check_equal(zk_tree_root);
assert!(
header.zk_tree_root() == zk_tree_root,
"ZK tree root must match that calculated."
);
}

assert!(
Expand Down Expand Up @@ -936,6 +946,13 @@ where
header.extrinsics_root() == new_header.extrinsics_root(),
"Transaction trie root must be valid.",
);

// check ZK tree root. This field is part of the block-hash preimage, so it must be
// re-derived from state on import; otherwise a block author could commit an arbitrary
// root into the header.
let zk_tree_root = new_header.zk_tree_root();
header.zk_tree_root().check_equal(zk_tree_root);
assert!(header.zk_tree_root() == zk_tree_root, "ZK tree root must match that calculated.",);
}

/// Check a given signed transaction for validity. This doesn't execute any
Expand Down
22 changes: 22 additions & 0 deletions frame/executive/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,28 @@ fn block_import_of_bad_state_root_fails() {
});
}

#[test]
#[should_panic(expected = "ZK tree root must match that calculated.")]
fn block_import_of_bad_zk_tree_root_fails() {
new_test_ext(1).execute_with(|| {
let mut header = Header::new(
1,
array_bytes::hex_n_into_unchecked(
"03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314",
),
array_bytes::hex_n_into_unchecked(
"d6b465f5a50c9f8d5a6edc0f01d285a6b19030f097d3aaf1649b7be81649f118",
),
[69u8; 32].into(),
Digest { logs: vec![] },
);
// Forge a ZK tree root that does not match on-chain state (which is the default zero
// root). Before the `final_checks` fix this block would import successfully.
header.set_zk_tree_root([42u8; 32].into());
Executive::execute_block(TestBlock { header, extrinsics: vec![] }.into());
});
}

#[test]
#[should_panic]
fn block_import_of_bad_extrinsic_root_fails() {
Expand Down
124 changes: 111 additions & 13 deletions frame/support-procedural/src/dynamic_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ fn ensure_codec_index(attrs: &Vec<syn::Attribute>, span: Span) -> Result<()> {
///
/// This allows the outer `#[dynamic_params(..)]` attribute to specify some arguments that don't
/// need to be repeated every time.
///
/// Arguments already present on the inner attribute take precedence and are left untouched;
/// the outer arguments are only injected when the inner attribute has none.
struct MacroInjectArgs {
runtime_params: syn::Ident,
params_pallet: syn::Type,
Expand All @@ -159,20 +162,24 @@ impl VisitMut for MacroInjectArgs {
let attr = item.attrs.iter_mut().find(|attr| attr.path().is_ident("dynamic_pallet_params"));

if let Some(attr) = attr {
match &attr.meta {
syn::Meta::Path(path) =>
assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params"),
_ => (),
// Only inject the outer arguments if the inner attribute does not specify its own:
// either a bare `#[dynamic_pallet_params]` or an empty `#[dynamic_pallet_params()]`.
let has_own_args = match &attr.meta {
syn::Meta::Path(_) => false,
syn::Meta::List(list) => !list.tokens.is_empty(),
syn::Meta::NameValue(_) => true,
};

if !has_own_args {
let runtime_params = &self.runtime_params;
let params_pallet = &self.params_pallet;

attr.meta = syn::parse2::<syn::Meta>(quote! {
dynamic_pallet_params(#runtime_params, #params_pallet)
})
.unwrap()
.into();
}

let runtime_params = &self.runtime_params;
let params_pallet = &self.params_pallet;

attr.meta = syn::parse2::<syn::Meta>(quote! {
dynamic_pallet_params(#runtime_params, #params_pallet)
})
.unwrap()
.into();
}

visit_mut::visit_item_mod_mut(self, item);
Expand Down Expand Up @@ -570,3 +577,94 @@ impl ToTokens for DynamicParamAggregatedEnum {
fn crate_access() -> core::result::Result<syn::Path, TokenStream> {
generate_access_from_frame_or_crate("frame-support").map_err(|e| e.to_compile_error())
}

#[cfg(test)]
mod tests {
use super::*;

fn inject(module: TokenStream) -> syn::ItemMod {
let mut params_mod: syn::ItemMod = parse2(module).unwrap();
MacroInjectArgs {
runtime_params: syn::Ident::new("RuntimeParameters", Span::call_site()),
params_pallet: parse2(quote!(dynamic_params::pallet_parameters::Parameters::<Runtime>))
.unwrap(),
}
.visit_item_mod_mut(&mut params_mod);
params_mod
}

fn inner_attr(params_mod: &syn::ItemMod) -> &syn::Attribute {
let (_, items) = params_mod.content.as_ref().unwrap();
items
.iter()
.find_map(|i| match i {
syn::Item::Mod(m) =>
m.attrs.iter().find(|attr| attr.path().is_ident("dynamic_pallet_params")),
_ => None,
})
.unwrap()
}

#[test]
fn outer_args_are_injected_into_bare_inner_attribute() {
let params_mod = inject(quote! {
pub mod dynamic_params {
#[dynamic_pallet_params]
#[codec(index = 0)]
pub mod inner {}
}
});

let attr = inner_attr(&params_mod);
assert_eq!(
attr.meta.to_token_stream().to_string(),
quote!(dynamic_pallet_params(
RuntimeParameters,
dynamic_params::pallet_parameters::Parameters::<Runtime>
))
.to_string(),
);
}

#[test]
fn outer_args_are_injected_into_empty_inner_attribute() {
let params_mod = inject(quote! {
pub mod dynamic_params {
#[dynamic_pallet_params()]
#[codec(index = 0)]
pub mod inner {}
}
});

let attr = inner_attr(&params_mod);
assert_eq!(
attr.meta.to_token_stream().to_string(),
quote!(dynamic_pallet_params(
RuntimeParameters,
dynamic_params::pallet_parameters::Parameters::<Runtime>
))
.to_string(),
);
}

#[test]
fn inner_attribute_args_take_precedence_over_outer() {
let params_mod = inject(quote! {
pub mod dynamic_params {
#[dynamic_pallet_params(InnerRuntimeParameters, pallet_other::Parameters::<Runtime>)]
#[codec(index = 0)]
pub mod inner {}
}
});

let attr = inner_attr(&params_mod);
assert_eq!(
attr.meta.to_token_stream().to_string(),
quote!(dynamic_pallet_params(
InnerRuntimeParameters,
pallet_other::Parameters::<Runtime>
))
.to_string(),
);
}
}
Loading
Loading