git.delta.rocks / unique-network / refs/commits / f02a706842f1

difftreelog

source

runtime/common/ethereum/precompiles/utils/data.rs13.7 KiBsourcehistory
1// Copyright 2019-2022 PureStake Inc.2// Copyright 2022      Stake Technologies3// This file is part of Utils package, originally developed by Purestake Inc.4// Utils package used in Astar Network in terms of GPLv3.5//6// Utils is free software: you can redistribute it and/or modify7// it under the terms of the GNU General Public License as published by8// the Free Software Foundation, either version 3 of the License, or9// (at your option) any later version.1011// Utils is distributed in the hope that it will be useful,12// but WITHOUT ANY WARRANTY; without even the implied warranty of13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the14// GNU General Public License for more details.1516// You should have received a copy of the GNU General Public License17// along with Utils.  If not, see <http://www.gnu.org/licenses/>.1819use core::{any::type_name, ops::Range};2021use sp_core::{H160, H256, U256};22use sp_std::{borrow::ToOwned, convert::TryInto, vec, vec::Vec};2324use super::{EvmResult, Gasometer};2526/// The `address` type of Solidity.27/// H160 could represent 2 types of data (bytes20 and address) that are not encoded the same way.28/// To avoid issues writing H160 is thus not supported.29#[derive(Clone, Copy, Debug, Eq, PartialEq)]30pub struct Address(pub H160);3132impl From<H160> for Address {33	fn from(a: H160) -> Address {34		Address(a)35	}36}3738impl From<Address> for H160 {39	fn from(a: Address) -> H160 {40		a.041	}42}4344/// The `bytes`/`string` type of Solidity.45/// It is different from `Vec<u8>` which will be serialized with padding for each `u8` element46/// of the array, while `Bytes` is tightly packed.47#[derive(Clone, Debug, Eq, PartialEq)]48pub struct Bytes(pub Vec<u8>);4950impl From<&[u8]> for Bytes {51	fn from(a: &[u8]) -> Self {52		Self(a.to_owned())53	}54}5556impl From<&str> for Bytes {57	fn from(a: &str) -> Self {58		a.as_bytes().into()59	}60}6162impl Into<Vec<u8>> for Bytes {63	fn into(self) -> Vec<u8> {64		self.065	}66}6768/// Wrapper around an EVM input slice, helping to parse it.69/// Provide functions to parse common types.70#[derive(Clone, Copy, Debug)]71pub struct EvmDataReader<'a> {72	input: &'a [u8],73	cursor: usize,74}7576impl<'a> EvmDataReader<'a> {77	/// Create a new input parser.78	pub fn new(input: &'a [u8]) -> Self {79		Self { input, cursor: 0 }80	}8182	/// Create a new input parser from a selector-initial input.83	pub fn new_with_selector<T>(gasometer: &Gasometer, input: &'a [u8]) -> EvmResult<(Self, T)>84	where85		T: num_enum::TryFromPrimitive<Primitive = u32>,86	{87		if input.len() < 4 {88			return Err(gasometer.revert("tried to parse selector out of bounds"));89		}9091		let mut buffer = [0u8; 4];92		buffer.copy_from_slice(&input[0..4]);93		let selector = T::try_from_primitive(u32::from_be_bytes(buffer)).map_err(|_| {94			log::trace!(95				target: "precompile-utils",96				"Failed to match function selector for {}",97				type_name::<T>()98			);99			gasometer.revert("unknown selector")100		})?;101102		Ok((Self::new(&input[4..]), selector))103	}104105	/// Check the input has at least the correct amount of arguments before the end (32 bytes values).106	pub fn expect_arguments(&self, gasometer: &Gasometer, args: usize) -> EvmResult {107		if self.input.len() >= self.cursor + args * 32 {108			Ok(())109		} else {110			Err(gasometer.revert("input doesn't match expected length"))111		}112	}113114	/// Read data from the input.115	/// Must be provided a gasometer to generate correct Revert errors.116	/// TODO : Benchmark and add cost of parsing to gasometer ?117	pub fn read<T: EvmData>(&mut self, gasometer: &Gasometer) -> EvmResult<T> {118		T::read(self, gasometer)119	}120121	/// Reads a pointer, returning a reader targetting the pointed location.122	pub fn read_pointer(&mut self, gasometer: &Gasometer) -> EvmResult<Self> {123		let offset: usize = self124			.read::<U256>(gasometer)125			.map_err(|_| gasometer.revert("tried to parse array offset out of bounds"))?126			.try_into()127			.map_err(|_| gasometer.revert("array offset is too large"))?;128129		if offset >= self.input.len() {130			return Err(gasometer.revert("pointer points out of bounds"));131		}132133		Ok(Self {134			input: &self.input[offset..],135			cursor: 0,136		})137	}138139	/// Move the reading cursor with provided length, and return a range from the previous cursor140	/// location to the new one.141	/// Checks cursor overflows.142	fn move_cursor(&mut self, gasometer: &Gasometer, len: usize) -> EvmResult<Range<usize>> {143		let start = self.cursor;144		let end = self145			.cursor146			.checked_add(len)147			.ok_or_else(|| gasometer.revert("data reading cursor overflow"))?;148149		self.cursor = end;150151		Ok(start..end)152	}153}154155/// Help build an EVM input/output data.156///157/// Functions takes `self` to allow chaining all calls like158/// `EvmDataWriter::new().write(...).write(...).build()`.159/// While it could be more ergonomic to take &mut self, this would160/// prevent to have a `build` function that don't clone the output.161#[derive(Clone, Debug)]162pub struct EvmDataWriter {163	pub(crate) data: Vec<u8>,164	offset_data: Vec<OffsetDatum>,165	selector: Option<u32>,166}167168#[derive(Clone, Debug)]169struct OffsetDatum {170	// Offset location in the container data.171	offset_position: usize,172	// Data pointed by the offset that must be inserted at the end of container data.173	data: Vec<u8>,174	// Inside of arrays, the offset is not from the start of array data (length), but from the start175	// of the item. This shift allow to correct this.176	offset_shift: usize,177}178179impl EvmDataWriter {180	/// Creates a new empty output builder (without selector).181	pub fn new() -> Self {182		Self {183			data: vec![],184			offset_data: vec![],185			selector: None,186		}187	}188189	/// Return the built data.190	pub fn build(mut self) -> Vec<u8> {191		Self::bake_offsets(&mut self.data, self.offset_data);192193		if let Some(selector) = self.selector {194			let mut output = selector.to_be_bytes().to_vec();195			output.append(&mut self.data);196			output197		} else {198			self.data199		}200	}201202	/// Add offseted data at the end of this writer's data, updating the offsets.203	fn bake_offsets(output: &mut Vec<u8>, offsets: Vec<OffsetDatum>) {204		for mut offset_datum in offsets {205			let offset_position = offset_datum.offset_position;206			let offset_position_end = offset_position + 32;207208			// The offset is the distance between the start of the data and the209			// start of the pointed data (start of a struct, length of an array).210			// Offsets in inner data are relative to the start of their respective "container".211			// However in arrays the "container" is actually the item itself instead of the whole212			// array, which is corrected by `offset_shift`.213			let free_space_offset = output.len() - offset_datum.offset_shift;214215			// Override dummy offset to the offset it will be in the final output.216			U256::from(free_space_offset)217				.to_big_endian(&mut output[offset_position..offset_position_end]);218219			// Append this data at the end of the current output.220			output.append(&mut offset_datum.data);221		}222	}223224	/// Write arbitrary bytes.225	/// Doesn't handle any alignement checks, prefer using `write` instead if possible.226	fn write_raw_bytes(mut self, value: &[u8]) -> Self {227		self.data.extend_from_slice(value);228		self229	}230231	/// Write data of requested type.232	pub fn write<T: EvmData>(mut self, value: T) -> Self {233		T::write(&mut self, value);234		self235	}236237	/// Writes a pointer to given data.238	/// The data will be appended when calling `build`.239	/// Initially write a dummy value as offset in this writer's data, which will be replaced by240	/// the correct offset once the pointed data is appended.241	///242	/// Takes `&mut self` since its goal is to be used inside `EvmData` impl and not in chains.243	pub fn write_pointer(&mut self, data: Vec<u8>) {244		let offset_position = self.data.len();245		H256::write(self, H256::repeat_byte(0xff));246247		self.offset_data.push(OffsetDatum {248			offset_position,249			data,250			offset_shift: 0,251		});252	}253}254255impl Default for EvmDataWriter {256	fn default() -> Self {257		Self::new()258	}259}260261/// Data that can be converted from and to EVM data types.262pub trait EvmData: Sized {263	fn read(reader: &mut EvmDataReader, gasometer: &Gasometer) -> EvmResult<Self>;264	fn write(writer: &mut EvmDataWriter, value: Self);265}266267impl EvmData for H256 {268	fn read(reader: &mut EvmDataReader, gasometer: &Gasometer) -> EvmResult<Self> {269		let range = reader.move_cursor(gasometer, 32)?;270271		let data = reader272			.input273			.get(range)274			.ok_or_else(|| gasometer.revert("tried to parse H256 out of bounds"))?;275276		Ok(H256::from_slice(data))277	}278279	fn write(writer: &mut EvmDataWriter, value: Self) {280		writer.data.extend_from_slice(value.as_bytes());281	}282}283284impl EvmData for Address {285	fn read(reader: &mut EvmDataReader, gasometer: &Gasometer) -> EvmResult<Self> {286		let range = reader.move_cursor(gasometer, 32)?;287288		let data = reader289			.input290			.get(range)291			.ok_or_else(|| gasometer.revert("tried to parse H160 out of bounds"))?;292293		Ok(H160::from_slice(&data[12..32]).into())294	}295296	fn write(writer: &mut EvmDataWriter, value: Self) {297		H256::write(writer, value.0.into());298	}299}300301impl EvmData for U256 {302	fn read(reader: &mut EvmDataReader, gasometer: &Gasometer) -> EvmResult<Self> {303		let range = reader.move_cursor(gasometer, 32)?;304305		let data = reader306			.input307			.get(range)308			.ok_or_else(|| gasometer.revert("tried to parse U256 out of bounds"))?;309310		Ok(U256::from_big_endian(data))311	}312313	fn write(writer: &mut EvmDataWriter, value: Self) {314		let mut buffer = [0u8; 32];315		value.to_big_endian(&mut buffer);316		writer.data.extend_from_slice(&buffer);317	}318}319320macro_rules! impl_evmdata_for_uints {321	($($uint:ty, )*) => {322		$(323			impl EvmData for $uint {324				fn read(reader: &mut EvmDataReader, gasometer: &Gasometer) -> EvmResult<Self> {325					let range = reader.move_cursor(gasometer, 32)?;326327					let data = reader328						.input329						.get(range)330						.ok_or_else(|| gasometer.revert(alloc::format!(331							"tried to parse {} out of bounds", core::any::type_name::<Self>()332						)))?;333334					let mut buffer = [0u8; core::mem::size_of::<Self>()];335					buffer.copy_from_slice(&data[32 - core::mem::size_of::<Self>()..]);336					Ok(Self::from_be_bytes(buffer))337				}338339				fn write(writer: &mut EvmDataWriter, value: Self) {340					let mut buffer = [0u8; 32];341					buffer[32 - core::mem::size_of::<Self>()..].copy_from_slice(&value.to_be_bytes());342					writer.data.extend_from_slice(&buffer);343				}344			}345		)*346	};347}348349impl_evmdata_for_uints!(u16, u32, u64, u128,);350351// The implementation for u8 is specific, for performance reasons.352impl EvmData for u8 {353	fn read(reader: &mut EvmDataReader, gasometer: &Gasometer) -> EvmResult<Self> {354		let range = reader.move_cursor(gasometer, 32)?;355356		let data = reader357			.input358			.get(range)359			.ok_or_else(|| gasometer.revert("tried to parse u64 out of bounds"))?;360361		Ok(data[31])362	}363364	fn write(writer: &mut EvmDataWriter, value: Self) {365		let mut buffer = [0u8; 32];366		buffer[31] = value;367368		writer.data.extend_from_slice(&buffer);369	}370}371372impl EvmData for bool {373	fn read(reader: &mut EvmDataReader, gasometer: &Gasometer) -> EvmResult<Self> {374		let h256 = H256::read(reader, gasometer)375			.map_err(|_| gasometer.revert("tried to parse bool out of bounds"))?;376377		Ok(!h256.is_zero())378	}379380	fn write(writer: &mut EvmDataWriter, value: Self) {381		let mut buffer = [0u8; 32];382		if value {383			buffer[31] = 1;384		}385386		writer.data.extend_from_slice(&buffer);387	}388}389390impl<T: EvmData> EvmData for Vec<T> {391	fn read(reader: &mut EvmDataReader, gasometer: &Gasometer) -> EvmResult<Self> {392		let mut inner_reader = reader.read_pointer(gasometer)?;393394		let array_size: usize = inner_reader395			.read::<U256>(gasometer)396			.map_err(|_| gasometer.revert("tried to parse array length out of bounds"))?397			.try_into()398			.map_err(|_| gasometer.revert("array length is too large"))?;399400		let mut array = vec![];401402		let mut item_reader = EvmDataReader {403			input: inner_reader404				.input405				.get(32..)406				.ok_or_else(|| gasometer.revert("try to read array items out of bound"))?,407			cursor: 0,408		};409410		for _ in 0..array_size {411			array.push(item_reader.read(gasometer)?);412		}413414		Ok(array)415	}416417	fn write(writer: &mut EvmDataWriter, value: Self) {418		let mut inner_writer = EvmDataWriter::new().write(U256::from(value.len()));419420		for inner in value {421			// Any offset in items are relative to the start of the item instead of the422			// start of the array. However if there is offseted data it must but appended after423			// all items (offsets) are written. We thus need to rely on `compute_offsets` to do424			// that, and must store a "shift" to correct the offsets.425			let shift = inner_writer.data.len();426			let item_writer = EvmDataWriter::new().write(inner);427428			inner_writer = inner_writer.write_raw_bytes(&item_writer.data);429			for mut offset_datum in item_writer.offset_data {430				offset_datum.offset_shift += 32;431				offset_datum.offset_position += shift;432				inner_writer.offset_data.push(offset_datum);433			}434		}435436		writer.write_pointer(inner_writer.build());437	}438}439440impl EvmData for Bytes {441	fn read(reader: &mut EvmDataReader, gasometer: &Gasometer) -> EvmResult<Self> {442		let mut inner_reader = reader.read_pointer(gasometer)?;443444		// Read bytes/string size.445		let array_size: usize = inner_reader446			.read::<U256>(gasometer)447			.map_err(|_| gasometer.revert("tried to parse bytes/string length out of bounds"))?448			.try_into()449			.map_err(|_| gasometer.revert("bytes/string length is too large"))?;450451		// Get valid range over the bytes data.452		let range = inner_reader.move_cursor(gasometer, array_size)?;453454		let data = inner_reader455			.input456			.get(range)457			.ok_or_else(|| gasometer.revert("tried to parse bytes/string out of bounds"))?;458459		let bytes = Self(data.to_owned());460461		Ok(bytes)462	}463464	fn write(writer: &mut EvmDataWriter, value: Self) {465		let length = value.0.len();466467		// Pad the data.468		// Leave it as is if a multiple of 32, otherwise pad to next469		// multiple or 32.470		let chunks = length / 32;471		let padded_size = match length % 32 {472			0 => chunks * 32,473			_ => (chunks + 1) * 32,474		};475476		let mut value = value.0.to_vec();477		value.resize(padded_size, 0);478479		writer.write_pointer(480			EvmDataWriter::new()481				.write(U256::from(length))482				.write_raw_bytes(&value)483				.build(),484		);485	}486}