Repository navigation
Support large-account commits in MagicBlock Protocol v2 #191
Description
Activity
Proposed design
One generic preallocate instruction (buffer-kind arg) covering both the
commit_statePDA and the undelegate buffer PDA — fixes commit of large accounts and prevents the corresponding undelegation failure for
the same accounts.Since the 10,240-byte growth cap resets per top-level instruction, a small instruction can grow a DLP-owned PDA in ≤10,240-byte steps, and all steps can be packed into the same transaction as the commit/finalize
(or undelegate), preserving atomicity.New instruction:
PreallocateBuffer(discriminator 28)- Args (borsh, ~13 bytes total incl. discriminator):
kind: enum { CommitState, UndelegateBuffer, DelegatedAccount },target_size: u32. - Accounts:
payer (signer, writable),delegated_account,delegation_record,buffer PDA (writable),commit_record,system_program. - Behavior (idempotent, "move current size toward target by ≤10,240"):
- buffer uninitialized →
create_pdawithmin(target, 10_240); - buffer already DLP-owned → verify PDA derivation, then resize by
min(remaining, 10_240)(direct realloc + rent top-up from payer); - refuses to touch the buffer of an in-flight commit (
commit_recordmust be uninitialized).
- buffer uninitialized →
kind = DelegatedAccount(optional, for the finalize growth case): pre-grows the delegated account itself; gated onpayer == delegation_record.authoritysince it changes observable base-layer state. The
buffer kinds can stay permissionless: the PDAs are DLP-owned and always zero-filled, size is corrected at commit/undelegate time, and all rent is swept to the validator when the buffer is closed.
A validator committing an account of size
Nsendsceil(N / 10_240)preallocate instructions followed by the commit + finalize in one transaction (e.g. 15,524 bytes → 2 chunks).Processor changes
process_commit_state_internal: replacerequire_uninitialized_pda(commit_state_account, ...)with a derivation-only check, then branch — uninitialized →create_pdaat exact size (current behavior,
≤10,240-byte accounts unaffected); already DLP-owned → resize to exactdata_len(shrink uncapped, residual growth ≤10,240). Double-commit protection rests on the unchangedcommit_recorduninitialized check,
which is sufficient sincecommit_state/commit_recordare always created and closed together.- Same relaxation for the undelegate buffer in
undelegate.rsandundelegate_confined_account.rs(their pending-commit guard already runs before buffer creation). - New pinocchio
resize_pdahelper insrc/processor/fast/utils/pda.rs(rent top-up transfer +AccountView::resize), mirroring the slow-pathresize_pda.
Safety note: a preallocated buffer can only ever contain zeros (created zeroed, growth zero-fills, only DLP can write it), which matches the zero-tail assumption of
merge_diff_copy, andcopy_from_slicefully
overwrites it after the exact-size resize.SDK / API
dlp-api: new discriminator variant, args struct, and instruction builder, including a convenience helper returning the fullVec<Instruction>chunk sequence for a given size; IDL entry in
idl/delegation.json.- Validator-side (separate repo): the committor must emit the preallocate chunks when
data_len > 10_240.
Reacted by Sarfaraz Nawaz- Args (borsh, ~13 bytes total incl. discriminator):
Do we need buffer account then which is handled currently by
magicblock-committor-program?
Seediff_buffer_accountinprocess_commit_diff_from_bufferwhich would contain same data ascommit_statenow.It would be good to have one of them. Say we get rid of
magicblock-committor-program.
Then 2 flows are:process_commit_diff_from_buffer/process_commit_state_from_buffergot called -commit_statemust be initialized.process_commit_diff/process_commit_stateis called -commit_stateisn't initialized and we need to create it and copy arg data into it
1st flow wouldn't necessarily mean data over 10kb in this case. Validator might choose it because otherwise it may not fit data in tx(if it would be via Args)
Do we need buffer account then which is handled currently by
magicblock-committor-program? Seediff_buffer_accountinprocess_commit_diff_from_bufferwhich would contain same data ascommit_statenow.It would be good to have one of them. Say we get rid of
magicblock-committor-program. Then 2 flows are:process_commit_diff_from_buffer/process_commit_state_from_buffergot called -commit_statemust be initialized.process_commit_diff/process_commit_stateis called -commit_stateisn't initialized and we need to create it and copy arg data into it
1st flow wouldn't necessarily mean data over 10kb in this case. Validator might choose it because otherwise it may not fit data in tx(if it would be via Args)
No ideally we don't, if that's roughly the same amounts of changes, we should to it right away
The proposed solution should work.
However, what @taco-paco suggested is also a great optimization. We should eventually implement that as well.
We now fail early mentioning we don't support this case, and it's gonna be supported in dlpv2
- changed the title
[-][Bug] Failed to reallocate account data[/-][+]Support large-account commits in MagicBlock Protocol v2[/+]on Sep 17, 2026 - addedenhancementNew feature or requestNew feature or requestand removedbugSomething isn't workingSomething isn't working
on Sep 17, 2026 - added a parent issue
on Sep 17, 2026
Goal
Commit and finalize account states larger than 10 KiB (10,240 bytes) through MagicBlock Protocol v2, including changes that grow the destination account.
Why
The existing buffered commit path creates a full-sized commit-state PDA in one CPI, which can fail with
Failed to reallocate account datafor large states. The reported devnet transaction demonstrates the original failure. MBV now rejects oversized accounts earlier; that makes the limitation clearer but does not provide support.V2 already has incremental state-buffer uploads, while finalization still resizes the destination in one call. Buffer capacity alone does not establish end-to-end support.
Scope
This is v2 work under #132. Backporting support to v1 is outside this issue.