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 as u64
124 / me.target_signatures_per_slot as u64,
125 ));
126
127 trace!(
128 "desired_lamports_per_signature: {}",
129 desired_lamports_per_signature
130 );
131
132 let gap = desired_lamports_per_signature as i64
133 - base_fee_rate_governor.lamports_per_signature as i64;
134
135 if gap == 0 {
136 me.lamports_per_signature = desired_lamports_per_signature;
137 } else {
138 let gap_adjust =
141 std::cmp::max(1, me.target_lamports_per_signature / 20) as i64 * gap.signum();
142
143 trace!(
144 "lamports_per_signature gap is {}, adjusting by {}",
145 gap,
146 gap_adjust
147 );
148
149 me.lamports_per_signature =
150 me.max_lamports_per_signature
151 .min(me.min_lamports_per_signature.max(
152 (base_fee_rate_governor.lamports_per_signature as i64 + gap_adjust)
153 as u64,
154 ));
155 }
156 } else {
157 me.lamports_per_signature = base_fee_rate_governor.target_lamports_per_signature;
158 me.min_lamports_per_signature = me.target_lamports_per_signature;
159 me.max_lamports_per_signature = me.target_lamports_per_signature;
160 }
161 debug!(
162 "new_derived(): lamports_per_signature: {}",
163 me.lamports_per_signature
164 );
165 me
166 }
167
168 pub fn clone_with_lamports_per_signature(&self, lamports_per_signature: u64) -> Self {
169 Self {
170 lamports_per_signature,
171 ..*self
172 }
173 }
174
175 pub fn burn(&self, fees: u64) -> (u64, u64) {
177 let burned = fees * u64::from(self.burn_percent) / 100;
178 (fees - burned, burned)
179 }
180
181 pub fn create_fee_calculator(&self) -> FeeCalculator {
183 FeeCalculator::new(self.lamports_per_signature)
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use {
190 super::*,
191 crate::{pubkey::Pubkey, system_instruction},
192 };
193
194 #[test]
195 fn test_fee_rate_governor_burn() {
196 let mut fee_rate_governor = FeeRateGovernor::default();
197 assert_eq!(fee_rate_governor.burn(2), (1, 1));
198
199 fee_rate_governor.burn_percent = 0;
200 assert_eq!(fee_rate_governor.burn(2), (2, 0));
201
202 fee_rate_governor.burn_percent = 100;
203 assert_eq!(fee_rate_governor.burn(2), (0, 2));
204 }
205
206 #[test]
207 #[allow(deprecated)]
208 fn test_fee_calculator_calculate_fee() {
209 let message = Message::default();
211 assert_eq!(FeeCalculator::default().calculate_fee(&message), 0);
212
213 assert_eq!(FeeCalculator::new(1).calculate_fee(&message), 0);
215
216 let pubkey0 = Pubkey::from([0; 32]);
218 let pubkey1 = Pubkey::from([1; 32]);
219 let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
220 let message = Message::new(&[ix0], Some(&pubkey0));
221 assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 2);
222
223 let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
225 let ix1 = system_instruction::transfer(&pubkey1, &pubkey0, 1);
226 let message = Message::new(&[ix0, ix1], Some(&pubkey0));
227 assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 4);
228 }
229
230 #[test]
231 #[allow(deprecated)]
232 fn test_fee_calculator_calculate_fee_secp256k1() {
233 use crate::instruction::Instruction;
234 let pubkey0 = Pubkey::from([0; 32]);
235 let pubkey1 = Pubkey::from([1; 32]);
236 let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
237 let mut secp_instruction = Instruction {
238 program_id: crate::secp256k1_program::id(),
239 accounts: vec![],
240 data: vec![],
241 };
242 let mut secp_instruction2 = Instruction {
243 program_id: crate::secp256k1_program::id(),
244 accounts: vec![],
245 data: vec![1],
246 };
247
248 let message = Message::new(
249 &[
250 ix0.clone(),
251 secp_instruction.clone(),
252 secp_instruction2.clone(),
253 ],
254 Some(&pubkey0),
255 );
256 assert_eq!(FeeCalculator::new(1).calculate_fee(&message), 2);
257
258 secp_instruction.data = vec![0];
259 secp_instruction2.data = vec![10];
260 let message = Message::new(&[ix0, secp_instruction, secp_instruction2], Some(&pubkey0));
261 assert_eq!(FeeCalculator::new(1).calculate_fee(&message), 11);
262 }
263
264 #[test]
265 fn test_fee_rate_governor_derived_default() {
266 solana_logger::setup();
267
268 let f0 = FeeRateGovernor::default();
269 assert_eq!(
270 f0.target_signatures_per_slot,
271 DEFAULT_TARGET_SIGNATURES_PER_SLOT
272 );
273 assert_eq!(
274 f0.target_lamports_per_signature,
275 DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE
276 );
277 assert_eq!(f0.lamports_per_signature, 0);
278
279 let f1 = FeeRateGovernor::new_derived(&f0, DEFAULT_TARGET_SIGNATURES_PER_SLOT);
280 assert_eq!(
281 f1.target_signatures_per_slot,
282 DEFAULT_TARGET_SIGNATURES_PER_SLOT
283 );
284 assert_eq!(
285 f1.target_lamports_per_signature,
286 DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE
287 );
288 assert_eq!(
289 f1.lamports_per_signature,
290 DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE / 2
291 ); }
293
294 #[test]
295 fn test_fee_rate_governor_derived_adjust() {
296 solana_logger::setup();
297
298 let mut f = FeeRateGovernor {
299 target_lamports_per_signature: 100,
300 target_signatures_per_slot: 100,
301 ..FeeRateGovernor::default()
302 };
303 f = FeeRateGovernor::new_derived(&f, 0);
304
305 let mut count = 0;
307 loop {
308 let last_lamports_per_signature = f.lamports_per_signature;
309
310 f = FeeRateGovernor::new_derived(&f, std::u64::MAX);
311 info!("[up] f.lamports_per_signature={}", f.lamports_per_signature);
312
313 if f.lamports_per_signature == last_lamports_per_signature {
315 break;
316 }
317 assert!(count < 1000);
319 count += 1;
320 }
321
322 let mut count = 0;
324 loop {
325 let last_lamports_per_signature = f.lamports_per_signature;
326 f = FeeRateGovernor::new_derived(&f, 0);
327
328 info!(
329 "[down] f.lamports_per_signature={}",
330 f.lamports_per_signature
331 );
332
333 if f.lamports_per_signature == last_lamports_per_signature {
335 break;
336 }
337
338 assert!(count < 1000);
340 count += 1;
341 }
342
343 let mut count = 0;
345 while f.lamports_per_signature != f.target_lamports_per_signature {
346 f = FeeRateGovernor::new_derived(&f, f.target_signatures_per_slot);
347 info!(
348 "[target] f.lamports_per_signature={}",
349 f.lamports_per_signature
350 );
351 assert!(count < 100);
353 count += 1;
354 }
355 }
356}