mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: impl rlp for receipt (#83)
* feat: impl rlp for receipt * fix: change to bloom * chore: rustfmt
This commit is contained in:
29
Cargo.lock
generated
29
Cargo.lock
generated
@ -816,8 +816,10 @@ checksum = "11da94e443c60508eb62cf256243a64da87304c2802ac2528847f79d750007ef"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
"fixed-hash",
|
||||
"impl-codec",
|
||||
"impl-rlp",
|
||||
"impl-serde",
|
||||
"scale-info",
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
@ -829,9 +831,11 @@ checksum = "b2827b94c556145446fcce834ca86b7abf0c39a805883fe20e72c5bfdb5a0dc6"
|
||||
dependencies = [
|
||||
"ethbloom",
|
||||
"fixed-hash",
|
||||
"impl-codec",
|
||||
"impl-rlp",
|
||||
"impl-serde",
|
||||
"primitive-types",
|
||||
"scale-info",
|
||||
"uint",
|
||||
]
|
||||
|
||||
@ -2073,6 +2077,7 @@ dependencies = [
|
||||
"impl-codec",
|
||||
"impl-rlp",
|
||||
"impl-serde",
|
||||
"scale-info",
|
||||
"uint",
|
||||
]
|
||||
|
||||
@ -2732,6 +2737,30 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scale-info"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "333af15b02563b8182cd863f925bd31ef8fa86a0e095d30c091956057d436153"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"derive_more",
|
||||
"parity-scale-codec",
|
||||
"scale-info-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scale-info-derive"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53f56acbd0743d29ffa08f911ab5397def774ad01bab3786804cf6ee057fb5e1"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.20"
|
||||
|
||||
@ -11,7 +11,7 @@ arrayvec = { version = "0.7", default-features = false }
|
||||
auto_impl = "1"
|
||||
bytes = { version = "1", default-features = false }
|
||||
ethnum = { version = "1", default-features = false, optional = true }
|
||||
ethereum-types = { version = "0.13", default-features = false, optional = true }
|
||||
ethereum-types = { version = "0.13", features = ["codec"], optional = true }
|
||||
reth-rlp-derive = { version = "0.1", path = "../rlp-derive", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@ -233,7 +233,7 @@ mod ethereum_types_support {
|
||||
fixed_hash_impl!(H256);
|
||||
fixed_hash_impl!(H512);
|
||||
fixed_hash_impl!(H520);
|
||||
//TODO fixed_hash_impl!(Bloom);
|
||||
fixed_hash_impl!(Bloom);
|
||||
|
||||
macro_rules! fixed_uint_impl {
|
||||
($t:ty, $n_bytes:tt) => {
|
||||
|
||||
@ -209,7 +209,7 @@ mod ethereum_types_support {
|
||||
fixed_hash_impl!(H256);
|
||||
fixed_hash_impl!(H512);
|
||||
fixed_hash_impl!(H520);
|
||||
//TODO fixed_hash_impl!(Bloom);
|
||||
fixed_hash_impl!(Bloom);
|
||||
|
||||
macro_rules! fixed_uint_impl {
|
||||
($t:ty, $n_bytes:tt) => {
|
||||
|
||||
@ -13,8 +13,7 @@ bytes = "1.2"
|
||||
|
||||
serde = "1.0"
|
||||
thiserror = "1"
|
||||
|
||||
reth-rlp = { path = "../common/rlp", features = ["derive"]}
|
||||
reth-rlp = { path = "../common/rlp", features = ["std", "derive", "ethereum-types"]}
|
||||
parity-scale-codec = { version = "3.2.1", features = ["derive", "bytes"] }
|
||||
reth-codecs = { version = "0.1.0", path = "../codecs" }
|
||||
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
use crate::{Address, H256};
|
||||
use reth_codecs::main_codec;
|
||||
use reth_rlp::{RlpDecodable, RlpEncodable};
|
||||
|
||||
/// Ethereum Log
|
||||
#[main_codec]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, RlpDecodable, RlpEncodable)]
|
||||
pub struct Log {
|
||||
/// Contract that emitted this log.
|
||||
pub address: Address,
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
use crate::{Log, TxType, H256};
|
||||
use crate::{Bloom, Log, TxType};
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use reth_rlp::{length_of_length, Decodable, Encodable};
|
||||
|
||||
use reth_codecs::main_codec;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// Receipt containing result of transaction execution.
|
||||
#[main_codec]
|
||||
@ -8,11 +12,215 @@ pub struct Receipt {
|
||||
/// Receipt type.
|
||||
pub tx_type: TxType,
|
||||
/// If transaction is executed successfully.
|
||||
///
|
||||
/// This is the `statusCode`
|
||||
pub success: bool,
|
||||
/// Gas used
|
||||
pub cumulative_gas_used: u64,
|
||||
/// Bloom filter.
|
||||
pub bloom: H256,
|
||||
pub bloom: Bloom,
|
||||
/// Log send from contracts.
|
||||
pub logs: Vec<Log>,
|
||||
}
|
||||
|
||||
impl Receipt {
|
||||
/// Returns the rlp header for the receipt payload.
|
||||
fn receipt_rlp_header(&self) -> reth_rlp::Header {
|
||||
let mut rlp_head = reth_rlp::Header { list: true, payload_length: 0 };
|
||||
|
||||
rlp_head.payload_length += self.success.length();
|
||||
rlp_head.payload_length += self.cumulative_gas_used.length();
|
||||
rlp_head.payload_length += self.bloom.length();
|
||||
rlp_head.payload_length += self.logs.length();
|
||||
|
||||
rlp_head
|
||||
}
|
||||
|
||||
/// Encodes the receipt data.
|
||||
fn encode_receipt(&self, out: &mut dyn BufMut) {
|
||||
self.receipt_rlp_header().encode(out);
|
||||
self.success.encode(out);
|
||||
self.cumulative_gas_used.encode(out);
|
||||
self.bloom.encode(out);
|
||||
self.logs.encode(out);
|
||||
}
|
||||
|
||||
/// Returns the length of the receipt data.
|
||||
fn receipt_length(&self) -> usize {
|
||||
let rlp_head = self.receipt_rlp_header();
|
||||
length_of_length(rlp_head.payload_length) + rlp_head.payload_length
|
||||
}
|
||||
|
||||
/// Decodes the receipt payload
|
||||
fn decode_receipt(buf: &mut &[u8], tx_type: TxType) -> Result<Self, reth_rlp::DecodeError> {
|
||||
let b = &mut &**buf;
|
||||
let rlp_head = reth_rlp::Header::decode(b)?;
|
||||
if !rlp_head.list {
|
||||
return Err(reth_rlp::DecodeError::UnexpectedString)
|
||||
}
|
||||
let started_len = b.len();
|
||||
let this = Self {
|
||||
tx_type,
|
||||
success: reth_rlp::Decodable::decode(b)?,
|
||||
cumulative_gas_used: reth_rlp::Decodable::decode(b)?,
|
||||
bloom: reth_rlp::Decodable::decode(b)?,
|
||||
logs: reth_rlp::Decodable::decode(b)?,
|
||||
};
|
||||
let consumed = started_len - b.len();
|
||||
if consumed != rlp_head.payload_length {
|
||||
return Err(reth_rlp::DecodeError::ListLengthMismatch {
|
||||
expected: rlp_head.payload_length,
|
||||
got: consumed,
|
||||
})
|
||||
}
|
||||
*buf = *b;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Receipt {
|
||||
fn length(&self) -> usize {
|
||||
let mut payload_len = self.receipt_length();
|
||||
// account for eip-2718 type prefix and set the list
|
||||
if matches!(self.tx_type, TxType::EIP1559 | TxType::EIP2930) {
|
||||
payload_len += 1;
|
||||
// we include a string header for typed receipts, so include the length here
|
||||
payload_len = length_of_length(payload_len);
|
||||
}
|
||||
|
||||
payload_len
|
||||
}
|
||||
fn encode(&self, out: &mut dyn BufMut) {
|
||||
if matches!(self.tx_type, TxType::Legacy) {
|
||||
self.encode_receipt(out);
|
||||
return
|
||||
}
|
||||
|
||||
let mut payload = BytesMut::new();
|
||||
self.encode_receipt(&mut payload);
|
||||
let payload_length = payload.len() + 1;
|
||||
|
||||
let header = reth_rlp::Header { list: false, payload_length };
|
||||
header.encode(out);
|
||||
|
||||
match self.tx_type {
|
||||
TxType::EIP2930 => {
|
||||
out.put_u8(0x01);
|
||||
}
|
||||
TxType::EIP1559 => {
|
||||
out.put_u8(0x02);
|
||||
}
|
||||
_ => unreachable!("legacy handled; qed."),
|
||||
}
|
||||
|
||||
out.put_slice(payload.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Receipt {
|
||||
fn decode(buf: &mut &[u8]) -> Result<Self, reth_rlp::DecodeError> {
|
||||
// a receipt is either encoded as a string (non legacy) or a list (legacy).
|
||||
// We should not consume the buffer if we are decoding a legacy receipt, so let's
|
||||
// check if the first byte is between 0x80 and 0xbf.
|
||||
let rlp_type = *buf
|
||||
.first()
|
||||
.ok_or(reth_rlp::DecodeError::Custom("cannot decode a receipt from empty bytes"))?;
|
||||
|
||||
match rlp_type.cmp(&reth_rlp::EMPTY_LIST_CODE) {
|
||||
Ordering::Less => {
|
||||
// strip out the string header
|
||||
let _header = reth_rlp::Header::decode(buf)?;
|
||||
let receipt_type = *buf.first().ok_or(reth_rlp::DecodeError::Custom(
|
||||
"typed receipt cannot be decoded from an empty slice",
|
||||
))?;
|
||||
if receipt_type == 0x01 {
|
||||
buf.advance(1);
|
||||
Self::decode_receipt(buf, TxType::EIP2930)
|
||||
} else if receipt_type == 0x02 {
|
||||
buf.advance(1);
|
||||
Self::decode_receipt(buf, TxType::EIP1559)
|
||||
} else {
|
||||
Err(reth_rlp::DecodeError::Custom("invalid receipt type"))
|
||||
}
|
||||
}
|
||||
Ordering::Equal => {
|
||||
Err(reth_rlp::DecodeError::Custom("an empty list is not a valid receipt encoding"))
|
||||
}
|
||||
Ordering::Greater => Self::decode_receipt(buf, TxType::Legacy),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Address, H256};
|
||||
use ethers_core::{types::Bytes, utils::hex};
|
||||
use reth_rlp::{Decodable, Encodable};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
// Test vector from: https://eips.ethereum.org/EIPS/eip-2481
|
||||
fn encode_legacy_receipt() {
|
||||
let expected = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap();
|
||||
|
||||
let mut data = vec![];
|
||||
let receipt = Receipt {
|
||||
tx_type: TxType::Legacy,
|
||||
bloom: [0; 256].into(),
|
||||
cumulative_gas_used: 0x1u64,
|
||||
logs: vec![Log {
|
||||
address: Address::from_str("0000000000000000000000000000000000000011").unwrap(),
|
||||
topics: vec![
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000dead",
|
||||
)
|
||||
.unwrap(),
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000beef",
|
||||
)
|
||||
.unwrap(),
|
||||
],
|
||||
data: Bytes::from_str("0100ff").unwrap().0,
|
||||
}],
|
||||
success: false,
|
||||
};
|
||||
|
||||
receipt.encode(&mut data);
|
||||
|
||||
// check that the rlp length equals the length of the expected rlp
|
||||
assert_eq!(receipt.length(), expected.len());
|
||||
assert_eq!(data, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Test vector from: https://eips.ethereum.org/EIPS/eip-2481
|
||||
fn decode_legacy_receipt() {
|
||||
let data = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap();
|
||||
|
||||
// EIP658Receipt
|
||||
let expected = Receipt {
|
||||
tx_type: TxType::Legacy,
|
||||
bloom: [0; 256].into(),
|
||||
cumulative_gas_used: 0x1u64,
|
||||
logs: vec![Log {
|
||||
address: Address::from_str("0000000000000000000000000000000000000011").unwrap(),
|
||||
topics: vec![
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000dead",
|
||||
)
|
||||
.unwrap(),
|
||||
H256::from_str(
|
||||
"000000000000000000000000000000000000000000000000000000000000beef",
|
||||
)
|
||||
.unwrap(),
|
||||
],
|
||||
data: Bytes::from_str("0100ff").unwrap().0,
|
||||
}],
|
||||
success: false,
|
||||
};
|
||||
|
||||
let receipt = Receipt::decode(&mut &data[..]).unwrap();
|
||||
assert_eq!(receipt, expected);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user