Skip to content

feat: forward original bincoded transaction#991

Open
bmuddha wants to merge 3 commits intobmuddha/accountsdb/checksumfrom
bmuddha/transaction/original-bincode
Open

feat: forward original bincoded transaction#991
bmuddha wants to merge 3 commits intobmuddha/accountsdb/checksumfrom
bmuddha/transaction/original-bincode

Conversation

@bmuddha
Copy link
Collaborator

@bmuddha bmuddha commented Feb 20, 2026

Summary

Keep the original bincoded body of transaction along with sanitized
version. Forward it to the scheduler, so it can be reused for the
purposes of replication and future ledger persistence.

Compatibility

  • No breaking changes

Testing

  • all tests are passing

Checklist

Summary by CodeRabbit

  • Refactor
    • Transaction flow now preserves original encoded bytes across preparation, submission, scheduling, and execution; encoding failures are logged and abort scheduling.
  • New Features
    • Responses for transaction submission now deliver a serialized signature payload.
    • Support added for callers to submit pre-encoded transactions.
  • Bug Fixes
    • Signature extraction, replay checks, and account preparation updated for the nested/encoded transaction shape.
  • Style
    • Minor tracing/log adjustments for clearer diagnostics.

Copy link
Collaborator Author

bmuddha commented Feb 20, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds WithEncoded to carry pre-encoded bincode bytes alongside transactions, updates SanitizeableTransaction with sanitize_with_encoded, and extends ProcessableTransaction to include an encoded: Option<Vec> field. prepare_transaction now returns WithEncoded, and callers (schedulers, aperture handlers, account-cloner, tickers, scheduled commits) were updated to accept and propagate the encoded bytes. bincode and serde were added as dependencies to support encoding.

Assessment against linked issues

Objective Addressed Explanation
ProcessableTransaction includes original_bincode: Option<Vec) [#959] Field was added as encoded: Option<Vec<u8>>, not named original_bincode.
Capture and attach raw bytes at entry point (prepare_transaction) [#959]
Propagate through SanitizeableTransaction trait or wrapper type [#959]

Out-of-scope changes

Code Change Explanation
Removal of scheduling/executing trace logs (magicblock-aperture/src/requests/http/send_transaction.rs) Observability/tracing code was removed; unrelated to preserving original bincode payloads.
Minor debug/tracing formatting change in account resolution (magicblock-aperture/src/requests/http/mod.rs) Logging macro formatting adjusted; not part of payload-preservation objective.

Suggested reviewers

  • thlorenz
  • GabrielePicco
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bmuddha/transaction/original-bincode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
magicblock-aperture/src/requests/http/simulate_transaction.rs (1)

53-57: ⚠️ Potential issue | 🟠 Major

simulate() receives stale encoded bytes — pass transaction.txn instead.

When replace_recent_blockhash=true, prepare_transaction replaces the blockhash in the deserialized transaction before constructing WithEncoded. The encoded field therefore contains the original wire bytes (with the client's blockhash), while transaction.txn holds the modified SanitizedTransaction. Passing the full WithEncoded<SanitizedTransaction> to simulate() propagates this inconsistency: TransactionSchedulerHandle::send calls sanitize_with_encoded(true), which returns Some(stale_bytes), so ProcessableTransaction { encoded: Some(stale_bytes), ... } reaches the scheduler with bytes that don't match the transaction being simulated.

If any downstream consumer reads encoded on a simulation ProcessableTransaction, it will operate on the wrong bytes (wrong blockhash).

The fix is to pass only the inner sanitized transaction so encoded is None in the resulting ProcessableTransaction:

🐛 Proposed fix
-        let result = self
-            .transactions_scheduler
-            .simulate(transaction)
-            .await
-            .map_err(RpcError::transaction_simulation)?;
+        let result = self
+            .transactions_scheduler
+            .simulate(transaction.txn)
+            .await
+            .map_err(RpcError::transaction_simulation)?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-aperture/src/requests/http/simulate_transaction.rs` around lines
53 - 57, The call to transactions_scheduler.simulate(...) is passing a
WithEncoded<SanitizedTransaction> which contains stale encoded bytes (the
original client wire bytes) because prepare_transaction replaced the blockhash
on the deserialized txn but left encoded unchanged; change the call to pass the
inner sanitized transaction (transaction.txn) instead so the scheduler receives
a transaction with encoded == None and cannot propagate stale bytes—locate the
simulate(...) invocation in simulate_transaction.rs and replace the WithEncoded
argument with transaction.txn, leaving all other error mapping
(RpcError::transaction_simulation) intact; this ensures
TransactionSchedulerHandle::send and its sanitize_with_encoded(true) path
produce a ProcessableTransaction without stale encoded bytes.
magicblock-aperture/src/requests/http/send_transaction.rs (1)

17-35: ⚠️ Potential issue | 🟡 Minor

signature span field is declared but never recorded.

fields(signature = tracing::field::Empty) creates a lazy span field that must be explicitly filled via Span::current().record(...). Since that call was removed (per PR summary), the field is always empty in traces — a regression in observability for this hot path.

🔧 Proposed fix — record signature after extraction
+use tracing::Span;
 
 #[instrument(skip(self, request), fields(signature = tracing::field::Empty))]
 pub(crate) async fn send_transaction(
     &self,
     request: &mut JsonRequest,
 ) -> HandlerResult {
     // ...
     let signature = *transaction.txn.signature();
+    Span::current().record("signature", tracing::field::display(&signature));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-aperture/src/requests/http/send_transaction.rs` around lines 17 -
35, The span field "signature" declared on send_transaction (fields(signature =
tracing::field::Empty)) is never recorded; after extracting signature (the
signature variable) call Span::current().record(...) to populate that field (use
tracing::field::display(...) or the string form of signature) so the span
contains the actual signature value; ensure this happens right after let
signature = *transaction.txn.signature() within the send_transaction function.
magicblock-aperture/src/requests/http/mod.rs (1)

176-215: ⚠️ Potential issue | 🟠 Major

WithEncoded invariant broken when replace_blockhash=true.

After the blockhash replacement at line 201–204, transaction.message holds the new blockhash but encoded still contains the original wire bytes. The returned WithEncoded { txn, encoded } therefore has txn.recent_blockhash != bincode::deserialize::<VersionedTransaction>(&encoded).recent_blockhash. The doc comment acknowledges bytes are "unused" for simulation, but the struct conveys no such contract — encoded looks like it matches txn to any future reader.

The downstream impact is tracked in the simulate_transaction.rs comment above. The fix there (passing transaction.txn to simulate()) is sufficient, but you may also want to tighten the invariant here by only constructing WithEncoded when bytes are guaranteed consistent:

💡 Alternative — conditionally preserve encoded bytes
-        let txn = transaction.sanitize(sigverify)?;
-        Ok(WithEncoded { txn, encoded })
+        let txn = transaction.sanitize(sigverify)?;
+        // Only preserve bytes when they still correspond to the sanitized txn
+        // (i.e., the blockhash was not replaced)
+        if replace_blockhash {
+            Ok(WithEncoded { txn, encoded: Vec::new() })
+        } else {
+            Ok(WithEncoded { txn, encoded })
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-aperture/src/requests/http/mod.rs` around lines 176 - 215,
prepare_transaction currently mutates transaction.message when replace_blockhash
is true but leaves `encoded` as the original wire bytes, breaking the invariant
that `encoded` matches the returned `txn`; fix this in prepare_transaction by
reserializing the modified `transaction` into `encoded` after calling
set_recent_blockhash (e.g. call
bincode::serialize(&transaction).map_err(RpcError::invalid_params) and assign to
`encoded`) so that the returned WithEncoded { txn, encoded } stays consistent
(alternatively, if you intentionally don't want to preserve bytes for
simulations, return an empty/absent encoded only when replace_blockhash is true,
but prefer reserializing to maintain the invariant).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@magicblock-core/src/link/transactions.rs`:
- Around line 169-172: The bincode serialization failure in with_encoded is
incorrectly mapped to TransactionError::SanitizeFailure; update the error
handling to either (preferred) add a new enum variant like
TransactionError::SerializationError (or TransactionError::InvalidTransaction)
in the TransactionError definition and map bincode::serialize errors to that
variant, updating all match/uses accordingly, or (if adding a variant is not
possible now) add a clear inline comment above the map_err call explaining that
serialization failures are being mapped to SanitizeFailure as a deliberate
substitution and why; reference the with_encoded function and the WithEncoded
result wrapper when making the change.
- Around line 164-173: Remove the dead helper function by deleting the
with_encoded<T> function (the block that calls bincode::serialize, maps errors
to TransactionError::SanitizeFailure, and returns WithEncoded { txn, encoded });
also remove any now-unused imports or references introduced solely for that
function (e.g., bincode::serialize, Serialize bound, and WithEncoded if it is
only used by this helper) so there are no lingering unused symbols.

---

Outside diff comments:
In `@magicblock-aperture/src/requests/http/mod.rs`:
- Around line 176-215: prepare_transaction currently mutates transaction.message
when replace_blockhash is true but leaves `encoded` as the original wire bytes,
breaking the invariant that `encoded` matches the returned `txn`; fix this in
prepare_transaction by reserializing the modified `transaction` into `encoded`
after calling set_recent_blockhash (e.g. call
bincode::serialize(&transaction).map_err(RpcError::invalid_params) and assign to
`encoded`) so that the returned WithEncoded { txn, encoded } stays consistent
(alternatively, if you intentionally don't want to preserve bytes for
simulations, return an empty/absent encoded only when replace_blockhash is true,
but prefer reserializing to maintain the invariant).

In `@magicblock-aperture/src/requests/http/send_transaction.rs`:
- Around line 17-35: The span field "signature" declared on send_transaction
(fields(signature = tracing::field::Empty)) is never recorded; after extracting
signature (the signature variable) call Span::current().record(...) to populate
that field (use tracing::field::display(...) or the string form of signature) so
the span contains the actual signature value; ensure this happens right after
let signature = *transaction.txn.signature() within the send_transaction
function.

In `@magicblock-aperture/src/requests/http/simulate_transaction.rs`:
- Around line 53-57: The call to transactions_scheduler.simulate(...) is passing
a WithEncoded<SanitizedTransaction> which contains stale encoded bytes (the
original client wire bytes) because prepare_transaction replaced the blockhash
on the deserialized txn but left encoded unchanged; change the call to pass the
inner sanitized transaction (transaction.txn) instead so the scheduler receives
a transaction with encoded == None and cannot propagate stale bytes—locate the
simulate(...) invocation in simulate_transaction.rs and replace the WithEncoded
argument with transaction.txn, leaving all other error mapping
(RpcError::transaction_simulation) intact; this ensures
TransactionSchedulerHandle::send and its sanitize_with_encoded(true) path
produce a ProcessableTransaction without stale encoded bytes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
magicblock-aperture/src/requests/http/mod.rs (2)

186-199: 🧹 Nitpick | 🔵 Trivial

Naming of encoded local variable may mislead future readers

The variable encoded (line 186) holds bytes obtained by decoding the base58/base64 client string. They are indeed the bincode form, but the flow — decode base58/base64 → call the result encodedbincode::deserialize(&encoded) — reads counter-intuitively. The former name decoded (per the AI summary) was more accurate at the point of assignment, while bincode_bytes or wire_bytes would be unambiguous in both contexts.

This is purely a readability nit; no behaviour is affected.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-aperture/src/requests/http/mod.rs` around lines 186 - 199, Rename
the local variable currently named `encoded` (used in the match on
`UiTransactionEncoding` and passed to `bincode::deserialize`) to a clearer name
such as `decoded`, `bincode_bytes`, or `wire_bytes` so the flow "decode
base58/base64 → deserialize bincode" reads unambiguously; update all references
(the match arms producing the Vec<u8> and the subsequent
`bincode::deserialize(&encoded)` call that constructs the
`VersionedTransaction`) to use the new identifier.

176-215: ⚠️ Potential issue | 🟡 Minor

WithEncoded invariant violated when replace_blockhash=true

When replace_blockhash=true, transaction.message is mutated (new blockhash set) before sanitization, but encoded still holds the original wire bytes. The returned WithEncoded { txn, encoded } is therefore inconsistent: deserializing encoded yields a transaction with a different blockhash than txn.message().recent_blockhash().

The doc comment acknowledges "bytes are unused" for simulation, and today's callers (simulate_transaction.rs) do correctly drop encoded. However, this bakes a broken invariant into the return type: WithEncoded implies encoded is the canonical serialized form of txn, which is false here. Any future caller that reaches for encoded in the simulation path will silently get stale bytes.

Consider either:

  • Returning Option<Vec<u8>> for the encoded field (or None when replace_blockhash=true), or
  • Splitting this into two functions so the simulation path never produces a WithEncoded at all.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-aperture/src/requests/http/mod.rs` around lines 176 - 215, The
prepare_transaction function currently returns WithEncoded { txn, encoded } even
when replace_blockhash=true, violating the invariant that encoded serializes
txn; to fix, change the return to omit or nullify encoded for the simulation
path: update WithEncoded usage or type so its encoded field is Option<Vec<u8>>
(or change prepare_transaction to return Result<SanitizedTransaction> for
replace_blockhash=true and Result<WithEncoded> otherwise), ensure
prepare_transaction (and callers like simulate_transaction.rs) construct
WithEncoded only when encoded matches txn (i.e., when replace_blockhash is
false) and return None/absent encoded when the blockhash was replaced before
sanitize; adjust signatures and call sites to reflect the new
optional/alternative return so no stale encoded bytes are exposed.
♻️ Duplicate comments (1)
magicblock-accounts/src/scheduled_commits_processor.rs (1)

204-208: Same misleading "64KB" comment as in magicblock-api/src/tickers.rs — see note there.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-accounts/src/scheduled_commits_processor.rs` around lines 204 -
208, The comment beside the with_encoded(intent_sent_transaction) match is
misleading (mentions "64KB" constraint); update the comment and/or log to
accurately reflect the real reason for bincode failure: either remove the
incorrect "all intent transactions are smaller than 64KB by construction" clause
or replace it with a factual note (e.g., "unexpected bincode serialization
failure — should be unreachable under normal inputs"); keep the error! call
as-is (error!("Failed to bincode intent transaction");) and ensure the code
references the same symbols (with_encoded and intent_sent_transaction) so the
comment now correctly documents why this branch is considered unreachable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@magicblock-aperture/src/requests/http/request_airdrop.rs`:
- Around line 38-40: The code currently constructs SerdeSignature from
txn.signatures.first().cloned().unwrap_or_default(), which silently returns an
all-zero signature if the invariant is violated; change this to fail hard and
surface the error instead: in the scope that builds the signature (referencing
SerdeSignature and txn.signatures), replace unwrap_or_default() with an explicit
check or expect() that returns an Err (or propagates with ?/bail!) when
txn.signatures.first() is None so the handler returns a clear error rather than
a zero signature.

In `@magicblock-api/src/tickers.rs`:
- Around line 93-97: The comment incorrectly attributes infallibility of
bincode::serialize to a 64KB size guarantee; instead update the comment and log
to state that serialization should not fail because the Transaction type (and
all its fields) implements Serialize, not because of any size limit. Locate the
with_encoded(tx) call and its surrounding comment/error (the block that
currently says "Unreachable case, all schedule commit txns are smaller than 64KB
by construction" and the error!("Failed to bincode intent transaction")),
replace the explanatory text to mention the Serialize invariant (or remove the
unreachable claim) and adjust the log message to reflect an unexpected
serialization failure rather than a size-related one; apply the same fix in the
analogous spot in scheduled_commits_processor.rs.

---

Outside diff comments:
In `@magicblock-aperture/src/requests/http/mod.rs`:
- Around line 186-199: Rename the local variable currently named `encoded` (used
in the match on `UiTransactionEncoding` and passed to `bincode::deserialize`) to
a clearer name such as `decoded`, `bincode_bytes`, or `wire_bytes` so the flow
"decode base58/base64 → deserialize bincode" reads unambiguously; update all
references (the match arms producing the Vec<u8> and the subsequent
`bincode::deserialize(&encoded)` call that constructs the
`VersionedTransaction`) to use the new identifier.
- Around line 176-215: The prepare_transaction function currently returns
WithEncoded { txn, encoded } even when replace_blockhash=true, violating the
invariant that encoded serializes txn; to fix, change the return to omit or
nullify encoded for the simulation path: update WithEncoded usage or type so its
encoded field is Option<Vec<u8>> (or change prepare_transaction to return
Result<SanitizedTransaction> for replace_blockhash=true and Result<WithEncoded>
otherwise), ensure prepare_transaction (and callers like
simulate_transaction.rs) construct WithEncoded only when encoded matches txn
(i.e., when replace_blockhash is false) and return None/absent encoded when the
blockhash was replaced before sanitize; adjust signatures and call sites to
reflect the new optional/alternative return so no stale encoded bytes are
exposed.

---

Duplicate comments:
In `@magicblock-accounts/src/scheduled_commits_processor.rs`:
- Around line 204-208: The comment beside the
with_encoded(intent_sent_transaction) match is misleading (mentions "64KB"
constraint); update the comment and/or log to accurately reflect the real reason
for bincode failure: either remove the incorrect "all intent transactions are
smaller than 64KB by construction" clause or replace it with a factual note
(e.g., "unexpected bincode serialization failure — should be unreachable under
normal inputs"); keep the error! call as-is (error!("Failed to bincode intent
transaction");) and ensure the code references the same symbols (with_encoded
and intent_sent_transaction) so the comment now correctly documents why this
branch is considered unreachable.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0168241 and 9edc6c4.

📒 Files selected for processing (6)
  • magicblock-account-cloner/src/lib.rs
  • magicblock-accounts/src/scheduled_commits_processor.rs
  • magicblock-aperture/src/requests/http/mod.rs
  • magicblock-aperture/src/requests/http/request_airdrop.rs
  • magicblock-aperture/src/requests/http/simulate_transaction.rs
  • magicblock-api/src/tickers.rs

@bmuddha bmuddha marked this pull request as ready for review February 26, 2026 14:15
@bmuddha bmuddha self-assigned this Feb 26, 2026
@bmuddha bmuddha force-pushed the bmuddha/accountsdb/checksum branch from 3ed3c71 to 25651e8 Compare March 1, 2026 17:18
@bmuddha bmuddha force-pushed the bmuddha/transaction/original-bincode branch from 9edc6c4 to 378e32d Compare March 1, 2026 17:18
@bmuddha bmuddha force-pushed the bmuddha/accountsdb/checksum branch from 25651e8 to 5f00021 Compare March 2, 2026 10:41
@bmuddha bmuddha force-pushed the bmuddha/transaction/original-bincode branch 2 times, most recently from 618b99c to 26235a1 Compare March 3, 2026 18:28
@bmuddha bmuddha force-pushed the bmuddha/accountsdb/checksum branch 2 times, most recently from 1d4ffb8 to 1bae5f8 Compare March 4, 2026 12:00
@bmuddha bmuddha force-pushed the bmuddha/transaction/original-bincode branch from 26235a1 to b8d302a Compare March 4, 2026 12:00
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@magicblock-accounts/src/scheduled_commits_processor.rs`:
- Around line 204-210: The current early return when
with_encoded(intent_sent_transaction) fails prevents sent-commit signaling;
change the logic to fall back to executing the original intent_sent_transaction
unencoded: attempt with_encoded(intent_sent_transaction) and if it Ok(txn) use
that, otherwise log a warning and set txn to the original
intent_sent_transaction (clone or reference as needed) and continue to call
internal_transaction_scheduler.execute(txn). Ensure the fallback preserves the
same error handling path so sent-commit signaling still occurs after execute.

In `@magicblock-aperture/src/requests/http/simulate_transaction.rs`:
- Around line 55-56: The simulation call is discarding the original bincode
bytes by passing transaction.txn; preserve the encoded wrapper produced by
prepare_transaction by passing the full wrapper instead. Update the
.simulate(...) invocation to pass the whole transaction wrapper (the variable
named transaction returned from prepare_transaction) rather than transaction.txn
so the encoded payload flows through the scheduler to downstream components
(refer to prepare_transaction and the simulate method).

In `@magicblock-api/src/tickers.rs`:
- Around line 93-99: The current pre-encode path returns early on
with_encoded(tx) failure which skips processing; instead, when with_encoded(...)
returns Err, log a warning and fall back to calling
transaction_scheduler.execute with the original raw transaction (rather than
returning). Preserve the original transaction variable (e.g., rename the encoded
result to encoded_tx or keep raw_tx) so that if with_encoded fails you can still
call transaction_scheduler.execute(raw_tx). Keep the existing error logging for
execute(...).await and only treat encode failure as non-fatal fallback.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a10d0080-11e9-413a-91fa-b2c142170fa3

📥 Commits

Reviewing files that changed from the base of the PR and between 9edc6c4 and b8d302a.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • test-integration/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • magicblock-account-cloner/src/lib.rs
  • magicblock-accounts/src/scheduled_commits_processor.rs
  • magicblock-aperture/src/requests/http/mod.rs
  • magicblock-aperture/src/requests/http/request_airdrop.rs
  • magicblock-aperture/src/requests/http/send_transaction.rs
  • magicblock-aperture/src/requests/http/simulate_transaction.rs
  • magicblock-api/src/tickers.rs
  • magicblock-core/Cargo.toml
  • magicblock-core/src/link/transactions.rs
  • magicblock-processor/src/scheduler/tests.rs

@bmuddha bmuddha force-pushed the bmuddha/transaction/original-bincode branch from b8d302a to 1d366ad Compare March 5, 2026 16:55
@bmuddha bmuddha force-pushed the bmuddha/accountsdb/checksum branch from 1bae5f8 to 2a14e70 Compare March 5, 2026 16:55
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (4)
magicblock-accounts/src/scheduled_commits_processor.rs (1)

204-209: ⚠️ Potential issue | 🟠 Major

Don’t abort sent-commit signaling when pre-encoding fails.

Line 204 returns early on with_encoded(...) failure, so the scheduler execution path is skipped. Since encoded bytes are optional, this should fall back to executing the raw transaction.

Suggested patch
-        let Ok(txn) = with_encoded(intent_sent_transaction) else {
-            // Unreachable case, all intent transactions are smaller than 64KB by construction
-            error!("Failed to bincode intent transaction");
-            return;
-        };
-        match internal_transaction_scheduler.execute(txn).await {
+        let exec_result = match with_encoded(intent_sent_transaction.clone()) {
+            Ok(txn) => internal_transaction_scheduler.execute(txn).await,
+            Err(err) => {
+                error!(
+                    error = ?err,
+                    "Failed to pre-encode intent transaction; falling back to unencoded transaction"
+                );
+                internal_transaction_scheduler
+                    .execute(intent_sent_transaction)
+                    .await
+            }
+        };
+        match exec_result {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-accounts/src/scheduled_commits_processor.rs` around lines 204 -
209, The current early return when with_encoded(intent_sent_transaction) fails
aborts the sent-commit signaling; instead, if with_encoded(...) returns Err,
fall back to executing the raw intent_sent_transaction payload via
internal_transaction_scheduler.execute so the scheduling path still runs. Update
the block around with_encoded(intent_sent_transaction) to try using the encoded
txn when Ok(txn) and on Err call internal_transaction_scheduler.execute with the
original intent_sent_transaction (or its unencoded representation), ensuring
error logging remains but no early return prevents scheduling.
magicblock-aperture/src/requests/http/simulate_transaction.rs (1)

55-56: ⚠️ Potential issue | 🟠 Major

Don’t unwrap away encoded bytes before scheduling simulation.

Line 55 passes transaction.txn, which drops the original encoded payload captured by prepare_transaction. Pass the wrapper so sanitize_with_encoded can forward bytes consistently.

Suggested patch
-            .simulate(transaction.txn)
+            .simulate(transaction)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-aperture/src/requests/http/simulate_transaction.rs` around lines
55 - 56, The call to .simulate(...) is passing transaction.txn which discards
the encoded payload prepared by prepare_transaction; instead pass the full
wrapper (the transaction struct/value returned by prepare_transaction) so
sanitize_with_encoded can access and forward the original encoded bytes
consistently—locate the simulate invocation (the .simulate(...) chain) and
replace the argument transaction.txn with the transaction wrapper variable (the
value returned from prepare_transaction) so downstream sanitize_with_encoded
receives the wrapper rather than raw/decoded bytes.
magicblock-core/src/link/transactions.rs (1)

170-171: 🧹 Nitpick | 🔵 Trivial

Clarify why serialization failures are mapped to SanitizeFailure.

Line 170–171 maps a bincode serialization error to TransactionError::SanitizeFailure, which is non-obvious and easy to misread during debugging. Please document this substitution inline (or route through a more precise error path if one exists).

Suggested patch
-    let encoded = bincode::serialize(&txn)
-        .map_err(|_| TransactionError::SanitizeFailure)?;
+    let encoded = bincode::serialize(&txn)
+        // TransactionError has no serialization-specific variant here;
+        // use SanitizeFailure as the closest existing error category.
+        .map_err(|_| TransactionError::SanitizeFailure)?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-core/src/link/transactions.rs` around lines 170 - 171, The
bincode::serialize(&txn) call maps any serialization error to
TransactionError::SanitizeFailure, which is confusing; either add an inline
comment next to the serialization call explaining why a serialization failure is
considered a sanitize failure for txn (e.g., serialization indicates invalid
transaction structure after sanitization), or change the error mapping to a more
precise variant (create/return TransactionError::SerializationFailure or wrap
the original bincode error) and preserve the underlying error info; update the
map_err usage around the encoded = bincode::serialize(&txn) expression and
reference TransactionError::SanitizeFailure and the txn variable when making the
change.
magicblock-api/src/tickers.rs (1)

93-99: ⚠️ Potential issue | 🟠 Major

Use raw-transaction fallback when pre-encoding fails.

Line 93–97 returns early if with_encoded(...) fails, which skips scheduled-commit acceptance entirely. Treat pre-encoding as best-effort and continue with the unencoded transaction.

Suggested patch
-    let Ok(tx) = with_encoded(tx) else {
-        // Unreachable case, all schedule commit txns are smaller than 64KB by construction
-        error!("Failed to bincode intent transaction");
-        return;
-    };
-    if let Err(err) = transaction_scheduler.execute(tx).await {
+    let exec_result = match with_encoded(tx.clone()) {
+        Ok(tx) => transaction_scheduler.execute(tx).await,
+        Err(err) => {
+            error!(
+                error = ?err,
+                "Failed to pre-encode scheduled commits transaction; falling back to unencoded transaction"
+            );
+            transaction_scheduler.execute(tx).await
+        }
+    };
+    if let Err(err) = exec_result {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-api/src/tickers.rs` around lines 93 - 99, The current code returns
early when with_encoded(tx) fails, skipping scheduled-commit acceptance; instead
treat pre-encoding as best‑effort: when with_encoded(tx) returns Err, log a
warning and continue using the original unencoded transaction value for the call
to transaction_scheduler.execute(tx). Update the branch around with_encoded and
the call site (symbols: with_encoded and transaction_scheduler.execute) so the
execute path accepts the raw tx on encoding failure (ensuring the tx variable is
the unencoded transaction in that branch) and only aborts on execute errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@magicblock-accounts/src/scheduled_commits_processor.rs`:
- Around line 204-209: The current early return when
with_encoded(intent_sent_transaction) fails aborts the sent-commit signaling;
instead, if with_encoded(...) returns Err, fall back to executing the raw
intent_sent_transaction payload via internal_transaction_scheduler.execute so
the scheduling path still runs. Update the block around
with_encoded(intent_sent_transaction) to try using the encoded txn when Ok(txn)
and on Err call internal_transaction_scheduler.execute with the original
intent_sent_transaction (or its unencoded representation), ensuring error
logging remains but no early return prevents scheduling.

In `@magicblock-aperture/src/requests/http/simulate_transaction.rs`:
- Around line 55-56: The call to .simulate(...) is passing transaction.txn which
discards the encoded payload prepared by prepare_transaction; instead pass the
full wrapper (the transaction struct/value returned by prepare_transaction) so
sanitize_with_encoded can access and forward the original encoded bytes
consistently—locate the simulate invocation (the .simulate(...) chain) and
replace the argument transaction.txn with the transaction wrapper variable (the
value returned from prepare_transaction) so downstream sanitize_with_encoded
receives the wrapper rather than raw/decoded bytes.

In `@magicblock-api/src/tickers.rs`:
- Around line 93-99: The current code returns early when with_encoded(tx) fails,
skipping scheduled-commit acceptance; instead treat pre-encoding as best‑effort:
when with_encoded(tx) returns Err, log a warning and continue using the original
unencoded transaction value for the call to transaction_scheduler.execute(tx).
Update the branch around with_encoded and the call site (symbols: with_encoded
and transaction_scheduler.execute) so the execute path accepts the raw tx on
encoding failure (ensuring the tx variable is the unencoded transaction in that
branch) and only aborts on execute errors.

In `@magicblock-core/src/link/transactions.rs`:
- Around line 170-171: The bincode::serialize(&txn) call maps any serialization
error to TransactionError::SanitizeFailure, which is confusing; either add an
inline comment next to the serialization call explaining why a serialization
failure is considered a sanitize failure for txn (e.g., serialization indicates
invalid transaction structure after sanitization), or change the error mapping
to a more precise variant (create/return TransactionError::SerializationFailure
or wrap the original bincode error) and preserve the underlying error info;
update the map_err usage around the encoded = bincode::serialize(&txn)
expression and reference TransactionError::SanitizeFailure and the txn variable
when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e11b47af-4a6c-4547-ad08-b26ba9699aca

📥 Commits

Reviewing files that changed from the base of the PR and between b8d302a and 1d366ad.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • test-integration/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • magicblock-account-cloner/src/lib.rs
  • magicblock-accounts/src/scheduled_commits_processor.rs
  • magicblock-aperture/src/requests/http/mod.rs
  • magicblock-aperture/src/requests/http/request_airdrop.rs
  • magicblock-aperture/src/requests/http/send_transaction.rs
  • magicblock-aperture/src/requests/http/simulate_transaction.rs
  • magicblock-api/src/tickers.rs
  • magicblock-core/Cargo.toml
  • magicblock-core/src/link/transactions.rs
  • magicblock-processor/src/scheduler/tests.rs

@bmuddha bmuddha force-pushed the bmuddha/accountsdb/checksum branch from 2a14e70 to 93c53dc Compare March 6, 2026 08:12
@bmuddha bmuddha force-pushed the bmuddha/transaction/original-bincode branch from 1d366ad to fad4f15 Compare March 6, 2026 08:12
bmuddha added 3 commits March 6, 2026 12:19
keep original bincoded transaction along with
the sanitized version, this can will be used
by future ledger and replicator implementations
@bmuddha bmuddha force-pushed the bmuddha/transaction/original-bincode branch from fad4f15 to 924a009 Compare March 6, 2026 08:19
@bmuddha bmuddha force-pushed the bmuddha/accountsdb/checksum branch from 93c53dc to 6e4f626 Compare March 6, 2026 08:19
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
magicblock-aperture/src/requests/http/simulate_transaction.rs (1)

53-56: ⚠️ Potential issue | 🟠 Major

Pass the wrapper to simulate.

Line 55 strips the original bincode captured by prepare_transaction, so the simulation path no longer preserves the encoded payload.

🐛 Suggested fix
         let result = self
             .transactions_scheduler
-            .simulate(transaction.txn)
+            .simulate(transaction)
             .await
             .map_err(RpcError::transaction_simulation)?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-aperture/src/requests/http/simulate_transaction.rs` around lines
53 - 56, The simulate call is currently passing transaction.txn which strips the
original bincode prepared by prepare_transaction; change the call to pass the
full wrapper produced by prepare_transaction (the variable holding the wrapped
transaction used earlier in this function) into
transactions_scheduler.simulate(...) so the encoded payload is preserved by
simulate and the simulation path receives the original bincode wrapper.
magicblock-api/src/tickers.rs (1)

93-99: ⚠️ Potential issue | 🟠 Major

Fall back to the raw transaction when pre-encoding fails.

Returning here skips accept_scheduled_commits entirely, even though the scheduler can still process the transaction without encoded.

♻️ Suggested fix
-    let Ok(tx) = with_encoded(tx) else {
-        // Unreachable case, all schedule commit txns are smaller than 64KB by construction
-        error!("Failed to bincode intent transaction");
-        return;
-    };
-    if let Err(err) = transaction_scheduler.execute(tx).await {
+    let exec_result = match with_encoded(tx.clone()) {
+        Ok(encoded_tx) => transaction_scheduler.execute(encoded_tx).await,
+        Err(err) => {
+            warn!(
+                error = ?err,
+                "Failed to pre-encode scheduled commits transaction; falling back to raw transaction"
+            );
+            transaction_scheduler.execute(tx).await
+        }
+    };
+    if let Err(err) = exec_result {
         error!(error = ?err, "Failed to accept scheduled commits");
         return;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-api/src/tickers.rs` around lines 93 - 99, The code currently
returns early when with_encoded(tx) fails, skipping the scheduler; instead,
change the flow so that if with_encoded(tx) errors you log the failure and fall
back to using the original raw transaction for scheduling. Concretely, update
the match around with_encoded(tx) (the call to with_encoded and the error log
"Failed to bincode intent transaction") to not return on Err but to proceed
using the original tx value when invoking transaction_scheduler.execute(tx).
Ensure the error case uses the original transaction variable and still calls
transaction_scheduler.execute(tx).
magicblock-accounts/src/scheduled_commits_processor.rs (1)

204-209: ⚠️ Potential issue | 🟠 Major

Keep sent-commit signaling alive on encode failure.

Early-returning here drops the signal transaction after register_scheduled_commit_sent(sent_commit), even though the raw transaction is still usable.

♻️ Suggested fix
-        let Ok(txn) = with_encoded(intent_sent_transaction) else {
-            // Unreachable case, all intent transactions are smaller than 64KB by construction
-            error!("Failed to bincode intent transaction");
-            return;
-        };
-        match internal_transaction_scheduler.execute(txn).await {
+        let exec_result = match with_encoded(intent_sent_transaction.clone()) {
+            Ok(txn) => internal_transaction_scheduler.execute(txn).await,
+            Err(err) => {
+                warn!(
+                    error = ?err,
+                    "Failed to pre-encode sent-commit signal transaction; falling back to raw transaction"
+                );
+                internal_transaction_scheduler
+                    .execute(intent_sent_transaction)
+                    .await
+            }
+        };
+        match exec_result {
             Ok(()) => {
                 debug!("Sent commit signaled")
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-accounts/src/scheduled_commits_processor.rs` around lines 204 -
209, The early return on bincode encode failure (the
with_encoded(intent_sent_transaction) else branch) drops the
previously-registered sent-commit signal; instead, log the encode error but do
not return—fall back to executing the original raw intent_sent_transaction so
register_scheduled_commit_sent(sent_commit) remains effective. Concretely,
replace the else { error!(...); return; } with code that logs the error and sets
txn to a fallback representation (e.g., use the original intent_sent_transaction
or call the scheduler with the raw transaction) before calling
internal_transaction_scheduler.execute(txn).await, keeping the sent-commit
signaling alive.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@magicblock-account-cloner/src/lib.rs`:
- Around line 105-108: send_tx currently forces serialization by calling
with_encoded(tx) before scheduling, which turns an optional/missing encoded into
a hard failure; change send_tx to avoid unconditional with_encoded: extract the
signature as before, and call tx_scheduler.execute with the already-encoded
payload only when Transaction has its encoded field present (or when
with_encoded would succeed), otherwise pass the raw Transaction (or a variant
the scheduler accepts) so a missing encoded does not cause a clone failure;
update references to with_encoded, send_tx, Transaction, and tx_scheduler to
perform a conditional/optional encoding path.

In `@magicblock-aperture/src/requests/http/request_airdrop.rs`:
- Around line 42-44: In requestAirdrop, avoid failing when the optional
pre-encoding (with_encoded(txn)) fails; instead try to pre-encode but fall back
to passing the original Transaction to transactions_scheduler.execute. Update
the call site that currently does
self.transactions_scheduler.execute(with_encoded(txn)?).await? to attempt
with_encoded(txn) and, on Err, call execute with the original txn (i.e., use the
encoded value when Ok or the raw Transaction when Err) so the scheduler still
receives a valid input without turning the optimization into an RPC failure.

---

Duplicate comments:
In `@magicblock-accounts/src/scheduled_commits_processor.rs`:
- Around line 204-209: The early return on bincode encode failure (the
with_encoded(intent_sent_transaction) else branch) drops the
previously-registered sent-commit signal; instead, log the encode error but do
not return—fall back to executing the original raw intent_sent_transaction so
register_scheduled_commit_sent(sent_commit) remains effective. Concretely,
replace the else { error!(...); return; } with code that logs the error and sets
txn to a fallback representation (e.g., use the original intent_sent_transaction
or call the scheduler with the raw transaction) before calling
internal_transaction_scheduler.execute(txn).await, keeping the sent-commit
signaling alive.

In `@magicblock-aperture/src/requests/http/simulate_transaction.rs`:
- Around line 53-56: The simulate call is currently passing transaction.txn
which strips the original bincode prepared by prepare_transaction; change the
call to pass the full wrapper produced by prepare_transaction (the variable
holding the wrapped transaction used earlier in this function) into
transactions_scheduler.simulate(...) so the encoded payload is preserved by
simulate and the simulation path receives the original bincode wrapper.

In `@magicblock-api/src/tickers.rs`:
- Around line 93-99: The code currently returns early when with_encoded(tx)
fails, skipping the scheduler; instead, change the flow so that if
with_encoded(tx) errors you log the failure and fall back to using the original
raw transaction for scheduling. Concretely, update the match around
with_encoded(tx) (the call to with_encoded and the error log "Failed to bincode
intent transaction") to not return on Err but to proceed using the original tx
value when invoking transaction_scheduler.execute(tx). Ensure the error case
uses the original transaction variable and still calls
transaction_scheduler.execute(tx).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b73a5b98-1218-4e73-af7c-f608d1d15cb7

📥 Commits

Reviewing files that changed from the base of the PR and between 1d366ad and fad4f15.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • test-integration/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • magicblock-account-cloner/src/lib.rs
  • magicblock-accounts/src/scheduled_commits_processor.rs
  • magicblock-aperture/src/requests/http/mod.rs
  • magicblock-aperture/src/requests/http/request_airdrop.rs
  • magicblock-aperture/src/requests/http/send_transaction.rs
  • magicblock-aperture/src/requests/http/simulate_transaction.rs
  • magicblock-api/src/tickers.rs
  • magicblock-core/Cargo.toml
  • magicblock-core/src/link/transactions.rs
  • magicblock-processor/src/scheduler/tests.rs

Copy link
Collaborator

@thlorenz thlorenz left a comment

Choose a reason for hiding this comment

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

LGTM aside from the naming nit which we probably should just clarify in a comment in the right place.

) -> RpcResult<SanitizedTransaction> {
let decoded = match encoding {
) -> RpcResult<WithEncoded<SanitizedTransaction>> {
let encoded = match encoding {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Confused by the naming here, it's calling decode but then naming the var encoded


Ok(transaction.sanitize(sigverify)?)
let txn = transaction.sanitize(sigverify)?;
Ok(WithEncoded { txn, encoded })
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again very confusing naming.
It's a bs58 decoded and bincode serialized value.
So maybe calling it serialized may make more sense?


/// Wraps a transaction with its pre-encoded bincode representation.
/// Use for internally-constructed transactions that need encoded bytes.
pub struct WithEncoded<T> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's not pre-encoded, but a bincode serialized value (slight difference).

We don't use bincode::encode/decode, only bincode:de/serialize.
Even though from the docs it looks like there is actually an en/decode.
Very confusing, maybe you can explain (as part of a comment added to the above where the value was named confusingly).

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.

2 participants