Make lamports_per_signature dynamic based on cluster load (#4562)

* Make lamports_per_signature dynamic based on cluster load

* Move transaction-fees.md to implemented
This commit is contained in:
Michael Vines
2019-06-10 22:18:32 -07:00
committed by GitHub
parent a4035a3c65
commit e4d8ea11ac
14 changed files with 391 additions and 115 deletions

View File

@ -1,16 +1,105 @@
use crate::message::Message;
use log::*;
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct FeeCalculator {
// The current cost of a signature This amount may increase/decrease over time based on
// cluster processing load.
pub lamports_per_signature: u64,
// The target cost of a signature when the cluster is operating around target_signatures_per_slot
// signatures
pub target_lamports_per_signature: u64,
// Used to estimate the desired processing capacity of the cluster. As the signatures for
// recent slots are fewer/greater than this value, lamports_per_signature will decrease/increase
// for the next slot. A value of 0 disables lamports_per_signature fee adjustments
pub target_signatures_per_slot: usize,
#[serde(skip)]
pub min_lamports_per_signature: u64,
#[serde(skip)]
pub max_lamports_per_signature: u64,
}
impl FeeCalculator {
pub fn new(lamports_per_signature: u64) -> Self {
Self {
lamports_per_signature,
pub fn new(target_lamports_per_signature: u64) -> Self {
let base_fee_calculator = Self {
target_lamports_per_signature,
lamports_per_signature: target_lamports_per_signature,
..FeeCalculator::default()
};
Self::new_derived(&base_fee_calculator, 0)
}
pub fn new_derived(
base_fee_calculator: &FeeCalculator,
latest_signatures_per_slot: usize,
) -> Self {
let mut me = base_fee_calculator.clone();
if me.target_signatures_per_slot > 0 {
// lamports_per_signature can range from 50% to 1000% of
// target_lamports_per_signature
//
// TODO: Are these decent limits?
//
me.min_lamports_per_signature = std::cmp::max(1, me.target_lamports_per_signature / 2);
me.max_lamports_per_signature = me.target_lamports_per_signature * 10;
// What the cluster should charge at `latest_signatures_per_slot`
let desired_lamports_per_signature = std::cmp::min(
me.max_lamports_per_signature,
std::cmp::max(
me.min_lamports_per_signature,
me.target_lamports_per_signature
* std::cmp::min(latest_signatures_per_slot, std::u32::MAX as usize) as u64
/ me.target_signatures_per_slot as u64,
),
);
trace!(
"desired_lamports_per_signature: {}",
desired_lamports_per_signature
);
let gap = desired_lamports_per_signature as i64
- base_fee_calculator.lamports_per_signature as i64;
if gap == 0 {
me.lamports_per_signature = desired_lamports_per_signature;
} else {
// Adjust fee by 5% of target_lamports_per_signature to produce a smooth increase/decrease in fees over time.
//
// TODO: Is this fee curve smooth enough or too smooth?
//
let gap_adjust =
std::cmp::max(1, me.target_lamports_per_signature as i64 / 20) * gap.signum();
trace!(
"lamports_per_signature gap is {}, adjusting by {}",
gap,
gap_adjust
);
me.lamports_per_signature = std::cmp::min(
me.max_lamports_per_signature,
std::cmp::max(
me.min_lamports_per_signature,
(base_fee_calculator.lamports_per_signature as i64 + gap_adjust) as u64,
),
);
}
} else {
me.lamports_per_signature = base_fee_calculator.target_lamports_per_signature;
}
debug!(
"new_derived(): lamports_per_signature: {}",
me.lamports_per_signature
);
me
}
pub fn calculate_fee(&self, message: &Message) -> u64 {
@ -46,4 +135,54 @@ mod tests {
let message = Message::new(vec![ix0, ix1]);
assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 4);
}
#[test]
fn test_fee_calculator_derived_default() {
solana_logger::setup();
let mut f0 = FeeCalculator::default();
assert_eq!(f0.target_signatures_per_slot, 0);
assert_eq!(f0.target_lamports_per_signature, 0);
assert_eq!(f0.lamports_per_signature, 0);
f0.target_lamports_per_signature = 42;
let f1 = FeeCalculator::new_derived(&f0, 4242);
assert_eq!(f1.target_signatures_per_slot, 0);
assert_eq!(f1.target_lamports_per_signature, 42);
assert_eq!(f1.lamports_per_signature, 42);
}
#[test]
fn test_fee_calculator_derived_adjust() {
solana_logger::setup();
let mut f = FeeCalculator::default();
f.target_lamports_per_signature = 100;
f.target_signatures_per_slot = 100;
f = FeeCalculator::new_derived(&f, 0);
// Ramp fees up
while f.lamports_per_signature < f.max_lamports_per_signature {
f = FeeCalculator::new_derived(&f, std::usize::MAX);
info!("[up] f.lamports_per_signature={}", f.lamports_per_signature);
}
// Ramp fees down
while f.lamports_per_signature > f.min_lamports_per_signature {
f = FeeCalculator::new_derived(&f, 0);
info!(
"[down] f.lamports_per_signature={}",
f.lamports_per_signature
);
}
// Arrive at target rate
while f.lamports_per_signature != f.target_lamports_per_signature {
f = FeeCalculator::new_derived(&f, f.target_signatures_per_slot);
info!(
"[target] f.lamports_per_signature={}",
f.lamports_per_signature
);
}
}
}