Unverified Commit 3f36f02e authored by Hero Bird's avatar Hero Bird Committed by GitHub
Browse files

Implement new crypto hash API (#499)

* [core] add new crypto hash API

* [core] move new crypto hash definitions to dedicated module

* [core] integrate the new env::hash module into core::storage module

* [core] fix LayoutCryptoHasher impls

* [core] add doc test to hash_encoded

* [core] fix minor things in lazy hash map

* [core] apply rustfmt

* [core] remove old crypto hash module

* [core] base DynamicAllocation::key implementation on new hash API

* [core] remove old hash impls from on-chain env

* [core] make DynamicAllocation::key impl work on Wasm32 target

* [core] remove the entire deprecated ink_core::hash module
parent 7838c861
Pipeline #109181 passed with stages
in 8 minutes and 7 seconds
......@@ -28,6 +28,8 @@ cfg-if = "0.1"
array-init = "0.1"
generic-array = "0.14.1"
paste = "0.1"
arrayref = "0.3"
static_assertions = "1.1"
# Hashes for the off-chain environment.
sha2 = { version = "0.9", optional = true }
......
......@@ -240,34 +240,28 @@ macro_rules! gen_tests_for_backend {
mod lazyhmap_backend {
use super::*;
use ink_core::{
hash::hasher::Blake2x256Hasher,
env::hash::Blake2x256,
storage::lazy::lazy_hmap::{
Entry,
LazyHashMap,
},
};
gen_tests_for_backend!(LazyHashMap<i32, i32, Blake2x256Hasher>);
gen_tests_for_backend!(LazyHashMap<i32, i32, Blake2x256>);
pub fn insert(
hmap: &mut LazyHashMap<i32, i32, Blake2x256Hasher>,
hmap: &mut LazyHashMap<i32, i32, Blake2x256>,
key: i32,
value: i32,
) -> Option<i32> {
hmap.put_get(&key, Some(value))
}
pub fn take(
hmap: &mut LazyHashMap<i32, i32, Blake2x256Hasher>,
key: &i32,
) -> Option<i32> {
pub fn take(hmap: &mut LazyHashMap<i32, i32, Blake2x256>, key: &i32) -> Option<i32> {
hmap.put_get(key, None)
}
pub fn contains_key(
hmap: &LazyHashMap<i32, i32, Blake2x256Hasher>,
key: &i32,
) -> bool {
pub fn contains_key(hmap: &LazyHashMap<i32, i32, Blake2x256>, key: &i32) -> bool {
hmap.get(key).is_some()
}
......
......@@ -29,6 +29,10 @@ use crate::env::{
EnvInstance,
OnInstance,
},
hash::{
CryptoHash,
HashOutput,
},
EnvTypes,
Result,
Topics,
......@@ -513,40 +517,37 @@ pub fn println(content: &str) {
<EnvInstance as OnInstance>::on_instance(|instance| Env::println(instance, content))
}
/// Built-in efficient cryptographic hash functions.
pub mod hash {
use super::*;
/// Conducts the crypto hash of the given input and stores the result in `output`.
pub fn hash_bytes<H>(input: &[u8], output: &mut <H as HashOutput>::Type)
where
H: CryptoHash,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
instance.hash_bytes::<H>(input, output)
})
}
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)
);
/// Conducts the crypto hash of the given encoded input and stores the result in `output`.
///
/// # Example
///
/// ```
/// # use ink_core::env::hash::{Sha2x256, HashOutput};
/// 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 encodable = (42, "foo", true); // Implements `scale::Encode`
/// let mut output = <Sha2x256 as HashOutput>::Type::default(); // 256-bit buffer
/// ink_core::env::hash_encoded::<Sha2x256, _>(&encodable, &mut output);
/// assert_eq!(output, EXPECTED);
/// ```
pub fn hash_encoded<H, T>(input: &T, output: &mut <H as HashOutput>::Type)
where
H: CryptoHash,
T: scale::Encode,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
instance.hash_encoded::<H, T>(input, output)
})
}
......@@ -18,6 +18,10 @@ use crate::env::{
CallParams,
CreateParams,
},
hash::{
CryptoHash,
HashOutput,
},
EnvTypes,
Result,
Topics,
......@@ -113,21 +117,16 @@ 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 crypto hash of the given input and stores the result in `output`.
fn hash_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type)
where
H: CryptoHash;
/// 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]);
/// Conducts the crypto hash of the given encoded input and stores the result in `output`.
fn hash_encoded<H, T>(&mut self, input: &T, output: &mut <H as HashOutput>::Type)
where
H: CryptoHash,
T: scale::Encode;
/// Calls the chain extension with the given ID and inputs.
///
......
......@@ -222,7 +222,7 @@ mod tests {
fn empty_args_works() {
let empty_list = ArgumentList::empty();
let encoded = scale::Encode::encode(&empty_list);
assert_eq!(encoded, Vec::new());
assert_eq!(encoded, <Vec<u8>>::new());
}
#[test]
......
......@@ -19,15 +19,21 @@ use super::{
};
use crate::env::{
call::{
utils::ReturnType,
CallParams,
CreateParams,
utils::ReturnType,
},
Blake2x128,
Blake2x256,
CryptoHash,
Env,
EnvError,
EnvTypes,
HashOutput,
Keccak256,
Result,
ReturnFlags,
Sha2x256,
Topics,
TypedEnv,
};
......@@ -61,6 +67,54 @@ impl EnvInstance {
}
}
impl CryptoHash for Blake2x128 {
fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
type OutputType = [u8; 16];
static_assertions::assert_type_eq_all!(
<Blake2x128 as HashOutput>::Type,
OutputType
);
let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 16);
hashing::blake2b_128(input, output);
}
}
impl CryptoHash for Blake2x256 {
fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
type OutputType = [u8; 32];
static_assertions::assert_type_eq_all!(
<Blake2x256 as HashOutput>::Type,
OutputType
);
let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32);
hashing::blake2b_256(input, output);
}
}
impl CryptoHash for Sha2x256 {
fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
type OutputType = [u8; 32];
static_assertions::assert_type_eq_all!(
<Sha2x256 as HashOutput>::Type,
OutputType
);
let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32);
hashing::sha2_256(input, output);
}
}
impl CryptoHash for Keccak256 {
fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
type OutputType = [u8; 32];
static_assertions::assert_type_eq_all!(
<Keccak256 as HashOutput>::Type,
OutputType
);
let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32);
hashing::keccak_256(input, output);
}
}
impl Env for EnvInstance {
fn set_contract_storage<V>(&mut self, key: &Key, value: &V)
where
......@@ -116,20 +170,20 @@ impl Env for EnvInstance {
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::blake2b_256(input, output)
}
fn hash_blake2_128(input: &[u8], output: &mut [u8; 16]) {
hashing::blake2b_128(input, output)
fn hash_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type)
where
H: CryptoHash,
{
<H as CryptoHash>::hash(input, output)
}
fn hash_sha2_256(input: &[u8], output: &mut [u8; 32]) {
hashing::sha2_256(input, output)
fn hash_encoded<H, T>(&mut self, input: &T, output: &mut <H as HashOutput>::Type)
where
H: CryptoHash,
T: scale::Encode,
{
let encoded = input.encode();
self.hash_bytes::<H>(&encoded[..], output)
}
#[cfg(feature = "ink-unstable-chain-extensions")]
......@@ -143,7 +197,11 @@ impl Env for EnvInstance {
}
impl EnvInstance {
fn transfer_impl<T>(&mut self, destination: T::AccountId, value: T::Balance) -> Result<()>
fn transfer_impl<T>(
&mut self,
destination: T::AccountId,
value: T::Balance,
) -> Result<()>
where
T: EnvTypes,
{
......
......@@ -20,20 +20,62 @@ use super::{
};
use crate::env::{
call::{
utils::ReturnType,
CallParams,
CreateParams,
utils::ReturnType,
},
Blake2x128,
Blake2x256,
CryptoHash,
Env,
EnvError,
EnvTypes,
Keccak256,
HashOutput,
Result,
ReturnFlags,
Sha2x256,
Topics,
TypedEnv,
};
use ink_primitives::Key;
impl CryptoHash for Blake2x128 {
fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
type OutputType = [u8; 16];
static_assertions::assert_type_eq_all!(<Blake2x128 as HashOutput>::Type, OutputType);
let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 16);
ext::hash_blake2_128(input, output);
}
}
impl CryptoHash for Blake2x256 {
fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
type OutputType = [u8; 32];
static_assertions::assert_type_eq_all!(<Blake2x256 as HashOutput>::Type, OutputType);
let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32);
ext::hash_blake2_256(input, output);
}
}
impl CryptoHash for Sha2x256 {
fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
type OutputType = [u8; 32];
static_assertions::assert_type_eq_all!(<Sha2x256 as HashOutput>::Type, OutputType);
let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32);
ext::hash_sha2_256(input, output);
}
}
impl CryptoHash for Keccak256 {
fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
type OutputType = [u8; 32];
static_assertions::assert_type_eq_all!(<Keccak256 as HashOutput>::Type, OutputType);
let output: &mut OutputType = arrayref::array_mut_ref!(output, 0, 32);
ext::hash_keccak_256(input, output);
}
}
impl From<ext::Error> for EnvError {
fn from(ext_error: ext::Error) -> Self {
match ext_error {
......@@ -141,28 +183,25 @@ impl Env for EnvInstance {
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_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type)
where
H: CryptoHash,
{
<H as CryptoHash>::hash(input, output)
}
fn hash_sha2_256(input: &[u8], output: &mut [u8; 32]) {
ext::hash_sha2_256(input, output)
fn hash_encoded<H, T>(&mut self, input: &T, output: &mut <H as HashOutput>::Type)
where
H: CryptoHash,
T: scale::Encode,
{
let mut scope = self.scoped_buffer();
let enc_input = scope.take_encoded(input);
<H as CryptoHash>::hash(enc_input, output)
}
#[cfg(feature = "ink-unstable-chain-extensions")]
fn call_chain_extension<I, O>(
&mut self,
func_id: u32,
input: &I,
) -> Result<O>
fn call_chain_extension<I, O>(&mut self, func_id: u32, input: &I) -> Result<O>
where
I: scale::Encode,
O: scale::Decode,
......
// 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.
//! Provides type definitions and traits for the built-in cryptographic hashes.
/// The output type of a built-in cryptographic hash function.
pub trait HashOutput: private::Sealed {
/// The output type of the crypto hash.
///
/// This should be a byte array with some constant size such as `[u8; 32]`.
type Type: Default;
}
/// Types that are usable as built-in cryptographic hashes.
pub trait CryptoHash: HashOutput + private::Sealed {
/// Hashes the given raw byte input and copies the result into `output`.
fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type);
}
/// The SHA2 crypto hash with 256-bit output.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Sha2x256 {}
/// The KECCAK crypto hash with 256-bit output.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Keccak256 {}
/// The BLAKE2 crypto hash with 256-bit output.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Blake2x256 {}
/// The BLAKE2 crypto hash with 128-bit output.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Blake2x128 {}
mod private {
/// Seals the implementation of `CryptoHash` and `HashOutput`.
pub trait Sealed {}
}
impl private::Sealed for Sha2x256 {}
impl private::Sealed for Keccak256 {}
impl private::Sealed for Blake2x256 {}
impl private::Sealed for Blake2x128 {}
impl HashOutput for Sha2x256 {
type Type = [u8; 32];
}
impl HashOutput for Keccak256 {
type Type = [u8; 32];
}
impl HashOutput for Blake2x256 {
type Type = [u8; 32];
}
impl HashOutput for Blake2x128 {
type Type = [u8; 16];
}
......@@ -23,6 +23,7 @@ mod backend;
pub mod call;
mod engine;
mod error;
pub mod hash;
mod types;
#[cfg(test)]
......@@ -43,6 +44,14 @@ pub use self::{
EnvError,
Result,
},
hash::{
Blake2x128,
Blake2x256,
CryptoHash,
HashOutput,
Keccak256,
Sha2x256,
},
types::{
AccountId,
Clear,
......
......@@ -19,7 +19,7 @@ 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);
env::hash_bytes::<env::hash::Keccak256>(TEST_INPUT, &mut output);
assert_eq!(
output,
[
......@@ -32,7 +32,7 @@ fn test_hash_keccak_256() {
#[test]
fn test_hash_sha2_256() {
let mut output = [0x00_u8; 32];
env::hash::sha2_256(TEST_INPUT, &mut output);
env::hash_bytes::<env::hash::Sha2x256>(TEST_INPUT, &mut output);
assert_eq!(
output,
[
......@@ -45,7 +45,7 @@ fn test_hash_sha2_256() {
#[test]
fn test_hash_blake2_256() {
let mut output = [0x00_u8; 32];
env::hash::blake2_256(TEST_INPUT, &mut output);
env::hash_bytes::<env::hash::Blake2x256>(TEST_INPUT, &mut output);
assert_eq!(
output,
[
......@@ -58,7 +58,7 @@ fn test_hash_blake2_256() {
#[test]
fn test_hash_blake2_128() {
let mut output = [0x00_u8; 16];
env::hash::blake2_128(TEST_INPUT, &mut output);
env::hash_bytes::<env::hash::Blake2x128>(TEST_INPUT, &mut output);
assert_eq!(
output,
[180, 158, 48, 21, 171, 163, 217, 175, 145, 160, 25, 159, 213, 142, 103, 242]
......
// 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.
#[cfg(feature = "std")]
use ::std::io::{
Result as IoResult,
Write,
};
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()
}
}