Repository: paritytech/ink
Pull request #326 (2020-02-25 15:04:46)

Add Example Tests
- ERC20 tests

athei: You are not using `run_test`. Should you be using it or is this the new way (without it).
Robbepop: > You are not using `run_test`. Should you be using it or is this the new way (without it).

Not using `run_test` is actually wrong and I am still trying to find out what is broken with it atm. In the end what we want is a proc macro `#[ink(test)]` that handles all test-cases for you.
athei: That would be awesome. Getting rid of that indentation level.
Robbepop: > That would be awesome. Getting rid of that indentation level.

https://github.com/paritytech/ink/issues/335
riusricardo: I agree with you @athei and @Robbepop, I'm aware of this. This is why I left it as draft PR.
I did it the same as Alex and got into the bug. I'm just waiting for this bug to be fixed.
Robbepop: @riusricardo What is the state of this PR?
codecov-io: # [Codecov](https://codecov.io/gh/paritytech/ink/pull/326?src=pr&el=h1) Report
> Merging [#326](https://codecov.io/gh/paritytech/ink/pull/326?src=pr&el=desc) into [master](https://codecov.io/gh/paritytech/ink/commit/8bc75a5c2e2675e3f76a2fc5441b97755c3a26d3?src=pr&el=desc) will **decrease** coverage by `8.3%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/paritytech/ink/pull/326/graphs/tree.svg?width=650&token=tYyVaCZb7o&height=150&src=pr)](https://codecov.io/gh/paritytech/ink/pull/326?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #326      +/-   ##
==========================================
- Coverage   20.58%   12.28%   -8.31%     
==========================================
  Files           2        7       +5     
  Lines          34      464     +430     
==========================================
+ Hits            7       57      +50     
- Misses         27      407     +380
```


| [Impacted Files](https://codecov.io/gh/paritytech/ink/pull/326?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [core/derive/src/lib.rs](https://codecov.io/gh/paritytech/ink/pull/326/diff?src=pr&el=tree#diff-Y29yZS9kZXJpdmUvc3JjL2xpYi5ycw==) | | |
| [primitives/src/byte\_utils.rs](https://codecov.io/gh/paritytech/ink/pull/326/diff?src=pr&el=tree#diff-cHJpbWl0aXZlcy9zcmMvYnl0ZV91dGlscy5ycw==) | `0% <0%> (ø)` | |
| [abi/src/layout.rs](https://codecov.io/gh/paritytech/ink/pull/326/diff?src=pr&el=tree#diff-YWJpL3NyYy9sYXlvdXQucnM=) | `35.21% <0%> (ø)` | |
| [abi/src/specs.rs](https://codecov.io/gh/paritytech/ink/pull/326/diff?src=pr&el=tree#diff-YWJpL3NyYy9zcGVjcy5ycw==) | `11.68% <0%> (ø)` | |
| [primitives/src/hash.rs](https://codecov.io/gh/paritytech/ink/pull/326/diff?src=pr&el=tree#diff-cHJpbWl0aXZlcy9zcmMvaGFzaC5ycw==) | `0% <0%> (ø)` | |
| [primitives/src/key.rs](https://codecov.io/gh/paritytech/ink/pull/326/diff?src=pr&el=tree#diff-cHJpbWl0aXZlcy9zcmMva2V5LnJz) | `0% <0%> (ø)` | |
| [abi/src/lib.rs](https://codecov.io/gh/paritytech/ink/pull/326/diff?src=pr&el=tree#diff-YWJpL3NyYy9saWIucnM=) | `0% <0%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/paritytech/ink/pull/326?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/paritytech/ink/pull/326?src=pr&el=footer). Last update [8bc75a5...ffa1c1b](https://codecov.io/gh/paritytech/ink/pull/326?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 00fe0c2668..8317e9d175 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -53,7 +53,7 @@ variables:
     - /^v[0-9]+\.[0-9]+.*$/        # i.e. v1.0, v2.1rc1
     - schedules
     - web
-    - /^[0-9]+$/                   # PRs
+    - branches
   dependencies:                    []
   interruptible:                   true
   retry:
diff --git a/.travis.yml b/.travis.yml
index 4d28fa7228..2a8c61c9bb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,10 @@ cache: cargo
 dist: trusty
 sudo: true
 
+branches:
+  only:
+  - master
+
 rust:
   - nightly-2019-11-17
 
diff --git a/abi/Cargo.toml b/abi/Cargo.toml
index 19ebc4ea5d..29a63c3266 100644
--- a/abi/Cargo.toml
+++ b/abi/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ink_abi"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
@@ -15,9 +15,9 @@ categories = ["no-std", "embedded"]
 include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
 
 [dependencies]
-ink_abi_derive = { version = "2.0.0", path = "derive", default-features = false, optional = true }
-ink_prelude = { version = "2.0.0", path = "../prelude/", default-features = false }
-ink_primitives = { version = "2.0.0", path = "../primitives/", default-features = false }
+ink_abi_derive = { version = "2.1.0", path = "derive", default-features = false, optional = true }
+ink_prelude = { version = "2.1.0", path = "../prelude/", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../primitives/", default-features = false }
 
 serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
 derive_more = { version = "0.99", default-features = false, features = ["from"] }
diff --git a/abi/derive/Cargo.toml b/abi/derive/Cargo.toml
index bfbb96028a..942b4d2fbf 100644
--- a/abi/derive/Cargo.toml
+++ b/abi/derive/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ink_abi_derive"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 license = "APACHE-2.0"
diff --git a/alloc/Cargo.toml b/alloc/Cargo.toml
index c3dbf03676..5b1a952dc1 100644
--- a/alloc/Cargo.toml
+++ b/alloc/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ink_alloc"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
diff --git a/bors.toml b/bors.toml
new file mode 100644
index 0000000000..9adbc1a19a
--- /dev/null
+++ b/bors.toml
@@ -0,0 +1,7 @@
+status = [
+    "ci/gitlab/gitlab.parity.io"
+]
+# The default is one hour (3600 sec).
+timeout_sec = 1800
+use_squash_merge = true
+required_approvals = 1
diff --git a/core/Cargo.toml b/core/Cargo.toml
index ada6b44dae..0de17dd346 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ink_core"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
@@ -15,17 +15,24 @@ categories = ["no-std", "embedded"]
 include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
 
 [dependencies]
-ink_abi = { version = "2.0.0", path = "../abi/", default-features = false, features = ["derive"], optional = true }
-ink_alloc = { version = "2.0.0", path = "../alloc/", default-features = false }
-ink_primitives = { version = "2.0.0", path = "../primitives/", default-features = false }
-ink_core_derive = { version = "2.0.0", path = "derive", default-features = false }
-ink_prelude = { version = "2.0.0", path = "../prelude/", default-features = false }
+ink_abi = { version = "2.1.0", path = "../abi/", default-features = false, features = ["derive"], optional = true }
+ink_alloc = { version = "2.1.0", path = "../alloc/", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../primitives/", default-features = false }
+ink_core_derive = { version = "2.1.0", path = "derive", default-features = false }
+ink_prelude = { version = "2.1.0", path = "../prelude/", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive", "full"] }
 derive_more = { version = "0.99", default-features = false, features = ["from", "display"] }
 smallvec = { version = "1.2", default-features = false, features = ["union"] }
 cfg-if = "0.1"
 num-traits = { version = "0.2", default-features = false, features = ["i128"] }
+paste = "0.1"
+
+# Hashes for the off-chain environment.
+byteorder = { version = "1.3", optional = true }
+blake2-rfc = { version = "0.2", optional = true }
+sha2 = { version = "0.8", optional = true }
+tiny-keccak = { version = "2.0", optional = true }
 
 # Only used in the off-chain environment.
 #
@@ -55,6 +62,11 @@ std = [
     "rand",
     "rand/std",
     "num-traits/std",
+    # Enables hashing crates for off-chain environment.
+    "byteorder",
+    "blake2-rfc",
+    "sha2",
+    "tiny-keccak",
 ]
 ink-generate-abi = [
     "ink_abi",
diff --git a/core/derive/Cargo.toml b/core/derive/Cargo.toml
index 141d602f23..52f211bc84 100644
--- a/core/derive/Cargo.toml
+++ b/core/derive/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ink_core_derive"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
@@ -11,14 +11,14 @@ readme = "../README.md"
 proc-macro = true
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../primitives", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../primitives", default-features = false }
 quote = "1.0"
 syn = { version = "1.0", features = ["full"] }
 proc-macro2 = "1.0"
 synstructure = "0.12"
 
 [dev-dependencies]
-ink_core = { version = "2.0.0", path = ".." }
+ink_core = { version = "2.1.0", path = ".." }
 trybuild = "1.0.24"
 
 [features]
diff --git a/core/src/env/api.rs b/core/src/env/api.rs
index 5ce8cd0cad..4d04ee2b05 100644
--- a/core/src/env/api.rs
+++ b/core/src/env/api.rs
@@ -506,3 +506,41 @@ where
         Env::get_runtime_storage::<R>(instance, runtime_key)
     })
 }
+
+/// Built-in efficient cryptographic hash functions.
+pub mod hash {
+    use super::*;
+
+    macro_rules! impl_hash_fn {
+        ( $(#[$doc:meta])* fn $name:ident($output_len:literal) ) => {
+            paste::item! {
+                $( #[$doc] )*
+                pub fn $name(input: &[u8], output: &mut [u8; $output_len]) {
+                    // No need to actually access the environmental instance
+                    // if we only call one of its inherent methods.
+                    <EnvInstance as Env>::[<hash_ $name>](input, output)
+                }
+            }
+        };
+    }
+    impl_hash_fn!(
+        /// Conducts the SHA2 256-bit hash of the given bytes and
+        /// puts the result into the output buffer.
+        fn sha2_256(32)
+    );
+    impl_hash_fn!(
+        /// Conducts the KECCAK 256-bit hash of the given bytes and
+        /// puts the result into the output buffer.
+        fn keccak_256(32)
+    );
+    impl_hash_fn!(
+        /// Conducts the BLAKE2 256-bit hash of the given bytes and
+        /// puts the result into the output buffer.
+        fn blake2_256(32)
+    );
+    impl_hash_fn!(
+        /// Conducts the BLAKE2 128-bit hash of the given bytes and
+        /// puts the result into the output buffer.
+        fn blake2_128(16)
+    );
+}
diff --git a/core/src/env/backend.rs b/core/src/env/backend.rs
index f8ba6a3da8..9c8957b948 100644
--- a/core/src/env/backend.rs
+++ b/core/src/env/backend.rs
@@ -77,6 +77,22 @@ pub trait Env {
 
     /// Prints the given contents to the console log.
     fn println(&mut self, content: &str);
+
+    /// Conducts the SHA2 256-bit hash of the input
+    /// puts the result into the output buffer.
+    fn hash_sha2_256(input: &[u8], output: &mut [u8; 32]);
+
+    /// Conducts the KECCAK 256-bit hash of the input
+    /// puts the result into the output buffer.
+    fn hash_keccak_256(input: &[u8], output: &mut [u8; 32]);
+
+    /// Conducts the BLAKE2 256-bit hash of the input
+    /// puts the result into the output buffer.
+    fn hash_blake2_256(input: &[u8], output: &mut [u8; 32]);
+
+    /// Conducts the BLAKE2 128-bit hash of the input
+    /// puts the result into the output buffer.
+    fn hash_blake2_128(input: &[u8], output: &mut [u8; 16]);
 }
 
 /// Environmental contract functionality.
diff --git a/core/src/env/engine/off_chain/hashing.rs b/core/src/env/engine/off_chain/hashing.rs
new file mode 100644
index 0000000000..7188175da8
--- /dev/null
+++ b/core/src/env/engine/off_chain/hashing.rs
@@ -0,0 +1,47 @@
+// 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.
+
+//! Implementations of supported cryptographic hash functions.
+
+/// Conduct the BLAKE2 256-bit hash and place the result into `output`.
+pub fn blake2_256(input: &[u8], output: &mut [u8; 32]) {
+    output.copy_from_slice(blake2_rfc::blake2b::blake2b(32, &[], input).as_bytes());
+}
+
+/// Conduct the BLAKE2 128-bit hash and place the result into `output`.
+pub fn blake2_128(input: &[u8], output: &mut [u8; 16]) {
+    output.copy_from_slice(blake2_rfc::blake2b::blake2b(16, &[], input).as_bytes());
+}
+
+/// Conduct the KECCAK 256-bit hash and place the result into `output`.
+pub fn keccak_256(input: &[u8], output: &mut [u8; 32]) {
+    use ::tiny_keccak::{
+        Hasher,
+        Keccak,
+    };
+    let mut keccak = Keccak::v256();
+    keccak.update(input);
+    keccak.finalize(output)
+}
+
+/// Conduct the SHA2 256-bit hash and place the result into `output`.
+pub fn sha2_256(input: &[u8], output: &mut [u8; 32]) {
+    use ::sha2::{
+        Digest,
+        Sha256,
+    };
+    let mut hasher = Sha256::new();
+    hasher.input(input);
+    output.copy_from_slice(&hasher.result());
+}
diff --git a/core/src/env/engine/off_chain/impls.rs b/core/src/env/engine/off_chain/impls.rs
index 58da8668e3..e5d27675e4 100644
--- a/core/src/env/engine/off_chain/impls.rs
+++ b/core/src/env/engine/off_chain/impls.rs
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 use super::{
+    hashing,
     Account,
     EnvInstance,
 };
@@ -111,6 +112,22 @@ impl Env for EnvInstance {
     fn println(&mut self, content: &str) {
         self.console.println(content)
     }
+
+    fn hash_keccak_256(input: &[u8], output: &mut [u8; 32]) {
+        hashing::keccak_256(input, output)
+    }
+
+    fn hash_blake2_256(input: &[u8], output: &mut [u8; 32]) {
+        hashing::blake2_256(input, output)
+    }
+
+    fn hash_blake2_128(input: &[u8], output: &mut [u8; 16]) {
+        hashing::blake2_128(input, output)
+    }
+
+    fn hash_sha2_256(input: &[u8], output: &mut [u8; 32]) {
+        hashing::sha2_256(input, output)
+    }
 }
 
 impl TypedEnv for EnvInstance {
diff --git a/core/src/env/engine/off_chain/mod.rs b/core/src/env/engine/off_chain/mod.rs
index 29945e7807..ab91134431 100644
--- a/core/src/env/engine/off_chain/mod.rs
+++ b/core/src/env/engine/off_chain/mod.rs
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 mod db;
+mod hashing;
 mod impls;
 mod runtime_calls;
 mod runtime_storage;
diff --git a/core/src/env/engine/on_chain/ext.rs b/core/src/env/engine/on_chain/ext.rs
index c168e10a69..72f308bc86 100644
--- a/core/src/env/engine/on_chain/ext.rs
+++ b/core/src/env/engine/on_chain/ext.rs
@@ -101,6 +101,11 @@ mod sys {
 
         pub fn ext_random_seed(subject_ptr: u32, subject_len: u32);
         pub fn ext_println(str_ptr: u32, str_len: u32);
+
+        pub fn ext_hash_keccak_256(input_ptr: u32, input_len: u32, output_ptr: u32);
+        pub fn ext_hash_blake2_256(input_ptr: u32, input_len: u32, output_ptr: u32);
+        pub fn ext_hash_blake2_128(input_ptr: u32, input_len: u32, output_ptr: u32);
+        pub fn ext_hash_sha2_256(input_ptr: u32, input_len: u32, output_ptr: u32);
     }
 }
 
@@ -301,3 +306,23 @@ pub fn println(content: &str) {
     let bytes = content.as_bytes();
     unsafe { sys::ext_println(bytes.as_ptr() as u32, bytes.len() as u32) }
 }
+
+macro_rules! impl_hash_fn {
+    ( $name:ident, $bytes_result:literal ) => {
+        paste::item! {
+            pub fn [<hash_ $name>](input: &[u8], output: &mut [u8; $bytes_result]) {
+                unsafe {
+                    sys::[<ext_hash_ $name>](
+                        input.as_ptr() as u32,
+                        input.len() as u32,
+                        output.as_ptr() as u32,
+                    )
+                }
+            }
+        }
+    };
+}
+impl_hash_fn!(sha2_256, 32);
+impl_hash_fn!(keccak_256, 32);
+impl_hash_fn!(blake2_256, 32);
+impl_hash_fn!(blake2_128, 16);
diff --git a/core/src/env/engine/on_chain/impls.rs b/core/src/env/engine/on_chain/impls.rs
index 748ca7c75f..b66fdcff8d 100644
--- a/core/src/env/engine/on_chain/impls.rs
+++ b/core/src/env/engine/on_chain/impls.rs
@@ -178,6 +178,22 @@ impl Env for EnvInstance {
     fn println(&mut self, content: &str) {
         ext::println(content)
     }
+
+    fn hash_keccak_256(input: &[u8], output: &mut [u8; 32]) {
+        ext::hash_keccak_256(input, output)
+    }
+
+    fn hash_blake2_256(input: &[u8], output: &mut [u8; 32]) {
+        ext::hash_blake2_256(input, output)
+    }
+
+    fn hash_blake2_128(input: &[u8], output: &mut [u8; 16]) {
+        ext::hash_blake2_128(input, output)
+    }
+
+    fn hash_sha2_256(input: &[u8], output: &mut [u8; 32]) {
+        ext::hash_sha2_256(input, output)
+    }
 }
 
 impl TypedEnv for EnvInstance {
diff --git a/core/src/env/mod.rs b/core/src/env/mod.rs
index 38ee67dbf6..98000466d9 100644
--- a/core/src/env/mod.rs
+++ b/core/src/env/mod.rs
@@ -24,6 +24,9 @@ mod engine;
 mod error;
 mod types;
 
+#[cfg(test)]
+mod tests;
+
 #[cfg(any(feature = "std", test, doc))]
 #[doc(inline)]
 pub use self::engine::off_chain::test_api as test;
diff --git a/core/src/hash/accumulator.rs b/core/src/hash/accumulator.rs
new file mode 100644
index 0000000000..16b5dd0835
--- /dev/null
+++ b/core/src/hash/accumulator.rs
@@ -0,0 +1,98 @@
+// Copyright 2018-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.
+
+use ink_prelude::vec::Vec;
+
+/// Hash builder that accumulates a buffer on the contract side.
+pub trait Accumulator {
+    /// Resets the buffer which cleans all state from it.
+    ///
+    /// # Note
+    ///
+    /// Useful when using `Vec` or similar as accumulator.
+    fn reset(&mut self);
+    /// Writes the given bytes into the buffer.
+    fn write(&mut self, bytes: &[u8]);
+    /// Returns a shared reference to the slice of the current state of the buffer.
+    fn as_slice(&self) -> &[u8];
+}
+
+impl Accumulator for Vec<u8> {
+    fn reset(&mut self) {
+        self.clear()
+    }
+
+    fn write(&mut self, bytes: &[u8]) {
+        // This could theoretically be speed-up by using `unsafe` `set_len`
+        // and `[u8]` `copy_from_slice` methods.
+        self.extend_from_slice(bytes)
+    }
+
+    fn as_slice(&self) -> &[u8] {
+        self.as_slice()
+    }
+}
+
+impl<'a, T> Accumulator for &'a mut T
+where
+    T: Accumulator,
+{
+    fn reset(&mut self) {
+        <T as Accumulator>::reset(self)
+    }
+
+    fn write(&mut self, bytes: &[u8]) {
+        <T as Accumulator>::write(self, bytes)
+    }
+
+    fn as_slice(&self) -> &[u8] {
+        <T as Accumulator>::as_slice(self)
+    }
+}
+
+/// Wraps a bytes buffer and turns it into an accumulator.
+///
+/// # Panics
+///
+/// Upon hash calculation if the underlying buffer length does not suffice the
+/// needs of the accumulated hash buffer.
+pub struct Wrap<'a> {
+    /// The underlying wrapped buffer.
+    buffer: &'a mut [u8],
+    /// The current length of the filled area.
+    len: usize,
+}
+
+impl<'a> From<&'a mut [u8]> for Wrap<'a> {
+    fn from(buffer: &'a mut [u8]) -> Self {
+        Self { buffer, len: 0 }
+    }
+}
+
+impl<'a> Accumulator for Wrap<'a> {
+    fn reset(&mut self) {
+        self.len = 0;
+    }
+
+    fn write(&mut self, bytes: &[u8]) {
+        let len = self.len;
+        let bytes_len = bytes.len();
+        self.buffer[len..(len + bytes_len)].copy_from_slice(bytes);
+        self.len += bytes_len;
+    }
+
+    fn as_slice(&self) -> &[u8] {
+        &self.buffer[..self.len]
+    }
+}
diff --git a/core/src/hash/builder.rs b/core/src/hash/builder.rs
new file mode 100644
index 0000000000..d823c6d41a
--- /dev/null
+++ b/core/src/hash/builder.rs
@@ -0,0 +1,289 @@
+// Copyright 2018-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.
+
+use super::{
+    hasher::Hasher,
+    Accumulator,
+};
+use core::marker::PhantomData;
+
+/// Generic hash builder to construct hashes given a builder strategy.
+///
+/// - `H` defines the crytographic hash to be conducted.
+/// - `S` defines the strategy.
+///   Currently only accumulating strategy is supported but in the future there
+///   might be support for incremental hash builders.
+///
+/// # Note
+///
+/// This is just a base type and should not be used directly.
+/// Use one of the following concrete hash builders instead:
+///
+/// - [`Sha2x256`](`crate::hash::Sha2x256`)
+/// - [`Keccak256`](`crate::hash::Keccak256`)
+/// - [`Blake2x256`](`crate::hash::Blake2x256`)
+/// - [`Blake2x128`](`crate::hash::Blake2x128`)
+pub struct HashBuilder<H, S> {
+    /// The strategy used to build up the hash.
+    strategy: S,
+    /// The underlying cryptographic hasher.
+    hasher: PhantomData<fn() -> H>,
+}
+
+impl<H, S> scale::Output for HashBuilder<H, S>
+where
+    S: Accumulator,
+{
+    fn write(&mut self, bytes: &[u8]) {
+        <S as Accumulator>::write(&mut self.strategy, bytes)
+    }
+}
+
+impl<H, S> From<S> for HashBuilder<H, S>
+where
+    S: Accumulator,
+{
+    fn from(accumulator: S) -> Self {
+        Self {
+            hasher: Default::default(),
+            strategy: accumulator,
+        }
+    }
+}
+
+pub trait Finalize<H>
+where
+    H: Hasher,
+{
+    fn finalize_using(&self, output: &mut <H as Hasher>::Output);
+    fn finalize(&self) -> <H as Hasher>::Output;
+}
+
+impl<H, S> Finalize<H> for HashBuilder<H, S>
+where
+    H: Hasher,
+    S: Accumulator,
+{
+    fn finalize_using(&self, output: &mut <H as Hasher>::Output) {
+        <H as Hasher>::finalize_immediate(self.strategy.as_slice(), output)
+    }
+
+    fn finalize(&self) -> <H as Hasher>::Output {
+        let mut output = <<H as Hasher>::Output as Default>::default();
+        Self::finalize_using(self, &mut output);
+        output
+    }
+}
+
+impl<H, S> HashBuilder<H, S>
+where
+    Self: Finalize<H> + scale::Output,
+    H: Hasher,
+{
+    /// Conducts the hash for the given bytes.
+    ///
+    /// Puts the resulting hash into the provided output buffer.
+    ///
+    /// # Note
+    ///
+    /// Prefer the simpler [`hash_raw`](`HashBuilder::hash_raw`)
+    /// if you do _not_ need full control over the `output` buffer.
+    pub fn hash_raw_using(&mut self, input: &[u8], output: &mut <H as Hasher>::Output)
+    where
+        H: Hasher,
+    {
+        <Self as scale::Output>::write(self, input);
+        self.finalize_using(output)
+    }
+
+    /// Returns the hash for the given bytes.
+    ///
+    /// # Note
+    ///
+    /// Use [`hash_raw_using`](`HashBuilder::hash_raw_using`)
+    /// if you need full control over the `output` buffer.
+    pub fn hash_raw(&mut self, input: &[u8]) -> <H as Hasher>::Output
+    where
+        H: Hasher,
+    {
+        <Self as scale::Output>::write(self, input);
+        self.finalize()
+    }
+
+    /// Conducts the hash for the encoded input.
+    ///
+    /// Puts the resulting hash into the provided output buffer.
+    ///
+    /// # Note
+    ///
+    /// Prefer the simpler [`hash_encoded`](`HashBuilder::hash_encoded`)
+    /// if you do _not_ need full control over the `output` buffer.
+    ///
+    /// # Examples
+    ///
+    /// In the following examples the `EXPECTED` buffer is defined as:
+    ///
+    /// ```
+    /// const EXPECTED: [u8; 32] = [
+    ///     243, 242, 58, 110, 205, 68, 100, 244, 187, 55, 188, 248,  29, 136, 145, 115,
+    ///     186, 134, 14, 175, 178, 99, 183,  21,   4, 94,  92,  69, 199, 207, 241, 179,
+    /// ];
+    /// ```
+    ///
+    /// ## 1. Using a new `Vec` as accumulating buffer.
+    ///
+    /// This is the simplest way to call this API but does not avoid heap
+    /// allocations.
+    ///
+    /// ```
+    /// # use ink_core::hash::Sha2x256;
+    /// # use ink_prelude::vec::Vec;
+    /// # const EXPECTED: [u8; 32] = [
+    /// #   243, 242, 58, 110, 205, 68, 100, 244, 187, 55, 188, 248,  29, 136, 145, 115,
+    /// #   186, 134, 14, 175, 178, 99, 183,  21,   4, 94,  92,  69, 199, 207, 241, 179,
+    /// # ];
+    /// let hashable = (42, "foo", true); // Implements `core::hash::Hash`
+    /// let mut output = [0x00_u8; 32]; // 256-bit buffer
+    /// let mut hasher = Sha2x256::from(Vec::new());
+    /// hasher.hash_encoded_using(&hashable, &mut output);
+    /// assert_eq!(output, EXPECTED);
+    /// ```
+    ///
+    /// ## 2. Using an existing `Vec` as accumulating buffer.
+    ///
+    /// This API is preferred if the call site already has an allocated buffer
+    /// that it can reuse. This will reset the existing buffer and might grow it.
+    ///
+    /// ```
+    /// # use ink_core::hash::Sha2x256;
+    /// # use ink_prelude::vec::Vec;
+    /// # const EXPECTED: [u8; 32] = [
+    /// #   243, 242, 58, 110, 205, 68, 100, 244, 187, 55, 188, 248,  29, 136, 145, 115,
+    /// #   186, 134, 14, 175, 178, 99, 183,  21,   4, 94,  92,  69, 199, 207, 241, 179,
+    /// # ];
+    /// let hashable = (42, "foo", true); // Implements `core::hash::Hash`
+    /// let mut output = [0x00_u8; 32]; // 256-bit buffer
+    /// let mut accumulator = Vec::with_capacity(32);
+    /// let mut hasher = Sha2x256::from(&mut accumulator);
+    /// hasher.hash_encoded_using(&hashable, &mut output);
+    /// assert_eq!(output, EXPECTED);
+    /// ```
+    ///
+    /// ## 3. Using a wrapped static buffer as accumulating buffer.
+    ///
+    /// This API avoids heap allocation completely but might panic in cases
+    /// where the static buffer is too small.
+    ///
+    /// ```
+    /// # use ink_core::hash::{Sha2x256, Wrap};
+    /// # use ink_prelude::vec::Vec;
+    /// # const EXPECTED: [u8; 32] = [
+    /// #   243, 242, 58, 110, 205, 68, 100, 244, 187, 55, 188, 248,  29, 136, 145, 115,
+    /// #   186, 134, 14, 175, 178, 99, 183,  21,   4, 94,  92,  69, 199, 207, 241, 179,
+    /// # ];
+    /// let hashable = (42, "foo", true); // Implements `core::hash::Hash`
+    /// let mut output = [0x00_u8; 32]; // 256-bit buffer
+    /// let mut accumulator = [0x00_u8; 64];
+    /// let mut hasher = Sha2x256::from(Wrap::from(accumulator.as_mut()));
+    /// hasher.hash_encoded_using(&hashable, &mut output);
+    /// assert_eq!(output, EXPECTED);
+    /// ```
+    pub fn hash_encoded_using<T>(&mut self, input: &T, output: &mut <H as Hasher>::Output)
+    where
+        H: Hasher,
+        T: scale::Encode,
+    {
+        <T as scale::Encode>::encode_to(&input, self);
+        self.finalize_using(output)
+    }
+
+    /// Returns the hash for the encoded input.
+    ///
+    /// # Note
+    ///
+    /// Use [`hash_encoded_using`](`HashBuilder::hash_encoded_using`)
+    /// if you need full control over the `output` buffer.
+    ///
+    /// # Examples
+    ///
+    /// In the following examples the `EXPECTED` buffer is defined as:
+    ///
+    /// ```
+    /// const EXPECTED: [u8; 32] = [
+    ///     243, 242, 58, 110, 205, 68, 100, 244, 187, 55, 188, 248,  29, 136, 145, 115,
+    ///     186, 134, 14, 175, 178, 99, 183,  21,   4, 94,  92,  69, 199, 207, 241, 179,
+    /// ];
+    /// ```
+    ///
+    /// ## 1. Using a new `Vec` as accumulating buffer.
+    ///
+    /// This is the simplest way to call this API but does not avoid heap
+    /// allocations.
+    ///
+    /// ```
+    /// # use ink_core::hash::Sha2x256;
+    /// # use ink_prelude::vec::Vec;
+    /// # const EXPECTED: [u8; 32] = [
+    /// #   243, 242, 58, 110, 205, 68, 100, 244, 187, 55, 188, 248,  29, 136, 145, 115,
+    /// #   186, 134, 14, 175, 178, 99, 183,  21,   4, 94,  92,  69, 199, 207, 241, 179,
+    /// # ];
+    /// let hashable = (42, "foo", true); // Implements `core::hash::Hash`
+    /// let mut hasher = Sha2x256::from(Vec::new());
+    /// assert_eq!(hasher.hash_encoded(&hashable), EXPECTED);
+    /// ```
+    ///
+    /// ## 2. Using an existing `Vec` as accumulating buffer.
+    ///
+    /// This API is preferred if the call site already has an allocated buffer
+    /// that it can reuse. This will reset the existing buffer and might grow it.
+    ///
+    /// ```
+    /// # use ink_core::hash::Sha2x256;
+    /// # use ink_prelude::vec::Vec;
+    /// # const EXPECTED: [u8; 32] = [
+    /// #   243, 242, 58, 110, 205, 68, 100, 244, 187, 55, 188, 248,  29, 136, 145, 115,
+    /// #   186, 134, 14, 175, 178, 99, 183,  21,   4, 94,  92,  69, 199, 207, 241, 179,
+    /// # ];
+    /// let hashable = (42, "foo", true); // Implements `core::hash::Hash`
+    /// let mut accumulator = Vec::with_capacity(32);
+    /// let mut hasher = Sha2x256::from(&mut accumulator);
+    /// assert_eq!(hasher.hash_encoded(&hashable), EXPECTED);
+    /// ```
+    ///
+    /// ## 3. Using a wrapped static buffer as accumulating buffer.
+    ///
+    /// This API avoids heap allocation completely but might panic in cases
+    /// where the static buffer is too small.
+    ///
+    /// ```
+    /// # use ink_core::hash::{Sha2x256, Wrap};
+    /// # use ink_prelude::vec::Vec;
+    /// # const EXPECTED: [u8; 32] = [
+    /// #   243, 242, 58, 110, 205, 68, 100, 244, 187, 55, 188, 248,  29, 136, 145, 115,
+    /// #   186, 134, 14, 175, 178, 99, 183,  21,   4, 94,  92,  69, 199, 207, 241, 179,
+    /// # ];
+    /// let hashable = (42, "foo", true); // Implements `core::hash::Hash`
+    /// let mut accumulator = [0x00_u8; 64];
+    /// let mut hasher = Sha2x256::from(Wrap::from(accumulator.as_mut()));
+    /// assert_eq!(hasher.hash_encoded(&hashable), EXPECTED);
+    /// ```
+    pub fn hash_encoded<T>(&mut self, input: &T) -> <H as Hasher>::Output
+    where
+        H: Hasher,
+        T: scale::Encode,
+    {
+        <T as scale::Encode>::encode_to(&input, self);
+        self.finalize()
+    }
+}
diff --git a/core/src/hash/hasher.rs b/core/src/hash/hasher.rs
new file mode 100644
index 0000000000..e6c6a3ef9f
--- /dev/null
+++ b/core/src/hash/hasher.rs
@@ -0,0 +1,63 @@
+// Copyright 2018-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.
+
+//! Supported cryptographic hashing algorithms.
+
+/// Types that implement this trait are marker types that identify a supported
+/// cryptographic hash function.
+pub trait Hasher {
+    /// The output of the hash function.
+    ///
+    /// # Note
+    ///
+    /// This is a byte slice with varying lengths, e.g. `[u8; 32]`, [`u8; 16]`, etc.
+    type Output: Default;
+
+    /// Finalizes the hash using the underlying procedure.
+    fn finalize_immediate(input: &[u8], output: &mut Self::Output);
+}
+
+macro_rules! impl_hasher_for {
+    (
+        $( #[$doc:meta] )*
+        struct $ty_name:ident($fn_name:ident, $output_len:literal);
+    ) => {
+        $( #[$doc] )*
+        pub enum $ty_name {}
+
+        impl Hasher for $ty_name {
+            type Output = [u8; $output_len];
+
+            fn finalize_immediate(input: &[u8], output: &mut Self::Output) {
+                crate::env::hash::$fn_name(input, output)
+            }
+        }
+    };
+}
+impl_hasher_for! {
+    /// SHA2 256-bit hasher.
+    struct Sha2x256Hasher(sha2_256, 32);
+}
+impl_hasher_for! {
+    /// KECCAK 256-bit hasher.
+    struct Keccak256Hasher(keccak_256, 32);
+}
+impl_hasher_for! {
+    /// BLAKE2 256-bit hasher.
+    struct Blake2x256Hasher(blake2_256, 32);
+}
+impl_hasher_for! {
+    /// BLAKE2 128-bit hasher.
+    struct Blake2x128Hasher(blake2_128, 16);
+}
diff --git a/core/src/hash/mod.rs b/core/src/hash/mod.rs
new file mode 100644
index 0000000000..12521264ec
--- /dev/null
+++ b/core/src/hash/mod.rs
@@ -0,0 +1,36 @@
+// Copyright 2018-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.
+
+//! High-level, built-in and efficient cryptographic hashing routines.
+
+mod accumulator;
+mod builder;
+pub mod hasher;
+
+pub use self::{
+    accumulator::{
+        Accumulator,
+        Wrap,
+    },
+    builder::HashBuilder,
+};
+
+/// SHA2 256-bit hash builder.
+pub type Sha2x256<S> = HashBuilder<hasher::Sha2x256Hasher, S>;
+/// KECCAK 256-bit hash builder.
+pub type Keccak256<S> = HashBuilder<hasher::Keccak256Hasher, S>;
+/// BLAKE2 256-bit hash builder.
+pub type Blake2x256<S> = HashBuilder<hasher::Blake2x256Hasher, S>;
+/// BLAKE2 128-bit hash builder.
+pub type Blake2x128<S> = HashBuilder<hasher::Blake2x128Hasher, S>;
diff --git a/core/src/lib.rs b/core/src/lib.rs
index d9408e2d6b..9efdc71345 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -47,6 +47,7 @@
 extern crate ink_alloc;
 
 pub mod env;
+pub mod hash;
 pub mod storage;
 
 // Needed for derive macros of `core/derive` sub crate.
diff --git a/examples/delegator/Cargo.toml b/examples/delegator/Cargo.toml
index 2c7e62a972..5157728df0 100644
--- a/examples/delegator/Cargo.toml
+++ b/examples/delegator/Cargo.toml
@@ -1,20 +1,20 @@
 [package]
 name = "delegator"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
-ink_core = { version = "2.0.0", path = "../../core", default-features = false }
-ink_lang = { version = "2.0.0", path = "../../lang", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../primitives", default-features = false }
+ink_abi = { version = "2.1.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
+ink_core = { version = "2.1.0", path = "../../core", default-features = false }
+ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
 
-adder = { version = "2.0.0", path = "adder", default-features = false, features = ["ink-as-dependency"] }
-subber = { version = "2.0.0", path = "subber", default-features = false, features = ["ink-as-dependency"] }
-accumulator = { version = "2.0.0", path = "accumulator", default-features = false, features = ["ink-as-dependency"] }
+adder = { version = "2.1.0", path = "adder", default-features = false, features = ["ink-as-dependency"] }
+subber = { version = "2.1.0", path = "subber", default-features = false, features = ["ink-as-dependency"] }
+accumulator = { version = "2.1.0", path = "accumulator", default-features = false, features = ["ink-as-dependency"] }
 
 [dependencies.type-metadata]
 git = "https://github.com/type-metadata/type-metadata.git"
diff --git a/examples/delegator/accumulator/Cargo.toml b/examples/delegator/accumulator/Cargo.toml
index ed2f3d83e0..ee24abe371 100644
--- a/examples/delegator/accumulator/Cargo.toml
+++ b/examples/delegator/accumulator/Cargo.toml
@@ -1,14 +1,14 @@
 [package]
 name = "accumulator"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../../../abi", default-features = false, features = ["derive"], optional = true }
-ink_core = { version = "2.0.0", path = "../../../core", default-features = false }
-ink_lang = { version = "2.0.0", path = "../../../lang", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../../primitives", default-features = false }
+ink_abi = { version = "2.1.0", path = "../../../abi", default-features = false, features = ["derive"], optional = true }
+ink_core = { version = "2.1.0", path = "../../../core", default-features = false }
+ink_lang = { version = "2.1.0", path = "../../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
 
diff --git a/examples/delegator/accumulator/lib.rs b/examples/delegator/accumulator/lib.rs
index 9216496647..d79375d354 100644
--- a/examples/delegator/accumulator/lib.rs
+++ b/examples/delegator/accumulator/lib.rs
@@ -1,4 +1,4 @@
-// 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.
diff --git a/examples/delegator/adder/Cargo.toml b/examples/delegator/adder/Cargo.toml
index 772258baa5..e5f60f8a7d 100644
--- a/examples/delegator/adder/Cargo.toml
+++ b/examples/delegator/adder/Cargo.toml
@@ -1,16 +1,16 @@
 [package]
 name = "adder"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../../../abi", default-features = false, features = ["derive"], optional = true }
-ink_core = { version = "2.0.0", path = "../../../core", default-features = false }
-ink_lang = { version = "2.0.0", path = "../../../lang", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../../primitives", default-features = false }
+ink_abi = { version = "2.1.0", path = "../../../abi", default-features = false, features = ["derive"], optional = true }
+ink_core = { version = "2.1.0", path = "../../../core", default-features = false }
+ink_lang = { version = "2.1.0", path = "../../../lang", default-features = false }
 
-accumulator = { version = "2.0.0", path = "../accumulator", default-features = false, features = ["ink-as-dependency"] }
+accumulator = { version = "2.1.0", path = "../accumulator", default-features = false, features = ["ink-as-dependency"] }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
 
diff --git a/examples/delegator/adder/lib.rs b/examples/delegator/adder/lib.rs
index fb96554d82..8c5d3d509e 100644
--- a/examples/delegator/adder/lib.rs
+++ b/examples/delegator/adder/lib.rs
@@ -1,4 +1,4 @@
-// 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.
diff --git a/examples/delegator/lib.rs b/examples/delegator/lib.rs
index b1d54391f5..15f9a76c21 100644
--- a/examples/delegator/lib.rs
+++ b/examples/delegator/lib.rs
@@ -1,4 +1,4 @@
-// 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.
diff --git a/examples/delegator/subber/Cargo.toml b/examples/delegator/subber/Cargo.toml
index 7490c5f4d1..c86f628d99 100644
--- a/examples/delegator/subber/Cargo.toml
+++ b/examples/delegator/subber/Cargo.toml
@@ -1,16 +1,16 @@
 [package]
 name = "subber"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../../../abi", default-features = false, features = ["derive"], optional = true }
-ink_core = { version = "2.0.0", path = "../../../core", default-features = false }
-ink_lang = { version = "2.0.0", path = "../../../lang", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../../primitives", default-features = false }
+ink_abi = { version = "2.1.0", path = "../../../abi", default-features = false, features = ["derive"], optional = true }
+ink_core = { version = "2.1.0", path = "../../../core", default-features = false }
+ink_lang = { version = "2.1.0", path = "../../../lang", default-features = false }
 
-accumulator = { version = "2.0.0", path = "../accumulator", default-features = false, features = ["ink-as-dependency"] }
+accumulator = { version = "2.1.0", path = "../accumulator", default-features = false, features = ["ink-as-dependency"] }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
 
diff --git a/examples/delegator/subber/lib.rs b/examples/delegator/subber/lib.rs
index 882f36e8bb..76451e7cbd 100644
--- a/examples/delegator/subber/lib.rs
+++ b/examples/delegator/subber/lib.rs
@@ -1,4 +1,4 @@
-// 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.
diff --git a/examples/dns/Cargo.toml b/examples/dns/Cargo.toml
index 46f6dc4ca7..df3840f5a8 100644
--- a/examples/dns/Cargo.toml
+++ b/examples/dns/Cargo.toml
@@ -1,14 +1,14 @@
 [package]
 name = "dns"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
-ink_core = { version = "2.0.0", path = "../../core", default-features = false }
-ink_lang = { version = "2.0.0", path = "../../lang", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../primitives", default-features = false }
+ink_abi = { version = "2.1.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
+ink_core = { version = "2.1.0", path = "../../core", default-features = false }
+ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
 
diff --git a/examples/erc20/Cargo.toml b/examples/erc20/Cargo.toml
index c056909187..b996166f20 100644
--- a/examples/erc20/Cargo.toml
+++ b/examples/erc20/Cargo.toml
@@ -1,14 +1,14 @@
 [package]
 name = "erc20"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
-ink_core = { version = "2.0.0", path = "../../core", default-features = false }
-ink_lang = { version = "2.0.0", path = "../../lang", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../primitives", default-features = false }
+ink_abi = { version = "2.1.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
+ink_core = { version = "2.1.0", path = "../../core", default-features = false }
+ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
 
diff --git a/examples/erc20/src/lib.rs b/examples/erc20/src/lib.rs
index 22b1206857..2fa0e9a8cd 100644
--- a/examples/erc20/src/lib.rs
+++ b/examples/erc20/src/lib.rs
@@ -1,4 +1,4 @@
-// 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.
@@ -142,4 +142,146 @@ mod erc20 {
             *self.allowances.get(&(*owner, *spender)).unwrap_or(&0)
         }
     }
+
+    /// Unit tests.
+    #[cfg(test)]
+    mod tests {
+        /// Imports all the definitions from the outer scope so we can use them here.
+        use super::*;
+        use ink_core::env;
+
+        /// The default constructor does its job.
+        #[test]
+        fn new_works() {
+            // Constructor works.
+            let _erc20 = Erc20::new(100);
+            // Transfer event triggered during initial contruction.
+            assert_eq!(1, env::test::recorded_events().count());
+        }
+
+        /// The total supply was applied.
+        #[test]
+        fn total_supply_works() {
+            // Constructor works.
+            let erc20 = Erc20::new(100);
+            // Transfer event triggered during initial contruction.
+            assert_eq!(env::test::recorded_events().count(), 1);
+            // Get the token total supply.
+            assert_eq!(erc20.total_supply(), 100);
+        }
+
+        /// Get the actual balance of an account.
+        #[test]
+        fn balance_of_works() {
+            // Constructor works
+            let erc20 = Erc20::new(100);
+            // Transfer event triggered during initial contruction
+            assert_eq!(env::test::recorded_events().count(), 1);
+            let accounts = env::test::default_accounts::<env::DefaultEnvTypes>()
+                .expect("Cannot get accounts");
+            // Alice owns all the tokens on deployment
+            assert_eq!(erc20.balance_of(accounts.alice), 100);
+            // Bob does not owns tokens
+            assert_eq!(erc20.balance_of(accounts.bob), 0);
+        }
+
+        #[test]
+        fn transfer_works() {
+            // Constructor works.
+            let mut erc20 = Erc20::new(100);
+            // Transfer event triggered during initial contruction.
+            assert_eq!(1, env::test::recorded_events().count());
+            let accounts = env::test::default_accounts::<env::DefaultEnvTypes>()
+                .expect("Cannot get accounts");
+
+            assert_eq!(erc20.balance_of(accounts.bob), 0);
+            // Alice transfers 10 tokens to Bob.
+            assert_eq!(erc20.transfer(accounts.bob, 10), true);
+            // The second Transfer event takes place.
+            assert_eq!(2, env::test::recorded_events().count());
+            // Bob owns 10 tokens.
+            assert_eq!(erc20.balance_of(accounts.bob), 10);
+        }
+
+        #[test]
+        fn invalid_transfer_should_fail() {
+            // Constructor works.
+            let mut erc20 = Erc20::new(100);
+            // Transfer event triggered during initial contruction.
+            assert_eq!(env::test::recorded_events().count(), 1);
+            let accounts = env::test::default_accounts::<env::DefaultEnvTypes>()
+                .expect("Cannot get accounts");
+
+            assert_eq!(erc20.balance_of(accounts.bob), 0);
+            // Get contract address.
+            let callee =
+                env::account_id::<env::DefaultEnvTypes>().unwrap_or([0x0; 32].into());
+            // Create call
+            let mut data =
+                env::call::CallData::new(env::call::Selector::from_str("balance_of"));
+            data.push_arg(&accounts.bob);
+            // Push the new execution context to set Bob as caller
+            assert_eq!(
+                env::test::push_execution_context::<env::DefaultEnvTypes>(
+                    accounts.bob,
+                    callee,
+                    1000000,
+                    1000000,
+                    data
+                ),
+                ()
+            );
+
+            // Bob fails to transfers 10 tokens to Eve.
+            assert_eq!(erc20.transfer(accounts.eve, 10), false);
+            // Alice owns all the tokens.
+            assert_eq!(erc20.balance_of(accounts.alice), 100);
+            assert_eq!(erc20.balance_of(accounts.bob), 0);
+            assert_eq!(erc20.balance_of(accounts.eve), 0);
+        }
+
+        #[test]
+        fn transfer_from_works() {
+            // Constructor works.
+            let mut erc20 = Erc20::new(100);
+            // Transfer event triggered during initial contruction.
+            assert_eq!(env::test::recorded_events().count(), 1);
+            let accounts = env::test::default_accounts::<env::DefaultEnvTypes>()
+                .expect("Cannot get accounts");
+
+            // Bob fails to transfer tokens owned by Alice.
+            assert_eq!(erc20.transfer_from(accounts.alice, accounts.eve, 10), false);
+            // Alice approves Bob for token transfers on her behalf.
+            assert_eq!(erc20.approve(accounts.bob, 10), true);
+
+            // The approve event takes place.
+            assert_eq!(env::test::recorded_events().count(), 2);
+
+            // Get contract address.
+            let callee =
+                env::account_id::<env::DefaultEnvTypes>().unwrap_or([0x0; 32].into());
+            // Create call.
+            let mut data =
+                env::call::CallData::new(env::call::Selector::from_str("balance_of"));
+            data.push_arg(&accounts.bob);
+            // Push the new execution context to set Bob as caller.
+            assert_eq!(
+                env::test::push_execution_context::<env::DefaultEnvTypes>(
+                    accounts.bob,
+                    callee,
+                    1000000,
+                    1000000,
+                    data
+                ),
+                ()
+            );
+
+            // Bob transfers tokens from Alice to Eve.
+            assert_eq!(erc20.transfer_from(accounts.alice, accounts.eve, 10), true);
+            // The third event takes place.
+            assert_eq!(env::test::recorded_events().count(), 3);
+            // Eve owns tokens.
+            assert_eq!(erc20.balance_of(accounts.eve), 10);
+        }
+    }
 }
diff --git a/examples/erc721/Cargo.toml b/examples/erc721/Cargo.toml
index a8d06e2917..58a6b108db 100644
--- a/examples/erc721/Cargo.toml
+++ b/examples/erc721/Cargo.toml
@@ -1,14 +1,14 @@
 [package]
 name = "erc721"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
-ink_core = { version = "2.0.0", path = "../../core", default-features = false }
-ink_lang = { version = "2.0.0", path = "../../lang", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../primitives", default-features = false }
+ink_abi = { version = "2.1.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
+ink_core = { version = "2.1.0", path = "../../core", default-features = false }
+ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
 
diff --git a/examples/flipper/Cargo.toml b/examples/flipper/Cargo.toml
index 3fb9739e67..5765b8f933 100644
--- a/examples/flipper/Cargo.toml
+++ b/examples/flipper/Cargo.toml
@@ -1,14 +1,14 @@
 [package]
 name = "flipper"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
-ink_core = { version = "2.0.0", path = "../../core", default-features = false }
-ink_lang = { version = "2.0.0", path = "../../lang", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../primitives", default-features = false }
+ink_abi = { version = "2.1.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
+ink_core = { version = "2.1.0", path = "../../core", default-features = false }
+ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
 
diff --git a/examples/flipper/src/lib.rs b/examples/flipper/src/lib.rs
index 0ca55331e5..6876f90b98 100644
--- a/examples/flipper/src/lib.rs
+++ b/examples/flipper/src/lib.rs
@@ -1,4 +1,4 @@
-// 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.
diff --git a/examples/incrementer/Cargo.toml b/examples/incrementer/Cargo.toml
index 7bb7c806d3..0a24fc3f64 100644
--- a/examples/incrementer/Cargo.toml
+++ b/examples/incrementer/Cargo.toml
@@ -1,14 +1,14 @@
 [package]
 name = "incrementer"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
-ink_core = { version = "2.0.0", path = "../../core", default-features = false }
-ink_lang = { version = "2.0.0", path = "../../lang", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../primitives", default-features = false }
+ink_abi = { version = "2.1.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
+ink_core = { version = "2.1.0", path = "../../core", default-features = false }
+ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
 
diff --git a/examples/incrementer/src/lib.rs b/examples/incrementer/src/lib.rs
index df5bd6b84b..efc458044e 100644
--- a/examples/incrementer/src/lib.rs
+++ b/examples/incrementer/src/lib.rs
@@ -1,4 +1,4 @@
-// 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.
diff --git a/examples/multisig_plain/Cargo.toml b/examples/multisig_plain/Cargo.toml
index 0ed5517de6..d0db318718 100755
--- a/examples/multisig_plain/Cargo.toml
+++ b/examples/multisig_plain/Cargo.toml
@@ -1,15 +1,15 @@
 [package]
 name = "multisig_plain"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
-ink_core = { version = "2.0.0", path = "../../core", default-features = false }
-ink_lang = { version = "2.0.0", path = "../../lang", default-features = false }
-ink_prelude = { version = "2.0.0", path = "../../prelude", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../primitives", default-features = false }
+ink_abi = { version = "2.1.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
+ink_core = { version = "2.1.0", path = "../../core", default-features = false }
+ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
+ink_prelude = { version = "2.1.0", path = "../../prelude", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
 
diff --git a/examples/runtime-storage/Cargo.toml b/examples/runtime-storage/Cargo.toml
index a849cc01ae..2e3640db18 100755
--- a/examples/runtime-storage/Cargo.toml
+++ b/examples/runtime-storage/Cargo.toml
@@ -1,17 +1,17 @@
 [package]
 name = "runtime_storage"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
-ink_core = { version = "2.0.0", path = "../../core", default-features = false }
-ink_lang = { version = "2.0.0", path = "../../lang", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../primitives", default-features = false }
+ink_prelude = { version = "2.1.0", path = "../../prelude", default-features = false }
+ink_abi = { version = "2.1.0", path = "../../abi", default-features = false, features = ["derive"], optional = true }
+ink_core = { version = "2.1.0", path = "../../core", default-features = false }
+ink_lang = { version = "2.1.0", path = "../../lang", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
-blake2-rfc = { version = "0.2.18", default-features = false }
 
 [dependencies.type-metadata]
 git = "https://github.com/type-metadata/type-metadata.git"
@@ -33,7 +33,6 @@ crate-type = [
 default = ["test-env"]
 std = [
     "ink_primitives/std",
-    "blake2-rfc/std",
     "ink_abi/std",
     "ink_core/std",
     "scale/std",
diff --git a/examples/runtime-storage/src/lib.rs b/examples/runtime-storage/src/lib.rs
index 3013e3b8d7..daf0448c3d 100755
--- a/examples/runtime-storage/src/lib.rs
+++ b/examples/runtime-storage/src/lib.rs
@@ -1,4 +1,4 @@
-// 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.
@@ -16,24 +16,40 @@
 
 use ink_lang as ink;
 
-mod crypto {
-    /// Do a Blake2 256-bit hash and place result in `dest`.
-    pub fn blake2_256_into(data: &[u8], dest: &mut [u8; 32]) {
-        dest.copy_from_slice(blake2_rfc::blake2b::blake2b(32, &[], data).as_bytes());
-    }
+#[ink::contract(version = "0.1.0")]
+mod runtime {
+    use ink_core::{
+        env,
+        hash::Blake2x128,
+    };
+    use ink_prelude::{
+        format,
+        vec,
+        vec::Vec,
+    };
+    use scale::{
+        Decode,
+        Encode,
+    };
 
-    /// Do a Blake2 256-bit hash and return result.
-    pub fn blake2_256(data: &[u8]) -> [u8; 32] {
-        let mut r = [0; 32];
-        blake2_256_into(data, &mut r);
-        r
+    /// All balance information for an account, mirroring the structure defined in the runtime.
+    /// Copied from [substrate](https://github.com/paritytech/substrate/blob/2c87fe171bc341755a43a3b32d67560469f8daac/frame/system/src/lib.rs#L307)
+    #[derive(Encode, Decode)]
+    pub struct AccountData {
+        free: Balance,
+        _reserved: Balance,
+        _misc_frozen: Balance,
+        _fee_frozen: Balance,
     }
-}
 
-#[ink::contract(version = "0.1.0")]
-mod runtime {
-    use super::crypto;
-    use scale::KeyedVec as _;
+    /// Information of an account, mirroring the structure defined in the runtime
+    /// Copied from [substrate](https://github.com/paritytech/substrate/blob/2c87fe171bc341755a43a3b32d67560469f8daac/frame/system/src/lib.rs#L307)
+    #[derive(Encode, Decode)]
+    pub struct AccountInfo {
+        _nonce: u32,
+        _refcount: u8,
+        data: AccountData,
+    }
 
     /// This simple contract reads a value from runtime storage
     #[ink(storage)]
@@ -43,19 +59,59 @@ mod runtime {
         #[ink(constructor)]
         fn new(&mut self) {}
 
-        /// Returns the account balance, read directly from runtime storage
+        /// Returns an account's free balance, read directly from runtime storage
+        ///
+        /// # Key Scheme
+        ///
+        /// A key for the [substrate storage map]
+        /// (https://github.com/paritytech/substrate/blob/dd97b1478b31a4715df7e88a5ebc6664425fb6c6/frame/support/src/storage/generator/map.rs#L28)
+        /// is constructed with:
+        ///
+        /// ```nocompile
+        /// Twox128(module_prefix) ++ Twox128(storage_prefix) ++ Hasher(encode(key))
+        /// ```
+        ///
+        /// For the `System` module's `Account` map, the [hasher implementation]
+        /// (https://github.com/paritytech/substrate/blob/2c87fe171bc341755a43a3b32d67560469f8daac/frame/system/src/lib.rs#L349)
+        /// is `blake2_128_concat`.
         #[ink(message)]
         fn get_balance(&self, account: AccountId) -> Balance {
-            const BALANCE_OF: &[u8] = b"Balances FreeBalance";
-            let key = crypto::blake2_256(&account.to_keyed_vec(BALANCE_OF));
-            let result = self.env().get_runtime_storage::<Balance>(&key[..]);
-            result.unwrap_or_else(|| Ok(0)).unwrap_or_default()
+            let mut key = vec![
+                // Precomputed: Twox128("System")
+                38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247,
+                // Precomputed: Twox128("Account")
+                185, 157, 136, 14, 198, 129, 121, 156, 12, 243, 14, 136, 134, 55, 29, 169,
+            ];
+
+            let encoded_account = &account.encode();
+
+            let mut blake2_128 = Blake2x128::from(Vec::new());
+            let hashed_account = blake2_128.hash_raw(&encoded_account);
+
+            // The hasher is `Blake2_128Concat` which appends the unhashed account to the hashed account
+            key.extend_from_slice(&hashed_account);
+            key.extend_from_slice(&encoded_account);
+
+            // fetch from runtime storage
+            let result = self.env().get_runtime_storage::<AccountInfo>(&key[..]);
+            match result {
+                Some(Ok(account_info)) => account_info.data.free,
+                Some(Err(err)) => {
+                    env::println(&format!("Error reading AccountInfo {:?}", err));
+                    0
+                }
+                None => {
+                    env::println(&format!("No data at key {:?}", key));
+                    0
+                }
+            }
         }
     }
 
     #[cfg(all(test))]
     mod tests {
         use super::*;
+        use ink_core::env;
 
         #[test]
         fn non_existent_account_returns_zero() {
@@ -63,5 +119,41 @@ mod runtime {
             let account: AccountId = [0u8; 32].into();
             assert_eq!(contract.get_balance(account), 0);
         }
+
+        #[test]
+        fn returns_account_balance_from_storage() {
+            let contract = RuntimeStorage::new();
+            let account: AccountId = [0u8; 32].into();
+            let balance = 1_000_000;
+
+            let account_info = AccountInfo {
+                data: AccountData {
+                    free: balance,
+                    _reserved: 0,
+                    _fee_frozen: 0,
+                    _misc_frozen: 0,
+                },
+                _nonce: 0,
+                _refcount: 0,
+            };
+
+            let encoded_account = &account.encode();
+
+            let mut key = vec![
+                // Precomputed: Twox128("System")
+                38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247,
+                // Precomputed: Twox128("Account")
+                185, 157, 136, 14, 198, 129, 121, 156, 12, 243, 14, 136, 134, 55, 29, 169,
+            ];
+
+            let mut blake2_128 = Blake2x128::from(Vec::new());
+            let hashed_account = blake2_128.hash_raw(&encoded_account);
+
+            key.extend_from_slice(&hashed_account);
+            key.extend_from_slice(&encoded_account);
+
+            env::test::set_runtime_storage(&key, account_info);
+            assert_eq!(contract.get_balance(account), balance);
+        }
     }
 }
diff --git a/lang/Cargo.toml b/lang/Cargo.toml
index c9711bf093..410093f9bd 100644
--- a/lang/Cargo.toml
+++ b/lang/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ink_lang"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
@@ -15,11 +15,11 @@ categories = ["no-std", "embedded"]
 include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
 
 [dependencies]
-ink_core = { version = "2.0.0", path = "../core", default-features = false }
-ink_primitives = { version = "2.0.0", path = "../primitives", default-features = false }
-ink_abi = { version = "2.0.0", path = "../abi", default-features = false, optional = true }
-ink_prelude = { version = "2.0.0", path = "../prelude/", default-features = false }
-ink_lang_macro = { version = "2.0.0", path = "macro", default-features = false }
+ink_core = { version = "2.1.0", path = "../core", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../primitives", default-features = false }
+ink_abi = { version = "2.1.0", path = "../abi", default-features = false, optional = true }
+ink_prelude = { version = "2.1.0", path = "../prelude/", default-features = false }
+ink_lang_macro = { version = "2.1.0", path = "macro", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive", "full"] }
 derive_more = { version = "0.99", default-features = false, features = ["from"] }
diff --git a/lang/macro/Cargo.toml b/lang/macro/Cargo.toml
index 81d7a5df70..56ede98163 100644
--- a/lang/macro/Cargo.toml
+++ b/lang/macro/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ink_lang_macro"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
@@ -15,7 +15,7 @@ categories = ["no-std", "embedded"]
 include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
 
 [dependencies]
-ink_primitives = { version = "2.0.0", path = "../../primitives/", default-features = false }
+ink_primitives = { version = "2.1.0", path = "../../primitives/", default-features = false }
 
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive"] }
 quote = "1"
@@ -30,9 +30,9 @@ derive_more = { version = "0.99", default-features = false, features = ["from"]
 regex = "1.3"
 
 [dev-dependencies]
-ink_abi = { version = "2.0.0", path = "../../abi/" }
-ink_core = { version = "2.0.0", path = "../../core/" }
-ink_lang = { version = "2.0.0", path = "..", default-features = false, features = ["ink-generate-abi"] }
+ink_abi = { version = "2.1.0", path = "../../abi/" }
+ink_core = { version = "2.1.0", path = "../../core/" }
+ink_lang = { version = "2.1.0", path = "..", default-features = false, features = ["ink-generate-abi"] }
 
 trybuild = "1.0.24"
 pretty_assertions = "0.6.1"
diff --git a/prelude/Cargo.toml b/prelude/Cargo.toml
index 83b1f3c7b6..5a70c20c74 100644
--- a/prelude/Cargo.toml
+++ b/prelude/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ink_prelude"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Robin Freyler <robin@parity.io>", "Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml
index d008d9281b..4604f22331 100644
--- a/primitives/Cargo.toml
+++ b/primitives/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ink_primitives"
-version = "2.0.0"
+version = "2.1.0"
 authors = ["Robin Freyler <robin@parity.io>", "Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
@@ -18,7 +18,7 @@ categories = ["no-std", "embedded"]
 include = ["/Cargo.toml", "src/**/*.rs", "/README.md", "/LICENSE"]
 
 [dependencies]
-ink_prelude = { version = "2.0.0", path = "../prelude/", default-features = false }
+ink_prelude = { version = "2.1.0", path = "../prelude/", default-features = false }
 tiny-keccak = { version = "2.0", features = ["keccak"] }
 scale = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive", "full"] }
 


Test patch:
diff --git a/core/src/env/tests.rs b/core/src/env/tests.rs
new file mode 100644
index 0000000000..a6ed2ebeb0
--- /dev/null
+++ b/core/src/env/tests.rs
@@ -0,0 +1,66 @@
+// 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.
+
+use crate::env;
+
+static TEST_INPUT: &[u8] = b"DEAD_BEEF";
+
+#[test]
+fn test_hash_keccak_256() {
+    let mut output = [0x00_u8; 32];
+    env::hash::keccak_256(TEST_INPUT, &mut output);
+    assert_eq!(
+        output,
+        [
+            24, 230, 209, 59, 127, 30, 158, 244, 60, 177, 132, 150, 167, 244, 64, 69,
+            184, 123, 185, 44, 211, 199, 208, 179, 14, 64, 126, 140, 217, 69, 36, 216
+        ]
+    );
+}
+
+#[test]
+fn test_hash_sha2_256() {
+    let mut output = [0x00_u8; 32];
+    env::hash::sha2_256(TEST_INPUT, &mut output);
+    assert_eq!(
+        output,
+        [
+            136, 15, 25, 218, 88, 54, 49, 152, 115, 168, 147, 189, 207, 171, 243, 129,
+            161, 76, 15, 141, 197, 106, 111, 213, 19, 197, 133, 219, 181, 233, 195, 120
+        ]
+    );
+}
+
+#[test]
+fn test_hash_blake2_256() {
+    let mut output = [0x00_u8; 32];
+    env::hash::blake2_256(TEST_INPUT, &mut output);
+    assert_eq!(
+        output,
+        [
+            244, 247, 235, 182, 194, 161, 28, 69, 34, 106, 237, 7, 57, 87, 190, 12, 92,
+            171, 91, 176, 135, 52, 247, 94, 8, 112, 94, 183, 140, 101, 208, 120
+        ]
+    );
+}
+
+#[test]
+fn test_hash_blake2_128() {
+    let mut output = [0x00_u8; 16];
+    env::hash::blake2_128(TEST_INPUT, &mut output);
+    assert_eq!(
+        output,
+        [180, 158, 48, 21, 171, 163, 217, 175, 145, 160, 25, 159, 213, 142, 103, 242]
+    );
+}