Skip to content
This repository was archived by the owner on Sep 28, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
904470b
add SetAppendix in remote_transact
gitofdeepanshu Jun 2, 2023
401c1ce
formatting abstract params
gitofdeepanshu Jun 2, 2023
bcff3fd
update remote_transact()
gitofdeepanshu Jun 2, 2023
cc97ce4
remove println
gitofdeepanshu Jun 4, 2023
af7fcef
add send_xcm function to precompile
gitofdeepanshu Jun 8, 2023
527b642
fix remote_transact call
gitofdeepanshu Jun 8, 2023
97b60aa
small fixes xcm.sol
gitofdeepanshu Jun 8, 2023
55886ec
fix typos xcm.sol
gitofdeepanshu Jun 8, 2023
a9b7a5d
change bytes32 to bytes in send_xcm()
gitofdeepanshu Jun 8, 2023
3df8b14
add test for xcm_call
gitofdeepanshu Jun 12, 2023
a064674
fix xcm_send using Bytes
gitofdeepanshu Jun 12, 2023
75a1bea
add multilocation for xcm-precompile
gitofdeepanshu Jun 13, 2023
d1c0d56
update licenses
gitofdeepanshu Jun 13, 2023
b77fcc2
fix scope issue for String
gitofdeepanshu Jun 14, 2023
edc738e
fmt
gitofdeepanshu Jun 14, 2023
450c780
minor refactor, add log:trace
gitofdeepanshu Jun 20, 2023
a4cf0be
Empty-Commit
gitofdeepanshu Jun 20, 2023
83ae4ad
make input field in EvmDataReader pub(crate)
gitofdeepanshu Jun 23, 2023
65a22ef
remove setAppendix from remote_transact()
gitofdeepanshu Jun 26, 2023
0171f31
old interface added for legacy support
gitofdeepanshu Jun 26, 2023
6c5a6ea
fmt
gitofdeepanshu Jun 26, 2023
6038c63
add tests for multilocation and bytes
gitofdeepanshu Jun 26, 2023
60bcbb6
data encapsulation fix
gitofdeepanshu Jun 26, 2023
27cd351
newline in tests
gitofdeepanshu Jul 9, 2023
028e3dc
add mock and test::transfer for xtokens
gitofdeepanshu Jul 14, 2023
37aa31c
test: add transfer_multiasset
gitofdeepanshu Jul 17, 2023
8117882
add new functions for xtokens
gitofdeepanshu Jul 17, 2023
cd3360e
test: transfer_multi_currencies
gitofdeepanshu Jul 18, 2023
fba5ee3
test: transfer_multi_assets
gitofdeepanshu Jul 19, 2023
c61f391
test: transfer_multiassets_cannot_insert_more_than_max
gitofdeepanshu Jul 19, 2023
7dafadb
test: transfer_multi_currencies_cannot_insert_more_than_max
gitofdeepanshu Jul 19, 2023
910eee0
more tests and fixes
gitofdeepanshu Jul 19, 2023
6a6b941
typo fix
gitofdeepanshu Jul 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.3
xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
orml-xtokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.39", default-features = false }
orml-xcm-support = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.39", default-features = false }
orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.39", default-features = false }

# (native)
polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39" }
Expand Down
220 changes: 220 additions & 0 deletions precompiles/utils/src/bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// This file is part of Astar.

// Copyright 2019-2022 PureStake Inc.
// Copyright (C) 2022-2023 Stake Technologies Pte.Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This file is part of Utils package, originally developed by Purestake Inc.
// Utils package used in Astar Network in terms of GPLv3.
//
// Utils 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.

// Utils 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 Utils. If not, see <http://www.gnu.org/licenses/>.

use super::*;
use alloc::borrow::ToOwned;
pub use alloc::string::String;
use sp_core::{ConstU32, Get};

type ConstU32Max = ConstU32<{ u32::MAX }>;

pub type UnboundedBytes = BoundedBytesString<BytesKind, ConstU32Max>;
pub type BoundedBytes<S> = BoundedBytesString<BytesKind, S>;

trait Kind {
fn signature() -> String;
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BytesKind;

impl Kind for BytesKind {
fn signature() -> String {
String::from("bytes")
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StringKind;

impl Kind for StringKind {
fn signature() -> String {
String::from("string")
}
}

/// The `bytes/string` type of Solidity.
/// It is different from `Vec<u8>` which will be serialized with padding for each `u8` element
/// of the array, while `Bytes` is tightly packed.
#[derive(Debug)]
pub struct BoundedBytesString<K, S> {
data: Vec<u8>,
_phantom: PhantomData<(K, S)>,
}

impl<K: Kind, S: Get<u32>> Clone for BoundedBytesString<K, S> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
_phantom: PhantomData,
}
}
}

impl<K1, S1, K2, S2> PartialEq<BoundedBytesString<K2, S2>> for BoundedBytesString<K1, S1> {
fn eq(&self, other: &BoundedBytesString<K2, S2>) -> bool {
self.data.eq(&other.data)
}
}

impl<K, S> Eq for BoundedBytesString<K, S> {}

impl<K, S: Get<u32>> BoundedBytesString<K, S> {
pub fn as_bytes(&self) -> &[u8] {
&self.data
}

pub fn as_str(&self) -> Result<&str, sp_std::str::Utf8Error> {
sp_std::str::from_utf8(&self.data)
}
}

impl<K: Kind, S: Get<u32>> EvmData for BoundedBytesString<K, S> {
fn read(reader: &mut EvmDataReader) -> EvmResult<Self> {
let mut inner_reader = reader.read_pointer()?;

// Read bytes/string size.
let array_size: usize = inner_reader
.read::<U256>()
.map_err(|_| revert("length, out of bounds"))?
.try_into()
.map_err(|_| revert("length, value too large"))?;

if array_size > S::get() as usize {
return Err(revert("length, value too large").into());
}

// Get valid range over the bytes data.
let range = inner_reader.move_cursor(array_size)?;

let data = inner_reader
.get_input_from_range(range)
.ok_or_else(|| revert(K::signature()))?;

let bytes = Self {
data: data.to_owned(),
_phantom: PhantomData,
};

Ok(bytes)
}

fn write(writer: &mut EvmDataWriter, value: Self) {
let value: Vec<_> = value.into();
let length = value.len();

// Pad the data.
// Leave it as is if a multiple of 32, otherwise pad to next
// multiple or 32.
let chunks = length / 32;
let padded_size = match length % 32 {
0 => chunks * 32,
_ => (chunks + 1) * 32,
};

let mut value = value.to_vec();
value.resize(padded_size, 0);

writer.write_pointer(
EvmDataWriter::new()
.write(U256::from(length))
.write_raw_bytes(&value)
.build(),
);
}

fn has_static_size() -> bool {
false
}
}

// BytesString <=> Vec/&[u8]

impl<K, S> From<BoundedBytesString<K, S>> for Vec<u8> {
fn from(value: BoundedBytesString<K, S>) -> Self {
value.data
}
}

impl<K, S> From<Vec<u8>> for BoundedBytesString<K, S> {
fn from(value: Vec<u8>) -> Self {
Self {
data: value,
_phantom: PhantomData,
}
}
}

impl<K, S> From<&[u8]> for BoundedBytesString<K, S> {
fn from(value: &[u8]) -> Self {
Self {
data: value.to_vec(),
_phantom: PhantomData,
}
}
}

impl<K, S, const N: usize> From<[u8; N]> for BoundedBytesString<K, S> {
fn from(value: [u8; N]) -> Self {
Self {
data: value.to_vec(),
_phantom: PhantomData,
}
}
}

impl<K, S, const N: usize> From<&[u8; N]> for BoundedBytesString<K, S> {
fn from(value: &[u8; N]) -> Self {
Self {
data: value.to_vec(),
_phantom: PhantomData,
}
}
}

// BytesString <=> String/str

impl<K, S> TryFrom<BoundedBytesString<K, S>> for String {
type Error = alloc::string::FromUtf8Error;

fn try_from(value: BoundedBytesString<K, S>) -> Result<Self, Self::Error> {
alloc::string::String::from_utf8(value.data)
}
}

impl<K, S> From<&str> for BoundedBytesString<K, S> {
fn from(value: &str) -> Self {
Self {
data: value.as_bytes().into(),
_phantom: PhantomData,
}
}
}

impl<K, S> From<String> for BoundedBytesString<K, S> {
fn from(value: String) -> Self {
Self {
data: value.as_bytes().into(),
_phantom: PhantomData,
}
}
}
114 changes: 110 additions & 4 deletions precompiles/utils/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
use crate::{revert, EvmResult};

use alloc::borrow::ToOwned;
use core::{any::type_name, ops::Range};
use core::{any::type_name, marker::PhantomData, ops::Range};
use impl_trait_for_tuples::impl_for_tuples;
use sp_core::{H160, H256, U256};
use sp_core::{Get, H160, H256, U256};
use sp_std::{convert::TryInto, vec, vec::Vec};

/// The `address` type of Solidity.
Expand Down Expand Up @@ -175,6 +175,11 @@ impl<'a> EvmDataReader<'a> {
})
}

/// Return Option<&[u8]> from a given range for EvmDataReader
pub fn get_input_from_range(&self, range: Range<usize>) -> Option<&[u8]> {
self.input.get(range)
}

/// Read remaining bytes
pub fn read_till_end(&mut self) -> EvmResult<&[u8]> {
let range = self.move_cursor(self.input.len() - self.cursor)?;
Expand All @@ -190,7 +195,7 @@ impl<'a> EvmDataReader<'a> {
/// 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<Range<usize>> {
pub fn move_cursor(&mut self, len: usize) -> EvmResult<Range<usize>> {
let start = self.cursor;
let end = self
.cursor
Expand Down Expand Up @@ -285,7 +290,7 @@ impl EvmDataWriter {

/// Write arbitrary bytes.
/// Doesn't handle any alignement checks, prefer using `write` instead if possible.
fn write_raw_bytes(mut self, value: &[u8]) -> Self {
pub fn write_raw_bytes(mut self, value: &[u8]) -> Self {
self.data.extend_from_slice(value);
self
}
Expand Down Expand Up @@ -604,3 +609,104 @@ impl EvmData for Bytes {
false
}
}
/// Wrapper around a Vec that provides a max length bound on read.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BoundedVec<T, S> {
inner: Vec<T>,
_phantom: PhantomData<S>,
}

impl<T: EvmData, S: Get<u32>> EvmData for BoundedVec<T, S> {
fn read(reader: &mut EvmDataReader) -> EvmResult<Self> {
let mut inner_reader = reader.read_pointer()?;

let array_size: usize = inner_reader
.read::<U256>()
.map_err(|_| revert("out of bounds: length of array"))?
.try_into()
.map_err(|_| revert("value too large : Array has more than max items allowed"))?;

if array_size > S::get() as usize {
return Err(revert("value too large : Array has more than max items allowed").into());
}

let mut array = vec![];

let mut item_reader = EvmDataReader {
input: inner_reader
.input
.get(32..)
.ok_or_else(|| revert("read out of bounds: array content"))?,
cursor: 0,
};

for _ in 0..array_size {
array.push(item_reader.read()?);
}

Ok(BoundedVec {
inner: array,
_phantom: PhantomData,
})
}

fn write(writer: &mut EvmDataWriter, value: Self) {
let value: Vec<_> = value.into();
let mut inner_writer = EvmDataWriter::new().write(U256::from(value.len()));

for inner in value {
// Any offset in items are relative to the start of the item instead of the
// start of the array. However if there is offseted data it must but appended after
// all items (offsets) are written. We thus need to rely on `compute_offsets` to do
// that, and must store a "shift" to correct the offsets.
let shift = inner_writer.data.len();
let item_writer = EvmDataWriter::new().write(inner);

inner_writer = inner_writer.write_raw_bytes(&item_writer.data);
for mut offset_datum in item_writer.offset_data {
offset_datum.offset_shift += 32;
offset_datum.offset_position += shift;
inner_writer.offset_data.push(offset_datum);
}
}

writer.write_pointer(inner_writer.build());
}

fn has_static_size() -> bool {
false
}
}

impl<T, S> From<Vec<T>> for BoundedVec<T, S> {
fn from(value: Vec<T>) -> Self {
BoundedVec {
inner: value,
_phantom: PhantomData,
}
}
}

impl<T: Clone, S> From<&[T]> for BoundedVec<T, S> {
fn from(value: &[T]) -> Self {
BoundedVec {
inner: value.to_vec(),
_phantom: PhantomData,
}
}
}

impl<T: Clone, S, const N: usize> From<[T; N]> for BoundedVec<T, S> {
fn from(value: [T; N]) -> Self {
BoundedVec {
inner: value.to_vec(),
_phantom: PhantomData,
}
}
}

impl<T, S> From<BoundedVec<T, S>> for Vec<T> {
fn from(value: BoundedVec<T, S>) -> Self {
value.inner
}
}
4 changes: 3 additions & 1 deletion precompiles/utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ use pallet_evm::{GasWeightMapping, Log};
use sp_core::{H160, H256, U256};
use sp_std::{marker::PhantomData, vec, vec::Vec};

mod data;
pub mod bytes;
pub mod data;
pub mod xcm;

pub use data::{Address, Bytes, EvmData, EvmDataReader, EvmDataWriter};
pub use precompile_utils_macro::{generate_function_selector, keccak256};
Expand Down
Loading