Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions lang-v2/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,9 @@ fn impl_accounts(input: &DeriveInput) -> TokenStream2 {
.collect();

if named_fields.named.len() > 255 {
// Syntactic top-level field cap. Flattened Nested account counts are
// separately bounded by the HEADER_SIZE assert emitted on the
// TryAccounts impl (duplicate-tracking / u8 offset domain is 256 bits).
return syn::Error::new(name.span(), "`Accounts` derive supports at most 255 fields")
.to_compile_error();
}
Expand Down Expand Up @@ -1975,6 +1978,14 @@ fn impl_accounts(input: &DeriveInput) -> TokenStream2 {
}
}

// Flattened declared-account count must fit the 256-bit duplicate
// bitvec and u8 field-offset domain. Top-level field count alone is
// not enough: Nested<Inner> expands to Inner::HEADER_SIZE slots.
const _: () = assert!(
<#name as anchor_lang_v2::TryAccounts>::HEADER_SIZE <= 255,
"`Accounts` flattened HEADER_SIZE must be <= 255 (duplicate-tracking domain)"
);

#[cfg(feature = "idl-build")]
#[doc(hidden)]
impl #name {
Expand Down
35 changes: 35 additions & 0 deletions lang-v2/tests/macro_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,41 @@ pub struct Bad {
);
}

#[test]
#[cfg_attr(
miri,
ignore = "spawns cargo and writes temporary workspaces; covered by normal cargo test"
)]
fn nested_accounts_flattened_header_size_must_fit_u8_domain() {
// Top-level field count is only 2, but Nested expands to 128+128 = 256
// slots — past the 256-bit duplicate / u8 offset domain.
let chunk_fields: String = (0..128)
.map(|i| format!(" pub a{i}: UncheckedAccount,\n"))
.collect();
let source = format!(
r#"
use anchor_lang_v2::prelude::*;

declare_id!("11111111111111111111111111111111");

#[derive(Accounts)]
pub struct Chunk {{
{chunk_fields}}}

#[derive(Accounts)]
pub struct Outer {{
pub left: Nested<Chunk>,
pub right: Nested<Chunk>,
}}
"#
);
compile_fail_case(
"nested_header_size_overflow",
&source,
&["HEADER_SIZE must be <= 255"],
);
}

#[test]
#[cfg_attr(
miri,
Expand Down
Loading