From c3347f323c0a6d7784772624d91edebf4a76d36d Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 17 Jul 2024 22:50:28 +0200 Subject: [PATCH] feat: make `to_compact` borrow (#9488) Co-authored-by: joshieDo <93316087+joshieDo@users.noreply.github.com> --- crates/primitives-traits/src/account.rs | 4 +- crates/primitives-traits/src/log.rs | 2 +- crates/primitives-traits/src/storage.rs | 2 +- crates/primitives-traits/src/withdrawal.rs | 2 +- crates/primitives/src/receipt.rs | 2 +- .../primitives/src/transaction/access_list.rs | 2 +- crates/primitives/src/transaction/mod.rs | 6 +- .../primitives/src/transaction/signature.rs | 2 +- crates/primitives/src/transaction/tx_type.rs | 8 +- crates/stages/types/src/checkpoints.rs | 6 +- crates/storage/codecs/Cargo.toml | 2 +- crates/storage/codecs/derive/src/arbitrary.rs | 4 +- .../codecs/derive/src/compact/flags.rs | 27 +++- .../codecs/derive/src/compact/generator.rs | 76 +++++++--- .../storage/codecs/derive/src/compact/mod.rs | 90 +++++++----- crates/storage/codecs/derive/src/lib.rs | 9 +- .../storage/codecs/src/alloy/access_list.rs | 8 +- .../codecs/src/alloy/authorization_list.rs | 12 +- .../codecs/src/alloy/genesis_account.rs | 32 +++- crates/storage/codecs/src/alloy/log.rs | 12 +- crates/storage/codecs/src/alloy/request.rs | 2 +- crates/storage/codecs/src/alloy/trie.rs | 8 +- crates/storage/codecs/src/alloy/txkind.rs | 2 +- crates/storage/codecs/src/alloy/withdrawal.rs | 2 +- crates/storage/codecs/src/lib.rs | 137 +++++++++++++++--- crates/storage/db-api/src/models/accounts.rs | 2 +- .../db-api/src/models/client_version.rs | 9 +- crates/storage/db-api/src/models/mod.rs | 4 +- crates/trie/common/src/hash_builder/state.rs | 4 +- crates/trie/common/src/nibbles.rs | 4 +- crates/trie/common/src/storage.rs | 2 +- crates/trie/common/src/subnode.rs | 6 +- 32 files changed, 337 insertions(+), 153 deletions(-) diff --git a/crates/primitives-traits/src/account.rs b/crates/primitives-traits/src/account.rs index 84192846d..8ecbf19a9 100644 --- a/crates/primitives-traits/src/account.rs +++ b/crates/primitives-traits/src/account.rs @@ -67,7 +67,7 @@ impl Bytecode { } impl Compact for Bytecode { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -209,7 +209,7 @@ mod tests { 2, JumpTable::from_slice(&[0]), ))); - let len = bytecode.clone().to_compact(&mut buf); + let len = bytecode.to_compact(&mut buf); assert_eq!(len, 16); let (decoded, remainder) = Bytecode::from_compact(&buf, len); diff --git a/crates/primitives-traits/src/log.rs b/crates/primitives-traits/src/log.rs index 7812c0102..dcb7cced5 100644 --- a/crates/primitives-traits/src/log.rs +++ b/crates/primitives-traits/src/log.rs @@ -58,7 +58,7 @@ mod tests { fn test_roundtrip_conversion_between_log_and_alloy_log(log in arb::()) { // Convert log to buffer and then create alloy_log from buffer and compare let mut compacted_log = Vec::::new(); - let len = log.clone().to_compact(&mut compacted_log); + let len = log.to_compact(&mut compacted_log); let alloy_log = AlloyLog::from_compact(&compacted_log, len).0; assert_eq!(log, alloy_log.into()); diff --git a/crates/primitives-traits/src/storage.rs b/crates/primitives-traits/src/storage.rs index b8bd000d0..0c9f5c3a2 100644 --- a/crates/primitives-traits/src/storage.rs +++ b/crates/primitives-traits/src/storage.rs @@ -31,7 +31,7 @@ impl From<(B256, U256)> for StorageEntry { // and compress second part of the value. If we have compression // over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey impl Compact for StorageEntry { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { diff --git a/crates/primitives-traits/src/withdrawal.rs b/crates/primitives-traits/src/withdrawal.rs index 3f4c6768e..cabdb0525 100644 --- a/crates/primitives-traits/src/withdrawal.rs +++ b/crates/primitives-traits/src/withdrawal.rs @@ -144,7 +144,7 @@ mod tests { // Convert to buffer and then create alloy_access_list from buffer and // compare let mut compacted_reth_withdrawal = Vec::::new(); - let len = withdrawal.clone().to_compact(&mut compacted_reth_withdrawal); + let len = withdrawal.to_compact(&mut compacted_reth_withdrawal); // decode the compacted buffer to AccessList let alloy_withdrawal = Withdrawal::from_compact(&compacted_reth_withdrawal, len).0; diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 91027f66f..ab5722a34 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -658,7 +658,7 @@ mod tests { }; let mut data = vec![]; - receipt.clone().to_compact(&mut data); + receipt.to_compact(&mut data); let (decoded, _) = Receipt::from_compact(&data[..], data.len()); assert_eq!(decoded, receipt); } diff --git a/crates/primitives/src/transaction/access_list.rs b/crates/primitives/src/transaction/access_list.rs index d961c4f6c..636a910a2 100644 --- a/crates/primitives/src/transaction/access_list.rs +++ b/crates/primitives/src/transaction/access_list.rs @@ -73,7 +73,7 @@ mod tests { // Convert access_list to buffer and then create alloy_access_list from buffer and // compare let mut compacted_reth_access_list = Vec::::new(); - let len = access_list.clone().to_compact(&mut compacted_reth_access_list); + let len = access_list.to_compact(&mut compacted_reth_access_list); // decode the compacted buffer to AccessList let alloy_access_list = AccessList::from_compact(&compacted_reth_access_list, len).0; diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 5c2584dda..67322461f 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -681,7 +681,7 @@ impl From for Transaction { impl reth_codecs::Compact for Transaction { // Serializes the TxType to the buffer if necessary, returning 2 bits of the type as an // identifier instead of the length. - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -910,7 +910,7 @@ impl TransactionSignedNoHash { #[cfg(any(test, feature = "reth-codec"))] impl reth_codecs::Compact for TransactionSignedNoHash { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -2083,7 +2083,7 @@ mod tests { fn test_transaction_signed_to_from_compact(tx_signed_no_hash: TransactionSignedNoHash) { // zstd aware `to_compact` let mut buff: Vec = Vec::new(); - let written_bytes = tx_signed_no_hash.clone().to_compact(&mut buff); + let written_bytes = tx_signed_no_hash.to_compact(&mut buff); let (decoded, _) = TransactionSignedNoHash::from_compact(&buff, written_bytes); assert_eq!(tx_signed_no_hash, decoded); } diff --git a/crates/primitives/src/transaction/signature.rs b/crates/primitives/src/transaction/signature.rs index 7050d6c31..1f14cadef 100644 --- a/crates/primitives/src/transaction/signature.rs +++ b/crates/primitives/src/transaction/signature.rs @@ -43,7 +43,7 @@ impl Signature { #[cfg(any(test, feature = "reth-codec"))] impl reth_codecs::Compact for Signature { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { diff --git a/crates/primitives/src/transaction/tx_type.rs b/crates/primitives/src/transaction/tx_type.rs index 747537052..d31cba060 100644 --- a/crates/primitives/src/transaction/tx_type.rs +++ b/crates/primitives/src/transaction/tx_type.rs @@ -138,7 +138,7 @@ impl TryFrom for TxType { #[cfg(any(test, feature = "reth-codec"))] impl reth_codecs::Compact for TxType { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -147,16 +147,16 @@ impl reth_codecs::Compact for TxType { Self::Eip2930 => 1, Self::Eip1559 => 2, Self::Eip4844 => { - buf.put_u8(self as u8); + buf.put_u8(*self as u8); COMPACT_EXTENDED_IDENTIFIER_FLAG } Self::Eip7702 => { - buf.put_u8(self as u8); + buf.put_u8(*self as u8); COMPACT_EXTENDED_IDENTIFIER_FLAG } #[cfg(feature = "optimism")] Self::Deposit => { - buf.put_u8(self as u8); + buf.put_u8(*self as u8); COMPACT_EXTENDED_IDENTIFIER_FLAG } } diff --git a/crates/stages/types/src/checkpoints.rs b/crates/stages/types/src/checkpoints.rs index fb62feab5..c22f50dda 100644 --- a/crates/stages/types/src/checkpoints.rs +++ b/crates/stages/types/src/checkpoints.rs @@ -33,7 +33,7 @@ impl MerkleCheckpoint { } impl Compact for MerkleCheckpoint { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -47,7 +47,7 @@ impl Compact for MerkleCheckpoint { buf.put_u16(self.walker_stack.len() as u16); len += 2; - for item in self.walker_stack { + for item in &self.walker_stack { len += item.to_compact(buf); } @@ -393,7 +393,7 @@ mod tests { }; let mut buf = Vec::new(); - let encoded = checkpoint.clone().to_compact(&mut buf); + let encoded = checkpoint.to_compact(&mut buf); let (decoded, _) = MerkleCheckpoint::from_compact(&buf, encoded); assert_eq!(decoded, checkpoint); } diff --git a/crates/storage/codecs/Cargo.toml b/crates/storage/codecs/Cargo.toml index 224f919f1..4789ff6e1 100644 --- a/crates/storage/codecs/Cargo.toml +++ b/crates/storage/codecs/Cargo.toml @@ -31,7 +31,7 @@ alloy-eips = { workspace = true, default-features = false, features = [ "arbitrary", "serde", ] } -alloy-primitives = { workspace = true, features = ["arbitrary", "serde"] } +alloy-primitives = { workspace = true, features = ["arbitrary", "serde", "rand"] } alloy-consensus = { workspace = true, features = ["arbitrary"] } test-fuzz.workspace = true serde_json.workspace = true diff --git a/crates/storage/codecs/derive/src/arbitrary.rs b/crates/storage/codecs/derive/src/arbitrary.rs index 60bc9074a..8176d5d52 100644 --- a/crates/storage/codecs/derive/src/arbitrary.rs +++ b/crates/storage/codecs/derive/src/arbitrary.rs @@ -26,7 +26,7 @@ pub fn maybe_generate_tests(args: TokenStream, ast: &DeriveInput) -> TokenStream let mut buf = vec![]; let len = field.clone().to_compact(&mut buf); let (decoded, _) = super::#type_ident::from_compact(&buf, len); - assert!(field == decoded); + assert!(field == decoded, "maybe_generate_tests::compact"); } }); } else if arg.to_string() == "rlp" { @@ -37,7 +37,7 @@ pub fn maybe_generate_tests(args: TokenStream, ast: &DeriveInput) -> TokenStream let len = field.encode(&mut buf); let mut b = &mut buf.as_slice(); let decoded = super::#type_ident::decode(b).unwrap(); - assert_eq!(field, decoded); + assert_eq!(field, decoded, "maybe_generate_tests::rlp"); // ensure buffer is fully consumed by decode assert!(b.is_empty(), "buffer was not consumed entirely"); diff --git a/crates/storage/codecs/derive/src/compact/flags.rs b/crates/storage/codecs/derive/src/compact/flags.rs index 24757d8e6..5a0f33b32 100644 --- a/crates/storage/codecs/derive/src/compact/flags.rs +++ b/crates/storage/codecs/derive/src/compact/flags.rs @@ -4,6 +4,7 @@ use super::*; /// their potential presence. pub(crate) fn generate_flag_struct( ident: &Ident, + has_lifetime: bool, fields: &FieldList, is_zstd: bool, ) -> TokenStream2 { @@ -52,15 +53,29 @@ pub(crate) fn generate_flag_struct( let docs = format!("Fieldset that facilitates compacting the parent type. Used bytes: {total_bytes} | Unused bits: {unused_bits}"); let bitflag_encoded_bytes = format!("Used bytes by [`{flags_ident}`]"); + let impl_bitflag_encoded_bytes = if has_lifetime { + quote! { + impl<'a> #ident<'a> { + #[doc = #bitflag_encoded_bytes] + pub const fn bitflag_encoded_bytes() -> usize { + #total_bytes as usize + } + } + } + } else { + quote! { + impl #ident { + #[doc = #bitflag_encoded_bytes] + pub const fn bitflag_encoded_bytes() -> usize { + #total_bytes as usize + } + } + } + }; // Generate the flag struct. quote! { - impl #ident { - #[doc = #bitflag_encoded_bytes] - pub const fn bitflag_encoded_bytes() -> usize { - #total_bytes as usize - } - } + #impl_bitflag_encoded_bytes pub use #mod_flags_ident::#flags_ident; #[allow(non_snake_case)] mod #mod_flags_ident { diff --git a/crates/storage/codecs/derive/src/compact/generator.rs b/crates/storage/codecs/derive/src/compact/generator.rs index c28bf8d1a..9b1b09e47 100644 --- a/crates/storage/codecs/derive/src/compact/generator.rs +++ b/crates/storage/codecs/derive/src/compact/generator.rs @@ -4,7 +4,12 @@ use super::*; use convert_case::{Case, Casing}; /// Generates code to implement the `Compact` trait for a data type. -pub fn generate_from_to(ident: &Ident, fields: &FieldList, is_zstd: bool) -> TokenStream2 { +pub fn generate_from_to( + ident: &Ident, + has_lifetime: bool, + fields: &FieldList, + is_zstd: bool, +) -> TokenStream2 { let flags = format_ident!("{ident}Flags"); let to_compact = generate_to_compact(fields, ident, is_zstd); @@ -15,26 +20,58 @@ pub fn generate_from_to(ident: &Ident, fields: &FieldList, is_zstd: bool) -> Tok let fuzz = format_ident!("fuzz_test_{snake_case_ident}"); let test = format_ident!("fuzz_{snake_case_ident}"); + let lifetime = if has_lifetime { + quote! { 'a } + } else { + quote! {} + }; + + let impl_compact = if has_lifetime { + quote! { + impl<#lifetime> Compact for #ident<#lifetime> + } + } else { + quote! { + impl Compact for #ident + } + }; + + let fn_from_compact = if has_lifetime { + quote! { unimplemented!("from_compact not supported with ref structs") } + } else { + quote! { + let (flags, mut buf) = #flags::from(buf); + #from_compact + } + }; + + let fuzz_tests = if has_lifetime { + quote! {} + } else { + quote! { + #[cfg(test)] + #[allow(dead_code)] + #[test_fuzz::test_fuzz] + fn #fuzz(obj: #ident) { + let mut buf = vec![]; + let len = obj.clone().to_compact(&mut buf); + let (same_obj, buf) = #ident::from_compact(buf.as_ref(), len); + assert_eq!(obj, same_obj); + } + + #[test] + pub fn #test() { + #fuzz(#ident::default()) + } + } + }; + // Build function quote! { + #fuzz_tests - #[cfg(test)] - #[allow(dead_code)] - #[test_fuzz::test_fuzz] - fn #fuzz(obj: #ident) { - let mut buf = vec![]; - let len = obj.clone().to_compact(&mut buf); - let (same_obj, buf) = #ident::from_compact(buf.as_ref(), len); - assert_eq!(obj, same_obj); - } - - #[test] - pub fn #test() { - #fuzz(#ident::default()) - } - - impl Compact for #ident { - fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]> { + #impl_compact { + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]> { let mut flags = #flags::default(); let mut total_length = 0; #(#to_compact)* @@ -42,8 +79,7 @@ pub fn generate_from_to(ident: &Ident, fields: &FieldList, is_zstd: bool) -> Tok } fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { - let (flags, mut buf) = #flags::from(buf); - #from_compact + #fn_from_compact } } } diff --git a/crates/storage/codecs/derive/src/compact/mod.rs b/crates/storage/codecs/derive/src/compact/mod.rs index c2f0b1e56..31f97be8d 100644 --- a/crates/storage/codecs/derive/src/compact/mod.rs +++ b/crates/storage/codecs/derive/src/compact/mod.rs @@ -1,7 +1,7 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, TokenStream as TokenStream2}; use quote::{format_ident, quote}; -use syn::{parse_macro_input, Data, DeriveInput}; +use syn::{parse_macro_input, Data, DeriveInput, Generics}; mod generator; use generator::*; @@ -43,13 +43,20 @@ pub enum FieldTypes { pub fn derive(input: TokenStream, is_zstd: bool) -> TokenStream { let mut output = quote! {}; - let DeriveInput { ident, data, .. } = parse_macro_input!(input); + let DeriveInput { ident, data, generics, .. } = parse_macro_input!(input); + + let has_lifetime = has_lifetime(&generics); + let fields = get_fields(&data); - output.extend(generate_flag_struct(&ident, &fields, is_zstd)); - output.extend(generate_from_to(&ident, &fields, is_zstd)); + output.extend(generate_flag_struct(&ident, has_lifetime, &fields, is_zstd)); + output.extend(generate_from_to(&ident, has_lifetime, &fields, is_zstd)); output.into() } +pub fn has_lifetime(generics: &Generics) -> bool { + generics.lifetimes().next().is_some() +} + /// Given a list of fields on a struct, extract their fields and types. pub fn get_fields(data: &Data) -> FieldList { let mut fields = vec![]; @@ -60,7 +67,7 @@ pub fn get_fields(data: &Data) -> FieldList { for field in &data_fields.named { load_field(field, &mut fields, false); } - assert_eq!(fields.len(), data_fields.named.len()); + assert_eq!(fields.len(), data_fields.named.len(), "get_fields"); } syn::Fields::Unnamed(ref data_fields) => { assert_eq!( @@ -99,37 +106,54 @@ pub fn get_fields(data: &Data) -> FieldList { } fn load_field(field: &syn::Field, fields: &mut FieldList, is_enum: bool) { - if let syn::Type::Path(ref path) = field.ty { - let segments = &path.path.segments; - if !segments.is_empty() { - let mut ftype = String::new(); + match field.ty { + syn::Type::Reference(ref reference) => match &*reference.elem { + syn::Type::Path(path) => { + load_field_from_segments(&path.path.segments, is_enum, fields, field) + } + _ => unimplemented!("{:?}", &field.ident), + }, + syn::Type::Path(ref path) => { + load_field_from_segments(&path.path.segments, is_enum, fields, field) + } + _ => unimplemented!("{:?}", &field.ident), + } +} - let mut use_alt_impl: UseAlternative = false; +fn load_field_from_segments( + segments: &syn::punctuated::Punctuated, + is_enum: bool, + fields: &mut Vec, + field: &syn::Field, +) { + if !segments.is_empty() { + let mut ftype = String::new(); - for (index, segment) in segments.iter().enumerate() { - ftype.push_str(&segment.ident.to_string()); - if index < segments.len() - 1 { - ftype.push_str("::"); - } + let mut use_alt_impl: UseAlternative = false; - use_alt_impl = should_use_alt_impl(&ftype, segment); + for (index, segment) in segments.iter().enumerate() { + ftype.push_str(&segment.ident.to_string()); + if index < segments.len() - 1 { + ftype.push_str("::"); } - if is_enum { - fields.push(FieldTypes::EnumUnnamedField((ftype.to_string(), use_alt_impl))); - } else { - let should_compact = is_flag_type(&ftype) || - field.attrs.iter().any(|attr| { - attr.path().segments.iter().any(|path| path.ident == "maybe_zero") - }); + use_alt_impl = should_use_alt_impl(&ftype, segment); + } - fields.push(FieldTypes::StructField(( - field.ident.as_ref().map(|i| i.to_string()).unwrap_or_default(), - ftype, - should_compact, - use_alt_impl, - ))); - } + if is_enum { + fields.push(FieldTypes::EnumUnnamedField((ftype.to_string(), use_alt_impl))); + } else { + let should_compact = is_flag_type(&ftype) || + field.attrs.iter().any(|attr| { + attr.path().segments.iter().any(|path| path.ident == "maybe_zero") + }); + + fields.push(FieldTypes::StructField(( + field.ident.as_ref().map(|i| i.to_string()).unwrap_or_default(), + ftype, + should_compact, + use_alt_impl, + ))); } } } @@ -211,8 +235,8 @@ mod tests { let mut output = quote! {}; let DeriveInput { ident, data, .. } = parse2(f_struct).unwrap(); let fields = get_fields(&data); - output.extend(generate_flag_struct(&ident, &fields, false)); - output.extend(generate_from_to(&ident, &fields, false)); + output.extend(generate_flag_struct(&ident, false, &fields, false)); + output.extend(generate_from_to(&ident, false, &fields, false)); // Expected output in a TokenStream format. Commas matter! let should_output = quote! { @@ -267,7 +291,7 @@ mod tests { fuzz_test_test_struct(TestStruct::default()) } impl Compact for TestStruct { - fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]> { + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]> { let mut flags = TestStructFlags::default(); let mut total_length = 0; let mut buffer = bytes::BytesMut::new(); diff --git a/crates/storage/codecs/derive/src/lib.rs b/crates/storage/codecs/derive/src/lib.rs index b4d4e12c8..71af6619c 100644 --- a/crates/storage/codecs/derive/src/lib.rs +++ b/crates/storage/codecs/derive/src/lib.rs @@ -42,7 +42,8 @@ pub fn reth_codec(args: TokenStream, input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); let with_zstd = args.clone().into_iter().any(|tk| tk.to_string() == "zstd"); - + let without_arbitrary = args.clone().into_iter().any(|tk| tk.to_string() == "no_arbitrary"); + let compact = if with_zstd { quote! { #[derive(CompactZstd)] @@ -57,10 +58,8 @@ pub fn reth_codec(args: TokenStream, input: TokenStream) -> TokenStream { .into() }; - if let Some(first_arg) = args.clone().into_iter().next() { - if first_arg.to_string() == "no_arbitrary" { - return compact - } + if without_arbitrary { + return compact } let mut args = args.into_iter().collect::>(); diff --git a/crates/storage/codecs/src/alloy/access_list.rs b/crates/storage/codecs/src/alloy/access_list.rs index 6b18d8ad7..ecc29ea49 100644 --- a/crates/storage/codecs/src/alloy/access_list.rs +++ b/crates/storage/codecs/src/alloy/access_list.rs @@ -4,7 +4,7 @@ use alloy_primitives::Address; /// Implement `Compact` for `AccessListItem` and `AccessList`. impl Compact for AccessListItem { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -26,7 +26,7 @@ impl Compact for AccessListItem { } impl Compact for AccessList { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -56,7 +56,7 @@ mod tests { #[test] fn test_roundtrip_compact_access_list_item(access_list_item in arb::()) { let mut compacted_access_list_item = Vec::::new(); - let len = access_list_item.clone().to_compact(&mut compacted_access_list_item); + let len = access_list_item.to_compact(&mut compacted_access_list_item); let (decoded_access_list_item, _) = AccessListItem::from_compact(&compacted_access_list_item, len); assert_eq!(access_list_item, decoded_access_list_item); @@ -67,7 +67,7 @@ mod tests { #[test] fn test_roundtrip_compact_access_list(access_list in arb::()) { let mut compacted_access_list = Vec::::new(); - let len = access_list.clone().to_compact(&mut compacted_access_list); + let len = access_list.to_compact(&mut compacted_access_list); let (decoded_access_list, _) = AccessList::from_compact(&compacted_access_list, len); assert_eq!(access_list, decoded_access_list); diff --git a/crates/storage/codecs/src/alloy/authorization_list.rs b/crates/storage/codecs/src/alloy/authorization_list.rs index b9ba69dbc..ca779cb31 100644 --- a/crates/storage/codecs/src/alloy/authorization_list.rs +++ b/crates/storage/codecs/src/alloy/authorization_list.rs @@ -1,3 +1,5 @@ +use core::ops::Deref; + use crate::Compact; use alloy_eips::eip7702::{Authorization as AlloyAuthorization, SignedAuthorization}; use alloy_primitives::{Address, ChainId, U256}; @@ -17,7 +19,7 @@ struct Authorization { } impl Compact for AlloyAuthorization { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -38,11 +40,11 @@ impl Compact for AlloyAuthorization { } impl Compact for SignedAuthorization { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { - let (auth, signature) = self.into_parts(); + let signature = self.signature(); let (v, r, s) = (signature.v(), signature.r(), signature.s()); buf.put_u8(v.y_parity_byte()); buf.put_slice(r.as_le_slice()); @@ -50,7 +52,7 @@ impl Compact for SignedAuthorization { // to_compact doesn't write the len to buffer. // By placing it as last, we don't need to store it either. - 1 + 32 + 32 + auth.to_compact(buf) + 1 + 32 + 32 + self.deref().to_compact(buf) } fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { @@ -89,7 +91,7 @@ mod tests { .unwrap(), ); let mut compacted_authorization = Vec::::new(); - let len = authorization.clone().to_compact(&mut compacted_authorization); + let len = authorization.to_compact(&mut compacted_authorization); let (decoded_authorization, _) = SignedAuthorization::from_compact(&compacted_authorization, len); assert_eq!(authorization, decoded_authorization); diff --git a/crates/storage/codecs/src/alloy/genesis_account.rs b/crates/storage/codecs/src/alloy/genesis_account.rs index 3f6c83b44..26456be5d 100644 --- a/crates/storage/codecs/src/alloy/genesis_account.rs +++ b/crates/storage/codecs/src/alloy/genesis_account.rs @@ -7,6 +7,21 @@ use serde::{Deserialize, Serialize}; /// GenesisAccount acts as bridge which simplifies Compact implementation for AlloyGenesisAccount. /// /// Notice: Make sure this struct is 1:1 with `alloy_genesis::GenesisAccount` +#[reth_codec(no_arbitrary)] +#[derive(Debug, Clone, PartialEq, Eq)] +struct GenesisAccountRef<'a> { + /// The nonce of the account at genesis. + nonce: Option, + /// The balance of the account at genesis. + balance: &'a U256, + /// The account's bytecode at genesis. + code: Option<&'a Bytes>, + /// The account's storage at genesis. + storage: Option, + /// The account's private key. Should only be used for testing. + private_key: Option<&'a B256>, +} + #[reth_codec] #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] struct GenesisAccount { @@ -36,18 +51,21 @@ struct StorageEntry { } impl Compact for AlloyGenesisAccount { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { - let account = GenesisAccount { + let account = GenesisAccountRef { nonce: self.nonce, - balance: self.balance, - code: self.code, - storage: self.storage.map(|s| StorageEntries { - entries: s.into_iter().map(|(key, value)| StorageEntry { key, value }).collect(), + balance: &self.balance, + code: self.code.as_ref(), + storage: self.storage.as_ref().map(|s| StorageEntries { + entries: s + .iter() + .map(|(key, value)| StorageEntry { key: *key, value: *value }) + .collect(), }), - private_key: self.private_key, + private_key: self.private_key.as_ref(), }; account.to_compact(buf) } diff --git a/crates/storage/codecs/src/alloy/log.rs b/crates/storage/codecs/src/alloy/log.rs index eadcb894f..3303bfbc7 100644 --- a/crates/storage/codecs/src/alloy/log.rs +++ b/crates/storage/codecs/src/alloy/log.rs @@ -6,14 +6,14 @@ use bytes::BufMut; /// Implement `Compact` for `LogData` and `Log`. impl Compact for LogData { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: BufMut + AsMut<[u8]>, { let mut buffer = Vec::new(); - let (topics, data) = self.split(); - topics.specialized_to_compact(&mut buffer); - data.to_compact(&mut buffer); + + self.topics().specialized_to_compact(&mut buffer); + self.data.to_compact(&mut buffer); buf.put(&buffer[..]); buffer.len() } @@ -28,7 +28,7 @@ impl Compact for LogData { } impl Compact for Log { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: BufMut + AsMut<[u8]>, { @@ -60,7 +60,7 @@ mod tests { #[test] fn roundtrip(log: Log) { let mut buf = Vec::::new(); - let len = log.clone().to_compact(&mut buf); + let len = log.to_compact(&mut buf); let (decoded, _) = Log::from_compact(&buf, len); assert_eq!(log, decoded); } diff --git a/crates/storage/codecs/src/alloy/request.rs b/crates/storage/codecs/src/alloy/request.rs index 388f2a2b5..2447160be 100644 --- a/crates/storage/codecs/src/alloy/request.rs +++ b/crates/storage/codecs/src/alloy/request.rs @@ -7,7 +7,7 @@ use alloy_primitives::Bytes; use bytes::BufMut; impl Compact for Request { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: BufMut + AsMut<[u8]>, { diff --git a/crates/storage/codecs/src/alloy/trie.rs b/crates/storage/codecs/src/alloy/trie.rs index 24b311267..0aa8940f6 100644 --- a/crates/storage/codecs/src/alloy/trie.rs +++ b/crates/storage/codecs/src/alloy/trie.rs @@ -6,7 +6,7 @@ use alloy_trie::{hash_builder::HashBuilderValue, BranchNodeCompact, TrieMask}; use bytes::{Buf, BufMut}; impl Compact for HashBuilderValue { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: BufMut + AsMut<[u8]>, { @@ -42,7 +42,7 @@ impl Compact for HashBuilderValue { } impl Compact for BranchNodeCompact { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -99,7 +99,7 @@ impl Compact for BranchNodeCompact { } impl Compact for TrieMask { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -132,7 +132,7 @@ mod tests { ); let mut out = Vec::new(); - let compact_len = n.clone().to_compact(&mut out); + let compact_len = n.to_compact(&mut out); assert_eq!(BranchNodeCompact::from_compact(&out, compact_len).0, n); } } diff --git a/crates/storage/codecs/src/alloy/txkind.rs b/crates/storage/codecs/src/alloy/txkind.rs index b2d55400f..b4a2b1750 100644 --- a/crates/storage/codecs/src/alloy/txkind.rs +++ b/crates/storage/codecs/src/alloy/txkind.rs @@ -4,7 +4,7 @@ use crate::Compact; use alloy_primitives::{Address, TxKind}; impl Compact for TxKind { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { diff --git a/crates/storage/codecs/src/alloy/withdrawal.rs b/crates/storage/codecs/src/alloy/withdrawal.rs index 9d2a21d3f..58156d880 100644 --- a/crates/storage/codecs/src/alloy/withdrawal.rs +++ b/crates/storage/codecs/src/alloy/withdrawal.rs @@ -21,7 +21,7 @@ struct Withdrawal { } impl Compact for AlloyWithdrawal { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { diff --git a/crates/storage/codecs/src/lib.rs b/crates/storage/codecs/src/lib.rs index 56c33509f..0cff06ac8 100644 --- a/crates/storage/codecs/src/lib.rs +++ b/crates/storage/codecs/src/lib.rs @@ -20,7 +20,7 @@ pub use reth_codecs_derive::*; use alloy_primitives::{Address, Bloom, Bytes, FixedBytes, U256}; -use bytes::Buf; +use bytes::{Buf, BufMut}; #[cfg(not(feature = "std"))] extern crate alloc; @@ -49,7 +49,7 @@ mod alloy; /// size array like `Vec`. pub trait Compact: Sized { /// Takes a buffer which can be written to. *Ideally*, it returns the length written to. - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>; @@ -63,7 +63,7 @@ pub trait Compact: Sized { /// "Optional": If there's no good reason to use it, don't. #[inline] - fn specialized_to_compact(self, buf: &mut B) -> usize + fn specialized_to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -77,12 +77,25 @@ pub trait Compact: Sized { } } +impl Compact for &T { + fn to_compact(&self, buf: &mut B) -> usize + where + B: BufMut + AsMut<[u8]>, + { + (*self).to_compact(buf) + } + + fn from_compact(_: &[u8], _: usize) -> (Self, &[u8]) { + unimplemented!() + } +} + /// To be used with `Option` to place or replace one bit on the bitflag struct. pub type CompactPlaceholder = (); impl Compact for CompactPlaceholder { #[inline] - fn to_compact(self, _: &mut B) -> usize + fn to_compact(&self, _: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -100,7 +113,7 @@ macro_rules! impl_uint_compact { $( impl Compact for $name { #[inline] - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]> { let leading = self.leading_zeros() as usize / 8; @@ -132,7 +145,7 @@ where { /// Returns 0 since we won't include it in the `StructFlags`. #[inline] - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -172,7 +185,7 @@ where /// To be used by fixed sized types like `Vec`. #[inline] - fn specialized_to_compact(self, buf: &mut B) -> usize + fn specialized_to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -199,13 +212,64 @@ where } } +impl Compact for &[T] +where + T: Compact, +{ + /// Returns 0 since we won't include it in the `StructFlags`. + #[inline] + fn to_compact(&self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + encode_varuint(self.len(), buf); + + let mut tmp: Vec = Vec::with_capacity(64); + + for element in *self { + tmp.clear(); + + // We don't know the length until we compact it + let length = element.to_compact(&mut tmp); + encode_varuint(length, buf); + + buf.put_slice(&tmp); + } + + 0 + } + + #[inline] + fn from_compact(_: &[u8], _: usize) -> (Self, &[u8]) { + unimplemented!() + } + + /// To be used by fixed sized types like `&[B256]`. + #[inline] + fn specialized_to_compact(&self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + encode_varuint(self.len(), buf); + for element in *self { + element.to_compact(buf); + } + 0 + } + + #[inline] + fn specialized_from_compact(_: &[u8], _: usize) -> (Self, &[u8]) { + unimplemented!() + } +} + impl Compact for Option where T: Compact, { /// Returns 0 for `None` and 1 for `Some(_)`. #[inline] - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -238,7 +302,7 @@ where /// To be used by fixed sized types like `Option`. #[inline] - fn specialized_to_compact(self, buf: &mut B) -> usize + fn specialized_to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -264,7 +328,7 @@ where impl Compact for U256 { #[inline] - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -289,12 +353,12 @@ impl Compact for U256 { impl Compact for Bytes { #[inline] - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { let len = self.len(); - buf.put(self.0); + buf.put_slice(&self.0); len } @@ -306,11 +370,11 @@ impl Compact for Bytes { impl Compact for [u8; N] { #[inline] - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { - buf.put_slice(&self); + buf.put_slice(&self[..]); N } @@ -333,7 +397,7 @@ macro_rules! impl_compact_for_wrapped_bytes { $( impl Compact for $name { #[inline] - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]> { @@ -353,7 +417,7 @@ impl_compact_for_wrapped_bytes!(Address, Bloom); impl Compact for FixedBytes { #[inline] - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -370,11 +434,11 @@ impl Compact for FixedBytes { impl Compact for bool { /// `bool` vars go directly to the `StructFlags` and are not written to the buffer. #[inline] - fn to_compact(self, _: &mut B) -> usize + fn to_compact(&self, _: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { - self as usize + *self as usize } /// `bool` expects the real value to come in `len`, and does not advance the cursor. @@ -426,7 +490,7 @@ mod tests { let arr = [1, 2, 3, 4, 5]; let list = Bytes::copy_from_slice(&arr); let mut buf = vec![]; - assert_eq!(list.clone().to_compact(&mut buf), list.len()); + assert_eq!(list.to_compact(&mut buf), list.len()); // Add some noise data. buf.push(1); @@ -506,7 +570,7 @@ mod tests { let mut buf = vec![]; // Vec doesn't return a total length - assert_eq!(list.clone().to_compact(&mut buf), 0); + assert_eq!(list.to_compact(&mut buf), 0); // Add some noise data in the end that should be returned by `from_compact`. buf.extend([1u8, 2]); @@ -561,6 +625,33 @@ mod tests { }); } + #[test] + fn compact_slice() { + let vec_list = vec![B256::ZERO, B256::random(), B256::random(), B256::ZERO]; + + // to_compact + { + let mut vec_buf = vec![]; + assert_eq!(vec_list.to_compact(&mut vec_buf), 0); + + let mut slice_buf = vec![]; + assert_eq!(vec_list.as_slice().to_compact(&mut slice_buf), 0); + + assert_eq!(vec_buf, slice_buf); + } + + // specialized_to_compact + { + let mut vec_buf = vec![]; + assert_eq!(vec_list.specialized_to_compact(&mut vec_buf), 0); + + let mut slice_buf = vec![]; + assert_eq!(vec_list.as_slice().specialized_to_compact(&mut slice_buf), 0); + + assert_eq!(vec_buf, slice_buf); + } + } + #[reth_codec] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] struct TestStruct { @@ -627,15 +718,15 @@ mod tests { #[test_fuzz::test_fuzz] fn compact_test_enum_all_variants(var0: TestEnum, var1: TestEnum, var2: TestEnum) { let mut buf = vec![]; - var0.clone().to_compact(&mut buf); + var0.to_compact(&mut buf); assert_eq!(TestEnum::from_compact(&buf, buf.len()).0, var0); let mut buf = vec![]; - var1.clone().to_compact(&mut buf); + var1.to_compact(&mut buf); assert_eq!(TestEnum::from_compact(&buf, buf.len()).0, var1); let mut buf = vec![]; - var2.clone().to_compact(&mut buf); + var2.to_compact(&mut buf); assert_eq!(TestEnum::from_compact(&buf, buf.len()).0, var2); } diff --git a/crates/storage/db-api/src/models/accounts.rs b/crates/storage/db-api/src/models/accounts.rs index e6479d8c0..0eee31ec6 100644 --- a/crates/storage/db-api/src/models/accounts.rs +++ b/crates/storage/db-api/src/models/accounts.rs @@ -27,7 +27,7 @@ pub struct AccountBeforeTx { // and compress second part of the value. If we have compression // over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey impl Compact for AccountBeforeTx { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { diff --git a/crates/storage/db-api/src/models/client_version.rs b/crates/storage/db-api/src/models/client_version.rs index d2025bdd9..d7c9939a9 100644 --- a/crates/storage/db-api/src/models/client_version.rs +++ b/crates/storage/db-api/src/models/client_version.rs @@ -23,14 +23,13 @@ impl ClientVersion { } impl Compact for ClientVersion { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { - let Self { version, git_sha, build_timestamp } = self; - version.into_bytes().to_compact(buf); - git_sha.into_bytes().to_compact(buf); - build_timestamp.into_bytes().to_compact(buf) + self.version.as_bytes().to_compact(buf); + self.git_sha.as_bytes().to_compact(buf); + self.build_timestamp.as_bytes().to_compact(buf) } fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { diff --git a/crates/storage/db-api/src/models/mod.rs b/crates/storage/db-api/src/models/mod.rs index 1533df123..ff12a2fdb 100644 --- a/crates/storage/db-api/src/models/mod.rs +++ b/crates/storage/db-api/src/models/mod.rs @@ -185,7 +185,7 @@ macro_rules! impl_compression_for_compact { type Compressed = Vec; fn compress_to_buf>(self, buf: &mut B) { - let _ = Compact::to_compact(self, buf); + let _ = Compact::to_compact(&self, buf); } } @@ -235,7 +235,7 @@ macro_rules! impl_compression_fixed_compact { type Compressed = Vec; fn compress_to_buf>(self, buf: &mut B) { - let _ = Compact::to_compact(self, buf); + let _ = Compact::to_compact(&self, buf); } fn uncompressable_ref(&self) -> Option<&[u8]> { diff --git a/crates/trie/common/src/hash_builder/state.rs b/crates/trie/common/src/hash_builder/state.rs index 3a8f8e420..4b102800f 100644 --- a/crates/trie/common/src/hash_builder/state.rs +++ b/crates/trie/common/src/hash_builder/state.rs @@ -60,7 +60,7 @@ impl From for HashBuilderState { } impl Compact for HashBuilderState { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -163,7 +163,7 @@ mod tests { #[test] fn hash_builder_state_roundtrip(state in arb::()) { let mut buf = vec![]; - let len = state.clone().to_compact(&mut buf); + let len = state.to_compact(&mut buf); let (decoded, _) = HashBuilderState::from_compact(&buf, len); assert_eq!(state, decoded); } diff --git a/crates/trie/common/src/nibbles.rs b/crates/trie/common/src/nibbles.rs index 5a46887f8..991fb68f3 100644 --- a/crates/trie/common/src/nibbles.rs +++ b/crates/trie/common/src/nibbles.rs @@ -57,7 +57,7 @@ impl core::borrow::Borrow<[u8]> for StoredNibbles { } impl Compact for StoredNibbles { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -98,7 +98,7 @@ impl From for Nibbles { } impl Compact for StoredNibblesSubKey { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { diff --git a/crates/trie/common/src/storage.rs b/crates/trie/common/src/storage.rs index 0a6f5e836..b61abb116 100644 --- a/crates/trie/common/src/storage.rs +++ b/crates/trie/common/src/storage.rs @@ -15,7 +15,7 @@ pub struct StorageTrieEntry { // and compress second part of the value. If we have compression // over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey impl Compact for StorageTrieEntry { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { diff --git a/crates/trie/common/src/subnode.rs b/crates/trie/common/src/subnode.rs index db3b204e9..98ce76a32 100644 --- a/crates/trie/common/src/subnode.rs +++ b/crates/trie/common/src/subnode.rs @@ -14,7 +14,7 @@ pub struct StoredSubNode { } impl Compact for StoredSubNode { - fn to_compact(self, buf: &mut B) -> usize + fn to_compact(&self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -33,7 +33,7 @@ impl Compact for StoredSubNode { len += 1; } - if let Some(node) = self.node { + if let Some(node) = &self.node { buf.put_u8(1); len += 1; len += node.to_compact(buf); @@ -87,7 +87,7 @@ mod tests { }; let mut encoded = vec![]; - subnode.clone().to_compact(&mut encoded); + subnode.to_compact(&mut encoded); let (decoded, _) = StoredSubNode::from_compact(&encoded[..], 0); assert_eq!(subnode, decoded);