postchain-client 0.0.5

Just another Chromia Postchain client implemented in Rust.
Documentation
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
#![allow(warnings)]

//! Merkle tree-based hashing implementation for Generic Tree Value (GTV) data structures.
//! 
//! This module implements a specialized Merkle tree for hashing GTV data structures. It provides:
//! - Construction of Merkle trees from GTV data (arrays, dictionaries, and primitive values)
//! - Cryptographic hashing using SHA-256 with type-specific prefixes
//! - Deterministic hash computation for complex nested data structures
//! 
//! # Architecture
//! The implementation consists of three main components:
//! - `BinaryTreeNode`: The basic building block of the Merkle tree
//! - `BinaryTreeFactory`: Constructs Merkle trees from GTV data
//! - `MerkleHashCalculator`: Computes cryptographic hashes of the tree
//! 
//! # Hash Prefixes
//! Different node types use distinct prefixes to ensure unique hashes:
//! - Leaf nodes: 1 (for actual data)
//! - Internal nodes: 0 (for tree structure)
//! - Array nodes: 7 (for ordered sequences)
//! - Dictionary nodes: 8 (for key-value mappings)
//! 
//! # Examples
//! 
//! Hashing an array:
//! ```
//! use crate::utils::operation::Params;
//! 
//! let array_data = Params::Array(vec![
//!     Params::Text("foo".to_string()),
//!     Params::Text("bar".to_string())
//! ]);
//! 
//! let hash_version = 2;
//! let hash = gtv_hash(array_data, hash_version).unwrap();
//! ```
//! 
//! Hashing a dictionary:
//! ```
//! use std::collections::BTreeMap;
//! use crate::utils::operation::Params;
//! 
//! let mut dict = BTreeMap::new();
//! dict.insert("key".to_string(), Params::Integer(42));
//! let dict_data = Params::Dict(dict);
//! 
//! let hash_version = 1;
//! let hash = gtv_hash(dict_data, hash_version).unwrap();
//! ```
//! 
//! # Error Handling
//! The module uses `HashError` to handle error cases:
//! - `EmptyArray`: When processing an empty array structure
//! - `EmptyDict`: When processing an empty dictionary structure

use sha2::{Sha256, Digest};
use crate::utils::operation::Params;
use crate::encoding::gtv::encode_value as gtv_encode_value;

/// Represents different types of nodes in the Merkle tree structure.
/// 
/// Each node type serves a specific purpose in building and hashing the tree:
/// - `Node`: Regular internal nodes that combine and hash their children
/// - `Leaf`: Contains actual GTV data to be hashed
/// - `EmptyLeaf`: Used for padding incomplete trees (returns zero hash)
/// - `DictNode`: Special node for dictionaries with sorted key-value pairs
/// - `ArrayNode`: Special node for arrays preserving element order
/// 
/// The node type determines:
/// 1. How the node's hash is computed
/// 2. What prefix is used in the hash computation
/// 3. How child nodes are processed
#[derive(Clone, PartialEq, Debug)]
enum NodeType {
    /// Internal node with two children, uses prefix 0
    Node,
    /// Leaf node containing actual data, uses prefix 1
    Leaf,
    /// Empty leaf node for padding, returns zero hash
    EmptyLeaf,
    /// Dictionary node for key-value pairs, uses prefix 8
    DictNode,
    /// Array node for ordered elements, uses prefix 7
    ArrayNode,
}

/// Errors that can occur during Merkle tree construction and hashing.
/// 
/// These errors help identify issues with input data structures:
/// - `EmptyArray`: Indicates an attempt to process an invalid or empty array
/// - `EmptyDict`: Indicates an attempt to process an invalid or empty dictionary
/// 
/// # Example
/// ```
/// use crate::utils::operation::Params;
/// 
/// // Attempting to hash an empty array
/// let hash_version = 2;
/// let empty_array = Params::Array(vec![]);
/// match gtv_hash(empty_array, hash_version) {
///     Ok(_) => println!("Hash computed successfully"),
///     Err(HashError::EmptyArray(msg)) => println!("{}", msg),
///     _ => println!("Other error occurred"),
/// }
/// ```
#[derive(Clone, Debug)]
pub enum HashError {
    /// Error when processing an invalid or empty array
    EmptyArray(String),
    /// Error when processing an invalid or empty dictionary
    EmptyDict(String),
}

/// Represents a node in the binary Merkle tree.
/// 
/// A `BinaryTreeNode` is the fundamental building block of the Merkle tree structure.
/// Each node can be:
/// - An internal node (Node, ArrayNode, or DictNode) with left and right children
/// - A leaf node containing a GTV value
/// - An empty leaf node used for padding incomplete trees
/// 
/// The node's behavior during hash computation is determined by its `type_of_node` field,
/// which specifies what prefix to use and how to process child nodes.
/// 
/// # Node Structure
/// - `left`: Left child node (for internal nodes)
/// - `right`: Right child node (for internal nodes)
/// - `value`: Stored GTV value (for leaf nodes)
/// - `type_of_node`: Determines the node's role and hash computation method
#[derive(Clone, Debug)]
struct BinaryTreeNode {
    /// Left child of the node
    left: Option<Box<BinaryTreeNode>>,
    /// Right child of the node
    right: Option<Box<BinaryTreeNode>>,
    /// Value stored in the node (for leaf nodes)
    value: Option<Box<Params>>,
    /// Type of the node (internal, leaf, empty, etc.)
    type_of_node: NodeType
}

impl Default for BinaryTreeNode {
    fn default() -> Self {
        BinaryTreeNode {
            left: None,
            right: None,
            value: None,
            type_of_node: NodeType::EmptyLeaf,
        }
    }
}

impl BinaryTreeNode {
    /// Creates a new internal node with specified children, value, and type.
    /// 
    /// # Arguments
    /// * `left` - Left child node
    /// * `right` - Right child node
    /// * `value` - Optional value stored in the node
    /// * `type_of_node` - Type of the node
    fn new_node(left: Option<Box<BinaryTreeNode>>, right: Option<Box<BinaryTreeNode>>, value: Option<Box<Params>>, type_of_node: NodeType) -> Self {
        BinaryTreeNode {
            left, right, value, type_of_node
        }
    }

    /// Creates a new leaf node with an optional value.
    /// 
    /// # Arguments
    /// * `value` - Optional value to store in the leaf
    /// * `is_empty_leaf` - If true, creates an empty leaf node
    fn new_leaf(value: Option<Box<Params>>, is_empty_leaf: bool) -> Box<Self> {
        let type_of_node = match is_empty_leaf {
            true => NodeType::EmptyLeaf,
            false => NodeType::Leaf,
        };

        Box::new(BinaryTreeNode {
            value, type_of_node, ..Default::default()
        })
    }
}

/// Factory for creating binary Merkle trees from GTV data structures.
#[derive(Clone, Debug)]
struct BinaryTreeFactory;

impl BinaryTreeFactory {
    /// Processes a layer of nodes in the Merkle tree construction.
    /// 
    /// This function implements the core Merkle tree building logic by:
    /// 1. Taking pairs of nodes and combining them into parent nodes
    /// 2. Recursively processing layers until a single root node is formed
    /// 3. Handling odd numbers of nodes by promoting the last unpaired node
    /// 
    /// # Arguments
    /// * `leaves` - Vector of nodes to process into a tree layer
    /// 
    /// # Returns
    /// * `Ok(Box<BinaryTreeNode>)` - The root node of the processed layer
    /// * `Err(HashError::EmptyArray)` - If the input vector is empty
    /// 
    /// # Note
    /// When processing an odd number of nodes, the last node is promoted to the next layer without a pair
    fn process_layer(leaves: Vec<Box<BinaryTreeNode>>) -> Result<Box<BinaryTreeNode>, HashError> {
        if leaves.is_empty() {
            return Err(HashError::EmptyArray("Cannot process empty layer of nodes".to_string()));
        }

        if leaves.len() == 1 {
            return Ok(leaves.into_iter().next().unwrap());
        }

        let results = leaves.chunks(2)
            .map(|chunk| {
                if chunk.len() == 2 {
                    let left = chunk[0].clone();
                    let right = chunk[1].clone();
                    BinaryTreeNode::new_node(Some(left), Some(right), None, NodeType::Node)
                } else {
                    *chunk[0].clone()
                }
            })
            .map(Box::new)
            .collect::<Vec<_>>();

        Self::process_layer(results)
    }

    /// Processes an array parameter into a Merkle tree node.
    /// 
    /// Creates a tree structure from an array of values, with array-specific
    /// hash prefixes (7) for the nodes. Handles special cases:
    /// - Empty arrays get two empty leaf children
    /// - Single-element arrays get an empty right child
    /// 
    /// # Arguments
    /// * `params` - Box containing array parameters to process
    /// * `hash_version` - Hash version
    /// 
    /// # Returns
    /// * `Ok(Box<BinaryTreeNode>)` - A tree node representing the array structure
    /// * `Err(HashError::EmptyArray)` - If the input is not a valid array parameter
    /// 
    /// # Note
    /// The resulting tree preserves the order of array elements in the leaf nodes
    fn process_array_node(params: Box<Params>, hash_version: u8) -> Result<Box<BinaryTreeNode>, HashError> {
        if let Params::Array(array_value) = &*params {
            if array_value.is_empty() {
                let left = BinaryTreeNode::new_leaf(None, true);
                let right = BinaryTreeNode::new_leaf(None, true);
                let value = Box::new(Params::Array(Vec::new()));
                return Ok(Box::new(BinaryTreeNode::new_node(Some(left), Some(right), Some(value), NodeType::ArrayNode)));
            }

            if hash_version == 1 && array_value.len() == 1 {
                let av = array_value[0].clone();

                if let Params::Array(_) = av {
                    return Self::build_tree(Box::new(av), hash_version);
                }

                if let Params::Dict(_) = av {
                    return Self::build_tree(Box::new(Params::Array(av.dict_to_array())), hash_version);
                }
            }

            let leaves: Result<Vec<_>, _> = array_value
                .iter()
                .map(|value| Box::new(value.clone()))
                .map(|params| Self::build_tree(params, hash_version))
                .collect();

            let leaves = leaves?;

            let value = Box::new(Params::Array(array_value.clone()));

            let tree_root = if leaves.len() == 1 {
                let left = leaves.into_iter().next().unwrap();
                let right = BinaryTreeNode::new_leaf(None, true);
                BinaryTreeNode::new_node(Some(left), Some(right), None, NodeType::Node)
            } else {
                *Self::process_layer(leaves)?
            };

            let node = BinaryTreeNode::new_node(tree_root.left, tree_root.right, Some(value), NodeType::ArrayNode);
            Ok(Box::new(node))
        } else {
            Err(HashError::EmptyArray("Invalid array parameter provided".to_string()))
        }
    }

    /// Processes a dictionary parameter into a Merkle tree node.
    /// 
    /// Creates a tree structure from dictionary key-value pairs, with
    /// dictionary-specific hash prefixes (8) for the nodes. Special handling:
    /// - Empty dictionaries get two empty leaf children
    /// - Keys and values are stored as alternating leaf nodes
    /// - Keys are stored as Text parameters
    /// 
    /// # Arguments
    /// * `params` - Box containing dictionary parameters to process
    /// * `hash_version` - Hash version
    /// 
    /// # Returns
    /// * `Ok(Box<BinaryTreeNode>)` - A tree node representing the dictionary structure
    /// * `Err(HashError::EmptyDict)` - If the input is not a valid dictionary parameter
    /// 
    /// # Note
    /// Dictionary entries are processed in sorted order by key to ensure consistent hashing
    fn process_dict_node(params: Box<Params>, hash_version: u8) -> Result<Box<BinaryTreeNode>, HashError> {
        if let Params::Dict(dict_value) = &*params {
            if dict_value.is_empty() {
                let left = BinaryTreeNode::new_leaf(None, true);
                let right = BinaryTreeNode::new_leaf(None, true);
                let value = Box::new(Params::Dict(std::collections::BTreeMap::new()));
                return Ok(Box::new(BinaryTreeNode::new_node(Some(left), Some(right), Some(value), NodeType::DictNode)));
            }

            let leaves: Result<Vec<_>, _> = dict_value
                .iter()
                .flat_map(|(key, value)| {
                    let key_leaf = BinaryTreeNode::new_leaf(Some(Box::new(Params::Text(key.clone()))), false);
                    let value_tree = Self::build_tree(Box::new(value.clone()), hash_version);
                    match value_tree {
                        Ok(tree) => vec![Ok(key_leaf), Ok(tree)],
                        Err(err) => vec![Err(err)],
                    }
                })
                .collect();

            let leaves = leaves?;
            let value = Box::new(Params::Dict(dict_value.clone()));

            let tree_root = if leaves.len() == 1 {
                let left = leaves.into_iter().next().unwrap();
                let right = BinaryTreeNode::new_leaf(None, true);
                BinaryTreeNode::new_node(Some(left), Some(right), None, NodeType::Node)
            } else {
                *Self::process_layer(leaves)?
            };

            let node = BinaryTreeNode::new_node(tree_root.left, tree_root.right, Some(value), NodeType::DictNode);
            Ok(Box::new(node))
        } else {
            Err(HashError::EmptyDict("Invalid dictionary parameter provided".to_string()))
        }
    }

    /// Builds a complete Merkle tree from a parameter value.
    /// 
    /// Recursively processes the input parameter based on its type:
    /// - Arrays are processed into ArrayNode trees
    /// - Dictionaries are processed into DictNode trees
    /// - Other values become leaf nodes
    /// 
    /// # Arguments
    /// * `params` - Box containing the parameter to process into a tree
    /// * `hash_version` - Hash version
    /// 
    /// # Returns
    /// * `Ok(Box<BinaryTreeNode>)` - The root node of the complete Merkle tree
    /// * `Err(HashError)` - If processing fails due to invalid input
    /// 
    /// # Note
    /// The resulting tree structure preserves the semantic structure of the input data
    fn build_tree(params: Box<Params>, hash_version: u8) -> Result<Box<BinaryTreeNode>, HashError> {
        match *params {
            Params::Array(_) =>
                Self::process_array_node(params, hash_version),
            Params::Dict(_) =>
                Self::process_dict_node(params, hash_version),
            _ =>
                Ok(BinaryTreeNode::new_leaf(Some(params), false))
        }
    }
}

/// Calculator for computing Merkle tree hashes using SHA-256.
struct MerkleHashCalculator;

const HASH_PREFIX_LEAF: u8 = 1;
const HASH_PREFIX_NODE: u8 = 0;
const HASH_PREFIX_NODE_ARRAY: u8 = 7;
const HASH_PREFIX_NODE_DICT: u8 = 8;

impl MerkleHashCalculator {
    /// Computes SHA-256 hash of input data.
    /// 
    /// Uses the SHA-256 algorithm to create a cryptographic hash
    /// of the input data bytes.
    /// 
    /// # Arguments
    /// * `data` - Slice of bytes to hash
    /// 
    /// # Returns
    /// A fixed-size array containing the 32-byte SHA-256 hash value
    fn sha256(data: &[u8]) -> [u8; 32] {
        let mut hasher = Sha256::new();
        hasher.update(data);
        hasher.finalize().into()
    }

    /// Calculates hash for a leaf node.
    /// 
    /// Creates a hash for a leaf node by:
    /// 1. Prepending the leaf prefix (1)
    /// 2. GTV-encoding the parameter value
    /// 3. Computing SHA-256 of the combined bytes
    /// 
    /// # Arguments
    /// * `value` - Optional parameter value to hash. Must be Some for non-empty leaves
    /// 
    /// # Returns
    /// A fixed-size array containing the 32-byte hash of the leaf node
    /// 
    /// # Note
    /// The leaf prefix ensures leaf node hashes are distinct from internal node hashes
    fn calculate_leaf_hash(value: &Params) -> [u8; 32] {
        let gev = gtv_encode_value(value);
        let mut buffer = Vec::with_capacity(1 + gev.len());
        buffer.push(HASH_PREFIX_LEAF);
        buffer.extend_from_slice(&gev);
        Self::sha256(&buffer)
    }

    /// Calculates hash for an internal node.
    /// 
    /// Creates a hash for an internal node by:
    /// 1. Prepending the node type prefix
    /// 2. Concatenating left and right child hashes
    /// 3. Computing SHA-256 of the combined bytes
    /// 
    /// # Arguments
    /// * `has_prefix` - Node type prefix:
    ///   - 0 for regular internal nodes
    ///   - 7 for array nodes
    ///   - 8 for dictionary nodes
    /// * `left` - 32-byte hash of the left child
    /// * `right` - 32-byte hash of the right child
    /// 
    /// # Returns
    /// A fixed-size array containing the 32-byte hash of the internal node
    /// 
    /// # Note
    /// Different prefixes ensure unique hashes for different node types
    fn calculate_node_hash(has_prefix: u8, left: [u8; 32], right: [u8; 32]) -> [u8; 32] {
        let mut buffer = [0u8; 65];
        buffer[0] = has_prefix;
        buffer[1..33].copy_from_slice(&left);
        buffer[33..].copy_from_slice(&right);
        Self::sha256(&buffer)
    }

    /// Recursively calculates the Merkle hash of a tree node.
    /// 
    /// Traverses the tree structure and computes hashes according to node types:
    /// - Empty leaves return a zero hash
    /// - Regular leaves are hashed with prefix 1
    /// - Array nodes are hashed with prefix 7
    /// - Dictionary nodes are hashed with prefix 8
    /// - Other internal nodes are hashed with prefix 0
    /// 
    /// # Arguments
    /// * `btn` - Root node of the tree or subtree to hash
    /// 
    /// # Returns
    /// A fixed-size array containing the 32-byte Merkle hash of the tree/subtree
    /// 
    /// # Note
    /// The hash computation preserves the structural properties of the tree
    fn calculate_merkle_hash(btn: &BinaryTreeNode) -> [u8; 32] {
        match &btn.type_of_node {
            NodeType::EmptyLeaf => [0; 32],
            NodeType::Leaf => Self::calculate_leaf_hash(btn.value.as_ref().unwrap()),
            NodeType::ArrayNode | NodeType::DictNode | NodeType::Node => {
                let has_prefix = match btn.type_of_node {
                    NodeType::ArrayNode => HASH_PREFIX_NODE_ARRAY,
                    NodeType::DictNode => HASH_PREFIX_NODE_DICT,
                    _ => HASH_PREFIX_NODE,
                };
                let left_hash = btn.left.as_ref().map(|left| Self::calculate_merkle_hash(left)).unwrap_or([0; 32]);
                let right_hash = btn.right.as_ref().map(|right| Self::calculate_merkle_hash(right)).unwrap_or([0; 32]);

                Self::calculate_node_hash(has_prefix, left_hash, right_hash)
            }
        }
    }
}

/// Computes a cryptographic hash of a GTV (Generic Tree Value) parameter using a Merkle tree.
/// 
/// This function:
/// 1. Constructs a Merkle tree from the input GTV data
/// 2. Computes SHA-256 hashes for each node with type-specific prefixes
/// 3. Combines hashes up the tree to produce a final 32-byte hash
/// 
/// The hashing process ensures:
/// - Unique hashes for different data structures
/// - Order preservation for arrays
/// - Consistent hashing for dictionaries (using sorted keys)
/// - Distinct representations for different node types via prefixes
/// 
/// # Arguments
/// * `value` - GTV parameter to hash (can be array, dictionary, or primitive value)
/// 
/// # Returns
/// * `Ok([u8; 32])` - A fixed-size array 32-byte SHA-256 hash of the parameter
/// * `Err(HashError)` - If processing fails due to invalid input
/// 
/// # Examples
/// 
/// Hashing primitive values:
/// ```
/// use crate::utils::operation::Params;
/// 
/// let hash_version = 2;
/// 
/// // Hash an integer
/// let int_hash = gtv_hash(Params::Integer(42), hash_version).unwrap();
/// 
/// // Hash a string
/// let text_hash = gtv_hash(Params::Text("hello".to_string()), hash_version).unwrap();
/// ```
/// 
/// Hashing nested structures:
/// ```
/// use std::collections::BTreeMap;
/// use crate::utils::operation::Params;
/// 
/// // Create a nested structure
/// let mut dict = BTreeMap::new();
/// dict.insert("array".to_string(), Params::Array(vec![
///     Params::Integer(1),
///     Params::Text("two".to_string())
/// ]));
/// let data = Params::Dict(dict);
/// 
/// // Compute hash
/// let hash_version = 2;
/// let hash = gtv_hash(data, hash_version).unwrap();
/// ```
pub fn gtv_hash(value: Params, hash_version: u8) -> Result<[u8; 32], HashError> {
    let tree = BinaryTreeFactory::build_tree(Box::new(value), hash_version)?;
    Ok(MerkleHashCalculator::calculate_merkle_hash(&tree))
}

#[test]
fn test_gtv_hash() {
    use std::collections::BTreeMap;

    let data1 = Params::Array(vec![
        Params::Text("foo".to_string()), Params::Array(vec![
            Params::Text("bar2".to_string()), Params::Text("bar2".to_string())
        ])
    ]);

    let mut data2_btree: BTreeMap<String, Params> = BTreeMap::new();
    data2_btree.insert("foo".to_string(), Params::Integer(-1));
    data2_btree.insert("foo1".to_string(), Params::Text("OK".to_string()));
    data2_btree.insert("bar".to_string(), Params::BigInteger(i128::MAX.into()));
    data2_btree.insert("bar1".to_string(), Params::BigInteger((1000000000000 as i128).into()));

    let data2 = Params::Dict(data2_btree);
    
    let result1 = gtv_hash(data1, 2).unwrap();
    let result2 = gtv_hash(data2, 2).unwrap();

    assert_eq!("6357d3200e0dfb1bce5f3eb789714842747b39810248f83dba6382c7e7020e20", hex::encode(result1));
    assert_eq!("9f3d80d08a942b86e20932ad74356703dba7ba78b792f2d6ad93201ab9a71bab", hex::encode(result2));
}

#[test]
fn test_gtv_hash_v1() {
    let data1 = Params::Array(vec![Params::Text("a".to_string())]);
    let data2 = Params::Array(vec![Params::Array(vec![Params::Text("a".to_string())])]);
    let data3 = Params::Array(vec![
        Params::Array(vec![
            Params::Array(vec![Params::Text("a".to_string())])
            ])
        ]);

    let result1 = gtv_hash(data1, 1).unwrap();
    let result2 = gtv_hash(data2, 1).unwrap();
    let result3 = gtv_hash(data3, 1).unwrap();

    let expected_hash_result = "5ad2414edcd34b9a8bdc22921b8a1b8cef6cab04115dd0e7eb000b05353b315a";

    assert_eq!(hex::encode(result1), expected_hash_result);
    assert_eq!(hex::encode(result2), expected_hash_result);
    assert_eq!(hex::encode(result3), expected_hash_result);
}

#[test]
fn test_gtv_hash_v2() {
    let data1 = Params::Array(vec![Params::Text("a".to_string())]);
    let data2 = Params::Array(vec![Params::Array(vec![Params::Text("a".to_string())])]);
    let data3 = Params::Array(vec![
        Params::Array(vec![
            Params::Array(vec![Params::Text("a".to_string())])
            ])
        ]);

    let result1 = gtv_hash(data1, 2).unwrap();
    let result2 = gtv_hash(data2, 2).unwrap();
    let result3 = gtv_hash(data3, 2).unwrap();

    assert_eq!(hex::encode(result1), "5ad2414edcd34b9a8bdc22921b8a1b8cef6cab04115dd0e7eb000b05353b315a");
    assert_eq!(hex::encode(result2), "19605d1044cc20248e315f98f2d4c4aa7adfe6861607a0d000641837c3b962f8");
    assert_eq!(hex::encode(result3), "574b45c58e62ff7b786ee644579ffea593c89541498c1692fb8c99d811265166");
}

#[test]
fn test_gtv_hash_v1_and_v2_of_array_of_dicts() {
    let data = Params::Array(vec![
        Params::Dict(std::collections::BTreeMap::from([
            ("a".to_string(), Params::Text("b".to_string())),
            ("c".to_string(), Params::Text("d".to_string()))
        ]))
    ]);

    let hash_v1_result = "891cdf10ff613a90899ff0ffe1a515d8ed74fe71e36249f0b6dd175eec70805d";
    let hash_v2_result = "9d2f6cfa72538e24584363ada5882c2be3f83d75aff598d0009330db22d961ff";

    let result = gtv_hash(data.clone(), 1).unwrap();
    assert_eq!(hex::encode(result), hash_v1_result);

    let result = gtv_hash(data, 2).unwrap();
    assert_eq!(hex::encode(result), hash_v2_result);

    let a1 =  Params::Dict(std::collections::BTreeMap::from([
        ("a1".to_string(), Params::Text("b".to_string()))
    ]));
    let c1 =  Params::Dict(std::collections::BTreeMap::from([
        ("c1".to_string(), Params::Text("d".to_string()))
    ]));

    let data = Params::Array(vec![
        Params::Dict(std::collections::BTreeMap::from([
            ("a".to_string(), a1),
            ("c".to_string(), c1)
        ]))
    ]);

    let hash_v1_result = "132fc201e78c96fc2c563a6cff21fa12e45815871e34a267b72c41c0fe48f410";
    let hash_v2_result = "ea56d66de794ad212183de103aca17df8eec177bf299ff203cc0aeb287a76495";

    let result = gtv_hash(data.clone(), 1).unwrap();
    assert_eq!(hex::encode(result), hash_v1_result);

    let result = gtv_hash(data, 2).unwrap();
    assert_eq!(hex::encode(result), hash_v2_result);
}