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);
+}