Unverified Commit fa906cad authored by Hero Bird's avatar Hero Bird Committed by GitHub

Major clean up of ink! codebase (#99)

* [ink] Update rust-toolchain from 2019-04-20 to 2019-05-21

* [core,lang] Update & fix Cargo information

* [chores] Update rustfmt version to the current toolchain

* [core] Clean up, fix some warnings

* [core] Remove unneeded dependency

* [core] Ignore return value of Key ops

Fixes a warning

* [core,model] Adjust warning level

* [lang] Minor rustfmt run

* [ink] Add topics to events

For now, ink! will simply leave topics slice empty.

* [core] Add ext_gas_left, ext_gas_price and ext_value_transferred bindings

* [core] Clean up some code by dedup

* [core] Further code clean up

* [core] Simplify SrmlEnv::load impl
parent 1748b51a
......@@ -51,7 +51,7 @@ use_field_init_shorthand = true # changed
force_explicit_abi = true
condense_wildcard_suffixes = false
color = "Auto"
required_version = "1.2.1"
required_version = "1.2.2"
unstable_features = true # changed
disable_all_formatting = false
skip_children = false
......
[package]
name = "ink_core"
version = "0.1.0"
authors = ["Robin Freyler <robin@parity.io>", "Parity Technologies <admin@parity.io>"]
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0"
readme = "README.md"
# repository = "https://github.com/robbepop/substrate-contract"
# homepage = "https://github.com/robbepop/substrate-contract"
# documentation = "https://robbepop.github.io/pwasm-abi/substrate-contract/"
repository = "https://github.com/paritytech/ink"
documentation = "https://github.com/paritytech/ink/wiki"
homepage = "https://www.parity.io/"
description = "[pDSL: Parity eDSL] Rust based eDSL for writing smart contracts for Substrate"
description = "[ink!] Rust based eDSL for writing smart contracts for Substrate"
keywords = ["wasm", "parity", "webassembly", "blockchain", "edsl"]
categories = ["no-std", "embedded"]
......@@ -20,7 +20,6 @@ include = ["/Cargo.toml", "src/**/*.rs", "/README.md", "/LICENSE"]
[dependencies]
ink_utils = { path = "../utils/" }
parity-codec = { version = "3.2", default-features = false, features = ["derive", "full"] }
log = "0.4"
wee_alloc = { version = "0.4", default-features = false }
[features]
......
......@@ -67,6 +67,21 @@ pub fn now() -> Moment {
ContractEnv::now()
}
/// Returns the current gas price.
pub fn gas_price() -> Balance {
ContractEnv::gas_price()
}
/// Returns the amount of gas left for the contract execution.
pub fn gas_left() -> Balance {
ContractEnv::gas_left()
}
/// Returns the transferred value.
pub fn value_transferred() -> Balance {
ContractEnv::value_transferred()
}
/// Returns the current smart contract exection back to the caller
/// and return the given encoded value.
///
......@@ -92,8 +107,8 @@ pub fn println(content: &str) {
}
/// Deposits raw event data through the Contracts module.
pub fn deposit_raw_event(data: &[u8]) {
ContractEnv::deposit_raw_event(data)
pub fn deposit_raw_event(topics: &[Hash], data: &[u8]) {
ContractEnv::deposit_raw_event(topics, data)
}
/// Stores the given value under the specified key in the contract storage.
......
......@@ -28,10 +28,7 @@ use crate::{
storage::Key,
};
use core::{
convert::{
TryFrom,
TryInto,
},
convert::TryFrom,
marker::PhantomData,
};
use parity_codec::Decode;
......@@ -79,17 +76,10 @@ where
/// Loads the value stored at the given key if any.
unsafe fn load(key: Key) -> Option<Vec<u8>> {
const SUCCESS: u32 = 0;
let result = sys::ext_get_storage(key.as_bytes().as_ptr() as u32);
if result != SUCCESS {
return None
}
let size = sys::ext_scratch_size();
let mut value = Vec::new();
if size > 0 {
value.resize(size as usize, 0);
sys::ext_scratch_copy(value.as_mut_ptr() as u32, 0, size);
if sys::ext_get_storage(key.as_bytes().as_ptr() as u32) == SUCCESS {
return Some(Self::read_scratch_buffer())
}
Some(value)
None
}
}
......@@ -108,37 +98,29 @@ impl<T: EnvTypes> SrmlEnv<T> {
}
}
macro_rules! impl_getters_for_srml_env {
( $( ($name:ident, $ext_name:ident, $ret_type:ty) ),* ) => {
$(
fn $name() -> $ret_type {
unsafe { sys::$ext_name() };
Decode::decode(&mut &Self::read_scratch_buffer()[..])
.ok_or(concat!(
stringify!($name), " received an incorrectly sized buffer from SRML"
))
.expect(concat!(
stringify!($name), " expects to receive a correctly sized buffer"
))
}
)*
}
}
impl<T> Env for SrmlEnv<T>
where
T: EnvTypes,
<T as EnvTypes>::AccountId: for<'a> TryFrom<&'a [u8]>,
<T as EnvTypes>::Hash: for<'a> TryFrom<&'a [u8]>,
{
fn address() -> <Self as EnvTypes>::AccountId {
unsafe { sys::ext_address() };
Self::read_scratch_buffer()
.as_slice()
.try_into()
.map_err(|_| "address received an incorrectly sized buffer from SRML")
.expect("we expect to always receive a correctly sized buffer here")
}
fn balance() -> <Self as EnvTypes>::Balance {
unsafe { sys::ext_balance() };
Decode::decode(&mut &Self::read_scratch_buffer()[..])
.ok_or("balance received an incorrectly sized buffer from SRML")
.expect("we expect to always receive a correctly sized buffer here")
}
fn caller() -> <Self as EnvTypes>::AccountId {
unsafe { sys::ext_caller() };
Self::read_scratch_buffer()
.as_slice()
.try_into()
.map_err(|_| "caller received an incorrectly sized buffer from SRML")
.expect("we expect to always receive a correctly sized buffer here")
}
fn input() -> Vec<u8> {
let size = unsafe { sys::ext_input_size() };
if size == 0 {
......@@ -153,21 +135,16 @@ where
}
}
fn random_seed() -> <Self as EnvTypes>::Hash {
unsafe { sys::ext_random_seed() };
Self::read_scratch_buffer()
.as_slice()
.try_into()
.map_err(|_| "random_seed received an incorrectly sized buffer from SRML")
.expect("we expect to always receive a correctly sized buffer here")
}
fn now() -> <Self as EnvTypes>::Moment {
unsafe { sys::ext_now() };
Decode::decode(&mut &Self::read_scratch_buffer()[..])
.ok_or("now received an incorrectly sized buffer from SRML")
.expect("we expect to receive a correctly sized buffer here")
}
impl_getters_for_srml_env!(
(address, ext_address, <Self as EnvTypes>::AccountId),
(balance, ext_balance, <Self as EnvTypes>::Balance),
(caller, ext_caller, <Self as EnvTypes>::AccountId),
(random_seed, ext_random_seed, <Self as EnvTypes>::Hash),
(now, ext_now, <Self as EnvTypes>::Moment),
(gas_price, ext_gas_price, <Self as EnvTypes>::Balance),
(gas_left, ext_gas_left, <Self as EnvTypes>::Balance),
(value_transferred, ext_value_transferred, <Self as EnvTypes>::Balance)
);
unsafe fn r#return(data: &[u8]) -> ! {
sys::ext_return(data.as_ptr() as u32, data.len() as u32);
......@@ -177,7 +154,14 @@ where
unsafe { sys::ext_println(content.as_ptr() as u32, content.len() as u32) }
}
fn deposit_raw_event(data: &[u8]) {
unsafe { sys::ext_deposit_event(data.as_ptr() as u32, data.len() as u32) }
fn deposit_raw_event(topics: &[<Self as EnvTypes>::Hash], 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,
)
}
}
}
......@@ -55,7 +55,12 @@ extern "C" {
pub fn ext_println(str_ptr: u32, str_len: u32);
/// Deposits raw event data through the Contracts module.
pub fn ext_deposit_event(data_ptr: u32, data_len: u32);
pub fn ext_deposit_event(
topics_ptr: u32,
topics_len: u32,
data_ptr: u32,
data_len: u32,
);
/// Writes the contents of the buffer at `value_ptr` into the
/// storage slot associated with the given key or clears the
......@@ -90,29 +95,29 @@ extern "C" {
/// Stores the address of the current contract into the scratch buffer.
pub fn ext_address();
// Stores the gas price for the current transaction into the scratch buffer.
//
// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten.
/// Stores the balance of the current account into the scratch buffer.
///
/// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten.
pub fn ext_balance();
/// Stores the gas price for the current transaction into the scratch buffer.
///
/// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten.
pub fn ext_gas_price();
// Stores the amount of gas left into the scratch buffer.
//
// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten.
/// Stores the amount of gas left into the scratch buffer.
///
/// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten.
pub fn ext_gas_left();
// Stores the balance of the current account into the scratch buffer.
//
// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten.
pub fn ext_balance();
// Stores the value transferred along with this call or as endowment into the scratch buffer.
//
// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten.
/// Stores the value transferred along with this call or as endowment into the scratch buffer.
///
/// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten.
pub fn ext_value_transferred();
// Load the latest block RNG seed into the scratch buffer.
/// Load the latest block RNG seed into the scratch buffer.
pub fn ext_random_seed();
// Load the latest block timestamp into the scratch buffer.
/// Load the latest block timestamp into the scratch buffer.
pub fn ext_now();
}
......@@ -26,13 +26,14 @@ use parity_codec::{
};
/// The SRML fundamental types.
pub struct DefaultSrmlTypes;
#[allow(unused)]
pub enum DefaultSrmlTypes {}
impl EnvTypes for DefaultSrmlTypes {
type AccountId = self::AccountId;
type Balance = self::Balance;
type Hash = self::Hash;
type Moment = self::Moment;
type AccountId = AccountId;
type Balance = Balance;
type Hash = Hash;
type Moment = Moment;
}
/// The default SRML address type.
......
......@@ -16,7 +16,10 @@
use super::*;
use crate::{
env::srml,
env::{
self,
srml,
},
memory::collections::hash_map::{
Entry,
HashMap,
......@@ -31,12 +34,15 @@ use std::convert::TryFrom;
/// A wrapper for the generic bytearray used for data in contract events.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EventData(Vec<u8>);
pub struct EventData {
topics: Vec<env::Hash>,
data: Vec<u8>,
}
impl EventData {
/// Returns the uninterpreted bytes of the emitted event.
fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
fn data_as_bytes(&self) -> &[u8] {
self.data.as_slice()
}
}
......@@ -158,6 +164,12 @@ pub struct TestEnvData {
total_writes: u64,
/// Deposited events of the contract invocation.
events: Vec<EventData>,
/// The current gas price.
gas_price: srml::Balance,
/// The remaining gas.
gas_left: srml::Balance,
/// The total transferred value.
value_transferred: srml::Balance,
}
impl Default for TestEnvData {
......@@ -174,6 +186,9 @@ impl Default for TestEnvData {
total_reads: Cell::new(0),
total_writes: 0,
events: Vec::new(),
gas_price: 0,
gas_left: 0,
value_transferred: 0,
}
}
}
......@@ -250,8 +265,11 @@ impl TestEnvData {
}
/// Appends new event data to the end of the bytearray.
pub fn add_event(&mut self, event_data: &[u8]) {
let new_event = EventData(event_data.to_vec());
pub fn add_event(&mut self, topics: &[env::Hash], event_data: &[u8]) {
let new_event = EventData {
topics: topics.to_vec(),
data: event_data.to_vec(),
};
self.events.push(new_event);
}
......@@ -267,7 +285,9 @@ impl TestEnvData {
/// Returns an iterator over all emitted events.
pub fn emitted_events(&self) -> impl Iterator<Item = &[u8]> {
self.events.iter().map(|event_data| event_data.as_bytes())
self.events
.iter()
.map(|event_data| event_data.data_as_bytes())
}
}
......@@ -287,22 +307,18 @@ impl TestEnvData {
/// same data as was expected upon invocation.
const FAILURE: i32 = -1;
/// Returns the address of the contract.
pub fn address(&self) -> srml::AccountId {
self.address
}
/// Returns the balance of the contract.
pub fn balance(&self) -> srml::Balance {
self.balance
}
/// Returns the caller of the contract invocation.
pub fn caller(&self) -> srml::AccountId {
self.caller
}
/// Stores the given value under the given key in the contract storage.
pub fn store(&mut self, key: Key, value: &[u8]) {
self.inc_total_writes();
match self.storage.entry(key) {
......@@ -313,41 +329,41 @@ impl TestEnvData {
}
}
/// Clears the value under the given key in the contract storage.
pub fn clear(&mut self, key: Key) {
// Storage clears count as storage write.
self.inc_total_writes();
self.storage.remove(&key);
}
/// Returns the value under the given key in the contract storage if any.
pub fn load(&self, key: Key) -> Option<Vec<u8>> {
self.inc_total_reads();
self.storage.get(&key).map(|loaded| loaded.read())
}
/// Returns the input data for the contract invocation.
pub fn input(&self) -> Vec<u8> {
self.input.clone()
}
/// Returns the random seed for the contract invocation.
pub fn random_seed(&self) -> srml::Hash {
self.random_seed
}
/// Returns the timestamp for the contract invocation.
pub fn now(&self) -> srml::Moment {
self.now
}
/// Returns the data to the internal caller.
///
/// # Note
///
/// This exits the current process (contract invocation)
/// with a return code that is successful if the returned
/// data matches the expected return data.
pub fn gas_price(&self) -> srml::Balance {
self.gas_price
}
pub fn gas_left(&self) -> srml::Balance {
self.gas_left
}
pub fn value_transferred(&self) -> srml::Balance {
self.value_transferred
}
pub fn r#return(&self, data: &[u8]) -> ! {
let expected_bytes = self.expected_return.clone();
let exit_code = if expected_bytes == data {
......@@ -358,14 +374,12 @@ impl TestEnvData {
std::process::exit(exit_code)
}
/// Prints the given content.
pub fn println(&self, content: &str) {
println!("{}", content)
}
/// Deposits raw event data through the Contracts module.
pub fn deposit_raw_event(&mut self, data: &[u8]) {
self.add_event(&data);
pub fn deposit_raw_event(&mut self, topics: &[env::Hash], data: &[u8]) {
self.add_event(topics, data);
}
}
......@@ -442,8 +456,7 @@ impl TestEnv {
/// Sets the timestamp for the next contract invocation.
pub fn set_now(timestamp: srml::Moment) {
TEST_ENV_DATA
.with(|test_env| test_env.borrow_mut().set_now(timestamp))
TEST_ENV_DATA.with(|test_env| test_env.borrow_mut().set_now(timestamp))
}
/// Returns an iterator over all emitted events.
......@@ -458,8 +471,6 @@ impl TestEnv {
}
}
const TEST_ENV_LOG_TARGET: &str = "test-env";
impl EnvTypes for TestEnv {
type AccountId = srml::AccountId;
type Balance = srml::Balance;
......@@ -469,75 +480,46 @@ impl EnvTypes for TestEnv {
impl EnvStorage for TestEnv {
unsafe fn store(key: Key, value: &[u8]) {
log::debug!(
target: TEST_ENV_LOG_TARGET,
"TestEnv::store(\n\tkey: {:?},\n\tval: {:?}\n)",
key,
value,
);
TEST_ENV_DATA.with(|test_env| test_env.borrow_mut().store(key, value))
}
unsafe fn clear(key: Key) {
log::debug!(
target: TEST_ENV_LOG_TARGET,
"TestEnv::clear(\n\tkey: {:?}\n)",
key,
);
TEST_ENV_DATA.with(|test_env| test_env.borrow_mut().clear(key))
}
unsafe fn load(key: Key) -> Option<Vec<u8>> {
log::debug!(
target: TEST_ENV_LOG_TARGET,
"TestEnv::load(\n\tkey: {:?}\n)",
key,
);
TEST_ENV_DATA.with(|test_env| test_env.borrow().load(key))
}
}
macro_rules! impl_env_getters_for_test_env {
( $( ($fn_name:ident, $ret_name:ty) ),* ) => {
$(
fn $fn_name() -> $ret_name {
TEST_ENV_DATA.with(|test_env| test_env.borrow().$fn_name())
}
)*
}
}
impl Env for TestEnv
where
<Self as EnvTypes>::AccountId: for<'a> TryFrom<&'a [u8]>,
<Self as EnvTypes>::Hash: for<'a> TryFrom<&'a [u8]>,
{
fn address() -> <Self as EnvTypes>::AccountId {
log::debug!(target: TEST_ENV_LOG_TARGET, "TestEnv::address()");
TEST_ENV_DATA.with(|test_env| test_env.borrow().address())
}
fn balance() -> <Self as EnvTypes>::Balance {
log::debug!(target: TEST_ENV_LOG_TARGET, "TestEnv::balance()");
TEST_ENV_DATA.with(|test_env| test_env.borrow().balance())
}
fn caller() -> <Self as EnvTypes>::AccountId {
log::debug!(target: TEST_ENV_LOG_TARGET, "TestEnv::caller()");
TEST_ENV_DATA.with(|test_env| test_env.borrow().caller())
}
fn input() -> Vec<u8> {
log::debug!(target: TEST_ENV_LOG_TARGET, "TestEnv::input()",);
TEST_ENV_DATA.with(|test_env| test_env.borrow().input())
}
fn random_seed() -> <Self as EnvTypes>::Hash {
log::debug!(target: TEST_ENV_LOG_TARGET, "TestEnv::random_seed()",);
TEST_ENV_DATA.with(|test_env| test_env.borrow().random_seed())
}
fn now() -> <Self as EnvTypes>::Moment {
log::debug!(target: TEST_ENV_LOG_TARGET, "TestEnv::now()",);
TEST_ENV_DATA.with(|test_env| test_env.borrow().now())
}
impl_env_getters_for_test_env!(
(address, <Self as EnvTypes>::AccountId),
(balance, <Self as EnvTypes>::Balance),
(caller, <Self as EnvTypes>::AccountId),
(input, Vec<u8>),
(random_seed, <Self as EnvTypes>::Hash),
(now, <Self as EnvTypes>::Moment),
(gas_price, <Self as EnvTypes>::Balance),
(gas_left, <Self as EnvTypes>::Balance),
(value_transferred, <Self as EnvTypes>::Balance)
);
unsafe fn r#return(data: &[u8]) -> ! {
log::debug!(
target: TEST_ENV_LOG_TARGET,
"TestEnv::return_(\n\tdata: {:?}\n)",
data,
);
TEST_ENV_DATA.with(|test_env| test_env.borrow().r#return(data))
}
......@@ -545,7 +527,8 @@ where
TEST_ENV_DATA.with(|test_env| test_env.borrow().println(content))
}
fn deposit_raw_event(data: &[u8]) {
TEST_ENV_DATA.with(|test_env| test_env.borrow_mut().deposit_raw_event(data))
fn deposit_raw_event(topics: &[<Self as EnvTypes>::Hash], data: &[u8]) {
TEST_ENV_DATA
.with(|test_env| test_env.borrow_mut().deposit_raw_event(topics, data))
}
}
......@@ -79,6 +79,15 @@ pub trait Env: EnvTypes + EnvStorage {
/// Get the timestamp of the latest block.
fn now() -> <Self as EnvTypes>::Moment;
/// Returns the current gas price.
fn gas_price() -> <Self as EnvTypes>::Balance;
/// Returns the gas left for this contract execution.
fn gas_left() -> <Self as EnvTypes>::Balance;
/// Returns the amount of value that has been transferred.
fn value_transferred() -> <Self as EnvTypes>::Balance;
/// Returns from the contract execution with the given value.
///
/// # Safety
......@@ -96,5 +105,5 @@ pub trait Env: EnvTypes + EnvStorage {
fn println(content: &str);
/// Deposits raw event data through Contracts module.
fn deposit_raw_event(data: &[u8]);
fn deposit_raw_event(topics: &[<Self as EnvTypes>::Hash], data: &[u8]);
}
......@@ -17,11 +17,40 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(
not(feature = "std"),
feature(alloc, core_intrinsics, lang_items, alloc_error_handler,)
feature(core_intrinsics, lang_items, alloc_error_handler,)
)]
#![deny(
bad_style,
const_err,
dead_code,
improper_ctypes,
legacy_directory_ownership,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
plugin_as_library,
private_in_public,
safe_extern_statics,
unconditional_recursion,
unions_with_drop_fields,
unused,
unused_allocation,
unused_comparisons,
unused_parens,
while_true,
// missing_docs,
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
// unused_import_braces,
// unused_qualifications,
// unused_results,
// missing-copy-implementations
)]
#[cfg(not(feature = "std"))]
// #[macro_use]
extern crate alloc;
// Use `wee_alloc` as the global allocator.
......
......@@ -20,7 +20,6 @@ use core::{
};
#[panic_handler]
#[no_mangle]
pub fn panic(_info: &PanicInfo) -> ! {
unsafe { core::intrinsics::abort() }
}
......
......@@ -18,8 +18,6 @@ use super::*;
use crate::storage::Key;
const BUMP_ALLOC_LOG_TARGET: &str = "bump_alloc";
/// An allocator that is meant to allocate contract storage at
/// compile-time by simply bumping its current allocation key.
///
......@@ -55,10 +53,6 @@ impl BumpAlloc {
impl Allocate for BumpAlloc {
fn alloc(&mut self, size: u64) -> Key {
if size == 0 {
log::warn!(
target: BUMP_ALLOC_LOG_TARGET,
"tried allocating for a zero size",
);
panic!(
"[psdl_core::BumpAlloc::alloc] Error: \
cannot allocate zero (0) bytes"
......@@ -66,12 +60,6 @@ impl Allocate for BumpAlloc {
}
let key = self.offset_key;
self.inc_offset_key