casper_types/chainspec/
refund_handling.rs

1/// Configuration options of refund handling that are executed as part of handle payment
2/// finalization.
3use num_rational::Ratio;
4use num_traits::Zero;
5use serde::{Deserialize, Serialize};
6
7use crate::bytesrepr::{self, FromBytes, ToBytes};
8
9const REFUND_HANDLING_REFUND_TAG: u8 = 0;
10const REFUND_HANDLING_BURN_TAG: u8 = 1;
11const REFUND_HANDLING_NONE_TAG: u8 = 2;
12
13/// Defines how refunds are calculated.
14#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
15#[serde(tag = "type", rename_all = "snake_case")]
16pub enum RefundHandling {
17    /// Refund of excess payment amount goes to either a pre-defined purse, or back to the sender
18    /// and the rest of the payment amount goes to the block proposer.
19    Refund {
20        /// Computes how much refund goes back to the user after deducting gas spent from the paid
21        /// amount.
22        ///
23        /// user_part = (payment_amount - gas_spent_amount) * refund_ratio
24        /// validator_part = payment_amount - user_part
25        ///
26        /// Any dust amount that was a result of multiplying by refund_ratio goes back to user.
27        refund_ratio: Ratio<u64>,
28    },
29    /// Burns the refund amount.
30    Burn {
31        /// Computes how much of the refund amount is burned after deducting gas spent from the
32        /// paid amount.
33        refund_ratio: Ratio<u64>,
34    },
35    /// No refunds.
36    // in 1.x the default was Refund
37    // RefundHandling::Refund {
38    //     refund_ratio: Ratio::new(99, 100),
39    // }
40    // in 2.0 the default payment mode is Fixed with Fee Elimination on,
41    // thus there is nothing to refund.
42    #[default]
43    NoRefund,
44}
45
46impl RefundHandling {
47    /// Returns true if we don't need to process a refund.
48    pub fn skip_refund(&self) -> bool {
49        match self {
50            RefundHandling::NoRefund => true,
51            RefundHandling::Refund { refund_ratio } => refund_ratio.is_zero(),
52            RefundHandling::Burn { .. } => false,
53        }
54    }
55
56    /// Returns refund ratio.
57    pub fn refund_ratio(&self) -> Ratio<u64> {
58        match self {
59            RefundHandling::Refund { refund_ratio } | RefundHandling::Burn { refund_ratio } => {
60                *refund_ratio
61            }
62            RefundHandling::NoRefund => Ratio::zero(),
63        }
64    }
65}
66
67impl ToBytes for RefundHandling {
68    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
69        let mut buffer = bytesrepr::allocate_buffer(self)?;
70
71        match self {
72            RefundHandling::Refund { refund_ratio } => {
73                buffer.push(REFUND_HANDLING_REFUND_TAG);
74                buffer.extend(refund_ratio.to_bytes()?);
75            }
76            RefundHandling::Burn { refund_ratio } => {
77                buffer.push(REFUND_HANDLING_BURN_TAG);
78                buffer.extend(refund_ratio.to_bytes()?);
79            }
80            RefundHandling::NoRefund => {
81                buffer.push(REFUND_HANDLING_NONE_TAG);
82            }
83        }
84
85        Ok(buffer)
86    }
87
88    fn serialized_length(&self) -> usize {
89        1 + match self {
90            RefundHandling::Refund { refund_ratio } => refund_ratio.serialized_length(),
91            RefundHandling::Burn { refund_ratio } => refund_ratio.serialized_length(),
92            RefundHandling::NoRefund => 0,
93        }
94    }
95}
96
97impl FromBytes for RefundHandling {
98    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
99        let (tag, rem) = u8::from_bytes(bytes)?;
100        match tag {
101            REFUND_HANDLING_REFUND_TAG => {
102                let (refund_ratio, rem) = FromBytes::from_bytes(rem)?;
103                Ok((RefundHandling::Refund { refund_ratio }, rem))
104            }
105            REFUND_HANDLING_BURN_TAG => {
106                let (refund_ratio, rem) = FromBytes::from_bytes(rem)?;
107                Ok((RefundHandling::Burn { refund_ratio }, rem))
108            }
109            REFUND_HANDLING_NONE_TAG => Ok((RefundHandling::NoRefund, rem)),
110            _ => Err(bytesrepr::Error::Formatting),
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn bytesrepr_roundtrip_for_refund() {
121        let refund_config = RefundHandling::Refund {
122            refund_ratio: Ratio::new(49, 313),
123        };
124        bytesrepr::test_serialization_roundtrip(&refund_config);
125    }
126
127    #[test]
128    fn bytesrepr_roundtrip_for_burn() {
129        let refund_config = RefundHandling::Burn {
130            refund_ratio: Ratio::new(49, 313),
131        };
132        bytesrepr::test_serialization_roundtrip(&refund_config);
133    }
134
135    #[test]
136    fn bytesrepr_roundtrip_for_no_refund() {
137        let refund_config = RefundHandling::NoRefund;
138        bytesrepr::test_serialization_roundtrip(&refund_config);
139    }
140}