1#![allow(clippy::integer_arithmetic)]
4use {
5 crate::{clock::DEFAULT_MS_PER_SLOT, ed25519_program, message::Message, secp256k1_program},
6 log::*,
7};
8
9#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone, Copy, Debug, AbiExample)]
10#[serde(rename_all = "camelCase")]
11pub struct FeeCalculator {
12 pub lamports_per_signature: u64,
17}
18
19impl FeeCalculator {
20 pub fn new(lamports_per_signature: u64) -> Self {
21 Self {
22 lamports_per_signature,
23 }
24 }
25
26 #[deprecated(
27 since = "1.9.0",
28 note = "Please do not use, will no longer be available in the future"
29 )]
30 pub fn calculate_fee(&self, message: &Message) -> u64 {
31 let mut num_signatures: u64 = 0;
32 for instruction in &message.instructions {
33 let program_index = instruction.program_id_index as usize;
34 if program_index < message.account_keys.len() {
36 let id = message.account_keys[program_index];
37 if (secp256k1_program::check_id(&id) || ed25519_program::check_id(&id))
38 && !instruction.data.is_empty()
39 {
40 num_signatures += instruction.data[0] as u64;
41 }
42 }
43 }
44
45 self.lamports_per_signature
46 * (u64::from(message.header.num_required_signatures) + num_signatures)
47 }
48}
49
50#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, AbiExample)]
51#[serde(rename_all = "camelCase")]
52pub struct FeeRateGovernor {
53 #[serde(skip)]
56 pub lamports_per_signature: u64,
57
58 pub target_lamports_per_signature: u64,
61
62 pub target_signatures_per_slot: u64,
66
67 pub min_lamports_per_signature: u64,
68 pub max_lamports_per_signature: u64,
69
70 pub burn_percent: u8,
72}
73
74pub const DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE: u64 = 10_000;
75pub const DEFAULT_TARGET_SIGNATURES_PER_SLOT: u64 = 50 * DEFAULT_MS_PER_SLOT;
76
77pub const DEFAULT_BURN_PERCENT: u8 = 50;
79
80impl Default for FeeRateGovernor {
81 fn default() -> Self {
82 Self {
83 lamports_per_signature: 0,
84 target_lamports_per_signature: DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE,
85 target_signatures_per_slot: DEFAULT_TARGET_SIGNATURES_PER_SLOT,
86 min_lamports_per_signature: 0,
87 max_lamports_per_signature: 0,
88 burn_percent: DEFAULT_BURN_PERCENT,
89 }
90 }
91}
92
93impl FeeRateGovernor {
94 pub fn new(target_lamports_per_signature: u64, target_signatures_per_slot: u64) -> Self {
95 let base_fee_rate_governor = Self {
96 target_lamports_per_signature,
97 lamports_per_signature: target_lamports_per_signature,
98 target_signatures_per_slot,
99 ..FeeRateGovernor::default()
100 };
101
102 Self::new_derived(&base_fee_rate_governor, 0)
103 }
104
105 pub fn new_derived(
106 base_fee_rate_governor: &FeeRateGovernor,
107 latest_signatures_per_slot: u64,
108 ) -> Self {
109 let mut me = base_fee_rate_governor.clone();
110
111 if me.target_signatures_per_slot > 0 {
112 me.min_lamports_per_signature = std::cmp::max(1, me.target_lamports_per_signature / 2);
115 me.max_lamports_per_signature = me.target_lamports_per_signature * 10;
116
117 let desired_lamports_per_signature =
119 me.max_lamports_per_signature
120 .min(me.min_lamports_per_signature.max(
121 me.target_lamports_per_signature
122 * std::cmp::min(latest_signatures_per_slot, std::u32::MAX as u64)
123 / me.target_signatures_per_slot,
124 ));
125
126 trace!(
127 "desired_lamports_per_signature: {}",
128 desired_lamports_per_signature
129 );
130
131 let gap = desired_lamports_per_signature as i64
132 - base_fee_rate_governor.lamports_per_signature as i64;
133
134 if gap == 0 {
135 me.lamports_per_signature = desired_lamports_per_signature;
136 } else {
137 let gap_adjust =
140 std::cmp::max(1, me.target_lamports_per_signature / 20) as i64 * gap.signum();
141
142 trace!(
143 "lamports_per_signature gap is {}, adjusting by {}",
144 gap,
145 gap_adjust
146 );
147
148 me.lamports_per_signature =
149 me.max_lamports_per_signature
150 .min(me.min_lamports_per_signature.max(
151 (base_fee_rate_governor.lamports_per_signature as i64 + gap_adjust)
152 as u64,
153 ));
154 }
155 } else {
156 me.lamports_per_signature = base_fee_rate_governor.target_lamports_per_signature;
157 me.min_lamports_per_signature = me.target_lamports_per_signature;
158 me.max_lamports_per_signature = me.target_lamports_per_signature;
159 }
160 debug!(
161 "new_derived(): lamports_per_signature: {}",
162 me.lamports_per_signature
163 );
164 me
165 }
166
167 pub fn clone_with_lamports_per_signature(&self, lamports_per_signature: u64) -> Self {
168 Self {
169 lamports_per_signature,
170 ..*self
171 }
172 }
173
174 pub fn burn(&self, fees: u64) -> (u64, u64) {
176 let burned = fees * u64::from(self.burn_percent) / 100;
177 (fees - burned, burned)
178 }
179
180 pub fn create_fee_calculator(&self) -> FeeCalculator {
182 FeeCalculator::new(self.lamports_per_signature)
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use {
189 super::*,
190 crate::{pubkey::Pubkey, system_instruction},
191 };
192
193 #[test]
194 fn test_fee_rate_governor_burn() {
195 let mut fee_rate_governor = FeeRateGovernor::default();
196 assert_eq!(fee_rate_governor.burn(2), (1, 1));
197
198 fee_rate_governor.burn_percent = 0;
199 assert_eq!(fee_rate_governor.burn(2), (2, 0));
200
201 fee_rate_governor.burn_percent = 100;
202 assert_eq!(fee_rate_governor.burn(2), (0, 2));
203 }
204
205 #[test]
206 #[allow(deprecated)]
207 fn test_fee_calculator_calculate_fee() {
208 let message = Message::default();
210 assert_eq!(FeeCalculator::default().calculate_fee(&message), 0);
211
212 assert_eq!(FeeCalculator::new(1).calculate_fee(&message), 0);
214
215 let pubkey0 = Pubkey::from([0; 32]);
217 let pubkey1 = Pubkey::from([1; 32]);
218 let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
219 let message = Message::new(&[ix0], Some(&pubkey0));
220 assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 2);
221
222 let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
224 let ix1 = system_instruction::transfer(&pubkey1, &pubkey0, 1);
225 let message = Message::new(&[ix0, ix1], Some(&pubkey0));
226 assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 4);
227 }
228
229 #[test]
230 #[allow(deprecated)]
231 fn test_fee_calculator_calculate_fee_secp256k1() {
232 use crate::instruction::Instruction;
233 let pubkey0 = Pubkey::from([0; 32]);
234 let pubkey1 = Pubkey::from([1; 32]);
235 let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
236 let mut secp_instruction = Instruction {
237 program_id: crate::secp256k1_program::id(),
238 accounts: vec![],
239 data: vec![],
240 };
241 let mut secp_instruction2 = Instruction {
242 program_id: crate::secp256k1_program::id(),
243 accounts: vec![],
244 data: vec![1],
245 };
246
247 let message = Message::new(
248 &[
249 ix0.clone(),
250 secp_instruction.clone(),
251 secp_instruction2.clone(),
252 ],
253 Some(&pubkey0),
254 );
255 assert_eq!(FeeCalculator::new(1).calculate_fee(&message), 2);
256
257 secp_instruction.data = vec![0];
258 secp_instruction2.data = vec![10];
259 let message = Message::new(&[ix0, secp_instruction, secp_instruction2], Some(&pubkey0));
260 assert_eq!(FeeCalculator::new(1).calculate_fee(&message), 11);
261 }
262
263 #[test]
264 fn test_fee_rate_governor_derived_default() {
265 solana_logger::setup();
266
267 let f0 = FeeRateGovernor::default();
268 assert_eq!(
269 f0.target_signatures_per_slot,
270 DEFAULT_TARGET_SIGNATURES_PER_SLOT
271 );
272 assert_eq!(
273 f0.target_lamports_per_signature,
274 DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE
275 );
276 assert_eq!(f0.lamports_per_signature, 0);
277
278 let f1 = FeeRateGovernor::new_derived(&f0, DEFAULT_TARGET_SIGNATURES_PER_SLOT);
279 assert_eq!(
280 f1.target_signatures_per_slot,
281 DEFAULT_TARGET_SIGNATURES_PER_SLOT
282 );
283 assert_eq!(
284 f1.target_lamports_per_signature,
285 DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE
286 );
287 assert_eq!(
288 f1.lamports_per_signature,
289 DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE / 2
290 ); }
292
293 #[test]
294 fn test_fee_rate_governor_derived_adjust() {
295 solana_logger::setup();
296
297 let mut f = FeeRateGovernor {
298 target_lamports_per_signature: 100,
299 target_signatures_per_slot: 100,
300 ..FeeRateGovernor::default()
301 };
302 f = FeeRateGovernor::new_derived(&f, 0);
303
304 let mut count = 0;
306 loop {
307 let last_lamports_per_signature = f.lamports_per_signature;
308
309 f = FeeRateGovernor::new_derived(&f, std::u64::MAX);
310 info!("[up] f.lamports_per_signature={}", f.lamports_per_signature);
311
312 if f.lamports_per_signature == last_lamports_per_signature {
314 break;
315 }
316 assert!(count < 1000);
318 count += 1;
319 }
320
321 let mut count = 0;
323 loop {
324 let last_lamports_per_signature = f.lamports_per_signature;
325 f = FeeRateGovernor::new_derived(&f, 0);
326
327 info!(
328 "[down] f.lamports_per_signature={}",
329 f.lamports_per_signature
330 );
331
332 if f.lamports_per_signature == last_lamports_per_signature {
334 break;
335 }
336
337 assert!(count < 1000);
339 count += 1;
340 }
341
342 let mut count = 0;
344 while f.lamports_per_signature != f.target_lamports_per_signature {
345 f = FeeRateGovernor::new_derived(&f, f.target_signatures_per_slot);
346 info!(
347 "[target] f.lamports_per_signature={}",
348 f.lamports_per_signature
349 );
350 assert!(count < 100);
352 count += 1;
353 }
354 }
355}