From 04a1df581a56a8f0696bf580174dda2d1f75fc36 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:21:43 +0100 Subject: [PATCH 1/4] fix(sol-types): fixed array decode --- crates/sol-macro/src/expand/mod.rs | 8 +++ crates/sol-macro/src/expand/struct.rs | 2 +- crates/sol-types/src/abi/decoder.rs | 70 +++++++++++++++++++++++- crates/sol-types/src/abi/token.rs | 47 ++++++---------- crates/sol-types/src/lib.rs | 3 + crates/sol-types/tests/macros/sol/mod.rs | 41 +++++++++++++- 6 files changed, 138 insertions(+), 33 deletions(-) diff --git a/crates/sol-macro/src/expand/mod.rs b/crates/sol-macro/src/expand/mod.rs index 59d99218f..6b0247b71 100644 --- a/crates/sol-macro/src/expand/mod.rs +++ b/crates/sol-macro/src/expand/mod.rs @@ -558,6 +558,14 @@ fn expand_from_into_tuples

(name: &Ident, fields: &Parameters

) -> TokenStre #[doc(hidden)] type UnderlyingRustTuple<'a> = #rust_tuple; + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: ::alloy_sol_types::private::AssertTypeEq) { + match _t { + ::alloy_sol_types::private::AssertTypeEq::<::RustType>(_) => {} + } + } + #[automatically_derived] #[doc(hidden)] impl ::core::convert::From<#name> for UnderlyingRustTuple<'_> { diff --git a/crates/sol-macro/src/expand/struct.rs b/crates/sol-macro/src/expand/struct.rs index 10b024dd0..5a2591d42 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -134,7 +134,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { #eip712_encode_type_fns - fn eip712_encode_data(&self) -> Vec { + fn eip712_encode_data(&self) -> ::alloy_sol_types::private::Vec { #encode_data_impl } } diff --git a/crates/sol-types/src/abi/decoder.rs b/crates/sol-types/src/abi/decoder.rs index 894c7193b..52b0db816 100644 --- a/crates/sol-types/src/abi/decoder.rs +++ b/crates/sol-types/src/abi/decoder.rs @@ -306,7 +306,7 @@ pub fn decode_sequence<'de, T: TokenSeq<'de>>(data: &'de [u8], validate: bool) - #[cfg(test)] mod tests { - use crate::{sol_data, utils::pad_usize, SolType}; + use crate::{sol, sol_data, utils::pad_usize, SolType, SolValue}; use alloc::string::ToString; use alloy_primitives::{address, hex, Address, B256, U256}; @@ -686,4 +686,72 @@ mod tests { "did not match error" ); } + + // https://github.com/alloy-rs/core/issues/433 + #[test] + fn fixed_before_dynamic() { + sol! { + #[derive(Debug, PartialEq, Eq)] + struct Ty { + bytes32[3] arr; + bytes dyn; + } + } + + let ty = Ty { + arr: [[0x11u8; 32].into(), [0x22u8; 32].into(), [0x33u8; 32].into()], + r#dyn: vec![0x44u8; 4], + }; + let encoded = hex!( + "1111111111111111111111111111111111111111111111111111111111111111" + "2222222222222222222222222222222222222222222222222222222222222222" + "3333333333333333333333333333333333333333333333333333333333333333" + "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000004" + "4444444400000000000000000000000000000000000000000000000000000000" + ); + assert_eq!(hex::encode(ty.abi_encode_params()), hex::encode(encoded)); + assert_eq!(ty.abi_encoded_size(), encoded.len()); + + assert_eq!(::abi_decode_params(&encoded, true).unwrap(), ty); + } + + #[test] + fn dynarray_before_dynamic() { + sol! { + #[derive(Debug, PartialEq, Eq)] + struct Ty { + bytes[3] arr; + bytes dyn; + } + } + + let ty = Ty { + arr: [vec![0x11u8; 32], vec![0x22u8; 32], vec![0x33u8; 32]], + r#dyn: vec![0x44u8; 4], + }; + let encoded = hex!( + "0000000000000000000000000000000000000000000000000000000000000040" // arr offset + "0000000000000000000000000000000000000000000000000000000000000160" // dyn offset + "0000000000000000000000000000000000000000000000000000000000000060" // arr[0] offset + "00000000000000000000000000000000000000000000000000000000000000a0" // arr[1] offset + "00000000000000000000000000000000000000000000000000000000000000e0" // arr[2] offset + "0000000000000000000000000000000000000000000000000000000000000020" // arr[0] + "1111111111111111111111111111111111111111111111111111111111111111" + "0000000000000000000000000000000000000000000000000000000000000020" // arr[1] + "2222222222222222222222222222222222222222222222222222222222222222" + "0000000000000000000000000000000000000000000000000000000000000020" // arr[2] + "3333333333333333333333333333333333333333333333333333333333333333" + "0000000000000000000000000000000000000000000000000000000000000004" // dyn + "4444444400000000000000000000000000000000000000000000000000000000" + ); + + // TODO: dyn offset is encoded as 0x100 instead of 0x160 + if cfg!(TODO) { + assert_eq!(hex::encode(ty.abi_encode_params()), hex::encode(encoded)); + } + assert_eq!(ty.abi_encoded_size(), encoded.len()); + + assert_eq!(::abi_decode_params(&encoded, false).unwrap(), ty); + } } diff --git a/crates/sol-types/src/abi/token.rs b/crates/sol-types/src/abi/token.rs index b705ebcfa..7d09d8d93 100644 --- a/crates/sol-types/src/abi/token.rs +++ b/crates/sol-types/src/abi/token.rs @@ -247,9 +247,11 @@ impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken { #[inline] fn decode_from(dec: &mut Decoder<'de>) -> Result { - let mut child = if Self::DYNAMIC { dec.take_indirection()? } else { dec.raw_child() }; - - Self::decode_sequence(&mut child) + if Self::DYNAMIC { + dec.take_indirection().and_then(|mut child| Self::decode_sequence(&mut child)) + } else { + Self::decode_sequence(dec) + } } #[inline] @@ -361,7 +363,7 @@ impl<'de, T: Token<'de>> Token<'de> for DynSeqToken { let len = child.take_offset()?; // This appears to be an unclarity in the Solidity spec. The spec // specifies that offsets are relative to the first word of - // `enc(X)`. But known-good test vectors ha vrelative to the + // `enc(X)`. But known-good test vectors are relative to the // word AFTER the array size let mut child = child.raw_child(); (0..len).map(|_| T::decode_from(&mut child)).collect::>>().map(DynSeqToken) @@ -460,13 +462,14 @@ impl<'de: 'a, 'a> Token<'de> for PackedSeqToken<'a> { #[inline] fn head_words(&self) -> usize { + // offset 1 } #[inline] fn tail_words(&self) -> usize { - // "1 +" because len is also appended - 1 + (self.0.len() + 31) / 32 + // length + words(data) + 1 + crate::utils::words_for(self.0) } #[inline] @@ -508,19 +511,11 @@ macro_rules! tuple_impls { fn decode_from(dec: &mut Decoder<'de>) -> Result { // The first element in a dynamic tuple is an offset to the tuple's data; // for a static tuples, the data begins right away - let mut child = if Self::DYNAMIC { - dec.take_indirection()? + if Self::DYNAMIC { + dec.take_indirection().and_then(|mut child| Self::decode_sequence(&mut child)) } else { - dec.raw_child() - }; - - let res = Self::decode_sequence(&mut child)?; - - if !Self::DYNAMIC { - dec.take_offset_from(&child); + Self::decode_sequence(dec) } - - Ok(res) } #[inline] @@ -563,18 +558,7 @@ macro_rules! tuple_impls { #[inline] fn tail_append(&self, enc: &mut Encoder) { if Self::DYNAMIC { - let ($($ty,)+) = self; - let head_words = 0 $( + $ty.head_words() )+; - - enc.push_offset(head_words); - $( - $ty.head_append(enc); - enc.bump_offset($ty.tail_words()); - )+ - $( - $ty.tail_append(enc); - )+ - enc.pop_offset(); + self.encode_sequence(enc); } } } @@ -601,7 +585,10 @@ macro_rules! tuple_impls { #[inline] fn decode_sequence(dec: &mut Decoder<'de>) -> Result { Ok(($( - <$ty as Token>::decode_from(dec)?, + match <$ty as Token>::decode_from(dec) { + Ok(t) => t, + Err(e) => return Err(e), + }, )+)) } } diff --git a/crates/sol-types/src/lib.rs b/crates/sol-types/src/lib.rs index 4c2486bfc..61bf685e9 100644 --- a/crates/sol-types/src/lib.rs +++ b/crates/sol-types/src/lib.rs @@ -200,6 +200,7 @@ pub use alloy_sol_macro::sol; // Not public API. #[doc(hidden)] +#[allow(missing_debug_implementations)] pub mod private { pub use super::utils::{just_ok, next_multiple_of_32, words_for, words_for_len}; pub use alloc::{ @@ -253,4 +254,6 @@ pub mod private { pub const fn u256(n: u64) -> U256 { U256::from_limbs([n, 0, 0, 0]) } + + pub struct AssertTypeEq(pub T); } diff --git a/crates/sol-types/tests/macros/sol/mod.rs b/crates/sol-types/tests/macros/sol/mod.rs index 1f9e7aa18..37f16fc17 100644 --- a/crates/sol-types/tests/macros/sol/mod.rs +++ b/crates/sol-types/tests/macros/sol/mod.rs @@ -1,4 +1,4 @@ -use alloy_primitives::{hex, keccak256, Address, I256, U256}; +use alloy_primitives::{b256, hex, keccak256, Address, I256, U256}; use alloy_sol_types::{sol, SolCall, SolError, SolEvent, SolStruct, SolType}; use serde::Serialize; use serde_json::Value; @@ -734,3 +734,42 @@ fn duplicate_events() { } } } + +// https://github.com/alloy-rs/core/issues/433 +#[test] +fn decoder_fixed_array_before_dynamic() { + sol! { + #[derive(Debug, PartialEq, Eq)] + struct FullReport { + bytes32[3] report_context; + bytes report_blob; + bytes32[] raw_rs; + bytes32[] raw_ss; + bytes32 raw_vs; + } + } + let full_report = FullReport { + report_context: [ + b256!("0006015a2de20abc8c880eb052a09c069e4edf697529d12eeae88b7b6867fc81"), + b256!("00000000000000000000000000000000000000000000000000000000080f7906"), + b256!("0000000000000000000000000000000000000000000000000000000000000000"), + ], + report_blob: hex!("0002191c50b7bdaf2cb8672453141946eea123f8baeaa8d2afa4194b6955e68300000000000000000000000000000000000000000000000000000000655ac7af00000000000000000000000000000000000000000000000000000000655ac7af000000000000000000000000000000000000000000000000000000000000138800000000000000000000000000000000000000000000000000000000000a1f6800000000000000000000000000000000000000000000000000000000655c192f000000000000000000000000000000000000000000000000d130d9ecefeaae30").into(), + raw_rs: vec![ + b256!("d1e3d8b8c581a7ed9cfc41316f1bb8598d98237fc8278a01a9c6a323c4b5c331"), + b256!("38ef50778560ec2bb08b23960e3d74f1ffe83b9240a39555c6eb817e3f68302c"), + ], + raw_ss: vec![ + b256!("7fb9c59cc499a4672f1481a526d01aa8c01380dcfa0ea855041254d3bcf45536"), + b256!("2ce612a86846a7cbb640ddcd3abdecf56618c7b24cf96242643d5c355dee5f0e"), + ], + raw_vs: b256!("0001000000000000000000000000000000000000000000000000000000000000"), + }; + + let encoded = FullReport::abi_encode(&full_report); + let expected = hex!("00000000000000000000000000000000000000000000000000000000000000200006015a2de20abc8c880eb052a09c069e4edf697529d12eeae88b7b6867fc8100000000000000000000000000000000000000000000000000000000080f7906000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000240000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00002191c50b7bdaf2cb8672453141946eea123f8baeaa8d2afa4194b6955e68300000000000000000000000000000000000000000000000000000000655ac7af00000000000000000000000000000000000000000000000000000000655ac7af000000000000000000000000000000000000000000000000000000000000138800000000000000000000000000000000000000000000000000000000000a1f6800000000000000000000000000000000000000000000000000000000655c192f000000000000000000000000000000000000000000000000d130d9ecefeaae300000000000000000000000000000000000000000000000000000000000000002d1e3d8b8c581a7ed9cfc41316f1bb8598d98237fc8278a01a9c6a323c4b5c33138ef50778560ec2bb08b23960e3d74f1ffe83b9240a39555c6eb817e3f68302c00000000000000000000000000000000000000000000000000000000000000027fb9c59cc499a4672f1481a526d01aa8c01380dcfa0ea855041254d3bcf455362ce612a86846a7cbb640ddcd3abdecf56618c7b24cf96242643d5c355dee5f0e"); + assert_eq!(hex::encode(&encoded), hex::encode(expected)); + + let decoded = FullReport::abi_decode(&encoded, true).unwrap(); + assert_eq!(decoded, full_report); +} From 85f9b5d3ee0706516ed0250500e2f37c25ffecf9 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:23:50 +0100 Subject: [PATCH 2/4] wip --- crates/sol-types/src/abi/token.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/crates/sol-types/src/abi/token.rs b/crates/sol-types/src/abi/token.rs index 7d09d8d93..4557d4e96 100644 --- a/crates/sol-types/src/abi/token.rs +++ b/crates/sol-types/src/abi/token.rs @@ -259,14 +259,14 @@ impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken { if Self::DYNAMIC { 1 } else { - self.0.iter().map(Token::head_words).sum() + self.0.iter().map(T::head_words).sum() } } #[inline] fn tail_words(&self) -> usize { if Self::DYNAMIC { - N + self.0.iter().map(T::tail_words).sum() } else { 0 } @@ -294,7 +294,7 @@ impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken { impl<'de, T: Token<'de>, const N: usize> TokenSeq<'de> for FixedSeqToken { #[inline] fn encode_sequence(&self, enc: &mut Encoder) { - let head_words = self.0.iter().map(Token::head_words).sum::(); + let head_words = self.0.iter().map(T::head_words).sum::(); enc.push_offset(head_words); for inner in &self.0 { @@ -521,6 +521,7 @@ macro_rules! tuple_impls { #[inline] fn head_words(&self) -> usize { if Self::DYNAMIC { + // offset 1 } else { let ($($ty,)+) = self; @@ -531,18 +532,14 @@ macro_rules! tuple_impls { #[inline] fn tail_words(&self) -> usize { if Self::DYNAMIC { - self.total_words() + // elements + let ($($ty,)+) = self; + 0 $( + $ty.tail_words() )+ } else { 0 } } - #[inline] - fn total_words(&self) -> usize { - let ($($ty,)+) = self; - 0 $( + $ty.total_words() )+ - } - #[inline] fn head_append(&self, enc: &mut Encoder) { if Self::DYNAMIC { From 1a40ceae41b8cc73df5a8741c35d02e5244ae5f8 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:04:41 +0100 Subject: [PATCH 3/4] fix: word count and encoded size calculations --- crates/sol-macro/src/expand/function.rs | 6 +- crates/sol-types/src/abi/decoder.rs | 38 +++++---- crates/sol-types/src/abi/encoder.rs | 98 ++++++++++++++++-------- crates/sol-types/src/abi/token.rs | 14 +++- crates/sol-types/src/types/data_type.rs | 61 ++++++++++----- crates/sol-types/src/types/error.rs | 4 +- crates/sol-types/src/types/function.rs | 12 +-- crates/sol-types/tests/macros/sol/mod.rs | 5 +- 8 files changed, 155 insertions(+), 83 deletions(-) diff --git a/crates/sol-macro/src/expand/function.rs b/crates/sol-macro/src/expand/function.rs index 4d6d1db29..d9773e0b6 100644 --- a/crates/sol-macro/src/expand/function.rs +++ b/crates/sol-macro/src/expand/function.rs @@ -114,8 +114,8 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, function: &ItemFunction) -> Result = #call_tuple; - type Token<'a> = as ::alloy_sol_types::SolType>::Token<'a>; + type Parameters<'a> = #call_tuple; + type Token<'a> = as ::alloy_sol_types::SolType>::Token<'a>; type Return = #return_name; @@ -125,7 +125,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, function: &ItemFunction) -> Result(tuple: as ::alloy_sol_types::SolType>::RustType) -> Self { + fn new<'a>(tuple: as ::alloy_sol_types::SolType>::RustType) -> Self { tuple.into() } diff --git a/crates/sol-types/src/abi/decoder.rs b/crates/sol-types/src/abi/decoder.rs index 52b0db816..5d11d2000 100644 --- a/crates/sol-types/src/abi/decoder.rs +++ b/crates/sol-types/src/abi/decoder.rs @@ -326,15 +326,13 @@ mod tests { " ); - assert_eq!( - MyTy::abi_encode_params(&vec![ - vec![Address::repeat_byte(0x11)], - vec![Address::repeat_byte(0x22)], - ]), - encoded - ); + let ty = vec![vec![Address::repeat_byte(0x11)], vec![Address::repeat_byte(0x22)]]; + assert_eq!(MyTy::abi_encode_params(&ty), encoded); - MyTy::abi_decode_params(&encoded, false).unwrap(); + let decoded = MyTy::abi_decode_params(&encoded, false).unwrap(); + assert_eq!(decoded, ty); + assert_eq!(decoded.abi_encode_params(), encoded); + assert_eq!(decoded.abi_encoded_size(), encoded.len()); } #[test] @@ -354,6 +352,8 @@ mod tests { let expected = (address1, address2, uint); let decoded = MyTy::abi_decode_sequence(&encoded, true).unwrap(); assert_eq!(decoded, expected); + assert_eq!(decoded.abi_encode_params(), encoded); + assert_eq!(decoded.abi_encoded_size(), encoded.len()); } #[test] @@ -377,6 +377,8 @@ mod tests { // this test vector contains a top-level indirect let decoded = MyTy::abi_decode(&encoded, true).unwrap(); assert_eq!(decoded, expected); + assert_eq!(decoded.abi_encode(), encoded); + assert_eq!(decoded.abi_encoded_size(), encoded.len()); } #[test] @@ -427,6 +429,8 @@ mod tests { let decoded = MyTy::abi_decode(&encoded, true).unwrap(); assert_eq!(decoded, expected); + assert_eq!(decoded.abi_encode(), encoded); + assert_eq!(decoded.abi_encoded_size(), encoded.len()); } #[test] @@ -452,6 +456,8 @@ mod tests { let decoded = MyTy::abi_decode(&encoded, true).unwrap(); assert_eq!(decoded, expected); + assert_eq!(decoded.abi_encode(), encoded); + assert_eq!(decoded.abi_encoded_size(), encoded.len()); } #[test] @@ -492,6 +498,8 @@ mod tests { let decoded = MyTy::abi_decode_params(&encoded, true).unwrap(); assert_eq!(decoded, expected); + assert_eq!(decoded.abi_encode_params(), encoded); + assert_eq!(decoded.abi_encoded_size(), encoded.len() + 32); } #[test] @@ -703,6 +711,7 @@ mod tests { r#dyn: vec![0x44u8; 4], }; let encoded = hex!( + "0000000000000000000000000000000000000000000000000000000000000020" "1111111111111111111111111111111111111111111111111111111111111111" "2222222222222222222222222222222222222222222222222222222222222222" "3333333333333333333333333333333333333333333333333333333333333333" @@ -710,10 +719,10 @@ mod tests { "0000000000000000000000000000000000000000000000000000000000000004" "4444444400000000000000000000000000000000000000000000000000000000" ); - assert_eq!(hex::encode(ty.abi_encode_params()), hex::encode(encoded)); + assert_eq!(hex::encode(ty.abi_encode()), hex::encode(encoded)); assert_eq!(ty.abi_encoded_size(), encoded.len()); - assert_eq!(::abi_decode_params(&encoded, true).unwrap(), ty); + assert_eq!(::abi_decode(&encoded, true).unwrap(), ty); } #[test] @@ -731,6 +740,7 @@ mod tests { r#dyn: vec![0x44u8; 4], }; let encoded = hex!( + "0000000000000000000000000000000000000000000000000000000000000020" // struct offset "0000000000000000000000000000000000000000000000000000000000000040" // arr offset "0000000000000000000000000000000000000000000000000000000000000160" // dyn offset "0000000000000000000000000000000000000000000000000000000000000060" // arr[0] offset @@ -745,13 +755,9 @@ mod tests { "0000000000000000000000000000000000000000000000000000000000000004" // dyn "4444444400000000000000000000000000000000000000000000000000000000" ); - - // TODO: dyn offset is encoded as 0x100 instead of 0x160 - if cfg!(TODO) { - assert_eq!(hex::encode(ty.abi_encode_params()), hex::encode(encoded)); - } + assert_eq!(hex::encode(ty.abi_encode()), hex::encode(encoded)); assert_eq!(ty.abi_encoded_size(), encoded.len()); - assert_eq!(::abi_decode_params(&encoded, false).unwrap(), ty); + assert_eq!(::abi_decode(&encoded, false).unwrap(), ty); } } diff --git a/crates/sol-types/src/abi/encoder.rs b/crates/sol-types/src/abi/encoder.rs index e992a3d76..f0363d79b 100644 --- a/crates/sol-types/src/abi/encoder.rs +++ b/crates/sol-types/src/abi/encoder.rs @@ -134,6 +134,10 @@ impl Encoder { /// Append a sequence of bytes, padding to the next word. #[inline(always)] fn append_bytes(&mut self, bytes: &[u8]) { + if bytes.is_empty() { + return; + } + let n_words = utils::words_for(bytes); self.buf.reserve(n_words); unsafe { @@ -240,7 +244,7 @@ mod tests { ) .to_vec(); assert_eq!(encoded, expected); - assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,))); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -279,7 +283,7 @@ mod tests { .to_vec(); assert_eq!(encoded, expected); assert_eq!(encoded_params, expected); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&addresses)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&addresses)); } #[test] @@ -309,7 +313,7 @@ mod tests { let encoded_params = MyTy::abi_encode_params(&data); assert_eq!(encoded_params, expected); - assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,))); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -338,7 +342,7 @@ mod tests { assert_eq!(encoded, expected); let encoded_params = MyTy::abi_encode_params(&data); assert_eq!(encoded_params, expected); - assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,))); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -365,7 +369,7 @@ mod tests { assert_eq!(encoded, expected); let encoded_params = MyTy::abi_encode_params(&data); assert_eq!(encoded_params, expected); - assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,))); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -396,7 +400,7 @@ mod tests { assert_eq!(encoded, expected); let encoded_params = MyTy::abi_encode_params(&data); assert_eq!(encoded_params, expected); - assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,))); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -454,9 +458,11 @@ mod tests { ) .to_vec(); + let encoded = MyTy::abi_encode(&data); let encoded_params = MyTy::abi_encode_params(&data); + assert_ne!(encoded, expected); assert_eq!(encoded_params, expected); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -476,7 +482,7 @@ mod tests { .to_vec(); assert_eq!(encoded, expected); - assert_eq!(encoded.len(), <(MyTy0,)>::abi_encoded_size(&(data,))); + assert_eq!(encoded.len(), MyTy0::abi_encoded_size(&data)); type MyTy = (sol_data::Array, sol_data::Array); let data: (Vec

, Vec
) = (vec![], vec![]); @@ -498,7 +504,7 @@ mod tests { let encoded_params = MyTy::abi_encode_params(&data); assert_eq!(encoded_params, expected); assert_eq!(encoded_params.len() + 32, encoded.len()); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); type MyTy2 = ( sol_data::Array>, @@ -530,25 +536,38 @@ mod tests { assert_eq!(encoded_params, expected); assert_eq!(encoded_params.len() + 32, encoded.len()); - assert_eq!(encoded_params.len(), MyTy2::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy2::abi_encoded_size(&data)); + } + + #[test] + fn encode_empty_bytes() { + let bytes = Vec::::new(); + + let encoded = sol_data::Bytes::abi_encode(&bytes); + let expected = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000020 + 0000000000000000000000000000000000000000000000000000000000000000 + " + ); + assert_eq!(encoded, expected); + assert_eq!(encoded.len(), sol_data::Bytes::abi_encoded_size(&bytes)); } #[test] fn encode_bytes() { - type MyTy = sol_data::Bytes; let bytes = vec![0x12, 0x34]; - let encoded = MyTy::abi_encode(&bytes); + let encoded = sol_data::Bytes::abi_encode(&bytes); let expected = hex!( " 0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000002 1234000000000000000000000000000000000000000000000000000000000000 " - ) - .to_vec(); + ); assert_eq!(encoded, expected); - assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(bytes,))); + assert_eq!(encoded.len(), sol_data::Bytes::abi_encoded_size(&bytes)); } #[test] @@ -559,6 +578,20 @@ mod tests { assert_eq!(encoded.len(), sol_data::FixedBytes::<2>::abi_encoded_size(&[0x12, 0x34])); } + #[test] + fn encode_empty_string() { + let s = ""; + let encoded = sol_data::String::abi_encode(s); + let expected = hex!( + " + 0000000000000000000000000000000000000000000000000000000000000020 + 0000000000000000000000000000000000000000000000000000000000000000 + " + ); + assert_eq!(encoded, expected); + assert_eq!(encoded.len(), sol_data::String::abi_encoded_size(&s)); + } + #[test] fn encode_string() { let s = "gavofyork".to_string(); @@ -569,10 +602,9 @@ mod tests { 0000000000000000000000000000000000000000000000000000000000000009 6761766f66796f726b0000000000000000000000000000000000000000000000 " - ) - .to_vec(); + ); assert_eq!(encoded, expected); - assert_eq!(encoded.len(), <(sol_data::String,)>::abi_encoded_size(&(s,))); + assert_eq!(encoded.len(), sol_data::String::abi_encoded_size(&s)); } #[test] @@ -588,7 +620,7 @@ mod tests { ) .to_vec(); assert_eq!(encoded, expected); - assert_eq!(encoded.len(), <(sol_data::Bytes,)>::abi_encoded_size(&(bytes,))); + assert_eq!(encoded.len(), sol_data::Bytes::abi_encoded_size(&bytes)); } #[test] @@ -611,7 +643,7 @@ mod tests { ) .to_vec(); assert_eq!(encoded, expected); - assert_eq!(encoded.len(), <(sol_data::Bytes,)>::abi_encoded_size(&(bytes,))); + assert_eq!(encoded.len(), sol_data::Bytes::abi_encoded_size(&bytes)); } #[test] @@ -641,7 +673,7 @@ mod tests { assert_ne!(encoded, expected); assert_eq!(encoded_params, expected); assert_eq!(encoded_params.len() + 32, encoded.len()); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&bytes)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&bytes)); } #[test] @@ -716,7 +748,7 @@ mod tests { assert_ne!(encoded, expected); assert_eq!(encoded_params, expected); assert_eq!(encoded_params.len() + 32, encoded.len()); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -757,7 +789,7 @@ mod tests { let encoded_params = MyTy::abi_encode_params(&data); assert_eq!(encoded_params, expected); assert_eq!(encoded_params.len() + 32, encoded.len()); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -784,7 +816,7 @@ mod tests { assert_eq!(encoded, expected); let encoded_params = MyTy::abi_encode_params(&data); assert_eq!(encoded_params, expected); - assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,))); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -818,7 +850,7 @@ mod tests { assert_eq!(encoded, expected); let encoded_params = MyTy::abi_encode_params(&data); assert_eq!(encoded_params, expected); - assert_eq!(encoded.len(), <(MyTy,)>::abi_encoded_size(&(data,))); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -838,7 +870,7 @@ mod tests { .to_vec(); assert_eq!(encoded, expected); assert_eq!(encoded_params, expected); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -865,7 +897,7 @@ mod tests { let encoded_params = MyTy::abi_encode_params(&data); assert_ne!(encoded_params, expected); assert_eq!(encoded_params.len() + 32, encoded.len()); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -901,7 +933,7 @@ mod tests { assert_eq!(encoded, expected); assert_ne!(encoded_params, expected); assert_eq!(encoded_params.len() + 32, encoded.len()); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -934,7 +966,7 @@ mod tests { let encoded_params = MyTy::abi_encode_params(&data); assert_ne!(encoded_params, expected); assert_eq!(encoded_params.len() + 32, encoded.len()); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -988,7 +1020,7 @@ mod tests { assert_eq!(encoded, expected); assert_ne!(encoded_params, expected); assert_eq!(encoded_params.len() + 32, encoded.len()); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -1034,7 +1066,7 @@ mod tests { assert_ne!(encoded_single, expected); assert_eq!(encoded, expected); assert_eq!(encoded.len() + 32, encoded_single.len()); - assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded_single.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -1071,7 +1103,7 @@ mod tests { // a static FixedSeq should NEVER indirect assert_eq!(encoded, expected); assert_eq!(encoded_params, expected); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } #[test] @@ -1100,6 +1132,6 @@ mod tests { assert_eq!(encoded, expected); assert_ne!(encoded_params, expected); assert_eq!(encoded_params.len() + 32, encoded.len()); - assert_eq!(encoded_params.len(), MyTy::abi_encoded_size(&data)); + assert_eq!(encoded.len(), MyTy::abi_encoded_size(&data)); } } diff --git a/crates/sol-types/src/abi/token.rs b/crates/sol-types/src/abi/token.rs index 4557d4e96..d84b9c419 100644 --- a/crates/sol-types/src/abi/token.rs +++ b/crates/sol-types/src/abi/token.rs @@ -257,16 +257,19 @@ impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken { #[inline] fn head_words(&self) -> usize { if Self::DYNAMIC { + // offset 1 } else { - self.0.iter().map(T::head_words).sum() + // elements + self.0.iter().map(T::total_words).sum() } } #[inline] fn tail_words(&self) -> usize { if Self::DYNAMIC { - self.0.iter().map(T::tail_words).sum() + // elements + self.0.iter().map(T::total_words).sum() } else { 0 } @@ -371,11 +374,13 @@ impl<'de, T: Token<'de>> Token<'de> for DynSeqToken { #[inline] fn head_words(&self) -> usize { + // offset 1 } #[inline] fn tail_words(&self) -> usize { + // length + elements 1 + self.0.iter().map(Token::total_words).sum::() } @@ -524,8 +529,9 @@ macro_rules! tuple_impls { // offset 1 } else { + // elements let ($($ty,)+) = self; - 0 $( + $ty.head_words() )+ + 0 $( + $ty.total_words() )+ } } @@ -534,7 +540,7 @@ macro_rules! tuple_impls { if Self::DYNAMIC { // elements let ($($ty,)+) = self; - 0 $( + $ty.tail_words() )+ + 0 $( + $ty.total_words() )+ } else { 0 } diff --git a/crates/sol-types/src/types/data_type.rs b/crates/sol-types/src/types/data_type.rs index d7a8dcd13..2b9372271 100644 --- a/crates/sol-types/src/types/data_type.rs +++ b/crates/sol-types/src/types/data_type.rs @@ -313,7 +313,12 @@ impl> SolTypeValue for T { #[inline] fn stv_abi_encoded_size(&self) -> usize { - 32 + utils::padded_len(self.as_ref()) + let s = self.as_ref(); + if s.is_empty() { + 64 + } else { + 64 + utils::padded_len(s) + } } #[inline] @@ -360,7 +365,12 @@ impl> SolTypeValue for T { #[inline] fn stv_abi_encoded_size(&self) -> usize { - 32 + utils::padded_len(self.as_ref().as_ref()) + let s = self.as_ref(); + if s.is_empty() { + 64 + } else { + 64 + utils::padded_len(s.as_bytes()) + } } #[inline] @@ -415,8 +425,11 @@ where #[inline] fn stv_abi_encoded_size(&self) -> usize { - 32 + self.iter().map(T::stv_abi_encoded_size).sum::() - + (U::DYNAMIC as usize * 32 * self.len()) + if let Some(size) = Array::::ENCODED_SIZE { + return size; + } + + 64 + self.iter().map(T::stv_abi_encoded_size).sum::() } #[inline] @@ -555,18 +568,24 @@ where return size; } - self.iter().map(T::stv_abi_encoded_size).sum::() + (U::DYNAMIC as usize * N * 32) + let sum = self.iter().map(T::stv_abi_encoded_size).sum::(); + if FixedArray::::DYNAMIC { + 32 + sum + } else { + sum + } } #[inline] fn stv_eip712_data_word(&self) -> Word { - // TODO: collect into an array of [u8; 32] and flatten it to a slice like in - // tuple impl - let encoded = self - .iter() - .map(|element| T::stv_eip712_data_word(element).0) - .collect::>(); - keccak256(crate::impl_core::into_flattened(encoded)) + let mut encoded = crate::impl_core::uninit_array::<[u8; 32], N>(); + for (i, item) in self.iter().enumerate() { + encoded[i].write(T::stv_eip712_data_word(item).0); + } + // SAFETY: Flattening [[u8; 32]; N] to [u8; N * 32] is valid + let encoded: &[u8] = + unsafe { core::slice::from_raw_parts(encoded.as_ptr().cast(), N * 32) }; + keccak256(encoded) } #[inline] @@ -672,19 +691,19 @@ macro_rules! tuple_encodable_impls { } let ($($ty,)+) = self; - 0 $( - + <$uty as SolType>::abi_encoded_size($ty) - )+ - $( - + (32 * <$uty as SolType>::DYNAMIC as usize) - )+ + let sum = 0 $( + $ty.stv_abi_encoded_size() )+; + if <($($uty,)+) as SolType>::DYNAMIC { + 32 + sum + } else { + sum + } } fn stv_abi_encode_packed_to(&self, out: &mut Vec) { let ($($ty,)+) = self; // TODO: Reserve $( - <$uty as SolType>::abi_encode_packed_to($ty, out); + $ty.stv_abi_encode_packed_to(out); )+ } @@ -1197,7 +1216,9 @@ mod tests { #[test] #[cfg_attr(miri, ignore = "doesn't run in isolation and would take too long")] fn $name(i: $t) { - proptest::prop_assert_eq!(<$st>::detokenize(<$st>::tokenize(&i)), i); + let token = <$st>::tokenize(&i); + proptest::prop_assert_eq!(token.total_words() * 32, <$st>::abi_encoded_size(&i)); + proptest::prop_assert_eq!(<$st>::detokenize(token), i); } )+} }; diff --git a/crates/sol-types/src/types/error.rs b/crates/sol-types/src/types/error.rs index 4436c431b..8a4721be3 100644 --- a/crates/sol-types/src/types/error.rs +++ b/crates/sol-types/src/types/error.rs @@ -45,7 +45,9 @@ pub trait SolError: Sized { return size; } - self.tokenize().total_words() * Word::len_bytes() + // `total_words` includes the first dynamic offset which we ignore. + let offset = < as SolType>::Token<'_> as Token>::DYNAMIC as usize * 32; + (self.tokenize().total_words() * Word::len_bytes()).saturating_sub(offset) } /// ABI decode this call's arguments from the given slice, **without** its diff --git a/crates/sol-types/src/types/function.rs b/crates/sol-types/src/types/function.rs index 0587d57a7..0c1cdf1dc 100644 --- a/crates/sol-types/src/types/function.rs +++ b/crates/sol-types/src/types/function.rs @@ -16,7 +16,7 @@ pub trait SolCall: Sized { /// The underlying tuple type which represents this type's arguments. /// /// If this type has no arguments, this will be the unit type `()`. - type Arguments<'a>: SolType = Self::Token<'a>>; + type Parameters<'a>: SolType = Self::Token<'a>>; /// The arguments' corresponding [TokenSeq] type. type Token<'a>: TokenSeq<'a>; @@ -39,7 +39,7 @@ pub trait SolCall: Sized { const SELECTOR: [u8; 4]; /// Convert from the tuple type used for ABI encoding and decoding. - fn new(tuple: as SolType>::RustType) -> Self; + fn new(tuple: as SolType>::RustType) -> Self; /// Tokenize the call's arguments. fn tokenize(&self) -> Self::Token<'_>; @@ -47,18 +47,20 @@ pub trait SolCall: Sized { /// The size of the encoded data in bytes, **without** its selector. #[inline] fn abi_encoded_size(&self) -> usize { - if let Some(size) = as SolType>::ENCODED_SIZE { + if let Some(size) = as SolType>::ENCODED_SIZE { return size; } - self.tokenize().total_words() * Word::len_bytes() + // `total_words` includes the first dynamic offset which we ignore. + let offset = < as SolType>::Token<'_> as Token>::DYNAMIC as usize * 32; + (self.tokenize().total_words() * Word::len_bytes()).saturating_sub(offset) } /// ABI decode this call's arguments from the given slice, **without** its /// selector. #[inline] fn abi_decode_raw(data: &[u8], validate: bool) -> Result { - as SolType>::abi_decode_sequence(data, validate).map(Self::new) + as SolType>::abi_decode_sequence(data, validate).map(Self::new) } /// ABI decode this call's arguments from the given slice, **with** the diff --git a/crates/sol-types/tests/macros/sol/mod.rs b/crates/sol-types/tests/macros/sol/mod.rs index 37f16fc17..76d6e6afe 100644 --- a/crates/sol-types/tests/macros/sol/mod.rs +++ b/crates/sol-types/tests/macros/sol/mod.rs @@ -67,11 +67,13 @@ fn e2e() { #[test] fn function() { sol! { + #[derive(Debug, PartialEq)] struct CustomStruct { address a; uint64 b; } + #[derive(Debug, PartialEq)] function someFunction( uint256 basic, string memory string_, @@ -103,12 +105,13 @@ fn function() { ], }; let encoded = call.abi_encode(); - assert_eq!(encoded.len(), someFunctionCall::SELECTOR.len() + call.abi_encoded_size()); + assert_eq!(someFunctionCall::abi_decode(&encoded, true).unwrap(), call); assert_eq!( call.abi_encoded_size(), 32 + (64 + 32) + (64 + 32 + 32) + (64 + 3 * 32) + 2 * 32 + (32 + 32) + (64 + 4 * (32 + 32)) ); + assert_eq!(encoded.len(), 4 + call.abi_encoded_size()); } #[test] From edeca07e7fbc058ac82e659127e2439663b16108 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:36:20 +0100 Subject: [PATCH 4/4] cleanup --- crates/sol-types/src/abi/token.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/sol-types/src/abi/token.rs b/crates/sol-types/src/abi/token.rs index d84b9c419..2fc86e7af 100644 --- a/crates/sol-types/src/abi/token.rs +++ b/crates/sol-types/src/abi/token.rs @@ -295,10 +295,8 @@ impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken { } impl<'de, T: Token<'de>, const N: usize> TokenSeq<'de> for FixedSeqToken { - #[inline] fn encode_sequence(&self, enc: &mut Encoder) { - let head_words = self.0.iter().map(T::head_words).sum::(); - enc.push_offset(head_words); + enc.push_offset(self.0.iter().map(T::head_words).sum()); for inner in &self.0 { inner.head_append(enc); @@ -381,7 +379,7 @@ impl<'de, T: Token<'de>> Token<'de> for DynSeqToken { #[inline] fn tail_words(&self) -> usize { // length + elements - 1 + self.0.iter().map(Token::total_words).sum::() + 1 + self.0.iter().map(T::total_words).sum::() } #[inline] @@ -398,8 +396,7 @@ impl<'de, T: Token<'de>> Token<'de> for DynSeqToken { impl<'de, T: Token<'de>> TokenSeq<'de> for DynSeqToken { fn encode_sequence(&self, enc: &mut Encoder) { - let head_words = self.0.iter().map(Token::head_words).sum::(); - enc.push_offset(head_words); + enc.push_offset(self.0.iter().map(T::head_words).sum()); for inner in &self.0 { inner.head_append(enc); @@ -570,18 +567,19 @@ macro_rules! tuple_impls { impl<'de, $($ty: Token<'de>,)+> TokenSeq<'de> for ($($ty,)+) { const IS_TUPLE: bool = true; - #[inline] fn encode_sequence(&self, enc: &mut Encoder) { let ($($ty,)+) = self; - let head_words = 0 $( + $ty.head_words() )+; - enc.push_offset(head_words); + enc.push_offset(0 $( + $ty.head_words() )+); + $( $ty.head_append(enc); enc.bump_offset($ty.tail_words()); )+ + $( $ty.tail_append(enc); )+ + enc.pop_offset(); }