This commit is contained in:
2026-05-29 07:38:24 +02:00
parent 96b6209482
commit ffa4acbccb
15 changed files with 1982 additions and 107 deletions

View File

@@ -7,7 +7,8 @@
//! deterministic local pipeline over their signatures.
const LOCAL_PIPELINE_DEX_DECODER_SCOPE: &str = "dex_decode.local_pipeline";
const LOCAL_PIPELINE_DEX_DECODER_VERSION: &str = "dex_decode.v0.7.44.ledger1";
const LOCAL_PIPELINE_DEX_DECODER_VERSION: &str =
"dex_decode.v0.7.45.dlmm_add_liquidity_strategies1";
fn default_skip_certified_dex_decode() -> bool {
return true;
@@ -193,9 +194,11 @@ impl LocalPipelineReplayService {
signature = %signature,
"replaying local pipeline for persisted transaction"
);
let transaction_result =
crate::query_chain_transactions_get_by_signature(self.database.as_ref(), signature.as_str())
.await;
let transaction_result = crate::query_chain_transactions_get_by_signature(
self.database.as_ref(),
signature.as_str(),
)
.await;
let transaction = match transaction_result {
Ok(Some(transaction)) => transaction,
Ok(None) => {
@@ -260,9 +263,8 @@ impl LocalPipelineReplayService {
);
},
None => {
let decode_result = dex_decode
.decode_transaction_by_signature(signature.as_str())
.await;
let decode_result =
dex_decode.decode_transaction_by_signature(signature.as_str()).await;
match decode_result {
Ok(decoded_events) => {
result.decoded_event_count += decoded_events.len();
@@ -542,11 +544,8 @@ impl LocalPipelineReplayService {
signature: &str,
decoded_events: &[crate::DexDecodedEventDto],
) -> Result<crate::DexDecodeReplayLedgerDto, crate::Error> {
let ledger_result = build_success_dex_decode_replay_ledger(
transaction_id,
signature,
decoded_events,
);
let ledger_result =
build_success_dex_decode_replay_ledger(transaction_id, signature, decoded_events);
let ledger = match ledger_result {
Ok(ledger) => ledger,
Err(error) => return Err(error),
@@ -597,10 +596,23 @@ fn build_success_dex_decode_replay_ledger(
Err(error) => {
return Err(crate::Error::Db(format!(
"cannot convert decoded event count '{}' to i64: {}",
decoded_events.len(), error
decoded_events.len(),
error
)));
},
};
let effective_event_count_usize = count_effective_decoded_events(decoded_events);
let effective_event_count_result = i64::try_from(effective_event_count_usize);
let effective_event_count = match effective_event_count_result {
Ok(effective_event_count) => effective_event_count,
Err(error) => {
return Err(crate::Error::Db(format!(
"cannot convert effective decoded event count '{}' to i64: {}",
effective_event_count_usize, error
)));
},
};
let instruction_audit_count = event_count - effective_event_count;
let distinct_token_mint_count_usize = count_distinct_decoded_event_token_mints(decoded_events);
let distinct_token_mint_count_result = i64::try_from(distinct_token_mint_count_usize);
let distinct_token_mint_count = match distinct_token_mint_count_result {
@@ -612,7 +624,7 @@ fn build_success_dex_decode_replay_ledger(
)));
},
};
let force_replay_required = event_count > 1 || distinct_token_mint_count > 2;
let force_replay_required = effective_event_count > 1 || distinct_token_mint_count > 2;
let decode_status = if event_count == 0 {
crate::DexDecodeReplayLedgerDto::STATUS_NO_EVENTS.to_string()
} else {
@@ -625,6 +637,8 @@ fn build_success_dex_decode_replay_ledger(
};
let status_reason = build_dex_decode_replay_ledger_status_reason(
event_count,
effective_event_count,
instruction_audit_count,
distinct_token_mint_count,
force_replay_required,
);
@@ -642,9 +656,22 @@ fn build_success_dex_decode_replay_ledger(
));
}
fn count_distinct_decoded_event_token_mints(
decoded_events: &[crate::DexDecodedEventDto],
) -> usize {
fn count_effective_decoded_events(decoded_events: &[crate::DexDecodedEventDto]) -> usize {
let mut count = 0_usize;
for event in decoded_events {
if is_instruction_audit_event(event) {
continue;
}
count += 1;
}
return count;
}
fn is_instruction_audit_event(event: &crate::DexDecodedEventDto) -> bool {
return event.event_kind.ends_with(".instruction_audit");
}
fn count_distinct_decoded_event_token_mints(decoded_events: &[crate::DexDecodedEventDto]) -> usize {
let mut mints = std::collections::BTreeSet::<std::string::String>::new();
for event in decoded_events {
insert_optional_mint(&mut mints, &event.lp_mint);
@@ -668,6 +695,8 @@ fn insert_optional_mint(
fn build_dex_decode_replay_ledger_status_reason(
event_count: i64,
effective_event_count: i64,
instruction_audit_count: i64,
distinct_token_mint_count: i64,
force_replay_required: bool,
) -> std::string::String {
@@ -676,11 +705,11 @@ fn build_dex_decode_replay_ledger_status_reason(
}
if force_replay_required {
return format!(
"decode completed but remains unsafe for skip: event_count={event_count}, distinct_token_mint_count={distinct_token_mint_count}"
"decode completed but remains unsafe for skip: event_count={event_count}, effective_event_count={effective_event_count}, instruction_audit_count={instruction_audit_count}, distinct_token_mint_count={distinct_token_mint_count}"
);
}
return format!(
"decode completed and certified for skip: event_count={event_count}, distinct_token_mint_count={distinct_token_mint_count}"
"decode completed and certified for skip: event_count={event_count}, effective_event_count={effective_event_count}, instruction_audit_count={instruction_audit_count}, distinct_token_mint_count={distinct_token_mint_count}"
);
}
@@ -692,3 +721,57 @@ pub async fn replay_local_pipeline(
let service = crate::LocalPipelineReplayService::new(database);
return service.replay_local_pipeline(config).await;
}
#[cfg(test)]
mod tests {
fn make_decoded_event(
event_kind: &str,
token_a_mint: std::option::Option<&str>,
token_b_mint: std::option::Option<&str>,
) -> crate::DexDecodedEventDto {
return crate::DexDecodedEventDto::new(
1,
Some(10),
"meteora_dlmm".to_string(),
crate::METEORA_DLMM_PROGRAM_ID.to_string(),
event_kind.to_string(),
Some("pool".to_string()),
None,
token_a_mint.map(|value| return value.to_string()),
token_b_mint.map(|value| return value.to_string()),
None,
"{}".to_string(),
);
}
#[test]
fn ledger_certifies_one_effective_event_with_instruction_audits() {
let events = vec![
make_decoded_event("meteora_dlmm.swap", Some("mint-a"), Some("mint-b")),
make_decoded_event("meteora_dlmm.instruction_audit", None, None),
make_decoded_event("meteora_dlmm.instruction_audit", None, None),
];
let ledger = super::build_success_dex_decode_replay_ledger(1, "sig", events.as_slice())
.expect("ledger must build");
assert_eq!(ledger.event_count, 3);
assert_eq!(ledger.distinct_token_mint_count, 2);
assert!(!ledger.force_replay_required);
assert_eq!(ledger.certainty, crate::DexDecodeReplayLedgerDto::CERTAINTY_SURE);
assert!(ledger.can_skip_decode());
}
#[test]
fn ledger_keeps_multiple_effective_events_unsafe() {
let events = vec![
make_decoded_event("meteora_dlmm.swap", Some("mint-a"), Some("mint-b")),
make_decoded_event("meteora_dlmm.swap", Some("mint-a"), Some("mint-b")),
make_decoded_event("meteora_dlmm.instruction_audit", None, None),
];
let ledger = super::build_success_dex_decode_replay_ledger(1, "sig", events.as_slice())
.expect("ledger must build");
assert_eq!(ledger.event_count, 3);
assert!(ledger.force_replay_required);
assert_eq!(ledger.certainty, crate::DexDecodeReplayLedgerDto::CERTAINTY_UNSAFE);
assert!(!ledger.can_skip_decode());
}
}