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