Add set_account to solana-program-test (#21415)

For writing tests, it is often desirable to create a certain situation
that would be tedious, or even impossible to create by sending
individual transactions.

For example, a common attack vector on Solana is to create a copy of
some existing account that stores configuration data, but manipulate the
data stored there, and then to call a program and pass in the
manipulated account, instead of the real account.

If one wants to test this, one option is to write a program that you can
call to write arbitrary data into a new account account (and possibly
change its owner), then include that program in the test context, and
send a transaction to call it. This is extremely tedious, and developers
are not going to bother doing it. I myself would rather fork
solana-program-test to add this `set_account` method, than to write that
program. Having a “god mode” method to just write an account, lowers the
barrier to writing comprehensive tests.

A second reason for introducing this method, is defense in depth. There
may be states of the bank that are not reachable *yet* by only sending
transactions, but that you might want to test against either way. For
example, right now, the balance of a stake account cannot decrease
without going through the stake program. But what if Solana were to
introduce slashing in the future, and you want to ensure your program is
robust against decreases in stake account balance? Right now there is no
way to test this, but by introducing this “god mode” to write accounts,
the situation becomes testable.
This commit is contained in:
Ruud van Asseldonk 2021-11-24 17:13:38 +01:00 committed by GitHub
parent 81c497d263
commit e7fa412465
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 12 additions and 0 deletions

View File

@ -1037,6 +1037,18 @@ impl ProgramTestContext {
bank.store_account(vote_account_address, &vote_account);
}
/// Create or overwrite an account, subverting normal runtime checks.
///
/// This method exists to make it easier to set up artificial situations
/// that would be difficult to replicate by sending individual transactions.
/// Beware that it can be used to create states that would not be reachable
/// by sending transactions!
pub fn set_account(&mut self, address: &Pubkey, account: &AccountSharedData) {
let bank_forks = self.bank_forks.read().unwrap();
let bank = bank_forks.working_bank();
bank.store_account(address, account);
}
/// Force the working bank ahead to a new slot
pub fn warp_to_slot(&mut self, warp_slot: Slot) -> Result<(), ProgramTestError> {
let mut bank_forks = self.bank_forks.write().unwrap();