precompile_utils/solidity/codec/
mod.rs

1// This file is part of Frontier.
2
3// Copyright (c) Moonsong Labs.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0
6
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11// 	http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18
19//! Solidity encoding following the
20//! [Contract ABI Specification](https://docs.soliditylang.org/en/v0.8.19/abi-spec.html#abi)
21
22pub mod bytes;
23pub mod native;
24
25#[cfg(any(feature = "codec-xcm", test))]
26pub mod xcm;
27
28use crate::solidity::revert::{MayRevert, RevertReason};
29use alloc::{vec, vec::Vec};
30use core::{convert::TryInto, marker::PhantomData, ops::Range};
31use sp_core::{H256, U256};
32
33pub use alloc::string::String;
34pub use bytes::{BoundedBytes, BoundedString, UnboundedBytes, UnboundedString};
35pub use native::{Address, BoundedVec};
36
37// derive macro
38pub use precompile_utils_macro::Codec;
39
40/// Data that can be encoded/encoded following the Solidity ABI Specification.
41pub trait Codec: Sized {
42	fn read(reader: &mut Reader) -> MayRevert<Self>;
43	fn write(writer: &mut Writer, value: Self);
44	fn has_static_size() -> bool;
45	fn signature() -> String;
46	fn is_explicit_tuple() -> bool {
47		false
48	}
49}
50
51/// Encode the value into its Solidity ABI format.
52/// If `T` is a tuple it is encoded as a Solidity tuple with dynamic-size offset.
53fn encode<T: Codec>(value: T) -> Vec<u8> {
54	Writer::new().write(value).build()
55}
56
57/// Encode the value into its Solidity ABI format.
58/// If `T` is a tuple every element is encoded without a prefixed offset.
59/// It matches the encoding of Solidity function arguments and return value, or event data.
60pub fn encode_arguments<T: Codec>(value: T) -> Vec<u8> {
61	let output = encode(value);
62	if T::is_explicit_tuple() && !T::has_static_size() {
63		output[32..].to_vec()
64	} else {
65		output
66	}
67}
68
69pub use self::{encode_arguments as encode_return_value, encode_arguments as encode_event_data};
70
71/// Encode the value as the arguments of a Solidity function with given selector.
72/// If `T` is a tuple each member represents an argument of the function.
73pub fn encode_with_selector<T: Codec>(selector: u32, value: T) -> Vec<u8> {
74	Writer::new_with_selector(selector)
75		.write_raw_bytes(&encode_arguments(value))
76		.build()
77}
78
79/// Decode the value from its Solidity ABI format.
80/// If `T` is a tuple it is decoded as a Solidity tuple with dynamic-size offset.
81fn decode<T: Codec>(input: &[u8]) -> MayRevert<T> {
82	Reader::new(input).read()
83}
84
85/// Decode the value from its Solidity ABI format.
86/// If `T` is a tuple every element is decoded without a prefixed offset.
87/// It matches the encoding of Solidity function arguments and return value, or event data.
88pub fn decode_arguments<T: Codec>(input: &[u8]) -> MayRevert<T> {
89	if T::is_explicit_tuple() && !T::has_static_size() {
90		let writer = Writer::new();
91		let mut writer = writer.write(U256::from(32));
92		writer.write_pointer(input.to_vec());
93		let input = writer.build();
94		decode(&input)
95	} else {
96		decode(input)
97	}
98}
99
100pub use self::{decode_arguments as decode_return_value, decode_arguments as decode_event_data};
101
102/// Extracts the selector from the start of the input, or returns `None` if the input is too short.
103pub fn selector(input: &[u8]) -> Option<u32> {
104	input.get(0..4).map(|s| {
105		let mut buffer = [0u8; 4];
106		buffer.copy_from_slice(s);
107		u32::from_be_bytes(buffer)
108	})
109}
110
111/// Wrapper around an EVM input slice.
112#[derive(Clone, Copy, Debug)]
113pub struct Reader<'inner> {
114	input: &'inner [u8],
115	cursor: usize,
116}
117
118impl<'inner> Reader<'inner> {
119	/// Create a Reader.
120	pub fn new(input: &'inner [u8]) -> Self {
121		Self { input, cursor: 0 }
122	}
123
124	/// Create a Reader while skipping an initial selector.
125	pub fn new_skip_selector(input: &'inner [u8]) -> MayRevert<Self> {
126		if input.len() < 4 {
127			return Err(RevertReason::read_out_of_bounds("selector").into());
128		}
129
130		Ok(Self::new(&input[4..]))
131	}
132
133	/// Check the input has at least the correct amount of arguments before the end (32 bytes values).
134	pub fn expect_arguments(&self, args: usize) -> MayRevert {
135		if self.input.len() >= self.cursor + args * 32 {
136			Ok(())
137		} else {
138			Err(RevertReason::ExpectedAtLeastNArguments(args).into())
139		}
140	}
141
142	/// Read data from the input.
143	pub fn read<T: Codec>(&mut self) -> MayRevert<T> {
144		T::read(self)
145	}
146
147	/// Read raw bytes from the input.
148	/// Doesn't handle any alignment checks, prefer using `read` instead of possible.
149	/// Returns an error if trying to parse out of bounds.
150	pub fn read_raw_bytes(&mut self, len: usize) -> MayRevert<&[u8]> {
151		let range = self.move_cursor(len)?;
152
153		let data = self
154			.input
155			.get(range)
156			.ok_or_else(|| RevertReason::read_out_of_bounds("raw bytes"))?;
157
158		Ok(data)
159	}
160
161	/// Reads a pointer, returning a reader targetting the pointed location.
162	pub fn read_pointer(&mut self) -> MayRevert<Self> {
163		let offset: usize = self
164			.read::<U256>()
165			.map_err(|_| RevertReason::read_out_of_bounds("pointer"))?
166			.try_into()
167			.map_err(|_| RevertReason::value_is_too_large("pointer"))?;
168
169		if offset >= self.input.len() {
170			return Err(RevertReason::PointerToOutofBound.into());
171		}
172
173		Ok(Self {
174			input: &self.input[offset..],
175			cursor: 0,
176		})
177	}
178
179	/// Read remaining bytes
180	pub fn read_till_end(&mut self) -> MayRevert<&[u8]> {
181		let range = self.move_cursor(self.input.len() - self.cursor)?;
182
183		let data = self
184			.input
185			.get(range)
186			.ok_or_else(|| RevertReason::read_out_of_bounds("raw bytes"))?;
187
188		Ok(data)
189	}
190
191	/// Move the reading cursor with provided length, and return a range from the previous cursor
192	/// location to the new one.
193	/// Checks cursor overflows.
194	fn move_cursor(&mut self, len: usize) -> MayRevert<Range<usize>> {
195		let start = self.cursor;
196		let end = self
197			.cursor
198			.checked_add(len)
199			.ok_or(RevertReason::CursorOverflow)?;
200
201		self.cursor = end;
202
203		Ok(start..end)
204	}
205}
206
207/// Help build an EVM input/output data.
208///
209/// Functions takes `self` to allow chaining all calls like
210/// `Writer::new().write(...).write(...).build()`.
211/// While it could be more ergonomic to take &mut self, this would
212/// prevent to have a `build` function that don't clone the output.
213#[derive(Clone, Debug, Default)]
214pub struct Writer {
215	pub(crate) data: Vec<u8>,
216	offset_data: Vec<OffsetChunk>,
217	selector: Option<u32>,
218}
219
220#[derive(Clone, Debug)]
221struct OffsetChunk {
222	// Offset location in the container data.
223	offset_position: usize,
224	// Data pointed by the offset that must be inserted at the end of container data.
225	data: Vec<u8>,
226	// Inside of arrays, the offset is not from the start of array data (length), but from the start
227	// of the item. This shift allow to correct this.
228	offset_shift: usize,
229}
230
231impl Writer {
232	/// Creates a new empty output builder (without selector).
233	pub fn new() -> Self {
234		Default::default()
235	}
236
237	/// Creates a new empty output builder with provided selector.
238	/// Selector will only be appended before the data when calling
239	/// `build` to not mess with the offsets.
240	pub fn new_with_selector(selector: impl Into<u32>) -> Self {
241		Self {
242			data: vec![],
243			offset_data: vec![],
244			selector: Some(selector.into()),
245		}
246	}
247
248	// Return the built data.
249	pub fn build(mut self) -> Vec<u8> {
250		Self::bake_offsets(&mut self.data, self.offset_data);
251
252		if let Some(selector) = self.selector {
253			let mut output = selector.to_be_bytes().to_vec();
254			output.append(&mut self.data);
255			output
256		} else {
257			self.data
258		}
259	}
260
261	/// Add offseted data at the end of this writer's data, updating the offsets.
262	fn bake_offsets(output: &mut Vec<u8>, offsets: Vec<OffsetChunk>) {
263		for mut offset_chunk in offsets {
264			let offset_position = offset_chunk.offset_position;
265			let offset_position_end = offset_position + 32;
266
267			// The offset is the distance between the start of the data and the
268			// start of the pointed data (start of a struct, length of an array).
269			// Offsets in inner data are relative to the start of their respective "container".
270			// However in arrays the "container" is actually the item itself instead of the whole
271			// array, which is corrected by `offset_shift`.
272			let free_space_offset = output.len() - offset_chunk.offset_shift;
273
274			// Override dummy offset to the offset it will be in the final output.
275			U256::from(free_space_offset)
276				.write_as_big_endian(&mut output[offset_position..offset_position_end]);
277
278			// Append this data at the end of the current output.
279			output.append(&mut offset_chunk.data);
280		}
281	}
282
283	/// Write arbitrary bytes.
284	/// Doesn't handle any alignement checks, prefer using `write` instead if possible.
285	fn write_raw_bytes(mut self, value: &[u8]) -> Self {
286		self.data.extend_from_slice(value);
287		self
288	}
289
290	/// Write data of requested type.
291	pub fn write<T: Codec>(mut self, value: T) -> Self {
292		T::write(&mut self, value);
293		self
294	}
295
296	/// Writes a pointer to given data.
297	/// The data will be appended when calling `build`.
298	/// Initially write a dummy value as offset in this writer's data, which will be replaced by
299	/// the correct offset once the pointed data is appended.
300	///
301	/// Takes `&mut self` since its goal is to be used inside `solidity::Codec` impl and not in chains.
302	pub fn write_pointer(&mut self, data: Vec<u8>) {
303		let offset_position = self.data.len();
304		H256::write(self, H256::repeat_byte(0xff));
305
306		self.offset_data.push(OffsetChunk {
307			offset_position,
308			data,
309			offset_shift: 0,
310		});
311	}
312}
313
314/// Adapter to parse data as a first type then convert it to another one.
315/// Useful for old precompiles in which Solidity arguments where set larger than
316/// the needed Rust type.
317#[derive(Clone, Copy, Debug)]
318pub struct Convert<P, C> {
319	inner: C,
320	_phantom: PhantomData<P>,
321}
322
323impl<P, C> From<C> for Convert<P, C> {
324	fn from(value: C) -> Self {
325		Self {
326			inner: value,
327			_phantom: PhantomData,
328		}
329	}
330}
331
332impl<P, C> Convert<P, C> {
333	pub fn converted(self) -> C {
334		self.inner
335	}
336}
337
338impl<P, C> Codec for Convert<P, C>
339where
340	P: Codec + TryInto<C>,
341	C: Codec + Into<P>,
342{
343	fn read(reader: &mut Reader) -> MayRevert<Self> {
344		let c = P::read(reader)?
345			.try_into()
346			.map_err(|_| RevertReason::value_is_too_large(C::signature()))?;
347
348		Ok(Self {
349			inner: c,
350			_phantom: PhantomData,
351		})
352	}
353
354	fn write(writer: &mut Writer, value: Self) {
355		P::write(writer, value.inner.into())
356	}
357
358	fn has_static_size() -> bool {
359		P::has_static_size()
360	}
361
362	fn signature() -> String {
363		P::signature()
364	}
365}