Optimize account copies and use RefCell to handle duplicate accounts in BPF programs (#7958)
This commit is contained in:
57
programs/bpf/c/src/dup_accounts/dup_accounts.c
Normal file
57
programs/bpf/c/src/dup_accounts/dup_accounts.c
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @brief Example C-based BPF program that exercises duplicate keyed ka
|
||||
* passed to it
|
||||
*/
|
||||
#include <solana_sdk.h>
|
||||
|
||||
/**
|
||||
* Custom error for when input serialization fails
|
||||
*/
|
||||
|
||||
extern uint32_t entrypoint(const uint8_t *input) {
|
||||
#define FAILURE 1
|
||||
#define INVALID_INPUT 2
|
||||
|
||||
SolKeyedAccount ka[4];
|
||||
SolParameters params = (SolParameters) { .ka = ka };
|
||||
|
||||
if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(ka))) {
|
||||
return INVALID_INPUT;
|
||||
}
|
||||
|
||||
switch (params.data[0]) {
|
||||
case(1):
|
||||
sol_log("modify first account userdata");
|
||||
ka[2].userdata[0] = 1;
|
||||
break;
|
||||
case(2):
|
||||
sol_log("modify first account userdata");
|
||||
ka[3].userdata[0] = 2;
|
||||
break;
|
||||
case(3):
|
||||
sol_log("modify both account userdata");
|
||||
ka[2].userdata[0] += 1;
|
||||
ka[3].userdata[0] += 2;
|
||||
break;
|
||||
case(4):
|
||||
sol_log("modify first account lamports");
|
||||
*ka[1].lamports -= 1;
|
||||
*ka[2].lamports += 1;
|
||||
break;
|
||||
case(5):
|
||||
sol_log("modify first account lamports");
|
||||
*ka[1].lamports -= 2;
|
||||
*ka[3].lamports += 2;
|
||||
break;
|
||||
case(6):
|
||||
sol_log("modify both account lamports");
|
||||
*ka[1].lamports -= 3;
|
||||
*ka[2].lamports += 1;
|
||||
*ka[3].lamports += 2;
|
||||
break;
|
||||
default:
|
||||
sol_log("Unrecognized command");
|
||||
return FAILURE;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
@@ -12,32 +12,32 @@ fn process_instruction(_program_id: &Pubkey, accounts: &mut [AccountInfo], data:
|
||||
match data[0] {
|
||||
1 => {
|
||||
info!("modify first account data");
|
||||
accounts[2].data[0] = 1;
|
||||
accounts[2].m.borrow_mut().data[0] = 1;
|
||||
}
|
||||
2 => {
|
||||
info!("modify first account data");
|
||||
accounts[3].data[0] = 2;
|
||||
accounts[3].m.borrow_mut().data[0] = 2;
|
||||
}
|
||||
3 => {
|
||||
info!("modify both account data, should fail");
|
||||
accounts[2].data[0] = 1;
|
||||
accounts[3].data[0] = 2;
|
||||
info!("modify both account data");
|
||||
accounts[2].m.borrow_mut().data[0] += 1;
|
||||
accounts[3].m.borrow_mut().data[0] += 2;
|
||||
}
|
||||
4 => {
|
||||
info!("modify first account lamports");
|
||||
*accounts[1].lamports -= 1;
|
||||
*accounts[2].lamports += 1;
|
||||
*accounts[1].m.borrow_mut().lamports -= 1;
|
||||
*accounts[2].m.borrow_mut().lamports += 1;
|
||||
}
|
||||
5 => {
|
||||
info!("modify first account lamports");
|
||||
*accounts[1].lamports -= 2;
|
||||
*accounts[3].lamports += 2;
|
||||
*accounts[1].m.borrow_mut().lamports -= 2;
|
||||
*accounts[3].m.borrow_mut().lamports += 2;
|
||||
}
|
||||
6 => {
|
||||
info!("modify both account lamports, should fail");
|
||||
*accounts[1].lamports -= 1;
|
||||
*accounts[2].lamports += 1;
|
||||
*accounts[3].lamports += 2;
|
||||
info!("modify both account lamports");
|
||||
*accounts[1].m.borrow_mut().lamports -= 3;
|
||||
*accounts[2].m.borrow_mut().lamports += 1;
|
||||
*accounts[3].m.borrow_mut().lamports += 2;
|
||||
}
|
||||
_ => {
|
||||
info!("Unrecognized command");
|
||||
|
@@ -8,7 +8,7 @@ fn process_instruction(_program_id: &Pubkey, accounts: &mut [AccountInfo], _data
|
||||
// account 0 is the mint and not owned by this program, any debit of its lamports
|
||||
// should result in a failed program execution. Test to ensure that this debit
|
||||
// is seen by the runtime and fails as expected
|
||||
*accounts[0].lamports -= 1;
|
||||
*accounts[0].m.borrow_mut().lamports -= 1;
|
||||
|
||||
SUCCESS
|
||||
}
|
||||
|
@@ -27,10 +27,14 @@ mod bpf {
|
||||
mod bpf_c {
|
||||
use super::*;
|
||||
use solana_runtime::loader_utils::create_invoke_instruction;
|
||||
use solana_sdk::account::Account;
|
||||
use solana_sdk::bpf_loader;
|
||||
use solana_sdk::client::SyncClient;
|
||||
use solana_sdk::instruction::{AccountMeta, Instruction};
|
||||
use solana_sdk::signature::KeypairUtil;
|
||||
use std::io::Read;
|
||||
use std::sync::Arc;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
#[test]
|
||||
fn test_program_bpf_c() {
|
||||
@@ -72,6 +76,81 @@ mod bpf {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_program_bpf_c_duplicate_accounts() {
|
||||
solana_logger::setup();
|
||||
|
||||
let filename = create_bpf_path("dup_accounts");
|
||||
let mut file = File::open(filename).unwrap();
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
|
||||
let GenesisConfigInfo {
|
||||
genesis_config,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config(50);
|
||||
|
||||
let bank = Arc::new(Bank::new(&genesis_config));
|
||||
let bank_client = BankClient::new_shared(&bank);
|
||||
let program_id = load_program(&bank_client, &mint_keypair, &bpf_loader::id(), elf);
|
||||
|
||||
let payee_account = Account::new(10, 1, &program_id);
|
||||
let payee_pubkey = Pubkey::new_rand();
|
||||
bank.store_account(&payee_pubkey, &payee_account);
|
||||
|
||||
let account = Account::new(10, 1, &program_id);
|
||||
let pubkey = Pubkey::new_rand();
|
||||
let account_metas = vec![
|
||||
AccountMeta::new(mint_keypair.pubkey(), true),
|
||||
AccountMeta::new(payee_pubkey, false),
|
||||
AccountMeta::new(pubkey, false),
|
||||
AccountMeta::new(pubkey, false),
|
||||
];
|
||||
|
||||
bank.store_account(&pubkey, &account);
|
||||
let instruction = Instruction::new(program_id, &1u8, account_metas.clone());
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(data[0], 1);
|
||||
|
||||
bank.store_account(&pubkey, &account);
|
||||
let instruction = Instruction::new(program_id, &2u8, account_metas.clone());
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(data[0], 2);
|
||||
|
||||
bank.store_account(&pubkey, &account);
|
||||
let instruction = Instruction::new(program_id, &3u8, account_metas.clone());
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(data[0], 3);
|
||||
|
||||
bank.store_account(&pubkey, &account);
|
||||
let instruction = Instruction::new(program_id, &4u8, account_metas.clone());
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
let lamports = bank_client.get_balance(&pubkey).unwrap();
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(lamports, 11);
|
||||
|
||||
bank.store_account(&pubkey, &account);
|
||||
let instruction = Instruction::new(program_id, &5u8, account_metas.clone());
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
let lamports = bank_client.get_balance(&pubkey).unwrap();
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(lamports, 12);
|
||||
|
||||
bank.store_account(&pubkey, &account);
|
||||
let instruction = Instruction::new(program_id, &6u8, account_metas.clone());
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
let lamports = bank_client.get_balance(&pubkey).unwrap();
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(lamports, 13);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "bpf_rust")]
|
||||
@@ -193,7 +272,9 @@ mod bpf {
|
||||
bank.store_account(&pubkey, &account);
|
||||
let instruction = Instruction::new(program_id, &3u8, account_metas.clone());
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
assert!(!result.is_ok());
|
||||
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(data[0], 3);
|
||||
|
||||
bank.store_account(&pubkey, &account);
|
||||
let instruction = Instruction::new(program_id, &4u8, account_metas.clone());
|
||||
@@ -212,7 +293,9 @@ mod bpf {
|
||||
bank.store_account(&pubkey, &account);
|
||||
let instruction = Instruction::new(program_id, &6u8, account_metas.clone());
|
||||
let result = bank_client.send_instruction(&mint_keypair, instruction);
|
||||
assert!(!result.is_ok());
|
||||
let lamports = bank_client.get_balance(&pubkey).unwrap();
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(lamports, 13);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user