Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions crates/flashtypes/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,26 @@ impl Flashblock {
})
}

/// Returns true if this flashblock carries the base payload data.
pub fn has_base_payload(&self) -> bool {
self.base.is_some()
}

/// Returns true if this is the first flashblock in the payload sequence.
pub fn is_first_chunk(&self) -> bool {
self.index == 0
}

/// Returns the number of transactions carried by this flashblock.
pub fn transaction_count(&self) -> usize {
self.diff.transactions.len()
}

/// Returns true if the flashblock includes withdrawals.
pub fn has_withdrawals(&self) -> bool {
!self.diff.withdrawals.is_empty()
}

fn try_parse_message(bytes: Bytes) -> Result<String, FlashblockDecodeError> {
if let Ok(text) = std::str::from_utf8(&bytes)
&& text.trim_start().starts_with('{')
Expand Down Expand Up @@ -106,6 +126,24 @@ mod tests {
assert!(Flashblock::try_decode_message(bytes).is_err());
}

#[test]
fn helper_methods_reflect_flashblock_state() {
let mut payload = sample_payload(json!({
"receipts": {},
"new_account_balances": {},
"block_number": 321u64
}));
payload.index = 0;

let flashblock = Flashblock::try_decode_message(encode_plain(&payload))
.expect("payload should decode");

assert!(flashblock.has_base_payload());
assert!(flashblock.is_first_chunk());
assert_eq!(flashblock.transaction_count(), 1);
assert!(!flashblock.has_withdrawals());
}

fn encode_plain(payload: &FlashblocksPayloadV1) -> Bytes {
Bytes::from(serde_json::to_vec(payload).expect("serialize payload"))
}
Expand Down
6 changes: 6 additions & 0 deletions crates/flashtypes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ mod payload;
pub use payload::{
ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, FlashblocksPayloadV1,
};

pub mod validation;
pub use validation::{
is_non_zero_address, is_valid_blob_gas, is_valid_block_hash, is_valid_gas_usage,
is_valid_state_root, is_valid_timestamp, is_valid_transaction_bytes,
};
59 changes: 59 additions & 0 deletions crates/flashtypes/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,62 @@ pub struct Metadata {
/// Block number this flashblock belongs to.
pub block_number: u64,
}

impl Metadata {
/// Returns true if there are no receipts or balance updates.
pub fn is_empty(&self) -> bool {
self.receipts.is_empty() && self.new_account_balances.is_empty()
}

/// Returns the number of receipts tracked by this metadata.
pub fn receipts_len(&self) -> usize {
self.receipts.len()
}

/// Returns the number of balance updates in this metadata.
pub fn balance_updates_len(&self) -> usize {
self.new_account_balances.len()
}

/// Fetches the updated balance for a given address, if it exists.
pub fn balance_for(&self, address: &Address) -> Option<&U256> {
self.new_account_balances.get(address)
}

/// Returns true if there is a balance update for the provided address.
pub fn has_balance_update(&self, address: &Address) -> bool {
self.new_account_balances.contains_key(address)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn reports_empty_metadata() {
let metadata = Metadata::default();

assert!(metadata.is_empty());
assert_eq!(metadata.receipts_len(), 0);
assert_eq!(metadata.balance_updates_len(), 0);
}

#[test]
fn reports_balance_updates() {
let mut balances = HashMap::default();
let address = Address::from([0x11u8; 20]);
balances.insert(address, U256::from(42u64));

let metadata = Metadata {
receipts: HashMap::default(),
new_account_balances: balances,
block_number: 1,
};

assert!(!metadata.is_empty());
assert_eq!(metadata.balance_updates_len(), 1);
assert!(metadata.has_balance_update(&address));
assert_eq!(metadata.balance_for(&address), Some(&U256::from(42u64)));
}
}
78 changes: 78 additions & 0 deletions crates/flashtypes/src/validation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use alloy_primitives::{Address, B256};

/// Returns true if the address is non-zero.
pub fn is_non_zero_address(address: Address) -> bool {
address != Address::ZERO
}

/// Returns true if the state root is non-zero.
pub fn is_valid_state_root(state_root: B256) -> bool {
state_root != B256::ZERO
}

/// Returns true if the block hash is non-zero.
pub fn is_valid_block_hash(block_hash: B256) -> bool {
block_hash != B256::ZERO
}

/// Returns true if blob gas is either absent or positive.
pub fn is_valid_blob_gas(blob_gas_used: Option<u64>) -> bool {
blob_gas_used.map_or(true, |value| value > 0)
}

/// Returns true if gas used does not exceed the provided limit.
pub fn is_valid_gas_usage(gas_used: u64, gas_limit: u64) -> bool {
gas_limit > 0 && gas_used <= gas_limit
}

/// Returns true if the timestamp is non-zero.
pub fn is_valid_timestamp(timestamp: u64) -> bool {
timestamp > 0
}

/// Returns true if the transaction bytes vector is non-empty.
pub fn is_valid_transaction_bytes(bytes: &[u8]) -> bool {
!bytes.is_empty()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn validates_addresses() {
assert!(is_non_zero_address(Address::from([1u8; 20])));
assert!(!is_non_zero_address(Address::ZERO));
}

#[test]
fn validates_hashes() {
assert!(is_valid_state_root(B256::from([0xAAu8; 32])));
assert!(!is_valid_state_root(B256::ZERO));
assert!(is_valid_block_hash(B256::from([0x11u8; 32])));
assert!(!is_valid_block_hash(B256::ZERO));
}

#[test]
fn validates_blob_gas() {
assert!(is_valid_blob_gas(None));
assert!(is_valid_blob_gas(Some(1)));
assert!(!is_valid_blob_gas(Some(0)));
}

#[test]
fn validates_gas_usage() {
assert!(is_valid_gas_usage(5, 10));
assert!(is_valid_gas_usage(0, 1));
assert!(!is_valid_gas_usage(11, 10));
assert!(!is_valid_gas_usage(1, 0));
}

#[test]
fn validates_timestamp_and_tx_bytes() {
assert!(is_valid_timestamp(1));
assert!(!is_valid_timestamp(0));
assert!(is_valid_transaction_bytes(&[0x01, 0x02]));
assert!(!is_valid_transaction_bytes(&[]));
}
}