merklicious_sdk/
lib.rs

1pub use hdk_extensions::hdi;
2pub use hdk_extensions::holo_hash;
3pub use hdk_extensions::hdk;
4pub use hdk_extensions::hdi_extensions;
5pub use hdk_extensions;
6
7use std::collections::BTreeMap;
8use hex;
9use hdi_extensions::guest_error;
10use hdi::prelude::*;
11use hdk::prelude::sys_time;
12use hmac::{ Hmac, Mac };
13use sha2::{ Sha256, Digest };
14use rmp_serde;
15
16type HmacSha256 = Hmac<Sha256>;
17
18
19
20//
21// General Functions
22//
23/// Get a current timestamp according to the HDK's [`sys_time`]
24pub fn now() -> ExternResult<u64> {
25    sys_time()
26	.map( |t| (t.as_micros() / 1000) as u64 )
27}
28
29/// Serialize the given data using [`rmp_serde`] and return the SHA-256 hash
30pub fn sha256<T>(data: &T) -> ExternResult<[u8; 32]>
31where
32    T: Serialize + std::fmt::Debug,
33{
34    let bytes = rmp_serde::to_vec( &data )
35        .or(Err(guest_error!(format!("Failed to serialize input; {:#?}", data))))?;
36    let mut hasher = Sha256::new();
37    hasher.update( &bytes );
38    Ok(
39        <[u8; 32]>::from( hasher.finalize() )
40    )
41}
42
43
44
45// Trait for common fields
46/// Common fields that are expected on some entry structs
47pub trait CommonFields<'a> {
48    /// A spot for holding data that is not relevant to integrity validation
49    fn metadata(&'a self) -> &'a BTreeMap<String, rmpv::Value>;
50}
51
52/// Auto-implement the [`CommonFields`] trait
53///
54/// The input must be a struct with fields matching each common field method.
55///
56/// #### Example
57/// ```ignore
58/// struct PostEntry {
59///     pub message: String,
60///
61///     // Common fields
62///     pub metadata: BTreeMap<String, rmpv::Value>,
63/// }
64/// common_fields!( PostEntry );
65/// ```
66#[macro_export]
67macro_rules! common_fields {
68    ( $name:ident ) => {
69        impl<'a> CommonFields<'a> for $name {
70            fn metadata(&'a self) -> &'a BTreeMap<String, rmpv::Value> {
71                &self.metadata
72            }
73        }
74    };
75}
76
77
78
79//
80// Common Structs
81//
82/// The piece of data that a Merkle Tree leaf represents
83#[hdk_entry_helper]
84#[derive(Clone)]
85pub struct LeafDataBlock {
86    /// The field descriptor
87    pub label: String,
88    /// The field data
89    pub value: rmpv::Value,
90    /// Some entropy to prevent value guessing
91    #[serde(with = "serde_bytes")]
92    pub salt: Vec<u8>,
93}
94
95impl LeafDataBlock {
96    /// Get a sha256 hash of this struct
97    pub fn hash(&self) -> ExternResult<[u8; 32]> {
98        sha256( &self )
99    }
100}
101
102/// All the information required to verify a leaf
103#[derive(Clone, Debug, Serialize, Deserialize)]
104pub struct LeafProofPayload {
105    /// The Merkle proof hash list
106    pub proof: Vec<[u8; 32]>,
107    /// The leaf's index in the Merkle tree
108    pub index: u64,
109    /// The revealed leaf data
110    pub target: LeafDataBlock,
111    /// The sha256 hash of the target leaf
112    pub leaf: [u8; 32],
113    /// The Merkle tree's root hash
114    pub root: [u8; 32],
115    /// The total number of leaves in the Merkle tree
116    pub total_leaves: u64,
117}
118
119
120
121//
122// Tree Entry
123//
124/// An entry struct for storing the leaf data blocks that were used to create a tree
125#[hdk_entry_helper]
126#[derive(Clone)]
127pub struct DataBlocksEntry {
128    /// A list of leaf data blocks
129    pub blocks: Vec<LeafDataBlock>,
130
131    // common fields
132    pub metadata: BTreeMap<String, rmpv::Value>,
133}
134common_fields!( DataBlocksEntry );
135
136
137/// An entry struct that represents a Merkle tree
138#[hdk_entry_helper]
139#[derive(Clone)]
140pub struct TreeEntry {
141    /// The leaf data blocks used to create this tree
142    pub data_blocks: ActionHash,
143    /// The leaf hashes of this Merkle tree
144    pub leaves: Vec<[u8; 32]>,
145    /// A secret entropy used for creating deterministic salts
146    pub entropy: Vec<u8>,
147    /// The root hash of this Merkle tree
148    pub root: [u8; 32],
149
150    // common fields
151    pub metadata: BTreeMap<String, rmpv::Value>,
152}
153common_fields!( TreeEntry );
154
155impl TreeEntry {
156    /// Get the Merkle tree root as a hex string
157    pub fn root_as_hex(&self) -> String {
158        hex::encode( self.root.to_owned() )
159    }
160}
161
162
163
164//
165// Claim Entry
166//
167/// An entry struct for making a claim about a Merkle tree
168#[hdk_entry_helper]
169#[derive(Clone)]
170pub struct ClaimEntry {
171    /// The name of this claim
172    pub name: String,
173    /// The author making the claim
174    pub author: AgentPubKey,
175    /// A reference to the Merkle tree
176    pub root: [u8; 32],
177
178    // common fields
179    pub metadata: BTreeMap<String, rmpv::Value>,
180}
181common_fields!( ClaimEntry );
182
183
184
185//
186// CSR Input Structs
187//
188/// Input required for a leaf data block
189#[derive(Clone, Debug, Serialize, Deserialize)]
190pub struct LeafInput {
191    /// The field descriptor
192    pub label: String,
193    /// The field data
194    pub value: rmpv::Value,
195}
196
197impl LeafInput {
198    /// Create a [`LeafDataBlock`] from this leaf input
199    ///
200    /// This method generates a deterministic salt using the entropy and index provided
201    pub fn into_data_block(self, entropy: &Vec<u8>, index: usize) -> ExternResult<LeafDataBlock> {
202        let mut hmac = HmacSha256::new_from_slice( entropy.as_slice() )
203            .or(Err(guest_error!(format!("Failed to create hmac with entropy: {:#?}", entropy ))))?;
204
205        hmac.update( &index.to_le_bytes() );
206        let result = hmac.finalize();
207
208        Ok(
209            LeafDataBlock {
210                label: self.label,
211                value: self.value,
212                salt: result.into_bytes().to_vec(),
213            }
214        )
215    }
216}
217
218type OptionalBytes = Option<serde_bytes::ByteBuf>;
219
220/// Input required for creating a tree entry
221#[derive(Clone, Debug, Serialize, Deserialize)]
222pub struct CreateTreeInput {
223    /// A list of data blocks used as the Merkle tree leaves
224    pub leaves: Vec<LeafInput>,
225    /// Entropy used for creating deterministic salts for each leaf
226    pub entropy: OptionalBytes,
227}
228
229/// Input required for getting a leaf proof
230#[derive(Clone, Debug, Serialize, Deserialize)]
231pub struct GetLeafProofInput {
232    /// The create action for the target tree entry
233    pub tree_id: ActionHash,
234    /// The label of the target leaf
235    pub label: String,
236}
237
238/// Input required for verifying a single leaf proof
239#[derive(Clone, Debug, Serialize, Deserialize)]
240pub struct VerifyLeafProofInput {
241    /// The Merkle proof hash list
242    pub proof: Vec<[u8; 32]>,
243    /// The leaf's index in the Merkle tree
244    pub index: u64,
245    /// The sha256 hash of the target leaf
246    pub leaf: [u8; 32],
247    /// The Merkle tree's root hash
248    pub root: [u8; 32],
249    /// The total number of leaves in the Merkle tree
250    pub total_leaves: u64,
251}
252
253
254
255#[cfg(test)]
256mod tests {
257    use super::{ sha256, Serialize };
258
259    #[test]
260    fn test_sha256() {
261        #[derive(Debug, Serialize)]
262        pub struct Data( Option<u8> );
263
264        assert_eq!( sha256( &Data(None) ).unwrap(), [
265            228, 255, 94, 125, 122, 127, 8, 233,
266            128, 10, 62, 37, 203, 119, 69, 51,
267            203, 32, 4, 13, 243, 11, 107, 161,
268            15, 149, 111, 154, 205, 14, 179, 247
269        ] );
270    }
271}