Skip to content

SIMD-406: Restrict the number of accounts in an instruction#406

Merged
simd-bot[bot] merged 8 commits into
solana-foundation:mainfrom
LucasSte:max-accounts
Jan 6, 2026
Merged

SIMD-406: Restrict the number of accounts in an instruction#406
simd-bot[bot] merged 8 commits into
solana-foundation:mainfrom
LucasSte:max-accounts

Conversation

@LucasSte
Copy link
Copy Markdown
Contributor

No description provided.

@simd-bot
Copy link
Copy Markdown

simd-bot Bot commented Nov 19, 2025

Hello @LucasSte! Welcome to the SIMD process. By opening this PR you are affirming that your SIMD has been thoroughly discussed and vetted in the SIMD discussion section. The SIMD PR section should only be used to submit a final technical specification for review. If your design / idea still needs discussion, please close this PR and create a new discussion here.

This PR requires the following approvals before it can be merged:

Once all requirements are met, you can merge this PR by commenting /merge.

@LucasSte LucasSte changed the title Restrict the number of accounts in an instruction SIMD-406: Restrict the number of accounts in an instruction Nov 19, 2025
@LucasSte LucasSte force-pushed the max-accounts branch 3 times, most recently from 05b895f to d01e14b Compare November 19, 2025 18:11
@simd-bot
Copy link
Copy Markdown

simd-bot Bot commented Nov 19, 2025

Thanks, @Lichtso!

⚠️ Status: Cannot merge yet

@simd-bot
Copy link
Copy Markdown

simd-bot Bot commented Nov 19, 2025

Thanks, @mjain-jump!

⚠️ Status: Cannot merge yet

@ptaffet-jump
Copy link
Copy Markdown
Contributor

Can you clarify what happens if there are exactly 256 accounts?

both user deployed programs and CPIs only allow 255 accounts

vs.

if it references more than 256 accounts, and must throw
`InstructionError::MaxAccountsExceeded

accounts, each being a `u8` index referencing a transaction account.

Allowing instructions to reference more accounts than those contained in the
transaction is pointless, because it entails some accounts will be aliased,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

pointless

is it? instruction account references map to roles as defined by that instruction. multiple roles may reasonably be filled by the same account

restricting instruction account references to a unique set would force role mapping on chain, either statically in the program or dynamically via instruction data. the latter would cost addition transaction byte bloat, both would incur additional cu cost and risk of error

this seems like a naive motivation

Copy link
Copy Markdown
Contributor Author

@LucasSte LucasSte Nov 19, 2025

Choose a reason for hiding this comment

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

My wording there might have been imprecise. What I wanted to say is that allowing instructions to reference more accounts than the number of pubkeys the message format allows (256) is not reasonable.

The argument for that is: for what the protocol offers today, that is not necessary. On one side, there is a hard limit of 255 for CPI and programs. On the other, builtins and precompiles don't use that many.

We will continue to allow account aliasing and instructions referencing more accounts than the number of pubkeys in a transaction, provided that you are not overusing the message format.

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.

I rewrote the motivation in 9ce30cb

Comment on lines +16 to +19
This SIMD imposes a hard restriction on the maximum number of accounts an
instruction may refer to. There is already a limit of 255 accounts during
serialization in ABI v0 and v1, but it does not apply to builtin invocations
or precompiles.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
This SIMD imposes a hard restriction on the maximum number of accounts an
instruction may refer to. There is already a limit of 255 accounts during
serialization in ABI v0 and v1, but it does not apply to builtin invocations
or precompiles.
This SIMD imposes a hard restriction on the maximum number of account
references that an instruction may declare. There is already a limit of
255 account rederences imposed during serialization in ABI v0 and v1,
but it does not current apply to builtin invocations or precompiles.

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.

@LucasSte
Copy link
Copy Markdown
Contributor Author

Can you clarify what happens if there are exactly 256 accounts?

both user deployed programs and CPIs only allow 255 accounts

vs.

if it references more than 256 accounts, and must throw
`InstructionError::MaxAccountsExceeded

Good point. We can keep erroring out at 255 for programs and CPI, or we can increase the serialization limit to 256. Any thoughts @Lichtso?

@Lichtso
Copy link
Copy Markdown
Contributor

Lichtso commented Nov 19, 2025

Limit should be u8::MAX meaning 255, not 256 in all paths / cases.

@simd-bot
Copy link
Copy Markdown

simd-bot Bot commented Nov 20, 2025

Thanks, @cmoyes-jump!

⚠️ Status: Cannot merge yet

Comment thread proposals/0406-maximum-instruction-accounts.md Outdated
@LucasSte
Copy link
Copy Markdown
Contributor Author

I addressed all the feedback in the previous three commits, including the reduction of the limit to 255.

@simd-bot
Copy link
Copy Markdown

simd-bot Bot commented Nov 20, 2025

Thanks, @Lichtso!

⚠️ Status: Cannot merge yet

topointon-jump
topointon-jump previously approved these changes Nov 20, 2025
@simd-bot
Copy link
Copy Markdown

simd-bot Bot commented Nov 20, 2025

Thanks, @topointon-jump!

⚠️ Status: Cannot merge yet

@Lichtso Lichtso requested a review from t-nelson November 20, 2025 21:47
Comment on lines +42 to +45
For every instruction that goes through program runtime (CPI, user deployed
program invocation, builtin invocation or precompiles), program runtime must
check if it references more than 255 accounts, and must throw
`InstructionError::MaxAccountsExceeded` when that is the case.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Where should this check occur exactly? When should it throw? Ideally as early as possible.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah agreed. I think that in addition to the program runtime restriction, we should also add a sanitization check for the number of accounts passed to top-level instructions.

Copy link
Copy Markdown
Contributor Author

@LucasSte LucasSte Nov 21, 2025

Choose a reason for hiding this comment

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

Where should this check occur exactly? When should it throw?

The exact position I was thinking is in prepare_next_instruction and prepare_next_top_level_instruction. I did not detail the function names since this is Agave specific. I'll write down "right before account deduplication" as an alternative.

we should also add a sanitization check for the number of accounts passed to top-level instructions.

Yes, I also had that idea, but thought it would difficult to pass down a boolean through the sanitization trait we have. Since everyone also believes in that being useful, I am going to update the SIMD with that requirement.

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.

we should also add a sanitization check for the number of accounts passed to top-level instructions.

On a second thought, transaction sanitization is used in far too many places throughout the mono repo. It is going to be time consuming to get everything right there. Do you think the additional check is still worth it?

Copy link
Copy Markdown
Contributor Author

@LucasSte LucasSte Nov 21, 2025

Choose a reason for hiding this comment

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

I mentioned where the check happens in 959ecc3.

I didn't yet write about sanitization, because I'm waiting for your opinion on that.

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.

Just so I can compile all feedback and amend the SIMD, are we going to place the check in both runtime and sanitization?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If we throw in sanitization then the runtime check becomes unreachable. So, for the SIMD it is either or. Specifying both is inconsistent / self conflicting.

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.

Regarding the implementation in Agave, the check in the program-runtime is easier to do, since we already have the feature gating infrastructure in place. Implementing it in sanitization involves more code changes throughout the monorepo.

I see people weighing on both approaches, but I still did not understood if one is preferable over the other. Does anyone have any strong reason for the check not to be in runtime?

I'm happy to change if that is the case, but for simplicity my preference would be runtime.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks like we have most people agreeing on adding a protocol error for transactions which use too many accounts in an instruction. If you need some additional convincing, v1 Transactions will have a sanitization limit of 255 accounts per instructions, so I think it makes sense to limit ix accounts in legacy and v0 transactions in the same way. I think the Agave implementation should be fine, we can discuss later if you want.

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.

Thanks for the feedback. I updated the SIMD to use only a check during sanitization. The latest version is in 5bc845a.

Comment thread proposals/0406-maximum-instruction-accounts.md Outdated
Comment on lines +42 to +45
For every instruction that goes through program runtime (CPI, user deployed
program invocation, builtin invocation or precompiles), program runtime must
check if it references more than 255 accounts, and must throw
`InstructionError::MaxAccountsExceeded` when that is the case.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

did not detail the function names since this is Agave specific. I'll write down "right before account deduplication" as an alternative.

I think this is as close as we can get without a spec. 😅

Maybe FD can help us clear up the exact location/timing of this check - @mjain-jump @topointon-jump ? The most important piece is specifying which errors this should eclipse and which ones it should not.

Co-authored-by: Joe C <jcaulfield135@gmail.com>
topointon-jump
topointon-jump previously approved these changes Dec 1, 2025
@simd-bot
Copy link
Copy Markdown

simd-bot Bot commented Dec 1, 2025

Thanks, topointon-jump!

⚠️ Status: Cannot merge yet

@topointon-jump topointon-jump self-requested a review December 2, 2025 15:42
topointon-jump
topointon-jump previously approved these changes Dec 4, 2025
@simd-bot
Copy link
Copy Markdown

simd-bot Bot commented Dec 4, 2025

Thanks, topointon-jump!

⚠️ Status: Cannot merge yet

A new verification step must be included in message sanitization for message
formats legacy and v0. The verification step must examine every instruction in
a transaction, check if the former references more than 255 accounts, and
throw `SanitizationError::ValueOutOfBounds` otherwise. Transactions violating
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We don't need to specify the error here, it's an implementation detail. It's enough to say that the transaction and any block including this transaction will be rejected.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

While the specific error code is not part of consensus it is still good to specify it for fuzzing between validator implementations. At least that is how we handled it in other SIMDs.

@topointon-jump
Copy link
Copy Markdown
Contributor

@LucasSte shall we merge this?

@topointon-jump
Copy link
Copy Markdown
Contributor

@bw-solana mind taking a look?

Comment on lines +42 to +43
A new verification step must be included in message sanitization for message
formats legacy and v0. The verification step must examine every instruction in
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

how about the new Transaction v1 format?

Copy link
Copy Markdown
Contributor

@topointon-jump topointon-jump Jan 6, 2026

Choose a reason for hiding this comment

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

The new transaction v1 format also specifies this limit, so this applies to all transaction formats. Definitely worth specifying 👍

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.

The limit for v1 transactions won't be behind the feature gate implied by this SIMD. Is it worth specifying if that is the case?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe just mention it for clarity?

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.

I mentioned v1 transactions and dead blocks (from the following thread) in f600fbc

formats legacy and v0. The verification step must examine every instruction in
a transaction, check if the former references more than 255 accounts, and
throw `SanitizationError::ValueOutOfBounds` otherwise. Transactions violating
such a restriction must be considered invalid and not included in a block.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we mention that blocks should be declared dead if they include such transactions? Or is the block still good but the transaction does not execute?

Is this fine for bankless leader because we can perform static analysis and determine validity?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we mention that blocks should be declared dead if they include such transactions?

Yes we should mention this.

Is this fine for bankless leader because we can perform static analysis and determine validity?

Yes this is correct.

@simd-bot
Copy link
Copy Markdown

simd-bot Bot commented Jan 6, 2026

Thanks, bw-solana!

⚠️ Status: Cannot merge yet

@simd-bot
Copy link
Copy Markdown

simd-bot Bot commented Jan 6, 2026

✅ All approvals received! @LucasSte, you can now merge this by commenting /merge.

Status: Ready to merge

@LucasSte
Copy link
Copy Markdown
Contributor Author

LucasSte commented Jan 6, 2026

/merge

@simd-bot simd-bot Bot merged commit 68a2fac into solana-foundation:main Jan 6, 2026
2 checks passed
@simd-bot
Copy link
Copy Markdown

simd-bot Bot commented Jan 6, 2026

✅ Merge successful! LucasSte's PR has been merged.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants