dsfb_densor_runtime/
receipt.rs1use crate::manifest::DensorManifest;
9use crate::seal::{to_hex, CanonicalHasher};
10use crate::stage::StageReceiptSummary;
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct RuntimeReceiptV1 {
16 pub pipeline_id: String,
17 pub stages: Vec<StageReceiptSummary>,
19 pub non_claim: String,
21 pub receipt_hash: String,
24}
25
26impl RuntimeReceiptV1 {
27 pub const NON_CLAIM: &'static str = "A deterministic record of which stages ran over which densors against \
28 which frozen authorities — NOT a claim that the result is correct, optimal, or meaningful in any domain; \
29 the runtime is a mechanism, the meaning lives in the authorities it cites.";
30
31 fn seal(
32 pipeline_id: &str,
33 manifest: &DensorManifest,
34 stages: &[StageReceiptSummary],
35 ) -> String {
36 let mut h = CanonicalHasher::new();
37 h.field("schema", b"dsfb_densor_runtime_receipt_v1");
38 h.field("pipeline_id", pipeline_id.as_bytes());
39 for a in &manifest.authorities {
41 h.field("authority_name", a.name.as_bytes());
42 h.hash32("authority_hash", &a.hash);
43 }
44 for s in stages {
45 h.field("stage_id", s.stage_id.as_bytes());
46 h.hash32("input_hash", &s.input_hash);
47 h.hash32("output_hash", &s.output_hash);
48 for a in &s.authority_hashes {
49 h.field("stage_authority", a.name.as_bytes());
50 h.hash32("stage_authority_hash", &a.hash);
51 }
52 }
53 h.field("non_claim", Self::NON_CLAIM.as_bytes());
54 h.finalize_hex()
55 }
56
57 pub fn build(manifest: &DensorManifest, stages: Vec<StageReceiptSummary>) -> Self {
59 let receipt_hash = Self::seal(&manifest.pipeline_id, manifest, &stages);
60 RuntimeReceiptV1 {
61 pipeline_id: manifest.pipeline_id.clone(),
62 stages,
63 non_claim: Self::NON_CLAIM.to_string(),
64 receipt_hash,
65 }
66 }
67
68 pub fn verify(&self, manifest: &DensorManifest) -> bool {
70 self.non_claim == Self::NON_CLAIM
71 && self.receipt_hash == Self::seal(&self.pipeline_id, manifest, &self.stages)
72 }
73
74 pub fn short(&self) -> &str {
76 if self.receipt_hash.len() >= 12 {
77 &self.receipt_hash[..12]
78 } else {
79 &self.receipt_hash
80 }
81 }
82}
83
84pub fn hex(h: &[u8; 32]) -> String {
86 to_hex(h)
87}