use cost model to limit new account creation (#21369)
* use cost model to limit new account creation * handle every system instruction * remove & * simplify match * simplify match * add datapoint for account data size * add postgres error handling * handle accounts:unlock_accounts
This commit is contained in:
committed by
GitHub
parent
025a5a3b9c
commit
90f41fd9b7
@@ -10,7 +10,10 @@ use {
|
||||
execute_cost_table::ExecuteCostTable,
|
||||
},
|
||||
log::*,
|
||||
solana_sdk::{pubkey::Pubkey, transaction::SanitizedTransaction},
|
||||
solana_sdk::{
|
||||
instruction::CompiledInstruction, program_utils::limited_deserialize, pubkey::Pubkey,
|
||||
system_instruction::SystemInstruction, system_program, transaction::SanitizedTransaction,
|
||||
},
|
||||
std::collections::HashMap,
|
||||
};
|
||||
|
||||
@@ -27,6 +30,7 @@ pub struct TransactionCost {
|
||||
// `cost_weight` is a multiplier could be applied to transaction cost,
|
||||
// if set to zero allows the transaction to bypass cost limit check.
|
||||
pub cost_weight: u32,
|
||||
pub account_data_size: u64,
|
||||
}
|
||||
|
||||
impl Default for TransactionCost {
|
||||
@@ -38,6 +42,7 @@ impl Default for TransactionCost {
|
||||
data_bytes_cost: 0u64,
|
||||
execution_cost: 0u64,
|
||||
cost_weight: 1u32,
|
||||
account_data_size: 0u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,6 +123,7 @@ impl CostModel {
|
||||
tx_cost.data_bytes_cost = self.get_data_bytes_cost(transaction);
|
||||
tx_cost.execution_cost = self.get_transaction_cost(transaction);
|
||||
tx_cost.cost_weight = self.calculate_cost_weight(transaction);
|
||||
tx_cost.account_data_size = self.calculate_account_data_size(transaction);
|
||||
|
||||
debug!("transaction {:?} has cost {:?}", transaction, tx_cost);
|
||||
tx_cost
|
||||
@@ -201,6 +207,59 @@ impl CostModel {
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_account_data_size_on_deserialized_system_instruction(
|
||||
instruction: SystemInstruction,
|
||||
) -> u64 {
|
||||
match instruction {
|
||||
SystemInstruction::CreateAccount {
|
||||
lamports: _lamports,
|
||||
space,
|
||||
owner: _owner,
|
||||
} => space,
|
||||
SystemInstruction::CreateAccountWithSeed {
|
||||
base: _base,
|
||||
seed: _seed,
|
||||
lamports: _lamports,
|
||||
space,
|
||||
owner: _owner,
|
||||
} => space,
|
||||
SystemInstruction::Allocate { space } => space,
|
||||
SystemInstruction::AllocateWithSeed {
|
||||
base: _base,
|
||||
seed: _seed,
|
||||
space,
|
||||
owner: _owner,
|
||||
} => space,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_account_data_size_on_instruction(
|
||||
program_id: &Pubkey,
|
||||
instruction: &CompiledInstruction,
|
||||
) -> u64 {
|
||||
if program_id == &system_program::id() {
|
||||
if let Ok(instruction) = limited_deserialize(&instruction.data) {
|
||||
return Self::calculate_account_data_size_on_deserialized_system_instruction(
|
||||
instruction,
|
||||
);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// eventually, potentially determine account data size of all writable accounts
|
||||
/// at the moment, calculate account data size of account creation
|
||||
fn calculate_account_data_size(&self, transaction: &SanitizedTransaction) -> u64 {
|
||||
transaction
|
||||
.message()
|
||||
.program_instructions_iter()
|
||||
.map(|(program_id, instruction)| {
|
||||
Self::calculate_account_data_size_on_instruction(program_id, instruction)
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn calculate_cost_weight(&self, transaction: &SanitizedTransaction) -> u32 {
|
||||
if is_simple_vote_transaction(transaction) {
|
||||
// vote has zero cost weight, so it bypasses block cost limit checking
|
||||
@@ -272,6 +331,53 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cost_model_data_len_cost() {
|
||||
let lamports = 0;
|
||||
let owner = Pubkey::default();
|
||||
let seed = String::default();
|
||||
let space = 100;
|
||||
let base = Pubkey::default();
|
||||
for instruction in [
|
||||
SystemInstruction::CreateAccount {
|
||||
lamports,
|
||||
space,
|
||||
owner,
|
||||
},
|
||||
SystemInstruction::CreateAccountWithSeed {
|
||||
base,
|
||||
seed: seed.clone(),
|
||||
lamports,
|
||||
space,
|
||||
owner,
|
||||
},
|
||||
SystemInstruction::Allocate { space },
|
||||
SystemInstruction::AllocateWithSeed {
|
||||
base,
|
||||
seed,
|
||||
space,
|
||||
owner,
|
||||
},
|
||||
] {
|
||||
assert_eq!(
|
||||
space,
|
||||
CostModel::calculate_account_data_size_on_deserialized_system_instruction(
|
||||
instruction
|
||||
)
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
0,
|
||||
CostModel::calculate_account_data_size_on_deserialized_system_instruction(
|
||||
SystemInstruction::TransferWithSeed {
|
||||
lamports,
|
||||
from_seed: String::default(),
|
||||
from_owner: Pubkey::default(),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cost_model_simple_transaction() {
|
||||
let (mint_keypair, start_hash) = test_setup();
|
||||
|
Reference in New Issue
Block a user