-
Notifications
You must be signed in to change notification settings - Fork 29
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(op-consensus): op-alloy-consensus
#4
Changes from all commits
6066171
cfbeee9
1eb8cff
4ae4b22
af3099b
6c67854
fbe40e6
2d72c8e
bb22136
74ed633
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
[package] | ||
name = "op-alloy-consensus" | ||
description = "Optimism alloy consensus types" | ||
|
||
version.workspace = true | ||
edition.workspace = true | ||
rust-version.workspace = true | ||
authors.workspace = true | ||
license.workspace = true | ||
homepage.workspace = true | ||
repository.workspace = true | ||
exclude.workspace = true | ||
|
||
[dependencies] | ||
alloy-primitives = { workspace = true, features = ["rlp"] } | ||
alloy-consensus.workspace = true | ||
alloy-rlp.workspace = true | ||
alloy-eips.workspace = true | ||
alloy-serde = { workspace = true, optional = true } | ||
|
||
# arbitrary | ||
arbitrary = { workspace = true, features = ["derive"], optional = true } | ||
|
||
# serde | ||
serde = { workspace = true, features = ["derive"], optional = true } | ||
|
||
[dev-dependencies] | ||
alloy-signer.workspace = true | ||
arbitrary = { workspace = true, features = ["derive"] } | ||
tokio = { workspace = true, features = ["macros"] } | ||
serde_json.workspace = true | ||
|
||
[features] | ||
default = ["std"] | ||
std = ["alloy-eips/std", "alloy-consensus/std"] | ||
k256 = ["alloy-primitives/k256", "alloy-consensus/k256"] | ||
kzg = ["alloy-eips/kzg", "alloy-consensus/kzg", "std"] | ||
arbitrary = ["std", "dep:arbitrary", "alloy-consensus/arbitrary", "alloy-eips/arbitrary", "alloy-primitives/rand"] | ||
serde = ["dep:serde", "dep:alloy-serde", "alloy-primitives/serde", "alloy-consensus/serde", "alloy-eips/serde"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# op-alloy-consensus | ||
|
||
OP Stack consensus interface. | ||
|
||
This crate contains constants, types, and functions for implementing Optimism EL consensus and communication. This | ||
includes an extended `OpTxEnvelope` type with [deposit transactions][deposit], and receipts containing OP Stack | ||
specific fields (`deposit_nonce` + `deposit_receipt_version`). | ||
|
||
In general a type belongs in this crate if it exists in the `alloy-consensus` crate, but was modified from the base Ethereum protocol in the OP Stack. | ||
For consensus types that are not modified by the OP Stack, the `alloy-consensus` types should be used instead. | ||
|
||
[deposit]: https://specs.optimism.io/protocol/deposits.html | ||
|
||
## Provenance | ||
|
||
Much of this code was ported from [reth-primitives] as part of ongoing alloy migrations. | ||
|
||
[reth-primitives]: https://github.com/paradigmxyz/reth/tree/main/crates/primitives |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#![doc = include_str!("../README.md")] | ||
#![doc( | ||
html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg", | ||
html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico" | ||
)] | ||
#![warn( | ||
missing_copy_implementations, | ||
missing_debug_implementations, | ||
missing_docs, | ||
unreachable_pub, | ||
clippy::missing_const_for_fn, | ||
rustdoc::all | ||
)] | ||
#![cfg_attr(not(test), warn(unused_crate_dependencies))] | ||
#![deny(unused_must_use, rust_2018_idioms)] | ||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] | ||
#![cfg_attr(not(feature = "std"), no_std)] | ||
|
||
#[cfg(not(feature = "std"))] | ||
extern crate alloc; | ||
|
||
mod receipt; | ||
pub use receipt::{OpReceipt, OpReceiptEnvelope, OpReceiptWithBloom, OpTxReceipt}; | ||
|
||
mod transaction; | ||
pub use transaction::{OpTxEnvelope, OpTxType, OpTypedTransaction, TxDeposit}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
use crate::{OpReceipt, OpReceiptWithBloom, OpTxType}; | ||
use alloy_eips::eip2718::{Decodable2718, Encodable2718}; | ||
use alloy_primitives::{Bloom, Log}; | ||
use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable}; | ||
|
||
/// Receipt envelope, as defined in [EIP-2718], modified for OP Stack chains. | ||
/// | ||
/// This enum distinguishes between tagged and untagged legacy receipts, as the | ||
/// in-protocol merkle tree may commit to EITHER 0-prefixed or raw. Therefore | ||
/// we must ensure that encoding returns the precise byte-array that was | ||
/// decoded, preserving the presence or absence of the `TransactionType` flag. | ||
/// | ||
/// Transaction receipt payloads are specified in their respective EIPs. | ||
/// | ||
/// [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718 | ||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
#[cfg_attr(feature = "serde", serde(tag = "type"))] | ||
#[non_exhaustive] | ||
pub enum OpReceiptEnvelope<T = Log> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should just reuse the regular alloy consensus types here |
||
/// Receipt envelope with no type flag. | ||
#[cfg_attr(feature = "serde", serde(rename = "0x0", alias = "0x00"))] | ||
Legacy(OpReceiptWithBloom<T>), | ||
/// Receipt envelope with type flag 1, containing a [EIP-2930] receipt. | ||
/// | ||
/// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 | ||
#[cfg_attr(feature = "serde", serde(rename = "0x1", alias = "0x01"))] | ||
Eip2930(OpReceiptWithBloom<T>), | ||
/// Receipt envelope with type flag 2, containing a [EIP-1559] receipt. | ||
/// | ||
/// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 | ||
#[cfg_attr(feature = "serde", serde(rename = "0x2", alias = "0x02"))] | ||
Eip1559(OpReceiptWithBloom<T>), | ||
/// Receipt envelope with type flag 3, containing a [EIP-4844] receipt. | ||
/// | ||
/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 | ||
#[cfg_attr(feature = "serde", serde(rename = "0x3", alias = "0x03"))] | ||
Eip4844(OpReceiptWithBloom<T>), | ||
/// Receipt envelope with type flag 126, containing a [deposit] receipt. | ||
/// | ||
/// [deposit]: https://specs.optimism.io/protocol/deposits.html | ||
#[cfg_attr(feature = "serde", serde(rename = "0x7E", alias = "0x7E"))] | ||
Deposit(OpReceiptWithBloom<T>), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, only this variant needs the Op specific receipt type, right? the others could reuse the the alloy-consensus one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, only this variant needs it. Decided to use all Technically, all of the receipts within OP consensus do use this format, though the extra |
||
} | ||
|
||
impl<T> OpReceiptEnvelope<T> { | ||
/// Return the [`OpTxType`] of the inner receipt. | ||
pub const fn tx_type(&self) -> OpTxType { | ||
match self { | ||
Self::Legacy(_) => OpTxType::Legacy, | ||
Self::Eip2930(_) => OpTxType::Eip2930, | ||
Self::Eip1559(_) => OpTxType::Eip1559, | ||
Self::Eip4844(_) => OpTxType::Eip4844, | ||
Self::Deposit(_) => OpTxType::Deposit, | ||
} | ||
} | ||
|
||
/// Return true if the transaction was successful. | ||
pub fn is_success(&self) -> bool { | ||
self.status() | ||
} | ||
|
||
/// Returns the success status of the receipt's transaction. | ||
pub fn status(&self) -> bool { | ||
self.as_receipt().unwrap().status | ||
} | ||
|
||
/// Returns the cumulative gas used at this receipt. | ||
pub fn cumulative_gas_used(&self) -> u128 { | ||
self.as_receipt().unwrap().cumulative_gas_used | ||
} | ||
|
||
/// Return the receipt logs. | ||
pub fn logs(&self) -> &[T] { | ||
&self.as_receipt().unwrap().logs | ||
} | ||
|
||
/// Return the receipt's bloom. | ||
pub fn logs_bloom(&self) -> &Bloom { | ||
&self.as_receipt_with_bloom().unwrap().logs_bloom | ||
} | ||
|
||
/// Return the receipt's deposit_nonce. | ||
pub fn deposit_nonce(&self) -> Option<u64> { | ||
self.as_receipt().unwrap().deposit_nonce | ||
} | ||
|
||
/// Return the receipt's deposit version. | ||
pub fn deposit_receipt_version(&self) -> Option<u64> { | ||
self.as_receipt().unwrap().deposit_receipt_version | ||
} | ||
|
||
/// Return the inner receipt with bloom. Currently this is infallible, | ||
/// however, future receipt types may be added. | ||
pub const fn as_receipt_with_bloom(&self) -> Option<&OpReceiptWithBloom<T>> { | ||
match self { | ||
Self::Legacy(t) | ||
| Self::Eip2930(t) | ||
| Self::Eip1559(t) | ||
| Self::Eip4844(t) | ||
| Self::Deposit(t) => Some(t), | ||
} | ||
} | ||
|
||
/// Return the inner receipt. Currently this is infallible, however, future | ||
/// receipt types may be added. | ||
pub const fn as_receipt(&self) -> Option<&OpReceipt<T>> { | ||
match self { | ||
Self::Legacy(t) | ||
| Self::Eip2930(t) | ||
| Self::Eip1559(t) | ||
| Self::Eip4844(t) | ||
| Self::Deposit(t) => Some(&t.receipt), | ||
} | ||
} | ||
} | ||
|
||
impl OpReceiptEnvelope { | ||
/// Get the length of the inner receipt in the 2718 encoding. | ||
pub fn inner_length(&self) -> usize { | ||
self.as_receipt_with_bloom().unwrap().length() | ||
} | ||
|
||
/// Calculate the length of the rlp payload of the network encoded receipt. | ||
pub fn rlp_payload_length(&self) -> usize { | ||
let length = self.as_receipt_with_bloom().unwrap().length(); | ||
match self { | ||
Self::Legacy(_) => length, | ||
_ => length + 1, | ||
} | ||
} | ||
} | ||
|
||
impl Encodable for OpReceiptEnvelope { | ||
fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { | ||
self.network_encode(out) | ||
} | ||
|
||
fn length(&self) -> usize { | ||
let mut payload_length = self.rlp_payload_length(); | ||
if !self.is_legacy() { | ||
payload_length += length_of_length(payload_length); | ||
} | ||
payload_length | ||
} | ||
} | ||
|
||
impl Decodable for OpReceiptEnvelope { | ||
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { | ||
match Self::network_decode(buf) { | ||
Ok(t) => Ok(t), | ||
Err(_) => Err(alloy_rlp::Error::Custom("Unexpected type")), | ||
} | ||
} | ||
} | ||
|
||
impl Encodable2718 for OpReceiptEnvelope { | ||
fn type_flag(&self) -> Option<u8> { | ||
match self { | ||
Self::Legacy(_) => None, | ||
Self::Eip2930(_) => Some(OpTxType::Eip2930 as u8), | ||
Self::Eip1559(_) => Some(OpTxType::Eip1559 as u8), | ||
Self::Eip4844(_) => Some(OpTxType::Eip4844 as u8), | ||
Self::Deposit(_) => Some(OpTxType::Deposit as u8), | ||
} | ||
} | ||
|
||
fn encode_2718_len(&self) -> usize { | ||
self.inner_length() + !self.is_legacy() as usize | ||
} | ||
|
||
fn encode_2718(&self, out: &mut dyn BufMut) { | ||
match self.type_flag() { | ||
None => {} | ||
Some(ty) => out.put_u8(ty), | ||
} | ||
self.as_receipt_with_bloom().unwrap().encode(out); | ||
} | ||
} | ||
|
||
impl Decodable2718 for OpReceiptEnvelope { | ||
fn typed_decode(ty: u8, buf: &mut &[u8]) -> alloy_rlp::Result<Self> { | ||
let receipt = Decodable::decode(buf)?; | ||
match ty.try_into().map_err(|_| alloy_rlp::Error::Custom("Unexpected type"))? { | ||
OpTxType::Legacy => { | ||
Err(alloy_rlp::Error::Custom("type-0 eip2718 transactions are not supported")) | ||
} | ||
OpTxType::Eip2930 => Ok(Self::Eip2930(receipt)), | ||
OpTxType::Eip1559 => Ok(Self::Eip1559(receipt)), | ||
OpTxType::Eip4844 => Ok(Self::Eip4844(receipt)), | ||
OpTxType::Deposit => Ok(Self::Deposit(receipt)), | ||
} | ||
} | ||
|
||
fn fallback_decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { | ||
Ok(Self::Legacy(Decodable::decode(buf)?)) | ||
} | ||
} | ||
|
||
#[cfg(all(test, feature = "arbitrary"))] | ||
impl<'a, T> arbitrary::Arbitrary<'a> for OpReceiptEnvelope<T> | ||
where | ||
T: arbitrary::Arbitrary<'a>, | ||
{ | ||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { | ||
let receipt = OpReceiptWithBloom::<T>::arbitrary(u)?; | ||
|
||
match u.int_in_range(0..=4)? { | ||
0 => Ok(Self::Legacy(receipt)), | ||
1 => Ok(Self::Eip2930(receipt)), | ||
2 => Ok(Self::Eip1559(receipt)), | ||
3 => Ok(Self::Eip4844(receipt)), | ||
_ => Ok(Self::Deposit(receipt)), | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PTAL at the review here w.r.t type duplication between alloy upstream and op-alloy #2
it should be possible to wrap the internal receipt type, I would think? maybe not