feat(db): Make TransactionSigned compactable (#252)

This commit is contained in:
joshieDo
2022-11-28 15:29:30 +08:00
committed by GitHub
parent b02a5c4bbf
commit 3efc6ee67e
37 changed files with 545 additions and 391 deletions

View File

@@ -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"]

View File

@@ -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 = []

View File

@@ -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");

View File

@@ -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

View File

@@ -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

View File

@@ -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()) ||

View File

@@ -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)

View File

@@ -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]