Newer
Older
// Copyright 2019-2020 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.
//! External C API to communicate with substrate contracts runtime module.
//!
//! Refer to substrate SRML contract module for more documentation.
use crate::env::{
EnvError,
Result,
use ink_primitives::Key;
/// Returned by the host environment if a contract call trapped.
const TRAP_RETURN_CODE: u32 = 0x0100;
mod sys {
extern "C" {
pub fn ext_instantiate(
init_code_ptr: u32,
init_code_len: u32,
gas: u64,
value_ptr: u32,
value_len: u32,
input_data_ptr: u32,
input_data_len: u32,
) -> u32;
pub fn ext_call(
callee_ptr: u32,
callee_len: u32,
gas: u64,
value_ptr: u32,
value_len: u32,
input_data_ptr: u32,
input_data_len: u32,
) -> u32;
pub fn ext_transfer(
account_id_ptr: u32,
account_id_len: u32,
value_ptr: u32,
value_len: u32,
) -> u32;
pub fn ext_deposit_event(
topics_ptr: u32,
topics_len: u32,
data_ptr: u32,
data_len: u32,
);
pub fn ext_set_storage(key_ptr: u32, value_ptr: u32, value_len: u32);
pub fn ext_clear_storage(key_ptr: u32);
pub fn ext_get_storage(key_ptr: u32) -> u32;
pub fn ext_get_runtime_storage(key_ptr: u32, key_len: u32) -> u32;
pub fn ext_restore_to(
dest_ptr: u32,
dest_len: u32,
code_hash_ptr: u32,
code_hash_len: u32,
rent_allowance_ptr: u32,
rent_allowance_len: u32,
delta_ptr: u32,
delta_count: u32,
);
pub fn ext_terminate(beneficiary_ptr: u32, beneficiary_len: u32) -> !;
pub fn ext_dispatch_call(call_ptr: u32, call_len: u32);
pub fn ext_scratch_size() -> u32;
pub fn ext_scratch_read(dst_ptr: u32, offset: u32, len: u32);
pub fn ext_scratch_write(src_ptr: u32, len: u32);
pub fn ext_caller();
pub fn ext_block_number();
pub fn ext_address();
pub fn ext_balance();
pub fn ext_gas_price();
pub fn ext_gas_left();
pub fn ext_value_transferred();
pub fn ext_now();
pub fn ext_rent_allowance();
pub fn ext_minimum_balance();
pub fn ext_tombstone_deposit();
pub fn ext_set_rent_allowance(value_ptr: u32, value_len: u32);
pub fn ext_random_seed(subject_ptr: u32, subject_len: u32);
pub fn ext_println(str_ptr: u32, str_len: u32);
}
}
pub fn create(
code_hash: &[u8],
gas_limit: u64,
value: &[u8],
create_data: &[u8],
) -> Result<()> {
let ret_code = unsafe {
code_hash.as_ptr() as u32,
code_hash.len() as u32,
gas_limit,
value.as_ptr() as u32,
value.len() as u32,
create_data.as_ptr() as u32,
create_data.len() as u32,
)
};
match ret_code {
0 => Ok(()),
c if c == TRAP_RETURN_CODE => Err(EnvError::ContractInstantiationTrapped),
err if err <= 0xFF => Err(EnvError::ContractInstantiationFailState(err as u8)),
_unknown => panic!("encountered unknown error code upon contract call"),
pub fn call(callee: &[u8], gas_limit: u64, value: &[u8], call_data: &[u8]) -> Result<()> {
let ret_code = unsafe {
sys::ext_call(
callee.as_ptr() as u32,
callee.len() as u32,
gas_limit,
value.as_ptr() as u32,
value.len() as u32,
call_data.as_ptr() as u32,
call_data.len() as u32,
)
};
match ret_code {
0 => Ok(()),
c if c == TRAP_RETURN_CODE => Err(EnvError::ContractInstantiationTrapped),
err if err <= 0xFF => Err(EnvError::ContractInstantiationFailState(err as u8)),
_unknown => panic!("encountered unknown error code upon contract call"),
pub fn transfer(account_id: &[u8], value: &[u8]) -> Result<()> {
let ret_code = unsafe {
sys::ext_transfer(
account_id.as_ptr() as u32,
account_id.len() as u32,
value.as_ptr() as u32,
value.len() as u32,
)
};
match ret_code {
0 => Ok(()),
1 => Err(EnvError::TransferCallFailed),
_unknown => panic!("encountered unknown error code upon transfer"),
}
}
pub fn deposit_event(topics: &[u8], data: &[u8]) {
unsafe {
sys::ext_deposit_event(
topics.as_ptr() as u32,
topics.len() as u32,
data.as_ptr() as u32,
data.len() as u32,
)
}
}
pub fn set_storage(key: &[u8], encoded_value: &[u8]) {
unsafe {
sys::ext_set_storage(
key.as_ptr() as u32,
encoded_value.as_ptr() as u32,
encoded_value.len() as u32,
)
pub fn clear_storage(key: &[u8]) {
unsafe { sys::ext_clear_storage(key.as_ptr() as u32) }
pub fn get_storage(key: &[u8]) -> Result<()> {
let ret_code = unsafe { sys::ext_get_storage(key.as_ptr() as u32) };
match ret_code {
0 => Ok(()),
1 => Err(EnvError::MissingContractStorageEntry),
_unknown => panic!("encountered unexpected return code"),
}
}
pub fn get_runtime_storage(runtime_key: &[u8]) -> Result<()> {
let ret_code = unsafe {
sys::ext_get_runtime_storage(
runtime_key.as_ptr() as u32,
runtime_key.len() as u32,
)
};
match ret_code {
0 => Ok(()),
1 => Err(EnvError::MissingRuntimeStorageEntry),
_unknown => panic!("encountered unsupported return code"),
}
/// Restores a tombstone to the original smart contract.
///
/// # Params
///
/// - `account_id`: Encoded bytes of the `AccountId` of the to-be-restored contract.
/// - `code_hash`: Encoded code hash of the to-be-restored contract.
/// - `rent_allowance`: The encoded rent allowance of the restored contract
/// upon successful restoration.
/// - `filtered_keys`: Storage keys that will be ignored for the tombstone hash
/// match calculation that decide whether the original contract
/// storage and the storage of the restorer contract is equal.
code_hash: &[u8],
rent_allowance: &[u8],
filtered_keys: &[Key],
) {
unsafe {
sys::ext_restore_to(
account_id.as_ptr() as u32,
account_id.len() as u32,
code_hash.as_ptr() as u32,
code_hash.len() as u32,
rent_allowance.as_ptr() as u32,
rent_allowance.len() as u32,
filtered_keys.as_ptr() as u32,
filtered_keys.len() as u32,
)
}
}
pub fn terminate(beneficiary: &[u8]) -> ! {
unsafe { sys::ext_terminate(beneficiary.as_ptr() as u32, beneficiary.len() as u32) }
}
pub fn dispatch_call(call: &[u8]) {
unsafe { sys::ext_dispatch_call(call.as_ptr() as u32, call.len() as u32) }
}
pub fn scratch_size() -> usize {
(unsafe { sys::ext_scratch_size() }) as usize
}
pub fn scratch_read(dest: &mut [u8], offset: u32) {
unsafe { sys::ext_scratch_read(dest.as_mut_ptr() as u32, offset, dest.len() as u32) }
pub fn scratch_write(src: &[u8]) {
unsafe { sys::ext_scratch_write(src.as_ptr() as u32, src.len() as u32) }
}
macro_rules! impl_ext_wrapper_for {
( $( ($name:ident => $ext_name:ident), )* ) => {
$(
pub fn $name() {
unsafe {
sys::$ext_name()
}
}
)*
}
}
impl_ext_wrapper_for! {
(caller => ext_caller),
(block_number => ext_block_number),
(address => ext_address),
(balance => ext_balance),
(gas_price => ext_gas_price),
(gas_left => ext_gas_left),
(value_transferred => ext_value_transferred),
(now => ext_now),
(rent_allowance => ext_rent_allowance),
(minimum_balance => ext_minimum_balance),
(tombstone_deposit => ext_tombstone_deposit),
pub fn set_rent_allowance(value: &[u8]) {
unsafe { sys::ext_set_rent_allowance(value.as_ptr() as u32, value.len() as u32) }
}
pub fn random_seed(subject: &[u8]) {
unsafe { sys::ext_random_seed(subject.as_ptr() as u32, subject.len() as u32) }
}
pub fn println(content: &str) {
let bytes = content.as_bytes();
unsafe { sys::ext_println(bytes.as_ptr() as u32, bytes.len() as u32) }
}