Skip to content

feat(mimd-0025/mimd-0027): durable intent execution - #1368

Open
taco-paco wants to merge 137 commits into
masterfrom
feat/mimd-0025/main
Open

feat(mimd-0025/mimd-0027): durable intent execution#1368
taco-paco wants to merge 137 commits into
masterfrom
feat/mimd-0025/main

Conversation

@taco-paco

@taco-paco taco-paco commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR implements both MIMD-0025 and MIMD-0027. Briefly, it introduces Outbox for Intent which allows to restore it execution stage and continue execution from a correct point.

Single Intent Executor split into 3 and which correspond to different starting points of Intents. Those executors are derived from Intent execution stage.

#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub enum ExecutionStage {
    SingleStage(PendingTransaction),
    TwoStage(TwoStageProgress),
}


#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub enum TwoStageProgress {
    Committing(PendingTransaction),
    Finalizing {
        commit: Signature,
        finalize: PendingTransaction,
    },
}

For PendingTransaction logic and use see MIMD-0027.

Apart from pure MIMDs implementation PR extract a common logic into utils and changes file structure for better readability. This PR also removes Persister as we will use Solana Accounts instead of it

Breaking Changes

  • None
  • Yes — migration path described below

Test Plan

Summary by CodeRabbit

  • New Features

    • Added outbox-based intent scheduling and execution flow.
    • Added support for single-stage and two-stage intent processing, including recovery after interruptions.
    • Added improved callback timing and retry handling for long-running transactions.
  • Bug Fixes

    • Improved handling of pending transactions so execution can resume more reliably.
    • Fixed intent acceptance and stage updates to better track on-chain execution progress.
    • Updated transaction size and retry behavior to reduce failed sends.

taco-paco added 30 commits May 27, 2026 16:15
# Conflicts:
#	magicblock-accounts/src/scheduled_commits_processor.rs
#	magicblock-api/src/magic_validator.rs
# Conflicts:
#	magicblock-accounts/src/scheduled_commits_processor.rs
#	magicblock-api/src/magic_validator.rs
#	magicblock-committor-service/src/committor_processor.rs
#	magicblock-committor-service/src/service.rs
#	test-integration/test-committor-service/tests/test_ix_commit_local.rs
# Conflicts:
#	magicblock-api/src/magic_validator.rs
#	magicblock-committor-service/src/committor_processor.rs
@taco-paco
taco-paco marked this pull request as ready for review July 7, 2026 14:15
taco-paco added 3 commits July 8, 2026 11:53
# Conflicts:
#	Cargo.lock
#	test-integration/Cargo.lock
# Conflicts:
#	test-integration/Cargo.lock
@taco-paco taco-paco changed the title feat(mimd-0025): durable intent execution feat(mimd-0025/mimd-0027): durable intent execution Jul 14, 2026
# Conflicts:
#	magicblock-committor-service/src/intent_executor/mod.rs
#	magicblock-committor-service/src/intent_executor/single_stage_executor.rs
#	magicblock-committor-service/src/intent_executor/two_stage_executor.rs
#	magicblock-committor-service/src/intent_executor/utils.rs
#	magicblock-committor-service/src/tasks/task_strategist.rs
#	magicblock-committor-service/src/transaction_preparator/delivery_preparator.rs
#	test-integration/Cargo.lock
#	test-integration/test-committor-service/tests/test_intent_executor.rs
@snawaz

snawaz commented Jul 14, 2026

Copy link
Copy Markdown
Contributor
image

Wow, that's a lot of changes. Does this PR also include heavy refactoring?

Should we strive for smaller PRs so reviews can be done more thoroughly?

# Conflicts:
#	magicblock-api/src/magic_sys_adapter.rs
#	magicblock-committor-service/src/intent_engine/intent_scheduler.rs
#	magicblock-committor-service/src/intent_executor/mod.rs
#	magicblock-committor-service/src/intent_executor/two_stage_executor.rs
#	magicblock-committor-service/src/intent_executor/utils.rs
#	magicblock-committor-service/src/persist/commit_persister.rs
#	magicblock-committor-service/src/tasks/commit_finalize_task.rs
#	magicblock-committor-service/src/tasks/commit_task.rs
#	magicblock-committor-service/src/tasks/task_builder.rs
#	magicblock-committor-service/src/tasks/task_strategist.rs
#	magicblock-committor-service/src/transaction_preparator/delivery_preparator.rs
#	magicblock-metrics/src/metrics/mod.rs
#	test-integration/Cargo.lock
#	test-integration/test-committor-service/tests/test_delivery_preparator.rs
#	test-integration/test-committor-service/tests/test_ix_commit_local.rs
@taco-paco

taco-paco commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Wow, that's a lot of changes. Does this PR also include heavy refactoring?
Should we strive for smaller PRs so reviews can be done more thoroughly?

It does include refactoring. A lot of code that would be otherwise duplicated in different IntentExecutors was extracted into utils. Patching logic was extracted into patcher.rs. Most of them don't have any business logic changes.

The main logic shall be easily reviewable because of centralization in single general functions: stage_execution_loop, execute_single_stage_flow, execute_two_stage_flow.

I agree that would be better to split it off, but it wasn't really planned and is something that happened in the middle of writing code so it would be hard to extract those changes into separate PRs. Will try to forsee this next time

@thlorenz thlorenz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I found several lifecycle issues that look blocking before merge and commented in place except for the below two:

Outside-diff 1

Location: magicblock-committor-service/src/intent_engine/intent_execution_engine.rs:281-290

I could not find a good in-diff anchor for this one. The behavior appears to predate the PR, but it is still worth calling out if we want the new durable outbox design to close this lifecycle gap:

The intent scheduler completes and releases committed pubkeys even when result.inner is an error.
In a two-stage flow, the commit stage may already have been sent/recorded while the finalize stage failed.
Releasing the keys at that point lets later intents touching the same accounts execute before the failed intent is retried, finalized, or marked terminal, which conflicts with per-account intent serialization and recovery safety.

The engine should keep those account keys blocked or move the intent into a durable terminal/retry state before unblocking dependent intents.

Outside-diff 2

Location: magicblock-committor-service/src/tasks/task_strategist.rs:140-156

I could not find a fair in-diff anchor for this one either. The task-count limit was not introduced by this PR, but it still seems critical for the commit pipeline:

The strategist limits single-stage packing by task count only, while each commit/finalize/undelegate task advertises 120,000 CU and assemble_tasks_tx_with_uniqueness_nonce sums those values into one compute-budget limit. A 22-task single-stage transaction can request 2,640,000 CU, and large two-stage commit batches can exceed Solana's 1,400,000 CU transaction cap as well.

The strategist should enforce the transaction-level CU cap when selecting single-stage and two-stage task groups, or split stages so every produced transaction stays below the cap.

@@ -158,17 +157,15 @@ where
}
_ = interval.tick() => {
let accept_result = self

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This worker accepts scheduled intents without checking that the validator is in primary mode.

Since magicblock-api still starts this service during normal startup and the new AcceptScheduleCommits/SetIntentExecutionStage handlers do not guard on CoordinationMode::Primary, replicas can mutate MagicContext/outbox state and start base-layer intent execution even though CoordinationMode::Replica is documented as having no side effects.

Can we gate this accept/schedule loop on primary mode, or otherwise keep the service disabled for replicas?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Valid, thanks, fixed 01c6b28


// Log data
log_sent_commit(invoke_context, &commit);
commit

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

For failed intent execution this returns before closing the outbox PDA, but the code has already removed the entry from SENT_COMMITS.

The committor reports both successes and failures through this path, so a failed intent leaves the durable outbox account open while dropping the local failure report.
After restart it can be rediscovered and retried instead of staying terminally failed.

Can we record the failure and close/consume the outbox atomically, or close first and then surface a durable failure status?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In the world without network errors we have intents that can't fail. If intent failed due to Action error - we patch it and execute until it succeeds. Other than that Commit, Finalize, Undelegate can't fail, if they do we have a problem but we shouldn't close intent.
So not closing intent here is a design choice. If say intent failed due to unknown network reason we should not close it but have it reloaded upon restart or retried until it succeeds.

accepted_intents
});

let intent_bundles= intent_bundles.into_iter().map(OutboxIntentBundle::accepted).collect();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

With the new durable outbox flow, accepted outbox bundles can be fed back into the execution scheduler from both fresh accepts and recovery/rescheduling. The scheduler still only tracks executing intents through committed pubkeys, so standalone action bundles, where get_all_committed_pubkeys() is empty, are never recorded as executing and duplicate intent IDs are not caught while the first action is in flight.

Since standalone actions have external base-layer side effects, can we track executing intent IDs independently of committed pubkeys before scheduling these accepted outbox bundles?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

are never recorded as executing and duplicate intent IDs are not caught while the first action is in flight.

What do you mean by "duplicate intent IDs"? there can't be duplicate intent ids as each intent is +1 from previous one. All OutboxIntent are mapped from ScheduledIntents so thier ids can't contain duplicates

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Restart doesn't do anything as MagicContext and OutboxItntents are accounts in Accounts DB and new intents will just get +1 intent id, still no duplicates

# Conflicts:
#	magicblock-committor-service/src/service.rs
#	test-integration/Cargo.lock
# Conflicts:
#	magicblock-committor-service/src/committor_processor.rs
#	magicblock-committor-service/src/intent_engine/intent_execution_engine.rs
#	magicblock-committor-service/src/intent_executor/error.rs
#	magicblock-committor-service/src/intent_executor/intent_execution_client.rs
#	magicblock-committor-service/src/intent_executor/mod.rs
#	magicblock-committor-service/src/intent_executor/two_stage_executor.rs
#	magicblock-committor-service/src/persist/commit_persister.rs
#	magicblock-committor-service/src/persist/db.rs
#	magicblock-committor-service/src/service.rs
#	magicblock-rpc-client/src/lib.rs
#	magicblock-rpc-client/src/utils.rs
#	test-integration/Cargo.lock
@GabrielePicco
GabrielePicco requested a review from thlorenz July 23, 2026 07:01
@taco-paco taco-paco mentioned this pull request Jul 29, 2026
2 tasks
feat: switched notify and commitsent places. Doesn't break integration
# Conflicts:
#	magicblock-committor-service/src/committor_processor.rs
#	magicblock-committor-service/src/persist/commit_persister.rs
#	magicblock-committor-service/src/persist/db.rs
#	magicblock-committor-service/src/service.rs
#	test-integration/Cargo.lock
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants