Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit 927cf87

Browse files
mergify[bot]tao-stones
authored andcommitted
v1.18: add precompile signature metrics to cost tracker (backport of #133) (#143)
* add precompile signature metrics to cost tracker (#133) (cherry picked from commit 9770cd9) * merge fix * fmt --------- Co-authored-by: Tao Zhu <[email protected]> Co-authored-by: Tao Zhu <[email protected]>
1 parent 1f3917a commit 927cf87

File tree

5 files changed

+203
-16
lines changed

5 files changed

+203
-16
lines changed

cost-model/src/block_cost_limits.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ pub const MAX_CONCURRENCY: u64 = 4;
2424
pub const COMPUTE_UNIT_TO_US_RATIO: u64 = 30;
2525
/// Number of compute units for one signature verification.
2626
pub const SIGNATURE_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 24;
27+
/// Number of compute units for one secp256k1 signature verification.
28+
pub const SECP256K1_VERIFY_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 223;
29+
/// Number of compute units for one ed25519 signature verification.
30+
pub const ED25519_VERIFY_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 76;
2731
/// Number of compute units for one write lock
2832
pub const WRITE_LOCK_UNITS: u64 = COMPUTE_UNIT_TO_US_RATIO * 10;
2933
/// Number of data bytes per compute units
@@ -43,8 +47,8 @@ lazy_static! {
4347
(bpf_loader::id(), solana_bpf_loader_program::DEFAULT_LOADER_COMPUTE_UNITS),
4448
(loader_v4::id(), solana_loader_v4_program::DEFAULT_COMPUTE_UNITS),
4549
// Note: These are precompile, run directly in bank during sanitizing;
46-
(secp256k1_program::id(), COMPUTE_UNIT_TO_US_RATIO * 24),
47-
(ed25519_program::id(), COMPUTE_UNIT_TO_US_RATIO * 24),
50+
(secp256k1_program::id(), 0),
51+
(ed25519_program::id(), 0),
4852
]
4953
.iter()
5054
.cloned()

cost-model/src/cost_model.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl CostModel {
4343
} else {
4444
let mut tx_cost = UsageCostDetails::new_with_default_capacity();
4545

46-
tx_cost.signature_cost = Self::get_signature_cost(transaction);
46+
Self::get_signature_cost(&mut tx_cost, transaction);
4747
Self::get_write_lock_cost(&mut tx_cost, transaction, feature_set);
4848
Self::get_transaction_cost(&mut tx_cost, transaction, feature_set);
4949
tx_cost.account_data_size = Self::calculate_account_data_size(transaction);
@@ -53,8 +53,26 @@ impl CostModel {
5353
}
5454
}
5555

56-
fn get_signature_cost(transaction: &SanitizedTransaction) -> u64 {
57-
transaction.signatures().len() as u64 * SIGNATURE_COST
56+
fn get_signature_cost(tx_cost: &mut UsageCostDetails, transaction: &SanitizedTransaction) {
57+
let signatures_count_detail = transaction.message().get_signature_details();
58+
tx_cost.num_transaction_signatures = signatures_count_detail.num_transaction_signatures();
59+
tx_cost.num_secp256k1_instruction_signatures =
60+
signatures_count_detail.num_secp256k1_instruction_signatures();
61+
tx_cost.num_ed25519_instruction_signatures =
62+
signatures_count_detail.num_ed25519_instruction_signatures();
63+
tx_cost.signature_cost = signatures_count_detail
64+
.num_transaction_signatures()
65+
.saturating_mul(SIGNATURE_COST)
66+
.saturating_add(
67+
signatures_count_detail
68+
.num_secp256k1_instruction_signatures()
69+
.saturating_mul(SECP256K1_VERIFY_COST),
70+
)
71+
.saturating_add(
72+
signatures_count_detail
73+
.num_ed25519_instruction_signatures()
74+
.saturating_mul(ED25519_VERIFY_COST),
75+
);
5876
}
5977

6078
fn get_writable_accounts(transaction: &SanitizedTransaction) -> Vec<Pubkey> {

cost-model/src/cost_tracker.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ pub struct CostTracker {
5858
vote_cost: u64,
5959
transaction_count: u64,
6060
account_data_size: u64,
61+
transaction_signature_count: u64,
62+
secp256k1_instruction_signature_count: u64,
63+
ed25519_instruction_signature_count: u64,
6164
}
6265

6366
impl Default for CostTracker {
@@ -77,6 +80,9 @@ impl Default for CostTracker {
7780
vote_cost: 0,
7881
transaction_count: 0,
7982
account_data_size: 0,
83+
transaction_signature_count: 0,
84+
secp256k1_instruction_signature_count: 0,
85+
ed25519_instruction_signature_count: 0,
8086
}
8187
}
8288
}
@@ -153,6 +159,21 @@ impl CostTracker {
153159
("costliest_account", costliest_account.to_string(), String),
154160
("costliest_account_cost", costliest_account_cost as i64, i64),
155161
("account_data_size", self.account_data_size, i64),
162+
(
163+
"transaction_signature_count",
164+
self.transaction_signature_count,
165+
i64
166+
),
167+
(
168+
"secp256k1_instruction_signature_count",
169+
self.secp256k1_instruction_signature_count,
170+
i64
171+
),
172+
(
173+
"ed25519_instruction_signature_count",
174+
self.ed25519_instruction_signature_count,
175+
i64
176+
),
156177
);
157178
}
158179

@@ -213,6 +234,18 @@ impl CostTracker {
213234
self.add_transaction_execution_cost(tx_cost, tx_cost.sum());
214235
saturating_add_assign!(self.account_data_size, tx_cost.account_data_size());
215236
saturating_add_assign!(self.transaction_count, 1);
237+
saturating_add_assign!(
238+
self.transaction_signature_count,
239+
tx_cost.num_transaction_signatures()
240+
);
241+
saturating_add_assign!(
242+
self.secp256k1_instruction_signature_count,
243+
tx_cost.num_secp256k1_instruction_signatures()
244+
);
245+
saturating_add_assign!(
246+
self.ed25519_instruction_signature_count,
247+
tx_cost.num_ed25519_instruction_signatures()
248+
);
216249
}
217250

218251
fn remove_transaction_cost(&mut self, tx_cost: &TransactionCost) {
@@ -222,6 +255,15 @@ impl CostTracker {
222255
.account_data_size
223256
.saturating_sub(tx_cost.account_data_size());
224257
self.transaction_count = self.transaction_count.saturating_sub(1);
258+
self.transaction_signature_count = self
259+
.transaction_signature_count
260+
.saturating_sub(tx_cost.num_transaction_signatures());
261+
self.secp256k1_instruction_signature_count = self
262+
.secp256k1_instruction_signature_count
263+
.saturating_sub(tx_cost.num_secp256k1_instruction_signatures());
264+
self.ed25519_instruction_signature_count = self
265+
.ed25519_instruction_signature_count
266+
.saturating_sub(tx_cost.num_ed25519_instruction_signatures());
225267
}
226268

227269
/// Apply additional actual execution units to cost_tracker

cost-model/src/transaction_cost.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,27 @@ impl TransactionCost {
8787
Self::Transaction(usage_cost) => &usage_cost.writable_accounts,
8888
}
8989
}
90+
91+
pub fn num_transaction_signatures(&self) -> u64 {
92+
match self {
93+
Self::SimpleVote { .. } => 1,
94+
Self::Transaction(usage_cost) => usage_cost.num_transaction_signatures,
95+
}
96+
}
97+
98+
pub fn num_secp256k1_instruction_signatures(&self) -> u64 {
99+
match self {
100+
Self::SimpleVote { .. } => 0,
101+
Self::Transaction(usage_cost) => usage_cost.num_secp256k1_instruction_signatures,
102+
}
103+
}
104+
105+
pub fn num_ed25519_instruction_signatures(&self) -> u64 {
106+
match self {
107+
Self::SimpleVote { .. } => 0,
108+
Self::Transaction(usage_cost) => usage_cost.num_ed25519_instruction_signatures,
109+
}
110+
}
90111
}
91112

92113
const MAX_WRITABLE_ACCOUNTS: usize = 256;
@@ -102,6 +123,9 @@ pub struct UsageCostDetails {
102123
pub bpf_execution_cost: u64,
103124
pub loaded_accounts_data_size_cost: u64,
104125
pub account_data_size: u64,
126+
pub num_transaction_signatures: u64,
127+
pub num_secp256k1_instruction_signatures: u64,
128+
pub num_ed25519_instruction_signatures: u64,
105129
}
106130

107131
impl Default for UsageCostDetails {
@@ -115,6 +139,9 @@ impl Default for UsageCostDetails {
115139
bpf_execution_cost: 0u64,
116140
loaded_accounts_data_size_cost: 0u64,
117141
account_data_size: 0u64,
142+
num_transaction_signatures: 0u64,
143+
num_secp256k1_instruction_signatures: 0u64,
144+
num_ed25519_instruction_signatures: 0u64,
118145
}
119146
}
120147
}
@@ -133,6 +160,10 @@ impl PartialEq for UsageCostDetails {
133160
&& self.bpf_execution_cost == other.bpf_execution_cost
134161
&& self.loaded_accounts_data_size_cost == other.loaded_accounts_data_size_cost
135162
&& self.account_data_size == other.account_data_size
163+
&& self.num_transaction_signatures == other.num_transaction_signatures
164+
&& self.num_secp256k1_instruction_signatures
165+
== other.num_secp256k1_instruction_signatures
166+
&& self.num_ed25519_instruction_signatures == other.num_ed25519_instruction_signatures
136167
&& to_hash_set(&self.writable_accounts) == to_hash_set(&other.writable_accounts)
137168
}
138169
}

sdk/program/src/message/sanitized.rs

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -347,17 +347,7 @@ impl SanitizedMessage {
347347
}
348348

349349
pub fn num_signatures(&self) -> u64 {
350-
let mut num_signatures = u64::from(self.header().num_required_signatures);
351-
// This next part is really calculating the number of pre-processor
352-
// operations being done and treating them like a signature
353-
for (program_id, instruction) in self.program_instructions_iter() {
354-
if secp256k1_program::check_id(program_id) || ed25519_program::check_id(program_id) {
355-
if let Some(num_verifies) = instruction.data.first() {
356-
num_signatures = num_signatures.saturating_add(u64::from(*num_verifies));
357-
}
358-
}
359-
}
360-
num_signatures
350+
self.get_signature_details().total_signatures()
361351
}
362352

363353
/// Returns the number of requested write-locks in this message.
@@ -367,6 +357,68 @@ impl SanitizedMessage {
367357
.len()
368358
.saturating_sub(self.num_readonly_accounts()) as u64
369359
}
360+
361+
/// return detailed signature counts
362+
pub fn get_signature_details(&self) -> TransactionSignatureDetails {
363+
let mut transaction_signature_details = TransactionSignatureDetails {
364+
num_transaction_signatures: u64::from(self.header().num_required_signatures),
365+
..TransactionSignatureDetails::default()
366+
};
367+
368+
// counting the number of pre-processor operations separately
369+
for (program_id, instruction) in self.program_instructions_iter() {
370+
if secp256k1_program::check_id(program_id) {
371+
if let Some(num_verifies) = instruction.data.first() {
372+
transaction_signature_details.num_secp256k1_instruction_signatures =
373+
transaction_signature_details
374+
.num_secp256k1_instruction_signatures
375+
.saturating_add(u64::from(*num_verifies));
376+
}
377+
} else if ed25519_program::check_id(program_id) {
378+
if let Some(num_verifies) = instruction.data.first() {
379+
transaction_signature_details.num_ed25519_instruction_signatures =
380+
transaction_signature_details
381+
.num_ed25519_instruction_signatures
382+
.saturating_add(u64::from(*num_verifies));
383+
}
384+
}
385+
}
386+
387+
transaction_signature_details
388+
}
389+
}
390+
391+
#[derive(Default)]
392+
/// Transaction signature details including the number of transaction signatures
393+
/// and precompile signatures.
394+
pub struct TransactionSignatureDetails {
395+
num_transaction_signatures: u64,
396+
num_secp256k1_instruction_signatures: u64,
397+
num_ed25519_instruction_signatures: u64,
398+
}
399+
400+
impl TransactionSignatureDetails {
401+
/// return total number of signature, treating pre-processor operations as signature
402+
pub(crate) fn total_signatures(&self) -> u64 {
403+
self.num_transaction_signatures
404+
.saturating_add(self.num_secp256k1_instruction_signatures)
405+
.saturating_add(self.num_ed25519_instruction_signatures)
406+
}
407+
408+
/// return the number of transaction signatures
409+
pub fn num_transaction_signatures(&self) -> u64 {
410+
self.num_transaction_signatures
411+
}
412+
413+
/// return the number of secp256k1 instruction signatures
414+
pub fn num_secp256k1_instruction_signatures(&self) -> u64 {
415+
self.num_secp256k1_instruction_signatures
416+
}
417+
418+
/// return the number of ed25519 instruction signatures
419+
pub fn num_ed25519_instruction_signatures(&self) -> u64 {
420+
self.num_ed25519_instruction_signatures
421+
}
370422
}
371423

372424
#[cfg(test)]
@@ -561,4 +613,44 @@ mod tests {
561613
}
562614
}
563615
}
616+
617+
#[test]
618+
fn test_get_signature_details() {
619+
let key0 = Pubkey::new_unique();
620+
let key1 = Pubkey::new_unique();
621+
let loader_key = Pubkey::new_unique();
622+
623+
let loader_instr = CompiledInstruction::new(2, &(), vec![0, 1]);
624+
let mock_secp256k1_instr = CompiledInstruction::new(3, &[1u8; 10], vec![]);
625+
let mock_ed25519_instr = CompiledInstruction::new(4, &[5u8; 10], vec![]);
626+
627+
let message = SanitizedMessage::try_from(legacy::Message::new_with_compiled_instructions(
628+
2,
629+
1,
630+
2,
631+
vec![
632+
key0,
633+
key1,
634+
loader_key,
635+
secp256k1_program::id(),
636+
ed25519_program::id(),
637+
],
638+
Hash::default(),
639+
vec![
640+
loader_instr,
641+
mock_secp256k1_instr.clone(),
642+
mock_ed25519_instr,
643+
mock_secp256k1_instr,
644+
],
645+
))
646+
.unwrap();
647+
648+
let signature_details = message.get_signature_details();
649+
// expect 2 required transaction signatures
650+
assert_eq!(2, signature_details.num_transaction_signatures);
651+
// expect 2 secp256k1 instruction signatures - 1 for each mock_secp2561k1_instr
652+
assert_eq!(2, signature_details.num_secp256k1_instruction_signatures);
653+
// expect 5 ed25519 instruction signatures from mock_ed25519_instr
654+
assert_eq!(5, signature_details.num_ed25519_instruction_signatures);
655+
}
564656
}

0 commit comments

Comments
 (0)