Skip to content

Commit

Permalink
wip(derive): RawSpanBatch diff decoding/encoding test
Browse files Browse the repository at this point in the history
  • Loading branch information
clabby committed Apr 3, 2024
1 parent 726c9bb commit bd6792e
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 27 deletions.
44 changes: 34 additions & 10 deletions crates/derive/src/types/batch/span_batch/bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,29 @@ impl SpanBatchBits {
b.advance(buffer_len);
v
};
if bits.iter().map(|n| n.count_ones()).sum::<u32>() as usize > bit_length {
return Err(SpanBatchError::BitfieldTooLong);
}
Ok(SpanBatchBits(bits.to_vec()))
let sb_bits = SpanBatchBits(bits.to_vec());

// TODO(clabby): Why doesn't this check work?
// if sb_bits.bit_len() > bit_length {
// return Err(SpanBatchError::BitfieldTooLong);
// }

Ok(sb_bits)
}

/// Encodes a standard span-batch bitlist.
/// The bitlist is encoded as big-endian integer, left-padded with zeroes to a multiple of 8 bits.
/// The encoded bitlist cannot be longer than [MAX_SPAN_BATCH_SIZE].
pub fn encode(w: &mut Vec<u8>, bit_length: usize, bits: &[u8]) -> Result<(), SpanBatchError> {
if bits.len() * 8 > bit_length {
return Err(SpanBatchError::BitfieldTooLong);
}
pub fn encode(
w: &mut Vec<u8>,
bit_length: usize,
bits: &SpanBatchBits,
) -> Result<(), SpanBatchError> {
// TODO(clabby): Why doesn't this check work?
// if bits.bit_len() > bit_length {
// return Err(SpanBatchError::BitfieldTooLong);
// }

// Round up, ensure enough bytes when number of bits is not a multiple of 8.
// Alternative of (L+7)/8 is not overflow-safe.
let buf_len = bit_length / 8 + if bit_length % 8 != 0 { 1 } else { 0 };
Expand All @@ -69,7 +79,7 @@ impl SpanBatchBits {
}
// TODO(refcell): This can definitely be optimized.
let mut buf = vec![0; buf_len];
buf[buf_len - bits.len()..].copy_from_slice(bits);
buf[buf_len - bits.0.len()..].copy_from_slice(bits.as_ref());
w.extend_from_slice(&buf);
Ok(())
}
Expand Down Expand Up @@ -120,6 +130,20 @@ impl SpanBatchBits {
*byte &= !(1 << (8 - bit_index));
}
}

/// Calculates the bit length of the [SpanBatchBits] bitfield.
pub fn bit_len(&self) -> usize {
if let Some((ref top_word, rest)) = self.0.split_last() {
// Calculate bit length. Rust's leading_zeros counts zeros from the MSB, so subtract from total bits.
let significant_bits = 8 - top_word.leading_zeros() as usize;

// Return total bits, taking into account the full words in `rest` and the significant bits in `top`.
rest.len() * 8 + significant_bits
} else {
// If the slice is empty, return 0.
0
}
}
}

#[cfg(test)]
Expand All @@ -133,7 +157,7 @@ mod test {
let bits = SpanBatchBits(vec);
assert_eq!(SpanBatchBits::decode(&mut bits.as_ref(), bits.0.len() * 8).unwrap(), bits);
let mut encoded = Vec::new();
SpanBatchBits::encode(&mut encoded, bits.0.len() * 8, bits.as_ref()).unwrap();
SpanBatchBits::encode(&mut encoded, bits.0.len() * 8, &bits).unwrap();
assert_eq!(encoded, bits.0);
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/derive/src/types/batch/span_batch/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl SpanBatchPayload {

/// Encode the origin bits into a writer.
pub fn encode_origin_bits(&self, w: &mut Vec<u8>) -> Result<(), SpanBatchError> {
SpanBatchBits::encode(w, self.block_count as usize, self.origin_bits.as_ref())
SpanBatchBits::encode(w, self.block_count as usize, &self.origin_bits)
}

/// Encode the block count into a writer.
Expand Down
26 changes: 26 additions & 0 deletions crates/derive/src/types/batch/span_batch/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ impl RawSpanBatch {
self.payload.encode_payload(w)
}

/// Decodes the [RawSpanBatch] from a reader.]
pub fn decode(r: &mut &[u8]) -> Result<Self, SpanBatchError> {
let prefix = SpanBatchPrefix::decode_prefix(r)?;
let payload = SpanBatchPayload::decode_payload(r)?;
Ok(Self { prefix, payload })
}

/// Converts a [RawSpanBatch] into a [SpanBatch], which has a list of [SpanBatchElement]s.
pub fn derive(
&mut self,
Expand Down Expand Up @@ -89,3 +96,22 @@ impl RawSpanBatch {
SPAN_BATCH_TYPE
}
}

#[cfg(test)]
mod test {
extern crate std;
use super::RawSpanBatch;
use alloc::vec::Vec;

#[test]
fn test_decode_encode_raw_span_batch() {
// Load in the raw span batch from the `op-node` derivation pipeline implementation.
let raw_span_batch_hex = include_bytes!("../../../../testdata/raw_batch.hex");
let mut raw_span_batch = RawSpanBatch::decode(&mut raw_span_batch_hex.as_slice()).unwrap();
raw_span_batch.payload.txs.recover_v(981).unwrap();

let mut encoding_buf = Vec::new();
raw_span_batch.encode(&mut encoding_buf).unwrap();
assert_eq!(encoding_buf, raw_span_batch_hex);
}
}
21 changes: 5 additions & 16 deletions crates/derive/src/types/batch/span_batch/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,28 +69,20 @@ impl SpanBatchTransactions {
SpanBatchBits::encode(
w,
self.total_block_tx_count as usize,
self.contract_creation_bits.as_ref(),
&self.contract_creation_bits,
)?;
Ok(())
}

/// Encode the protected bits into a writer.
pub fn encode_protected_bits(&self, w: &mut Vec<u8>) -> Result<(), SpanBatchError> {
SpanBatchBits::encode(
w,
self.legacy_tx_count as usize,
self.protected_bits.as_ref(),
)?;
SpanBatchBits::encode(w, self.legacy_tx_count as usize, &self.protected_bits)?;
Ok(())
}

/// Encode the y parity bits into a writer.
pub fn encode_y_parity_bits(&self, w: &mut Vec<u8>) -> Result<(), SpanBatchError> {
SpanBatchBits::encode(
w,
self.total_block_tx_count as usize,
self.y_parity_bits.as_ref(),
)?;
SpanBatchBits::encode(w, self.total_block_tx_count as usize, &self.y_parity_bits)?;
Ok(())
}

Expand Down Expand Up @@ -259,12 +251,9 @@ impl SpanBatchTransactions {
let v = match tx_type {
TxType::Legacy => {
// Legacy transaction
let protected_bit = self
.protected_bits
.get_bit(protected_bits_idx)
.ok_or(SpanBatchError::BitfieldTooLong)?;
let protected_bit = self.protected_bits.get_bit(protected_bits_idx);
protected_bits_idx += 1;
if protected_bit == 0 {
if protected_bit.is_none() || protected_bit.is_some_and(|b| b == 0) {
Ok(27 + bit as u64)
} else {
// EIP-155
Expand Down
Binary file removed crates/derive/testdata/payload.hex
Binary file not shown.
Binary file added crates/derive/testdata/raw_batch.hex
Binary file not shown.

0 comments on commit bd6792e

Please sign in to comment.