Skip to content

counter(native): avoid panic, validate owner, checked_add, fail unknown instructions#531

Open
yukikm wants to merge 1 commit intosolana-developers:mainfrom
yukikm:fix/counter-native-validation
Open

counter(native): avoid panic, validate owner, checked_add, fail unknown instructions#531
yukikm wants to merge 1 commit intosolana-developers:mainfrom
yukikm:fix/counter-native-validation

Conversation

@yukikm
Copy link

@yukikm yukikm commented Feb 14, 2026

Fixes several footguns in basics/counter/native/program that are easy to copy into real programs: remove assert! panic on readonly account, add owner check, use checked_add, and fail unknown/empty instruction discriminants. Includes regression tests: test_unknown_instruction_fails, test_readonly_counter_fails.

Copilot AI review requested due to automatic review settings February 14, 2026 22:18
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the Solana-native counter example program to fail safely on malformed instructions and unsafe account inputs, and adds regression tests intended to prevent reintroducing these footguns.

Changes:

  • Avoids panics on-chain by replacing assert! with explicit error returns for readonly accounts.
  • Validates instruction discriminant (including empty data) and rejects unknown instructions.
  • Adds ownership validation and overflow-safe increment (checked_add) in the counter update path.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
basics/counter/native/program/src/lib.rs Adds instruction-data validation, unknown-instruction failures, owner + writable checks, and overflow-safe increment.
basics/counter/native/program/tests/test.rs Strengthens the happy-path test and adds new failure-mode tests (unknown instruction + readonly counter).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +120 to +134
// Mark counter account as readonly; program should return an error (not panic).
let ix = Instruction {
program_id,
accounts: vec![AccountMeta::new_readonly(counter_account.pubkey(), false)],
data: vec![0],
};

let tx = Transaction::new_signed_with_payer(
&[ix],
Some(&payer.pubkey()),
&[payer],
svm.latest_blockhash(),
);

assert!(svm.send_transaction(tx).is_err());
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

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

test_readonly_counter_fails currently only asserts that the transaction errors. If the program panics (the pre-change assert! behavior), LiteSVM may still surface that as an Err, so this test may not actually guard against panics as intended. Consider asserting on the specific failure reason (e.g., expected ProgramError/InstructionError) or checking the returned logs/error kind to ensure it's a graceful error rather than a panic/trap.

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +30
let (instruction_discriminant, instruction_data_inner) = instruction_data
.split_first()
.ok_or(ProgramError::InvalidInstructionData)?;

Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

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

The new split_first().ok_or(InvalidInstructionData)? behavior rejects empty instruction_data, but there is no regression test covering the empty-instruction case. Adding a test that sends an instruction with data: vec![] and asserts the expected error would help prevent this from regressing.

Copilot uses AI. Check for mistakes.
Comment on lines +70 to +73
counter.count = counter
.count
.checked_add(1)
.ok_or(ProgramError::ArithmeticOverflow)?;
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

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

The checked_add(1) overflow handling is new behavior, but there’s no test that demonstrates the overflow path returns ArithmeticOverflow. Consider adding a regression test that initializes counter.count to u64::MAX and asserts the transaction fails with the expected error.

Copilot uses AI. Check for mistakes.
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.

1 participant