diff --git a/ampd/src/solana/mod.rs b/ampd/src/solana/mod.rs index c704a719d..469e9a860 100644 --- a/ampd/src/solana/mod.rs +++ b/ampd/src/solana/mod.rs @@ -94,18 +94,16 @@ where }; // Check in the logs in a backward way the invocation comes from the gateway - if !event_comes_from_gateway(logs, desired_event_idx) { - error!("Event does not come from the gateway"); - return Vote::NotFound; - } - // Check that the event index is in the logs - let Some(event_log) = logs.get(desired_event_idx) else { - error!("Event index is out of bounds"); - return Vote::NotFound; + let log = match event_comes_from_gateway(logs, desired_event_idx) { + Ok(log) => log, + Err(err) => { + error!("Cannot find the gateway log: {}", err); + return Vote::NotFound; + } }; // Second ensure we can parse the event - let event = match gateway_event_stack::parse_gateway_logs(event_log) { + let event = match gateway_event_stack::parse_gateway_logs(&log) { Ok(ev) => ev, Err(err) => { error!("Cannot parse the gateway log: {}", err); @@ -122,27 +120,58 @@ where } // Check backward in the logs if the invocation comes from the gateway program, -// skipping native program invocations -fn event_comes_from_gateway(logs: &[String], desired_event_idx: usize) -> bool { +// skipping native program invocations and returning the data log if the event comes from the gateway. +// +// Example logs input (indexes are just for reference): +// +// 1. Program gtwLjHAsfKAR6GWB4hzTUAA1w4SDdFMKamtGA5ttMEe invoke [1] +// 2. Program log: Instruction: Call Contract", +// 3. Program data: Y2FsbCBjb250cmFjdF9fXw== 6NGe5cm7PkXHz/g8V2VdRg0nU0l7R48x8lll4s0Clz0= xtlu5J3pLn7c4BhqnNSrP1wDZK/pQOJVCYbk6sroJhY= ZXRoZXJldW0= MHgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2YzIwNjAzYzdiODc2NjgyYzEyMTczYmRlZjlhMWRjYTUyOGYxNGZk 8J+QqvCfkKrwn5Cq8J+Qqg==", +// 4. Program gtwLjHAsfKAR6GWB4hzTUAA1w4SDdFMKamtGA5ttMEe success" +// +// In the above log example, this function would return the data log at 3, if and only if the event comes from the gateway, +// which is determined by scanning log lines backwards till we find the pattern "Program invoke" at 1 for the first time. +// It will fail if it finds any other invocation before the gateway invocation, except for the system program. In that case it will omit it and +// continue scanning. +fn event_comes_from_gateway( + logs: &[String], + desired_event_idx: usize, +) -> Result<&str, Box> { let solana_gateway_id = axelar_solana_gateway::id().to_string(); let system_program_id = solana_sdk::system_program::id().to_string(); - for log in logs.iter().take(desired_event_idx).rev() { - let parts: Vec<&str> = log.split(' ').collect(); + // From the logs, we take only the logs from the desired event index to the first log + let mut logs = logs + .iter() + .take( + desired_event_idx + .checked_add(1) + .expect("To add 1 to index count to get elem take"), + ) + .rev(); // +1 because take() gets n elements, not index based. - if parts.len() < 3 && parts.len() > 4 { - continue; - } - if parts[0] == "Program" && parts[2] == "invoke" { - if parts[1] == system_program_id { + // This is the log that, if the event comes from the gateway, will contain the target data log + // It will be returned if the event comes from the gateway. + let data_log = logs.next().ok_or("Cannot find the first log")?; + + for log in logs { + let mut parts = log.split(' '); + + let program = parts.next().ok_or("Cannot find program log part")?; + let program_id = parts.next().ok_or("Cannot find program_id log part")?; + let action = parts.next().ok_or("Cannot find action log part")?; + + if program == "Program" && action == "invoke" { + if program_id == system_program_id { continue; } - if parts[1] == solana_gateway_id { - return true; + if program_id == solana_gateway_id { + // We return the data log to be processed by further functions if we confirm the log comes from the gateway + return Ok(data_log); } else { break; } } } - false + Err("Log does not belong to the gateway program".into()) }