Skip to content
This repository has been archived by the owner on Aug 4, 2021. It is now read-only.

Initial Call implementation #3

Merged
merged 20 commits into from
Aug 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 14 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
/target
# Ignore build artifacts
/target/
/examples/**/target/

# Ignore backup files creates by cargo fmt.
**/*.rs.bk

# Remove Cargo.lock when creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock

# Ignore VS Code artifacts.
**/.vscode/**

# Ignore history files.
**/.history/**
18 changes: 12 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,24 @@ categories = ["no-std", "embedded"]
include = ["/Cargo.toml", "src/**/*.rs", "/README.md", "/LICENSE"]

[dependencies]
# FIXME: remove aj-runtime-types branch once https://github.com/paritytech/ink/pull/108 merged
ink_core = { git = "https://github.com/paritytech/ink/", branch = "aj-runtime-types", package = "ink_core" }
parity-codec = { version = "3.2", default-features = false, features = ["derive"] }
ink_core = { git = "https://github.com/paritytech/ink/", package = "ink_core", default-features = false }
parity-codec = { version = "4.1", default-features = false, features = ["derive"] }

[dev-dependencies]
node-runtime = { git = "https://github.com/paritytech/substrate/", branch = "aj-ink-types", package = "node-runtime", features = ["std"] }
srml-contract = { git = "https://github.com/paritytech/substrate/", branch = "aj-ink-types", package = "srml-contract", features = ["core"] }
node-runtime = { git = "https://github.com/paritytech/substrate/", package = "node-runtime", features = ["std"] }
srml-system = { git = "https://github.com/paritytech/substrate/", package = "srml-system", features = ["std"] }
srml-timestamp = { git = "https://github.com/paritytech/substrate/", package = "srml-timestamp", features = ["std"] }
srml-contract = { git = "https://github.com/paritytech/substrate/", package = "srml-contracts", features = ["core"] }
srml-indices = { git = "https://github.com/paritytech/substrate/", package = "srml-indices", features = ["std"] }
quickcheck = "0.8"
quickcheck_macros = "0.8"

[features]
default = []
default = ["std"]
std = [
"ink_core/std",
]
test-env = [
"std",
"ink_core/test-env",
]
4 changes: 4 additions & 0 deletions examples/calls/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[target.wasm32-unknown-unknown]
rustflags = [
"-C", "link-args=-z stack-size=65536 --import-memory"
]
9 changes: 9 additions & 0 deletions examples/calls/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Ignore build artifacts from the local tests sub-crate.
/target/

# Ignore backup files creates by cargo fmt.
**/*.rs.bk

# Remove Cargo.lock when creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
44 changes: 44 additions & 0 deletions examples/calls/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[package]
name = "calls"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"

[dependencies]
ink_core = { git = "https://github.com/paritytech/ink/", package = "ink_core", default-features = false }
ink_model = { git = "https://github.com/paritytech/ink/", package = "ink_model", default-features = false }
ink_lang = { git = "https://github.com/paritytech/ink/", package = "ink_lang", default-features = false }
ink_types_node_runtime = { path = "../../", default-features = false }
parity-codec = { version = "4.1", default-features = false, features = ["derive"] }

[lib]
name = "calls"
crate-type = ["cdylib"]

[features]
default = [
"std",
"ink_lang/generate-api-description"
]
std = [
"ink_core/std",
"ink_model/std",
"ink_lang/std",
"ink_types_node_runtime/std",
"parity-codec/std",
]
test-env = [
"std",
"ink_core/test-env",
"ink_model/test-env",
"ink_lang/test-env",
]
generate-api-description = [
"ink_lang/generate-api-description"
]

[profile.release]
panic = "abort"
lto = true
opt-level = "z"
ascjones marked this conversation as resolved.
Show resolved Hide resolved
overflow-checks = true
14 changes: 14 additions & 0 deletions examples/calls/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
set -e

PROJNAME=calls

# cargo clean
# rm Cargo.lock

CARGO_INCREMENTAL=0 &&
cargo +nightly build --release --no-default-features --features generate-api-description --target=wasm32-unknown-unknown --verbose
wasm2wat -o target/$PROJNAME.wat target/wasm32-unknown-unknown/release/$PROJNAME.wasm
cat target/$PROJNAME.wat | sed "s/(import \"env\" \"memory\" (memory (;0;) 2))/(import \"env\" \"memory\" (memory (;0;) 2 16))/" > target/$PROJNAME-fixed.wat
wat2wasm -o target/$PROJNAME.wasm target/$PROJNAME-fixed.wat
wasm-prune --exports call,deploy target/$PROJNAME.wasm target/$PROJNAME-pruned.wasm
54 changes: 54 additions & 0 deletions examples/calls/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of ink!.
//
// ink! 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.
//
// ink! 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 ink!. If not, see <http://www.gnu.org/licenses/>.

#![cfg_attr(not(any(test, feature = "test-env")), no_std)]

use ink_lang::contract;
use ink_types_node_runtime::{calls, AccountIndex, NodeRuntimeTypes};

contract! {
#![env = NodeRuntimeTypes]

/// This simple dummy contract dispatches substrate runtime calls
struct Calls {}

impl Deploy for Calls {
fn deploy(&mut self) {
}
}

impl Calls {
/// Dispatches a `transfer` call to the Balances srml module
pub(external) fn balance_transfer(&mut self, dest: AccountId, value: Balance) {
let dest_addr = calls::Address::Id(dest);
let transfer_call = calls::Balances::<NodeRuntimeTypes, AccountIndex>::transfer(dest_addr, value);
env.dispatch_call(transfer_call);
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn dispatches_balances_call() {
let mut calls = Calls::deploy_mock();
assert_eq!(env::dispatched_calls().into_iter().count(), 0);
calls.balance_transfer(1, 10000);
assert_eq!(env::dispatched_calls().into_iter().count(), 1);
}
}
1 change: 0 additions & 1 deletion rust-toolchain

This file was deleted.

195 changes: 195 additions & 0 deletions src/calls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of ink!.
//
// ink! 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.
//
// ink! 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 ink!. If not, see <http://www.gnu.org/licenses/>.

use ink_core::env::EnvTypes;
use parity_codec::{
Decode,
Encode,
Input,
Output,
};
use core::convert::TryInto;

#[cfg_attr(feature = "std", derive(Debug, Clone, PartialEq, Eq))]
pub enum Address<T: EnvTypes, AccountIndex> {
Id(T::AccountId),
Index(AccountIndex),
}

/// Returns `b` if `b` is greater than `a` and otherwise `None`.
fn greater_than_or_none<T: PartialOrd>(a: T, b: T) -> Option<T> {
if a < b { Some(b) } else { None }
}

/// Decode implementation copied over from Substrate `Address` that can be found [here](substrate-address).
///
/// # Note
/// This implementation MUST be kept in sync with substrate, tests below will ensure that.
///
/// [substrate-address]: https://github.com/paritytech/substrate/blob/ec62d24c602912f07bbc416711376d9b8e5782c5/srml/indices/src/address.rs#L61
impl<T, AccountIndex> Decode for Address<T, AccountIndex> where
T: EnvTypes,
AccountIndex: Decode + From<u32> + PartialOrd + Copy + Clone,
{
fn decode<I: Input>(input: &mut I) -> Option<Self> {
Some(match input.read_byte()? {
x @ 0x00..=0xef => Address::Index(AccountIndex::from(x as u32)),
0xfc => Address::Index(AccountIndex::from(
greater_than_or_none(0xef, u16::decode(input)?)? as u32
)),
0xfd => Address::Index(AccountIndex::from(
greater_than_or_none(0xffff, u32::decode(input)?)?
)),
0xfe => Address::Index(
greater_than_or_none(0xffffffffu32.into(), Decode::decode(input)?)?
),
0xff => Address::Id(Decode::decode(input)?),
_ => return None,
})
}
}

/// Encode implementation copied over from Substrate `Address` that can be found [here](substrate-address).
///
/// # Note
/// This implementation MUST be kept in sync with substrate, tests below will ensure that.
///
/// [substrate-address]: https://github.com/paritytech/substrate/blob/ec62d24c602912f07bbc416711376d9b8e5782c5/srml/indices/src/address.rs#L83
impl<T, AccountIndex> Encode for Address<T, AccountIndex> where
T: EnvTypes,
AccountIndex: Encode + TryInto<u32> + Copy + Clone,
{
fn encode_to<O: Output>(&self, dest: &mut O) {
match *self {
Address::Id(ref i) => {
dest.push_byte(255);
dest.push(i);
}
Address::Index(i) => {
let maybe_u32: Result<u32, _> = i.try_into();
if let Ok(x) = maybe_u32 {
if x > 0xffff {
dest.push_byte(253);
dest.push(&x);
}
else if x >= 0xf0 {
dest.push_byte(252);
dest.push(&(x as u16));
}
else {
dest.push_byte(x as u8);
}

} else {
dest.push_byte(254);
dest.push(&i);
}
},
}
}
}

#[derive(Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Clone, PartialEq, Eq))]
pub enum Balances<T: EnvTypes, AccountIndex> {
#[allow(non_camel_case_types)]
transfer(Address<T, AccountIndex>, #[codec(compact)] T::Balance),
#[allow(non_camel_case_types)]
set_balance(
Address<T, AccountIndex>,
#[codec(compact)] T::Balance,
#[codec(compact)] T::Balance,
),
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{calls, Call, NodeRuntimeTypes, AccountIndex};

use node_runtime::{self, Runtime};
use parity_codec::{Decode, Encode};
use srml_indices::address;

#[test]
fn account_index_serialization() {
let account_index = 0u32;

let ink_address = Address::Index(account_index.into());
let srml_address: address::Address<[u8; 32], u32> = address::Address::Index(account_index);

let ink_encoded = ink_address.encode();
let srml_encoded = srml_address.encode();

assert_eq!(srml_encoded, ink_encoded);

let srml_decoded: address::Address<[u8; 32], u32> = Decode::decode(&mut ink_encoded.as_slice())
.expect("Account Index decodes to srml Address");
let srml_encoded = srml_decoded.encode();
let ink_decoded: Address<NodeRuntimeTypes, u32> = Decode::decode(&mut srml_encoded.as_slice())
.expect("Account Index decodes back to ink type");

assert_eq!(ink_address, ink_decoded);
}

#[test]
fn account_id_serialization() {
let account_id = [0u8; 32];

let ink_address = Address::Id(account_id.into());
let srml_address: address::Address<[u8; 32], u32> = address::Address::Id(account_id);

let ink_encoded = ink_address.encode();
let srml_encoded = srml_address.encode();

assert_eq!(srml_encoded, ink_encoded);

let srml_decoded: address::Address<[u8; 32], u32> = Decode::decode(&mut ink_encoded.as_slice())
.expect("Account Id decodes to srml Address");
let srml_encoded = srml_decoded.encode();
let ink_decoded: Address<NodeRuntimeTypes, u32> = Decode::decode(&mut srml_encoded.as_slice())
.expect("Account Id decodes decodes back to ink type");

assert_eq!(ink_address, ink_decoded);
}

#[test]
fn call_balance_transfer() {
let balance = 10_000;
let account_index = 0;

let contract_address = calls::Address::Index(account_index);
let contract_transfer = calls::Balances::<NodeRuntimeTypes, AccountIndex>::transfer(contract_address, balance);
let contract_call = Call::Balances(contract_transfer);

let srml_address = address::Address::Index(account_index);
let srml_transfer = node_runtime::BalancesCall::<Runtime>::transfer(srml_address, balance);
let srml_call = node_runtime::Call::Balances(srml_transfer);

let contract_call_encoded = contract_call.encode();
let srml_call_encoded = srml_call.encode();

assert_eq!(srml_call_encoded, contract_call_encoded);

let srml_call_decoded: node_runtime::Call = Decode::decode(&mut contract_call_encoded.as_slice())
.expect("Balances transfer call decodes to srml type");
let srml_call_encoded = srml_call_decoded.encode();
let contract_call_decoded: Call = Decode::decode(&mut srml_call_encoded.as_slice())
.expect("Balances transfer call decodes back to contract type");
assert_eq!(contract_call, contract_call_decoded);
}
}