From b161df3b09cb4be47cf4cf5ed91f2f726a2baeef Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Tue, 7 May 2024 11:21:58 +0200 Subject: [PATCH 1/4] feat: add eip-7685 enc/decode traits --- crates/eips/src/eip7685.rs | 112 +++++++++++++++++++++++++++++++++++++ crates/eips/src/lib.rs | 2 + 2 files changed, 114 insertions(+) create mode 100644 crates/eips/src/eip7685.rs diff --git a/crates/eips/src/eip7685.rs b/crates/eips/src/eip7685.rs new file mode 100644 index 00000000000..13569deb1d8 --- /dev/null +++ b/crates/eips/src/eip7685.rs @@ -0,0 +1,112 @@ +//! [EIP-7685]: General purpose execution layer requests +//! +//! Contains traits for encoding and decoding EIP-7685 requests, as well as validation functions. +//! +//! [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 + +#[cfg(not(feature = "std"))] +use crate::alloc::{vec, vec::Vec}; + +use alloy_rlp::BufMut; +use core::{ + fmt, + fmt::{Display, Formatter}, +}; + +/// [EIP-7685] decoding errors. +/// +/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 +#[derive(Clone, Copy, Debug)] +pub enum Eip7685Error { + /// Rlp error from [`alloy_rlp`]. + RlpError(alloy_rlp::Error), + /// Got an unexpected request type while decoding. + UnexpectedType(u8), + /// There was no request type in the buffer. + MissingType, +} + +impl Display for Eip7685Error { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::RlpError(err) => write!(f, "{err}"), + Self::UnexpectedType(t) => write!(f, "Unexpected request type. Got {t}."), + Self::MissingType => write!(f, "There was no type flag"), + } + } +} + +impl From for Eip7685Error { + fn from(err: alloy_rlp::Error) -> Self { + Self::RlpError(err) + } +} + +/// Decoding trait for [EIP-7685] requests. The trait should be implemented for an envelope that wraps each possible request type. +/// +/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 +pub trait Decodable7685: Sized { + /// Extract the type byte from the buffer, if any. The type byte is the + /// first byte. + fn extract_type_byte(buf: &mut &[u8]) -> Option { + buf.first().copied() + } + + /// Decode the appropriate variant, based on the request type. + /// + /// This function is invoked by [`Self::decode_7685`] with the type byte, and the tail of the + /// buffer. + /// + /// ## Note + /// + /// This should be a simple match block that invokes an inner type's decoder. The decoder is request type dependent. + fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result; + + /// Decode an EIP-7685 request into a concrete instance + fn decode_7685(buf: &mut &[u8]) -> Result { + Self::extract_type_byte(buf) + .map(|ty| Self::typed_decode(ty, &mut &buf[1..])) + .unwrap_or(Err(Eip7685Error::MissingType)) + } +} + +/// Encoding trait for [EIP-7685] requests. The trait should be implemented for an envelope that wraps each possible request type. +/// +/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 +pub trait Encodable7685: Sized + Send + Sync + 'static { + /// Return the request type. + fn request_type(&self) -> u8; + + /// Encode the request according to [EIP-7685] rules. + /// + /// First a 1-byte flag specifying the request type, then the encoded payload. + /// + /// The encoding of the payload is request-type dependent. + /// + /// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 + fn encode_7685(&self, out: &mut dyn BufMut) { + out.put_u8(self.request_type()); + self.encode_payload_7685(out); + } + + /// Encode the request payload. + /// + /// The encoding for the payload is request type dependent. + fn encode_payload_7685(&self, out: &mut dyn BufMut); + + /// Encode the request according to [EIP-7685] rules. + /// + /// First a 1-byte flag specifying the request type, then the encoded payload. + /// + /// The encoding of the payload is request-type dependent. + /// + /// This is a convenience method for encoding into a vec, and returning the + /// vec. + /// + /// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 + fn encoded_7685(&self) -> Vec { + let mut out = vec![]; + self.encode_7685(&mut out); + out + } +} diff --git a/crates/eips/src/lib.rs b/crates/eips/src/lib.rs index 89b0f3074a7..0eede111dd1 100644 --- a/crates/eips/src/lib.rs +++ b/crates/eips/src/lib.rs @@ -42,3 +42,5 @@ pub mod eip6110; pub mod merge; pub mod eip7002; + +pub mod eip7685; From c8af1bbeebc41381c7ead23da706fe569d4b3237 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Tue, 7 May 2024 11:26:18 +0200 Subject: [PATCH 2/4] chore: fmt --- crates/eips/src/eip7685.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/eips/src/eip7685.rs b/crates/eips/src/eip7685.rs index 13569deb1d8..c0cea9cda6d 100644 --- a/crates/eips/src/eip7685.rs +++ b/crates/eips/src/eip7685.rs @@ -42,7 +42,8 @@ impl From for Eip7685Error { } } -/// Decoding trait for [EIP-7685] requests. The trait should be implemented for an envelope that wraps each possible request type. +/// Decoding trait for [EIP-7685] requests. The trait should be implemented for an envelope that +/// wraps each possible request type. /// /// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 pub trait Decodable7685: Sized { @@ -59,7 +60,8 @@ pub trait Decodable7685: Sized { /// /// ## Note /// - /// This should be a simple match block that invokes an inner type's decoder. The decoder is request type dependent. + /// This should be a simple match block that invokes an inner type's decoder. The decoder is + /// request type dependent. fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result; /// Decode an EIP-7685 request into a concrete instance @@ -70,7 +72,8 @@ pub trait Decodable7685: Sized { } } -/// Encoding trait for [EIP-7685] requests. The trait should be implemented for an envelope that wraps each possible request type. +/// Encoding trait for [EIP-7685] requests. The trait should be implemented for an envelope that +/// wraps each possible request type. /// /// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 pub trait Encodable7685: Sized + Send + Sync + 'static { From 51d946d86308237808ede72f58fd80d367619d02 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Tue, 7 May 2024 13:25:17 +0200 Subject: [PATCH 3/4] feat: combined trait --- crates/eips/src/eip7685.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/eips/src/eip7685.rs b/crates/eips/src/eip7685.rs index c0cea9cda6d..16eacb7634b 100644 --- a/crates/eips/src/eip7685.rs +++ b/crates/eips/src/eip7685.rs @@ -113,3 +113,11 @@ pub trait Encodable7685: Sized + Send + Sync + 'static { out } } + +/// An [EIP-7685] request envelope, blanket implemented for types that impl +/// [`Encodable7685`] and [`Decodable7685`]. This envelope is a wrapper around +/// a request, differentiated by the request type. +/// +/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 +pub trait Eip7685RequestEnvelope: Decodable7685 + Encodable7685 {} +impl Eip7685RequestEnvelope for T where T: Decodable7685 + Encodable7685 {} From 4148426604f29a5270884300893f62863babbb1a Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Tue, 7 May 2024 17:59:02 +0200 Subject: [PATCH 4/4] chore: mark non_exhaustive --- crates/eips/src/eip7685.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/eips/src/eip7685.rs b/crates/eips/src/eip7685.rs index 16eacb7634b..575f14c5f05 100644 --- a/crates/eips/src/eip7685.rs +++ b/crates/eips/src/eip7685.rs @@ -17,6 +17,7 @@ use core::{ /// /// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 #[derive(Clone, Copy, Debug)] +#[non_exhaustive] pub enum Eip7685Error { /// Rlp error from [`alloy_rlp`]. RlpError(alloy_rlp::Error),