diff --git a/src/data.rs b/src/data.rs new file mode 100644 index 0000000000..75005e0794 --- /dev/null +++ b/src/data.rs @@ -0,0 +1,306 @@ +// Copyright 2019-2021 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Moonbeam is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Moonbeam. If not, see . + +use super::{error, EvmResult}; +use core::ops::Range; +use sp_core::{H160, H256, U256}; +use sp_std::{convert::TryInto, vec, vec::Vec}; + +/// The `address` type of Solidity. +/// H160 could represent 2 types of data (bytes20 and address) that are not encoded the same way. +/// To avoid issues writing H160 is thus not supported. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct Address(pub H160); + +impl From for Address { + fn from(a: H160) -> Address { + Address(a) + } +} + +impl From
for H160 { + fn from(a: Address) -> H160 { + a.0 + } +} + +/// Wrapper around an EVM input slice, helping to parse it. +/// Provide functions to parse common types. +#[derive(Clone, Copy, Debug)] +pub struct EvmDataReader<'a> { + input: &'a [u8], + cursor: usize, +} + +impl<'a> EvmDataReader<'a> { + /// Create a new input parser. + pub fn new(input: &'a [u8]) -> Self { + Self { input, cursor: 0 } + } + + /// Check the input has at least the correct amount of arguments before the end (32 bytes values). + pub fn expect_arguments(&self, args: usize) -> EvmResult { + if self.input.len() >= self.cursor + args * 32 { + Ok(()) + } else { + Err(error("input doesn't match expected length")) + } + } + + /// Read data from the input. + pub fn read(&mut self) -> EvmResult { + T::read(self) + } + + /// Read raw bytes from the input. + /// Doesn't handle any alignement checks, prefer using `read` instead of possible. + /// Returns an error if trying to parse out of bounds. + pub fn read_raw_bytes(&mut self, len: usize) -> EvmResult<&[u8]> { + let range = self.move_cursor(len)?; + + let data = self + .input + .get(range) + .ok_or_else(|| error("tried to parse raw bytes out of bounds"))?; + + Ok(data) + } + + /// Parse (4 bytes) selector. + /// Returns an error if trying to parse out of bounds. + pub fn read_selector(&mut self) -> EvmResult<&[u8]> { + self.read_raw_bytes(4) + .map_err(|_| error("tried to parse selector out of bounds")) + } + + /// Move the reading cursor with provided length, and return a range from the previous cursor + /// location to the new one. + /// Checks cursor overflows. + fn move_cursor(&mut self, len: usize) -> EvmResult> { + let start = self.cursor; + let end = self + .cursor + .checked_add(len) + .ok_or_else(|| error("data reading cursor overflow"))?; + + self.cursor = end; + + Ok(start..end) + } +} + +/// Help build an EVM input/output data. +#[derive(Clone, Debug)] +pub struct EvmDataWriter { + data: Vec, + arrays: Vec, +} + +#[derive(Clone, Debug)] +struct Array { + offset_position: usize, + data: Vec, + inner_arrays: Vec, +} + +impl EvmDataWriter { + /// Creates a new empty output builder. + pub fn new() -> Self { + Self { + data: vec![], + arrays: vec![], + } + } + + /// Return the built data. + pub fn build(mut self) -> Vec { + Self::build_arrays(&mut self.data, self.arrays, 0); + + self.data + } + + /// Build the array into data. + /// `global_offset` represents the start of the frame we are modifying. + /// While the main data will have a `global_offset` of 0, inner arrays will have a + /// `global_offset` corresponding to the start its parent array size data. + fn build_arrays(output: &mut Vec, arrays: Vec, global_offset: usize) { + for mut array in arrays { + let offset_position = array.offset_position; + let offset_position_end = offset_position + 32; + let free_space_offset = output.len() + global_offset; + + // Override dummy offset to the offset it will be in the final output. + U256::from(free_space_offset) + .to_big_endian(&mut output[offset_position..offset_position_end]); + + // Build inner arrays if any. + Self::build_arrays(&mut array.data, array.inner_arrays, free_space_offset); + + // Append this data at the end of the current output. + output.append(&mut array.data); + } + } + + /// Write arbitrary bytes. + /// Doesn't handle any alignement checks, prefer using `write` instead of possible. + pub fn write_raw_bytes(mut self, value: &[u8]) -> Self { + self.data.extend_from_slice(value); + self + } + + /// Write data of requested type. + pub fn write(mut self, value: T) -> Self { + T::write(&mut self, value); + self + } +} + +impl Default for EvmDataWriter { + fn default() -> Self { + Self::new() + } +} + +/// Data that can be converted from and to EVM data types. +pub trait EvmData: Sized { + fn read(reader: &mut EvmDataReader) -> EvmResult; + fn write(writer: &mut EvmDataWriter, value: Self); +} + +impl EvmData for H256 { + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; + + let data = reader + .input + .get(range) + .ok_or_else(|| error("tried to parse H256 out of bounds"))?; + + Ok(H256::from_slice(data)) + } + + fn write(writer: &mut EvmDataWriter, value: Self) { + writer.data.extend_from_slice(&value.as_bytes()); + } +} + +impl EvmData for Address { + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; + + let data = reader + .input + .get(range) + .ok_or_else(|| error("tried to parse H160 out of bounds"))?; + + Ok(H160::from_slice(&data[12..32]).into()) + } + + fn write(writer: &mut EvmDataWriter, value: Self) { + H256::write(writer, value.0.into()); + } +} + +impl EvmData for U256 { + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; + + let data = reader + .input + .get(range) + .ok_or_else(|| error("tried to parse U256 out of bounds"))?; + + Ok(U256::from_big_endian(data)) + } + + fn write(writer: &mut EvmDataWriter, value: Self) { + let mut buffer = [0u8; 32]; + value.to_big_endian(&mut buffer); + writer.data.extend_from_slice(&buffer); + } +} + +impl EvmData for bool { + fn read(reader: &mut EvmDataReader) -> EvmResult { + let h256 = H256::read(reader).map_err(|_| error("tried to parse bool out of bounds"))?; + + Ok(!h256.is_zero()) + } + + fn write(writer: &mut EvmDataWriter, value: Self) { + let mut buffer = [0u8; 32]; + if value { + buffer[31] = 1; + } + + writer.data.extend_from_slice(&buffer); + } +} + +impl EvmData for Vec { + fn read(reader: &mut EvmDataReader) -> EvmResult { + let array_start: usize = reader + .read::() + .map_err(|_| error("tried to parse array offset out of bounds"))? + .try_into() + .map_err(|_| error("array offset is too large"))?; + + // We temporarily move the cursor to the offset, we'll set it back afterward. + let original_cursor = reader.cursor; + reader.cursor = array_start; + + let array_size: usize = reader + .read::() + .map_err(|_| error("tried to parse array length out of bounds"))? + .try_into() + .map_err(|_| error("array length is too large"))?; + + let mut array = vec![]; + + for _ in 0..array_size { + array.push(reader.read()?); + } + + // We set back the cursor to its original location. + reader.cursor = original_cursor; + + Ok(array) + } + + fn write(writer: &mut EvmDataWriter, value: Self) { + let offset_position = writer.data.len(); + H256::write(writer, H256::repeat_byte(0xff)); + // 0xff = When debugging it makes spoting offset values easier. + + let mut inner_writer = EvmDataWriter::new(); + + // Write length. + inner_writer = inner_writer.write(U256::from(value.len())); + + // Write elements of array. + for inner in value { + inner_writer = inner_writer.write(inner); + } + + let array = Array { + offset_position, + data: inner_writer.data, + inner_arrays: inner_writer.arrays, + }; + + writer.arrays.push(array); + } +} diff --git a/src/lib.rs b/src/lib.rs index d4ada3ac02..2832c09083 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,9 +22,15 @@ use frame_support::{ traits::Get, }; use pallet_evm::{GasWeightMapping, Log}; -use sp_core::{H160, H256, U256}; +use sp_core::{H160, H256}; use sp_std::{marker::PhantomData, vec, vec::Vec}; +mod data; +pub use data::{Address, EvmData, EvmDataReader, EvmDataWriter}; + +#[cfg(test)] +mod tests; + /// Alias for Result returning an EVM precompile error. pub type EvmResult = Result; @@ -33,112 +39,6 @@ pub fn error(text: &'static str) -> ExitError { ExitError::Other(text.into()) } -/// Wrapper around an EVM input slice, helping to parse it. -/// Provide functions to parse common types. -#[derive(Clone, Copy, Debug)] -pub struct InputReader<'a> { - input: &'a [u8], - cursor: usize, -} - -impl<'a> InputReader<'a> { - /// Create a new input parser. - pub fn new(input: &'a [u8]) -> EvmResult { - if input.len() >= 4 { - Ok(Self { input, cursor: 4 }) - } else { - Err(error("input must at least contain a selector")) - } - } - - /// Extract selector from input. - pub fn selector(&self) -> &[u8] { - &self.input[0..4] - } - - /// Check the input has the correct amount of arguments (32 bytes values). - pub fn expect_arguments(&self, args: usize) -> EvmResult { - if self.input.len() == 4 + args * 32 { - Ok(()) - } else { - Err(error("input doesn't match expected length")) - } - } - - /// Parse a U256 value. - /// Returns an error if trying to parse out of bound. - pub fn read_u256(&mut self) -> EvmResult { - let range_end = self.cursor + 32; - - let data = self - .input - .get(self.cursor..range_end) - .ok_or_else(|| error("tried to parse out of bound"))?; - - self.cursor += 32; - - Ok(U256::from_big_endian(data)) - } - - /// Parse an address value. - /// Returns an error if trying to parse out of bound. - /// Ignores the 12 higher bytes. - pub fn read_address(&mut self) -> EvmResult { - let range_end = self.cursor + 32; - - let data = self - .input - .get(self.cursor..range_end) - .ok_or_else(|| error("tried to parse out of bound"))?; - - self.cursor += 32; - - Ok(H160::from_slice(&data[12..32])) - } -} - -/// Help build an EVM output data. -#[derive(Clone, Debug)] -pub struct OutputBuilder { - data: Vec, -} - -impl OutputBuilder { - /// Creates a new empty output builder. - pub fn new() -> Self { - Self { data: vec![] } - } - - /// Return the built data. - pub fn build(self) -> Vec { - self.data - } - - /// Push a U256 to the output. - pub fn write_u256>(mut self, value: T) -> Self { - let mut buffer = [0u8; 32]; - value.into().to_big_endian(&mut buffer); - self.data.extend_from_slice(&buffer); - self - } - - /// Push a U256 to the output. - pub fn write_bool>(mut self, value: T) -> Self { - let mut buffer = [0u8; 32]; - if value.into() { - buffer[31] = 1; - } - self.data.extend_from_slice(&buffer); - self - } -} - -impl Default for OutputBuilder { - fn default() -> Self { - Self::new() - } -} - /// Builder for PrecompileOutput. #[derive(Clone, Debug)] pub struct LogsBuilder { @@ -277,6 +177,10 @@ where } // Dispatch call. + // It may be possible to not record gas cost if the call returnes Pays::No. + // However while Substrate handle checking weight while not making the sender pay for it, + // the EVM doesn't. It seems this safer to always record the costs to avoid unmetered + // computations. let used_weight = call .dispatch(origin) .map_err(|_| error("dispatched call failed"))? @@ -304,6 +208,8 @@ where } /// Custom Gasometer to record costs in precompiles. +/// It is advised to record known costs as early as possible to +/// avoid unecessary computations if there is an Out of Gas. #[derive(Clone, Copy, Debug)] pub struct Gasometer { target_gas: Option, @@ -327,7 +233,7 @@ impl Gasometer { /// Record cost, and return error if it goes out of gas. pub fn record_cost(&mut self, cost: u64) -> EvmResult { - self.used_gas += cost; + self.used_gas = self.used_gas.checked_add(cost).ok_or(ExitError::OutOfGas)?; match self.target_gas { Some(gas_limit) if self.used_gas > gas_limit => Err(ExitError::OutOfGas), @@ -335,6 +241,40 @@ impl Gasometer { } } + /// Record cost of a log manualy. + /// This can be useful to record log costs early when their content have static size. + pub fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult { + // Cost calculation is copied from EVM code that is not publicly exposed by the crates. + // https://github.com/rust-blockchain/evm/blob/master/gasometer/src/costs.rs#L148 + + const G_LOG: u64 = 375; + const G_LOGDATA: u64 = 8; + const G_LOGTOPIC: u64 = 375; + + let topic_cost = G_LOGTOPIC + .checked_mul(topics as u64) + .ok_or(ExitError::OutOfGas)?; + + let data_cost = G_LOGDATA + .checked_mul(data_len as u64) + .ok_or(ExitError::OutOfGas)?; + + self.record_cost(G_LOG)?; + self.record_cost(topic_cost)?; + self.record_cost(data_cost)?; + + Ok(()) + } + + /// Record cost of logs. + pub fn record_log_costs(&mut self, logs: &[Log]) -> EvmResult { + for log in logs { + self.record_log_costs_manual(log.topics.len(), log.data.len())?; + } + + Ok(()) + } + /// Compute remaining gas. /// Returns error if out of gas. /// Returns None if no gas limit. diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000000..6356d51e63 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,413 @@ +// Copyright 2019-2021 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Moonbeam is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Moonbeam. If not, see . + +use super::*; +use sp_core::{H256, U256}; + +fn u256_repeat_byte(byte: u8) -> U256 { + let value = H256::repeat_byte(byte); + + U256::from_big_endian(value.as_bytes()) +} + +#[test] +fn write_bool() { + let value = true; + + let writer_output = EvmDataWriter::new().write(value).build(); + + let mut expected_output = [0u8; 32]; + expected_output[31] = 1; + + assert_eq!(writer_output, expected_output); +} + +#[test] +fn read_bool() { + let value = true; + + let writer_output = EvmDataWriter::new().write(value).build(); + + let mut reader = EvmDataReader::new(&writer_output); + let parsed: bool = reader.read().expect("to correctly parse bool"); + + assert_eq!(value, parsed); +} + +#[test] +fn write_u256() { + let value = U256::from(42); + + let writer_output = EvmDataWriter::new().write(value).build(); + + let mut expected_output = [0u8; 32]; + value.to_big_endian(&mut expected_output); + + assert_eq!(writer_output, expected_output); +} + +#[test] +fn read_u256() { + let value = U256::from(42); + let writer_output = EvmDataWriter::new().write(value).build(); + + let mut reader = EvmDataReader::new(&writer_output); + let parsed: U256 = reader.read().expect("to correctly parse U256"); + + assert_eq!(value, parsed); +} + +#[test] +#[should_panic(expected = "to correctly parse U256")] +fn read_u256_too_short() { + let value = U256::from(42); + let writer_output = EvmDataWriter::new().write(value).build(); + + let mut reader = EvmDataReader::new(&writer_output[0..31]); + let _: U256 = reader.read().expect("to correctly parse U256"); +} + +#[test] +fn write_h256() { + let mut raw = [0u8; 32]; + raw[0] = 42; + raw[12] = 43; + raw[31] = 44; + + let value = H256::from(raw); + + let output = EvmDataWriter::new().write(value).build(); + + assert_eq!(&output, &raw); +} + +#[test] +fn read_h256() { + let mut raw = [0u8; 32]; + raw[0] = 42; + raw[12] = 43; + raw[31] = 44; + let value = H256::from(raw); + let writer_output = EvmDataWriter::new().write(value).build(); + + let mut reader = EvmDataReader::new(&writer_output); + let parsed: H256 = reader.read().expect("to correctly parse H256"); + + assert_eq!(value, parsed); +} + +#[test] +#[should_panic(expected = "to correctly parse H256")] +fn read_h256_too_short() { + let mut raw = [0u8; 32]; + raw[0] = 42; + raw[12] = 43; + raw[31] = 44; + let value = H256::from(raw); + let writer_output = EvmDataWriter::new().write(value).build(); + + let mut reader = EvmDataReader::new(&writer_output[0..31]); + let _: H256 = reader.read().expect("to correctly parse H256"); +} + +#[test] +fn write_address() { + let value = H160::repeat_byte(0xAA); + + let output = EvmDataWriter::new().write(Address(value)).build(); + + assert_eq!(output.len(), 32); + assert_eq!(&output[12..32], value.as_bytes()); +} + +#[test] +fn read_address() { + let value = H160::repeat_byte(0xAA); + let writer_output = EvmDataWriter::new().write(Address(value)).build(); + + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Address = reader.read().expect("to correctly parse Address"); + + assert_eq!(value, parsed.0); +} + +#[test] +fn write_h256_array() { + let array = vec![ + H256::repeat_byte(0x11), + H256::repeat_byte(0x22), + H256::repeat_byte(0x33), + H256::repeat_byte(0x44), + H256::repeat_byte(0x55), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); + assert_eq!(writer_output.len(), 0xE0); + + // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. + let mut reader = EvmDataReader::new(&writer_output); + + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), 5.into()); + assert_eq!(reader.read::().expect("read 1st"), array[0]); + assert_eq!(reader.read::().expect("read 2nd"), array[1]); + assert_eq!(reader.read::().expect("read 3rd"), array[2]); + assert_eq!(reader.read::().expect("read 4th"), array[3]); + assert_eq!(reader.read::().expect("read 5th"), array[4]); +} + +#[test] +fn read_h256_array() { + let array = vec![ + H256::repeat_byte(0x11), + H256::repeat_byte(0x22), + H256::repeat_byte(0x33), + H256::repeat_byte(0x44), + H256::repeat_byte(0x55), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); + + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Vec = reader.read().expect("to correctly parse Vec"); + + assert_eq!(array, parsed); +} + +#[test] +fn write_u256_array() { + let array = vec![ + u256_repeat_byte(0x11), + u256_repeat_byte(0x22), + u256_repeat_byte(0x33), + u256_repeat_byte(0x44), + u256_repeat_byte(0x55), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); + assert_eq!(writer_output.len(), 0xE0); + + // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. + let mut reader = EvmDataReader::new(&writer_output); + + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), 5.into()); + assert_eq!(reader.read::().expect("read 1st"), array[0]); + assert_eq!(reader.read::().expect("read 2nd"), array[1]); + assert_eq!(reader.read::().expect("read 3rd"), array[2]); + assert_eq!(reader.read::().expect("read 4th"), array[3]); + assert_eq!(reader.read::().expect("read 5th"), array[4]); +} + +#[test] +fn read_u256_array() { + let array = vec![ + u256_repeat_byte(0x11), + u256_repeat_byte(0x22), + u256_repeat_byte(0x33), + u256_repeat_byte(0x44), + u256_repeat_byte(0x55), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); + + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Vec = reader.read().expect("to correctly parse Vec"); + + assert_eq!(array, parsed); +} + +#[test] +fn write_address_array() { + let array = vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + Address(H160::repeat_byte(0x44)), + Address(H160::repeat_byte(0x55)), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); + + // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. + let mut reader = EvmDataReader::new(&writer_output); + + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), 5.into()); + assert_eq!(reader.read::
().expect("read 1st"), array[0]); + assert_eq!(reader.read::
().expect("read 2nd"), array[1]); + assert_eq!(reader.read::
().expect("read 3rd"), array[2]); + assert_eq!(reader.read::
().expect("read 4th"), array[3]); + assert_eq!(reader.read::
().expect("read 5th"), array[4]); +} + +#[test] +fn read_address_array() { + let array = vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + Address(H160::repeat_byte(0x44)), + Address(H160::repeat_byte(0x55)), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); + + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Vec
= reader.read().expect("to correctly parse Vec"); + + assert_eq!(array, parsed); +} + +#[test] +fn read_address_array_size_too_big() { + let array = vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + Address(H160::repeat_byte(0x44)), + Address(H160::repeat_byte(0x55)), + ]; + let mut writer_output = EvmDataWriter::new().write(array.clone()).build(); + + U256::from(6).to_big_endian(&mut writer_output[0x20..0x40]); + + let mut reader = EvmDataReader::new(&writer_output); + match reader.read::>() { + Ok(_) => panic!("should not parse correctly"), + Err(ExitError::Other(err)) => assert_eq!(err, "tried to parse H160 out of bounds"), + Err(_) => panic!("unexpected error"), + } +} + +#[test] +fn write_address_nested_array() { + let array = vec![ + vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + ], + vec![ + Address(H160::repeat_byte(0x44)), + Address(H160::repeat_byte(0x55)), + ], + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); + assert_eq!(writer_output.len(), 0x160); + + writer_output + .chunks_exact(32) + .map(|chunk| H256::from_slice(chunk)) + .for_each(|hash| println!("{:?}", hash)); + + // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. + let mut reader = EvmDataReader::new(&writer_output); + + assert_eq!(reader.read::().expect("read offset"), 0x20.into()); // 0x00 + assert_eq!(reader.read::().expect("read size"), 2.into()); // 0x20 + assert_eq!(reader.read::().expect("read 1st offset"), 0x80.into()); // 0x40 + assert_eq!( + reader.read::().expect("read 2st offset"), + 0x100.into() + ); // 0x60 + assert_eq!(reader.read::().expect("read 1st size"), 3.into()); // 0x80 + assert_eq!(reader.read::
().expect("read 1-1"), array[0][0]); // 0xA0 + assert_eq!(reader.read::
().expect("read 1-2"), array[0][1]); // 0xC0 + assert_eq!(reader.read::
().expect("read 1-3"), array[0][2]); // 0xE0 + assert_eq!(reader.read::().expect("read 2nd size"), 2.into()); // 0x100 + assert_eq!(reader.read::
().expect("read 2-1"), array[1][0]); // 0x120 + assert_eq!(reader.read::
().expect("read 2-2"), array[1][1]); // 0x140 +} + +#[test] +fn read_address_nested_array() { + let array = vec![ + vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + ], + vec![ + Address(H160::repeat_byte(0x44)), + Address(H160::repeat_byte(0x55)), + ], + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); + + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Vec> = reader.read().expect("to correctly parse Vec>"); + + assert_eq!(array, parsed); +} + +#[test] + +fn write_multiple_arrays() { + let array1 = vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + ]; + + let array2 = vec![H256::repeat_byte(0x44), H256::repeat_byte(0x55)]; + + let writer_output = EvmDataWriter::new() + .write(array1.clone()) + .write(array2.clone()) + .build(); + + assert_eq!(writer_output.len(), 0x120); + + // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. + let mut reader = EvmDataReader::new(&writer_output); + + assert_eq!(reader.read::().expect("read 1st offset"), 0x40.into()); // 0x00 + assert_eq!(reader.read::().expect("read 2nd offset"), 0xc0.into()); // 0x20 + assert_eq!(reader.read::().expect("read 1st size"), 3.into()); // 0x40 + assert_eq!(reader.read::
().expect("read 1-1"), array1[0]); // 0x60 + assert_eq!(reader.read::
().expect("read 1-2"), array1[1]); // 0x80 + assert_eq!(reader.read::
().expect("read 1-3"), array1[2]); // 0xA0 + assert_eq!(reader.read::().expect("read 2nd size"), 2.into()); // 0xC0 + assert_eq!(reader.read::().expect("read 2-1"), array2[0]); // 0xE0 + assert_eq!(reader.read::().expect("read 2-2"), array2[1]); // 0x100 +} + +#[test] +fn read_multiple_arrays() { + let array1 = vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + ]; + + let array2 = vec![H256::repeat_byte(0x44), H256::repeat_byte(0x55)]; + + let writer_output = EvmDataWriter::new() + .write(array1.clone()) + .write(array2.clone()) + .build(); + + // offset 0x20 + // offset 0x40 + // size 0x60 + // 3 addresses 0xC0 + // size 0xE0 + // 2 H256 0x120 + assert_eq!(writer_output.len(), 0x120); + + let mut reader = EvmDataReader::new(&writer_output); + + let parsed: Vec
= reader.read().expect("to correctly parse Vec
"); + assert_eq!(array1, parsed); + + let parsed: Vec = reader.read().expect("to correctly parse Vec"); + assert_eq!(array2, parsed); +}