use core::fmt::Display;
use std::num::ParseIntError;
use std::str::FromStr;
use alloy::consensus::TxType;
use alloy::primitives::U256;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use crate::primitives::{task::datalake::envelope::default_increment, ChainId};
use super::TransactionsCollection;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionsInBlockDatalake {
pub chain_id: ChainId,
pub target_block: u64,
pub start_index: u64,
pub end_index: u64,
#[serde(default = "default_increment")]
pub increment: u64,
pub included_types: IncludedTypes,
pub sampled_property: TransactionsCollection,
}
impl TransactionsInBlockDatalake {
pub fn new(
chain_id: ChainId,
target_block: u64,
sampled_property: TransactionsCollection,
start_index: u64,
end_index: u64,
increment: u64,
included_types: IncludedTypes,
) -> Self {
Self {
chain_id,
target_block,
sampled_property,
start_index,
end_index,
increment,
included_types,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct IncludedTypes {
legacy: bool,
eip2930: bool,
eip1559: bool,
eip4844: bool,
}
impl IncludedTypes {
pub const ALL: Self = Self {
legacy: true,
eip1559: true,
eip2930: true,
eip4844: true,
};
pub fn to_be_bytes(&self) -> [u8; 4] {
let mut bytes = [0; 4];
if self.legacy {
bytes[0] = 1;
}
if self.eip2930 {
bytes[1] = 1;
}
if self.eip1559 {
bytes[2] = 1;
}
if self.eip4844 {
bytes[3] = 1;
}
bytes
}
pub fn from_be_bytes(bytes: [u8; 4]) -> Self {
let mut included_types = IncludedTypes {
legacy: false,
eip2930: false,
eip1559: false,
eip4844: false,
};
if bytes[0] == 1 {
included_types.legacy = true;
}
if bytes[1] == 1 {
included_types.eip2930 = true;
}
if bytes[2] == 1 {
included_types.eip1559 = true;
}
if bytes[3] == 1 {
included_types.eip4844 = true;
}
included_types
}
pub fn from_bytes(included_types: &[u8]) -> Self {
if included_types.len() != 4 {
panic!("Included types must be 4 bytes long");
}
if included_types.iter().any(|&x| x > 1) {
panic!("Included types must be either 0 or 1");
}
if included_types.iter().all(|&x| x == 0) {
panic!("At least one type must be included");
}
let mut inner = [0; 4];
inner.copy_from_slice(included_types);
Self::from_be_bytes(inner)
}
pub fn is_included(&self, target_type: TxType) -> bool {
let inner_bytes = self.to_be_bytes();
inner_bytes[target_type as usize] != 0
}
}
impl Display for IncludedTypes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let bytes = self.to_be_bytes();
write!(f, "{},{},{},{}", bytes[0], bytes[1], bytes[2], bytes[3])
}
}
impl From<IncludedTypes> for U256 {
fn from(value: IncludedTypes) -> U256 {
let mut bytes = [0; 32];
let inner_bytes = value.to_be_bytes();
bytes[28..32].copy_from_slice(&inner_bytes);
U256::from_be_bytes(bytes)
}
}
impl From<U256> for IncludedTypes {
fn from(value: U256) -> IncludedTypes {
let bytes: [u8; 32] = value.to_be_bytes();
let mut inner = [0; 4];
inner.copy_from_slice(&bytes[28..32]);
IncludedTypes::from_be_bytes(inner)
}
}
impl FromStr for IncludedTypes {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let included_types: Vec<u8> = s
.split(',')
.map(|x| x.parse())
.collect::<Result<Vec<_>, _>>()?;
if included_types.len() != 4 {
panic!("Included types must be 4 bytes long");
}
Ok(IncludedTypes::from_bytes(&included_types))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_included_types() {
let included_types = IncludedTypes::ALL;
assert!(included_types.is_included(TxType::Legacy));
assert!(included_types.is_included(TxType::Eip2930));
assert!(included_types.is_included(TxType::Eip1559));
assert!(included_types.is_included(TxType::Eip4844));
let uint256: U256 = included_types.into();
assert_eq!(uint256, U256::from(0x01010101));
let included_types = IncludedTypes::from(uint256);
assert!(included_types.is_included(TxType::Legacy));
assert!(included_types.is_included(TxType::Eip2930));
assert!(included_types.is_included(TxType::Eip1559));
assert!(included_types.is_included(TxType::Eip4844));
}
#[test]
fn test_included_types_partial() {
let included_types = IncludedTypes::from_bytes(&[1, 0, 1, 0]);
assert!(included_types.is_included(TxType::Legacy));
assert!(!included_types.is_included(TxType::Eip2930));
assert!(included_types.is_included(TxType::Eip1559));
assert!(!included_types.is_included(TxType::Eip4844));
let uint256: U256 = included_types.into();
assert_eq!(uint256, U256::from(0x01000100));
let included_types = IncludedTypes::from(uint256);
assert!(included_types.is_included(TxType::Legacy));
assert!(!included_types.is_included(TxType::Eip2930));
assert!(included_types.is_included(TxType::Eip1559));
assert!(!included_types.is_included(TxType::Eip4844));
}
#[test]
fn test_included_types_from_str_to_str() {
let input_str = "1,0,1,0";
let included_types = IncludedTypes::from_str(input_str).unwrap();
assert!(included_types.is_included(TxType::Legacy));
assert!(!included_types.is_included(TxType::Eip2930));
assert!(included_types.is_included(TxType::Eip1559));
assert!(!included_types.is_included(TxType::Eip4844));
let output_str = included_types.to_string();
assert_eq!(input_str, output_str);
}
}