substrate_runtime_hasher/
lib.rs

1use blake2::digest::{Update, VariableOutput};
2use blake2::VarBlake2b;
3use codec::Encode;
4use std::convert::TryInto;
5
6/// Expected size of the hash
7pub const SIZE: usize = 32;
8
9/// Type for our Proposal hash
10pub type ProposalHash = [u8; SIZE];
11
12/// The PREFIX is prepended to the data before hashing
13const PREFIX: [u8; 2] = [0x00, 0x03];
14
15/// This struct is a container for whatever we calculated.
16#[derive(Debug)]
17pub struct SrhResult {
18    /// This is the PropsalHash itself, not encoded
19    pub hash: ProposalHash,
20
21    /// Hex encoded proposal hash.
22    pub encodedd_hash: String,
23}
24
25/// Concatenate 2 arrays.
26pub fn concatenate_arrays<T: Clone>(x: &[T], y: &[T]) -> Vec<T> {
27    let mut concat = x.to_vec();
28    concat.extend_from_slice(y);
29    concat
30}
31
32/// Generate our result object
33pub fn get_result(buffer: &[u8]) -> SrhResult {
34    let res = buffer.using_encoded(|ref wasm_blob| {
35        let hash = get_proposal_hash(wasm_blob);
36
37        return SrhResult {
38            hash,
39            encodedd_hash: hex::encode(hash),
40        };
41    });
42    res
43}
44
45/// Calculate the proposal hash
46///
47/// # Arguments
48/// * `wasm_blob` - The WASM blob
49/// # Returns
50/// * `ProposalHash` - The hash of the proposal as calculated on chain
51fn get_proposal_hash(wasm_blob: &[u8]) -> ProposalHash {
52    let mut hasher = VarBlake2b::new(SIZE).unwrap();
53    hasher.update(concatenate_arrays(&PREFIX, &wasm_blob));
54    let mut result: ProposalHash = [0; SIZE];
55    hasher.finalize_variable(|res| {
56        result = res.try_into().expect("slice with incorrect length");
57    });
58    result
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn test_hash() {
67        assert_eq!(
68            get_proposal_hash(&[1, 2, 42]),
69            [
70                156, 244, 243, 93, 21, 8, 113, 238, 186, 17, 20, 52, 240, 236, 140, 15, 108, 26,
71                86, 5, 152, 148, 91, 162, 108, 168, 3, 65, 254, 162, 114, 46
72            ]
73        );
74    }
75
76    #[test]
77    fn test_hash_length() {
78        assert_eq!(32, get_proposal_hash(&[0]).len());
79    }
80
81    #[test]
82    fn test_get_result() {
83        let res = get_result(&[1, 2, 42]);
84        assert!(
85            res.encodedd_hash == "9388ba11b3f2a5db3ef9bf237f1c88ffb369d77ffa843fc67570c89c09fa9c0e"
86        );
87    }
88
89    #[test]
90    fn test_long_input() {
91        const SIZE_8MB: usize = 8 * 1024 * 1024;
92        let res = get_result(&[0; SIZE_8MB]);
93        assert!(
94            res.encodedd_hash == "9348da94fcffe94318313f8ce237211a7fd6c1531ab21b61606a1f7eeb8b2409"
95        );
96    }
97}