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