Skip to content

Commit 4c41814

Browse files
mergify[bot]tao-stones
authored andcommitted
v1.17: add precompile signature metrics to cost tracker (backport of pyth-network#133) (pyth-network#142)
* add precompile signature metrics to cost tracker (pyth-network#133) (cherry picked from commit 9770cd9) # 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>
1 parent 33ab450 commit 4c41814

File tree

5 files changed

+202
-15
lines changed

5 files changed

+202
-15
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);
4848
Self::get_transaction_cost(&mut tx_cost, transaction, feature_set);
4949
tx_cost.account_data_size = Self::calculate_account_data_size(transaction);
@@ -69,8 +69,26 @@ impl CostModel {
6969
)
7070
}
7171

72-
fn get_signature_cost(transaction: &SanitizedTransaction) -> u64 {
73-
transaction.signatures().len() as u64 * SIGNATURE_COST
72+
fn get_signature_cost(tx_cost: &mut UsageCostDetails, transaction: &SanitizedTransaction) {
73+
let signatures_count_detail = transaction.message().get_signature_details();
74+
tx_cost.num_transaction_signatures = signatures_count_detail.num_transaction_signatures();
75+
tx_cost.num_secp256k1_instruction_signatures =
76+
signatures_count_detail.num_secp256k1_instruction_signatures();
77+
tx_cost.num_ed25519_instruction_signatures =
78+
signatures_count_detail.num_ed25519_instruction_signatures();
79+
tx_cost.signature_cost = signatures_count_detail
80+
.num_transaction_signatures()
81+
.saturating_mul(SIGNATURE_COST)
82+
.saturating_add(
83+
signatures_count_detail
84+
.num_secp256k1_instruction_signatures()
85+
.saturating_mul(SECP256K1_VERIFY_COST),
86+
)
87+
.saturating_add(
88+
signatures_count_detail
89+
.num_ed25519_instruction_signatures()
90+
.saturating_mul(ED25519_VERIFY_COST),
91+
);
7492
}
7593

7694
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
@@ -62,6 +62,9 @@ pub struct CostTracker {
6262
/// The amount of total account data size remaining. If `Some`, then do not add transactions
6363
/// that would cause `account_data_size` to exceed this limit.
6464
account_data_size_limit: Option<u64>,
65+
transaction_signature_count: u64,
66+
secp256k1_instruction_signature_count: u64,
67+
ed25519_instruction_signature_count: u64,
6568
}
6669

6770
impl Default for CostTracker {
@@ -82,6 +85,9 @@ impl Default for CostTracker {
8285
transaction_count: 0,
8386
account_data_size: 0,
8487
account_data_size_limit: None,
88+
transaction_signature_count: 0,
89+
secp256k1_instruction_signature_count: 0,
90+
ed25519_instruction_signature_count: 0,
8591
}
8692
}
8793
}
@@ -167,6 +173,21 @@ impl CostTracker {
167173
("costliest_account", costliest_account.to_string(), String),
168174
("costliest_account_cost", costliest_account_cost as i64, i64),
169175
("account_data_size", self.account_data_size, i64),
176+
(
177+
"transaction_signature_count",
178+
self.transaction_signature_count,
179+
i64
180+
),
181+
(
182+
"secp256k1_instruction_signature_count",
183+
self.secp256k1_instruction_signature_count,
184+
i64
185+
),
186+
(
187+
"ed25519_instruction_signature_count",
188+
self.ed25519_instruction_signature_count,
189+
i64
190+
),
170191
);
171192
}
172193

@@ -234,6 +255,18 @@ impl CostTracker {
234255
self.add_transaction_execution_cost(tx_cost, tx_cost.sum());
235256
saturating_add_assign!(self.account_data_size, tx_cost.account_data_size());
236257
saturating_add_assign!(self.transaction_count, 1);
258+
saturating_add_assign!(
259+
self.transaction_signature_count,
260+
tx_cost.num_transaction_signatures()
261+
);
262+
saturating_add_assign!(
263+
self.secp256k1_instruction_signature_count,
264+
tx_cost.num_secp256k1_instruction_signatures()
265+
);
266+
saturating_add_assign!(
267+
self.ed25519_instruction_signature_count,
268+
tx_cost.num_ed25519_instruction_signatures()
269+
);
237270
}
238271

239272
fn remove_transaction_cost(&mut self, tx_cost: &TransactionCost) {
@@ -243,6 +276,15 @@ impl CostTracker {
243276
.account_data_size
244277
.saturating_sub(tx_cost.account_data_size());
245278
self.transaction_count = self.transaction_count.saturating_sub(1);
279+
self.transaction_signature_count = self
280+
.transaction_signature_count
281+
.saturating_sub(tx_cost.num_transaction_signatures());
282+
self.secp256k1_instruction_signature_count = self
283+
.secp256k1_instruction_signature_count
284+
.saturating_sub(tx_cost.num_secp256k1_instruction_signatures());
285+
self.ed25519_instruction_signature_count = self
286+
.ed25519_instruction_signature_count
287+
.saturating_sub(tx_cost.num_ed25519_instruction_signatures());
246288
}
247289

248290
/// 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: 102 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -347,23 +347,75 @@ 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
350+
self.get_signature_details().total_signatures()
351+
}
352+
353+
pub fn num_write_locks(&self) -> u64 {
354+
self.account_keys()
355+
.len()
356+
.saturating_sub(self.num_readonly_accounts()) as u64
357+
}
358+
359+
/// return detailed signature counts
360+
pub fn get_signature_details(&self) -> TransactionSignatureDetails {
361+
let mut transaction_signature_details = TransactionSignatureDetails {
362+
num_transaction_signatures: u64::from(self.header().num_required_signatures),
363+
..TransactionSignatureDetails::default()
364+
};
365+
366+
// counting the number of pre-processor operations separately
353367
for (program_id, instruction) in self.program_instructions_iter() {
354-
if secp256k1_program::check_id(program_id) || ed25519_program::check_id(program_id) {
368+
if secp256k1_program::check_id(program_id) {
355369
if let Some(num_verifies) = instruction.data.first() {
356-
num_signatures = num_signatures.saturating_add(u64::from(*num_verifies));
370+
transaction_signature_details.num_secp256k1_instruction_signatures =
371+
transaction_signature_details
372+
.num_secp256k1_instruction_signatures
373+
.saturating_add(u64::from(*num_verifies));
374+
}
375+
} else if ed25519_program::check_id(program_id) {
376+
if let Some(num_verifies) = instruction.data.first() {
377+
transaction_signature_details.num_ed25519_instruction_signatures =
378+
transaction_signature_details
379+
.num_ed25519_instruction_signatures
380+
.saturating_add(u64::from(*num_verifies));
357381
}
358382
}
359383
}
360-
num_signatures
384+
385+
transaction_signature_details
361386
}
387+
}
362388

363-
pub fn num_write_locks(&self) -> u64 {
364-
self.account_keys()
365-
.len()
366-
.saturating_sub(self.num_readonly_accounts()) as u64
389+
#[derive(Default)]
390+
/// Transaction signature details including the number of transaction signatures
391+
/// and precompile signatures.
392+
pub struct TransactionSignatureDetails {
393+
num_transaction_signatures: u64,
394+
num_secp256k1_instruction_signatures: u64,
395+
num_ed25519_instruction_signatures: u64,
396+
}
397+
398+
impl TransactionSignatureDetails {
399+
/// return total number of signature, treating pre-processor operations as signature
400+
pub(crate) fn total_signatures(&self) -> u64 {
401+
self.num_transaction_signatures
402+
.saturating_add(self.num_secp256k1_instruction_signatures)
403+
.saturating_add(self.num_ed25519_instruction_signatures)
404+
}
405+
406+
/// return the number of transaction signatures
407+
pub fn num_transaction_signatures(&self) -> u64 {
408+
self.num_transaction_signatures
409+
}
410+
411+
/// return the number of secp256k1 instruction signatures
412+
pub fn num_secp256k1_instruction_signatures(&self) -> u64 {
413+
self.num_secp256k1_instruction_signatures
414+
}
415+
416+
/// return the number of ed25519 instruction signatures
417+
pub fn num_ed25519_instruction_signatures(&self) -> u64 {
418+
self.num_ed25519_instruction_signatures
367419
}
368420
}
369421

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

0 commit comments

Comments
 (0)