Skip to content
Open
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
56 changes: 55 additions & 1 deletion magicblock-committor-service/src/intent_executor/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,20 @@ impl IntentExecutorError {
finalize_signature,
}
}
}

/// Maps a single-stage execution failure to [`IntentExecutorError`].
///
/// `commit_signature` stays `None` so retry semantics remain unchanged for
/// transient send failures. The failed transaction signature is still exposed
/// via [`IntentExecutorError::signatures`].
pub(crate) fn single_stage_finalize_execution_error(
err: TransactionStrategyExecutionError,
) -> IntentExecutorError {
IntentExecutorError::from_finalize_execution_error(err, None)
}

impl IntentExecutorError {
/// True when re-executing the whole intent from scratch may succeed:
/// the failure was transport/RPC-side rather than deterministic.
/// Once a commit landed (two-stage finalize failures) re-execution would
Expand Down Expand Up @@ -168,7 +181,11 @@ impl IntentExecutorError {
err: _,
commit_signature,
finalize_signature,
} => commit_signature.map(|el| (el, *finalize_signature)),
} => match (*commit_signature, *finalize_signature) {
(Some(commit), finalize) => Some((commit, finalize)),
(None, Some(finalize)) => Some((finalize, Some(finalize))),
_ => None,
},
IntentExecutorError::EmptyIntentError
| IntentExecutorError::FailedToFitError
| IntentExecutorError::SignerError(_) => None,
Expand Down Expand Up @@ -678,6 +695,43 @@ mod tests {
assert!(!err.is_transient());
}

#[test]
fn single_stage_finalize_failure_reports_shared_signature() {
let signature = solana_signature::Signature::new_unique();
let err = TransactionStrategyExecutionError::InternalError(
InternalError::MagicBlockRpcClientError(Box::new(
MagicBlockRpcClientError::SentTransactionError(
solana_transaction_error::TransactionError::InstructionError(
0,
solana_instruction::error::InstructionError::Custom(1),
),
signature,
),
)),
);

let wrapped = super::single_stage_finalize_execution_error(err);
assert_eq!(wrapped.signatures(), Some((signature, Some(signature))));
assert!(!wrapped.is_transient());
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

#[test]
fn single_stage_blockhash_not_found_failure_stays_transient() {
let signature = solana_signature::Signature::new_unique();
let err = TransactionStrategyExecutionError::InternalError(
InternalError::MagicBlockRpcClientError(Box::new(
MagicBlockRpcClientError::SentTransactionError(
solana_transaction_error::TransactionError::BlockhashNotFound,
signature,
),
)),
);

let wrapped = super::single_stage_finalize_execution_error(err);
assert_eq!(wrapped.signatures(), Some((signature, Some(signature))));
assert!(wrapped.is_transient());
}

#[test]
fn unrelated_internal_errors_do_not_trigger_single_stage_split() {
let err = TransactionStrategyExecutionError::InternalError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use tracing::{error, instrument};
use crate::{
intent_executor::{
error::{
IntentExecutorError, IntentExecutorResult,
TransactionStrategyExecutionError,
single_stage_finalize_execution_error, IntentExecutorError,
IntentExecutorResult, TransactionStrategyExecutionError,
},
intent_execution_client::IntentExecutionClient,
task_info_fetcher::{CacheTaskInfoFetcher, TaskInfoFetcher},
Expand Down Expand Up @@ -133,13 +133,7 @@ where
}
};

result.map_err(|err| {
IntentExecutorError::from_finalize_execution_error(
err,
// TODO(edwin): shall one stage have same signature for commit & finalize
None,
)
})
result.map_err(single_stage_finalize_execution_error)
}

pub fn has_callbacks(&self) -> bool {
Expand Down