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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
use super::*;
use crate::error::HoloHashError;
use crate::hash_type;
use crate::AgentPubKey;
use crate::EntryHash;
use std::convert::TryInto;
// Valid Holochain options for prefixes:
// hCAk 4100 <Buffer 84 20 24> * AGENT
// hCEk 4228 <Buffer 84 21 24> * ENTRY
// hCIk 4356 <Buffer 84 22 24> * NET_ID
// hCMk 4484 <Buffer 84 23 24>
// hCQk 4612 <Buffer 84 24 24> * DHTOP
// hCUk 4740 <Buffer 84 25 24>
// hCYk 4868 <Buffer 84 26 24>
// hCck 4996 <Buffer 84 27 24>
// hCgk 5124 <Buffer 84 28 24>
// hCkk 5252 <Buffer 84 29 24> * ACTION
// hCok 5380 <Buffer 84 2a 24> * WASM
// hCsk 5508 <Buffer 84 2b 24>
// hCwk 5636 <Buffer 84 2c 24>
// hC0k 5764 <Buffer 84 2d 24> * DNA
// hC4k 5892 <Buffer 84 2e 24>
// hC8k 6020 <Buffer 84 2f 24> * EXTERNAL

// Valid Holo.Host options for prefixes:
// hhAk 2054 <Buffer 86 10 24> * HOST KEY
// hhEk 2182 <Buffer 86 11 24>
// hhIk 2310 <Buffer 86 12 24>
// hhMk 2438 <Buffer 86 13 24>
// hhQk 2566 <Buffer 86 14 24> * ANONYMOUS KEY (They can Query-only / no source chain)
// hhUk 2694 <Buffer 86 15 24> * WEB USER KEY
// hhYk 2822 <Buffer 86 16 24>
// hhck 2950 <Buffer 86 17 24>
// hhgk 3078 <Buffer 86 18 24>
// hhkk 3206 <Buffer 86 19 24>
// hhok 3334 <Buffer 86 1a 24>
// hhsk 3462 <Buffer 86 1b 24>
// hhwk 3590 <Buffer 86 1c 24>
// hh0k 3718 <Buffer 86 1d 24> * HOSTED APP Bundle (UI with websdk, etc.)
// hh4k 3846 <Buffer 86 1e 24>
// hh8k 3974 <Buffer 86 1f 24>

pub(crate) const AGENT_PREFIX: &[u8] = &[0x84, 0x20, 0x24]; // uhCAk [132, 32, 36]
pub(crate) const ENTRY_PREFIX: &[u8] = &[0x84, 0x21, 0x24]; // uhCEk [132, 33, 36]
pub(crate) const DHTOP_PREFIX: &[u8] = &[0x84, 0x24, 0x24]; // uhCQk [132, 36, 36]
pub(crate) const DNA_PREFIX: &[u8] = &[0x84, 0x2d, 0x24]; // uhC0k [132, 45, 36]
pub(crate) const NET_ID_PREFIX: &[u8] = &[0x84, 0x22, 0x24]; // uhCIk [132, 34, 36]
pub(crate) const ACTION_PREFIX: &[u8] = &[0x84, 0x29, 0x24]; // uhCkk [132, 41, 36]
pub(crate) const WASM_PREFIX: &[u8] = &[0x84, 0x2a, 0x24]; // uhCok [132, 42, 36]
pub(crate) const EXTERNAL_PREFIX: &[u8] = &[0x84, 0x2f, 0x24]; // uhC8k [132, 47, 36]

/// A PrimitiveHashType is one with a multihash prefix.
/// In contrast, a non-primitive hash type could be one of several primitive
/// types, e.g. an `AnyDhtHash` can represent one of three primitive types.
pub trait PrimitiveHashType: HashType {
    /// Constructor
    fn new() -> Self;

    /// Get the 3 byte prefix, which is statically known for primitive hash types
    fn static_prefix() -> &'static [u8];

    /// Get a Display-worthy name for this hash type
    fn hash_name(self) -> &'static str;
}

impl<P: PrimitiveHashType> HashType for P {
    fn get_prefix(self) -> &'static [u8] {
        P::static_prefix()
    }

    fn try_from_prefix(prefix: &[u8]) -> HoloHashResult<Self> {
        if prefix == P::static_prefix() {
            Ok(P::new())
        } else {
            Err(HoloHashError::BadPrefix(
                PrimitiveHashType::hash_name(P::new()).to_string(),
                prefix.try_into().expect("3 byte prefix"),
            ))
        }
    }

    fn hash_name(self) -> &'static str {
        PrimitiveHashType::hash_name(self)
    }
}

macro_rules! primitive_hash_type {
    ($name: ident, $display: ident, $visitor: ident, $prefix: ident) => {
        /// The $name PrimitiveHashType
        #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
        #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
        pub struct $name;

        impl PrimitiveHashType for $name {
            fn new() -> Self {
                Self
            }

            fn static_prefix() -> &'static [u8] {
                &$prefix
            }

            fn hash_name(self) -> &'static str {
                stringify!($display)
            }
        }

        #[cfg(feature = "serialization")]
        impl serde::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: serde::Serializer,
            {
                serializer.serialize_bytes(self.get_prefix())
            }
        }

        #[cfg(feature = "serialization")]
        impl<'de> serde::Deserialize<'de> for $name {
            fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
            where
                D: serde::Deserializer<'de>,
            {
                deserializer.deserialize_bytes($visitor)
            }
        }

        #[cfg(feature = "serialization")]
        struct $visitor;

        #[cfg(feature = "serialization")]
        impl<'de> serde::de::Visitor<'de> for $visitor {
            type Value = $name;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("a HoloHash of primitive hash_type")
            }

            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                match v {
                    $prefix => Ok($name),
                    _ => panic!("unknown hash prefix during hash deserialization {:?}", v),
                }
            }

            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
            where
                A: serde::de::SeqAccess<'de>,
            {
                let mut vec = Vec::with_capacity(seq.size_hint().unwrap_or(0));

                while let Some(b) = seq.next_element()? {
                    vec.push(b);
                }

                self.visit_bytes(&vec)
            }
        }
    };
}

primitive_hash_type!(Agent, AgentPubKey, AgentVisitor, AGENT_PREFIX);
primitive_hash_type!(Entry, EntryHash, EntryVisitor, ENTRY_PREFIX);
primitive_hash_type!(Dna, DnaHash, DnaVisitor, DNA_PREFIX);
primitive_hash_type!(DhtOp, DhtOpHash, DhtOpVisitor, DHTOP_PREFIX);
primitive_hash_type!(Action, ActionHash, ActionVisitor, ACTION_PREFIX);
primitive_hash_type!(NetId, NetIdHash, NetIdVisitor, NET_ID_PREFIX);
primitive_hash_type!(Wasm, WasmHash, WasmVisitor, WASM_PREFIX);
primitive_hash_type!(External, ExternalHash, ExternalVisitor, EXTERNAL_PREFIX);

// DhtOps are mostly hashes
impl HashTypeSync for DhtOp {}
// Entries are capped at 16MB, which is small enough to hash synchronously
impl HashTypeSync for Entry {}
// Actions are only a few hundred bytes at most
impl HashTypeSync for Action {}
// A DnaHash is a hash of the DnaDef, which excludes the wasm bytecode
impl HashTypeSync for Dna {}

// We don't know what external data might be getting hashed but typically it
// would be small, like a reference to something in an external system such as
// a hash in a different DHT, an IPFS hash or a UUID, etc.
/// External hashes have a DHT location and hash prefix like all other native
/// holochain hashes but are NOT found/fetchable on the DHT.
/// External hashing makes no assumptions about the data that was digested to
/// create the hash so arbitrary bytes can be passed in.
/// It is valid to EITHER use an existing 32 byte hash/data as literal bytes
/// for an external hash (literal+prefix, no data loss) OR digest arbitrary
/// data into an external hash (support all data, opaque result).
impl HashTypeAsync for External {}

impl HashTypeAsync for NetId {}
impl HashTypeAsync for Wasm {}

impl From<AgentPubKey> for EntryHash {
    fn from(hash: AgentPubKey) -> EntryHash {
        hash.retype(hash_type::Entry)
    }
}

impl From<EntryHash> for AgentPubKey {
    fn from(hash: EntryHash) -> AgentPubKey {
        hash.retype(hash_type::Agent)
    }
}