Skip to main content

csv_adapter_core/
dag.rs

1//! State transition DAG types
2//!
3//! The DAG represents deterministic state transitions verified off-chain.
4//! Each node contains bytecode, witnesses, and validation data.
5
6use alloc::vec::Vec;
7use serde::{Deserialize, Serialize};
8
9use crate::hash::Hash;
10use crate::tagged_hash::csv_tagged_hash;
11
12/// A single node in the state transition DAG
13#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
14pub struct DAGNode {
15    /// Unique identifier for this node
16    pub node_id: Hash,
17    /// Deterministic VM bytecode (e.g., AluVM)
18    pub bytecode: Vec<u8>,
19    /// Authorizing signatures
20    pub signatures: Vec<Vec<u8>>,
21    /// Witness data for verification
22    pub witnesses: Vec<Vec<u8>>,
23    /// Hash of parent node(s) - empty for root
24    pub parents: Vec<Hash>,
25}
26
27impl DAGNode {
28    /// Create a new DAG node
29    pub fn new(
30        node_id: Hash,
31        bytecode: Vec<u8>,
32        signatures: Vec<Vec<u8>>,
33        witnesses: Vec<Vec<u8>>,
34        parents: Vec<Hash>,
35    ) -> Self {
36        Self {
37            node_id,
38            bytecode,
39            signatures,
40            witnesses,
41            parents,
42        }
43    }
44
45    /// Compute the node hash using tagged hashing
46    pub fn hash(&self) -> Hash {
47        let mut data = Vec::new();
48        data.extend_from_slice(self.node_id.as_bytes());
49        data.extend_from_slice(&self.bytecode);
50        for sig in &self.signatures {
51            data.extend_from_slice(sig);
52        }
53        for witness in &self.witnesses {
54            data.extend_from_slice(witness);
55        }
56        for parent in &self.parents {
57            data.extend_from_slice(parent.as_bytes());
58        }
59
60        Hash::new(csv_tagged_hash("dag-node", &data))
61    }
62}
63
64/// A segment of the state transition DAG
65#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
66pub struct DAGSegment {
67    /// Nodes in this segment
68    pub nodes: Vec<DAGNode>,
69    /// Root commitment hash
70    pub root_commitment: Hash,
71}
72
73impl DAGSegment {
74    /// Create a new DAG segment
75    pub fn new(nodes: Vec<DAGNode>, root_commitment: Hash) -> Self {
76        Self {
77            nodes,
78            root_commitment,
79        }
80    }
81
82    /// Validate DAG structure (topological ordering)
83    pub fn validate_structure(&self) -> Result<(), &'static str> {
84        // Basic validation: ensure all parent references exist
85        let node_ids: alloc::collections::BTreeSet<_> =
86            self.nodes.iter().map(|n| n.node_id).collect();
87
88        for node in &self.nodes {
89            for parent in &node.parents {
90                if !node_ids.contains(parent) {
91                    return Err("Parent node not found in DAG segment");
92                }
93            }
94        }
95
96        Ok(())
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use alloc::vec;
104
105    // ─────────────────────────────────────────────
106    // Existing tests (preserved)
107    // ─────────────────────────────────────────────
108
109    #[test]
110    fn test_dag_node_creation() {
111        let node = DAGNode::new(
112            Hash::new([1u8; 32]),
113            vec![0x01, 0x02, 0x03],
114            vec![vec![0xAB; 64]],
115            vec![vec![0xCD; 32]],
116            vec![],
117        );
118        assert_eq!(node.bytecode, vec![0x01, 0x02, 0x03]);
119    }
120
121    #[test]
122    fn test_dag_node_hash() {
123        let node = DAGNode::new(
124            Hash::new([1u8; 32]),
125            vec![0x01, 0x02],
126            vec![],
127            vec![],
128            vec![],
129        );
130        let hash = node.hash();
131        assert_eq!(hash.as_bytes().len(), 32);
132    }
133
134    #[test]
135    fn test_dag_segment_validation() {
136        let parent = DAGNode::new(Hash::new([1u8; 32]), vec![], vec![], vec![], vec![]);
137
138        let child = DAGNode::new(
139            Hash::new([2u8; 32]),
140            vec![],
141            vec![],
142            vec![],
143            vec![Hash::new([1u8; 32])],
144        );
145
146        let segment = DAGSegment::new(vec![parent, child], Hash::zero());
147
148        assert!(segment.validate_structure().is_ok());
149    }
150
151    #[test]
152    fn test_dag_segment_invalid_parent() {
153        let node = DAGNode::new(
154            Hash::new([1u8; 32]),
155            vec![],
156            vec![],
157            vec![],
158            vec![Hash::new([99u8; 32])], // Non-existent parent
159        );
160
161        let segment = DAGSegment::new(vec![node], Hash::zero());
162        assert!(segment.validate_structure().is_err());
163    }
164
165    // ─────────────────────────────────────────────
166    // NEW: Hash determinism
167    // ─────────────────────────────────────────────
168
169    #[test]
170    fn test_dag_node_hash_deterministic() {
171        let node1 = DAGNode::new(
172            Hash::new([1u8; 32]),
173            vec![0x01, 0x02, 0x03],
174            vec![vec![0xAB; 64]],
175            vec![vec![0xCD; 32]],
176            vec![Hash::new([4u8; 32])],
177        );
178        let node2 = DAGNode::new(
179            Hash::new([1u8; 32]),
180            vec![0x01, 0x02, 0x03],
181            vec![vec![0xAB; 64]],
182            vec![vec![0xCD; 32]],
183            vec![Hash::new([4u8; 32])],
184        );
185        // Identical inputs must produce identical hashes
186        assert_eq!(node1.hash(), node2.hash());
187    }
188
189    // ─────────────────────────────────────────────
190    // NEW: Hash uniqueness (different inputs → different hash)
191    // ─────────────────────────────────────────────
192
193    #[test]
194    fn test_dag_node_hash_differs_by_node_id() {
195        let node_a = DAGNode::new(Hash::new([1u8; 32]), vec![0x01], vec![], vec![], vec![]);
196        let node_b = DAGNode::new(Hash::new([2u8; 32]), vec![0x01], vec![], vec![], vec![]);
197        assert_ne!(node_a.hash(), node_b.hash());
198    }
199
200    #[test]
201    fn test_dag_node_hash_differs_by_bytecode() {
202        let node_a = DAGNode::new(
203            Hash::new([1u8; 32]),
204            vec![0x01, 0x02],
205            vec![],
206            vec![],
207            vec![],
208        );
209        let node_b = DAGNode::new(
210            Hash::new([1u8; 32]),
211            vec![0x03, 0x04],
212            vec![],
213            vec![],
214            vec![],
215        );
216        assert_ne!(node_a.hash(), node_b.hash());
217    }
218
219    #[test]
220    fn test_dag_node_hash_differs_by_signatures() {
221        let node_a = DAGNode::new(
222            Hash::new([1u8; 32]),
223            vec![],
224            vec![vec![0xAA; 64]],
225            vec![],
226            vec![],
227        );
228        let node_b = DAGNode::new(
229            Hash::new([1u8; 32]),
230            vec![],
231            vec![vec![0xBB; 64]],
232            vec![],
233            vec![],
234        );
235        assert_ne!(node_a.hash(), node_b.hash());
236    }
237
238    #[test]
239    fn test_dag_node_hash_differs_by_witnesses() {
240        let node_a = DAGNode::new(
241            Hash::new([1u8; 32]),
242            vec![],
243            vec![],
244            vec![vec![0xCC; 32]],
245            vec![],
246        );
247        let node_b = DAGNode::new(
248            Hash::new([1u8; 32]),
249            vec![],
250            vec![],
251            vec![vec![0xDD; 32]],
252            vec![],
253        );
254        assert_ne!(node_a.hash(), node_b.hash());
255    }
256
257    #[test]
258    fn test_dag_node_hash_differs_by_parents() {
259        let node_a = DAGNode::new(
260            Hash::new([1u8; 32]),
261            vec![],
262            vec![],
263            vec![],
264            vec![Hash::new([10u8; 32])],
265        );
266        let node_b = DAGNode::new(
267            Hash::new([1u8; 32]),
268            vec![],
269            vec![],
270            vec![],
271            vec![Hash::new([20u8; 32])],
272        );
273        assert_ne!(node_a.hash(), node_b.hash());
274    }
275
276    // ─────────────────────────────────────────────
277    // NEW: Multi-parent DAG validation
278    // ─────────────────────────────────────────────
279
280    #[test]
281    fn test_dag_segment_multi_parent_validation() {
282        let parent_a = DAGNode::new(Hash::new([1u8; 32]), vec![], vec![], vec![], vec![]);
283        let parent_b = DAGNode::new(Hash::new([2u8; 32]), vec![], vec![], vec![], vec![]);
284        let child = DAGNode::new(
285            Hash::new([3u8; 32]),
286            vec![],
287            vec![],
288            vec![],
289            vec![Hash::new([1u8; 32]), Hash::new([2u8; 32])],
290        );
291
292        let segment = DAGSegment::new(vec![parent_a, parent_b, child], Hash::zero());
293        assert!(segment.validate_structure().is_ok());
294    }
295
296    #[test]
297    fn test_dag_segment_multi_parent_missing_one() {
298        let parent_a = DAGNode::new(Hash::new([1u8; 32]), vec![], vec![], vec![], vec![]);
299        let child = DAGNode::new(
300            Hash::new([3u8; 32]),
301            vec![],
302            vec![],
303            vec![],
304            vec![Hash::new([1u8; 32]), Hash::new([99u8; 32])],
305        );
306
307        let segment = DAGSegment::new(vec![parent_a, child], Hash::zero());
308        assert!(segment.validate_structure().is_err());
309    }
310
311    // ─────────────────────────────────────────────
312    // NEW: Root node edge case
313    // ─────────────────────────────────────────────
314
315    #[test]
316    fn test_dag_root_node_has_no_parents() {
317        let root = DAGNode::new(Hash::new([1u8; 32]), vec![0x01], vec![], vec![], vec![]);
318        assert!(root.parents.is_empty());
319
320        let segment = DAGSegment::new(vec![root.clone()], Hash::zero());
321        assert!(segment.validate_structure().is_ok());
322    }
323
324    // ─────────────────────────────────────────────
325    // NEW: Empty segment validation
326    // ─────────────────────────────────────────────
327
328    #[test]
329    fn test_dag_segment_empty_valid() {
330        let segment = DAGSegment::new(vec![], Hash::zero());
331        assert!(segment.validate_structure().is_ok());
332    }
333
334    // ─────────────────────────────────────────────
335    // NEW: Serialization roundtrip (DAGNode, DAGSegment)
336    // ─────────────────────────────────────────────
337
338    #[test]
339    fn test_dag_node_serialization_roundtrip() {
340        let node = DAGNode::new(
341            Hash::new([1u8; 32]),
342            vec![0x01, 0x02, 0x03],
343            vec![vec![0xAB; 64]],
344            vec![vec![0xCD; 32]],
345            vec![Hash::new([4u8; 32])],
346        );
347
348        let bytes = bincode::serialize(&node).unwrap();
349        let restored: DAGNode = bincode::deserialize(&bytes).unwrap();
350        assert_eq!(node, restored);
351    }
352
353    #[test]
354    fn test_dag_segment_serialization_roundtrip() {
355        let parent = DAGNode::new(Hash::new([1u8; 32]), vec![0x01], vec![], vec![], vec![]);
356        let child = DAGNode::new(
357            Hash::new([2u8; 32]),
358            vec![0x02],
359            vec![vec![0xAB; 64]],
360            vec![],
361            vec![Hash::new([1u8; 32])],
362        );
363
364        let segment = DAGSegment::new(vec![parent, child], Hash::new([99u8; 32]));
365
366        let bytes = bincode::serialize(&segment).unwrap();
367        let restored: DAGSegment = bincode::deserialize(&bytes).unwrap();
368        assert_eq!(segment, restored);
369    }
370
371    #[test]
372    fn test_dag_node_serialization_preserves_hash() {
373        let node = DAGNode::new(
374            Hash::new([1u8; 32]),
375            vec![0x01, 0x02],
376            vec![vec![0xAB; 64]],
377            vec![],
378            vec![],
379        );
380        let original_hash = node.hash();
381
382        let bytes = bincode::serialize(&node).unwrap();
383        let restored: DAGNode = bincode::deserialize(&bytes).unwrap();
384        assert_eq!(original_hash, restored.hash());
385    }
386
387    // ─────────────────────────────────────────────
388    // NEW: Large DAG segment validation
389    // ─────────────────────────────────────────────
390
391    #[test]
392    fn test_dag_segment_large_chain() {
393        let mut nodes = Vec::new();
394
395        // Build a chain of 100 nodes
396        for i in 0..100u8 {
397            let mut id = [0u8; 32];
398            id[0] = i + 1;
399
400            let parents = if i == 0 {
401                // First node is root (no parents)
402                vec![]
403            } else {
404                let mut prev_id = [0u8; 32];
405                prev_id[0] = i;
406                vec![Hash::new(prev_id)]
407            };
408
409            let node = DAGNode::new(Hash::new(id), vec![i], vec![], vec![], parents);
410            nodes.push(node);
411        }
412
413        let segment = DAGSegment::new(nodes, Hash::zero());
414        assert!(segment.validate_structure().is_ok());
415    }
416
417    #[test]
418    fn test_dag_segment_large_diamond() {
419        // Build a diamond pattern: root → A, B → leaf
420        let root = DAGNode::new(Hash::new([0u8; 32]), vec![], vec![], vec![], vec![]);
421        let node_a = DAGNode::new(
422            Hash::new([1u8; 32]),
423            vec![],
424            vec![],
425            vec![],
426            vec![Hash::new([0u8; 32])],
427        );
428        let node_b = DAGNode::new(
429            Hash::new([2u8; 32]),
430            vec![],
431            vec![],
432            vec![],
433            vec![Hash::new([0u8; 32])],
434        );
435        let leaf = DAGNode::new(
436            Hash::new([3u8; 32]),
437            vec![],
438            vec![],
439            vec![],
440            vec![Hash::new([1u8; 32]), Hash::new([2u8; 32])],
441        );
442
443        let segment = DAGSegment::new(vec![root, node_a, node_b, leaf], Hash::zero());
444        assert!(segment.validate_structure().is_ok());
445    }
446
447    // ─────────────────────────────────────────────
448    // NEW: Duplicate node ID handling
449    // ─────────────────────────────────────────────
450
451    #[test]
452    fn test_dag_segment_duplicate_node_ids_still_valid() {
453        // Two nodes with same ID (structurally valid but semantically problematic)
454        let node_a = DAGNode::new(Hash::new([1u8; 32]), vec![0x01], vec![], vec![], vec![]);
455        let node_b = DAGNode::new(
456            Hash::new([1u8; 32]), // Same ID as node_a
457            vec![0x02],
458            vec![],
459            vec![],
460            vec![],
461        );
462        let child = DAGNode::new(
463            Hash::new([3u8; 32]),
464            vec![],
465            vec![],
466            vec![],
467            vec![Hash::new([1u8; 32])],
468        );
469
470        let segment = DAGSegment::new(vec![node_a, node_b, child], Hash::zero());
471        // Validates because the parent ID exists in the set
472        assert!(segment.validate_structure().is_ok());
473    }
474
475    // ─────────────────────────────────────────────
476    // NEW: Bytecode ordering in hash
477    // ─────────────────────────────────────────────
478
479    #[test]
480    fn test_dag_node_hash_bytecode_order_sensitive() {
481        let node_a = DAGNode::new(
482            Hash::new([1u8; 32]),
483            vec![0x01, 0x02, 0x03],
484            vec![],
485            vec![],
486            vec![],
487        );
488        let node_b = DAGNode::new(
489            Hash::new([1u8; 32]),
490            vec![0x03, 0x02, 0x01],
491            vec![],
492            vec![],
493            vec![],
494        );
495        assert_ne!(node_a.hash(), node_b.hash());
496    }
497
498    // ─────────────────────────────────────────────
499    // NEW: Signature/witness ordering effects
500    // ─────────────────────────────────────────────
501
502    #[test]
503    fn test_dag_node_hash_signature_order_sensitive() {
504        let node_a = DAGNode::new(
505            Hash::new([1u8; 32]),
506            vec![],
507            vec![vec![0xAA; 64], vec![0xBB; 64]],
508            vec![],
509            vec![],
510        );
511        let node_b = DAGNode::new(
512            Hash::new([1u8; 32]),
513            vec![],
514            vec![vec![0xBB; 64], vec![0xAA; 64]],
515            vec![],
516            vec![],
517        );
518        assert_ne!(node_a.hash(), node_b.hash());
519    }
520
521    #[test]
522    fn test_dag_node_hash_witness_order_sensitive() {
523        let node_a = DAGNode::new(
524            Hash::new([1u8; 32]),
525            vec![],
526            vec![],
527            vec![vec![0xCC; 32], vec![0xDD; 32]],
528            vec![],
529        );
530        let node_b = DAGNode::new(
531            Hash::new([1u8; 32]),
532            vec![],
533            vec![],
534            vec![vec![0xDD; 32], vec![0xCC; 32]],
535            vec![],
536        );
537        assert_ne!(node_a.hash(), node_b.hash());
538    }
539
540    #[test]
541    fn test_dag_node_hash_parent_order_sensitive() {
542        let node_a = DAGNode::new(
543            Hash::new([1u8; 32]),
544            vec![],
545            vec![],
546            vec![],
547            vec![Hash::new([10u8; 32]), Hash::new([20u8; 32])],
548        );
549        let node_b = DAGNode::new(
550            Hash::new([1u8; 32]),
551            vec![],
552            vec![],
553            vec![],
554            vec![Hash::new([20u8; 32]), Hash::new([10u8; 32])],
555        );
556        assert_ne!(node_a.hash(), node_b.hash());
557    }
558
559    // ─────────────────────────────────────────────
560    // NEW: Complex DAG with signatures and witnesses
561    // ─────────────────────────────────────────────
562
563    #[test]
564    fn test_dag_complex_structure_with_signatures_and_witnesses() {
565        let root = DAGNode::new(
566            Hash::new([1u8; 32]),
567            vec![0x01, 0x02],
568            vec![vec![0xAA; 64]],
569            vec![vec![0xBB; 32]],
570            vec![],
571        );
572        let child = DAGNode::new(
573            Hash::new([2u8; 32]),
574            vec![0x03, 0x04],
575            vec![vec![0xCC; 64], vec![0xDD; 64]],
576            vec![vec![0xEE; 32]],
577            vec![Hash::new([1u8; 32])],
578        );
579
580        let segment = DAGSegment::new(vec![root, child], Hash::zero());
581        assert!(segment.validate_structure().is_ok());
582        assert_ne!(segment.nodes[0].hash(), segment.nodes[1].hash());
583    }
584
585    // ─────────────────────────────────────────────
586    // NEW: DAG + Commitment integration
587    // ─────────────────────────────────────────────
588
589    #[cfg(feature = "std")]
590    mod integration {
591        use super::*;
592        use crate::commitment::Commitment;
593        use crate::proof::ProofBundle;
594        use crate::seal::SealRef;
595
596        #[test]
597        fn test_dag_hash_used_in_commitment() {
598            let node = DAGNode::new(
599                Hash::new([1u8; 32]),
600                vec![0x01, 0x02],
601                vec![vec![0xAB; 64]],
602                vec![],
603                vec![],
604            );
605            let dag_hash = node.hash();
606
607            // DAG hash can serve as transition payload hash in commitment
608            let seal = SealRef::new(vec![0xAA; 16], Some(42)).unwrap();
609            let domain = [0xBB; 32];
610            let commitment =
611                Commitment::simple(Hash::new([2u8; 32]), Hash::zero(), dag_hash, &seal, domain);
612
613            // Commitment produces a valid hash
614            assert_eq!(commitment.hash().as_bytes().len(), 32);
615        }
616
617        #[test]
618        fn test_dag_inside_proof_bundle_roundtrip() {
619            let node = DAGNode::new(
620                Hash::new([1u8; 32]),
621                vec![0x01],
622                vec![vec![0xAB; 64]],
623                vec![],
624                vec![],
625            );
626            let segment = DAGSegment::new(vec![node], Hash::new([99u8; 32]));
627
628            let bundle = ProofBundle::new(
629                segment.clone(),
630                vec![vec![0xCC; 64]],
631                SealRef::new(vec![1, 2, 3], Some(42)).unwrap(),
632                crate::seal::AnchorRef::new(vec![4, 5, 6], 100, vec![]).unwrap(),
633                crate::proof::InclusionProof::new(vec![], Hash::zero(), 0).unwrap(),
634                crate::proof::FinalityProof::new(vec![], 6, false).unwrap(),
635            )
636            .unwrap();
637
638            // Serialize and deserialize the full bundle (DAG included)
639            let bytes = bundle.to_bytes().unwrap();
640            let restored = ProofBundle::from_bytes(&bytes).unwrap();
641            assert_eq!(bundle.transition_dag, restored.transition_dag);
642        }
643
644        #[test]
645        fn test_dag_in_verify_proof_pipeline() {
646            use secp256k1::{Message, Secp256k1, SecretKey};
647            // The message signed is the DAG root commitment
648            let root_commitment = Hash::new([99u8; 32]);
649            let message: [u8; 32] = *root_commitment.as_bytes();
650            let secp = Secp256k1::new();
651            let secret_key = SecretKey::new(&mut secp256k1::rand::thread_rng());
652            let public_key = secp256k1::PublicKey::from_secret_key(&secp, &secret_key);
653            let msg = Message::from_digest_slice(&message).unwrap();
654            let signature_ecdsa = secp.sign_ecdsa(&msg, &secret_key);
655            let sig_bytes = signature_ecdsa.serialize_compact();
656            let pubkey_bytes = public_key.serialize();
657            let mut signature = Vec::with_capacity(4 + pubkey_bytes.len() + sig_bytes.len());
658            signature.extend_from_slice(&(pubkey_bytes.len() as u32).to_le_bytes());
659            signature.extend_from_slice(&pubkey_bytes);
660            signature.extend_from_slice(&sig_bytes);
661
662            let node = DAGNode::new(
663                Hash::new([1u8; 32]),
664                vec![0x01, 0x02],
665                vec![signature.clone()],
666                vec![],
667                vec![],
668            );
669            let segment = DAGSegment::new(vec![node], Hash::new([99u8; 32]));
670
671            let bundle = ProofBundle::new(
672                segment,
673                vec![signature],
674                SealRef::new(vec![1, 2, 3], Some(42)).unwrap(),
675                crate::seal::AnchorRef::new(vec![4, 5, 6], 100, vec![]).unwrap(),
676                crate::proof::InclusionProof::new(vec![0xDD; 32], Hash::new([10u8; 32]), 0)
677                    .unwrap(),
678                crate::proof::FinalityProof::new(vec![], 6, false).unwrap(),
679            )
680            .unwrap();
681
682            // Valid DAG passes verification
683            let seal_registry = |_id: &[u8]| false;
684            assert!(crate::proof_verify::verify_proof(
685                &bundle,
686                seal_registry,
687                crate::signature::SignatureScheme::Secp256k1
688            )
689            .is_ok());
690        }
691
692        #[test]
693        fn test_dag_with_invalid_parent_fails_in_proof_bundle() {
694            use secp256k1::{Message, Secp256k1, SecretKey};
695            let root_commitment = Hash::zero();
696            let message: [u8; 32] = *root_commitment.as_bytes();
697            let secp = Secp256k1::new();
698            let secret_key = SecretKey::new(&mut secp256k1::rand::thread_rng());
699            let public_key = secp256k1::PublicKey::from_secret_key(&secp, &secret_key);
700            let msg = Message::from_digest_slice(&message).unwrap();
701            let signature_ecdsa = secp.sign_ecdsa(&msg, &secret_key);
702            let sig_bytes = signature_ecdsa.serialize_compact();
703            let pubkey_bytes = public_key.serialize();
704            let mut signature = Vec::with_capacity(4 + pubkey_bytes.len() + sig_bytes.len());
705            signature.extend_from_slice(&(pubkey_bytes.len() as u32).to_le_bytes());
706            signature.extend_from_slice(&pubkey_bytes);
707            signature.extend_from_slice(&sig_bytes);
708
709            let node = DAGNode::new(
710                Hash::new([1u8; 32]),
711                vec![0x01],
712                vec![signature.clone()],
713                vec![],
714                vec![Hash::new([99u8; 32])], // Non-existent parent
715            );
716            let segment = DAGSegment::new(vec![node], Hash::zero());
717
718            let bundle = ProofBundle::new(
719                segment,
720                vec![signature],
721                SealRef::new(vec![1, 2, 3], Some(42)).unwrap(),
722                crate::seal::AnchorRef::new(vec![4, 5, 6], 100, vec![]).unwrap(),
723                crate::proof::InclusionProof::new(vec![0xDD; 32], Hash::new([10u8; 32]), 0)
724                    .unwrap(),
725                crate::proof::FinalityProof::new(vec![], 6, false).unwrap(),
726            )
727            .unwrap();
728
729            let seal_registry = |_id: &[u8]| false;
730            let result = crate::proof_verify::verify_proof(
731                &bundle,
732                seal_registry,
733                crate::signature::SignatureScheme::Secp256k1,
734            );
735            assert!(result.is_err());
736        }
737
738        #[test]
739        fn test_same_dag_produces_same_commitment_hash() {
740            // Build identical DAG twice
741            fn build_dag() -> DAGSegment {
742                let root = DAGNode::new(
743                    Hash::new([1u8; 32]),
744                    vec![0x01, 0x02],
745                    vec![vec![0xAA; 64]],
746                    vec![vec![0xBB; 32]],
747                    vec![],
748                );
749                let child = DAGNode::new(
750                    Hash::new([2u8; 32]),
751                    vec![0x03],
752                    vec![vec![0xCC; 64]],
753                    vec![],
754                    vec![Hash::new([1u8; 32])],
755                );
756                DAGSegment::new(vec![root, child], Hash::new([3u8; 32]))
757            }
758
759            let dag_a = build_dag();
760            let dag_b = build_dag();
761
762            // Use root commitment hashes as payload inputs
763            let seal = SealRef::new(vec![0xFF; 16], Some(1)).unwrap();
764            let domain = [0xEE; 32];
765
766            let commitment_a = Commitment::simple(
767                Hash::new([10u8; 32]),
768                Hash::zero(),
769                dag_a.root_commitment,
770                &seal,
771                domain,
772            );
773            let commitment_b = Commitment::simple(
774                Hash::new([10u8; 32]),
775                Hash::zero(),
776                dag_b.root_commitment,
777                &seal,
778                domain,
779            );
780
781            assert_eq!(commitment_a.hash(), commitment_b.hash());
782        }
783    }
784}