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
74
75pub const DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE: u64 = 25_000;
76pub const DEFAULT_TARGET_SIGNATURES_PER_SLOT: u64 = 25 * DEFAULT_MS_PER_SLOT;
77
78pub const DEFAULT_BURN_PERCENT: u8 = 50;
80
81impl Default for FeeRateGovernor {
82 fn default() -> Self {
83 Self {
84 lamports_per_signature: 0,
85 target_lamports_per_signature: DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE,
86 target_signatures_per_slot: DEFAULT_TARGET_SIGNATURES_PER_SLOT,
87 min_lamports_per_signature: 0,
88 max_lamports_per_signature: 0,
89 burn_percent: DEFAULT_BURN_PERCENT,
90 }
91 }
92}
93
94impl FeeRateGovernor {
95 pub fn new(target_lamports_per_signature: u64, target_signatures_per_slot: u64) -> Self {
96 let base_fee_rate_governor = Self {
97 target_lamports_per_signature,
98 lamports_per_signature: target_lamports_per_signature,
99 target_signatures_per_slot,
100 ..FeeRateGovernor::default()
101 };
102
103 Self::new_derived(&base_fee_rate_governor, 0)
104 }
105
106 pub fn new_derived(
107 base_fee_rate_governor: &FeeRateGovernor,
108 latest_signatures_per_slot: u64,
109 ) -> Self {
110 let mut me = base_fee_rate_governor.clone();
111
112 if me.target_signatures_per_slot > 0 {
113 me.min_lamports_per_signature = std::cmp::max(1, me.target_lamports_per_signature / 2);
116 me.max_lamports_per_signature = me.target_lamports_per_signature * 10;
117
118 let desired_lamports_per_signature =
120 me.max_lamports_per_signature
121 .min(me.min_lamports_per_signature.max(
122 me.target_lamports_per_signature
123 * std::cmp::min(latest_signatures_per_slot, std::u32::MAX as u64)
124 as u64
125 / me.target_signatures_per_slot as u64,
126 ));
127
128 trace!(
129 "desired_lamports_per_signature: {}",
130 desired_lamports_per_signature
131 );
132
133 let gap = desired_lamports_per_signature as i64
134 - base_fee_rate_governor.lamports_per_signature as i64;
135
136 if gap == 0 {
137 me.lamports_per_signature = desired_lamports_per_signature;
138 } else {
139 let gap_adjust =
142 std::cmp::max(1, me.target_lamports_per_signature / 20) as i64 * gap.signum();
143
144 trace!(
145 "lamports_per_signature gap is {}, adjusting by {}",
146 gap,
147 gap_adjust
148 );
149
150 me.lamports_per_signature =
151 me.max_lamports_per_signature
152 .min(me.min_lamports_per_signature.max(
153 (base_fee_rate_governor.lamports_per_signature as i64 + gap_adjust)
154 as u64,
155 ));
156 }
157 } else {
158 me.lamports_per_signature = base_fee_rate_governor.target_lamports_per_signature;
159 me.min_lamports_per_signature = me.target_lamports_per_signature;
160 me.max_lamports_per_signature = me.target_lamports_per_signature;
161 }
162 debug!(
163 "new_derived(): lamports_per_signature: {}",
164 me.lamports_per_signature
165 );
166 me
167 }
168
169 pub fn clone_with_lamports_per_signature(&self, lamports_per_signature: u64) -> Self {
170 Self {
171 lamports_per_signature,
172 ..*self
173 }
174 }
175
176 pub fn burn(&self, fees: u64) -> (u64, u64) {
178 let burned = fees * u64::from(self.burn_percent) / 100;
179 (fees - burned, burned)
180 }
181
182 pub fn create_fee_calculator(&self) -> FeeCalculator {
184 FeeCalculator::new(self.lamports_per_signature)
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use {
191 super::*,
192 crate::{pubkey::Pubkey, system_instruction},
193 };
194
195 #[test]
196 fn test_fee_rate_governor_burn() {
197 let mut fee_rate_governor = FeeRateGovernor::default();
198 assert_eq!(fee_rate_governor.burn(2), (1, 1));
199
200 fee_rate_governor.burn_percent = 0;
201 assert_eq!(fee_rate_governor.burn(2), (2, 0));
202
203 fee_rate_governor.burn_percent = 100;
204 assert_eq!(fee_rate_governor.burn(2), (0, 2));
205 }
206
207 #[test]
208 #[allow(deprecated)]
209 fn test_fee_calculator_calculate_fee() {
210 let message = Message::default();
212 assert_eq!(FeeCalculator::default().calculate_fee(&message), 0);
213
214 assert_eq!(FeeCalculator::new(1).calculate_fee(&message), 0);
216
217 let pubkey0 = Pubkey::from([0; 32]);
219 let pubkey1 = Pubkey::from([1; 32]);
220 let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
221 let message = Message::new(&[ix0], Some(&pubkey0));
222 assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 2);
223
224 let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
226 let ix1 = system_instruction::transfer(&pubkey1, &pubkey0, 1);
227 let message = Message::new(&[ix0, ix1], Some(&pubkey0));
228 assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 4);
229 }
230
231 #[test]
232 #[allow(deprecated)]
233 fn test_fee_calculator_calculate_fee_secp256k1() {
234 use crate::instruction::Instruction;
235 let pubkey0 = Pubkey::from([0; 32]);
236 let pubkey1 = Pubkey::from([1; 32]);
237 let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
238 let mut secp_instruction = Instruction {
239 program_id: crate::secp256k1_program::id(),
240 accounts: vec![],
241 data: vec![],
242 };
243 let mut secp_instruction2 = Instruction {
244 program_id: crate::secp256k1_program::id(),
245 accounts: vec![],
246 data: vec![1],
247 };
248
249 let message = Message::new(
250 &[
251 ix0.clone(),
252 secp_instruction.clone(),
253 secp_instruction2.clone(),
254 ],
255 Some(&pubkey0),
256 );
257 assert_eq!(FeeCalculator::new(1).calculate_fee(&message), 2);
258
259 secp_instruction.data = vec![0];
260 secp_instruction2.data = vec![10];
261 let message = Message::new(&[ix0, secp_instruction, secp_instruction2], Some(&pubkey0));
262 assert_eq!(FeeCalculator::new(1).calculate_fee(&message), 11);
263 }
264
265 #[test]
266 fn test_fee_rate_governor_derived_default() {
267 solana_logger::setup();
268
269 let f0 = FeeRateGovernor::default();
270 assert_eq!(
271 f0.target_signatures_per_slot,
272 DEFAULT_TARGET_SIGNATURES_PER_SLOT
273 );
274 assert_eq!(
275 f0.target_lamports_per_signature,
276 DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE
277 );
278 assert_eq!(f0.lamports_per_signature, 0);
279
280 let f1 = FeeRateGovernor::new_derived(&f0, DEFAULT_TARGET_SIGNATURES_PER_SLOT);
281 assert_eq!(
282 f1.target_signatures_per_slot,
283 DEFAULT_TARGET_SIGNATURES_PER_SLOT
284 );
285 assert_eq!(
286 f1.target_lamports_per_signature,
287 DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE
288 );
289 assert_eq!(
290 f1.lamports_per_signature,
291 DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE / 2
292 ); }
294
295 #[test]
296 fn test_fee_rate_governor_derived_adjust() {
297 solana_logger::setup();
298
299 let mut f = FeeRateGovernor {
300 target_lamports_per_signature: 100,
301 target_signatures_per_slot: 100,
302 ..FeeRateGovernor::default()
303 };
304 f = FeeRateGovernor::new_derived(&f, 0);
305
306 let mut count = 0;
308 loop {
309 let last_lamports_per_signature = f.lamports_per_signature;
310
311 f = FeeRateGovernor::new_derived(&f, std::u64::MAX);
312 info!("[up] f.lamports_per_signature={}", f.lamports_per_signature);
313
314 if f.lamports_per_signature == last_lamports_per_signature {
316 break;
317 }
318 assert!(count < 1000);
320 count += 1;
321 }
322
323 let mut count = 0;
325 loop {
326 let last_lamports_per_signature = f.lamports_per_signature;
327 f = FeeRateGovernor::new_derived(&f, 0);
328
329 info!(
330 "[down] f.lamports_per_signature={}",
331 f.lamports_per_signature
332 );
333
334 if f.lamports_per_signature == last_lamports_per_signature {
336 break;
337 }
338
339 assert!(count < 1000);
341 count += 1;
342 }
343
344 let mut count = 0;
346 while f.lamports_per_signature != f.target_lamports_per_signature {
347 f = FeeRateGovernor::new_derived(&f, f.target_signatures_per_slot);
348 info!(
349 "[target] f.lamports_per_signature={}",
350 f.lamports_per_signature
351 );
352 assert!(count < 100);
354 count += 1;
355 }
356 }
357}