Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol): Brotli Compression behind std #263

Merged
merged 4 commits into from
Nov 15, 2024
Merged
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
2 changes: 1 addition & 1 deletion crates/consensus/src/receipt/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ mod tests {
use alloy_rlp::Encodable;

#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
use alloc::vec;

// Test vector from: https://eips.ethereum.org/EIPS/eip-2481
#[test]
Expand Down
3 changes: 2 additions & 1 deletion crates/protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ alloy-eips.workspace = true
alloy-consensus.workspace = true

# Misc
brotli.workspace = true
tracing.workspace = true
derive_more.workspace = true
async-trait.workspace = true
Expand Down Expand Up @@ -62,5 +63,5 @@ test-utils = [
"dep:tracing-subscriber",
]
arbitrary = ["std", "dep:arbitrary", "alloy-consensus/arbitrary", "alloy-eips/arbitrary", "alloy-primitives/rand"]
std = ["op-alloy-consensus/std", "op-alloy-genesis/std"]
std = ["op-alloy-consensus/std", "op-alloy-genesis/std", "brotli/std"]
serde = ["dep:serde", "dep:alloy-serde", "op-alloy-consensus/serde", "op-alloy-genesis/serde"]
60 changes: 20 additions & 40 deletions crates/protocol/examples/batch_to_frames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,12 @@
//! Finally, once [Frame]s are built from the [ChannelOut], they are encoded and ready
//! to be batch-submitted to the data availability layer.

use alloy_consensus::{SignableTransaction, TxEip1559};
use alloy_eips::eip2718::{Decodable2718, Encodable2718};
use alloy_primitives::{hex, Address, BlockHash, Bytes, PrimitiveSignature, U256};
use brotli::enc::{BrotliCompress, BrotliEncoderParams};
use op_alloy_consensus::OpTxEnvelope;
use op_alloy_genesis::RollupConfig;
use op_alloy_protocol::{Batch, ChannelId, ChannelOut, SingleBatch, CHANNEL_ID_LENGTH};

#[cfg(feature = "std")]
fn main() {
use alloy_primitives::BlockHash;
use op_alloy_genesis::RollupConfig;
use op_alloy_protocol::{Batch, ChannelId, ChannelOut, SingleBatch};

// Use the example transaction
let transactions = example_transactions();

Expand All @@ -29,30 +26,16 @@ fn main() {
let epoch_num = 1;
let epoch_hash = BlockHash::ZERO;
let timestamp = 1;

let single_batch = SingleBatch { parent_hash, epoch_num, epoch_hash, timestamp, transactions };
let batch = Batch::Single(single_batch);

// Encode the batch.
let mut encoded = Vec::new();
batch.encode(&mut encoded).unwrap();
let config = RollupConfig::default();
let decoded = Batch::decode(&mut encoded.as_slice(), &config).unwrap();
assert_eq!(batch, decoded);
println!("Encoded Batch: {}", hex::encode(&encoded));

// Compress the encoded batch.
let compressed = compress_brotli(&encoded);
let expected = hex!("1b1301f82f0f6c3734f4821cd090ef3979d71a98e7e483b1dccdd525024c0ef16f425c7b4976a7acc0c94a0514b72c096d4dcc52f0b22dae193c70c86d0790a304a08152c8250031d091063ea0b00d00005082edde7ccf05bded2004462b5e80e1c42cd08e307f5baac723b22864cc6cd01ddde84efc7c018d7ada56c2fa8e3c5bedd494c3a7a884439d5771afcecaf196cb38");
assert_eq!(compressed, expected);
println!("Brotli-compressed batch: {}", hex::encode(&compressed));

// Create a new channel.
let id = random_channel_id();
let id = ChannelId::default();
let config = RollupConfig::default();
let mut channel_out = ChannelOut::new(id, &config);

// Add the compressed batch to the `ChannelOut`.
channel_out.add_raw_compressed_batch(compressed.into());
channel_out.add_batch(batch).unwrap();

// Output frames
while channel_out.ready_bytes() > 0 {
Expand All @@ -64,24 +47,16 @@ fn main() {
}

assert!(channel_out.closed);
println!("Successfully encoded Batch to frames");
}

/// Creates a random [ChannelId].
pub fn random_channel_id() -> ChannelId {
let mut id = [0; CHANNEL_ID_LENGTH];
id.iter_mut().for_each(|b| *b = rand::random());
id
}

/// Compresses the given bytes data using the Brotli compressor implemented
/// in the [`brotli`](https://crates.io/crates/brotli) crate.
pub fn compress_brotli(mut input: &[u8]) -> Vec<u8> {
let mut output = vec![];
BrotliCompress(&mut input, &mut output, &BrotliEncoderParams::default()).expect("succeeds");
output
}
#[cfg(feature = "std")]
fn example_transactions() -> Vec<alloy_primitives::Bytes> {
use alloy_consensus::{SignableTransaction, TxEip1559};
use alloy_eips::eip2718::{Decodable2718, Encodable2718};
use alloy_primitives::{Address, PrimitiveSignature, U256};
use op_alloy_consensus::OpTxEnvelope;

fn example_transactions() -> Vec<Bytes> {
let mut transactions = Vec::new();

// First Transaction in the batch.
Expand Down Expand Up @@ -128,3 +103,8 @@ fn example_transactions() -> Vec<Bytes> {

transactions
}

#[cfg(not(feature = "std"))]
fn main() {
/* not implemented for no_std */
}
23 changes: 14 additions & 9 deletions crates/protocol/src/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

use alloc::{vec, vec::Vec};
use alloy_primitives::{map::HashMap, Bytes};
use alloy_rlp::Encodable;
use op_alloy_genesis::RollupConfig;

use crate::{block::BlockInfo, frame::Frame, SingleBatch};
use crate::{block::BlockInfo, frame::Frame};

/// The frame overhead.
const FRAME_V0_OVERHEAD: usize = 23;
Expand All @@ -17,14 +16,18 @@ pub const CHANNEL_ID_LENGTH: usize = 16;
pub type ChannelId = [u8; CHANNEL_ID_LENGTH];

/// An error returned by the [ChannelOut] when adding single batches.
#[derive(Debug, Clone, Copy, derive_more::Display, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, derive_more::Display)]
pub enum ChannelOutError {
/// The channel is closed.
ChannelClosed,
/// The max frame size is too small.
MaxFrameSizeTooSmall,
/// Missing compressed batch data.
MissingData,
/// An error from brotli compression.
BrotliCompression,
/// An error encoding the `Batch`.
BatchEncoding,
}

impl core::error::Error for ChannelOutError {}
Expand Down Expand Up @@ -54,25 +57,26 @@ impl<'a> ChannelOut<'a> {
Self { id, config, rlp_length: 0, frame_number: 0, closed: false, compressed: None }
}

/// Accepts the given [SingleBatch] data into the [ChannelOut], compressing it
/// Accepts the given [crate::Batch] data into the [ChannelOut], compressing it
/// into frames.
pub fn add_single_batch(&mut self, batch: SingleBatch) -> Result<(), ChannelOutError> {
#[cfg(feature = "std")]
pub fn add_batch(&mut self, batch: crate::Batch) -> Result<(), ChannelOutError> {
if self.closed {
return Err(ChannelOutError::ChannelClosed);
}

// Encode the batch.
let mut buf = vec![];
batch.encode(&mut buf);
batch.encode(&mut buf).map_err(|_| ChannelOutError::BatchEncoding)?;

// Validate that the RLP length is within the channel's limits.
let max_rlp_bytes_per_channel = self.config.max_rlp_bytes_per_channel(batch.timestamp);
let max_rlp_bytes_per_channel = self.config.max_rlp_bytes_per_channel(batch.timestamp());
if self.rlp_length + buf.len() as u64 > max_rlp_bytes_per_channel {
return Err(ChannelOutError::ChannelClosed);
}

// Compress the batch.
todo!("compress the batch with a compression algorithm based on the rollup config")
self.compressed = Some(crate::compress_brotli(&buf).into());
Ok(())
}

/// Returns the number of bytes ready to be output to a frame.
Expand All @@ -81,6 +85,7 @@ impl<'a> ChannelOut<'a> {
}

/// Accepts the raw compressed batch data into the [ChannelOut].
#[deprecated]
pub fn add_raw_compressed_batch(&mut self, compressed: Bytes) {
self.compressed = Some(compressed);
}
Expand Down
2 changes: 2 additions & 0 deletions crates/protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ mod iter;
pub use iter::FrameIter;

mod utils;
#[cfg(feature = "std")]
pub use utils::compress_brotli;
pub use utils::{read_tx_data, starts_with_2718_deposit, to_system_config};

mod channel;
Expand Down
12 changes: 11 additions & 1 deletion crates/protocol/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Utility methods used by protocol types.

use alloc::vec::Vec;
use alloc::{vec, vec::Vec};
use alloy_consensus::TxType;
use alloy_primitives::B256;
use alloy_rlp::{Buf, Header};
Expand All @@ -12,6 +12,16 @@ use crate::{
SpanDecodingError,
};

/// Compresses the given bytes data using the Brotli compressor implemented
/// in the [`brotli`](https://crates.io/crates/brotli) crate.
#[cfg(feature = "std")]
pub fn compress_brotli(mut input: &[u8]) -> Vec<u8> {
use brotli::enc::{BrotliCompress, BrotliEncoderParams};
let mut output = vec![];
BrotliCompress(&mut input, &mut output, &BrotliEncoderParams::default()).expect("succeeds");
output
}

/// Returns if the given `value` is a deposit transaction.
pub fn starts_with_2718_deposit<B>(value: &B) -> bool
where
Expand Down
Loading