feat(mimd-0025/mimd-0027): durable intent execution - #1368
Conversation
# 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
# Conflicts: # Cargo.lock # test-integration/Cargo.lock
# Conflicts: # test-integration/Cargo.lock
# 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
# 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
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 The main logic shall be easily reviewable because of centralization in single general functions: 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 |
# Conflicts: # magicblock-committor-service/src/service.rs
thlorenz
left a comment
There was a problem hiding this comment.
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 | |||
There was a problem hiding this comment.
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?
|
|
||
| // Log data | ||
| log_sent_commit(invoke_context, &commit); | ||
| commit |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
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
…returns none if commitment wasn't reached
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.
For
PendingTransactionlogic 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
Test Plan
Summary by CodeRabbit
New Features
Bug Fixes