fix(rpc): type encoding (#155)

* fix(rpc): type encoding

* copy bytes from ethers
This commit is contained in:
Roman Krasiuk
2022-11-03 12:04:09 +02:00
committed by GitHub
parent becceb2802
commit bff27a4154
8 changed files with 261 additions and 33 deletions

13
Cargo.lock generated
View File

@ -1261,17 +1261,6 @@ dependencies = [
"instant", "instant",
] ]
[[package]]
name = "fastrlp"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "089263294bb1c38ac73649a6ad563dd9a5142c8dc0482be15b8b9acb22a1611e"
dependencies = [
"arrayvec",
"auto_impl",
"bytes",
]
[[package]] [[package]]
name = "ff" name = "ff"
version = "0.12.0" version = "0.12.0"
@ -3342,8 +3331,8 @@ dependencies = [
name = "reth-rpc-types" name = "reth-rpc-types"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"fastrlp",
"reth-primitives", "reth-primitives",
"reth-rlp",
"serde", "serde",
"serde_json", "serde_json",
] ]

View File

@ -11,9 +11,7 @@ Reth RPC types
[dependencies] [dependencies]
# reth # reth
reth-primitives = { path = "../../primitives" } reth-primitives = { path = "../../primitives" }
reth-rlp = {path = "../../common/rlp"}
# eth
fastrlp = { version = "0.1" }
# misc # misc
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

View File

@ -2,7 +2,7 @@ use crate::eth::transaction::typed::{
EIP1559TransactionRequest, EIP2930TransactionRequest, LegacyTransactionRequest, EIP1559TransactionRequest, EIP2930TransactionRequest, LegacyTransactionRequest,
TransactionKind, TypedTransactionRequest, TransactionKind, TypedTransactionRequest,
}; };
use reth_primitives::{rpc::transaction::eip2930::AccessListItem, Address, Bytes, U256}; use reth_primitives::{AccessList, Address, Bytes, U256};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Represents _all_ transaction requests received from RPC /// Represents _all_ transaction requests received from RPC
@ -33,7 +33,7 @@ pub struct TransactionRequest {
pub nonce: Option<U256>, pub nonce: Option<U256>,
/// warm storage access pre-payment /// warm storage access pre-payment
#[serde(default)] #[serde(default)]
pub access_list: Option<Vec<AccessListItem>>, pub access_list: Option<AccessList>,
/// EIP-2718 type /// EIP-2718 type
#[serde(rename = "type")] #[serde(rename = "type")]
pub transaction_type: Option<U256>, pub transaction_type: Option<U256>,

View File

@ -3,8 +3,8 @@
//! json input of an RPC call. Depending on what fields are set, it can be converted into the //! json input of an RPC call. Depending on what fields are set, it can be converted into the
//! container type [`TypedTransactionRequest`]. //! container type [`TypedTransactionRequest`].
use fastrlp::{RlpDecodable, RlpEncodable}; use reth_primitives::{AccessList, Address, Bytes, U256};
use reth_primitives::{rpc::transaction::eip2930::AccessListItem, Address, Bytes, U256}; use reth_rlp::{BufMut, Decodable, DecodeError, Encodable, RlpDecodable, RlpEncodable};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Container type for various Ethereum transaction requests /// Container type for various Ethereum transaction requests
@ -42,7 +42,7 @@ pub struct EIP2930TransactionRequest {
pub kind: TransactionKind, pub kind: TransactionKind,
pub value: U256, pub value: U256,
pub input: Bytes, pub input: Bytes,
pub access_list: Vec<AccessListItem>, pub access_list: AccessList,
} }
/// Represents an EIP-1559 transaction request /// Represents an EIP-1559 transaction request
@ -56,7 +56,7 @@ pub struct EIP1559TransactionRequest {
pub kind: TransactionKind, pub kind: TransactionKind,
pub value: U256, pub value: U256,
pub input: Bytes, pub input: Bytes,
pub access_list: Vec<AccessListItem>, pub access_list: AccessList,
} }
/// Represents the `to` field of a transaction request /// Represents the `to` field of a transaction request
@ -82,14 +82,14 @@ impl TransactionKind {
} }
} }
impl fastrlp::Encodable for TransactionKind { impl Encodable for TransactionKind {
fn length(&self) -> usize { fn length(&self) -> usize {
match self { match self {
TransactionKind::Call(to) => to.length(), TransactionKind::Call(to) => to.length(),
TransactionKind::Create => ([]).length(), TransactionKind::Create => ([]).length(),
} }
} }
fn encode(&self, out: &mut dyn fastrlp::BufMut) { fn encode(&self, out: &mut dyn BufMut) {
match self { match self {
TransactionKind::Call(to) => to.encode(out), TransactionKind::Call(to) => to.encode(out),
TransactionKind::Create => ([]).encode(out), TransactionKind::Create => ([]).encode(out),
@ -97,18 +97,18 @@ impl fastrlp::Encodable for TransactionKind {
} }
} }
impl fastrlp::Decodable for TransactionKind { impl Decodable for TransactionKind {
fn decode(buf: &mut &[u8]) -> Result<Self, fastrlp::DecodeError> { fn decode(buf: &mut &[u8]) -> Result<Self, DecodeError> {
if let Some(&first) = buf.first() { if let Some(&first) = buf.first() {
if first == 0x80 { if first == 0x80 {
*buf = &buf[1..]; *buf = &buf[1..];
Ok(TransactionKind::Create) Ok(TransactionKind::Create)
} else { } else {
let addr = <Address as fastrlp::Decodable>::decode(buf)?; let addr = <Address as Decodable>::decode(buf)?;
Ok(TransactionKind::Call(addr)) Ok(TransactionKind::Call(addr))
} }
} else { } else {
Err(fastrlp::DecodeError::InputTooShort) Err(DecodeError::InputTooShort)
} }
} }
} }

View File

@ -0,0 +1,226 @@
use reth_rlp::{Decodable, DecodeError, Encodable};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{
borrow::Borrow,
clone::Clone,
fmt::{Debug, Display, Formatter, LowerHex, Result as FmtResult},
ops::Deref,
str::FromStr,
};
use thiserror::Error;
/// Wrapper type around Bytes to deserialize/serialize "0x" prefixed ethereum hex strings
#[derive(Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
pub struct Bytes(
#[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")]
pub bytes::Bytes,
);
fn bytes_to_hex(b: &Bytes) -> String {
hex::encode(b.0.as_ref())
}
impl Debug for Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "Bytes(0x{})", bytes_to_hex(self))
}
}
impl Display for Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "0x{}", bytes_to_hex(self))
}
}
impl LowerHex for Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "0x{}", bytes_to_hex(self))
}
}
impl Bytes {
/// Return bytes as [Vec::<u8>]
pub fn to_vec(&self) -> Vec<u8> {
self.as_ref().to_vec()
}
}
impl Deref for Bytes {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.as_ref()
}
}
impl AsRef<[u8]> for Bytes {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl Borrow<[u8]> for Bytes {
fn borrow(&self) -> &[u8] {
self.as_ref()
}
}
impl IntoIterator for Bytes {
type Item = u8;
type IntoIter = bytes::buf::IntoIter<bytes::Bytes>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a Bytes {
type Item = &'a u8;
type IntoIter = core::slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.as_ref().iter()
}
}
impl From<bytes::Bytes> for Bytes {
fn from(src: bytes::Bytes) -> Self {
Self(src)
}
}
impl From<Vec<u8>> for Bytes {
fn from(src: Vec<u8>) -> Self {
Self(src.into())
}
}
impl<const N: usize> From<[u8; N]> for Bytes {
fn from(src: [u8; N]) -> Self {
src.to_vec().into()
}
}
impl<'a, const N: usize> From<&'a [u8; N]> for Bytes {
fn from(src: &'a [u8; N]) -> Self {
src.to_vec().into()
}
}
impl PartialEq<[u8]> for Bytes {
fn eq(&self, other: &[u8]) -> bool {
self.as_ref() == other
}
}
impl PartialEq<Bytes> for [u8] {
fn eq(&self, other: &Bytes) -> bool {
*other == *self
}
}
impl PartialEq<Vec<u8>> for Bytes {
fn eq(&self, other: &Vec<u8>) -> bool {
self.as_ref() == &other[..]
}
}
impl PartialEq<Bytes> for Vec<u8> {
fn eq(&self, other: &Bytes) -> bool {
*other == *self
}
}
impl PartialEq<bytes::Bytes> for Bytes {
fn eq(&self, other: &bytes::Bytes) -> bool {
other == self.as_ref()
}
}
impl Encodable for Bytes {
fn length(&self) -> usize {
self.0.length()
}
fn encode(&self, out: &mut dyn bytes::BufMut) {
self.0.encode(out)
}
}
impl Decodable for Bytes {
fn decode(buf: &mut &[u8]) -> Result<Self, DecodeError> {
Ok(Self(bytes::Bytes::decode(buf)?))
}
}
#[derive(Debug, Clone, Error)]
#[error("Failed to parse bytes: {0}")]
pub struct ParseBytesError(String);
impl FromStr for Bytes {
type Err = ParseBytesError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
if let Some(value) = value.strip_prefix("0x") {
hex::decode(value)
} else {
hex::decode(value)
}
.map(Into::into)
.map_err(|e| ParseBytesError(format!("Invalid hex: {}", e)))
}
}
fn serialize_bytes<S, T>(x: T, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: AsRef<[u8]>,
{
s.serialize_str(&format!("0x{}", hex::encode(x.as_ref())))
}
fn deserialize_bytes<'de, D>(d: D) -> Result<bytes::Bytes, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(d)?;
if let Some(value) = value.strip_prefix("0x") {
hex::decode(value)
} else {
hex::decode(&value)
}
.map(Into::into)
.map_err(|e| serde::de::Error::custom(e.to_string()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hex_formatting() {
let b = Bytes::from(vec![1, 35, 69, 103, 137, 171, 205, 239]);
let expected = String::from("0x0123456789abcdef");
assert_eq!(format!("{:x}", b), expected);
assert_eq!(format!("{}", b), expected);
}
#[test]
fn test_from_str() {
let b = Bytes::from_str("0x1213");
assert!(b.is_ok());
let b = b.unwrap();
assert_eq!(b.as_ref(), hex::decode("1213").unwrap());
let b = Bytes::from_str("1213");
let b = b.unwrap();
assert_eq!(b.as_ref(), hex::decode("1213").unwrap());
}
#[test]
fn test_debug_formatting() {
let b = Bytes::from(vec![1, 35, 69, 103, 137, 171, 205, 239]);
assert_eq!(format!("{:?}", b), "Bytes(0x0123456789abcdef)");
assert_eq!(format!("{:#?}", b), "Bytes(0x0123456789abcdef)");
}
}

View File

@ -12,6 +12,7 @@ mod block;
mod chain; mod chain;
mod error; mod error;
mod header; mod header;
mod hex_bytes;
mod integer_list; mod integer_list;
mod jsonu256; mod jsonu256;
mod log; mod log;
@ -23,6 +24,7 @@ pub use account::Account;
pub use block::{Block, BlockLocked}; pub use block::{Block, BlockLocked};
pub use chain::Chain; pub use chain::Chain;
pub use header::{Header, HeaderLocked}; pub use header::{Header, HeaderLocked};
pub use hex_bytes::Bytes;
pub use integer_list::IntegerList; pub use integer_list::IntegerList;
pub use jsonu256::JsonU256; pub use jsonu256::JsonU256;
pub use log::Log; pub use log::Log;
@ -51,10 +53,9 @@ pub type StorageKey = H256;
/// Storage value /// Storage value
pub type StorageValue = H256; pub type StorageValue = H256;
// NOTE: There is a benefit of using wrapped Bytes as it gives us serde and debug
pub use ethers_core::{ pub use ethers_core::{
types as rpc, types as rpc,
types::{BigEndianHash, Bloom, Bytes, H128, H160, H256, H512, H64, U128, U256, U64}, types::{BigEndianHash, Bloom, H128, H160, H256, H512, H64, U128, U256, U64},
}; };
#[doc(hidden)] #[doc(hidden)]

View File

@ -1,10 +1,13 @@
use reth_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper}; use reth_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
use serde::{Deserialize, Serialize};
use crate::{Address, H256}; use crate::{Address, H256};
/// A list of addresses and storage keys that the transaction plans to access. /// A list of addresses and storage keys that the transaction plans to access.
/// Accesses outside the list are possible, but become more expensive. /// Accesses outside the list are possible, but become more expensive.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, RlpDecodable, RlpEncodable)] #[derive(
Clone, Debug, PartialEq, Eq, Hash, Default, RlpDecodable, RlpEncodable, Serialize, Deserialize,
)]
pub struct AccessListItem { pub struct AccessListItem {
/// Account addresses that would be loaded at the start of execution /// Account addresses that would be loaded at the start of execution
pub address: Address, pub address: Address,
@ -13,5 +16,16 @@ pub struct AccessListItem {
} }
/// AccessList as defined in EIP-2930 /// AccessList as defined in EIP-2930
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, RlpDecodableWrapper, RlpEncodableWrapper)] #[derive(
Clone,
Debug,
PartialEq,
Eq,
Hash,
Default,
RlpDecodableWrapper,
RlpEncodableWrapper,
Serialize,
Deserialize,
)]
pub struct AccessList(pub Vec<AccessListItem>); pub struct AccessList(pub Vec<AccessListItem>);

View File

@ -632,10 +632,10 @@ mod tests {
use crate::{ use crate::{
transaction::{signature::Signature, TransactionKind}, transaction::{signature::Signature, TransactionKind},
Address, Transaction, TransactionSigned, H256, U256, Address, Bytes, Transaction, TransactionSigned, H256, U256,
}; };
use bytes::BytesMut; use bytes::BytesMut;
use ethers_core::{types::Bytes, utils::hex}; use ethers_core::utils::hex;
use reth_rlp::{Decodable, Encodable}; use reth_rlp::{Decodable, Encodable};
#[test] #[test]