1use std::str::FromStr;
2
3use alloy::primitives::{
4 utils::{parse_units, ParseUnits},
5 Address, U256,
6};
7use anyhow::{bail, ensure, Context};
8use ark_serialize::{
9 CanonicalDeserialize, CanonicalSerialize, Compress, Read, SerializationError, Valid, Validate,
10};
11use committable::{Commitment, Committable, RawCommitmentBuilder};
12use hotshot_contract_adapter::sol_types::Deposit;
13use hotshot_query_service::explorer::MonetaryValue;
14use hotshot_types::traits::block_contents::BuilderFee;
15use itertools::Itertools;
16use jf_merkle_tree::{
17 ForgetableMerkleTreeScheme, ForgetableUniversalMerkleTreeScheme, LookupResult,
18 MerkleCommitment, MerkleTreeError, MerkleTreeScheme, ToTraversalPath,
19 UniversalMerkleTreeScheme,
20};
21use num_traits::CheckedSub;
22use sequencer_utils::{
23 impl_serde_from_string_or_integer, impl_to_fixed_bytes, ser::FromStringOrInteger,
24};
25use thiserror::Error;
26
27use crate::{
28 eth_signature_key::EthKeyPair, v0_99::IterableFeeInfo, AccountQueryData, FeeAccount,
29 FeeAccountProof, FeeAmount, FeeInfo, FeeMerkleCommitment, FeeMerkleProof, FeeMerkleTree,
30 SeqTypes,
31};
32
33#[derive(Error, Debug, Eq, PartialEq)]
35pub enum FeeError {
36 #[error("Insuficcient Funds: have {balance:?}, required {amount:?}")]
37 InsufficientFunds {
38 balance: Option<FeeAmount>,
39 amount: FeeAmount,
40 },
41 #[error("Merkle Tree Error: {0}")]
42 MerkleTreeError(MerkleTreeError),
43}
44
45impl FeeInfo {
46 pub fn new(account: impl Into<FeeAccount>, amount: impl Into<FeeAmount>) -> Self {
47 Self {
48 account: account.into(),
49 amount: amount.into(),
50 }
51 }
52 pub fn base_fee(account: FeeAccount) -> Self {
56 Self {
57 account,
58 amount: FeeAmount::default(),
59 }
60 }
61
62 pub fn genesis() -> Self {
63 Self {
64 account: Default::default(),
65 amount: Default::default(),
66 }
67 }
68
69 pub fn account(&self) -> FeeAccount {
70 self.account
71 }
72
73 pub fn amount(&self) -> FeeAmount {
74 self.amount
75 }
76 pub fn from_builder_fees(fees: Vec<BuilderFee<SeqTypes>>) -> Vec<FeeInfo> {
78 fees.into_iter().map(FeeInfo::from).collect()
79 }
80}
81
82impl IterableFeeInfo for Vec<FeeInfo> {
83 fn amount(&self) -> Option<FeeAmount> {
85 self.iter()
86 .map(|fee_info| fee_info.amount.as_u64())
88 .collect::<Option<Vec<u64>>>()
89 .and_then(|amounts| amounts.iter().try_fold(0u64, |acc, n| acc.checked_add(*n)))
90 .map(FeeAmount::from)
91 }
92
93 fn accounts(&self) -> Vec<FeeAccount> {
95 self.iter()
96 .unique_by(|entry| &entry.account)
97 .map(|entry| entry.account)
98 .collect()
99 }
100}
101
102impl IterableFeeInfo for Vec<BuilderFee<SeqTypes>> {
103 fn amount(&self) -> Option<FeeAmount> {
105 self.iter()
106 .map(|fee_info| fee_info.fee_amount)
107 .try_fold(0u64, |acc, n| acc.checked_add(n))
108 .map(FeeAmount::from)
109 }
110
111 fn accounts(&self) -> Vec<FeeAccount> {
113 self.iter()
114 .unique_by(|entry| &entry.fee_account)
115 .map(|entry| entry.fee_account)
116 .collect()
117 }
118}
119
120impl From<BuilderFee<SeqTypes>> for FeeInfo {
121 fn from(fee: BuilderFee<SeqTypes>) -> Self {
122 Self {
123 amount: fee.fee_amount.into(),
124 account: fee.fee_account,
125 }
126 }
127}
128
129impl From<Deposit> for FeeInfo {
130 fn from(item: Deposit) -> Self {
131 Self {
132 amount: item.amount.into(),
133 account: item.user.into(),
134 }
135 }
136}
137
138impl Committable for FeeInfo {
139 fn commit(&self) -> Commitment<Self> {
140 RawCommitmentBuilder::new(&Self::tag())
141 .fixed_size_field("account", &self.account.to_fixed_bytes())
142 .fixed_size_field("amount", &self.amount.to_fixed_bytes())
143 .finalize()
144 }
145 fn tag() -> String {
146 "FEE_INFO".into()
147 }
148}
149
150impl_serde_from_string_or_integer!(FeeAmount);
151impl_to_fixed_bytes!(FeeAmount, U256);
152
153impl From<u64> for FeeAmount {
154 fn from(amt: u64) -> Self {
155 Self(U256::from(amt))
156 }
157}
158
159impl From<FeeAmount> for MonetaryValue {
160 fn from(value: FeeAmount) -> Self {
161 MonetaryValue::eth(value.0.to::<u128>() as i128)
162 }
163}
164
165impl CheckedSub for FeeAmount {
166 fn checked_sub(&self, v: &Self) -> Option<Self> {
167 self.0.checked_sub(v.0).map(FeeAmount)
168 }
169}
170
171impl FromStr for FeeAmount {
172 type Err = <U256 as FromStr>::Err;
173
174 fn from_str(s: &str) -> Result<Self, Self::Err> {
175 Ok(Self(s.parse()?))
176 }
177}
178
179impl FromStringOrInteger for FeeAmount {
180 type Binary = ethers_core::types::U256;
181 type Integer = u64;
182
183 fn from_binary(b: Self::Binary) -> anyhow::Result<Self> {
184 Ok(Self(U256::from_limbs(b.0)))
185 }
186
187 fn from_integer(i: Self::Integer) -> anyhow::Result<Self> {
188 Ok(i.into())
189 }
190
191 fn from_string(s: String) -> anyhow::Result<Self> {
192 if let Some(s) = s.strip_prefix("0x") {
195 return Ok(Self(U256::from_str_radix(s, 16)?));
196 }
197
198 let (base, unit) = s
200 .split_once(char::is_whitespace)
201 .unwrap_or((s.as_str(), "wei"));
202 match parse_units(base, unit)? {
203 ParseUnits::U256(n) => Ok(Self(n)),
204 ParseUnits::I256(_) => bail!("amount cannot be negative"),
205 }
206 }
207
208 fn to_binary(&self) -> anyhow::Result<Self::Binary> {
209 Ok(ethers_core::types::U256(self.0.into_limbs()))
210 }
211
212 fn to_string(&self) -> anyhow::Result<String> {
213 Ok(format!("{self}"))
214 }
215}
216
217impl FeeAmount {
218 pub fn as_u64(&self) -> Option<u64> {
219 if self.0 <= U256::from(u64::MAX) {
220 Some(self.0.to::<u64>())
221 } else {
222 None
223 }
224 }
225}
226impl FeeAccount {
227 pub fn address(&self) -> Address {
229 self.0
230 }
231 pub fn as_bytes(&self) -> &[u8] {
233 self.0.as_slice()
234 }
235 pub fn to_fixed_bytes(self) -> [u8; 20] {
237 self.0.into_array()
238 }
239 pub fn test_key_pair() -> EthKeyPair {
240 EthKeyPair::from_mnemonic(
241 "test test test test test test test test test test test junk",
242 0u32,
243 )
244 .unwrap()
245 }
246}
247
248impl FromStr for FeeAccount {
249 type Err = anyhow::Error;
250
251 fn from_str(s: &str) -> Result<Self, Self::Err> {
252 Ok(Self(s.parse()?))
253 }
254}
255
256impl Valid for FeeAmount {
257 fn check(&self) -> Result<(), SerializationError> {
258 Ok(())
259 }
260}
261
262impl Valid for FeeAccount {
263 fn check(&self) -> Result<(), SerializationError> {
264 Ok(())
265 }
266}
267
268impl CanonicalSerialize for FeeAmount {
269 fn serialize_with_mode<W: std::io::prelude::Write>(
270 &self,
271 mut writer: W,
272 _compress: Compress,
273 ) -> Result<(), SerializationError> {
274 Ok(writer.write_all(&self.to_fixed_bytes())?)
275 }
276
277 fn serialized_size(&self, _compress: Compress) -> usize {
278 core::mem::size_of::<U256>()
279 }
280}
281impl CanonicalDeserialize for FeeAmount {
282 fn deserialize_with_mode<R: Read>(
283 mut reader: R,
284 _compress: Compress,
285 _validate: Validate,
286 ) -> Result<Self, SerializationError> {
287 let mut bytes = [0u8; core::mem::size_of::<U256>()];
288 reader.read_exact(&mut bytes)?;
289 let value = U256::from_le_slice(&bytes);
290 Ok(Self(value))
291 }
292}
293impl CanonicalSerialize for FeeAccount {
294 fn serialize_with_mode<W: std::io::prelude::Write>(
295 &self,
296 mut writer: W,
297 _compress: Compress,
298 ) -> Result<(), SerializationError> {
299 Ok(writer.write_all(self.0.as_slice())?)
300 }
301
302 fn serialized_size(&self, _compress: Compress) -> usize {
303 core::mem::size_of::<Address>()
304 }
305}
306impl CanonicalDeserialize for FeeAccount {
307 fn deserialize_with_mode<R: Read>(
308 mut reader: R,
309 _compress: Compress,
310 _validate: Validate,
311 ) -> Result<Self, SerializationError> {
312 let mut bytes = [0u8; core::mem::size_of::<Address>()];
313 reader.read_exact(&mut bytes)?;
314 let value = Address::from_slice(&bytes);
315 Ok(Self(value))
316 }
317}
318
319impl ToTraversalPath<256> for FeeAccount {
320 fn to_traversal_path(&self, height: usize) -> Vec<usize> {
321 self.0
322 .as_slice()
323 .iter()
324 .take(height)
325 .map(|i| *i as usize)
326 .collect()
327 }
328}
329
330#[allow(dead_code)]
331impl FeeAccountProof {
332 pub fn presence(
333 pos: FeeAccount,
334 proof: <FeeMerkleTree as MerkleTreeScheme>::MembershipProof,
335 ) -> Self {
336 Self {
337 account: pos.into(),
338 proof: FeeMerkleProof::Presence(proof),
339 }
340 }
341
342 pub fn absence(
343 pos: FeeAccount,
344 proof: <FeeMerkleTree as UniversalMerkleTreeScheme>::NonMembershipProof,
345 ) -> Self {
346 Self {
347 account: pos.into(),
348 proof: FeeMerkleProof::Absence(proof),
349 }
350 }
351
352 pub fn prove(tree: &FeeMerkleTree, account: Address) -> Option<(Self, U256)> {
353 match tree.universal_lookup(FeeAccount(account)) {
354 LookupResult::Ok(balance, proof) => Some((
355 Self {
356 account,
357 proof: FeeMerkleProof::Presence(proof),
358 },
359 balance.0,
360 )),
361 LookupResult::NotFound(proof) => Some((
362 Self {
363 account,
364 proof: FeeMerkleProof::Absence(proof),
365 },
366 U256::ZERO,
367 )),
368 LookupResult::NotInMemory => None,
369 }
370 }
371
372 pub fn verify(&self, comm: &FeeMerkleCommitment) -> anyhow::Result<U256> {
373 match &self.proof {
374 FeeMerkleProof::Presence(proof) => {
375 ensure!(
376 FeeMerkleTree::verify(comm.digest(), FeeAccount(self.account), proof)?.is_ok(),
377 "invalid proof"
378 );
379 Ok(proof
380 .elem()
381 .context("presence proof is missing account balance")?
382 .0)
383 },
384 FeeMerkleProof::Absence(proof) => {
385 let tree = FeeMerkleTree::from_commitment(comm);
386 ensure!(
387 tree.non_membership_verify(FeeAccount(self.account), proof)?,
388 "invalid proof"
389 );
390 Ok(U256::ZERO)
391 },
392 }
393 }
394
395 pub fn remember(&self, tree: &mut FeeMerkleTree) -> anyhow::Result<()> {
396 match &self.proof {
397 FeeMerkleProof::Presence(proof) => {
398 tree.remember(
399 FeeAccount(self.account),
400 proof
401 .elem()
402 .context("presence proof is missing account balance")?,
403 proof,
404 )?;
405 Ok(())
406 },
407 FeeMerkleProof::Absence(proof) => {
408 tree.non_membership_remember(FeeAccount(self.account), proof)?;
409 Ok(())
410 },
411 }
412 }
413}
414
415impl From<(FeeAccountProof, U256)> for AccountQueryData {
416 fn from((proof, balance): (FeeAccountProof, U256)) -> Self {
417 Self { balance, proof }
418 }
419}
420
421pub fn retain_accounts(
425 state: &FeeMerkleTree,
426 accounts: impl IntoIterator<Item = FeeAccount>,
427) -> anyhow::Result<FeeMerkleTree> {
428 let mut snapshot = FeeMerkleTree::from_commitment(state.commitment());
429 for account in accounts {
430 match state.universal_lookup(account) {
431 LookupResult::Ok(elem, proof) => {
432 snapshot.remember(account, *elem, proof).unwrap();
435 },
436 LookupResult::NotFound(proof) => {
437 snapshot.non_membership_remember(account, proof).unwrap()
439 },
440 LookupResult::NotInMemory => {
441 bail!("missing account {account}");
442 },
443 }
444 }
445
446 Ok(snapshot)
447}
448
449#[cfg(test)]
450mod test {
451 use super::{Address, IterableFeeInfo};
452 use crate::{FeeAccount, FeeAmount, FeeInfo};
453
454 #[test]
455 fn test_iterable_fee_info() {
456 let addr = Address::default();
457 let fee = FeeInfo::new(addr, FeeAmount::from(1));
458 let fees = vec![fee, fee, fee];
459 let sum = fees.amount().unwrap();
461 assert_eq!(FeeAmount::from(3), sum);
462
463 let accounts = fees.accounts();
465 assert_eq!(vec![FeeAccount::from(Address::default())], accounts);
466 }
467}