Skip to content
Snippets Groups Projects
riscv64.rs 17.1 KiB
Newer Older
// Copyright (C) Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(unused_variables)]

use crate::{
	host::{CallFlags, HostFn, HostFnImpl, Result, StorageFlags},
use pallet_revive_proc_macro::unstable_hostfn;

mod sys {
	use crate::ReturnCode;

	#[polkavm_derive::polkavm_define_abi]
	mod abi {}

	impl abi::FromHost for ReturnCode {
		type Regs = (u64,);

		fn from_host((a0,): Self::Regs) -> Self {
			ReturnCode(a0 as _)
		}
	}

	#[polkavm_derive::polkavm_import(abi = self::abi)]
	extern "C" {
		pub fn set_storage(
			flags: u32,
			key_ptr: *const u8,
			key_len: u32,
			value_ptr: *const u8,
			value_len: u32,
		) -> ReturnCode;
		pub fn clear_storage(flags: u32, key_ptr: *const u8, key_len: u32) -> ReturnCode;
		pub fn get_storage(
			flags: u32,
			key_ptr: *const u8,
			key_len: u32,
			out_ptr: *mut u8,
			out_len_ptr: *mut u32,
		) -> ReturnCode;
		pub fn contains_storage(flags: u32, key_ptr: *const u8, key_len: u32) -> ReturnCode;
		pub fn take_storage(
			flags: u32,
			key_ptr: *const u8,
			key_len: u32,
			out_ptr: *mut u8,
			out_len_ptr: *mut u32,
		) -> ReturnCode;
		pub fn call(
			flags_and_callee: u64,
			ref_time_limit: u64,
			proof_size_limit: u64,
			deposit_and_value: u64,
			input_data: u64,
			output_data: u64,
		) -> ReturnCode;
		pub fn delegate_call(
			flags_and_callee: u64,
			ref_time_limit: u64,
			proof_size_limit: u64,
			deposit_ptr: *const u8,
			input_data: u64,
			output_data: u64,
		) -> ReturnCode;
		pub fn instantiate(
			ref_time_limit: u64,
			proof_size_limit: u64,
			deposit_and_value: u64,
			input_data: u64,
			output_data: u64,
			address_and_salt: u64,
		) -> ReturnCode;
		pub fn terminate(beneficiary_ptr: *const u8);
		pub fn call_data_copy(out_ptr: *mut u8, out_len: u32, offset: u32);
		pub fn call_data_load(out_ptr: *mut u8, offset: u32);
		pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32);
		pub fn caller(out_ptr: *mut u8);
		pub fn origin(out_ptr: *mut u8);
		pub fn is_contract(account_ptr: *const u8) -> ReturnCode;
		pub fn to_account_id(address_ptr: *const u8, out_ptr: *mut u8);
		pub fn code_hash(address_ptr: *const u8, out_ptr: *mut u8);
		pub fn code_size(address_ptr: *const u8) -> u64;
		pub fn own_code_hash(out_ptr: *mut u8);
		pub fn caller_is_origin() -> ReturnCode;
		pub fn caller_is_root() -> ReturnCode;
		pub fn address(out_ptr: *mut u8);
		pub fn weight_to_fee(ref_time: u64, proof_size: u64, out_ptr: *mut u8);
		pub fn weight_left(out_ptr: *mut u8, out_len_ptr: *mut u32);
		pub fn ref_time_left() -> u64;
		pub fn get_immutable_data(out_ptr: *mut u8, out_len_ptr: *mut u32);
		pub fn set_immutable_data(ptr: *const u8, len: u32);
		pub fn balance(out_ptr: *mut u8);
		pub fn balance_of(addr_ptr: *const u8, out_ptr: *mut u8);
		pub fn chain_id(out_ptr: *mut u8);
		pub fn value_transferred(out_ptr: *mut u8);
		pub fn now(out_ptr: *mut u8);
		pub fn gas_limit() -> u64;
		pub fn minimum_balance(out_ptr: *mut u8);
		pub fn deposit_event(
			topics_ptr: *const [u8; 32],
			num_topic: u32,
			data_ptr: *const u8,
			data_len: u32,
		);
		pub fn gas_price() -> u64;
		pub fn base_fee(out_ptr: *mut u8);
		pub fn block_number(out_ptr: *mut u8);
		pub fn block_hash(block_number_ptr: *const u8, out_ptr: *mut u8);
		pub fn block_author(out_ptr: *mut u8);
		pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8);
		pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8);
		pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8);
		pub fn hash_blake2_128(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8);
		pub fn call_chain_extension(
			id: u32,
			input_ptr: *const u8,
			input_len: u32,
			out_ptr: *mut u8,
			out_len_ptr: *mut u32,
		) -> ReturnCode;
		pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> ReturnCode;
		pub fn sr25519_verify(
			signature_ptr: *const u8,
			pub_key_ptr: *const u8,
			message_len: u32,
			message_ptr: *const u8,
		) -> ReturnCode;
		pub fn set_code_hash(code_hash_ptr: *const u8);
		pub fn ecdsa_to_eth_address(key_ptr: *const u8, out_ptr: *mut u8) -> ReturnCode;
		pub fn instantiation_nonce() -> u64;
		pub fn xcm_execute(msg_ptr: *const u8, msg_len: u32) -> ReturnCode;
		pub fn xcm_send(
			dest_ptr: *const u8,
			dest_len: *const u8,
			msg_ptr: *const u8,
			msg_len: u32,
			out_ptr: *mut u8,
		) -> ReturnCode;
		pub fn new_query(
			responder_ptr: *const u8,
			responder_len: *const u8,
			maybe_notify_ptr: *const u8,
			maybe_notify_len: u32,
			timeout_ptr: *const u8,
			out_ptr: *mut u8,
		) -> ReturnCode;
		pub fn return_data_copy(out_ptr: *mut u8, out_len_ptr: *mut u32, offset: u32);
	}
}

#[inline(always)]
fn extract_from_slice(output: &mut &mut [u8], new_len: usize) {
	debug_assert!(new_len <= output.len());
	let tmp = core::mem::take(output);
	*output = &mut tmp[..new_len];
}

#[inline(always)]
fn ptr_len_or_sentinel(data: &mut Option<&mut &mut [u8]>) -> (*mut u8, u32) {
	match data {
		Some(ref mut data) => (data.as_mut_ptr(), data.len() as _),
		None => (crate::SENTINEL as _, 0),
	}
}

#[inline(always)]
fn ptr_or_sentinel(data: &Option<&[u8; 32]>) -> *const u8 {
	match data {
		Some(ref data) => data.as_ptr(),
		None => crate::SENTINEL as _,
	}
}

impl HostFn for HostFnImpl {
	fn instantiate(
		ref_time_limit: u64,
		proof_size_limit: u64,
		mut address: Option<&mut [u8; 20]>,
		mut output: Option<&mut &mut [u8]>,
		let address = match address {
			Some(ref mut data) => data.as_mut_ptr(),
			None => crate::SENTINEL as _,
		};
		let (output_ptr, mut output_len_ptr) = ptr_len_or_sentinel(&mut output);
		let deposit_limit_ptr = deposit_limit.as_ptr();
		let salt_ptr = ptr_or_sentinel(&salt);
		let deposit_and_value = pack_hi_lo(deposit_limit_ptr as _, value.as_ptr() as _);
		let address_and_salt = pack_hi_lo(address as _, salt_ptr as _);
		let input_data = pack_hi_lo(input.len() as _, input.as_ptr() as _);
		let output_data = pack_hi_lo(&mut output_len_ptr as *mut _ as _, output_ptr as _);

		let ret_code = unsafe {
			sys::instantiate(
				ref_time_limit,
				proof_size_limit,
				deposit_and_value,
				input_data,
				output_data,
				address_and_salt,
			)
		};

		if let Some(ref mut output) = output {
			extract_from_slice(output, output_len_ptr as usize);
		}

		ret_code.into()
	}

	fn call(
		flags: CallFlags,
		ref_time_limit: u64,
		proof_size_limit: u64,
		input: &[u8],
		mut output: Option<&mut &mut [u8]>,
	) -> Result {
		let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
		let deposit_limit_ptr = deposit_limit.as_ptr();
		let flags_and_callee = pack_hi_lo(flags.bits(), callee.as_ptr() as _);
		let deposit_and_value = pack_hi_lo(deposit_limit_ptr as _, value.as_ptr() as _);
		let input_data = pack_hi_lo(input.len() as _, input.as_ptr() as _);
		let output_data = pack_hi_lo(&mut output_len as *mut _ as _, output_ptr as _);

		let ret_code = unsafe {
			sys::call(
				flags_and_callee,
				ref_time_limit,
				proof_size_limit,
				deposit_and_value,
				input_data,
				output_data,
			)
		};

		if let Some(ref mut output) = output {
			extract_from_slice(output, output_len as usize);
		}

		ret_code.into()
	}

	fn delegate_call(
		flags: CallFlags,
		address: &[u8; 20],
		ref_time_limit: u64,
		proof_size_limit: u64,
		input: &[u8],
		mut output: Option<&mut &mut [u8]>,
	) -> Result {
		let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
		let deposit_limit_ptr = deposit_limit.as_ptr();
		let flags_and_callee = pack_hi_lo(flags.bits(), address.as_ptr() as u32);
		let input_data = pack_hi_lo(input.len() as u32, input.as_ptr() as u32);
		let output_data = pack_hi_lo(&mut output_len as *mut _ as u32, output_ptr as u32);

		let ret_code = unsafe {
			sys::delegate_call(
				flags_and_callee,
				ref_time_limit,
				proof_size_limit,
				deposit_limit_ptr as _,
				input_data,
				output_data,
			)
		};
		if let Some(ref mut output) = output {
			extract_from_slice(output, output_len as usize);
		}

		ret_code.into()
	}

	fn deposit_event(topics: &[[u8; 32]], data: &[u8]) {
		unsafe {
			sys::deposit_event(
				topics.as_ptr(),
				topics.len() as u32,
				data.as_ptr(),
				data.len() as u32,
			)
		}
	}

	fn set_storage(flags: StorageFlags, key: &[u8], encoded_value: &[u8]) -> Option<u32> {
		let ret_code = unsafe {
			sys::set_storage(
				flags.bits(),
				key.as_ptr(),
				key.len() as u32,
				encoded_value.as_ptr(),
				encoded_value.len() as u32,
			)
		};
		ret_code.into()
	}

	fn get_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result {
		let mut output_len = output.len() as u32;
		let ret_code = {
			unsafe {
				sys::get_storage(
					flags.bits(),
					key.as_ptr(),
					key.len() as u32,
					output.as_mut_ptr(),
					&mut output_len,
				)
			}
		};
		extract_from_slice(output, output_len as usize);
		ret_code.into()
	}

	fn call_data_load(out_ptr: &mut [u8; 32], offset: u32) {
		unsafe { sys::call_data_load(out_ptr.as_mut_ptr(), offset) };
	fn gas_limit() -> u64 {
		unsafe { sys::gas_limit() }
	}

	fn call_data_size() -> u64 {
		unsafe { sys::call_data_size() }
	}

	fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! {
		unsafe { sys::seal_return(flags.bits(), return_value.as_ptr(), return_value.len() as u32) }
		panic!("seal_return does not return");
	}

	fn gas_price() -> u64 {
		unsafe { sys::gas_price() }
	}

	fn base_fee(output: &mut [u8; 32]) {
		unsafe { sys::base_fee(output.as_mut_ptr()) }
	}

	fn balance(output: &mut [u8; 32]) {
		unsafe { sys::balance(output.as_mut_ptr()) }
	}

	fn value_transferred(output: &mut [u8; 32]) {
		unsafe { sys::value_transferred(output.as_mut_ptr()) }
	}

	fn now(output: &mut [u8; 32]) {
		unsafe { sys::now(output.as_mut_ptr()) }
	}

	fn chain_id(output: &mut [u8; 32]) {
		unsafe { sys::chain_id(output.as_mut_ptr()) }
	fn address(output: &mut [u8; 20]) {
		unsafe { sys::address(output.as_mut_ptr()) }
	}

	fn caller(output: &mut [u8; 20]) {
		unsafe { sys::caller(output.as_mut_ptr()) }
	}

	fn origin(output: &mut [u8; 20]) {
		unsafe { sys::origin(output.as_mut_ptr()) }
	}

	fn block_number(output: &mut [u8; 32]) {
		unsafe { sys::block_number(output.as_mut_ptr()) }
	fn block_author(output: &mut [u8; 20]) {
		unsafe { sys::block_author(output.as_mut_ptr()) }
	}

	fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut [u8; 32]) {
		unsafe { sys::weight_to_fee(ref_time_limit, proof_size_limit, output.as_mut_ptr()) };
	}

	fn hash_keccak_256(input: &[u8], output: &mut [u8; 32]) {
		unsafe { sys::hash_keccak_256(input.as_ptr(), input.len() as u32, output.as_mut_ptr()) }
	}

	fn get_immutable_data(output: &mut &mut [u8]) {
		let mut output_len = output.len() as u32;
		unsafe { sys::get_immutable_data(output.as_mut_ptr(), &mut output_len) };
		extract_from_slice(output, output_len as usize);
	}

	fn set_immutable_data(data: &[u8]) {
		unsafe { sys::set_immutable_data(data.as_ptr(), data.len() as u32) }
	}

	fn balance_of(address: &[u8; 20], output: &mut [u8; 32]) {
		unsafe { sys::balance_of(address.as_ptr(), output.as_mut_ptr()) };
	}

	fn code_hash(address: &[u8; 20], output: &mut [u8; 32]) {
		unsafe { sys::code_hash(address.as_ptr(), output.as_mut_ptr()) }
	}

	fn code_size(address: &[u8; 20]) -> u64 {
		unsafe { sys::code_size(address.as_ptr()) }
	fn return_data_size() -> u64 {
		unsafe { sys::return_data_size() }
	}

	fn return_data_copy(output: &mut &mut [u8], offset: u32) {
		let mut output_len = output.len() as u32;
		{
			unsafe { sys::return_data_copy(output.as_mut_ptr(), &mut output_len, offset) };
		}
		extract_from_slice(output, output_len as usize);
	}

	fn ref_time_left() -> u64 {
		unsafe { sys::ref_time_left() }
	}

	#[unstable_hostfn]
	fn to_account_id(address: &[u8; 20], output: &mut [u8]) {
		unsafe { sys::to_account_id(address.as_ptr(), output.as_mut_ptr()) }
	}

	#[unstable_hostfn]
	fn block_hash(block_number_ptr: &[u8; 32], output: &mut [u8; 32]) {
		unsafe { sys::block_hash(block_number_ptr.as_ptr(), output.as_mut_ptr()) };
	#[unstable_hostfn]
	fn call_chain_extension(func_id: u32, input: &[u8], mut output: Option<&mut &mut [u8]>) -> u32 {
		let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
		let ret_code = {
			unsafe {
				sys::call_chain_extension(
					func_id,
					input.as_ptr(),
					input.len() as u32,
					output_ptr,
					&mut output_len,
				)
			}
		};

		if let Some(ref mut output) = output {
			extract_from_slice(output, output_len as usize);
		}
		ret_code.into_u32()
	}

	fn call_data_copy(output: &mut [u8], offset: u32) {
		let len = output.len() as u32;
		unsafe { sys::call_data_copy(output.as_mut_ptr(), len, offset) };
	}

	#[unstable_hostfn]
	fn call_runtime(call: &[u8]) -> Result {
		let ret_code = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) };
		ret_code.into()
	#[unstable_hostfn]
	fn caller_is_origin() -> bool {
		let ret_val = unsafe { sys::caller_is_origin() };
		ret_val.into_bool()
	#[unstable_hostfn]
	fn caller_is_root() -> bool {
		let ret_val = unsafe { sys::caller_is_root() };
		ret_val.into_bool()
	#[unstable_hostfn]
	fn clear_storage(flags: StorageFlags, key: &[u8]) -> Option<u32> {
		let ret_code = unsafe { sys::clear_storage(flags.bits(), key.as_ptr(), key.len() as u32) };
	#[unstable_hostfn]
	fn contains_storage(flags: StorageFlags, key: &[u8]) -> Option<u32> {
		let ret_code =
			unsafe { sys::contains_storage(flags.bits(), key.as_ptr(), key.len() as u32) };
		ret_code.into()
	#[unstable_hostfn]
	fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result {
		let ret_code = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) };
		ret_code.into()
	}

	#[unstable_hostfn]
	fn hash_sha2_256(input: &[u8], output: &mut [u8; 32]) {
		unsafe { sys::hash_sha2_256(input.as_ptr(), input.len() as u32, output.as_mut_ptr()) }
	}

	#[unstable_hostfn]
	fn hash_blake2_256(input: &[u8], output: &mut [u8; 32]) {
		unsafe { sys::hash_blake2_256(input.as_ptr(), input.len() as u32, output.as_mut_ptr()) }
	}
	#[unstable_hostfn]
	fn hash_blake2_128(input: &[u8], output: &mut [u8; 16]) {
		unsafe { sys::hash_blake2_128(input.as_ptr(), input.len() as u32, output.as_mut_ptr()) }
	}

	#[unstable_hostfn]
	fn is_contract(address: &[u8; 20]) -> bool {
		let ret_val = unsafe { sys::is_contract(address.as_ptr()) };
	#[unstable_hostfn]
	fn minimum_balance(output: &mut [u8; 32]) {
		unsafe { sys::minimum_balance(output.as_mut_ptr()) }
	}

	#[unstable_hostfn]
	fn own_code_hash(output: &mut [u8; 32]) {
		unsafe { sys::own_code_hash(output.as_mut_ptr()) }
	#[unstable_hostfn]
	fn set_code_hash(code_hash: &[u8; 32]) {
		unsafe { sys::set_code_hash(code_hash.as_ptr()) }
	#[unstable_hostfn]
	fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result {
		let ret_code = unsafe {
			sys::sr25519_verify(
				signature.as_ptr(),
				pub_key.as_ptr(),
				message.len() as u32,
				message.as_ptr(),
			)
		};
		ret_code.into()
	#[unstable_hostfn]
	fn take_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result {
		let mut output_len = output.len() as u32;
		let ret_code = {
			unsafe {
				sys::take_storage(
					flags.bits(),
					key.as_ptr(),
					key.len() as u32,
					output.as_mut_ptr(),
					&mut output_len,
				)
			}
		};
		extract_from_slice(output, output_len as usize);
		ret_code.into()
	#[unstable_hostfn]
	fn terminate(beneficiary: &[u8; 20]) -> ! {
		unsafe { sys::terminate(beneficiary.as_ptr()) }
		panic!("terminate does not return");
	#[unstable_hostfn]
	fn weight_left(output: &mut &mut [u8]) {
		let mut output_len = output.len() as u32;
		unsafe { sys::weight_left(output.as_mut_ptr(), &mut output_len) }
		extract_from_slice(output, output_len as usize)
	}

	#[unstable_hostfn]
	fn xcm_execute(msg: &[u8]) -> Result {
		let ret_code = unsafe { sys::xcm_execute(msg.as_ptr(), msg.len() as _) };
		ret_code.into()
	}

	#[unstable_hostfn]
	fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result {
		let ret_code = unsafe {
			sys::xcm_send(
				dest.as_ptr(),
				dest.len() as _,
				msg.as_ptr(),
				msg.len() as _,
				output.as_mut_ptr(),
			)

	#[unstable_hostfn]
	fn new_query(responder: &[u8], maybe_notify: &[u8], timeout: &[u8], output: &mut [u8; 32]) -> Result {
		let ret_code = unsafe {
			sys::new_query(
				responder.as_ptr(),
				responder.len() as _,
				maybe_notify.as_ptr(),
				maybe_notify.len() as _,
				timeout.as_ptr(),
				output.as_mut_ptr(),
			)
		};
		ret_code.into()
	}