Unverified Commit 321fe03a authored by Hero Bird's avatar Hero Bird Committed by GitHub
Browse files

Implementation of ink! env (revision 3) (#312)

* [core] initial implementation of env revision 3

* add engine abstraction layer and implement api functions

* add support for dispatch_call to env3

* add missing invoke_runtime to api.rs

* [core] fix restore_contract docs

* [core] initial implementation of off-chain environment

* [core] clean-up of env3 fundamental types

* [core] add WasmEnv::reset_buffer

* show test submodule of env3 when compiling with rustdoc

* [core] initial implementation of the off-chain env instance

Also many other adjustments.

* [core] further enhancements to off-chain env

* [core] implement untyped Env for off-chain environment

* [core] implemented a good chunk of TypedEnv for the off-chain environment

* [core] minor improvements to env3

* [core] further improvements and additions to test-api of env3

* [core] remove former test-api file

* [core] env3: add support for ext_tombstone_deposit

* [core] env3: add setting of rent allowa...
parent 6fd834d0
Pipeline #77049 passed with stages
in 12 minutes and 9 seconds
......@@ -18,7 +18,7 @@ variables:
CARGO_TARGET_DIR: "/ci-cache/${CI_PROJECT_NAME}/targets/${CI_COMMIT_REF_NAME}/${CI_JOB_NAME}"
CI_SERVER_NAME: "GitLab CI"
REGISTRY: registry.parity.io/parity/infrastructure/scripts
ALL_CRATES: "core alloc prelude utils lang2 lang2/macro"
ALL_CRATES: "core alloc prelude primitives lang lang/macro"
.collect-artifacts: &collect-artifacts
artifacts:
......@@ -147,7 +147,7 @@ examples-test:
stage: examples
<<: *docker-env
script:
- for example in examples/lang2/*; do
- for example in examples/*/; do
cargo test --verbose --manifest-path ${example}/Cargo.toml;
done
......@@ -155,7 +155,7 @@ examples-fmt:
stage: examples
<<: *docker-env
script:
- for example in examples/lang2/*; do
- for example in examples/*/; do
cargo fmt --verbose --manifest-path ${example}/Cargo.toml -- --check;
done
......@@ -163,7 +163,7 @@ examples-clippy-std:
stage: examples
<<: *docker-env
script:
- for example in examples/lang2/*; do
- for example in examples/*/; do
cargo clippy --verbose --manifest-path ${example}/Cargo.toml -- -D warnings;
done
......@@ -171,7 +171,7 @@ examples-clippy-wasm:
stage: examples
<<: *docker-env
script:
- for example in examples/lang2/*; do
- for example in examples/*/; do
cargo clippy --verbose --manifest-path ${example}/Cargo.toml --no-default-features --target wasm32-unknown-unknown -- -D warnings;
done
......@@ -180,7 +180,7 @@ examples-contract-build:
<<: *docker-env
script:
- *update-cargo-contract
- for example in examples/lang2/*; do
- for example in examples/*/; do
pushd $example &&
cargo contract build &&
popd;
......@@ -191,7 +191,7 @@ examples-generate-metadata:
<<: *docker-env
script:
- *update-cargo-contract
- for example in examples/lang2/*; do
- for example in examples/*/; do
pushd $example &&
cargo contract generate-metadata &&
popd;
......
......@@ -34,7 +34,7 @@ Following these will ensure that your pull request is going to be accepted.
1. Run `rustfmt` automatically.
1. Run `clippy` on your changes.
1. Run tests in `debug` and `release` mode using `--features test-env` for off-chain testing.
1. Run tests via `cargo test --release` for off-chain testing.
1. For critical parts perform some manual on-chain tests.
1. Build the code and run tests also for the `wasm32` target.
1. Try to run some examples and see if they are still working correctly.
......
......@@ -4,10 +4,8 @@ members = [
"abi",
"core",
"lang",
"lang2",
"model",
"prelude",
"utils",
"primitives",
]
exclude = [
"examples/",
......
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// Copyright {20\d{2}}-{20\d{2}} 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.
......
......@@ -14,8 +14,6 @@
[e2]: https://github.com/Aaronepower/tokei#badges
[f1]: https://img.shields.io/badge/docs-core-blue.svg
[f2]: https://paritytech.github.io/ink/ink_core
[g1]: https://img.shields.io/badge/docs-model-blue.svg
[g2]: https://paritytech.github.io/ink/ink_model
[h1]: https://img.shields.io/badge/docs-abi-blue.svg
[h2]: https://paritytech.github.io/ink/ink_abi
......@@ -27,9 +25,9 @@ For more information please visit [the ink! tutorial](https://substrate.dev/subs
## Developer Documentation
| `ink_abi` | `ink_core` | `ink_model` |
| ------------- | ------------- | ------------- |
| [![][h1]][h2] | [![][f1]][f2] | [![][g1]][g2] |
| `ink_abi` | `ink_core` |
| ------------- | ------------- |
| [![][h1]][h2] | [![][f1]][f2] |
### Interaction with Substrate
......@@ -81,14 +79,15 @@ To create your own version of the flipper contract, you first need to initialize
cargo contract new flipper
```
Below you can see the code using the `ink_lang2` version of ink!.
Below you can see the code using the `ink_lang` version of ink!.
```rust
use ink_core::storage;
use ink_lang2 as ink;
use ink_lang as ink;
#[ink::contract(version = "0.1.0")]
mod flipper {
use ink_core::storage;
/// The storage of the flipper contract.
#[ink(storage)]
struct Flipper {
......
......@@ -64,7 +64,7 @@ contract! {
<td>
```rust
use ink_lang2 as ink;
use ink_lang as ink;
#[ink::contract(version = "0.1.0")]
mod erc20 {
......@@ -78,7 +78,7 @@ mod erc20 {
> Note: we now require a mandatory ink! version in the header. You're welcome.
See the [ERC20 example](https://github.com/paritytech/ink/blob/master/examples/lang2/erc20/src/lib.rs).
See the [ERC20 example](https://github.com/paritytech/ink/blob/master/examples/erc20/src/lib.rs).
## ink! Contract Tag
......@@ -146,7 +146,7 @@ struct Erc20 {
</tr>
</table>
See the [ERC20 example](https://github.com/paritytech/ink/blob/master/examples/lang2/erc20/src/lib.rs).
See the [ERC20 example](https://github.com/paritytech/ink/blob/master/examples/erc20/src/lib.rs).
## Declaring Events
......@@ -193,7 +193,7 @@ struct Transfer {
</tr>
</table>
See the [ERC20 example](https://github.com/paritytech/ink/blob/master/examples/lang2/erc20/src/lib.rs).
See the [ERC20 example](https://github.com/paritytech/ink/blob/master/examples/erc20/src/lib.rs).
## Environment Handler
......@@ -275,7 +275,7 @@ fn total_supply(&self) -> Balance {
</tr>
</table>
See the [ERC20 example](https://github.com/paritytech/ink/blob/master/examples/lang2/erc20/src/lib.rs).
See the [ERC20 example](https://github.com/paritytech/ink/blob/master/examples/erc20/src/lib.rs).
## Defining a Constructor
......@@ -335,7 +335,7 @@ impl Erc20 {
</tr>
</table>
See the [ERC20 example](https://github.com/paritytech/ink/blob/master/examples/lang2/erc20/src/lib.rs).
See the [ERC20 example](https://github.com/paritytech/ink/blob/master/examples/erc20/src/lib.rs).
## Cross Contract Calls
......@@ -355,7 +355,7 @@ struct Delegator {
let result = self.adder.inc(by);
```
See the [delegator example](https://github.com/paritytech/ink/blob/master/examples/lang2/delegator/lib.rs).
See the [delegator example](https://github.com/paritytech/ink/blob/master/examples/delegator/lib.rs).
## Factory Contracts
......@@ -406,7 +406,7 @@ let accumulator = Accumulator::new(init_value)
</tr>
</table>
See the [delegator example](https://github.com/paritytech/ink/blob/master/examples/lang2/delegator/lib.rs).
See the [delegator example](https://github.com/paritytech/ink/blob/master/examples/delegator/lib.rs).
## Contract Tests
......@@ -432,7 +432,7 @@ let contract = MyContract::my_constructor(a, b);
Messages can simply be called on the returned instance as if `MyContract::my_constructor` returns a
`Self` instance.
See the [flipper example](https://github.com/paritytech/ink/blob/master/examples/lang2/flipper/src/lib.rs).
See the [flipper example](https://github.com/paritytech/ink/blob/master/examples/flipper/src/lib.rs).
**The off-chain test environment has lost a bit of power compared to the old ink! language.**
......
......@@ -19,6 +19,7 @@ serde = { version = "1.0", default-features = false, features = ["derive", "allo
derive_more = { version = "0.99.2", default-features = false, features = ["from"] }
ink_abi_derive = { path = "derive", default-features = false, optional = true }
ink_prelude = { path = "../prelude/", default-features = false }
ink_primitives = { path = "../primitives/", default-features = false }
type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"] }
[dev-dependencies]
......
......@@ -60,7 +60,7 @@ pub fn generate_impl(input: TokenStream2) -> Result<TokenStream2> {
}
};
Ok(wrap(ident, "HAS_LAYOUT", has_layout_impl))
Ok(wrap(has_layout_impl))
}
fn generate_fields_layout<'a>(
......@@ -97,8 +97,7 @@ fn generate_struct_layout(data_struct: &DataStruct) -> TokenStream2 {
Fields::Unnamed(ref fs) => generate_struct_fields_layout(&fs.unnamed),
Fields::Unit => {
quote! {
use type_metadata::Metadata as _;
_ink_abi::LayoutStruct::new(Self::meta_type(), __core::vec![])
_ink_abi::LayoutStruct::new(<Self as type_metadata::Metadata>::meta_type(), Vec::new())
}
}
}
......
......@@ -12,34 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#[cfg(not(feature = "std"))]
use alloc::{
format,
string::{
String,
ToString,
},
};
use proc_macro2::{
Span,
TokenStream as TokenStream2,
};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::Ident;
pub fn wrap(
ident: &Ident,
trait_name: &'static str,
impl_quote: TokenStream2,
) -> TokenStream2 {
let mut renamed = format!("_IMPL_{}_FOR_", trait_name);
renamed.push_str(ident.to_string().trim_start_matches("r#"));
let dummy_const = Ident::new(&renamed, Span::call_site());
pub fn wrap(impl_quote: TokenStream2) -> TokenStream2 {
quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const #dummy_const: () = {
const _: () = {
#[allow(unknown_lints)]
#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
#[allow(rust_2018_idioms)]
......
......@@ -41,6 +41,19 @@ pub trait HasLayout {
fn layout(&self) -> StorageLayout;
}
impl From<ink_primitives::Key> for LayoutKey {
fn from(key: ink_primitives::Key) -> Self {
LayoutKey(key.0)
}
}
impl HasLayout for ink_primitives::Key {
fn layout(&self) -> StorageLayout {
LayoutRange::cell(*self, <[u8; 32] as type_metadata::Metadata>::meta_type())
.into()
}
}
/// Either a concrete layout bound or another layout sub-struct.
#[derive(Debug, PartialEq, Eq, Serialize, From)]
#[serde(bound = "F::TypeId: Serialize")]
......@@ -73,7 +86,7 @@ impl IntoCompact for StorageLayout {
#[derive(Debug, PartialEq, Eq, From, Serialize)]
#[serde(transparent)]
pub struct LayoutKey(
/// Internals must be compatible with `ink_core::storage::Key`.
/// Internals must be compatible with `ink_primitives::Key`.
pub [u8; 32],
);
......
......@@ -17,27 +17,35 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
[dependencies]
ink_abi = { path = "../abi/", default-features = false, features = ["derive"], optional = true }
ink_alloc = { path = "../alloc/", default-features = false }
ink_utils = { path = "../utils/" }
ink_primitives = { path = "../primitives/", default-features = false }
ink_core_derive = { path = "derive", default-features = false }
ink_prelude = { path = "../prelude/", default-features = false }
scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive", "full"] }
type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true }
derive_more = { version = "0.99.2", default-features = false, features = ["from"] }
derive_more = { version = "0.99.2", default-features = false, features = ["from", "display"] }
smallvec = { version = "1.0", default-features = false, features = ["union"] }
cfg-if = "0.1"
num-traits = { version = "0.2.1", default-features = false, features = ["i128"] }
# Only used in the off-chain environment.
#
# Sadly couldn't be marked as dev-dependency.
# Never use this crate outside of the off-chain environment!
rand = { version = "0.7", default-features = false, features = ["alloc"], optional = true }
[features]
default = ["test-env"]
test-env = [
"std",
]
default = ["std"]
std = [
"ink_abi/std",
"ink_alloc/std",
"ink_prelude/std",
"ink_primitives/std",
"scale/std",
"type-metadata/std",
"rand",
"rand/std",
"num-traits/std",
]
ink-generate-abi = [
"ink_abi",
......
......@@ -16,7 +16,7 @@ mod utils;
use utils::*;
use ink_core::storage::Key;
use ink_primitives::Key;
use ink_core_derive::AllocateUsing;
#[derive(Default)]
......
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// 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.
......@@ -12,127 +12,458 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use scale::{
Decode,
Encode,
};
//! The public raw interface towards the host Wasm engine.
use super::ContractEnvStorage;
use crate::{
env::{
traits::{
Env,
EnvTypes,
},
CallError,
CreateError,
EnvStorage as _,
use crate::env::{
backend::{
Env,
TypedEnv,
},
call::{
CallData,
CallParams,
InstantiateParams,
ReturnType,
},
engine::{
EnvInstance,
OnInstance,
},
storage::Key,
EnvTypes,
Result,
Topics,
};
use ink_prelude::vec::Vec;
use ink_primitives::Key;
/// Returns the address of the caller of the executed contract.
///
/// # Errors
///
/// If the returned caller cannot be properly decoded.
pub fn caller<T>() -> Result<T::AccountId>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| TypedEnv::caller::<T>(instance))
}
/// Returns the transferred balance for the contract execution.
///
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn transferred_balance<T>() -> Result<T::Balance>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::transferred_balance::<T>(instance)
})
}
/// Returns the current price for gas.
///
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn gas_price<T>() -> Result<T::Balance>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::gas_price::<T>(instance)
})
}
/// Returns the amount of gas left for the contract execution.
///
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn gas_left<T>() -> Result<T::Balance>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| TypedEnv::gas_left::<T>(instance))
}
/// Returns the current block timestamp.
///
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn block_timestamp<T>() -> Result<T::Timestamp>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::block_timestamp::<T>(instance)
})
}
/// Stores the given value under the specified key in the contract storage.
/// Returns the account ID of the executed contract.
///
/// # Safety
/// # Note
///
/// This method was formerly known as `address`.
///
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn account_id<T>() -> Result<T::AccountId>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::account_id::<T>(instance)
})
}
/// Returns the balance of the executed contract.
///
/// # Errors
///
/// This operation is unsafe because it does not check for key integrity.
/// Users can compare this operation with a raw pointer dereferencing in Rust.
pub unsafe fn store(key: Key, value: &[u8]) {
ContractEnvStorage::store(key, value)
/// If the returned value cannot be properly decoded.
pub fn balance<T>() -> Result<T::Balance>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| TypedEnv::balance::<T>(instance))
}
/// Returns the current rent allowance for the executed contract.
///
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn rent_allowance<T>() -> Result<T::Balance>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::rent_allowance::<T>(instance)
})
}
/// Clears the data stored at the given key from the contract storage.
/// Returns the current block number.
///
/// # Safety
/// # Errors
///
/// This operation is unsafe because it does not check for key integrity.
/// Users can compare this operation with a raw pointer dereferencing in Rust.
pub unsafe fn clear(key: Key) {
ContractEnvStorage::clear(key)
/// If the returned value cannot be properly decoded.
pub fn block_number<T>() -> Result<T::BlockNumber>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::block_number::<T>(instance)
})
}
/// Returns the minimum balance for the contracts chain.
///
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn minimum_balance<T>() -> Result<T::Balance>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::minimum_balance::<T>(instance)
})
}
/// Returns the tombstone deposit for the contracts chain.
///
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn tombstone_deposit<T>() -> Result<T::Balance>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::tombstone_deposit::<T>(instance)
})
}
/// Emits an event with the given event data.
pub fn emit_event<T, Event>(event: Event)
where
T: EnvTypes,
Event: Topics<T> + scale::Encode,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::emit_event::<T, Event>(instance, event)
})
}
/// Loads the data stored at the given key from the contract storage.
/// Sets the rent allowance of the executed contract to the new value.
pub fn set_rent_allowance<T>(new_value: T::Balance)
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::set_rent_allowance::<T>(instance, new_value)
})
}
/// Writes the value to the contract storage under the given key.
pub fn set_contract_storage<V>(key: Key, value: &V)
where
V: scale::Encode,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
Env::set_contract_storage::<V>(instance, key, value)
})
}
/// Returns the value stored under the given key in the contract's storage if any.
///
/// # Safety
/// # Errors
///
/// This operation is unsafe because it does not check for key integrity.
/// Users can compare this operation with a raw pointer dereferencing in Rust.
pub unsafe fn load(key: Key) -> Option<Vec<u8>> {
ContractEnvStorage::load(key)
/// - If the decoding of the typed value failed
pub fn get_contract_storage<R>(key: Key) -> Option<Result<R>>
where
R: scale::Decode,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
Env::get_contract_storage::<R>(instance, key)
})
}
/// Clears the contract's storage key entry.
pub fn clear_contract_storage(key: Key) {
<EnvInstance as OnInstance>::on_instance(|instance| {
Env::clear_contract_storage(instance, key)
})
}
/// Returns the given data back to the caller.
/// Invokes a call to the runtime.
///