diff --git a/lang-v2/derive/src/lib.rs b/lang-v2/derive/src/lib.rs index d6a93d7402..030dac1a5d 100644 --- a/lang-v2/derive/src/lib.rs +++ b/lang-v2/derive/src/lib.rs @@ -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(); } @@ -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 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 { diff --git a/lang-v2/tests/macro_diagnostics.rs b/lang-v2/tests/macro_diagnostics.rs index 97f9da8a08..470d3e93c2 100644 --- a/lang-v2/tests/macro_diagnostics.rs +++ b/lang-v2/tests/macro_diagnostics.rs @@ -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, + pub right: Nested, +}} +"# + ); + compile_fail_case( + "nested_header_size_overflow", + &source, + &["HEADER_SIZE must be <= 255"], + ); +} + #[test] #[cfg_attr( miri,