Skip to content
Merged
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
5 changes: 4 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ light-indexed-merkle-tree = { version = "5.0.0", path = "program-libs/indexed-me
light-concurrent-merkle-tree = { version = "5.0.0", path = "program-libs/concurrent-merkle-tree" }
light-sparse-merkle-tree = { version = "0.3.0", path = "sparse-merkle-tree" }
light-client = { path = "sdk-libs/client", version = "0.23.0" }
light-event = { path = "sdk-libs/event", version = "0.23.0" }
light-event = { path = "sdk-libs/event", version = "0.23.1" }
light-hasher = { path = "program-libs/hasher", version = "5.0.0", default-features = false }
light-macros = { path = "program-libs/macros", version = "2.2.0" }
light-merkle-tree-reference = { path = "program-tests/merkle-tree", version = "4.0.0" }
Expand Down
3 changes: 2 additions & 1 deletion sdk-libs/event/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "light-event"
version = "0.23.0"
version = "0.23.1"
description = "Event types and utilities for Light Protocol"
repository = "https://github.com/Lightprotocol/light-protocol"
license = "Apache-2.0"
Expand All @@ -15,4 +15,5 @@ light-zero-copy = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
bs58 = { workspace = true }
rand = { workspace = true }
2 changes: 2 additions & 0 deletions sdk-libs/event/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@
pub mod error;
pub mod event;
pub mod parse;
#[cfg(test)]
mod regression_test;
24 changes: 13 additions & 11 deletions sdk-libs/event/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -738,18 +738,20 @@ fn create_nullifier_queue_indices(
.insert_into_queues_instruction
.input_sequence_numbers
.to_vec();
// For every sequence number:
// 1. Find every input compressed account
// 2. assign sequence number as nullifier queue index
// 3. increment the sequence number
internal_input_sequence_numbers.iter_mut().for_each(|seq| {
for (i, merkle_tree_pubkey) in input_merkle_tree_pubkeys.iter().enumerate() {
if *merkle_tree_pubkey == seq.tree_pubkey {
nullifier_queue_indices[i] = seq.seq.into();
seq.seq += 1;
}
// Walk input_compressed_accounts in order, assigning sequence numbers to batch
// accounts using a compact write index. Non-batch (legacy/concurrent) accounts
// have no matching sequence number entry and are skipped.
let mut batch_idx = 0usize;
for merkle_tree_pubkey in input_merkle_tree_pubkeys.iter() {
if let Some(seq) = internal_input_sequence_numbers
.iter_mut()
.find(|s| s.tree_pubkey == *merkle_tree_pubkey)
{
nullifier_queue_indices[batch_idx] = seq.seq.into();
seq.seq += 1;
batch_idx += 1;
}
});
}
nullifier_queue_indices
}

Expand Down
110 changes: 110 additions & 0 deletions sdk-libs/event/src/regression_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/// Regression test for index-out-of-bounds panic in create_nullifier_queue_indices.
/// Transaction: 3ybts1eFSC7QN6aU4ao6NJCgn7xTbtBVyzeLDZJf9eVN93vHZWupX4TXqHHgV18xf17eit7Uw5T135uabnpToKK4
/// Slot: 407265372 (mainnet)
/// This transaction crashed photon's indexer because `len` passed to
/// `create_nullifier_queue_indices` didn't match the number of input accounts.
#[cfg(test)]
mod tests {
use light_compressed_account::Pubkey;

use crate::parse::event_from_light_transaction;

fn pubkey(s: &str) -> Pubkey {
let bytes: [u8; 32] = bs58::decode(s).into_vec().unwrap().try_into().unwrap();
Pubkey::from(bytes)
}

fn ix_data(s: &str) -> Vec<u8> {
bs58::decode(s).into_vec().unwrap()
}

// Account addresses used in the transaction
const USER: &str = "33X2Tg3gdxTwouaVSxpcNwVHJt2ZYxo3Hm7UjH2i8M3r";
const REGISTERED_PDA: &str = "35hkDgaAKwMCaxRz2ocSZ6NaUrtKkyNqU6c4RV3tYJRh";
const NOOP: &str = "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV";
const CPI_CONTEXT: &str = "HwXnGK3tPkkVY6P439H2p68AxpeuWXd5PcrAxFpbmfbA";
const ACCOUNT_COMPRESSION: &str = "compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq";
const SOL_POOL: &str = "CHK57ywWSDncAoRu1F8QgwYJeXuAJyyBYT4LixLXvMZ1";
const SYSTEM_PROGRAM: &str = "11111111111111111111111111111111";
const BMT3_TREE: &str = "bmt3ccLd4bqSVZVeCJnH1F6C8jNygAhaDfxDwePyyGb";
const OQ3_QUEUE: &str = "oq3AxjekBWgo64gpauB6QtuZNesuv19xrhaC1ZM1THQ";
const SMT5_TREE: &str = "smt5uPaQT9n6b1qAkgyonmzRxtuazA53Rddwntqistc";
const NFQ5_QUEUE: &str = "nfq5b5xEguPtdD6uPetZduyrB5EUqad7gcUE46rALau";
const BMT2_TREE: &str = "bmt2UxoBxB9xWev4BkLvkGdapsz6sZGkzViPNph7VFi";
const OQ2_QUEUE: &str = "oq2UkeMsJLfXt2QHzim242SUi3nvjJs8Pn7Eac9H9vg";

// Program IDs
const LIGHT_SYSTEM: &str = "SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7";
const COMPUTE_BUDGET: &str = "ComputeBudget111111111111111111111111111111";

#[test]
fn test_mainnet_tx_407265372_no_panic() {
// Tx 3ybts1eFSC7QN...ToKK4, slot 407265372
// Before fix: panicked with "index out of bounds: the len is 3 but the index is 3"
let program_ids = vec![
pubkey(COMPUTE_BUDGET), // SetComputeUnitLimit
pubkey(COMPUTE_BUDGET), // SetComputeUnitPrice
pubkey(LIGHT_SYSTEM), // Light system invoke
pubkey(SYSTEM_PROGRAM), // SOL transfer (inner)
pubkey(SYSTEM_PROGRAM), // SOL transfer (inner)
pubkey(SYSTEM_PROGRAM), // SOL transfer (inner)
pubkey(ACCOUNT_COMPRESSION), // InsertIntoQueues (inner)
];

let instructions: Vec<Vec<u8>> = vec![
ix_data("K1FDJ7"),
ix_data("3cDeqiGMb6md"),
ix_data("7Xu3JKNhcxBjvH52amHsaGu55uKzfsGvVjkBKAcEAAByDYGHt2TQQRq8aam17wkuH3Vtu2xuLyh8nZRxaqTEZPKM88CTs2e9MMiHW1ZA2NmFwbtgeHLFSRvW2DCayZMqHZWGEPjKwXnEJjFfKCTiJDXLeHbqirZeH4M3rYpeudpPnbNH9F9vLchjWs73hKJ9aSLVJKnJNXyr6ZW4hZd8YKVk3jaS11oW2ndPQT8CzYAF79wu8uishgqpLN42RwytWTDpMNUq7mKRFj1LkKKnpv8ya9WRxDCCKHfp1zn8sc1YviTcMyFsDRBvnE7kibyhcvd6hY9PvojPZNWABNDHxMGZoUL8xoUNRiD8Fxk7DyWQBvtqwyoPSjgFmKA97yEp4Kvj8btYDP24t51GYYXZyKjfFHnShcmdoKxuGohShW1UdjAhSWMVySZ92KRXjVJm6uv7CD5uXRy5Kuqco9ZHwASTv6HE1fQCEWKDdvq8Nx8SBMZF9jPM8JKJEarj"),
ix_data("3Bxs4HHMpGEM2775"),
ix_data("3Bxs4PckVVt51W8w"),
ix_data("3Bxs4PckVVt51W8w"),
ix_data("42NS6uhgPkAU4qDJGz54pXVoPKYL4VENq5jdLryg8pPRKsdthWiNYkaBQEimb4SSscjPZ2uYSXD7TjANLcaUdRMjh7Hid94o5GpGTxM3Pg2ALYdg8Qps6w2Sn6FXc1cp2vWVaXFQicExxLSTUNSSZwKH2M2XiqDxZBSekyELNcXkJCji9heVWqiB48zJX1YDBMYKLgXu3MoFvUgGjpYRteuuw44rBYUSfrs5tNh5CdfMtNkUJVCEvr5LSWeRUYwwXT8shx53iYb186vE3Gm2qY1Up7PfHdqGH1KZmzNz6ZjU2oC2r6zUHxoAA4v7HhMiC2cgwFXMrVGnw2nfKunjEP7Xm2Q62G4uJHGH3aMucTrSKCiwc55czqV9RaUDZUrvtfbLUjwG7XcPwwaY9JusFs21sZNveGE9xm1groM6uGn8ERCc6oBtFhouRKpfQiGoWxKeSrS6K5KWEq5aJ7XsZcXkNSdNGsGtgGu4nDXDtGhbamhXUtVmXcEfMMsMfoSzm1Cj1HCP89thHHC6P52Wert8XAfeei8X8bfwRHw6SzVFTBKkP7W8vjE2PgjwD5rVprBxS5owL4HPEnuTdSoawLA5JEqucpqgvXv7qihuJZ5aEQ8q2JhayJx3hqDriN6g1Vc2br8MtGRPXuwQYAd84jJoS6puMoanPnyFccv35jaxkEwUi5vY8J88ejut9W4uP7JVBivLBXYgDyLteffxA5a6rhJtFZ"),
];

let accounts: Vec<Vec<Pubkey>> = vec![
vec![], // ComputeBudget
vec![], // ComputeBudget
// Light system invoke: user, registered PDA, noop, CPI context, account compression,
// SOL pool, system program, V2 trees (bmt3, oq3), V1 trees (smt5, nfq5), V2 trees (bmt2, oq2)
vec![
pubkey(USER),
pubkey(USER),
pubkey(REGISTERED_PDA),
pubkey(NOOP),
pubkey(CPI_CONTEXT),
pubkey(ACCOUNT_COMPRESSION),
pubkey(SOL_POOL),
pubkey(USER),
pubkey(SYSTEM_PROGRAM),
pubkey(BMT3_TREE),
pubkey(OQ3_QUEUE),
pubkey(SMT5_TREE),
pubkey(NFQ5_QUEUE),
pubkey(BMT2_TREE),
pubkey(OQ2_QUEUE),
],
// SOL transfers (inner)
vec![pubkey(SOL_POOL), pubkey(USER)],
vec![pubkey(USER), pubkey(BMT3_TREE)],
vec![pubkey(USER), pubkey(SMT5_TREE)],
// InsertIntoQueues (inner): CPI context, registered PDA, queues and trees
vec![
pubkey(CPI_CONTEXT),
pubkey(REGISTERED_PDA),
pubkey(OQ3_QUEUE),
pubkey(BMT3_TREE),
pubkey(NFQ5_QUEUE),
pubkey(SMT5_TREE),
pubkey(OQ2_QUEUE),
pubkey(BMT2_TREE),
],
];

let result = event_from_light_transaction(&program_ids, &instructions, accounts);
assert!(
result.is_ok(),
"event_from_light_transaction failed: {:?}",
result.err()
);
Comment on lines +103 to +108
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Assert that this fixture actually produces a parsed event.

This still passes on Ok(None), so a future pattern-matching regression could skip create_nullifier_queue_indices entirely and the test would stay green. Please assert Ok(Some(events)) so this fixture keeps exercising the failing path.

✅ Suggested tightening
-        let result = event_from_light_transaction(&program_ids, &instructions, accounts);
-        assert!(
-            result.is_ok(),
-            "event_from_light_transaction failed: {:?}",
-            result.err()
-        );
+        let events = event_from_light_transaction(&program_ids, &instructions, accounts)
+            .expect("event_from_light_transaction should parse this fixture")
+            .expect("fixture should match a Light event");
+        assert!(!events.is_empty(), "fixture should produce at least one parsed event");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let result = event_from_light_transaction(&program_ids, &instructions, accounts);
assert!(
result.is_ok(),
"event_from_light_transaction failed: {:?}",
result.err()
);
let events = event_from_light_transaction(&program_ids, &instructions, accounts)
.expect("event_from_light_transaction should parse this fixture")
.expect("fixture should match a Light event");
assert!(!events.is_empty(), "fixture should produce at least one parsed event");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk-libs/event/src/regression_test.rs` around lines 103 - 108, The test
currently only asserts result.is_ok() for the call to
event_from_light_transaction(&program_ids, &instructions, accounts) which still
allows Ok(None); change the assertion to require a parsed event by asserting the
result equals Ok(Some(_)) (or use assert!(matches!(result, Ok(Some(_))))) so the
fixture actually exercises the path that triggers create_nullifier_queue_indices
and fails if no events were parsed.

}
}
Loading
Loading