aqua_verifier/util.rs
1use crate::look_up::look_up::get_tx_data;
2use aqua_verifier_rs_types::crypt;
3use aqua_verifier_rs_types::models::content::RevisionContent;
4use aqua_verifier_rs_types::models::hash::Hash;
5use aqua_verifier_rs_types::models::metadata::RevisionMetadata;
6use aqua_verifier_rs_types::models::public_key::PublicKey;
7use aqua_verifier_rs_types::models::revision::Revision;
8use aqua_verifier_rs_types::models::signature::RevisionSignature;
9use aqua_verifier_rs_types::models::signature::Signature;
10use aqua_verifier_rs_types::models::timestamp::Timestamp;
11use aqua_verifier_rs_types::models::tx_hash::TxHash;
12use aqua_verifier_rs_types::models::witness::{MerkleNode, RevisionWitness};
13use ethers::utils::hash_message;
14use sha3::{Digest, Sha3_512};
15use tokio::runtime::Runtime;
16use std::collections::BTreeMap;
17use std::str;
18use std::str::FromStr;
19
20use crate::model::{ResultStatusEnum, RevisionVerificationResult};
21
22/// Struct to hold the result of file verification.
23#[derive(Debug)]
24pub struct VerifyFileResult {
25 /// The hash of the verified file if successful.
26 pub file_hash: Option<String>,
27 /// Error message if verification fails.
28 pub error_message: Option<String>,
29 /// Logs of the verification process.
30 pub logs: Vec<String>,
31}
32
33
34
35/// Generates a SHA3-512 hash sum from the given content.
36///
37/// # Arguments
38///
39/// * `content` - A string slice containing the content to hash.
40///
41/// # Returns
42///
43/// A hexadecimal representation of the SHA3-512 hash as a String.
44///
45/// # Example
46///
47/// ```
48/// let hash = get_hash_sum("hello world");
49/// println!("Hash: {}", hash);
50/// ```
51pub fn get_hash_sum(content: &str) -> String {
52 if content.is_empty() {
53 String::new()
54 } else {
55 let mut hasher = Sha3_512::new();
56 hasher.update(content.as_bytes());
57 format!("{:x}", hasher.finalize())
58 }
59}
60
61
62/// Generates a SHA3-512 hash from a Base64-encoded string.
63///
64/// # Parameters
65///
66/// * `b64` - A string slice that holds the Base64-encoded input.
67///
68/// # Returns
69///
70/// Returns an `Option<Vec<u8>>`. If the decoding is successful, it returns
71/// `Some` containing the hash as a vector of bytes. If decoding fails,
72/// it returns `None`.
73fn generate_hash_from_base64(b64: &str) -> Option<Vec<u8>> {
74 // Decode the Base64 string
75 let decoded_bytes_result = base64::decode(b64); //.expect("Failed to decode Base64 string");
76
77 if decoded_bytes_result.is_err() {
78 println!("unable t decode bytes.");
79 return None;
80 }
81 let decoded_bytes = decoded_bytes_result.unwrap();
82
83 // Create a Sha3_512 hasher
84 let mut hasher = Sha3_512::new();
85
86 // Write input data
87 hasher.update(&decoded_bytes);
88
89 // Read hash digest and consume hasher
90 let result = hasher.finalize();
91
92 // Return the hash as a vector of bytes
93 Some(result.to_vec())
94}
95
96/// Verifies the file content against its expected hash.
97///
98/// # Parameters
99///
100/// * `data` - A `RevisionContent` struct containing the file data and expected hash.
101///
102/// # Returns
103///
104/// Returns a tuple containing:
105/// - A boolean indicating whether the verification was successful.
106/// - A `VerifyFileResult` struct with details about the verification process.
107pub fn verify_file_util(data: RevisionContent) -> (bool, VerifyFileResult) {
108 let mut logs: Vec<String> = Vec::new();
109
110 let file_content_hash = data.content.file_hash;
111 let file_content = data.file.unwrap().data;
112 let hash_fromb64 = generate_hash_from_base64(file_content.to_string().as_str());
113
114 if hash_fromb64.is_none() {
115 logs.push("Error : unable to decode bytes in verifying revision content ".to_string());
116 return (
117 false,
118 VerifyFileResult {
119 file_hash: None,
120 error_message: Some("unable to decode bytes ".to_string()),
121 logs: logs,
122 },
123 );
124 }
125
126 let hash_gen = hex::encode(hash_fromb64.unwrap());
127
128 // logs.push(format!("Info : Hash generated {}", hash_gen));
129 // logs.push(format!("Info : file Hash in chain {}", file_content_hash.to_string()));
130 if file_content_hash.to_string() != hash_gen {
131 logs.push("Error : File content hash does not match the genrated hash".to_string());
132 return (
133 false,
134 VerifyFileResult {
135 file_hash: None,
136 error_message: Some("File content hash does not match ".to_string()),
137 logs: logs,
138 },
139 );
140 }
141 logs.push("Sucess : File content hash does matches the genrated hash".to_string());
142 (
143 true,
144 VerifyFileResult {
145 file_hash: Some(file_content_hash.to_string()),
146 error_message: None,
147 logs: logs,
148 },
149 )
150}
151
152/// Verifies that the content's hash matches the expected content hash.
153///
154/// # Parameters
155///
156/// * `data` - A reference to a `RevisionContent` struct containing the content to verify.
157///
158/// # Returns
159///
160/// Returns a tuple containing:
161/// - A boolean indicating whether the verification was successful.
162/// - A string representation of the computed content hash.
163pub fn verify_content_util(data: &RevisionContent) -> (bool, String) {
164 let mut content = String::new();
165 content += format!("{:#?}", data.content.file_hash).as_str();
166
167 let content_hash = get_hash_sum(&content);
168 let data_content_hash_str = format!("{:#?}", data.content_hash);
169 if content_hash == data_content_hash_str {
170 (true, content_hash)
171 } else {
172 (false, "Content hash does not match".to_string())
173 }
174}
175
176/// Verifies that the metadata matches its expected hash.
177///
178/// # Parameters
179///
180/// * `data` - A reference to a `RevisionMetadata` struct containing metadata to verify.
181///
182/// # Returns
183///
184/// Returns a tuple containing:
185/// - A boolean indicating whether the verification was successful.
186/// - A string representation of the computed metadata hash.
187pub fn verify_metadata_util(data: &RevisionMetadata) -> (bool, String) {
188 // let metadata_hash = calculate_metadata_hash(
189 // data.domain_id.clone(),
190 // data.time_stamp.clone(),
191 // data.previous_verification_hash,
192 // data.merge_hash,
193 // );
194 let metadata_hash = metadata_hash(
195 data.domain_id.clone().as_str(),
196 &data.time_stamp.clone(),
197 data.previous_verification_hash.as_ref(),
198 );
199 println!("Metadata hash generated {}", metadata_hash);
200 println!("Metadata hash stored {}", data.metadata_hash.to_string());
201
202 if metadata_hash.to_string() == data.metadata_hash.to_string() {
203 (true, metadata_hash.to_string())
204 } else {
205 (false, "Metadata hash does not match".to_string())
206 }
207}
208
209
210/// Calculates a metadata hash based on domain ID, timestamp, and optional hashes.
211///
212/// # Parameters
213///
214/// * `domain_id` - The domain identifier as a string.
215/// * `timestamp` - The timestamp associated with the metadata.
216/// * `previous_verification_hash` - An optional previous verification hash.
217/// * `merge_hash` - An optional merge hash.
218///
219/// # Returns
220///
221/// Returns a string representing the computed metadata hash.
222pub fn calculate_metadata_hash(
223 domain_id: String,
224 timestamp: Timestamp,
225 previous_verification_hash: Option<Hash>,
226 merge_hash: Option<Hash>,
227) -> String {
228 let mut content: String = domain_id + ×tamp.to_string();
229 if let Some(prev_hash) = previous_verification_hash {
230 content += &prev_hash.to_string();
231 }
232 if let Some(merge) = merge_hash {
233 content += &merge.to_string();
234 }
235 get_hash_sum(&content)
236}
237
238///! Verification utilities for digital signatures, witness data, and content integrity.
239///!
240///! This module provides functionality for:
241///! - Verifying digital signatures against provided verification hashes
242///! - Validating witness data and merkle proofs
243///! - Computing and verifying various types /// Verifies witness data and optionally checks merkle proof integrity.
244///
245/// # Arguments
246/// * `witness_data` - The witness data containing merkle proof and transaction information
247/// * `verification_hash` - The hash to verify against
248/// * `do_verify_merkle_proof` - Whether to verify the merkle proof
249/// * `verification_platform` - The platform to use for verification
250/// * `chain` - The blockchain network to use
251/// * `api_key` - API key for the verification platform
252///
253/// # Returns
254/// A tuple containing:
255/// * `bool` - Whether the witness data is valid
256/// * `String` - A status message
257/// * `Vec<String>` - Logs from the verification processof hashes (content, metadata, witness, etc.)
258///! - Managing revision verification chains
259///!
260///! # Examples
261///! ```rust
262///! let signature_data = RevisionSignature { /* ... */ };
263///! let verification_hash = Hash::new("...");
264///! let (is_valid, status) = verify_signature_util(signature_data, verification_hash);
265///! ```
266
267#[warn(unused_assignments)]
268
269/// Verifies a digital signature against a provided verification hash.
270///
271/// # Arguments
272/// * `data` - The signature data containing the signature and wallet address
273/// * `verification_hash` - The hash to verify against
274///
275/// # Returns
276/// A tuple containing:
277/// * `bool` - Whether the signature is valid
278/// * `String` - A status message describing the verification result
279pub fn verify_signature_util(data: RevisionSignature, verification_hash: Hash) -> (bool, String) {
280 if verification_hash.is_empty() {
281 return (false, "Verification hash must not be empty".to_string());
282 }
283
284 let padded_message = format!(
285 "I sign the following page verification_hash: [0x{}]",
286 verification_hash
287 );
288
289 let signature_string = format!("{:?}", data.signature);
290 println!("The signature is {}", signature_string);
291
292 let mut status = String::new();
293 let mut signature_ok = false;
294 match (
295 hash_message(padded_message),
296 ethers::types::Signature::from_str(signature_string.as_str()),
297 ) {
298 (hashed_msg, Ok(sig)) => {
299 println!("Gen signature {}", sig.clone());
300
301 // let clean_input_1 = if sig.to_string().to_lowercase().starts_with("0x") {
302 // sig.to_string().to_lowercase()[2..].to_string()
303 // } else {
304 // sig.to_string().to_lowercase()
305 // };
306 // let clean_input_2 = if signature_string.to_lowercase().starts_with("0x") {
307 // signature_string.to_lowercase()[2..].to_string()
308 // } else {
309 // signature_string.to_lowercase()
310 // };
311
312 // signature_ok = clean_input_1 == clean_input_2;
313
314 // status = if signature_ok {
315 // "Signature is Valid".to_string()
316 // } else {
317 // "Signature is invalid".to_string()
318 // };
319
320 //todo to be reviewed
321 match ethers::core::types::Signature::recover(&sig, hashed_msg) {
322 Ok(recovered_address_long) => {
323 let recovered_address = format!("{:?}", recovered_address_long);
324 let clean_input_1 = if recovered_address
325 .to_string()
326 .to_lowercase()
327 .starts_with("0x")
328 {
329 recovered_address.to_string().to_lowercase()[2..].to_string()
330 } else {
331 recovered_address.to_string().to_lowercase()
332 };
333 let clean_input_2 = if data
334 .wallet_address
335 .to_string()
336 .to_lowercase()
337 .starts_with("0x")
338 {
339 data.wallet_address.to_string().to_lowercase()[2..].to_string()
340 } else {
341 data.wallet_address.to_string().to_lowercase()
342 };
343
344 println!("1 {:#?}", clean_input_1);
345 println!("2 {:#?}", clean_input_2);
346
347 signature_ok = clean_input_1 == clean_input_2;
348 // signature_ok = recovered_address.to_string().to_lowercase()
349 // == data.wallet_address.to_string().to_lowercase();
350
351 status = if signature_ok {
352 "Signature is Valid".to_string()
353 } else {
354 "Signature is invalid".to_string()
355 };
356 }
357 Err(e) => {
358 status = format!("An error occurred retrieving signature: {}", e);
359 }
360 }
361 }
362 (_, Err(e)) => {
363 // Handle invalid signature format
364 status = format!("Invalid signature format: {}", e);
365 }
366 }
367
368 (signature_ok, status)
369}
370
371
372/// Verifies witness data and optionally checks merkle proof integrity.
373///
374/// # Arguments
375/// * `witness_data` - The witness data containing merkle proof and transaction information
376/// * `verification_hash` - The hash to verify against
377/// * `do_verify_merkle_proof` - Whether to verify the merkle proof
378/// * `verification_platform` - The platform to use for verification
379/// * `chain` - The blockchain network to use
380/// * `api_key` - API key for the verification platform
381///
382/// # Returns
383/// A tuple containing:
384/// * `bool` - Whether the witness data is valid
385/// * `String` - A status message
386/// * `Vec<String>` - Logs from the verification process
387pub fn verify_witness_util(
388 witness_data: RevisionWitness,
389 verification_hash: String,
390 do_verify_merkle_proof: bool,
391 verification_platform: String,
392 chain: String,
393 api_key: String,
394) -> (bool, String, Vec<String>) {
395 let logs = Vec::new();
396
397 let actual_witness_event_verification_hash = get_hash_sum(
398 &(witness_data
399 .domain_snapshot_genesis_hash
400 .clone()
401 .to_string()
402 + &witness_data.merkle_root.to_string()),
403 );
404
405 if actual_witness_event_verification_hash
406 != witness_data.witness_event_verification_hash.to_string()
407 {
408 return (false, "Verification hashes do not match".to_string(), logs);
409 }
410
411 if do_verify_merkle_proof {
412 println!("Doing merkle verification: ");
413 if verification_hash == witness_data.domain_snapshot_genesis_hash.to_string() {
414 return (
415 true,
416 "Verification hash is the same as domain snapshot genesis hash".to_string(),
417 logs
418 );
419 } else {
420 let merkle_proof_is_ok =
421 verify_merkle_integrity(&witness_data.structured_merkle_proof, verification_hash);
422 return (
423 merkle_proof_is_ok,
424 if merkle_proof_is_ok {
425 "Merkle proof is OK".to_string()
426 } else {
427 "Error verifying merkle proof".to_string()
428 },
429 logs
430 );
431 }
432 }
433
434 if verification_platform == "none"{
435 println!("No verificaton platform: ");
436 (true, "Look up not performed.".to_string(), logs)
437 }else{
438 println!("Verification platform found: ");
439 let tx_hash = witness_data.witness_event_transaction_hash.clone();
440 let tx_hash_string = format!("{}", tx_hash);
441 let tx_hash_par = tx_hash_string.as_str();
442 // Create a new runtime
443 let rt = Runtime::new().unwrap();
444
445 // Block on the async function to get its result
446 let get_tx_data_res = rt.block_on( get_tx_data(
447 tx_hash_par,
448 verification_platform,
449 chain,
450 api_key,
451 ));
452
453 let (input_data, _) = get_tx_data_res.unwrap();
454 let witness_event_verification_hash = format!("{}", witness_data.witness_event_verification_hash);
455
456 println!("Data received from getting tx information: {:#?} \n verification hash: {}", input_data, witness_event_verification_hash);
457
458 if input_data == witness_event_verification_hash {
459 (true, "Look up performed.".to_string(), logs)
460 }else{
461 (false, "Look up failed.".to_string(), logs)
462 }
463
464}
465 // TODO: Implement the checkTransaction function
466
467}
468
469/// Verifies the integrity of a merkle proof.
470///
471/// # Arguments
472/// * `merkle_branch` - The merkle proof branch nodes
473/// * `verification_hash` - The hash to verify against
474///
475/// # Returns
476/// `bool` - Whether the merkle proof is valid
477pub fn verify_merkle_integrity(merkle_branch: &[MerkleNode], verification_hash: String) -> bool {
478 if merkle_branch.is_empty() {
479 return false;
480 }
481
482 //todo
483 // let mut prev_successor: Option<Hash> = None;
484 // for node in merkle_branch {
485 // let leaves = [node.left_leaf.clone(), node.right_leaf.clone()];
486 // if let Some(ref prev_succ) = prev_successor {
487 // if !leaves.contains(prev_succ) {
488 // return false;
489 // }
490 // } else {
491 // if !leaves.contains(&Some(verification_hash.to_string())) {
492 // return false;
493 // }
494 // }
495
496 // let calculated_successor = if node.left_leaf.is_none() {
497 // node.right_leaf.clone()
498 // } else if node.right_leaf.is_none() {
499 // node.left_leaf.clone()
500 // } else {
501 // Some(get_hash_sum(&(node.left_leaf.as_ref().unwrap() + node.right_leaf.as_ref().unwrap())))
502 // };
503
504 // if calculated_successor != Some(node.successor.clone()) {
505 // return false;
506 // }
507
508 // prev_successor = Some(node.successor.clone());
509 // }
510
511 true
512}
513
514
515/// Checks if all verifications in a revision result are successful.
516///
517/// # Arguments
518/// * `revision_result` - The revision verification result containing multiple verification statuses
519///
520/// # Returns
521/// `bool` - Whether all available verifications were successful
522pub fn all_successful_verifications(revision_result: &RevisionVerificationResult) -> bool {
523 let verifications = [
524 &revision_result.file_verification,
525 &revision_result.content_verification,
526 &revision_result.witness_verification,
527 &revision_result.signature_verification,
528 &revision_result.metadata_verification,
529 ];
530
531 for verification in verifications {
532 if matches!(verification.status, ResultStatusEnum::AVAILABLE) && !verification.successful {
533 return false;
534 }
535 }
536
537 true
538}
539
540/// Computes a witness hash from its components.
541///
542/// # Arguments
543/// * `domain_snapshot_genesis_hash` - The genesis hash of the domain snapshot
544/// * `merkle_root` - The root of the merkle tree
545/// * `witness_network` - The network identifier
546/// * `witness_event_transaction_hash` - The transaction hash of the witness event
547///
548/// # Returns
549/// `Hash` - The computed witness hash
550pub fn witness_hash(
551 domain_snapshot_genesis_hash: &Hash,
552 merkle_root: &Hash,
553 witness_network: &str,
554 witness_event_transaction_hash: &TxHash,
555) -> Hash {
556 // 2.a create hasher {w}
557 let mut w = crypt::Hasher::default();
558 // 2.b add rev.witness.domain_snapshot_genesis_hash to hasher {w}
559 w.update(domain_snapshot_genesis_hash.to_stackstr());
560 // 2.c add rev.witness.merkle_root to hasher {w}
561 w.update(merkle_root.to_stackstr());
562 // 2.d add rev.witness.witness_network to hasher {w}
563 w.update(witness_network);
564 // 2.e add rev.witness.witness_event_transaction_hash to hasher {w}
565 w.update(witness_event_transaction_hash.to_stackstr());
566 Hash::from(w.finalize())
567}
568
569/// Computes a signature hash from a signature and public key.
570///
571/// # Arguments
572/// * `signature` - The digital signature
573/// * `public_key` - The public key used for signing
574///
575/// # Returns
576/// `Hash` - The computed signature hash
577pub fn signature_hash(signature: &Signature, public_key: &PublicKey) -> Hash {
578 // 4.a create hasher {s}
579 let mut s = crypt::Hasher::default();
580 // 4.b add rev.signature.signature to hasher {s}
581 s.update(signature.to_stackstr());
582 // 4.c add rev.signature.public_key to hasher {s}
583 s.update(public_key.to_stackstr());
584 Hash::from(s.finalize())
585}
586
587/// Computes a content hash from a map of content data.
588///
589/// # Arguments
590/// * `content` - Map of content key-value pairs
591///
592/// # Returns
593/// `Hash` - The computed content hash
594pub fn content_hash(content: &BTreeMap<String, String>) -> Hash {
595 // 3.a create hasher {c}
596 let mut c = crypt::Hasher::default();
597 // 3.b iterate over rev.content.content by its keys
598 for value in content.values() {
599 // 3.c add each value of rev.content.content to hasher {c}
600 c.update(value);
601 }
602 Hash::from(c.finalize())
603}
604
605/// Computes a metadata hash from revision metadata components.
606///
607/// # Arguments
608/// * `domain_id` - The domain identifier
609/// * `time_stamp` - The timestamp of the revision
610/// * `previous_verification_hash` - Optional hash from previous verification
611///
612/// # Returns
613/// `Hash` - The computed metadata hash
614pub fn metadata_hash(
615 domain_id: &str,
616 time_stamp: &Timestamp,
617 previous_verification_hash: Option<&Hash>,
618) -> Hash {
619 // 4.a create hasher {m}
620 let mut m = crypt::Hasher::default();
621 // 4.b add rev.metadata.domain_id to hasher {m}
622 m.update(domain_id);
623 // 4.c add rev.metadata.time_stamp (in format %Y%m%d%H%M%S) to hasher {m}
624 m.update(time_stamp.to_string());
625 // 4.d if rev.metadata.previous_verification_hash exists then add rev.metadata.previous_verification_hash to hasher {m}
626 if let Some(prev_verification_hash) = previous_verification_hash {
627 m.update(prev_verification_hash.to_stackstr());
628 }
629 Hash::from(m.finalize())
630}
631
632/// Computes a verification hash from multiple component hashes.
633///
634/// # Arguments
635/// * `content_hash` - Hash of the content
636/// * `metadata_hash` - Hash of the metadata
637/// * `signature_hash` - Optional signature hash
638/// * `witness_hash` - Optional witness hash
639///
640/// # Returns
641/// `Hash` - The computed verification hash
642pub fn verification_hash(
643 content_hash: &Hash,
644 metadata_hash: &Hash,
645 signature_hash: Option<&Hash>,
646 witness_hash: Option<&Hash>,
647) -> Hash {
648
649 let mut v = crypt::Hasher::default();
650 // 5.b add rev.content.content_hash to hasher {v}
651 v.update(content_hash.to_stackstr());
652 // 5.c add rev.metadata.metadata_hash to hasher {v}
653 v.update(metadata_hash.to_stackstr());
654 // 5.d if prev?.signature exists then add prev.signature.signature_hash to hasher {v}
655 if let Some(prev_signature_hash) = signature_hash {
656 v.update(prev_signature_hash.to_stackstr());
657 }
658 // 5.e if prev?.witness exists then add prev.witness.witness_hash to hasher {v}
659 if let Some(prev_witness_hash) = witness_hash {
660 v.update(prev_witness_hash.to_stackstr());
661 }
662 Hash::from(v.finalize())
663}
664
665
666/// Validates page data revisions for integrity and chain consistency.
667///
668/// # Arguments
669/// * `revisions` - Vector of hash-revision pairs to validate
670///
671/// # Returns
672/// A tuple containing:
673/// * `bool` - Whether the revisions are valid
674/// * `String` - A status message describing the validation result
675pub fn check_if_page_data_revision_are_okay(revisions: Vec<(Hash, Revision)>) -> (bool, String) {
676 let mut is_valid = (true, "".to_string());
677 let has_valid_genessis = revsions_has_valid_genesis(revisions.clone());
678 // tracing::debug!("revsions_has_valid_genesis {:#?}", has_valid_genessis);
679
680 if has_valid_genessis.is_none() {
681 return (
682 false,
683 "revisions do not contain a valid genesis".to_string(),
684 );
685 }
686
687 // check if the revision > metadata > previous_verification_hash is among the hash in revsions par
688 // if more that one is none return false
689 // there is a broken revision chain
690 let mut all_hashes: Vec<Hash> = Vec::new();
691 revisions
692 .iter()
693 .for_each(|(hash, _revision)| all_hashes.push(hash.clone()));
694
695 let genesis_hash_str = format!("{:#?}", has_valid_genessis.unwrap());
696
697 for (_index, (current_hash, current_revision)) in revisions.iter().enumerate() {
698 let current_hash_str = format!("{:#?}", current_hash);
699
700 // check hash if match the newly generated one
701 let recomputed_content_hash = compute_content_hash(¤t_revision.content);
702
703 match recomputed_content_hash {
704 Ok(data) => {
705 if data == *current_hash {
706 // tracing::error!("hashes match the generetaed one continue ...");
707 } else {
708 // tracing::error!("\n hashes do not match revision has {:#?} \n vs generated hash {:#?} \n",data,current_hash );
709 is_valid = (false, format!("a hash is not valid : {:#?}", current_hash));
710
711 break;
712 }
713 }
714 Err(_error) => {
715 // tracing::error!("an error occured {}", error);
716 is_valid = (false, "error generating a hash ".to_string());
717 break;
718 }
719 }
720 // let contnent_hash_str = format!("{:#?}", revision.content.content_hash);
721 // let data_str = format!("{:#?}", revision.content.content_hash);
722 // tracing::error!("returd conetnet is {} \n my json content hash is {} \n", data_str, contnent_hash_str);
723 // matches = data ==revision.content.content_hash ;//revision.content.content_hash;
724
725 // chec if the hash chain is valid (ie if there any orphan revisions)
726 if current_hash_str == genesis_hash_str {
727 // tracing::debug!("ignoring genessis hash is {:#?}", genesis_hash_str);
728 } else {
729 let contains = all_hashes.contains(current_hash);
730
731 if contains == false {
732 // tracing::debug!("cannot find hash is {:#?}", current_hash_str);
733 is_valid = (false, "Hash chain is invalid ".to_string());
734 break;
735 }
736 }
737 }
738
739 return is_valid;
740}
741
742
743/// Checks if revisions contain a valid genesis block.
744///
745/// # Arguments
746/// * `revisions` - Vector of hash-revision pairs to check
747///
748/// # Returns
749/// `Option<Hash>` - The genesis hash if found and valid, None otherwise
750pub fn revsions_has_valid_genesis(revisions: Vec<(Hash, Revision)>) -> Option<Hash> {
751 // let mut is_valid= true;
752
753 if revisions.len() <= 1 {
754 // tracing::debug!("The lengthe is equal to or ess than 1 {}", revisions.len());
755 return None;
756 }
757
758 let mut revision_genesis: Vec<&Revision> = Vec::new();
759
760 for (_index, (_hash, revision)) in revisions.iter().enumerate() {
761 match revision.metadata.previous_verification_hash {
762 Some(_data) => {
763 // tracing::debug!("The previous hash is {:#?}", data);
764 }
765 None => {
766 // tracing::debug!("pushing revision to vector {:#?}", revision);
767 revision_genesis.push(revision)
768 }
769 }
770 }
771
772 if revision_genesis.len() > 1 {
773 // tracing::debug!(
774 // "The genesis revision length {} are {:#?}",
775 // revision_genesis.len(),
776 // revision_genesis
777 // );
778 return None;
779 }
780
781 let res = revision_genesis.first();
782 if res.is_none() {
783 // tracing::debug!("No genesis hash (vec is empty)",);
784 return None;
785 }
786
787 // tracing::debug!("************************ {:#?}", res);
788 // we use unwrapp becasue we are guaranteed the res has value due to the if check above
789 return Some(res.unwrap().metadata.verification_hash);
790}
791
792/// Computes a content hash from revision content.
793///
794/// # Arguments
795/// * `content_par` - The revision content containing file data
796///
797/// # Returns
798/// `Result<Hash, String>` - The computed hash or an error message
799pub fn compute_content_hash(content_par: &RevisionContent) -> Result<Hash, String> {
800 let b64 = content_par.file.clone().unwrap().data;
801
802 let mut file_hasher = sha3::Sha3_512::default();
803 file_hasher.update(b64.clone());
804 let file_hash_current = Hash::from(file_hasher.finalize());
805
806 let mut content_current = BTreeMap::new();
807
808 content_current.insert("file_hash".to_owned(), file_hash_current.to_string());
809
810 // println!("{:#?}", content_current);
811 // tracing::debug!("{:#?}", content_current);
812
813 let content_hash_current = content_hash(&content_current.clone());
814
815 // tracing::debug!("{:#?}", content_hash_current);
816
817 Ok(content_hash_current)
818}
819
820
821/// Creates an empty hash using SHA3-512.
822///
823/// # Returns
824/// `Hash` - A hash computed from an empty string
825pub fn make_empty_hash() -> Hash {
826 let mut hasher = sha3::Sha3_512::default();
827 hasher.update("");
828 let empty_hash = Hash::from(hasher.finalize());
829 empty_hash
830}