1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use crate::prelude::*;

/// Hash anything that implements [`TryInto<Entry>`].
///
/// Hashes are typed in Holochain, e.g. [`HeaderHash`] and [`EntryHash`] are different and yield different
/// bytes for a given value. This ensures correctness and allows type based dispatch in various
/// areas of the codebase.
///
/// Usually you want to hash a value that you want to reference on the DHT with [`get`] etc. because
/// it represents some domain-specific data sourced externally or generated within the wasm.
/// [`HeaderHash`] hashes are _always_ generated by the process of committing something to a local
/// chain. Every host function that commits an entry returns the new [`HeaderHash`]. The [`HeaderHash`] can
/// also be used with [`get`] etc. to retreive a _specific_ element from the DHT rather than the
/// oldest live element.
/// However there is no way to _generate_ a header hash directly from a header from inside wasm.
/// [`Element`] values (entry+header pairs returned by [`get`] etc.) contain prehashed header structs
/// called [`HeaderHashed`], which is composed of a [`HeaderHash`] alongside the "raw" [`Header`] value. Generally the pre-hashing is
/// more efficient than hashing headers ad-hoc as hashing always needs to be done at the database
/// layer, so we want to re-use that as much as possible.
/// The header hash can be extracted from the Element as `element.header_hashed().as_hash()`.
///
/// @todo is there any use-case that can't be satisfied by the `header_hashed` approach?
///
/// Anything that is annotated with #[hdk_entry( .. )] or entry_def!( .. ) implements this so is
/// compatible automatically.
///
/// [`hash_entry`] is "dumb" in that it doesn't check that the entry is defined, committed, on the DHT or
/// any other validation, it simply generates the hash for the serialized representation of
/// something in the same way that the DHT would.
///
/// It is strongly recommended that you use the [`hash_entry`] function to calculate hashes to avoid
/// inconsistencies between hashes in the wasm guest and the host.
/// For example, a lot of the crypto crates in rust compile to wasm so in theory could generate the
/// hash in the guest, but there is the potential that the serialization logic could be slightly
/// different, etc.
///
/// ```ignore
/// #[hdk_entry(id="foo")]
/// struct Foo;
///
/// let foo_hash = hash_entry(Foo)?;
/// ```
pub fn hash_entry<I, E>(input: I) -> ExternResult<EntryHash>
where
    Entry: TryFrom<I, Error = E>,
    WasmError: From<E>,
{
    match HDK.with(|h| h.borrow().hash(HashInput::Entry(Entry::try_from(input)?)))? {
        HashOutput::Entry(entry_hash) => Ok(entry_hash),
        _ => unreachable!(),
    }
}

/// Hash a `Header` into a `HeaderHash`.
///
/// [`hash_entry`] has more of a discussion around different hash types and how
/// they are used within the HDK.
///
/// It is strongly recommended to use [`hash_header`] to calculate the hash rather than hand rolling an in-wasm solution.
/// Any inconsistencies in serialization or hash handling will result in dangling references to things due to a "corrupt" hash.
///
/// Note that usually relevant HDK functions return a [`HeaderHashed`] or [`SignedHeaderHashed`] which already has associated methods to access the `HeaderHash` of the inner `Header`.
/// In normal usage it is unlikely to be required to separately hash a [`Header`] like this.
pub fn hash_header(input: Header) -> ExternResult<HeaderHash> {
    match HDK.with(|h| h.borrow().hash(HashInput::Header(input)))? {
        HashOutput::Header(header_hash) => Ok(header_hash),
        _ => unreachable!(),
    }
}

/// Hash arbitrary bytes using BLAKE2b.
/// This is the same algorithm used by holochain for typed hashes.
/// Notably the output hash length is configurable.
pub fn hash_blake2b(input: Vec<u8>, output_len: u8) -> ExternResult<Vec<u8>> {
    match HDK.with(|h| h.borrow().hash(HashInput::Blake2B(input, output_len)))? {
        HashOutput::Blake2B(vec) => Ok(vec),
        _ => unreachable!(),
    }
}

/// @todo - not implemented on the host
pub fn hash_sha256(input: Vec<u8>) -> ExternResult<Vec<u8>> {
    match HDK.with(|h| h.borrow().hash(HashInput::Sha256(input)))? {
        HashOutput::Sha256(hash) => Ok(hash.as_ref().to_vec()),
        _ => unreachable!(),
    }
}

/// @todo - not implemented on the host
pub fn hash_sha512(input: Vec<u8>) -> ExternResult<Vec<u8>> {
    match HDK.with(|h| h.borrow().hash(HashInput::Sha512(input)))? {
        HashOutput::Sha512(hash) => Ok(hash.as_ref().to_vec()),
        _ => unreachable!(),
    }
}

pub fn hash_keccak256(input: Vec<u8>) -> ExternResult<Vec<u8>> {
    match HDK.with(|h| h.borrow().hash(HashInput::Keccak256(input)))? {
        HashOutput::Keccak256(hash) => Ok(hash.as_ref().to_vec()),
        _ => unreachable!(),
    }
}

pub fn hash_sha3(input: Vec<u8>) -> ExternResult<Vec<u8>> {
    match HDK.with(|h| h.borrow().hash(HashInput::Sha3256(input)))? {
        HashOutput::Sha3256(hash) => Ok(hash.as_ref().to_vec()),
        _ => unreachable!(),
    }
}