1use ethnum::i256;
19#[cfg(feature = "proptest")]
20use proptest_derive::Arbitrary;
21use rand::{distributions::Standard, prelude::Distribution};
22use serde::{Deserialize, Serialize};
23use serialize::{Deserializable, Serializable, Tagged};
24use std::{
25 fmt::Debug,
26 iter::Sum,
27 ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign},
28};
29
30#[derive(
31 Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serializable, Serialize, Deserialize, Default,
32)]
33#[tag = "cost-duration[v1]"]
34#[serde(transparent)]
35#[cfg_attr(feature = "proptest", derive(Arbitrary))]
36pub struct CostDuration(u64);
38
39impl Debug for CostDuration {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self.0 {
42 1..1_000 => write!(f, "{}ps", self.0),
43 1_000..1_000_000 => write!(f, "{:.3}ns", self.0 as f64 / 1e3f64),
44 1_000_000..1_000_000_000 => write!(f, "{:.3}μs", self.0 as f64 / 1e6f64),
45 1_000_000_000..1_000_000_000_000 => write!(f, "{:.3}ms", self.0 as f64 / 1e9f64),
46 _ => write!(f, "{:.3}s", self.0 as f64 / 1e12f64),
47 }
48 }
49}
50
51impl Distribution<CostDuration> for Standard {
52 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> CostDuration {
53 CostDuration(self.sample(rng))
54 }
55}
56
57impl Sum for CostDuration {
58 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
59 CostDuration(
60 iter.map(|i| i.0)
61 .reduce(|a, b| a.saturating_add(b))
62 .unwrap_or(0),
63 )
64 }
65}
66
67impl SubAssign for CostDuration {
68 fn sub_assign(&mut self, rhs: Self) {
69 *self = *self - rhs;
70 }
71}
72
73impl AddAssign for CostDuration {
74 fn add_assign(&mut self, rhs: Self) {
75 *self = *self + rhs;
76 }
77}
78
79impl Add for CostDuration {
80 type Output = CostDuration;
81 fn add(self, rhs: Self) -> Self::Output {
82 CostDuration(self.0.saturating_add(rhs.0))
83 }
84}
85
86impl Sub for CostDuration {
87 type Output = CostDuration;
88 fn sub(self, rhs: Self) -> Self::Output {
89 CostDuration(self.0.saturating_sub(rhs.0))
90 }
91}
92
93impl Mul<CostDuration> for usize {
94 type Output = CostDuration;
95 fn mul(self, rhs: CostDuration) -> Self::Output {
96 CostDuration((self as u64).saturating_mul(rhs.0))
97 }
98}
99
100impl Mul<CostDuration> for u64 {
101 type Output = CostDuration;
102 fn mul(self, rhs: CostDuration) -> Self::Output {
103 CostDuration(self.saturating_mul(rhs.0))
104 }
105}
106
107impl Mul<usize> for CostDuration {
108 type Output = CostDuration;
109 fn mul(self, rhs: usize) -> Self::Output {
110 CostDuration(self.0.saturating_mul(rhs as u64))
111 }
112}
113
114impl Mul<u64> for CostDuration {
115 type Output = CostDuration;
116 fn mul(self, rhs: u64) -> Self::Output {
117 CostDuration(self.0.saturating_mul(rhs))
118 }
119}
120
121impl Mul<f64> for CostDuration {
122 type Output = CostDuration;
123 fn mul(self, rhs: f64) -> Self::Output {
124 CostDuration((self.0 as f64 * rhs) as u64)
125 }
126}
127
128impl Mul<CostDuration> for f64 {
129 type Output = CostDuration;
130 fn mul(self, rhs: CostDuration) -> Self::Output {
131 CostDuration((self * rhs.0 as f64) as u64)
132 }
133}
134
135impl Div for CostDuration {
136 type Output = FixedPoint;
137 fn div(self, rhs: Self) -> Self::Output {
138 FixedPoint::from_u64_div(self.0, rhs.0)
139 }
140}
141
142impl Div<u64> for CostDuration {
143 type Output = CostDuration;
144 fn div(self, rhs: u64) -> Self::Output {
145 CostDuration(self.0.div_ceil(rhs))
146 }
147}
148
149#[derive(
150 PartialEq,
151 Eq,
152 PartialOrd,
153 Ord,
154 Debug,
155 Copy,
156 Clone,
157 Serializable,
158 Serialize,
159 Deserialize,
160 Default,
161)]
162#[tag = "synthetic-cost[v1]"]
163pub struct SyntheticCost {
166 #[serde(rename = "readTime")]
167 pub read_time: CostDuration,
169 #[serde(rename = "computeTime")]
170 pub compute_time: CostDuration,
172 #[serde(rename = "blockUsage")]
173 pub block_usage: u64,
175 #[serde(rename = "bytesWritten")]
176 pub bytes_written: u64,
180 #[serde(rename = "bytesChurned")]
181 pub bytes_churned: u64,
183}
184
185impl Mul<f64> for SyntheticCost {
186 type Output = SyntheticCost;
187 fn mul(self, rhs: f64) -> Self::Output {
188 SyntheticCost {
189 compute_time: self.compute_time * rhs,
190 read_time: self.read_time * rhs,
191 block_usage: (self.block_usage as f64 * rhs).ceil() as u64,
192 bytes_written: (self.bytes_written as f64 * rhs).ceil() as u64,
193 bytes_churned: (self.bytes_churned as f64 * rhs).ceil() as u64,
194 }
195 }
196}
197
198impl Add for SyntheticCost {
199 type Output = SyntheticCost;
200 fn add(self, rhs: Self) -> Self::Output {
201 SyntheticCost {
202 read_time: self.read_time + rhs.read_time,
203 compute_time: self.compute_time + rhs.compute_time,
204 block_usage: self.block_usage.saturating_add(rhs.block_usage),
205 bytes_written: self.bytes_written.saturating_add(rhs.bytes_written),
206 bytes_churned: self.bytes_churned.saturating_add(rhs.bytes_churned),
207 }
208 }
209}
210
211#[derive(
212 PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Serialize, Deserialize, Serializable,
213)]
214#[tag = "normalized-cost[v1]"]
215pub struct NormalizedCost {
217 #[serde(rename = "readTime")]
218 pub read_time: FixedPoint,
220 #[serde(rename = "computeTime")]
221 pub compute_time: FixedPoint,
223 #[serde(rename = "blockUsage")]
224 pub block_usage: FixedPoint,
226 #[serde(rename = "bytesWritten")]
227 pub bytes_written: FixedPoint,
229 #[serde(rename = "bytesChurned")]
230 pub bytes_churned: FixedPoint,
232}
233
234impl NormalizedCost {
235 pub const ZERO: NormalizedCost = NormalizedCost {
237 read_time: FixedPoint::ZERO,
238 compute_time: FixedPoint::ZERO,
239 block_usage: FixedPoint::ZERO,
240 bytes_written: FixedPoint::ZERO,
241 bytes_churned: FixedPoint::ZERO,
242 };
243}
244
245impl SyntheticCost {
246 pub const ZERO: SyntheticCost = SyntheticCost {
248 read_time: CostDuration::ZERO,
249 compute_time: CostDuration::ZERO,
250 block_usage: 0,
251 bytes_written: 0,
252 bytes_churned: 0,
253 };
254
255 pub fn max_time(&self) -> CostDuration {
257 CostDuration::max(self.read_time, self.compute_time)
258 }
259
260 pub fn normalize(self, limits: SyntheticCost) -> Option<NormalizedCost> {
262 let res = NormalizedCost {
263 read_time: self.read_time / limits.read_time,
264 compute_time: self.compute_time / limits.compute_time,
265 block_usage: FixedPoint::from_u64_div(self.block_usage, limits.block_usage),
266 bytes_written: FixedPoint::from_u64_div(self.bytes_written, limits.bytes_written),
267 bytes_churned: FixedPoint::from_u64_div(self.bytes_churned, limits.bytes_churned),
268 };
269 let vals = [
270 &res.read_time,
271 &res.compute_time,
272 &res.block_usage,
273 &res.bytes_written,
274 &res.bytes_churned,
275 ];
276 if vals.into_iter().any(|val| *val > FixedPoint::ONE) {
277 None
278 } else {
279 Some(res)
280 }
281 }
282}
283
284#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Serializable, Serialize, Deserialize)]
285#[tag = "fee-prices[v2]"]
286pub struct FeePrices {
290 #[serde(rename = "overallPrice")]
292 #[cfg_attr(
293 feature = "fixed-point-custom-serde",
294 serde(with = "fixed_point_custom_serde")
295 )]
296 pub overall_price: FixedPoint,
297 #[serde(rename = "readFactor")]
299 #[cfg_attr(
300 feature = "fixed-point-custom-serde",
301 serde(with = "fixed_point_custom_serde")
302 )]
303 pub read_factor: FixedPoint,
304 #[serde(rename = "computeFactor")]
306 #[cfg_attr(
307 feature = "fixed-point-custom-serde",
308 serde(with = "fixed_point_custom_serde")
309 )]
310 pub compute_factor: FixedPoint,
311 #[serde(rename = "blockUsageFactor")]
313 #[cfg_attr(
314 feature = "fixed-point-custom-serde",
315 serde(with = "fixed_point_custom_serde")
316 )]
317 pub block_usage_factor: FixedPoint,
318 #[serde(rename = "writeFactor")]
320 #[cfg_attr(
321 feature = "fixed-point-custom-serde",
322 serde(with = "fixed_point_custom_serde")
323 )]
324 pub write_factor: FixedPoint,
325}
326
327impl FeePrices {
328 pub fn update_from_fullness(
339 &self,
340 detailed_block_fullness: NormalizedCost,
341 overall_block_fullness: FixedPoint,
342 min_ratio: FixedPoint,
343 a: FixedPoint,
344 ) -> Self {
345 let multiplier = |frac| price_adjustment_function(frac, a) + FixedPoint::ONE;
346 let mut updated = FeePrices {
347 overall_price: self.overall_price * multiplier(overall_block_fullness),
348 read_factor: self.read_factor * multiplier(detailed_block_fullness.read_time),
349 compute_factor: self.compute_factor * multiplier(detailed_block_fullness.compute_time),
350 block_usage_factor: self.block_usage_factor
351 * multiplier(detailed_block_fullness.block_usage),
352 write_factor: self.write_factor
353 * multiplier(FixedPoint::max(
354 detailed_block_fullness.bytes_written,
355 detailed_block_fullness.bytes_churned,
356 )),
357 };
358 let mut dimensions = [
359 &mut updated.read_factor,
360 &mut updated.compute_factor,
361 &mut updated.block_usage_factor,
362 &mut updated.write_factor,
363 ];
364 let most_expensive_dimension = **dimensions
365 .iter()
366 .max()
367 .expect("max of 4 elements must exist");
368 for dim in dimensions.iter_mut() {
369 **dim = FixedPoint::max(**dim, most_expensive_dimension * min_ratio);
370 }
371 const MIN_COST: FixedPoint = FixedPoint(100);
375 updated.overall_price = FixedPoint::max(updated.overall_price, MIN_COST);
376 let factor_mean = dimensions
378 .iter()
379 .map(|d| **d)
380 .fold(FixedPoint::ZERO, FixedPoint::add)
381 / FixedPoint::from_u64_div(4, 1);
382 for dim in dimensions.into_iter() {
383 *dim = *dim / factor_mean;
384 }
385 updated
386 }
387
388 pub fn overall_cost(&self, tx_normalized: &NormalizedCost) -> FixedPoint {
393 let read_cost = self.read_factor * tx_normalized.read_time;
394 let compute_cost = self.compute_factor * tx_normalized.compute_time;
395 let block_usage_cost = self.block_usage_factor * tx_normalized.block_usage;
396 let write_cost = self.write_factor * tx_normalized.bytes_written;
397 let churn_cost = self.write_factor * tx_normalized.bytes_churned;
398 let utilization_cost =
399 FixedPoint::max(read_cost, FixedPoint::max(compute_cost, block_usage_cost));
400 self.overall_price * (utilization_cost + write_cost + churn_cost)
401 }
402}
403
404#[derive(
405 PartialEq,
406 Eq,
407 PartialOrd,
408 Ord,
409 Debug,
410 Copy,
411 Clone,
412 Serializable,
413 Serialize,
414 Deserialize,
415 Default,
416)]
417#[tag = "running-cost[v1]"]
418pub struct RunningCost {
421 #[serde(rename = "readTime")]
422 pub read_time: CostDuration,
424 #[serde(rename = "computeTime")]
425 pub compute_time: CostDuration,
427 #[serde(rename = "bytesWritten")]
428 pub bytes_written: u64,
432 #[serde(rename = "bytesDeleted")]
433 pub bytes_deleted: u64,
435}
436
437impl RunningCost {
438 pub const ZERO: RunningCost = RunningCost {
440 read_time: CostDuration::ZERO,
441 compute_time: CostDuration::ZERO,
442 bytes_written: 0,
443 bytes_deleted: 0,
444 };
445
446 pub const fn compute(time: CostDuration) -> RunningCost {
448 RunningCost {
449 read_time: CostDuration::ZERO,
450 compute_time: time,
451 bytes_written: 0,
452 bytes_deleted: 0,
453 }
454 }
455
456 pub fn max_time(&self) -> CostDuration {
458 CostDuration::max(self.read_time, self.compute_time)
459 }
460}
461
462impl Distribution<RunningCost> for Standard {
463 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> RunningCost {
464 RunningCost {
465 read_time: self.sample(rng),
466 compute_time: self.sample(rng),
467 bytes_written: self.sample(rng),
468 bytes_deleted: self.sample(rng),
469 }
470 }
471}
472
473impl From<RunningCost> for SyntheticCost {
474 fn from(value: RunningCost) -> Self {
475 let bytes_written = value.bytes_written.saturating_sub(value.bytes_deleted);
477 SyntheticCost {
478 read_time: value.read_time,
479 compute_time: value.compute_time,
480 block_usage: 0,
481 bytes_written,
482 bytes_churned: value.bytes_written - bytes_written,
483 }
484 }
485}
486
487impl From<SyntheticCost> for RunningCost {
489 fn from(value: SyntheticCost) -> Self {
490 RunningCost {
491 read_time: value.read_time,
492 compute_time: value.compute_time,
493 bytes_written: value.bytes_written,
494 bytes_deleted: 0,
495 }
496 }
497}
498
499impl std::iter::Sum for RunningCost {
500 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
501 let mut res = RunningCost::ZERO;
502 for i in iter {
503 res += i;
504 }
505 res
506 }
507}
508
509impl Mul<f64> for RunningCost {
510 type Output = RunningCost;
511 fn mul(self, rhs: f64) -> Self::Output {
512 RunningCost {
513 compute_time: self.compute_time * rhs,
514 read_time: self.read_time * rhs,
515 bytes_written: (self.bytes_written as f64 * rhs).ceil() as u64,
516 bytes_deleted: (self.bytes_deleted as f64 * rhs).ceil() as u64,
517 }
518 }
519}
520
521impl Mul<usize> for RunningCost {
522 type Output = RunningCost;
523 fn mul(self, rhs: usize) -> Self::Output {
524 self * rhs as u64
525 }
526}
527
528impl Mul<u64> for RunningCost {
529 type Output = RunningCost;
530 fn mul(self, rhs: u64) -> Self::Output {
531 RunningCost {
532 compute_time: self.compute_time * rhs,
533 read_time: self.read_time * rhs,
534 bytes_written: self.bytes_written.saturating_mul(rhs),
535 bytes_deleted: self.bytes_deleted.saturating_mul(rhs),
536 }
537 }
538}
539
540impl Add for RunningCost {
541 type Output = RunningCost;
542 fn add(self, rhs: Self) -> Self::Output {
543 RunningCost {
544 read_time: self.read_time + rhs.read_time,
545 compute_time: self.compute_time + rhs.compute_time,
546 bytes_written: self.bytes_written.saturating_add(rhs.bytes_written),
547 bytes_deleted: self.bytes_deleted.saturating_add(rhs.bytes_deleted),
548 }
549 }
550}
551
552impl AddAssign for RunningCost {
553 fn add_assign(&mut self, rhs: Self) {
554 *self = *self + rhs;
555 }
556}
557
558impl CostDuration {
559 pub const ZERO: CostDuration = CostDuration(0);
561 pub const SECOND: CostDuration = CostDuration(1_000_000_000_000);
563 pub const fn from_picoseconds(picoseconds: u64) -> CostDuration {
565 CostDuration(picoseconds)
566 }
567
568 pub const fn into_picoseconds(self) -> u64 {
570 self.0
571 }
572}
573
574#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serializable)]
575#[tag = "fixed-point[v1]"]
576pub struct FixedPoint(i128);
583
584impl Serialize for FixedPoint {
585 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
586 where
587 S: serde::Serializer,
588 {
589 serializer.serialize_f64((*self).into())
590 }
591}
592
593impl<'de> Deserialize<'de> for FixedPoint {
594 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
595 where
596 D: serde::Deserializer<'de>,
597 {
598 f64::deserialize(deserializer).map(Into::into)
599 }
600}
601
602impl Debug for FixedPoint {
603 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
604 write!(f, "FixedPoint({})", f64::from(*self))
605 }
606}
607
608impl From<f64> for FixedPoint {
609 fn from(float: f64) -> FixedPoint {
610 FixedPoint((float * 2f64.powi(64)) as i128)
611 }
612}
613
614impl From<FixedPoint> for i128 {
615 fn from(fp: FixedPoint) -> i128 {
616 fp.0 >> 64
617 }
618}
619
620impl From<FixedPoint> for f64 {
621 fn from(fp: FixedPoint) -> f64 {
622 fp.0 as f64 / 2f64.powi(64)
623 }
624}
625
626pub mod fixed_point_custom_serde {
628 use super::FixedPoint;
629
630 pub fn serialize<S>(value: &FixedPoint, s: S) -> Result<S::Ok, S::Error>
632 where
633 S: serde::Serializer,
634 {
635 s.serialize_i128(value.0)
636 }
637
638 pub fn deserialize<'de, D>(d: D) -> Result<FixedPoint, D::Error>
640 where
641 D: serde::Deserializer<'de>,
642 {
643 let inner_value = serde::Deserialize::deserialize(d)?;
644 Ok(FixedPoint(inner_value))
645 }
646}
647
648impl FixedPoint {
654 pub const ZERO: FixedPoint = FixedPoint(0);
656 pub const ONE: FixedPoint = FixedPoint::from_u64_div(1, 1);
658 pub const MIN_POSITIVE: FixedPoint = FixedPoint(1);
660 pub const MAX: FixedPoint = FixedPoint(i128::MAX);
662
663 pub fn into_atomic_units(self, base_unit: u128) -> u128 {
671 let raw = i256::from(self.0) * i256::from(base_unit);
672 let (res, rem) = raw.div_rem(i256::from(1u128 << 64));
673 let res = if rem <= 0 { 0 } else { 1 } + res;
674 if res < 0 {
675 0
676 } else if res > i256::from(u128::MAX) {
677 u128::MAX
678 } else {
679 res.as_u128()
680 }
681 }
682
683 pub fn powi(self, mut exp: i32) -> Self {
685 match exp {
686 i32::MIN..=-1 => FixedPoint::ONE / self.powi(-exp),
687 0 => FixedPoint::ONE,
688 1..=i32::MAX => {
689 let mut acc = FixedPoint::ONE;
690 let mut cur = self;
691 while exp >= 1 {
692 if exp & 0b1 != 0 {
693 acc = acc * cur;
694 }
695 cur = cur * cur;
696 exp >>= 1;
697 }
698 acc
699 }
700 }
701 }
702
703 pub const fn from_u64_div(a: u64, b: u64) -> FixedPoint {
708 if b == 0 {
712 return FixedPoint(i128::MAX);
713 }
714 let ashift = (a as u128) << 64;
715 let c = ashift.div_ceil(b as u128) as i128;
716 FixedPoint(c)
717 }
718
719 pub fn from_u128_div(a: u128, b: u128) -> FixedPoint {
721 if b == 0 {
725 return FixedPoint(i128::MAX);
726 }
727 let ashift = i256::from(a) * i256::from(1u128 << 64);
728 let (c, rem) = ashift.div_rem(i256::from(b));
729 let c = if rem == i256::ZERO {
730 i256::from(0u64)
731 } else {
732 i256::from(1u64)
733 } + c;
734 if c > i256::from(u128::MAX) {
735 FixedPoint(i128::MAX)
736 } else {
737 FixedPoint(c.as_i128())
738 }
739 }
740}
741
742impl Add for FixedPoint {
743 type Output = Self;
744 fn add(self, rhs: Self) -> Self::Output {
745 FixedPoint(self.0.saturating_add(rhs.0))
749 }
750}
751
752impl Sub for FixedPoint {
753 type Output = FixedPoint;
754 fn sub(self, rhs: Self) -> Self::Output {
755 FixedPoint(self.0.saturating_sub(rhs.0))
759 }
760}
761
762impl Neg for FixedPoint {
763 type Output = FixedPoint;
764 fn neg(self) -> Self::Output {
765 FixedPoint(self.0.saturating_neg())
766 }
767}
768
769impl Mul for FixedPoint {
770 type Output = Self;
771 fn mul(self, rhs: Self) -> Self::Output {
772 let ab = i256::from(self.0) * rhs.0;
776 let c = i256::min(i256::from(i128::MAX), ab >> 64).as_i128();
777 FixedPoint(c)
778 }
779}
780
781impl Div for FixedPoint {
782 type Output = Self;
783 fn div(self, rhs: Self) -> Self::Output {
785 if rhs.0 == 0 {
788 return FixedPoint(i128::MAX);
790 }
791 let a_abs = self.0.unsigned_abs();
792 let b_abs = rhs.0.unsigned_abs();
793 let a_sign = self.0.signum();
794 let b_sign = rhs.0.signum();
795 FixedPoint(FixedPoint::from_u128_div(a_abs, b_abs).0 * a_sign * b_sign)
796 }
797}
798
799pub fn price_adjustment_function(usage: FixedPoint, a: FixedPoint) -> FixedPoint {
802 const POINTS: &[FixedPoint] = &[
807 FixedPoint(-84764999863455367168), FixedPoint(-84764999863455367168), FixedPoint(-71791413020114739200), FixedPoint(-64122702906348363776), FixedPoint(-58624745660900909056), FixedPoint(-54315312289337933824), FixedPoint(-50756867729459126272), FixedPoint(-47715996328766365696), FixedPoint(-45053350700638961664), FixedPoint(-42679031418590216192), FixedPoint(-40531639450585882624), FixedPoint(-38567365939524018176), FixedPoint(-36753849332779208704), FixedPoint(-35066499762404089856), FixedPoint(-33486189434148528128), FixedPoint(-31997741738730885120), FixedPoint(-30588908944945000448), FixedPoint(-29249660330515177472), FixedPoint(-27971674063185141760), FixedPoint(-26747966611833823232), FixedPoint(-25572617290405310464), FixedPoint(-24440560042528645120), FixedPoint(-23347423671542177792), FixedPoint(-22289407577085239296), FixedPoint(-21263183918842343424), FixedPoint(-20265819725292941312), FixedPoint(-19294714246602784768), FixedPoint(-18347548093616457728), FixedPoint(-17422241585731162112), FixedPoint(-16516920363702972416), FixedPoint(-15629886784764432384), FixedPoint(-14759595957603760128), FixedPoint(-13904635528415858688), FixedPoint(-13063708520358164480), FixedPoint(-12235618674138603520), FixedPoint(-11419257849061355520), FixedPoint(-10613595130224742400), FixedPoint(-9817667354921738240), FixedPoint(-9030570824192866304), FixedPoint(-8251454007294845952), FixedPoint(-7479511080090284032), FixedPoint(-6713976164925600768), FixedPoint(-5954118160879564800), FixedPoint(-5199236070424976384), FixedPoint(-4448654742390539264), FixedPoint(-3701720962283612672), FixedPoint(-2957799830037197824), FixedPoint(-2216271372462491904), FixedPoint(-1476527343420476672), FixedPoint(-737968169202023424), FixedPoint(0), FixedPoint(737968169202024192), FixedPoint(1476527343420477440), FixedPoint(2216271372462496512), FixedPoint(2957799830037204480), FixedPoint(3701720962283612672), FixedPoint(4448654742390539264), FixedPoint(5199236070424971264), FixedPoint(5954118160879561728), FixedPoint(6713976164925597696), FixedPoint(7479511080090281984), FixedPoint(8251454007294848000), FixedPoint(9030570824192860160), FixedPoint(9817667354921742336), FixedPoint(10613595130224742400), FixedPoint(11419257849061359616), FixedPoint(12235618674138605568), FixedPoint(13063708520358168576), FixedPoint(13904635528415862784), FixedPoint(14759595957603747840), FixedPoint(15629886784764430336), FixedPoint(16516920363702964224), FixedPoint(17422241585731166208), FixedPoint(18347548093616463872), FixedPoint(19294714246602788864), FixedPoint(20265819725292945408), FixedPoint(21263183918842335232), FixedPoint(22289407577085243392), FixedPoint(23347423671542181888), FixedPoint(24440560042528653312), FixedPoint(25572617290405310464), FixedPoint(26747966611833827328), FixedPoint(27971674063185145856), FixedPoint(29249660330515173376), FixedPoint(30588908944945000448), FixedPoint(31997741738730881024), FixedPoint(33486189434148524032), FixedPoint(35066499762404098048), FixedPoint(36753849332779192320), FixedPoint(38567365939524001792), FixedPoint(40531639450585874432), FixedPoint(42679031418590240768), FixedPoint(45053350700638978048), FixedPoint(47715996328766390272), FixedPoint(50756867729459134464), FixedPoint(54315312289337958400), FixedPoint(58624745660900876288), FixedPoint(64122702906348298240), FixedPoint(71791413020114722816), FixedPoint(84764999863455252480), FixedPoint(84764999863455252480), ];
909
910 const POINT_LEN_FP: i128 = FixedPoint::ONE.0 / (POINTS.len() as i128 - 1);
912 let bucket = (usage.0 / POINT_LEN_FP).clamp(0, POINTS.len() as i128 - 2) as usize;
914 let b = POINTS[bucket];
915 let c = POINTS[bucket + 1];
916 let frac = FixedPoint::from_u128_div(
917 i128::max(0, usage.0 - bucket as i128 * POINT_LEN_FP) as u128,
918 POINT_LEN_FP as u128,
919 );
920 (b * (FixedPoint::ONE - frac) + c * frac) / a
921}
922
923#[cfg(test)]
924fn price_adjustment_target(usage: f64, a: f64) -> f64 {
926 -(1f64 / usage.clamp(0.01, 0.99) - 1f64).ln() / a
930}
931
932#[cfg(test)]
933mod tests {
934 use super::*;
935 use proptest::prelude::*;
936
937 fn within_permissible_error(a: f64, b: f64, epsilon: f64) {
938 if a - epsilon >= b || a + epsilon <= b {
939 panic!("{a} != {b} (with error {epsilon})");
940 }
941 }
942
943 #[test]
944 fn test_price_adjustment_static() {
952 const N: usize = 100;
953 let xs = (0..=N).map(|x| x as f64 / N as f64).collect::<Vec<_>>();
954 let target = xs
955 .iter()
956 .map(|x| price_adjustment_target(*x, 1f64))
957 .collect::<Vec<_>>();
958 let approx = xs
959 .iter()
960 .map(|x| f64::from(price_adjustment_function((*x).into(), FixedPoint::ONE)))
961 .collect::<Vec<_>>();
962 assert_eq!(price_adjustment_target(0.5, 1f64), 0f64);
963 for (x, point) in xs.iter().zip(target.iter()) {
966 println!(
967 "FixedPoint({}), // {x:0.2} => {point:0.5}",
968 FixedPoint::from(*point).0
969 );
970 }
971 for ((t, a), _x) in target.iter().zip(approx.iter()).zip(xs.iter()) {
972 within_permissible_error(*t, *a, 1e-9f64);
973 }
974 }
975
976 #[test]
977 fn test_pricing_cant_get_stuck() {
978 let mut cur = FeePrices {
979 overall_price: FixedPoint::ONE,
980 block_usage_factor: FixedPoint::ONE,
981 read_factor: FixedPoint::ONE,
982 write_factor: FixedPoint::ONE,
983 compute_factor: FixedPoint::ONE,
984 };
985 for _ in 0..10_000 {
986 cur = cur.update_from_fullness(
987 NormalizedCost {
988 read_time: FixedPoint::ZERO,
989 compute_time: FixedPoint::ZERO,
990 block_usage: FixedPoint::ZERO,
991 bytes_written: FixedPoint::ZERO,
992 bytes_churned: FixedPoint::ZERO,
993 },
994 FixedPoint::ZERO,
995 FixedPoint::from_u64_div(1, 4),
996 FixedPoint::from_u64_div(100, 1),
997 );
998 }
999 let dims = |cur: &FeePrices| {
1000 [
1001 cur.overall_price,
1002 cur.block_usage_factor,
1003 cur.read_factor,
1004 cur.write_factor,
1005 cur.compute_factor,
1006 ]
1007 };
1008 assert!(dims(&cur).into_iter().all(|price| price > FixedPoint::ZERO));
1009 let fraction = FixedPoint::from_u64_div(3, 4);
1010 let fullness = NormalizedCost {
1011 block_usage: fraction,
1012 compute_time: fraction,
1013 read_time: fraction,
1014 bytes_written: fraction,
1015 bytes_churned: fraction,
1016 };
1017 let next = cur.update_from_fullness(
1018 fullness,
1019 fraction,
1020 FixedPoint::from_u64_div(1, 4),
1021 FixedPoint::from_u64_div(100, 1),
1022 );
1023 assert!(next.overall_price > cur.overall_price);
1024 assert_eq!(next.compute_factor, cur.compute_factor);
1025 }
1026
1027 proptest! {
1028 #[test]
1029 fn test_price_adjustment(usage in (0f64..1f64)) {
1030 let a = price_adjustment_target(usage, 1f64);
1031 let b = f64::from(price_adjustment_function(FixedPoint::from(usage), FixedPoint::ONE));
1032 let epsilon = (a / 50f64).abs();
1033 within_permissible_error(
1034 a, b, epsilon
1035 );
1036 }
1037 #[test]
1038 fn fixed_point_powi(a in (1e-1f64..1e1f64), b in (-15..15)) {
1039 if a != 0.0 {
1040 let pure_error_factor = a.powi(b) / f64::from(FixedPoint::from(a).powi(b));
1041 let non_compounded_error_factor = pure_error_factor.powi(-b.abs());
1042 within_permissible_error(1.0, non_compounded_error_factor, 0.001);
1043 }
1044 }
1045 #[test]
1046 fn fixed_point_addition(a in (-1e18f64..1e18f64), b in (-1e18f64..1e18f64)) {
1047 assert_eq!(a + b, f64::from(FixedPoint::from(a) + FixedPoint::from(b)));
1048 }
1049 #[test]
1050 fn fixed_point_subtraction(a in (-1e18f64..1e18f64), b in (-1e18f64..1e18f64)) {
1051 assert_eq!(a - b, f64::from(FixedPoint::from(a) - FixedPoint::from(b)));
1052 }
1053 #[test]
1054 fn fixed_point_negation(a in (-1e18f64..1e18f64)) {
1055 assert_eq!(-a, f64::from(-FixedPoint::from(a)));
1056 }
1057 #[test]
1058 fn fixed_point_mul(a in (-1e9f64..1e9f64), b in (-1e9f64..1e9f64)) {
1059 assert_eq!(a * b, f64::from(FixedPoint::from(a) * FixedPoint::from(b)));
1060 }
1061 #[test]
1062 fn fixed_point_div(a in (-1e9f64..1e9f64), b in (-1e9f64..1e9f64)) {
1063 within_permissible_error(a / b, f64::from(FixedPoint::from(a) / FixedPoint::from(b)), 1e-3f64);
1064 }
1065 #[test]
1066 fn u64_div(a: u64, b in 1u64..u64::MAX) {
1067 within_permissible_error(a as f64 / b as f64, f64::from(FixedPoint::from_u64_div(a, b)), 1e-9f64);
1068 }
1069 #[test]
1070 fn u128_div(a: u128, b in 1u128..u128::MAX) {
1071 within_permissible_error(a as f64 / b as f64, f64::from(FixedPoint::from_u128_div(a, b)), 1e-9f64);
1072 }
1073 }
1074
1075 #[test]
1076 #[cfg(feature = "fixed-point-custom-serde")]
1077 fn test_fixed_point_custom_serde() {
1078 let expected_fee_prices = FeePrices {
1079 overall_price: FixedPoint::ONE,
1080 block_usage_factor: FixedPoint::MAX,
1081 read_factor: FixedPoint::from_u64_div(3, 4),
1082 write_factor: FixedPoint::from_u64_div(100, 1),
1083 compute_factor: FixedPoint::ZERO,
1084 };
1085
1086 let actual_fee_prices_str = r#"
1087 {
1088 "overallPrice": 18446744073709551616,
1089 "readFactor": 13835058055282163712,
1090 "computeFactor": 0,
1091 "blockUsageFactor": 170141183460469231731687303715884105727,
1092 "writeFactor": 1844674407370955161600
1093 }
1094 "#;
1095
1096 let actual_fee_prices: FeePrices =
1098 serde_json::from_str(actual_fee_prices_str).expect("failed to deserialize");
1099 assert_eq!(actual_fee_prices, expected_fee_prices);
1100
1101 let clean_actual_fee_prices_str = actual_fee_prices_str
1103 .replace(" ", "")
1104 .replace("\t", "")
1105 .replace("\n", "");
1106
1107 let expected_fee_prices_str =
1108 serde_json::to_string(&expected_fee_prices).expect("failed to serialize");
1109 assert_eq!(clean_actual_fee_prices_str, expected_fee_prices_str);
1110 }
1111}