// Copyright 2017 Parity Technologies (UK) Ltd. // This file is part of Substrate Demo. // Substrate Demo is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Substrate Demo is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Substrate Demo. If not, see . //! Means of mixing a series of hashes to create a single secure hash. //! //! Described in http://www.cs.huji.ac.il/~nati/PAPERS/coll_coin_fl.pdf #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] #[cfg(feature = "std")] use std::ops::{BitAnd, BitOr}; #[cfg(not(feature = "std"))] use core::ops::{BitAnd, BitOr}; pub const MAX_DEPTH: usize = 17; fn sub_mix(seeds: &[T]) -> T where T: BitAnd + BitOr + Copy { (seeds[0] & seeds[1]) | (seeds[1] & seeds[2]) | (seeds[0] & seeds[2]) } /// Mix a slice. pub fn triplet_mix(seeds: &[T]) -> Result where T: BitAnd + BitOr, T: Default + Copy { Ok(seeds.iter().cloned().triplet_mix()) } /// The mixed trait for mixing a sequence. pub trait TripletMix { /// The items in the sequence and simultaneously the return of the mixing. type Item; /// The output of the mixing algorithm on the sequence. Items in the sequence beyond /// the largest power of three that fits within the the sequence up until `3 ** MAX_DEPTH` /// are ignored. fn triplet_mix(self) -> Self::Item; } impl TripletMix for I where I: Iterator, T: BitAnd + BitOr + Default + Copy { type Item = T; fn triplet_mix(self) -> Self::Item { let mut accum = [[T::default(); 3]; MAX_DEPTH]; let mut result = T::default(); for (i, seed) in self.enumerate() { accum[0][i % 3] = seed; let mut index_at_depth = i; for depth in 0..MAX_DEPTH { if index_at_depth % 3 != 2 { break; } index_at_depth /= 3; result = sub_mix(&accum[depth]); // end of the threesome at depth. if depth == MAX_DEPTH - 1 { // end of our stack - bail with result. break; } else { // save in the stack for parent computation accum[depth + 1][index_at_depth % 3] = result; } } } result } } #[cfg(test)] mod tests { use super::*; #[test] fn sub_mix_works() { assert_eq!(sub_mix(&[0, 0, 0][..]), 0); assert_eq!(sub_mix(&[0, 0, 1][..]), 0); assert_eq!(sub_mix(&[0, 1, 0][..]), 0); assert_eq!(sub_mix(&[0, 1, 1][..]), 1); assert_eq!(sub_mix(&[1, 0, 0][..]), 0); assert_eq!(sub_mix(&[1, 0, 1][..]), 1); assert_eq!(sub_mix(&[1, 1, 0][..]), 1); assert_eq!(sub_mix(&[1, 1, 1][..]), 1); assert_eq!(sub_mix(&[0, 0, 0][..]), 0); assert_eq!(sub_mix(&[0, 0, 2][..]), 0); assert_eq!(sub_mix(&[0, 2, 0][..]), 0); assert_eq!(sub_mix(&[0, 2, 2][..]), 2); assert_eq!(sub_mix(&[2, 0, 0][..]), 0); assert_eq!(sub_mix(&[2, 0, 2][..]), 2); assert_eq!(sub_mix(&[2, 2, 0][..]), 2); assert_eq!(sub_mix(&[2, 2, 2][..]), 2); } #[test] fn triplet_mix_works_on_first_level() { assert_eq!(triplet_mix(&[0, 0, 0][..]).unwrap(), 0); assert_eq!(triplet_mix(&[0, 0, 1][..]).unwrap(), 0); assert_eq!(triplet_mix(&[0, 1, 0][..]).unwrap(), 0); assert_eq!(triplet_mix(&[0, 1, 1][..]).unwrap(), 1); assert_eq!(triplet_mix(&[1, 0, 0][..]).unwrap(), 0); assert_eq!(triplet_mix(&[1, 0, 1][..]).unwrap(), 1); assert_eq!(triplet_mix(&[1, 1, 0][..]).unwrap(), 1); assert_eq!(triplet_mix(&[1, 1, 1][..]).unwrap(), 1); assert_eq!(triplet_mix(&[0, 0, 0][..]).unwrap(), 0); assert_eq!(triplet_mix(&[0, 0, 2][..]).unwrap(), 0); assert_eq!(triplet_mix(&[0, 2, 0][..]).unwrap(), 0); assert_eq!(triplet_mix(&[0, 2, 2][..]).unwrap(), 2); assert_eq!(triplet_mix(&[2, 0, 0][..]).unwrap(), 0); assert_eq!(triplet_mix(&[2, 0, 2][..]).unwrap(), 2); assert_eq!(triplet_mix(&[2, 2, 0][..]).unwrap(), 2); assert_eq!(triplet_mix(&[2, 2, 2][..]).unwrap(), 2); } #[test] fn triplet_mix_works_on_second_level() { assert_eq!(triplet_mix(&[0, 0, 0, 0, 0, 1, 0, 1, 0][..]).unwrap(), 0); assert_eq!(triplet_mix(&[0, 1, 1, 1, 0, 0, 1, 0, 1][..]).unwrap(), 1); assert_eq!(triplet_mix(&[1, 1, 0, 1, 1, 1, 0, 0, 0][..]).unwrap(), 1); } #[test] fn triplet_mix_works_on_third_level() { assert_eq!(triplet_mix(&[0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0][..]).unwrap(), 1); } }