ggen_receipt/lib.rs
1//! Cryptographic receipt system for ggen operations.
2//!
3//! This crate provides a production-ready receipt system with Ed25519 signatures
4//! for verifiable operation tracking. Receipts can be chained together to form
5//! an auditable trail of operations.
6//!
7//! # Features
8//!
9//! - Ed25519 digital signatures for cryptographic verification
10//! - SHA-256 hashing for data integrity
11//! - Receipt chaining with hash links
12//! - Full serialization support with serde
13//! - Comprehensive error handling
14//!
15//! # Examples
16//!
17//! ## Creating and signing a receipt
18//!
19//! ```
20//! use ggen_receipt::{Receipt, generate_keypair};
21//!
22//! let (signing_key, verifying_key) = generate_keypair();
23//!
24//! let receipt = Receipt::new(
25//! "my-operation".to_string(),
26//! vec!["input-hash-1".to_string()],
27//! vec!["output-hash-1".to_string()],
28//! None,
29//! )
30//! .sign(&signing_key)
31//! .expect("Failed to sign receipt");
32//!
33//! // Verify the signature
34//! receipt.verify(&verifying_key).expect("Verification failed");
35//! ```
36//!
37//! ## Building a receipt chain
38//!
39//! ```
40//! use ggen_receipt::{Receipt, ReceiptChain, generate_keypair};
41//!
42//! let (signing_key, verifying_key) = generate_keypair();
43//!
44//! // Create genesis receipt
45//! let genesis = Receipt::new(
46//! "genesis-op".to_string(),
47//! vec![],
48//! vec![],
49//! None,
50//! )
51//! .sign(&signing_key)
52//! .expect("Failed to sign");
53//!
54//! let mut chain = ReceiptChain::from_genesis(genesis.clone())
55//! .expect("Failed to create chain");
56//!
57//! // Add a linked receipt
58//! let receipt2 = Receipt::new(
59//! "second-op".to_string(),
60//! vec![],
61//! vec![],
62//! None,
63//! )
64//! .chain(&genesis)
65//! .expect("Failed to chain")
66//! .sign(&signing_key)
67//! .expect("Failed to sign");
68//!
69//! chain.append(receipt2).expect("Failed to append");
70//!
71//! // Verify the entire chain
72//! chain.verify(&verifying_key).expect("Chain verification failed");
73//! ```
74
75#![forbid(unsafe_code)]
76#![deny(missing_docs)]
77
78pub mod chain;
79pub mod envelope;
80pub mod error;
81pub mod receipt;
82
83pub use chain::ReceiptChain;
84pub use envelope::{
85 payload_hash, EnvelopeChain, EnvelopeChainLink, EnvelopeSignature, PayloadRef, Producer,
86 ReceiptEnvelope, ENVELOPE_SCHEMA, HASH_PREFIX, SIGNATURE_ALGORITHM,
87};
88pub use error::{ReceiptError, Result};
89pub use receipt::{generate_keypair, hash_data, Receipt};
90
91/// Convenience function to create a new receipt chained to a parent.
92///
93/// Creates a receipt for `operation_id` with the given `input_hashes` and
94/// `output_hashes`, links it to `parent` via its hash, and signs it with
95/// `signing_key`. Returns the signed, chained receipt on success.
96///
97/// # Example
98///
99/// ```
100/// use ggen_receipt::{Receipt, create_chained_receipt, generate_keypair};
101///
102/// let (key, _vk) = generate_keypair();
103/// let parent = Receipt::new("parent-op".to_string(), vec![], vec![], None)
104/// .sign(&key).expect("sign");
105/// let child = create_chained_receipt(&parent, "child-op".to_string(), vec![], vec![], &key)
106/// .expect("chained");
107/// assert_eq!(child.previous_receipt_hash, Some(parent.hash().unwrap()));
108/// ```
109pub fn create_chained_receipt(
110 parent: &Receipt, operation_id: String, input_hashes: Vec<String>, output_hashes: Vec<String>,
111 signing_key: &ed25519_dalek::SigningKey,
112) -> Result<Receipt> {
113 Receipt::new(operation_id, input_hashes, output_hashes, None)
114 .chain(parent)?
115 .sign(signing_key)
116}