mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
feat(db): Make TransactionSigned compactable (#252)
This commit is contained in:
@@ -7,7 +7,7 @@ repository = "https://github.com/foundry-rs/reth"
|
||||
readme = "README.md"
|
||||
|
||||
[features]
|
||||
default = ["scale"]
|
||||
default = ["compact"]
|
||||
compact = ["codecs-derive/compact"]
|
||||
scale = ["codecs-derive/scale"]
|
||||
postcard = ["codecs-derive/postcard"]
|
||||
|
||||
@@ -19,7 +19,7 @@ serde = { version = "1.0.*", default-features = false }
|
||||
parity-scale-codec = { version = "3.2.1", features = ["derive", "bytes"] }
|
||||
|
||||
[features]
|
||||
default = ["scale"]
|
||||
default = ["compact"]
|
||||
compact = []
|
||||
scale = []
|
||||
postcard = []
|
||||
|
||||
@@ -71,6 +71,9 @@ fn build_struct_field_flags(
|
||||
|
||||
// Find out the adequate bit size for the length of each field, if applicable.
|
||||
for (name, ftype, is_compact) in fields {
|
||||
// This happens when dealing with a wrapper struct eg. Struct(pub U256).
|
||||
let name = if name.is_empty() { "placeholder" } else { name };
|
||||
|
||||
if *is_compact {
|
||||
if is_flag_type(ftype) {
|
||||
let name = format_ident!("{name}_len");
|
||||
|
||||
@@ -50,7 +50,13 @@ pub fn generate_from_to(ident: &Ident, fields: &FieldList) -> TokenStream2 {
|
||||
/// Generates code to implement the [`Compact`] trait method `to_compact`.
|
||||
fn generate_from_compact(fields: &FieldList, ident: &Ident) -> Vec<TokenStream2> {
|
||||
let mut lines = vec![];
|
||||
let known_types = ["H256", "H160", "Address", "Bloom", "Vec"];
|
||||
let mut known_types = vec!["H256", "H160", "Address", "Bloom", "Vec"];
|
||||
|
||||
// Only types without `bytes::Bytes` should be added here. It's currently manually added, since
|
||||
// it's hard to figure out with derive_macro which types have bytes::Bytes fields.
|
||||
//
|
||||
// This removes the requirement of the field to be placed last in the struct.
|
||||
known_types.append(&mut vec!["TransactionKind", "AccessList", "Signature"]);
|
||||
|
||||
// let mut handle = FieldListHandler::new(fields);
|
||||
let is_enum = fields.iter().any(|field| matches!(field, FieldTypes::EnumVariant(_)));
|
||||
@@ -66,24 +72,31 @@ fn generate_from_compact(fields: &FieldList, ident: &Ident) -> Vec<TokenStream2>
|
||||
};
|
||||
});
|
||||
} else {
|
||||
lines.append(&mut StructHandler::new(fields).generate_from(known_types.as_slice()));
|
||||
|
||||
let fields = fields.iter().filter_map(|field| {
|
||||
if let FieldTypes::StructField((name, _, _)) = field {
|
||||
let ident = format_ident!("{name}");
|
||||
return Some(quote! {
|
||||
#ident: #ident,
|
||||
})
|
||||
}
|
||||
None
|
||||
});
|
||||
let mut struct_handler = StructHandler::new(fields);
|
||||
lines.append(&mut struct_handler.generate_from(known_types.as_slice()));
|
||||
|
||||
// Builds the object instantiation.
|
||||
lines.push(quote! {
|
||||
let obj = #ident {
|
||||
#(#fields)*
|
||||
};
|
||||
});
|
||||
if struct_handler.is_wrapper {
|
||||
lines.push(quote! {
|
||||
let obj = #ident(placeholder);
|
||||
});
|
||||
} else {
|
||||
let fields = fields.iter().filter_map(|field| {
|
||||
if let FieldTypes::StructField((name, _, _)) = field {
|
||||
let ident = format_ident!("{name}");
|
||||
return Some(quote! {
|
||||
#ident: #ident,
|
||||
})
|
||||
}
|
||||
None
|
||||
});
|
||||
|
||||
lines.push(quote! {
|
||||
let obj = #ident {
|
||||
#(#fields)*
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
lines
|
||||
|
||||
@@ -124,16 +124,14 @@ fn load_field(field: &syn::Field, fields: &mut FieldList, is_enum: bool) {
|
||||
/// Given the field type in a string format, return the amount of bits necessary to save its maximum
|
||||
/// length.
|
||||
pub fn get_bit_size(ftype: &str) -> u8 {
|
||||
if ftype == "u64" || ftype == "BlockNumber" || ftype == "TxNumber" || ftype == "ChainId" {
|
||||
return 4
|
||||
} else if ftype == "TxType" {
|
||||
return 2
|
||||
} else if ftype == "bool" || ftype == "Option" {
|
||||
return 1
|
||||
} else if ftype == "U256" {
|
||||
return 6
|
||||
match ftype {
|
||||
"bool" | "Option" => 1,
|
||||
"TxType" => 2,
|
||||
"u64" | "BlockNumber" | "TxNumber" | "ChainId" => 4,
|
||||
"u128" => 5,
|
||||
"U256" | "TxHash" => 6,
|
||||
_ => 0,
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// Given the field type in a string format, checks if its type should be added to the
|
||||
|
||||
@@ -4,11 +4,16 @@ use super::*;
|
||||
pub struct StructHandler<'a> {
|
||||
fields_iterator: std::iter::Peekable<std::slice::Iter<'a, FieldTypes>>,
|
||||
lines: Vec<TokenStream2>,
|
||||
pub is_wrapper: bool,
|
||||
}
|
||||
|
||||
impl<'a> StructHandler<'a> {
|
||||
pub fn new(fields: &'a FieldList) -> Self {
|
||||
StructHandler { lines: vec![], fields_iterator: fields.iter().peekable() }
|
||||
StructHandler {
|
||||
lines: vec![],
|
||||
fields_iterator: fields.iter().peekable(),
|
||||
is_wrapper: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_field(&mut self) -> Option<&'a FieldTypes> {
|
||||
@@ -18,8 +23,6 @@ impl<'a> StructHandler<'a> {
|
||||
pub fn generate_to(mut self) -> Vec<TokenStream2> {
|
||||
while let Some(field) = self.next_field() {
|
||||
match field {
|
||||
// The following method will advance the
|
||||
// `fields_iterator` by itself and stop right before the next variant.
|
||||
FieldTypes::EnumVariant(_) => unreachable!(),
|
||||
FieldTypes::EnumUnnamedField(_) => unreachable!(),
|
||||
FieldTypes::StructField(field_descriptor) => self.to(field_descriptor),
|
||||
@@ -28,11 +31,9 @@ impl<'a> StructHandler<'a> {
|
||||
self.lines
|
||||
}
|
||||
|
||||
pub fn generate_from(mut self, known_types: &[&str]) -> Vec<TokenStream2> {
|
||||
pub fn generate_from(&mut self, known_types: &[&str]) -> Vec<TokenStream2> {
|
||||
while let Some(field) = self.next_field() {
|
||||
match field {
|
||||
// The following method will advance the
|
||||
// `fields_iterator` by itself and stop right before the next variant.
|
||||
FieldTypes::EnumVariant(_) => unreachable!(),
|
||||
FieldTypes::EnumUnnamedField(_) => unreachable!(),
|
||||
FieldTypes::StructField(field_descriptor) => {
|
||||
@@ -40,13 +41,30 @@ impl<'a> StructHandler<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.lines
|
||||
self.lines.clone()
|
||||
}
|
||||
|
||||
/// Generates `to_compact` code for a struct field.
|
||||
fn to(&mut self, field_descriptor: &StructFieldDescriptor) {
|
||||
let (name, ftype, is_compact) = field_descriptor;
|
||||
|
||||
// Should only happen on wrapper structs like `Struct(pub Field)`
|
||||
if name.is_empty() {
|
||||
self.is_wrapper = true;
|
||||
|
||||
self.lines.push(quote! {
|
||||
let _len = self.0.to_compact(&mut buffer);
|
||||
});
|
||||
|
||||
if is_flag_type(ftype) {
|
||||
self.lines.push(quote! {
|
||||
flags.set_placeholder_len(_len as u8);
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let name = format_ident!("{name}");
|
||||
let set_len_method = format_ident!("set_{name}_len");
|
||||
let len = format_ident!("{name}_len");
|
||||
@@ -77,8 +95,14 @@ impl<'a> StructHandler<'a> {
|
||||
fn from(&mut self, field_descriptor: &StructFieldDescriptor, known_types: &[&str]) {
|
||||
let (name, ftype, is_compact) = field_descriptor;
|
||||
|
||||
let name = format_ident!("{name}");
|
||||
let len = format_ident!("{name}_len");
|
||||
let (name, len) = if name.is_empty() {
|
||||
self.is_wrapper = true;
|
||||
|
||||
// Should only happen on wrapper structs like `Struct(pub Field)`
|
||||
(format_ident!("placeholder"), format_ident!("placeholder_len"))
|
||||
} else {
|
||||
(format_ident!("{name}"), format_ident!("{name}_len"))
|
||||
};
|
||||
|
||||
assert!(
|
||||
known_types.contains(&ftype.as_str()) ||
|
||||
|
||||
@@ -12,7 +12,10 @@ pub fn derive(input: TokenStream) -> TokenStream {
|
||||
#[proc_macro_attribute]
|
||||
#[rustfmt::skip]
|
||||
#[allow(unreachable_code)]
|
||||
pub fn main_codec(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pub fn main_codec(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
#[cfg(feature = "compact")]
|
||||
return use_compact(args, input);
|
||||
|
||||
#[cfg(feature = "scale")]
|
||||
return use_scale(args, input);
|
||||
|
||||
@@ -21,9 +24,6 @@ pub fn main_codec(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
|
||||
#[cfg(feature = "no_codec")]
|
||||
return no_codec(args, input);
|
||||
|
||||
#[cfg(feature = "compact")]
|
||||
return use_compact(args, input);
|
||||
|
||||
// no features
|
||||
no_codec(args, input)
|
||||
|
||||
@@ -28,26 +28,34 @@ pub trait Compact {
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl Compact for u64 {
|
||||
fn to_compact(self, buf: &mut impl bytes::BufMut) -> usize {
|
||||
let leading = self.leading_zeros() as usize / 8;
|
||||
buf.put_slice(&self.to_be_bytes()[leading..]);
|
||||
8 - leading
|
||||
}
|
||||
macro_rules! impl_uint_compact {
|
||||
($($name:tt),+) => {
|
||||
$(
|
||||
impl Compact for $name {
|
||||
fn to_compact(self, buf: &mut impl bytes::BufMut) -> usize {
|
||||
let leading = self.leading_zeros() as usize / 8;
|
||||
buf.put_slice(&self.to_be_bytes()[leading..]);
|
||||
std::mem::size_of::<$name>() - leading
|
||||
}
|
||||
|
||||
fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) {
|
||||
if len > 0 {
|
||||
let mut arr = [0; 8];
|
||||
arr[8 - len..].copy_from_slice(&buf[..len]);
|
||||
fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) {
|
||||
if len > 0 {
|
||||
let mut arr = [0; std::mem::size_of::<$name>()];
|
||||
arr[std::mem::size_of::<$name>() - len..].copy_from_slice(&buf[..len]);
|
||||
|
||||
buf.advance(len);
|
||||
buf.advance(len);
|
||||
|
||||
return (u64::from_be_bytes(arr), buf)
|
||||
}
|
||||
(0, buf)
|
||||
}
|
||||
return ($name::from_be_bytes(arr), buf)
|
||||
}
|
||||
(0, buf)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl_uint_compact!(u64, u128);
|
||||
|
||||
impl<T> Compact for Vec<T>
|
||||
where
|
||||
T: Compact + Default,
|
||||
@@ -144,12 +152,12 @@ impl Compact for Bytes {
|
||||
}
|
||||
|
||||
macro_rules! impl_hash_compact {
|
||||
($(($name:tt, $size:tt)),+) => {
|
||||
($($name:tt),+) => {
|
||||
$(
|
||||
impl Compact for $name {
|
||||
fn to_compact(self, buf: &mut impl bytes::BufMut) -> usize {
|
||||
buf.put_slice(&self.0);
|
||||
$size
|
||||
std::mem::size_of::<$name>()
|
||||
}
|
||||
|
||||
fn from_compact(mut buf: &[u8], len: usize) -> (Self,&[u8]) {
|
||||
@@ -158,9 +166,9 @@ macro_rules! impl_hash_compact {
|
||||
}
|
||||
|
||||
let v = $name::from_slice(
|
||||
buf.get(..$size).expect("size not matching"),
|
||||
buf.get(..std::mem::size_of::<$name>()).expect("size not matching"),
|
||||
);
|
||||
buf.advance($size);
|
||||
buf.advance(std::mem::size_of::<$name>());
|
||||
(v, buf)
|
||||
}
|
||||
}
|
||||
@@ -168,7 +176,7 @@ macro_rules! impl_hash_compact {
|
||||
};
|
||||
}
|
||||
|
||||
impl_hash_compact!((H256, 32), (H160, 20));
|
||||
impl_hash_compact!(H256, H160);
|
||||
|
||||
impl Compact for Bloom {
|
||||
fn to_compact(self, buf: &mut impl bytes::BufMut) -> usize {
|
||||
@@ -337,7 +345,7 @@ mod tests {
|
||||
assert_eq!(u64::from_compact(&buf, 8), (0xffffffffffffffffu64, vec![].as_slice()));
|
||||
}
|
||||
|
||||
#[use_compact]
|
||||
#[main_codec]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct TestStruct {
|
||||
f_u64: u64,
|
||||
@@ -389,7 +397,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[use_compact]
|
||||
#[main_codec]
|
||||
#[derive(Debug, PartialEq, Clone, Default)]
|
||||
pub enum TestEnum {
|
||||
#[default]
|
||||
|
||||
Reference in New Issue
Block a user