Skip to content

SIMD-406: Limit number of accounts in an instruction#10223

Merged
LucasSte merged 2 commits into
anza-xyz:masterfrom
LucasSte:simd-406-1
Feb 2, 2026
Merged

SIMD-406: Limit number of accounts in an instruction#10223
LucasSte merged 2 commits into
anza-xyz:masterfrom
LucasSte:simd-406-1

Conversation

@LucasSte
Copy link
Copy Markdown

@LucasSte LucasSte commented Jan 27, 2026

Problem

Currently, there is no restriction on how many accounts an instruction may reference. Even though transactions may be limited to 256 account keys, an instruction may reference up to u16::MAX keys with duplicates. In SIMD-406, we proposed restricting the number of accounts an instruction may reference to 255 to remove complexity from runtime.

It was decided that such a check belongs in message sanitization, and the implementation fitted best in the validator code rather than the SDK (see anza-xyz/solana-sdk#520 (comment)).

The check only affects transactions v0 and legacy. Moreover, it will only impact top-level instructions that invoke native programs and pre-compiles, since the runtime serialization cannot serialize more than 255 accounts and will already throw an error for that case.

Summary of Changes

  1. Create a feature gate.
  2. Add the check to fn RuntimeTransaction<SanitizedTransaction>::try_create in sdk_transactions.rs.
  3. Add the check to fn sanitize_instructions in sanitize.rs.
  4. Fix affected code.
  5. Created two tests: one in sanitize.rs and the other one in bank/tests.rs.

@mergify
Copy link
Copy Markdown

mergify Bot commented Jan 27, 2026

If this PR represents a change to the public RPC API:

  1. Make sure it includes a complementary update to rpc-client/ (example)
  2. Open a follow-up PR to update the JavaScript client @solana/kit (example)

Thank you for keeping the RPC clients in sync with the server API @LucasSte.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jan 27, 2026

Codecov Report

❌ Patch coverage is 94.22222% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.8%. Comparing base (d8724e9) to head (5438454).
⚠️ Report is 489 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff            @@
##           master   #10223    +/-   ##
========================================
  Coverage    82.8%    82.8%            
========================================
  Files         848      848            
  Lines      321446   321688   +242     
========================================
+ Hits       266394   266608   +214     
- Misses      55052    55080    +28     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@LucasSte LucasSte marked this pull request as ready for review January 27, 2026 22:41
@LucasSte LucasSte requested a review from a team as a code owner January 27, 2026 22:41
Comment on lines +92 to +100

if enable_instruction_account_limit {
for instr in tx.message.instructions() {
if instr.accounts.len() > solana_transaction_context::MAX_ACCOUNTS_PER_INSTRUCTION {
return Err(solana_transaction_error::TransactionError::SanitizeFailure);
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

To me this looks like the right place for this check, but the SIMD does say it will throw SanitizeError::ValueOutOfBounds.

Maybe we just need to update the proposal?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this is an internal type and error, it's not reported to users in any way; irrelevant for the SIMD since the error type is entirely different.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Likely that error should be reported by the actual SDK changes, right?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

We wanted to avoid making a breaking change to the SDK sanitization API so that's why the sanitization check is here. When the feature is activated, the check can be moved into the SDK and return the ValueOutOfBounds error.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Maybe we just need to update the proposal?

If this PR is merged, I can amend the SIMD by either removing the error type at all or updating it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If this PR is merged, I can amend the SIMD by either removing the error type at all or updating it.

Yeah, this is what I was thinking, maybe just take the variant name out of the proposal.

Comment thread runtime/src/bank/tests.rs
Comment on lines +9196 to +9198
#[test_case(false; "pre_simd406_limit_instruction_accounts")]
#[test_case(true; "simd406_limit_instruction_accounts")]
fn test_verify_transactions_accounts_limit(simd_406_enabled: bool) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Wouldn't you rather test this in runtime-transaction/src/runtime_transaction/sdk_transactions.rs? You're asserting the same error and it's only bubbling its way up to your bank caller.

Instead, you can just get rid of a lot of this setup and test the functionality directly.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Also, do you think you could add a test that ensures exactly 255 is allowed?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I like this test because all replay verification goes through Bank::verify_transaction so it's a nice place for a sanity check. But I agree that unit tests in sdk_transactions.rs should be added as well.

Comment on lines +113 to +115
// Vote instructions are created in the validator code, and they are not
// referencing more than 255 accounts, so it is safe to set this to true.
true,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Even though true is the correct choice here, I think the comment is misleading.

Sure, Vote instructions are typically created by validator software, but there's nothing that says they have to be. The simple vote transaction filtering doesn't know where the transaction originated and it also doesn't enforce a specific number of accounts. For the latter reason, though, true is the right setting here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

it also doesn't enforce a specific number of accounts.

I didn't follow. If it does not enforce a specific number of accounts, then, theoretically, it could receive instructions with more than 255 accounts, right?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

That's correct. If someone crafted a "simple vote" transaction on their own, outside validator software, they could include > 255 accounts and it would still be filtered as a simple vote transaction. This is why I said using true is correct.

Your comment says that the transactions are created in the validator code, but that's not guaranteed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

What about changing the comment to "Vote instructions do not need more than 255 accounts, and the vote transaction filtering does not enforce that, so enforcing the constraint here is right" ?

Comment on lines 9 to +10
enable_static_instruction_limit: bool,
enable_instruction_accounts_limit: bool,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

might consider adding a struct here: SanitizationFeatures or something with these bools on it. Rather than we continue to add more and more bools which are easily confused with order. wdyt?

Copy link
Copy Markdown

@apfitzge apfitzge left a comment

Choose a reason for hiding this comment

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

Commenting as a quick note; TxV1 automatically supports this feature because the new format directly uses a u8 for the number of accounts in top-level instructions.

@topointon-jump
Copy link
Copy Markdown

It would be awesome if we could get this into the 4.0 release 🙌

Comment thread feature-set/src/lib.rs Outdated
Comment on lines +92 to +100

if enable_instruction_account_limit {
for instr in tx.message.instructions() {
if instr.accounts.len() > solana_transaction_context::MAX_ACCOUNTS_PER_INSTRUCTION {
return Err(solana_transaction_error::TransactionError::SanitizeFailure);
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

We wanted to avoid making a breaking change to the SDK sanitization API so that's why the sanitization check is here. When the feature is activated, the check can be moved into the SDK and return the ValueOutOfBounds error.

Comment thread runtime/src/bank/tests.rs
Comment on lines +9196 to +9198
#[test_case(false; "pre_simd406_limit_instruction_accounts")]
#[test_case(true; "simd406_limit_instruction_accounts")]
fn test_verify_transactions_accounts_limit(simd_406_enabled: bool) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I like this test because all replay verification goes through Bank::verify_transaction so it's a nice place for a sanity check. But I agree that unit tests in sdk_transactions.rs should be added as well.

Comment thread runtime-transaction/src/runtime_transaction/sdk_transactions.rs Outdated
Comment thread runtime/src/bank/tests.rs Outdated
Copy link
Copy Markdown

@buffalojoec buffalojoec left a comment

Choose a reason for hiding this comment

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

Lgtm

@LucasSte LucasSte added this pull request to the merge queue Feb 2, 2026
Merged via the queue into anza-xyz:master with commit a4cbc6b Feb 2, 2026
50 checks passed
@LucasSte LucasSte deleted the simd-406-1 branch February 2, 2026 15:19
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.

6 participants