use {crate::block_cost_limits, solana_sdk::pubkey::Pubkey};
const SIMPLE_VOTE_USAGE_COST: u64 = 3428;
#[derive(Debug)]
pub enum TransactionCost {
SimpleVote { writable_accounts: Vec<Pubkey> },
Transaction(UsageCostDetails),
}
impl TransactionCost {
pub fn sum(&self) -> u64 {
match self {
Self::SimpleVote { .. } => SIMPLE_VOTE_USAGE_COST,
Self::Transaction(usage_cost) => usage_cost.sum(),
}
}
pub fn bpf_execution_cost(&self) -> u64 {
match self {
Self::SimpleVote { .. } => 0,
Self::Transaction(usage_cost) => usage_cost.bpf_execution_cost,
}
}
pub fn is_simple_vote(&self) -> bool {
match self {
Self::SimpleVote { .. } => true,
Self::Transaction(_) => false,
}
}
pub fn data_bytes_cost(&self) -> u64 {
match self {
Self::SimpleVote { .. } => 0,
Self::Transaction(usage_cost) => usage_cost.data_bytes_cost,
}
}
pub fn account_data_size(&self) -> u64 {
match self {
Self::SimpleVote { .. } => 0,
Self::Transaction(usage_cost) => usage_cost.account_data_size,
}
}
pub fn loaded_accounts_data_size_cost(&self) -> u64 {
match self {
Self::SimpleVote { .. } => 8, Self::Transaction(usage_cost) => usage_cost.loaded_accounts_data_size_cost,
}
}
pub fn signature_cost(&self) -> u64 {
match self {
Self::SimpleVote { .. } => block_cost_limits::SIGNATURE_COST,
Self::Transaction(usage_cost) => usage_cost.signature_cost,
}
}
pub fn write_lock_cost(&self) -> u64 {
match self {
Self::SimpleVote { .. } => block_cost_limits::WRITE_LOCK_UNITS.saturating_mul(2),
Self::Transaction(usage_cost) => usage_cost.write_lock_cost,
}
}
pub fn builtins_execution_cost(&self) -> u64 {
match self {
Self::SimpleVote { .. } => solana_vote_program::vote_processor::DEFAULT_COMPUTE_UNITS,
Self::Transaction(usage_cost) => usage_cost.builtins_execution_cost,
}
}
pub fn writable_accounts(&self) -> &[Pubkey] {
match self {
Self::SimpleVote { writable_accounts } => writable_accounts,
Self::Transaction(usage_cost) => &usage_cost.writable_accounts,
}
}
pub fn num_transaction_signatures(&self) -> u64 {
match self {
Self::SimpleVote { .. } => 1,
Self::Transaction(usage_cost) => usage_cost.num_transaction_signatures,
}
}
pub fn num_secp256k1_instruction_signatures(&self) -> u64 {
match self {
Self::SimpleVote { .. } => 0,
Self::Transaction(usage_cost) => usage_cost.num_secp256k1_instruction_signatures,
}
}
pub fn num_ed25519_instruction_signatures(&self) -> u64 {
match self {
Self::SimpleVote { .. } => 0,
Self::Transaction(usage_cost) => usage_cost.num_ed25519_instruction_signatures,
}
}
}
const MAX_WRITABLE_ACCOUNTS: usize = 256;
#[derive(Debug)]
pub struct UsageCostDetails {
pub writable_accounts: Vec<Pubkey>,
pub signature_cost: u64,
pub write_lock_cost: u64,
pub data_bytes_cost: u64,
pub builtins_execution_cost: u64,
pub bpf_execution_cost: u64,
pub loaded_accounts_data_size_cost: u64,
pub account_data_size: u64,
pub num_transaction_signatures: u64,
pub num_secp256k1_instruction_signatures: u64,
pub num_ed25519_instruction_signatures: u64,
}
impl Default for UsageCostDetails {
fn default() -> Self {
Self {
writable_accounts: Vec::with_capacity(MAX_WRITABLE_ACCOUNTS),
signature_cost: 0u64,
write_lock_cost: 0u64,
data_bytes_cost: 0u64,
builtins_execution_cost: 0u64,
bpf_execution_cost: 0u64,
loaded_accounts_data_size_cost: 0u64,
account_data_size: 0u64,
num_transaction_signatures: 0u64,
num_secp256k1_instruction_signatures: 0u64,
num_ed25519_instruction_signatures: 0u64,
}
}
}
#[cfg(test)]
impl PartialEq for UsageCostDetails {
fn eq(&self, other: &Self) -> bool {
fn to_hash_set(v: &[Pubkey]) -> std::collections::HashSet<&Pubkey> {
v.iter().collect()
}
self.signature_cost == other.signature_cost
&& self.write_lock_cost == other.write_lock_cost
&& self.data_bytes_cost == other.data_bytes_cost
&& self.builtins_execution_cost == other.builtins_execution_cost
&& self.bpf_execution_cost == other.bpf_execution_cost
&& self.loaded_accounts_data_size_cost == other.loaded_accounts_data_size_cost
&& self.account_data_size == other.account_data_size
&& self.num_transaction_signatures == other.num_transaction_signatures
&& self.num_secp256k1_instruction_signatures
== other.num_secp256k1_instruction_signatures
&& self.num_ed25519_instruction_signatures == other.num_ed25519_instruction_signatures
&& to_hash_set(&self.writable_accounts) == to_hash_set(&other.writable_accounts)
}
}
#[cfg(test)]
impl Eq for UsageCostDetails {}
impl UsageCostDetails {
#[cfg(test)]
pub fn new_with_capacity(capacity: usize) -> Self {
Self {
writable_accounts: Vec::with_capacity(capacity),
..Self::default()
}
}
pub fn new_with_default_capacity() -> Self {
Self::default()
}
pub fn sum(&self) -> u64 {
self.signature_cost
.saturating_add(self.write_lock_cost)
.saturating_add(self.data_bytes_cost)
.saturating_add(self.builtins_execution_cost)
.saturating_add(self.bpf_execution_cost)
.saturating_add(self.loaded_accounts_data_size_cost)
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::cost_model::CostModel,
solana_sdk::{
feature_set::FeatureSet,
hash::Hash,
message::SimpleAddressLoader,
signer::keypair::Keypair,
transaction::{MessageHash, SanitizedTransaction, VersionedTransaction},
},
solana_vote_program::vote_transaction,
};
#[test]
fn test_vote_transaction_cost() {
solana_logger::setup();
let node_keypair = Keypair::new();
let vote_keypair = Keypair::new();
let auth_keypair = Keypair::new();
let transaction = vote_transaction::new_vote_transaction(
vec![],
Hash::default(),
Hash::default(),
&node_keypair,
&vote_keypair,
&auth_keypair,
None,
);
let vote_transaction = SanitizedTransaction::try_create(
VersionedTransaction::from(transaction.clone()),
MessageHash::Compute,
Some(true),
SimpleAddressLoader::Disabled,
)
.unwrap();
let none_vote_transaction = SanitizedTransaction::try_create(
VersionedTransaction::from(transaction),
MessageHash::Compute,
Some(false),
SimpleAddressLoader::Disabled,
)
.unwrap();
let expected_vote_cost = SIMPLE_VOTE_USAGE_COST;
let expected_none_vote_cost = 20535;
let vote_cost = CostModel::calculate_cost(&vote_transaction, &FeatureSet::all_enabled());
let none_vote_cost =
CostModel::calculate_cost(&none_vote_transaction, &FeatureSet::all_enabled());
assert_eq!(expected_vote_cost, vote_cost.sum());
assert_eq!(expected_none_vote_cost, none_vote_cost.sum());
}
}