Problem
A transaction submitted twice is deduplicated correctly, but a duplicate submitted after the original settles is never told the outcome. It blocks until its own deadline and reports a timeout for a transaction that executed successfully.
AccountAccessor::create and AccountAccessor::update await the status with an 8-second EXECUTION_TIMEOUT, so a caller that races itself sees Engine("internal error: deadline has elapsed") even though the transaction succeeded.
Reproduction
This is reachable from ordinary validator behavior. magicblock-chainlink's ensure_delegation_action_dependencies force-refreshes a bundle's writable dependencies and issued two identical clones of the same account 0.8 ms apart:
06:40:25.856166 Cloning account pubkey=3jd362SA… slot=62199 owner=Tokenkeg…
06:40:25.857008 Cloning account pubkey=3jd362SA… slot=62199 owner=Tokenkeg…
Identical MagicRoot clone transactions share a signature, so the second was deduplicated. The clone landed at 06:36:39 with err: None, and chainlink gave up at 06:36:47.44, exactly eight seconds later. Chainlink then read that timeout as "post-delegation actions unsatisfiable," discarded the bundle, and undelegated the account instead. This reproduced identically in debug and release builds.
The result depends on timing:
- Original still in flight:
Subscribers::subscribe returns a receiver on the existing channel for that signature, so commit_execution reaches both submitters.
- Original already settled: the terminal broadcast removes the channel. A later duplicate creates a fresh channel that nothing will write to.
- During settlement:
commit_execution caches the status after the terminal broadcast, leaving a narrower window in which a subscriber finds neither a live channel nor a cached result.
Expected
Every duplicate submission should observe the original transaction's outcome without waiting for its own deadline. Settling a transaction should not expose a window with neither a live subscription nor a cached result.
Context
Sequencer::schedule drops the duplicate and returns without notifying subscribers:
// processor/src/sequencer/mod.rs
if !self.replay && !self.state.transactions().append(&txn).await? {
metrics::failed_transaction(FailureKind::SequencerDrop);
return Ok(());
};
append returns Ok(false) when caches.signatures.push finds the signature already present. Nothing is published to subscriptions.signatures for that submission.
The settlement ordering creates the additional window:
// keeper/src/accessor.rs
subs.signatures.send(&commit.signature, &commit.status, true);
self.keeper.caches.signatures.update(&commit.signature, Some(commit.status));
A possible fix is to replay the cached outcome to duplicate subscribers and update the cache before the terminal broadcast. A cached None means the original is still in flight, where the shared channel already serves both submitters.
This was implemented locally against a5e9f2d with regression tests in keeper/src/tests/subscriptions.rs. The workspace did not build standalone at that revision because solana-account failed on a wincode version split, so the tests could not be executed there.
Problem
A transaction submitted twice is deduplicated correctly, but a duplicate submitted after the original settles is never told the outcome. It blocks until its own deadline and reports a timeout for a transaction that executed successfully.
AccountAccessor::createandAccountAccessor::updateawait the status with an 8-secondEXECUTION_TIMEOUT, so a caller that races itself seesEngine("internal error: deadline has elapsed")even though the transaction succeeded.Reproduction
This is reachable from ordinary validator behavior.
magicblock-chainlink'sensure_delegation_action_dependenciesforce-refreshes a bundle's writable dependencies and issued two identical clones of the same account 0.8 ms apart:Identical MagicRoot clone transactions share a signature, so the second was deduplicated. The clone landed at
06:36:39witherr: None, and chainlink gave up at06:36:47.44, exactly eight seconds later. Chainlink then read that timeout as "post-delegation actions unsatisfiable," discarded the bundle, and undelegated the account instead. This reproduced identically in debug and release builds.The result depends on timing:
Subscribers::subscribereturns a receiver on the existing channel for that signature, socommit_executionreaches both submitters.commit_executioncaches the status after the terminal broadcast, leaving a narrower window in which a subscriber finds neither a live channel nor a cached result.Expected
Every duplicate submission should observe the original transaction's outcome without waiting for its own deadline. Settling a transaction should not expose a window with neither a live subscription nor a cached result.
Context
Sequencer::scheduledrops the duplicate and returns without notifying subscribers:appendreturnsOk(false)whencaches.signatures.pushfinds the signature already present. Nothing is published tosubscriptions.signaturesfor that submission.The settlement ordering creates the additional window:
A possible fix is to replay the cached outcome to duplicate subscribers and update the cache before the terminal broadcast. A cached
Nonemeans the original is still in flight, where the shared channel already serves both submitters.This was implemented locally against
a5e9f2dwith regression tests inkeeper/src/tests/subscriptions.rs. The workspace did not build standalone at that revision becausesolana-accountfailed on awincodeversion split, so the tests could not be executed there.