Эх сурвалжийг харах

v1.17: add precompile signature metrics to cost tracker (backport of #133) (#142)

* add precompile signature metrics to cost tracker (#133)

(cherry picked from commit 9770cd9083126b4dfe40fb207b0a3b8b21f33d21)

# Conflicts:
#	cost-model/src/cost_model.rs
#	cost-model/src/cost_tracker.rs

* merge fix

* fmt

---------

Co-authored-by: Tao Zhu <82401714+tao-stones@users.noreply.github.com>
Co-authored-by: Tao Zhu <tao@solana.com>
mergify[bot] 1 жил өмнө
parent
commit
2332aee4a2

+ 6 - 2
cost-model/src/block_cost_limits.rs

@@ -24,6 +24,10 @@ pub const MAX_CONCURRENCY: u64 = 4;
 pub const COMPUTE_UNIT_TO_US_RATIO: u64 = 30;
 /// Number of compute units for one signature verification.
 pub const SIGNATURE_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 24;
+/// Number of compute units for one secp256k1 signature verification.
+pub const SECP256K1_VERIFY_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 223;
+/// Number of compute units for one ed25519 signature verification.
+pub const ED25519_VERIFY_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 76;
 /// Number of compute units for one write lock
 pub const WRITE_LOCK_UNITS: u64 = COMPUTE_UNIT_TO_US_RATIO * 10;
 /// Number of data bytes per compute units
@@ -43,8 +47,8 @@ lazy_static! {
         (bpf_loader::id(), solana_bpf_loader_program::DEFAULT_LOADER_COMPUTE_UNITS),
         (loader_v4::id(), solana_loader_v4_program::DEFAULT_COMPUTE_UNITS),
         // Note: These are precompile, run directly in bank during sanitizing;
-        (secp256k1_program::id(), COMPUTE_UNIT_TO_US_RATIO * 24),
-        (ed25519_program::id(), COMPUTE_UNIT_TO_US_RATIO * 24),
+        (secp256k1_program::id(), 0),
+        (ed25519_program::id(), 0),
     ]
     .iter()
     .cloned()

+ 21 - 3
cost-model/src/cost_model.rs

@@ -43,7 +43,7 @@ impl CostModel {
         } else {
             let mut tx_cost = UsageCostDetails::new_with_default_capacity();
 
-            tx_cost.signature_cost = Self::get_signature_cost(transaction);
+            Self::get_signature_cost(&mut tx_cost, transaction);
             Self::get_write_lock_cost(&mut tx_cost, transaction);
             Self::get_transaction_cost(&mut tx_cost, transaction, feature_set);
             tx_cost.account_data_size = Self::calculate_account_data_size(transaction);
@@ -69,8 +69,26 @@ impl CostModel {
         )
     }
 
-    fn get_signature_cost(transaction: &SanitizedTransaction) -> u64 {
-        transaction.signatures().len() as u64 * SIGNATURE_COST
+    fn get_signature_cost(tx_cost: &mut UsageCostDetails, transaction: &SanitizedTransaction) {
+        let signatures_count_detail = transaction.message().get_signature_details();
+        tx_cost.num_transaction_signatures = signatures_count_detail.num_transaction_signatures();
+        tx_cost.num_secp256k1_instruction_signatures =
+            signatures_count_detail.num_secp256k1_instruction_signatures();
+        tx_cost.num_ed25519_instruction_signatures =
+            signatures_count_detail.num_ed25519_instruction_signatures();
+        tx_cost.signature_cost = signatures_count_detail
+            .num_transaction_signatures()
+            .saturating_mul(SIGNATURE_COST)
+            .saturating_add(
+                signatures_count_detail
+                    .num_secp256k1_instruction_signatures()
+                    .saturating_mul(SECP256K1_VERIFY_COST),
+            )
+            .saturating_add(
+                signatures_count_detail
+                    .num_ed25519_instruction_signatures()
+                    .saturating_mul(ED25519_VERIFY_COST),
+            );
     }
 
     fn get_writable_accounts(transaction: &SanitizedTransaction) -> Vec<Pubkey> {

+ 42 - 0
cost-model/src/cost_tracker.rs

@@ -62,6 +62,9 @@ pub struct CostTracker {
     /// The amount of total account data size remaining.  If `Some`, then do not add transactions
     /// that would cause `account_data_size` to exceed this limit.
     account_data_size_limit: Option<u64>,
+    transaction_signature_count: u64,
+    secp256k1_instruction_signature_count: u64,
+    ed25519_instruction_signature_count: u64,
 }
 
 impl Default for CostTracker {
@@ -82,6 +85,9 @@ impl Default for CostTracker {
             transaction_count: 0,
             account_data_size: 0,
             account_data_size_limit: None,
+            transaction_signature_count: 0,
+            secp256k1_instruction_signature_count: 0,
+            ed25519_instruction_signature_count: 0,
         }
     }
 }
@@ -167,6 +173,21 @@ impl CostTracker {
             ("costliest_account", costliest_account.to_string(), String),
             ("costliest_account_cost", costliest_account_cost as i64, i64),
             ("account_data_size", self.account_data_size, i64),
+            (
+                "transaction_signature_count",
+                self.transaction_signature_count,
+                i64
+            ),
+            (
+                "secp256k1_instruction_signature_count",
+                self.secp256k1_instruction_signature_count,
+                i64
+            ),
+            (
+                "ed25519_instruction_signature_count",
+                self.ed25519_instruction_signature_count,
+                i64
+            ),
         );
     }
 
@@ -234,6 +255,18 @@ impl CostTracker {
         self.add_transaction_execution_cost(tx_cost, tx_cost.sum());
         saturating_add_assign!(self.account_data_size, tx_cost.account_data_size());
         saturating_add_assign!(self.transaction_count, 1);
+        saturating_add_assign!(
+            self.transaction_signature_count,
+            tx_cost.num_transaction_signatures()
+        );
+        saturating_add_assign!(
+            self.secp256k1_instruction_signature_count,
+            tx_cost.num_secp256k1_instruction_signatures()
+        );
+        saturating_add_assign!(
+            self.ed25519_instruction_signature_count,
+            tx_cost.num_ed25519_instruction_signatures()
+        );
     }
 
     fn remove_transaction_cost(&mut self, tx_cost: &TransactionCost) {
@@ -243,6 +276,15 @@ impl CostTracker {
             .account_data_size
             .saturating_sub(tx_cost.account_data_size());
         self.transaction_count = self.transaction_count.saturating_sub(1);
+        self.transaction_signature_count = self
+            .transaction_signature_count
+            .saturating_sub(tx_cost.num_transaction_signatures());
+        self.secp256k1_instruction_signature_count = self
+            .secp256k1_instruction_signature_count
+            .saturating_sub(tx_cost.num_secp256k1_instruction_signatures());
+        self.ed25519_instruction_signature_count = self
+            .ed25519_instruction_signature_count
+            .saturating_sub(tx_cost.num_ed25519_instruction_signatures());
     }
 
     /// Apply additional actual execution units to cost_tracker

+ 31 - 0
cost-model/src/transaction_cost.rs

@@ -87,6 +87,27 @@ impl TransactionCost {
             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;
@@ -102,6 +123,9 @@ pub struct UsageCostDetails {
     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 {
@@ -115,6 +139,9 @@ impl Default for UsageCostDetails {
             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,
         }
     }
 }
@@ -133,6 +160,10 @@ impl PartialEq for UsageCostDetails {
             && 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)
     }
 }

+ 102 - 10
sdk/program/src/message/sanitized.rs

@@ -347,23 +347,75 @@ impl SanitizedMessage {
     }
 
     pub fn num_signatures(&self) -> u64 {
-        let mut num_signatures = u64::from(self.header().num_required_signatures);
-        // This next part is really calculating the number of pre-processor
-        // operations being done and treating them like a signature
+        self.get_signature_details().total_signatures()
+    }
+
+    pub fn num_write_locks(&self) -> u64 {
+        self.account_keys()
+            .len()
+            .saturating_sub(self.num_readonly_accounts()) as u64
+    }
+
+    /// return detailed signature counts
+    pub fn get_signature_details(&self) -> TransactionSignatureDetails {
+        let mut transaction_signature_details = TransactionSignatureDetails {
+            num_transaction_signatures: u64::from(self.header().num_required_signatures),
+            ..TransactionSignatureDetails::default()
+        };
+
+        // counting the number of pre-processor operations separately
         for (program_id, instruction) in self.program_instructions_iter() {
-            if secp256k1_program::check_id(program_id) || ed25519_program::check_id(program_id) {
+            if secp256k1_program::check_id(program_id) {
                 if let Some(num_verifies) = instruction.data.first() {
-                    num_signatures = num_signatures.saturating_add(u64::from(*num_verifies));
+                    transaction_signature_details.num_secp256k1_instruction_signatures =
+                        transaction_signature_details
+                            .num_secp256k1_instruction_signatures
+                            .saturating_add(u64::from(*num_verifies));
+                }
+            } else if ed25519_program::check_id(program_id) {
+                if let Some(num_verifies) = instruction.data.first() {
+                    transaction_signature_details.num_ed25519_instruction_signatures =
+                        transaction_signature_details
+                            .num_ed25519_instruction_signatures
+                            .saturating_add(u64::from(*num_verifies));
                 }
             }
         }
-        num_signatures
+
+        transaction_signature_details
     }
+}
 
-    pub fn num_write_locks(&self) -> u64 {
-        self.account_keys()
-            .len()
-            .saturating_sub(self.num_readonly_accounts()) as u64
+#[derive(Default)]
+/// Transaction signature details including the number of transaction signatures
+/// and precompile signatures.
+pub struct TransactionSignatureDetails {
+    num_transaction_signatures: u64,
+    num_secp256k1_instruction_signatures: u64,
+    num_ed25519_instruction_signatures: u64,
+}
+
+impl TransactionSignatureDetails {
+    /// return total number of signature, treating pre-processor operations as signature
+    pub(crate) fn total_signatures(&self) -> u64 {
+        self.num_transaction_signatures
+            .saturating_add(self.num_secp256k1_instruction_signatures)
+            .saturating_add(self.num_ed25519_instruction_signatures)
+    }
+
+    /// return the number of transaction signatures
+    pub fn num_transaction_signatures(&self) -> u64 {
+        self.num_transaction_signatures
+    }
+
+    /// return the number of secp256k1 instruction signatures
+    pub fn num_secp256k1_instruction_signatures(&self) -> u64 {
+        self.num_secp256k1_instruction_signatures
+    }
+
+    /// return the number of ed25519 instruction signatures
+    pub fn num_ed25519_instruction_signatures(&self) -> u64 {
+        self.num_ed25519_instruction_signatures
     }
 }
 
@@ -559,4 +611,44 @@ mod tests {
             }
         }
     }
+
+    #[test]
+    fn test_get_signature_details() {
+        let key0 = Pubkey::new_unique();
+        let key1 = Pubkey::new_unique();
+        let loader_key = Pubkey::new_unique();
+
+        let loader_instr = CompiledInstruction::new(2, &(), vec![0, 1]);
+        let mock_secp256k1_instr = CompiledInstruction::new(3, &[1u8; 10], vec![]);
+        let mock_ed25519_instr = CompiledInstruction::new(4, &[5u8; 10], vec![]);
+
+        let message = SanitizedMessage::try_from(legacy::Message::new_with_compiled_instructions(
+            2,
+            1,
+            2,
+            vec![
+                key0,
+                key1,
+                loader_key,
+                secp256k1_program::id(),
+                ed25519_program::id(),
+            ],
+            Hash::default(),
+            vec![
+                loader_instr,
+                mock_secp256k1_instr.clone(),
+                mock_ed25519_instr,
+                mock_secp256k1_instr,
+            ],
+        ))
+        .unwrap();
+
+        let signature_details = message.get_signature_details();
+        // expect 2 required transaction signatures
+        assert_eq!(2, signature_details.num_transaction_signatures);
+        // expect 2 secp256k1 instruction signatures - 1 for each mock_secp2561k1_instr
+        assert_eq!(2, signature_details.num_secp256k1_instruction_signatures);
+        // expect 5 ed25519 instruction signatures from mock_ed25519_instr
+        assert_eq!(5, signature_details.num_ed25519_instruction_signatures);
+    }
 }