Resized accounts must be rent exempt
This commit is contained in:
parent
82cb61dc36
commit
97d40ba3da
|
@ -5571,7 +5571,6 @@ dependencies = [
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"dir-diff",
|
"dir-diff",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"enum-iterator",
|
|
||||||
"flate2",
|
"flate2",
|
||||||
"fnv",
|
"fnv",
|
||||||
"index_list",
|
"index_list",
|
||||||
|
|
|
@ -3433,7 +3433,6 @@ dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"dir-diff",
|
"dir-diff",
|
||||||
"enum-iterator",
|
|
||||||
"flate2",
|
"flate2",
|
||||||
"fnv",
|
"fnv",
|
||||||
"index_list",
|
"index_list",
|
||||||
|
|
|
@ -190,7 +190,7 @@ fn process_instruction(
|
||||||
&system_instruction::create_account(
|
&system_instruction::create_account(
|
||||||
accounts[0].key,
|
accounts[0].key,
|
||||||
accounts[1].key,
|
accounts[1].key,
|
||||||
1,
|
3000000, // large enough for rent exemption
|
||||||
pre_len as u64,
|
pre_len as u64,
|
||||||
program_id,
|
program_id,
|
||||||
),
|
),
|
||||||
|
|
|
@ -54,6 +54,7 @@ use {
|
||||||
loader_instruction,
|
loader_instruction,
|
||||||
message::{v0::LoadedAddresses, Message, SanitizedMessage},
|
message::{v0::LoadedAddresses, Message, SanitizedMessage},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
|
rent::Rent,
|
||||||
signature::{keypair_from_seed, Keypair, Signer},
|
signature::{keypair_from_seed, Keypair, Signer},
|
||||||
system_instruction::{self, MAX_PERMITTED_DATA_LENGTH},
|
system_instruction::{self, MAX_PERMITTED_DATA_LENGTH},
|
||||||
system_program,
|
system_program,
|
||||||
|
@ -2641,11 +2642,13 @@ fn test_program_bpf_ro_account_modify() {
|
||||||
fn test_program_bpf_realloc() {
|
fn test_program_bpf_realloc() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
||||||
|
const START_BALANCE: u64 = 100_000_000_000;
|
||||||
|
|
||||||
let GenesisConfigInfo {
|
let GenesisConfigInfo {
|
||||||
genesis_config,
|
genesis_config,
|
||||||
mint_keypair,
|
mint_keypair,
|
||||||
..
|
..
|
||||||
} = create_genesis_config(50);
|
} = create_genesis_config(1_000_000_000_000);
|
||||||
let mint_pubkey = mint_keypair.pubkey();
|
let mint_pubkey = mint_keypair.pubkey();
|
||||||
let signer = &[&mint_keypair];
|
let signer = &[&mint_keypair];
|
||||||
|
|
||||||
|
@ -2665,7 +2668,7 @@ fn test_program_bpf_realloc() {
|
||||||
let mut bump = 0;
|
let mut bump = 0;
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let pubkey = keypair.pubkey();
|
let pubkey = keypair.pubkey();
|
||||||
let account = AccountSharedData::new(42, 5, &program_id);
|
let account = AccountSharedData::new(START_BALANCE, 5, &program_id);
|
||||||
bank.store_account(&pubkey, &account);
|
bank.store_account(&pubkey, &account);
|
||||||
|
|
||||||
// Realloc RO account
|
// Realloc RO account
|
||||||
|
@ -2897,11 +2900,14 @@ fn test_program_bpf_realloc() {
|
||||||
fn test_program_bpf_realloc_invoke() {
|
fn test_program_bpf_realloc_invoke() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
||||||
|
const START_BALANCE: u64 = 100_000_000_000;
|
||||||
|
|
||||||
let GenesisConfigInfo {
|
let GenesisConfigInfo {
|
||||||
genesis_config,
|
mut genesis_config,
|
||||||
mint_keypair,
|
mint_keypair,
|
||||||
..
|
..
|
||||||
} = create_genesis_config(50);
|
} = create_genesis_config(1_000_000_000_000);
|
||||||
|
genesis_config.rent = Rent::default();
|
||||||
let mint_pubkey = mint_keypair.pubkey();
|
let mint_pubkey = mint_keypair.pubkey();
|
||||||
let signer = &[&mint_keypair];
|
let signer = &[&mint_keypair];
|
||||||
|
|
||||||
|
@ -2928,7 +2934,7 @@ fn test_program_bpf_realloc_invoke() {
|
||||||
let mut bump = 0;
|
let mut bump = 0;
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let pubkey = keypair.pubkey().clone();
|
let pubkey = keypair.pubkey().clone();
|
||||||
let account = AccountSharedData::new(42, 5, &realloc_program_id);
|
let account = AccountSharedData::new(START_BALANCE, 5, &realloc_program_id);
|
||||||
bank.store_account(&pubkey, &account);
|
bank.store_account(&pubkey, &account);
|
||||||
let invoke_keypair = Keypair::new();
|
let invoke_keypair = Keypair::new();
|
||||||
let invoke_pubkey = invoke_keypair.pubkey().clone();
|
let invoke_pubkey = invoke_keypair.pubkey().clone();
|
||||||
|
@ -2954,6 +2960,8 @@ fn test_program_bpf_realloc_invoke() {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified)
|
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified)
|
||||||
);
|
);
|
||||||
|
let account = bank.get_account(&pubkey).unwrap();
|
||||||
|
assert_eq!(account.lamports(), START_BALANCE);
|
||||||
|
|
||||||
// Realloc account to 0
|
// Realloc account to 0
|
||||||
bank_client
|
bank_client
|
||||||
|
@ -2965,6 +2973,8 @@ fn test_program_bpf_realloc_invoke() {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let account = bank.get_account(&pubkey).unwrap();
|
||||||
|
assert_eq!(account.lamports(), START_BALANCE);
|
||||||
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
|
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
|
||||||
assert_eq!(0, data.len());
|
assert_eq!(0, data.len());
|
||||||
|
|
||||||
|
@ -3012,54 +3022,7 @@ fn test_program_bpf_realloc_invoke() {
|
||||||
TransactionError::InstructionError(0, InstructionError::InvalidRealloc)
|
TransactionError::InstructionError(0, InstructionError::InvalidRealloc)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Realloc to max length in max increase increments
|
// Realloc account to 0
|
||||||
for i in 0..MAX_PERMITTED_DATA_LENGTH as usize / MAX_PERMITTED_DATA_INCREASE {
|
|
||||||
bank_client
|
|
||||||
.send_and_confirm_message(
|
|
||||||
signer,
|
|
||||||
Message::new(
|
|
||||||
&[Instruction::new_with_bytes(
|
|
||||||
realloc_invoke_program_id,
|
|
||||||
&[INVOKE_REALLOC_EXTEND_MAX, 1, i as u8, (i / 255) as u8],
|
|
||||||
vec![
|
|
||||||
AccountMeta::new(pubkey, false),
|
|
||||||
AccountMeta::new_readonly(realloc_program_id, false),
|
|
||||||
],
|
|
||||||
)],
|
|
||||||
Some(&mint_pubkey),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
|
|
||||||
assert_eq!((i + 1) * MAX_PERMITTED_DATA_INCREASE, data.len());
|
|
||||||
}
|
|
||||||
for i in 0..data.len() {
|
|
||||||
assert_eq!(data[i], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// and one more time should fail
|
|
||||||
assert_eq!(
|
|
||||||
bank_client
|
|
||||||
.send_and_confirm_message(
|
|
||||||
signer,
|
|
||||||
Message::new(
|
|
||||||
&[Instruction::new_with_bytes(
|
|
||||||
realloc_invoke_program_id,
|
|
||||||
&[INVOKE_REALLOC_EXTEND_MAX, 2, 1, 1],
|
|
||||||
vec![
|
|
||||||
AccountMeta::new(pubkey, false),
|
|
||||||
AccountMeta::new_readonly(realloc_program_id, false),
|
|
||||||
],
|
|
||||||
)],
|
|
||||||
Some(&mint_pubkey),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.unwrap_err()
|
|
||||||
.unwrap(),
|
|
||||||
TransactionError::InstructionError(0, InstructionError::InvalidRealloc)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Realloc to 0
|
|
||||||
bank_client
|
bank_client
|
||||||
.send_and_confirm_message(
|
.send_and_confirm_message(
|
||||||
signer,
|
signer,
|
||||||
|
@ -3069,6 +3032,8 @@ fn test_program_bpf_realloc_invoke() {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let account = bank.get_account(&pubkey).unwrap();
|
||||||
|
assert_eq!(account.lamports(), START_BALANCE);
|
||||||
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
|
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
|
||||||
assert_eq!(0, data.len());
|
assert_eq!(0, data.len());
|
||||||
|
|
||||||
|
@ -3169,7 +3134,7 @@ fn test_program_bpf_realloc_invoke() {
|
||||||
assert_eq!(0, data.len());
|
assert_eq!(0, data.len());
|
||||||
|
|
||||||
// Realloc to 100 and check via CPI
|
// Realloc to 100 and check via CPI
|
||||||
let invoke_account = AccountSharedData::new(42, 5, &realloc_invoke_program_id);
|
let invoke_account = AccountSharedData::new(START_BALANCE, 5, &realloc_invoke_program_id);
|
||||||
bank.store_account(&invoke_pubkey, &invoke_account);
|
bank.store_account(&invoke_pubkey, &invoke_account);
|
||||||
bank_client
|
bank_client
|
||||||
.send_and_confirm_message(
|
.send_and_confirm_message(
|
||||||
|
@ -3199,42 +3164,6 @@ fn test_program_bpf_realloc_invoke() {
|
||||||
assert_eq!(data[i], 2);
|
assert_eq!(data[i], 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Realloc rescursively and fill data
|
|
||||||
let invoke_keypair = Keypair::new();
|
|
||||||
let invoke_pubkey = invoke_keypair.pubkey().clone();
|
|
||||||
let invoke_account = AccountSharedData::new(42, 0, &realloc_invoke_program_id);
|
|
||||||
bank.store_account(&invoke_pubkey, &invoke_account);
|
|
||||||
let mut instruction_data = vec![];
|
|
||||||
instruction_data.extend_from_slice(&[INVOKE_REALLOC_RECURSIVE, 1]);
|
|
||||||
instruction_data.extend_from_slice(&100_usize.to_le_bytes());
|
|
||||||
bank_client
|
|
||||||
.send_and_confirm_message(
|
|
||||||
signer,
|
|
||||||
Message::new(
|
|
||||||
&[Instruction::new_with_bytes(
|
|
||||||
realloc_invoke_program_id,
|
|
||||||
&instruction_data,
|
|
||||||
vec![
|
|
||||||
AccountMeta::new(invoke_pubkey, false),
|
|
||||||
AccountMeta::new_readonly(realloc_invoke_program_id, false),
|
|
||||||
],
|
|
||||||
)],
|
|
||||||
Some(&mint_pubkey),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let data = bank_client
|
|
||||||
.get_account_data(&invoke_pubkey)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(200, data.len());
|
|
||||||
for i in 0..100 {
|
|
||||||
assert_eq!(data[i], 1);
|
|
||||||
}
|
|
||||||
for i in 100..200 {
|
|
||||||
assert_eq!(data[i], 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create account, realloc, check
|
// Create account, realloc, check
|
||||||
let new_keypair = Keypair::new();
|
let new_keypair = Keypair::new();
|
||||||
let new_pubkey = new_keypair.pubkey().clone();
|
let new_pubkey = new_keypair.pubkey().clone();
|
||||||
|
@ -3267,7 +3196,7 @@ fn test_program_bpf_realloc_invoke() {
|
||||||
// Invoke, dealloc, and assign
|
// Invoke, dealloc, and assign
|
||||||
let pre_len = 100;
|
let pre_len = 100;
|
||||||
let new_len = pre_len * 2;
|
let new_len = pre_len * 2;
|
||||||
let mut invoke_account = AccountSharedData::new(42, pre_len, &realloc_program_id);
|
let mut invoke_account = AccountSharedData::new(START_BALANCE, pre_len, &realloc_program_id);
|
||||||
invoke_account.set_data_from_slice(&vec![1; pre_len]);
|
invoke_account.set_data_from_slice(&vec![1; pre_len]);
|
||||||
bank.store_account(&invoke_pubkey, &invoke_account);
|
bank.store_account(&invoke_pubkey, &invoke_account);
|
||||||
let mut instruction_data = vec![];
|
let mut instruction_data = vec![];
|
||||||
|
@ -3347,6 +3276,102 @@ fn test_program_bpf_realloc_invoke() {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
TransactionError::InstructionError(0, InstructionError::InvalidRealloc)
|
TransactionError::InstructionError(0, InstructionError::InvalidRealloc)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Realloc to 0
|
||||||
|
bank_client
|
||||||
|
.send_and_confirm_message(
|
||||||
|
signer,
|
||||||
|
Message::new(
|
||||||
|
&[realloc(&realloc_program_id, &pubkey, 0, &mut bump)],
|
||||||
|
Some(&mint_pubkey),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
|
||||||
|
assert_eq!(0, data.len());
|
||||||
|
|
||||||
|
// Realloc to max length in max increase increments
|
||||||
|
for i in 0..MAX_PERMITTED_DATA_LENGTH as usize / MAX_PERMITTED_DATA_INCREASE {
|
||||||
|
bank_client
|
||||||
|
.send_and_confirm_message(
|
||||||
|
signer,
|
||||||
|
Message::new(
|
||||||
|
&[Instruction::new_with_bytes(
|
||||||
|
realloc_invoke_program_id,
|
||||||
|
&[INVOKE_REALLOC_EXTEND_MAX, 1, i as u8, (i / 255) as u8],
|
||||||
|
vec![
|
||||||
|
AccountMeta::new(pubkey, false),
|
||||||
|
AccountMeta::new_readonly(realloc_program_id, false),
|
||||||
|
],
|
||||||
|
)],
|
||||||
|
Some(&mint_pubkey),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
|
||||||
|
assert_eq!((i + 1) * MAX_PERMITTED_DATA_INCREASE, data.len());
|
||||||
|
}
|
||||||
|
for i in 0..data.len() {
|
||||||
|
assert_eq!(data[i], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// and one more time should fail
|
||||||
|
assert_eq!(
|
||||||
|
bank_client
|
||||||
|
.send_and_confirm_message(
|
||||||
|
signer,
|
||||||
|
Message::new(
|
||||||
|
&[Instruction::new_with_bytes(
|
||||||
|
realloc_invoke_program_id,
|
||||||
|
&[INVOKE_REALLOC_EXTEND_MAX, 2, 1, 1],
|
||||||
|
vec![
|
||||||
|
AccountMeta::new(pubkey, false),
|
||||||
|
AccountMeta::new_readonly(realloc_program_id, false),
|
||||||
|
],
|
||||||
|
)],
|
||||||
|
Some(&mint_pubkey),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.unwrap_err()
|
||||||
|
.unwrap(),
|
||||||
|
TransactionError::InstructionError(0, InstructionError::InvalidRealloc)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Realloc rescursively and fill data
|
||||||
|
let invoke_keypair = Keypair::new();
|
||||||
|
let invoke_pubkey = invoke_keypair.pubkey().clone();
|
||||||
|
let invoke_account = AccountSharedData::new(START_BALANCE, 0, &realloc_invoke_program_id);
|
||||||
|
bank.store_account(&invoke_pubkey, &invoke_account);
|
||||||
|
let mut instruction_data = vec![];
|
||||||
|
instruction_data.extend_from_slice(&[INVOKE_REALLOC_RECURSIVE, 1]);
|
||||||
|
instruction_data.extend_from_slice(&100_usize.to_le_bytes());
|
||||||
|
bank_client
|
||||||
|
.send_and_confirm_message(
|
||||||
|
signer,
|
||||||
|
Message::new(
|
||||||
|
&[Instruction::new_with_bytes(
|
||||||
|
realloc_invoke_program_id,
|
||||||
|
&instruction_data,
|
||||||
|
vec![
|
||||||
|
AccountMeta::new(invoke_pubkey, false),
|
||||||
|
AccountMeta::new_readonly(realloc_invoke_program_id, false),
|
||||||
|
],
|
||||||
|
)],
|
||||||
|
Some(&mint_pubkey),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let data = bank_client
|
||||||
|
.get_account_data(&invoke_pubkey)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(200, data.len());
|
||||||
|
for i in 0..100 {
|
||||||
|
assert_eq!(data[i], 1);
|
||||||
|
}
|
||||||
|
for i in 100..200 {
|
||||||
|
assert_eq!(data[i], 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -20,7 +20,6 @@ bzip2 = "0.4.3"
|
||||||
dashmap = { version = "4.0.2", features = ["rayon", "raw-api"] }
|
dashmap = { version = "4.0.2", features = ["rayon", "raw-api"] }
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
dir-diff = "0.3.2"
|
dir-diff = "0.3.2"
|
||||||
enum-iterator = "0.7.0"
|
|
||||||
flate2 = "1.0.22"
|
flate2 = "1.0.22"
|
||||||
fnv = "1.0.7"
|
fnv = "1.0.7"
|
||||||
index_list = "0.2.7"
|
index_list = "0.2.7"
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use {
|
use {
|
||||||
enum_iterator::IntoEnumIterator,
|
|
||||||
log::*,
|
log::*,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account::{AccountSharedData, ReadableAccount},
|
account::{AccountSharedData, ReadableAccount},
|
||||||
|
@ -10,11 +9,15 @@ use {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, IntoEnumIterator)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub(crate) enum RentState {
|
pub(crate) enum RentState {
|
||||||
Uninitialized, // account.lamports == 0
|
/// account.lamports == 0
|
||||||
RentPaying, // 0 < account.lamports < rent-exempt-minimum
|
Uninitialized,
|
||||||
RentExempt, // account.lamports >= rent-exempt-minimum
|
/// 0 < account.lamports < rent-exempt-minimum
|
||||||
|
/// Parameter is the size of the account data
|
||||||
|
RentPaying(usize),
|
||||||
|
/// account.lamports >= rent-exempt-minimum
|
||||||
|
RentExempt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RentState {
|
impl RentState {
|
||||||
|
@ -22,27 +25,42 @@ impl RentState {
|
||||||
if account.lamports() == 0 {
|
if account.lamports() == 0 {
|
||||||
Self::Uninitialized
|
Self::Uninitialized
|
||||||
} else if !rent.is_exempt(account.lamports(), account.data().len()) {
|
} else if !rent.is_exempt(account.lamports(), account.data().len()) {
|
||||||
Self::RentPaying
|
Self::RentPaying(account.data().len())
|
||||||
} else {
|
} else {
|
||||||
Self::RentExempt
|
Self::RentExempt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn transition_allowed_from(&self, pre_rent_state: &RentState) -> bool {
|
pub(crate) fn transition_allowed_from(
|
||||||
// Only a legacy RentPaying account may end in the RentPaying state after message processing
|
&self,
|
||||||
!(self == &Self::RentPaying && pre_rent_state != &Self::RentPaying)
|
pre_rent_state: &RentState,
|
||||||
|
do_support_realloc: bool,
|
||||||
|
) -> bool {
|
||||||
|
if let Self::RentPaying(post_data_size) = self {
|
||||||
|
if let Self::RentPaying(pre_data_size) = pre_rent_state {
|
||||||
|
if do_support_realloc {
|
||||||
|
post_data_size == pre_data_size // Cannot be RentPaying if resized
|
||||||
|
} else {
|
||||||
|
true // RentPaying can continue to be RentPaying
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false // Only RentPaying can continue to be RentPaying
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true // Post not-RentPaying always ok
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn submit_rent_state_metrics(pre_rent_state: &RentState, post_rent_state: &RentState) {
|
pub(crate) fn submit_rent_state_metrics(pre_rent_state: &RentState, post_rent_state: &RentState) {
|
||||||
match (pre_rent_state, post_rent_state) {
|
match (pre_rent_state, post_rent_state) {
|
||||||
(&RentState::Uninitialized, &RentState::RentPaying) => {
|
(&RentState::Uninitialized, &RentState::RentPaying(_)) => {
|
||||||
inc_new_counter_info!("rent_paying_err-new_account", 1);
|
inc_new_counter_info!("rent_paying_err-new_account", 1);
|
||||||
}
|
}
|
||||||
(&RentState::RentPaying, &RentState::RentPaying) => {
|
(&RentState::RentPaying(_), &RentState::RentPaying(_)) => {
|
||||||
inc_new_counter_info!("rent_paying_ok-legacy", 1);
|
inc_new_counter_info!("rent_paying_ok-legacy", 1);
|
||||||
}
|
}
|
||||||
(_, &RentState::RentPaying) => {
|
(_, &RentState::RentPaying(_)) => {
|
||||||
inc_new_counter_info!("rent_paying_err-other", 1);
|
inc_new_counter_info!("rent_paying_err-other", 1);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -54,6 +72,7 @@ pub(crate) fn check_rent_state(
|
||||||
post_rent_state: Option<&RentState>,
|
post_rent_state: Option<&RentState>,
|
||||||
transaction_context: &TransactionContext,
|
transaction_context: &TransactionContext,
|
||||||
index: usize,
|
index: usize,
|
||||||
|
do_support_realloc: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if let Some((pre_rent_state, post_rent_state)) = pre_rent_state.zip(post_rent_state) {
|
if let Some((pre_rent_state, post_rent_state)) = pre_rent_state.zip(post_rent_state) {
|
||||||
let expect_msg = "account must exist at TransactionContext index if rent-states are Some";
|
let expect_msg = "account must exist at TransactionContext index if rent-states are Some";
|
||||||
|
@ -67,6 +86,7 @@ pub(crate) fn check_rent_state(
|
||||||
.get_account_at_index(index)
|
.get_account_at_index(index)
|
||||||
.expect(expect_msg)
|
.expect(expect_msg)
|
||||||
.borrow(),
|
.borrow(),
|
||||||
|
do_support_realloc,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -77,10 +97,11 @@ pub(crate) fn check_rent_state_with_account(
|
||||||
post_rent_state: &RentState,
|
post_rent_state: &RentState,
|
||||||
address: &Pubkey,
|
address: &Pubkey,
|
||||||
account_state: &AccountSharedData,
|
account_state: &AccountSharedData,
|
||||||
|
do_support_realloc: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
submit_rent_state_metrics(pre_rent_state, post_rent_state);
|
submit_rent_state_metrics(pre_rent_state, post_rent_state);
|
||||||
if !solana_sdk::incinerator::check_id(address)
|
if !solana_sdk::incinerator::check_id(address)
|
||||||
&& !post_rent_state.transition_allowed_from(pre_rent_state)
|
&& !post_rent_state.transition_allowed_from(pre_rent_state, do_support_realloc)
|
||||||
{
|
{
|
||||||
debug!(
|
debug!(
|
||||||
"Account {} not rent exempt, state {:?}",
|
"Account {} not rent exempt, state {:?}",
|
||||||
|
@ -133,7 +154,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RentState::from_account(&rent_paying_account, &rent),
|
RentState::from_account(&rent_paying_account, &rent),
|
||||||
RentState::RentPaying
|
RentState::RentPaying(account_data_size)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RentState::from_account(&rent_exempt_account, &rent),
|
RentState::from_account(&rent_exempt_account, &rent),
|
||||||
|
@ -143,16 +164,21 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transition_allowed_from() {
|
fn test_transition_allowed_from() {
|
||||||
for post_rent_state in RentState::into_enum_iter() {
|
let post_rent_state = RentState::Uninitialized;
|
||||||
for pre_rent_state in RentState::into_enum_iter() {
|
assert!(post_rent_state.transition_allowed_from(&RentState::Uninitialized, true));
|
||||||
if post_rent_state == RentState::RentPaying
|
assert!(post_rent_state.transition_allowed_from(&RentState::RentExempt, true));
|
||||||
&& pre_rent_state != RentState::RentPaying
|
assert!(post_rent_state.transition_allowed_from(&RentState::RentPaying(0), true));
|
||||||
{
|
|
||||||
assert!(!post_rent_state.transition_allowed_from(&pre_rent_state));
|
let post_rent_state = RentState::RentExempt;
|
||||||
} else {
|
assert!(post_rent_state.transition_allowed_from(&RentState::Uninitialized, true));
|
||||||
assert!(post_rent_state.transition_allowed_from(&pre_rent_state));
|
assert!(post_rent_state.transition_allowed_from(&RentState::RentExempt, true));
|
||||||
}
|
assert!(post_rent_state.transition_allowed_from(&RentState::RentPaying(0), true));
|
||||||
}
|
|
||||||
}
|
let post_rent_state = RentState::RentPaying(2);
|
||||||
|
assert!(!post_rent_state.transition_allowed_from(&RentState::Uninitialized, true));
|
||||||
|
assert!(!post_rent_state.transition_allowed_from(&RentState::RentExempt, true));
|
||||||
|
assert!(!post_rent_state.transition_allowed_from(&RentState::RentPaying(3), true));
|
||||||
|
assert!(!post_rent_state.transition_allowed_from(&RentState::RentPaying(1), true));
|
||||||
|
assert!(post_rent_state.transition_allowed_from(&RentState::RentPaying(2), true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -377,6 +377,7 @@ impl Accounts {
|
||||||
&payer_post_rent_state,
|
&payer_post_rent_state,
|
||||||
payer_address,
|
payer_address,
|
||||||
payer_account,
|
payer_account,
|
||||||
|
feature_set.is_active(&feature_set::do_support_realloc::id()),
|
||||||
);
|
);
|
||||||
// Feature gate only wraps the actual error return so that the metrics and debug
|
// Feature gate only wraps the actual error return so that the metrics and debug
|
||||||
// logging generated by `check_rent_state_with_account()` can be examined before
|
// logging generated by `check_rent_state_with_account()` can be examined before
|
||||||
|
|
|
@ -16285,12 +16285,22 @@ pub(crate) mod tests {
|
||||||
let rent_paying_account = Keypair::new();
|
let rent_paying_account = Keypair::new();
|
||||||
genesis_config.accounts.insert(
|
genesis_config.accounts.insert(
|
||||||
rent_paying_account.pubkey(),
|
rent_paying_account.pubkey(),
|
||||||
Account::new(rent_exempt_minimum - 1, account_data_size, &mock_program_id),
|
Account::new_rent_epoch(
|
||||||
|
rent_exempt_minimum - 1,
|
||||||
|
account_data_size,
|
||||||
|
&mock_program_id,
|
||||||
|
INITIAL_RENT_EPOCH + 1,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
let rent_exempt_account = Keypair::new();
|
let rent_exempt_account = Keypair::new();
|
||||||
genesis_config.accounts.insert(
|
genesis_config.accounts.insert(
|
||||||
rent_exempt_account.pubkey(),
|
rent_exempt_account.pubkey(),
|
||||||
Account::new(rent_exempt_minimum, account_data_size, &mock_program_id),
|
Account::new_rent_epoch(
|
||||||
|
rent_exempt_minimum,
|
||||||
|
account_data_size,
|
||||||
|
&mock_program_id,
|
||||||
|
INITIAL_RENT_EPOCH + 1,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
// Activate features, including require_rent_exempt_accounts
|
// Activate features, including require_rent_exempt_accounts
|
||||||
activate_all_features(&mut genesis_config);
|
activate_all_features(&mut genesis_config);
|
||||||
|
@ -16427,6 +16437,97 @@ pub(crate) mod tests {
|
||||||
assert!(check_account_is_rent_exempt(&rent_exempt_account.pubkey()));
|
assert!(check_account_is_rent_exempt(&rent_exempt_account.pubkey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_drained_created_account() {
|
||||||
|
let GenesisConfigInfo {
|
||||||
|
mut genesis_config,
|
||||||
|
mint_keypair,
|
||||||
|
..
|
||||||
|
} = create_genesis_config_with_leader(sol_to_lamports(100.), &Pubkey::new_unique(), 42);
|
||||||
|
genesis_config.rent = Rent::default();
|
||||||
|
activate_all_features(&mut genesis_config);
|
||||||
|
|
||||||
|
let mock_program_id = Pubkey::new_unique();
|
||||||
|
// small enough to not pay rent, thus bypassing the data clearing rent
|
||||||
|
// mechanism
|
||||||
|
let data_size_no_rent = 100;
|
||||||
|
// large enough to pay rent, will have data cleared
|
||||||
|
let data_size_rent = 10000;
|
||||||
|
let lamports_to_transfer = 100;
|
||||||
|
|
||||||
|
// Create legacy accounts of various kinds
|
||||||
|
let created_keypair = Keypair::new();
|
||||||
|
|
||||||
|
let mut bank = Bank::new_for_tests(&genesis_config);
|
||||||
|
bank.add_builtin(
|
||||||
|
"mock_program",
|
||||||
|
&mock_program_id,
|
||||||
|
mock_transfer_process_instruction,
|
||||||
|
);
|
||||||
|
let recent_blockhash = bank.last_blockhash();
|
||||||
|
|
||||||
|
// Create and drain a small data size account
|
||||||
|
let create_instruction = system_instruction::create_account(
|
||||||
|
&mint_keypair.pubkey(),
|
||||||
|
&created_keypair.pubkey(),
|
||||||
|
lamports_to_transfer,
|
||||||
|
data_size_no_rent,
|
||||||
|
&mock_program_id,
|
||||||
|
);
|
||||||
|
let account_metas = vec![
|
||||||
|
AccountMeta::new(mint_keypair.pubkey(), true),
|
||||||
|
AccountMeta::new(created_keypair.pubkey(), true),
|
||||||
|
AccountMeta::new(mint_keypair.pubkey(), false),
|
||||||
|
];
|
||||||
|
let transfer_from_instruction = Instruction::new_with_bincode(
|
||||||
|
mock_program_id,
|
||||||
|
&MockTransferInstruction::Transfer(lamports_to_transfer),
|
||||||
|
account_metas,
|
||||||
|
);
|
||||||
|
let tx = Transaction::new_signed_with_payer(
|
||||||
|
&[create_instruction, transfer_from_instruction],
|
||||||
|
Some(&mint_keypair.pubkey()),
|
||||||
|
&[&mint_keypair, &created_keypair],
|
||||||
|
recent_blockhash,
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = bank.process_transaction(&tx);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
// account data is not stored because of zero balance even though its
|
||||||
|
// data wasn't cleared
|
||||||
|
assert!(bank.get_account(&created_keypair.pubkey()).is_none());
|
||||||
|
|
||||||
|
// Create and drain a large data size account
|
||||||
|
let create_instruction = system_instruction::create_account(
|
||||||
|
&mint_keypair.pubkey(),
|
||||||
|
&created_keypair.pubkey(),
|
||||||
|
lamports_to_transfer,
|
||||||
|
data_size_rent,
|
||||||
|
&mock_program_id,
|
||||||
|
);
|
||||||
|
let account_metas = vec![
|
||||||
|
AccountMeta::new(mint_keypair.pubkey(), true),
|
||||||
|
AccountMeta::new(created_keypair.pubkey(), true),
|
||||||
|
AccountMeta::new(mint_keypair.pubkey(), false),
|
||||||
|
];
|
||||||
|
let transfer_from_instruction = Instruction::new_with_bincode(
|
||||||
|
mock_program_id,
|
||||||
|
&MockTransferInstruction::Transfer(lamports_to_transfer),
|
||||||
|
account_metas,
|
||||||
|
);
|
||||||
|
let tx = Transaction::new_signed_with_payer(
|
||||||
|
&[create_instruction, transfer_from_instruction],
|
||||||
|
Some(&mint_keypair.pubkey()),
|
||||||
|
&[&mint_keypair, &created_keypair],
|
||||||
|
recent_blockhash,
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = bank.process_transaction(&tx);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
// account data is not stored because of zero balance
|
||||||
|
assert!(bank.get_account(&created_keypair.pubkey()).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rent_state_changes_sysvars() {
|
fn test_rent_state_changes_sysvars() {
|
||||||
let GenesisConfigInfo {
|
let GenesisConfigInfo {
|
||||||
|
@ -16879,4 +16980,329 @@ pub(crate) mod tests {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
enum MockReallocInstruction {
|
||||||
|
Realloc(usize, u64, Pubkey),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mock_realloc_process_instruction(
|
||||||
|
_first_instruction_account: usize,
|
||||||
|
data: &[u8],
|
||||||
|
invoke_context: &mut InvokeContext,
|
||||||
|
) -> result::Result<(), InstructionError> {
|
||||||
|
let transaction_context = &invoke_context.transaction_context;
|
||||||
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||||
|
if let Ok(instruction) = bincode::deserialize(data) {
|
||||||
|
match instruction {
|
||||||
|
MockReallocInstruction::Realloc(new_size, new_balance, _) => {
|
||||||
|
// Set data length
|
||||||
|
instruction_context
|
||||||
|
.try_borrow_instruction_account(transaction_context, 1)?
|
||||||
|
.set_data_length(new_size);
|
||||||
|
|
||||||
|
// set balance
|
||||||
|
let current_balance = instruction_context
|
||||||
|
.try_borrow_instruction_account(transaction_context, 1)?
|
||||||
|
.get_lamports();
|
||||||
|
let diff_balance = (new_balance as i64).saturating_sub(current_balance as i64);
|
||||||
|
let amount = diff_balance.abs() as u64;
|
||||||
|
if diff_balance.is_positive() {
|
||||||
|
instruction_context
|
||||||
|
.try_borrow_instruction_account(transaction_context, 0)?
|
||||||
|
.checked_sub_lamports(amount)?;
|
||||||
|
instruction_context
|
||||||
|
.try_borrow_instruction_account(transaction_context, 1)?
|
||||||
|
.set_lamports(new_balance);
|
||||||
|
} else {
|
||||||
|
instruction_context
|
||||||
|
.try_borrow_instruction_account(transaction_context, 0)?
|
||||||
|
.checked_add_lamports(amount)?;
|
||||||
|
instruction_context
|
||||||
|
.try_borrow_instruction_account(transaction_context, 1)?
|
||||||
|
.set_lamports(new_balance);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(InstructionError::InvalidInstructionData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_mock_realloc_tx(
|
||||||
|
payer: &Keypair,
|
||||||
|
funder: &Keypair,
|
||||||
|
reallocd: &Pubkey,
|
||||||
|
new_size: usize,
|
||||||
|
new_balance: u64,
|
||||||
|
mock_program_id: Pubkey,
|
||||||
|
recent_blockhash: Hash,
|
||||||
|
) -> Transaction {
|
||||||
|
let account_metas = vec![
|
||||||
|
AccountMeta::new(funder.pubkey(), false),
|
||||||
|
AccountMeta::new(*reallocd, false),
|
||||||
|
];
|
||||||
|
let instruction = Instruction::new_with_bincode(
|
||||||
|
mock_program_id,
|
||||||
|
&MockReallocInstruction::Realloc(new_size, new_balance, Pubkey::new_unique()),
|
||||||
|
account_metas,
|
||||||
|
);
|
||||||
|
Transaction::new_signed_with_payer(
|
||||||
|
&[instruction],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
&[payer],
|
||||||
|
recent_blockhash,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_resize_and_rent() {
|
||||||
|
let GenesisConfigInfo {
|
||||||
|
mut genesis_config,
|
||||||
|
mint_keypair,
|
||||||
|
..
|
||||||
|
} = create_genesis_config_with_leader(1_000_000_000, &Pubkey::new_unique(), 42);
|
||||||
|
genesis_config.rent = Rent::default();
|
||||||
|
activate_all_features(&mut genesis_config);
|
||||||
|
|
||||||
|
let mut bank = Bank::new_for_tests(&genesis_config);
|
||||||
|
|
||||||
|
let mock_program_id = Pubkey::new_unique();
|
||||||
|
bank.add_builtin(
|
||||||
|
"mock_realloc_program",
|
||||||
|
&mock_program_id,
|
||||||
|
mock_realloc_process_instruction,
|
||||||
|
);
|
||||||
|
let recent_blockhash = bank.last_blockhash();
|
||||||
|
|
||||||
|
let account_data_size_small = 1024;
|
||||||
|
let rent_exempt_minimum_small =
|
||||||
|
genesis_config.rent.minimum_balance(account_data_size_small);
|
||||||
|
let account_data_size_large = 2048;
|
||||||
|
let rent_exempt_minimum_large =
|
||||||
|
genesis_config.rent.minimum_balance(account_data_size_large);
|
||||||
|
|
||||||
|
let funding_keypair = Keypair::new();
|
||||||
|
bank.store_account(
|
||||||
|
&funding_keypair.pubkey(),
|
||||||
|
&AccountSharedData::new(1_000_000_000, 0, &mock_program_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
let rent_paying_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
|
let mut rent_paying_account = AccountSharedData::new(
|
||||||
|
rent_exempt_minimum_small - 1,
|
||||||
|
account_data_size_small,
|
||||||
|
&mock_program_id,
|
||||||
|
);
|
||||||
|
rent_paying_account.set_rent_epoch(1);
|
||||||
|
|
||||||
|
// restore program-owned account
|
||||||
|
bank.store_account(&rent_paying_pubkey, &rent_paying_account);
|
||||||
|
|
||||||
|
// rent paying, realloc larger, fail because not rent exempt
|
||||||
|
let tx = create_mock_realloc_tx(
|
||||||
|
&mint_keypair,
|
||||||
|
&funding_keypair,
|
||||||
|
&rent_paying_pubkey,
|
||||||
|
account_data_size_large,
|
||||||
|
rent_exempt_minimum_small - 1,
|
||||||
|
mock_program_id,
|
||||||
|
recent_blockhash,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
bank.process_transaction(&tx).unwrap_err(),
|
||||||
|
TransactionError::InvalidRentPayingAccount,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
rent_exempt_minimum_small - 1,
|
||||||
|
bank.get_account(&rent_paying_pubkey).unwrap().lamports()
|
||||||
|
);
|
||||||
|
|
||||||
|
// rent paying, realloc larger and rent exempt
|
||||||
|
let tx = create_mock_realloc_tx(
|
||||||
|
&mint_keypair,
|
||||||
|
&funding_keypair,
|
||||||
|
&rent_paying_pubkey,
|
||||||
|
account_data_size_large,
|
||||||
|
rent_exempt_minimum_large,
|
||||||
|
mock_program_id,
|
||||||
|
recent_blockhash,
|
||||||
|
);
|
||||||
|
let result = bank.process_transaction(&tx);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
rent_exempt_minimum_large,
|
||||||
|
bank.get_account(&rent_paying_pubkey).unwrap().lamports()
|
||||||
|
);
|
||||||
|
|
||||||
|
// rent exempt, realloc small, fail because not rent exempt
|
||||||
|
let tx = create_mock_realloc_tx(
|
||||||
|
&mint_keypair,
|
||||||
|
&funding_keypair,
|
||||||
|
&rent_paying_pubkey,
|
||||||
|
account_data_size_small,
|
||||||
|
rent_exempt_minimum_small - 1,
|
||||||
|
mock_program_id,
|
||||||
|
recent_blockhash,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
bank.process_transaction(&tx).unwrap_err(),
|
||||||
|
TransactionError::InvalidRentPayingAccount,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
rent_exempt_minimum_large,
|
||||||
|
bank.get_account(&rent_paying_pubkey).unwrap().lamports()
|
||||||
|
);
|
||||||
|
|
||||||
|
// rent exempt, realloc smaller and rent exempt
|
||||||
|
let tx = create_mock_realloc_tx(
|
||||||
|
&mint_keypair,
|
||||||
|
&funding_keypair,
|
||||||
|
&rent_paying_pubkey,
|
||||||
|
account_data_size_small,
|
||||||
|
rent_exempt_minimum_small,
|
||||||
|
mock_program_id,
|
||||||
|
recent_blockhash,
|
||||||
|
);
|
||||||
|
let result = bank.process_transaction(&tx);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
rent_exempt_minimum_small,
|
||||||
|
bank.get_account(&rent_paying_pubkey).unwrap().lamports()
|
||||||
|
);
|
||||||
|
|
||||||
|
// rent exempt, realloc large, fail because not rent exempt
|
||||||
|
let tx = create_mock_realloc_tx(
|
||||||
|
&mint_keypair,
|
||||||
|
&funding_keypair,
|
||||||
|
&rent_paying_pubkey,
|
||||||
|
account_data_size_large,
|
||||||
|
rent_exempt_minimum_large - 1,
|
||||||
|
mock_program_id,
|
||||||
|
recent_blockhash,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
bank.process_transaction(&tx).unwrap_err(),
|
||||||
|
TransactionError::InvalidRentPayingAccount,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
rent_exempt_minimum_small,
|
||||||
|
bank.get_account(&rent_paying_pubkey).unwrap().lamports()
|
||||||
|
);
|
||||||
|
|
||||||
|
// rent exempt, realloc large and rent exempt
|
||||||
|
let tx = create_mock_realloc_tx(
|
||||||
|
&mint_keypair,
|
||||||
|
&funding_keypair,
|
||||||
|
&rent_paying_pubkey,
|
||||||
|
account_data_size_large,
|
||||||
|
rent_exempt_minimum_large,
|
||||||
|
mock_program_id,
|
||||||
|
recent_blockhash,
|
||||||
|
);
|
||||||
|
let result = bank.process_transaction(&tx);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
rent_exempt_minimum_large,
|
||||||
|
bank.get_account(&rent_paying_pubkey).unwrap().lamports()
|
||||||
|
);
|
||||||
|
|
||||||
|
let created_keypair = Keypair::new();
|
||||||
|
|
||||||
|
// create account, not rent exempt
|
||||||
|
let tx = system_transaction::create_account(
|
||||||
|
&mint_keypair,
|
||||||
|
&created_keypair,
|
||||||
|
recent_blockhash,
|
||||||
|
rent_exempt_minimum_small - 1,
|
||||||
|
account_data_size_small as u64,
|
||||||
|
&system_program::id(),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
bank.process_transaction(&tx).unwrap_err(),
|
||||||
|
TransactionError::InvalidRentPayingAccount,
|
||||||
|
);
|
||||||
|
|
||||||
|
// create account, rent exempt
|
||||||
|
let tx = system_transaction::create_account(
|
||||||
|
&mint_keypair,
|
||||||
|
&created_keypair,
|
||||||
|
recent_blockhash,
|
||||||
|
rent_exempt_minimum_small,
|
||||||
|
account_data_size_small as u64,
|
||||||
|
&system_program::id(),
|
||||||
|
);
|
||||||
|
let result = bank.process_transaction(&tx);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
rent_exempt_minimum_small,
|
||||||
|
bank.get_account(&created_keypair.pubkey())
|
||||||
|
.unwrap()
|
||||||
|
.lamports()
|
||||||
|
);
|
||||||
|
|
||||||
|
let created_keypair = Keypair::new();
|
||||||
|
// create account, no data
|
||||||
|
let tx = system_transaction::create_account(
|
||||||
|
&mint_keypair,
|
||||||
|
&created_keypair,
|
||||||
|
recent_blockhash,
|
||||||
|
rent_exempt_minimum_small - 1,
|
||||||
|
0,
|
||||||
|
&system_program::id(),
|
||||||
|
);
|
||||||
|
let result = bank.process_transaction(&tx);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
rent_exempt_minimum_small - 1,
|
||||||
|
bank.get_account(&created_keypair.pubkey())
|
||||||
|
.unwrap()
|
||||||
|
.lamports()
|
||||||
|
);
|
||||||
|
|
||||||
|
// alloc but not rent exempt
|
||||||
|
let tx = system_transaction::allocate(
|
||||||
|
&mint_keypair,
|
||||||
|
&created_keypair,
|
||||||
|
recent_blockhash,
|
||||||
|
(account_data_size_small + 1) as u64,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
bank.process_transaction(&tx).unwrap_err(),
|
||||||
|
TransactionError::InvalidRentPayingAccount,
|
||||||
|
);
|
||||||
|
|
||||||
|
// bring balance of account up to rent exemption
|
||||||
|
let tx = system_transaction::transfer(
|
||||||
|
&mint_keypair,
|
||||||
|
&created_keypair.pubkey(),
|
||||||
|
1,
|
||||||
|
recent_blockhash,
|
||||||
|
);
|
||||||
|
let result = bank.process_transaction(&tx);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
rent_exempt_minimum_small,
|
||||||
|
bank.get_account(&created_keypair.pubkey())
|
||||||
|
.unwrap()
|
||||||
|
.lamports()
|
||||||
|
);
|
||||||
|
|
||||||
|
// allocate as rent exempt
|
||||||
|
let tx = system_transaction::allocate(
|
||||||
|
&mint_keypair,
|
||||||
|
&created_keypair,
|
||||||
|
recent_blockhash,
|
||||||
|
account_data_size_small as u64,
|
||||||
|
);
|
||||||
|
let result = bank.process_transaction(&tx);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
rent_exempt_minimum_small,
|
||||||
|
bank.get_account(&created_keypair.pubkey())
|
||||||
|
.unwrap()
|
||||||
|
.lamports()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,9 @@ impl Bank {
|
||||||
let require_rent_exempt_accounts = self
|
let require_rent_exempt_accounts = self
|
||||||
.feature_set
|
.feature_set
|
||||||
.is_active(&feature_set::require_rent_exempt_accounts::id());
|
.is_active(&feature_set::require_rent_exempt_accounts::id());
|
||||||
|
let do_support_realloc = self
|
||||||
|
.feature_set
|
||||||
|
.is_active(&feature_set::do_support_realloc::id());
|
||||||
for (i, (pre_state_info, post_state_info)) in
|
for (i, (pre_state_info, post_state_info)) in
|
||||||
pre_state_infos.iter().zip(post_state_infos).enumerate()
|
pre_state_infos.iter().zip(post_state_infos).enumerate()
|
||||||
{
|
{
|
||||||
|
@ -66,6 +69,7 @@ impl Bank {
|
||||||
post_state_info.rent_state.as_ref(),
|
post_state_info.rent_state.as_ref(),
|
||||||
transaction_context,
|
transaction_context,
|
||||||
i,
|
i,
|
||||||
|
do_support_realloc,
|
||||||
) {
|
) {
|
||||||
// Feature gate only wraps the actual error return so that the metrics and debug
|
// Feature gate only wraps the actual error return so that the metrics and debug
|
||||||
// logging generated by `check_rent_state()` can be examined before feature
|
// logging generated by `check_rent_state()` can be examined before feature
|
||||||
|
|
|
@ -326,6 +326,21 @@ fn shared_new<T: WritableAccount>(lamports: u64, space: usize, owner: &Pubkey) -
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn shared_new_rent_epoch<T: WritableAccount>(
|
||||||
|
lamports: u64,
|
||||||
|
space: usize,
|
||||||
|
owner: &Pubkey,
|
||||||
|
rent_epoch: Epoch,
|
||||||
|
) -> T {
|
||||||
|
T::create(
|
||||||
|
lamports,
|
||||||
|
vec![0u8; space],
|
||||||
|
*owner,
|
||||||
|
bool::default(),
|
||||||
|
rent_epoch,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn shared_new_ref<T: WritableAccount>(
|
fn shared_new_ref<T: WritableAccount>(
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
space: usize,
|
space: usize,
|
||||||
|
@ -434,6 +449,9 @@ impl Account {
|
||||||
) -> Result<RefCell<Self>, bincode::Error> {
|
) -> Result<RefCell<Self>, bincode::Error> {
|
||||||
shared_new_ref_data_with_space(lamports, state, space, owner)
|
shared_new_ref_data_with_space(lamports, state, space, owner)
|
||||||
}
|
}
|
||||||
|
pub fn new_rent_epoch(lamports: u64, space: usize, owner: &Pubkey, rent_epoch: Epoch) -> Self {
|
||||||
|
shared_new_rent_epoch(lamports, space, owner, rent_epoch)
|
||||||
|
}
|
||||||
pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
|
pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
|
||||||
shared_deserialize_data(self)
|
shared_deserialize_data(self)
|
||||||
}
|
}
|
||||||
|
@ -490,6 +508,9 @@ impl AccountSharedData {
|
||||||
) -> Result<RefCell<Self>, bincode::Error> {
|
) -> Result<RefCell<Self>, bincode::Error> {
|
||||||
shared_new_ref_data_with_space(lamports, state, space, owner)
|
shared_new_ref_data_with_space(lamports, state, space, owner)
|
||||||
}
|
}
|
||||||
|
pub fn new_rent_epoch(lamports: u64, space: usize, owner: &Pubkey, rent_epoch: Epoch) -> Self {
|
||||||
|
shared_new_rent_epoch(lamports, space, owner, rent_epoch)
|
||||||
|
}
|
||||||
pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
|
pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
|
||||||
shared_deserialize_data(self)
|
shared_deserialize_data(self)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,20 @@ pub fn create_account(
|
||||||
Transaction::new(&[from_keypair, to_keypair], message, recent_blockhash)
|
Transaction::new(&[from_keypair, to_keypair], message, recent_blockhash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create and sign new SystemInstruction::Allocate transaction
|
||||||
|
pub fn allocate(
|
||||||
|
payer_keypair: &Keypair,
|
||||||
|
account_keypair: &Keypair,
|
||||||
|
recent_blockhash: Hash,
|
||||||
|
space: u64,
|
||||||
|
) -> Transaction {
|
||||||
|
let payer_pubkey = payer_keypair.pubkey();
|
||||||
|
let account_pubkey = account_keypair.pubkey();
|
||||||
|
let instruction = system_instruction::allocate(&account_pubkey, space);
|
||||||
|
let message = Message::new(&[instruction], Some(&payer_pubkey));
|
||||||
|
Transaction::new(&[payer_keypair, account_keypair], message, recent_blockhash)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create and sign new system_instruction::Assign transaction
|
/// Create and sign new system_instruction::Assign transaction
|
||||||
pub fn assign(from_keypair: &Keypair, recent_blockhash: Hash, program_id: &Pubkey) -> Transaction {
|
pub fn assign(from_keypair: &Keypair, recent_blockhash: Hash, program_id: &Pubkey) -> Transaction {
|
||||||
let from_pubkey = from_keypair.pubkey();
|
let from_pubkey = from_keypair.pubkey();
|
||||||
|
|
Loading…
Reference in New Issue