Skip to content

Commit

Permalink
Dedicated enveloped transaction/receipt encoding and decoding (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
sorpaas authored Oct 24, 2022
1 parent 755dffa commit 24739cc
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 114 deletions.
16 changes: 1 addition & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
# ethereum-rs

Apache-2 licensed common Ethereum structs shared by crates. Nightly version of etcommon-rs.

## List of Crates

Below are all crates provided by the ethereum-rs project.

| Name | Description | Crates.io | Documentation |
| ---- | ----------- | --------- | ------------- |
| ethereum-rlp | Recursive-length prefix encoding, decoding, and compression | [crates.io](https://crates.io/crates/ethereum-rlp) | [Documentation](https://docs.rs/ethereum-rlp) |
| ethereum-bigint | Big integer and hash implementation | [crates.io](https://crates.io/crates/ethereum-bigint) | [Documentation](https://docs.rs/ethereum-bigint) |
| ethereum-hexutil | Small hex decoding helpers | [crates.io](https://crates.io/crates/ethereum-hexutil) | [Documentation](https://docs.rs/ethereum-hexutil) |
| ethereum-bloom | Log bloom for Ethereum | [crates.io](https://crates.io/crates/ethereum-bloom) | [Documentation](https://docs.rs/ethereum-bloom) |
| ethereum-trie | Merkle Trie specialized for Ethereum | [crates.io](https://crates.io/crates/ethereum-trie) | [Documentation](https://docs.rs/ethereum-trie) |
| ethereum-block | Block, transaction and account structs for Ethereum | [crates.io](https://crates.io/crates/ethereum-block) | [Documentation](https://docs.rs/ethereum-block) |
| ethereum-block-core | Core block, transaction and account structs for Ethereum | [crates.io](https://crates.io/crates/ethereum-block-core) | [Documentation](https://docs.rs/ethereum-block-core) |
Apache-2 licensed common Ethereum structs shared by crates.
25 changes: 19 additions & 6 deletions src/block.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
util::ordered_trie_root, Header, PartialHeader, TransactionAny, TransactionV0, TransactionV1,
TransactionV2,
util::ordered_trie_root, EnvelopedDecodable, EnvelopedEncodable, Header, PartialHeader,
TransactionAny, TransactionV0, TransactionV1, TransactionV2,
};
use alloc::vec::Vec;
use ethereum_types::H256;
Expand All @@ -19,20 +19,33 @@ pub struct Block<T> {
pub ommers: Vec<Header>,
}

impl<T: Encodable> Encodable for Block<T> {
impl<T: EnvelopedEncodable> Encodable for Block<T> {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(3);
s.append(&self.header);
s.append_list(&self.transactions);
s.append_list::<Vec<u8>, _>(
&self
.transactions
.iter()
.map(|tx| EnvelopedEncodable::encode(tx).to_vec())
.collect::<Vec<_>>(),
);
s.append_list(&self.ommers);
}
}

impl<T: Decodable> Decodable for Block<T> {
impl<T: EnvelopedDecodable> Decodable for Block<T> {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
Ok(Self {
header: rlp.val_at(0)?,
transactions: rlp.list_at(1)?,
transactions: rlp
.list_at::<Vec<u8>>(1)?
.into_iter()
.map(|raw_tx| {
EnvelopedDecodable::decode(&raw_tx)
.map_err(|_| DecoderError::Custom("decode enveloped transaction failed"))
})
.collect::<Result<Vec<_>, _>>()?,
ommers: rlp.list_at(2)?,
})
}
Expand Down
46 changes: 46 additions & 0 deletions src/enveloped.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use bytes::BytesMut;

/// DecoderError for typed transactions.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum EnvelopedDecoderError<T> {
UnknownTypeId,
Payload(T),
}

impl<T> From<T> for EnvelopedDecoderError<T> {
fn from(e: T) -> Self {
Self::Payload(e)
}
}

/// Encodable typed transactions.
pub trait EnvelopedEncodable {
/// Convert self to an owned vector.
fn encode(&self) -> BytesMut {
let type_id = self.type_id();

let mut out = BytesMut::new();
if let Some(type_id) = type_id {
assert!(type_id <= 0x7f);
out.extend_from_slice(&[type_id]);
}

out.extend_from_slice(&self.encode_payload()[..]);
out
}

/// Type Id of the transaction.
fn type_id(&self) -> Option<u8>;

/// Encode inner payload.
fn encode_payload(&self) -> BytesMut;
}

/// Decodable typed transactions.
pub trait EnvelopedDecodable: Sized {
/// Inner payload decoder error.
type PayloadDecoderError;

/// Decode raw bytes to a Self type.
fn decode(bytes: &[u8]) -> Result<Self, EnvelopedDecoderError<Self::PayloadDecoderError>>;
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extern crate alloc;

mod account;
mod block;
mod enveloped;
mod header;
mod log;
mod receipt;
Expand All @@ -15,6 +16,7 @@ type Bytes = alloc::vec::Vec<u8>;

pub use account::Account;
pub use block::*;
pub use enveloped::*;
pub use header::{Header, PartialHeader};
pub use log::Log;
pub use receipt::*;
Expand Down
171 changes: 119 additions & 52 deletions src/receipt.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::util::enveloped;
use crate::Log;
use crate::{EnvelopedDecodable, EnvelopedDecoderError, EnvelopedEncodable, Log};
use alloc::vec::Vec;
use bytes::BytesMut;
use ethereum_types::{Bloom, H256, U256};
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
use rlp::{Decodable, DecoderError, Rlp};
use rlp_derive::{RlpDecodable, RlpEncodable};

#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable)]
Expand Down Expand Up @@ -37,8 +37,42 @@ pub type EIP1559ReceiptData = EIP658ReceiptData;

pub type ReceiptV0 = FrontierReceiptData;

impl EnvelopedEncodable for ReceiptV0 {
fn type_id(&self) -> Option<u8> {
None
}
fn encode_payload(&self) -> BytesMut {
rlp::encode(self)
}
}

impl EnvelopedDecodable for ReceiptV0 {
type PayloadDecoderError = DecoderError;

fn decode(bytes: &[u8]) -> Result<Self, EnvelopedDecoderError<Self::PayloadDecoderError>> {
Ok(rlp::decode(bytes)?)
}
}

pub type ReceiptV1 = EIP658ReceiptData;

impl EnvelopedEncodable for ReceiptV1 {
fn type_id(&self) -> Option<u8> {
None
}
fn encode_payload(&self) -> BytesMut {
rlp::encode(self)
}
}

impl EnvelopedDecodable for ReceiptV1 {
type PayloadDecoderError = DecoderError;

fn decode(bytes: &[u8]) -> Result<Self, EnvelopedDecoderError<Self::PayloadDecoderError>> {
Ok(rlp::decode(bytes)?)
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "with-codec",
Expand All @@ -52,34 +86,44 @@ pub enum ReceiptV2 {
EIP2930(EIP2930ReceiptData),
}

impl Encodable for ReceiptV2 {
fn rlp_append(&self, s: &mut RlpStream) {
impl EnvelopedEncodable for ReceiptV2 {
fn type_id(&self) -> Option<u8> {
match self {
Self::Legacy(_) => None,
Self::EIP2930(_) => Some(1),
}
}

fn encode_payload(&self) -> BytesMut {
match self {
Self::Legacy(r) => r.rlp_append(s),
Self::EIP2930(r) => enveloped(1, r, s),
Self::Legacy(r) => rlp::encode(r),
Self::EIP2930(r) => rlp::encode(r),
}
}
}

impl Decodable for ReceiptV2 {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
let slice = rlp.data()?;
impl EnvelopedDecodable for ReceiptV2 {
type PayloadDecoderError = DecoderError;

fn decode(bytes: &[u8]) -> Result<Self, EnvelopedDecoderError<Self::PayloadDecoderError>> {
if bytes.is_empty() {
return Err(EnvelopedDecoderError::UnknownTypeId);
}

let first = *slice.get(0).ok_or(DecoderError::Custom("empty slice"))?;
let first = bytes[0];

let rlp = Rlp::new(bytes);
if rlp.is_list() {
return Ok(Self::Legacy(Decodable::decode(rlp)?));
return Ok(Self::Legacy(Decodable::decode(&rlp)?));
}

let s = slice
.get(1..)
.ok_or(DecoderError::Custom("no receipt body"))?;
let s = &bytes[1..];

if first == 0x01 {
return rlp::decode(s).map(Self::EIP2930);
return Ok(Self::EIP2930(rlp::decode(s)?));
}

Err(DecoderError::Custom("invalid receipt type"))
Err(DecoderError::Custom("invalid receipt type").into())
}
}

Expand Down Expand Up @@ -107,39 +151,50 @@ pub enum ReceiptV3 {
EIP1559(EIP1559ReceiptData),
}

impl Encodable for ReceiptV3 {
fn rlp_append(&self, s: &mut RlpStream) {
impl EnvelopedEncodable for ReceiptV3 {
fn type_id(&self) -> Option<u8> {
match self {
Self::Legacy(_) => None,
Self::EIP2930(_) => Some(1),
Self::EIP1559(_) => Some(2),
}
}

fn encode_payload(&self) -> BytesMut {
match self {
Self::Legacy(r) => r.rlp_append(s),
Self::EIP2930(r) => enveloped(1, r, s),
Self::EIP1559(r) => enveloped(2, r, s),
Self::Legacy(r) => rlp::encode(r),
Self::EIP2930(r) => rlp::encode(r),
Self::EIP1559(r) => rlp::encode(r),
}
}
}

impl Decodable for ReceiptV3 {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
let slice = rlp.data()?;
impl EnvelopedDecodable for ReceiptV3 {
type PayloadDecoderError = DecoderError;

let first = *slice.get(0).ok_or(DecoderError::Custom("empty slice"))?;
fn decode(bytes: &[u8]) -> Result<Self, EnvelopedDecoderError<Self::PayloadDecoderError>> {
if bytes.is_empty() {
return Err(EnvelopedDecoderError::UnknownTypeId);
}

let first = bytes[0];

let rlp = Rlp::new(bytes);
if rlp.is_list() {
return Ok(Self::Legacy(Decodable::decode(rlp)?));
return Ok(Self::Legacy(Decodable::decode(&rlp)?));
}

let s = slice
.get(1..)
.ok_or(DecoderError::Custom("no receipt body"))?;
let s = &bytes[1..];

if first == 0x01 {
return rlp::decode(s).map(Self::EIP2930);
return Ok(Self::EIP2930(rlp::decode(s)?));
}

if first == 0x02 {
return rlp::decode(s).map(Self::EIP1559);
return Ok(Self::EIP1559(rlp::decode(s)?));
}

Err(DecoderError::Custom("invalid receipt type"))
Err(DecoderError::Custom("invalid receipt type").into())
}
}

Expand Down Expand Up @@ -170,48 +225,60 @@ pub enum ReceiptAny {
EIP1559(EIP1559ReceiptData),
}

impl Encodable for ReceiptAny {
fn rlp_append(&self, s: &mut RlpStream) {
impl EnvelopedEncodable for ReceiptAny {
fn type_id(&self) -> Option<u8> {
match self {
Self::Frontier(_) => None,
Self::EIP658(_) => None,
Self::EIP2930(_) => Some(1),
Self::EIP1559(_) => Some(2),
}
}

fn encode_payload(&self) -> BytesMut {
match self {
Self::Frontier(r) => r.rlp_append(s),
Self::EIP658(r) => r.rlp_append(s),
Self::EIP2930(r) => enveloped(1, r, s),
Self::EIP1559(r) => enveloped(2, r, s),
Self::Frontier(r) => rlp::encode(r),
Self::EIP658(r) => rlp::encode(r),
Self::EIP2930(r) => rlp::encode(r),
Self::EIP1559(r) => rlp::encode(r),
}
}
}

impl Decodable for ReceiptAny {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
let slice = rlp.data()?;
impl EnvelopedDecodable for ReceiptAny {
type PayloadDecoderError = DecoderError;

fn decode(bytes: &[u8]) -> Result<Self, EnvelopedDecoderError<Self::PayloadDecoderError>> {
if bytes.is_empty() {
return Err(EnvelopedDecoderError::UnknownTypeId);
}

let first = *slice.get(0).ok_or(DecoderError::Custom("empty slice"))?;
let first = bytes[0];

let rlp = Rlp::new(bytes);
if rlp.is_list() {
if rlp.item_count()? == 4 {
let first = rlp.at(0)?;
if first.is_data() && first.data()?.len() <= 1 {
return Ok(Self::Frontier(Decodable::decode(rlp)?));
return Ok(Self::Frontier(Decodable::decode(&rlp)?));
} else {
return Ok(Self::EIP658(Decodable::decode(rlp)?));
return Ok(Self::EIP658(Decodable::decode(&rlp)?));
}
}

return Err(DecoderError::RlpIncorrectListLen);
return Err(DecoderError::RlpIncorrectListLen.into());
}

let s = slice
.get(1..)
.ok_or(DecoderError::Custom("no receipt body"))?;
let s = &bytes[1..];

if first == 0x01 {
return rlp::decode(s).map(Self::EIP2930);
return Ok(Self::EIP2930(rlp::decode(s)?));
}

if first == 0x02 {
return rlp::decode(s).map(Self::EIP1559);
return Ok(Self::EIP1559(rlp::decode(s)?));
}

Err(DecoderError::Custom("invalid receipt type"))
Err(DecoderError::Custom("invalid receipt type").into())
}
}
Loading

0 comments on commit 24739cc

Please sign in to comment.