Prevent rent-paying account creation (#22292)

* Fixup typo

* Add new feature

* Add new TransactionError

* Add framework for checking account state before and after transaction processing

* Fail transactions that leave new rent-paying accounts

* Only check rent-state of writable tx accounts

* Review comments: combine process_result success behavior; log and metrics before feature activation

* Fix tests that assume rent-exempt accounts are okay

* Remove test no longer relevant

* Remove native/sysvar special case

* Move metrics submission to report legacy->legacy rent paying transitions as well
This commit is contained in:
Tyera Eulberg
2022-01-11 11:32:25 -07:00
committed by GitHub
parent a49ef49f87
commit 637e366b18
24 changed files with 819 additions and 179 deletions

View File

@ -4273,23 +4273,32 @@ pub fn create_test_transactions_and_populate_blockstore(
let keypair3 = keypairs[3];
let slot = bank.slot();
let blockhash = bank.confirmed_last_blockhash();
let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0);
// Generate transactions for processing
// Successful transaction
let success_tx =
solana_sdk::system_transaction::transfer(mint_keypair, &keypair1.pubkey(), 2, blockhash);
let success_tx = solana_sdk::system_transaction::transfer(
mint_keypair,
&keypair1.pubkey(),
rent_exempt_amount,
blockhash,
);
let success_signature = success_tx.signatures[0];
let entry_1 = solana_entry::entry::next_entry(&blockhash, 1, vec![success_tx]);
// Failed transaction, InstructionError
let ix_error_tx =
solana_sdk::system_transaction::transfer(keypair2, &keypair3.pubkey(), 10, blockhash);
let ix_error_tx = solana_sdk::system_transaction::transfer(
keypair2,
&keypair3.pubkey(),
2 * rent_exempt_amount,
blockhash,
);
let ix_error_signature = ix_error_tx.signatures[0];
let entry_2 = solana_entry::entry::next_entry(&entry_1.hash, 1, vec![ix_error_tx]);
// Failed transaction
let fail_tx = solana_sdk::system_transaction::transfer(
mint_keypair,
&keypair2.pubkey(),
2,
rent_exempt_amount,
Hash::default(),
);
let entry_3 = solana_entry::entry::next_entry(&entry_2.hash, 1, vec![fail_tx]);
@ -4412,6 +4421,7 @@ pub mod tests {
) -> RpcHandler {
let (bank_forks, alice, leader_vote_keypair) = new_bank_forks();
let bank = bank_forks.read().unwrap().working_bank();
let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0);
let vote_pubkey = leader_vote_keypair.pubkey();
let mut vote_account = bank.get_account(&vote_pubkey).unwrap_or_default();
@ -4431,7 +4441,8 @@ pub mod tests {
let keypair1 = Keypair::new();
let keypair2 = Keypair::new();
let keypair3 = Keypair::new();
bank.transfer(4, &alice, &keypair2.pubkey()).unwrap();
bank.transfer(rent_exempt_amount, &alice, &keypair2.pubkey())
.unwrap();
let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root()));
let confirmed_block_signatures = create_test_transactions_and_populate_blockstore(
vec![&alice, &keypair1, &keypair2, &keypair3],
@ -4501,10 +4512,14 @@ pub mod tests {
let validator_exit = create_validator_exit(&exit);
let blockhash = bank.confirmed_last_blockhash();
let tx = system_transaction::transfer(&alice, pubkey, 20, blockhash);
let tx = system_transaction::transfer(&alice, pubkey, rent_exempt_amount, blockhash);
bank.process_transaction(&tx).expect("process transaction");
let tx =
system_transaction::transfer(&alice, &non_circulating_accounts()[0], 20, blockhash);
let tx = system_transaction::transfer(
&alice,
&non_circulating_accounts()[0],
rent_exempt_amount,
blockhash,
);
bank.process_transaction(&tx).expect("process transaction");
let tx = system_transaction::transfer(&alice, pubkey, std::u64::MAX, blockhash);
@ -4812,13 +4827,16 @@ pub mod tests {
#[test]
fn test_get_supply() {
let bob_pubkey = solana_sdk::pubkey::new_rand();
let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey);
let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey);
let req = r#"{"jsonrpc":"2.0","id":1,"method":"getSupply"}"#;
let res = io.handle_request_sync(req, meta);
let json: Value = serde_json::from_str(&res.unwrap()).unwrap();
let supply: RpcSupply = serde_json::from_value(json["result"]["value"].clone())
.expect("actual response deserialization");
assert_eq!(supply.non_circulating, 20);
assert_eq!(
supply.non_circulating,
bank.get_minimum_balance_for_rent_exemption(0)
);
assert!(supply.circulating >= TEST_MINT_LAMPORTS);
assert!(supply.total >= TEST_MINT_LAMPORTS + 20);
let expected_accounts: Vec<String> = non_circulating_accounts()
@ -4837,13 +4855,16 @@ pub mod tests {
#[test]
fn test_get_supply_exclude_account_list() {
let bob_pubkey = solana_sdk::pubkey::new_rand();
let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey);
let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey);
let req = r#"{"jsonrpc":"2.0","id":1,"method":"getSupply","params":[{"excludeNonCirculatingAccountsList":true}]}"#;
let res = io.handle_request_sync(req, meta);
let json: Value = serde_json::from_str(&res.unwrap()).unwrap();
let supply: RpcSupply = serde_json::from_value(json["result"]["value"].clone())
.expect("actual response deserialization");
assert_eq!(supply.non_circulating, 20);
assert_eq!(
supply.non_circulating,
bank.get_minimum_balance_for_rent_exemption(0)
);
assert!(supply.circulating >= TEST_MINT_LAMPORTS);
assert!(supply.total >= TEST_MINT_LAMPORTS + 20);
assert!(supply.non_circulating_accounts.is_empty());
@ -5165,7 +5186,7 @@ pub mod tests {
"context":{"slot":0},
"value":{
"owner": "11111111111111111111111111111111",
"lamports": 20,
"lamports": bank.get_minimum_balance_for_rent_exemption(0),
"data": "",
"executable": false,
"rentEpoch": 0
@ -5236,9 +5257,11 @@ pub mod tests {
let bob_pubkey = solana_sdk::pubkey::new_rand();
let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey);
let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0);
let address = Pubkey::new(&[9; 32]);
let data = vec![1, 2, 3, 4, 5];
let mut account = AccountSharedData::new(42, 5, &Pubkey::default());
let mut account = AccountSharedData::new(rent_exempt_amount + 1, 5, &Pubkey::default());
account.set_data(data.clone());
bank.store_account(&address, &account);
@ -5256,7 +5279,7 @@ pub mod tests {
"context":{"slot":0},
"value":[{
"owner": "11111111111111111111111111111111",
"lamports": 20,
"lamports": rent_exempt_amount,
"data": ["", "base64"],
"executable": false,
"rentEpoch": 0
@ -5264,7 +5287,7 @@ pub mod tests {
null,
{
"owner": "11111111111111111111111111111111",
"lamports": 42,
"lamports": rent_exempt_amount + 1,
"data": [base64::encode(&data), "base64"],
"executable": false,
"rentEpoch": 0
@ -5376,7 +5399,7 @@ pub mod tests {
"pubkey": "{}",
"account": {{
"owner": "{}",
"lamports": 20,
"lamports": {},
"data": "",
"executable": false,
"rentEpoch": 0
@ -5386,7 +5409,8 @@ pub mod tests {
"id":1}}
"#,
bob.pubkey(),
new_program_id
new_program_id,
bank.get_minimum_balance_for_rent_exemption(0),
);
let expected: Response =
serde_json::from_str(&expected).expect("expected response deserialization");
@ -5579,8 +5603,10 @@ pub mod tests {
..
} = start_rpc_handler_with_tx(&solana_sdk::pubkey::new_rand());
let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0);
let bob_pubkey = solana_sdk::pubkey::new_rand();
let mut tx = system_transaction::transfer(&alice, &bob_pubkey, 1234, blockhash);
let mut tx =
system_transaction::transfer(&alice, &bob_pubkey, rent_exempt_amount, blockhash);
let tx_serialized_encoded = bs58::encode(serialize(&tx).unwrap()).into_string();
tx.signatures[0] = Signature::default();
let tx_badsig_serialized_encoded = bs58::encode(serialize(&tx).unwrap()).into_string();
@ -5621,7 +5647,7 @@ pub mod tests {
"data": ["", "base64"],
"executable": false,
"owner": "11111111111111111111111111111111",
"lamports": 1234,
"lamports": rent_exempt_amount,
"rentEpoch": 0
}
],
@ -5871,6 +5897,7 @@ pub mod tests {
blockhash,
alice,
confirmed_block_signatures,
bank,
..
} = start_rpc_handler_with_tx(&bob_pubkey);
@ -5889,7 +5916,12 @@ pub mod tests {
assert_eq!(None, result.confirmations);
// Test getSignatureStatus request on unprocessed tx
let tx = system_transaction::transfer(&alice, &bob_pubkey, 10, blockhash);
let tx = system_transaction::transfer(
&alice,
&bob_pubkey,
bank.get_minimum_balance_for_rent_exemption(0) + 10,
blockhash,
);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatuses","params":[["{}"]]}}"#,
tx.signatures[0]