diff --git a/lang/syn/src/codegen/program/dispatch.rs b/lang/syn/src/codegen/program/dispatch.rs index 13fc2fd9b9..4ae2ea1201 100644 --- a/lang/syn/src/codegen/program/dispatch.rs +++ b/lang/syn/src/codegen/program/dispatch.rs @@ -78,16 +78,17 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { accounts: &'info [AccountInfo<'info>], data: &'info [u8], ) -> anchor_lang::Result<()> { - #(#global_ixs)* - // Legacy IDL instructions have been removed in favor of Program Metadata // No IDL instructions are injected into programs anymore - // Dispatch Event CPI instruction + // Dispatch the Event CPI instruction before user instructions so a + // custom discriminator cannot shadow the reserved event-CPI tag. if data.starts_with(anchor_lang::event::EVENT_IX_TAG_LE) { return #event_cpi_handler; } + #(#global_ixs)* + #fallback_fn } } diff --git a/tests/events/programs/events/src/lib.rs b/tests/events/programs/events/src/lib.rs index c1c8e59526..a40a3cdbc0 100644 --- a/tests/events/programs/events/src/lib.rs +++ b/tests/events/programs/events/src/lib.rs @@ -31,6 +31,11 @@ pub mod events { }); Ok(()) } + + #[instruction(discriminator = 0xe4)] + pub fn event_cpi_shadow_probe(_ctx: Context) -> Result<()> { + Ok(()) + } } #[derive(Accounts)] @@ -43,6 +48,9 @@ pub struct TestEvent {} #[derive(Accounts)] pub struct TestEventCpi {} +#[derive(Accounts)] +pub struct EventCpiShadowProbe {} + #[event] pub struct MyEvent { pub data: u64, diff --git a/tests/events/tests/events.ts b/tests/events/tests/events.ts index d8c5237953..b859890c13 100644 --- a/tests/events/tests/events.ts +++ b/tests/events/tests/events.ts @@ -118,5 +118,18 @@ describe("Events", () => { throw new Error("Was able to invoke the self-CPI instruction"); }); + + it("Allows short custom discriminator overlapping event CPI tag", async () => { + const tx = new anchor.web3.Transaction(); + tx.add( + new anchor.web3.TransactionInstruction({ + programId: program.programId, + keys: [], + data: Buffer.from([0xe4]), + }) + ); + + await program.provider.sendAndConfirm(tx, [], confirmOptions); + }); }); });