Skip to content

Commit

Permalink
fix(sol-types): fixed array decode
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes committed Nov 23, 2023
1 parent 953f521 commit 2f5bf66
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 33 deletions.
8 changes: 8 additions & 0 deletions crates/sol-macro/src/expand/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,14 @@ fn expand_from_into_tuples<P>(name: &Ident, fields: &Parameters<P>) -> TokenStre
#[doc(hidden)]
type UnderlyingRustTuple<'a> = #rust_tuple;

#[cfg(test)]
#[allow(dead_code, unreachable_patterns)]
fn _type_assertion(_t: ::alloy_sol_types::private::AssertTypeEq<UnderlyingRustTuple>) {
match _t {
::alloy_sol_types::private::AssertTypeEq::<<UnderlyingSolTuple as ::alloy_sol_types::SolType>::RustType>(_) => {}
}
}

#[automatically_derived]
#[doc(hidden)]
impl ::core::convert::From<#name> for UnderlyingRustTuple<'_> {
Expand Down
2 changes: 1 addition & 1 deletion crates/sol-macro/src/expand/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result<TokenStream> {

#eip712_encode_type_fns

fn eip712_encode_data(&self) -> Vec<u8> {
fn eip712_encode_data(&self) -> ::alloy_sol_types::private::Vec<u8> {
#encode_data_impl
}
}
Expand Down
70 changes: 69 additions & 1 deletion crates/sol-types/src/abi/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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!(<Ty as SolType>::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!(<Ty as SolType>::abi_decode_params(&encoded, false).unwrap(), ty);
}
}
47 changes: 17 additions & 30 deletions crates/sol-types/src/abi/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,11 @@ impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken<T, N> {

#[inline]
fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
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]
Expand Down Expand Up @@ -361,7 +363,7 @@ impl<'de, T: Token<'de>> Token<'de> for DynSeqToken<T> {
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::<Result<Vec<T>>>().map(DynSeqToken)
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -508,19 +511,11 @@ macro_rules! tuple_impls {
fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
// 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]
Expand Down Expand Up @@ -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);
}
}
}
Expand All @@ -601,7 +585,10 @@ macro_rules! tuple_impls {
#[inline]
fn decode_sequence(dec: &mut Decoder<'de>) -> Result<Self> {
Ok(($(
<$ty as Token>::decode_from(dec)?,
match <$ty as Token>::decode_from(dec) {
Ok(t) => t,
Err(e) => return Err(e),
},
)+))
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/sol-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -253,4 +254,6 @@ pub mod private {
pub const fn u256(n: u64) -> U256 {
U256::from_limbs([n, 0, 0, 0])
}

pub struct AssertTypeEq<T>(pub T);
}
41 changes: 40 additions & 1 deletion crates/sol-types/tests/macros/sol/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
}

0 comments on commit 2f5bf66

Please sign in to comment.