pallet_revive/evm/
gas_encoder.rs1use crate::Weight;
20use core::ops::{Div, Rem};
21use frame_support::pallet_prelude::CheckedShl;
22use sp_arithmetic::traits::{One, Zero};
23use sp_core::U256;
24
25const SCALE: u128 = 100;
27
28fn round_up<T>(value: T, mask: T) -> T
33where
34 T: One + Zero + Copy + Rem<Output = T> + Div<Output = T>,
35 <T as Rem>::Output: PartialEq,
36{
37 let rest = if value % mask == T::zero() { T::zero() } else { T::one() };
38 value / mask + rest
39}
40
41fn log2_round_up<T>(val: T) -> u128
43where
44 T: Into<u128>,
45{
46 let val = val.into();
47 val.checked_ilog2()
48 .map(|v| if 1u128 << v == val { v } else { v + 1 })
49 .unwrap_or(0) as u128
50}
51
52mod private {
53 pub trait Sealed {}
54 impl Sealed for () {}
55}
56
57pub trait GasEncoder<Balance>: private::Sealed {
68 fn encode(gas_limit: U256, weight: Weight, deposit: Balance) -> U256;
71
72 fn decode(gas: U256) -> Option<(Weight, Balance)>;
75
76 fn as_encoded_values(weight: Weight, deposit: Balance) -> (Weight, Balance) {
78 let encoded = Self::encode(U256::zero(), weight, deposit);
79 Self::decode(encoded).expect("encoded values should be decodable; qed")
80 }
81}
82
83impl<Balance> GasEncoder<Balance> for ()
84where
85 Balance: Zero + One + CheckedShl + Into<u128>,
86{
87 fn encode(gas_limit: U256, weight: Weight, deposit: Balance) -> U256 {
96 let deposit: u128 = deposit.into();
97 let deposit_component = log2_round_up(deposit);
98
99 let proof_size = weight.proof_size();
100 let proof_size_component = SCALE * log2_round_up(proof_size);
101
102 let ref_time = weight.ref_time();
103 let ref_time_component = SCALE.pow(2) * log2_round_up(ref_time);
104
105 let components = U256::from(deposit_component + proof_size_component + ref_time_component);
106
107 let raw_gas_mask = U256::from(SCALE).pow(3.into());
108 let raw_gas_component = if gas_limit <= components {
109 U256::zero()
110 } else {
111 round_up(gas_limit, raw_gas_mask).saturating_mul(raw_gas_mask)
112 };
113
114 components.saturating_add(raw_gas_component)
115 }
116
117 fn decode(gas: U256) -> Option<(Weight, Balance)> {
118 let deposit = gas % SCALE;
119
120 let deposit = deposit.as_u32();
122 let proof_time = ((gas / SCALE) % SCALE).as_u32();
123 let ref_time = ((gas / SCALE.pow(2)) % SCALE).as_u32();
124
125 let ref_weight = match ref_time {
126 0 => 0,
127 64 => u64::MAX,
128 _ => 1u64.checked_shl(ref_time)?,
129 };
130
131 let proof_weight = match proof_time {
132 0 => 0,
133 64 => u64::MAX,
134 _ => 1u64.checked_shl(proof_time)?,
135 };
136
137 let weight = Weight::from_parts(ref_weight, proof_weight);
138
139 let deposit = match deposit {
140 0 => Balance::zero(),
141 _ => Balance::one().checked_shl(deposit)?,
142 };
143
144 Some((weight, deposit))
145 }
146}
147
148#[cfg(test)]
149mod test {
150 use super::*;
151
152 #[test]
153 fn test_gas_encoding_decoding_works() {
154 let raw_gas_limit = 111_111_999_999_999u128;
155 let weight = Weight::from_parts(222_999_999, 333_999_999);
156 let deposit = 444_999_999u64;
157
158 let encoded_gas = <() as GasEncoder<u64>>::encode(raw_gas_limit.into(), weight, deposit);
159 assert_eq!(encoded_gas, U256::from(111_112_000_282_929u128));
160 assert!(encoded_gas > raw_gas_limit.into());
161
162 let (decoded_weight, decoded_deposit) =
163 <() as GasEncoder<u64>>::decode(encoded_gas).unwrap();
164 assert!(decoded_weight.all_gte(weight));
165 assert!(weight.mul(2).all_gte(weight));
166
167 assert!(decoded_deposit >= deposit);
168 assert!(deposit * 2 >= decoded_deposit);
169
170 assert_eq!(
171 (decoded_weight, decoded_deposit),
172 <() as GasEncoder<u64>>::as_encoded_values(weight, deposit)
173 );
174 }
175
176 #[test]
177 fn test_encoding_zero_values_work() {
178 let encoded_gas = <() as GasEncoder<u64>>::encode(
179 Default::default(),
180 Default::default(),
181 Default::default(),
182 );
183
184 assert_eq!(encoded_gas, U256::from(0));
185
186 let (decoded_weight, decoded_deposit) =
187 <() as GasEncoder<u64>>::decode(encoded_gas).unwrap();
188 assert_eq!(Weight::default(), decoded_weight);
189 assert_eq!(0u64, decoded_deposit);
190
191 let encoded_gas =
192 <() as GasEncoder<u64>>::encode(U256::from(1), Default::default(), Default::default());
193 assert_eq!(encoded_gas, U256::from(1000000));
194 }
195
196 #[test]
197 fn test_encoding_max_values_work() {
198 let max_weight = Weight::from_parts(u64::MAX, u64::MAX);
199 let max_deposit = 1u64 << 63;
200 let encoded_gas =
201 <() as GasEncoder<u64>>::encode(Default::default(), max_weight, max_deposit);
202
203 assert_eq!(encoded_gas, U256::from(646463));
204
205 let (decoded_weight, decoded_deposit) =
206 <() as GasEncoder<u64>>::decode(encoded_gas).unwrap();
207 assert_eq!(max_weight, decoded_weight);
208 assert_eq!(max_deposit, decoded_deposit);
209 }
210
211 #[test]
212 fn test_overflow() {
213 assert_eq!(None, <() as GasEncoder<u64>>::decode(65_00u128.into()), "Invalid proof size");
214 assert_eq!(None, <() as GasEncoder<u64>>::decode(65_00_00u128.into()), "Invalid ref_time");
215 }
216}