trazaeo 0.5.4

Open-source provenance SDK and specification for verifiable EO and climate data workflows
Documentation
use crate::checkpoint::CheckpointArtifact;
use crate::envelope::{Attestation, PublishEnvelope};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PublishInput {
    pub schema_version: String,
    pub issued_at: String,
    pub subject_id: String,
    pub dataset_id: String,
    pub dataset_version: String,
    pub input_refs: Vec<String>,
    pub output_refs: Vec<String>,
    pub published_artifacts: Vec<CheckpointArtifact>,
    pub primary_artifact_id: String,
    pub checkpoint_manifest_ref: String,
    pub checkpoint_manifest_hash: String,
    pub checkpoint_id: String,
    pub checkpoint_log_root_hash: String,
    pub lineage_refs: Vec<String>,
    pub verification_policy_id: String,
    pub key_id: String,
    pub stac_refs: Vec<String>,
    pub reward_context_ref: Option<String>,
    pub reward_context_hash: Option<String>,
    pub provenance_start_mode: String,
    pub bootstrap_origin_label: Option<String>,
    pub reward_eligible: bool,
}

/// Builds publish envelope.
pub fn build_publish_envelope(input: &PublishInput, attestation: Attestation) -> PublishEnvelope {
    PublishEnvelope {
        schema_version: input.schema_version.clone(),
        envelope_type: "publish".to_string(),
        issued_at: input.issued_at.clone(),
        subject_id: input.subject_id.clone(),
        dataset_id: input.dataset_id.clone(),
        dataset_version: input.dataset_version.clone(),
        input_refs: input.input_refs.clone(),
        output_refs: input.output_refs.clone(),
        published_artifacts: input.published_artifacts.clone(),
        primary_artifact_id: input.primary_artifact_id.clone(),
        checkpoint_manifest_ref: input.checkpoint_manifest_ref.clone(),
        checkpoint_manifest_hash: input.checkpoint_manifest_hash.clone(),
        checkpoint_id: input.checkpoint_id.clone(),
        checkpoint_log_root_hash: input.checkpoint_log_root_hash.clone(),
        lineage_refs: input.lineage_refs.clone(),
        verification_policy_id: input.verification_policy_id.clone(),
        attestations: vec![attestation],
        key_id: input.key_id.clone(),
        stac_refs: input.stac_refs.clone(),
        reward_context_ref: input.reward_context_ref.clone(),
        reward_context_hash: input.reward_context_hash.clone(),
        provenance_start_mode: input.provenance_start_mode.clone(),
        bootstrap_origin_label: input.bootstrap_origin_label.clone(),
        reward_eligible: input.reward_eligible,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Handles attestation.
    fn attestation() -> Attestation {
        Attestation {
            signer_id: "publisher".to_string(),
            key_id: "key-1".to_string(),
            signature: "sig".to_string(),
            signed_at: "2026-01-01T00:00:00Z".to_string(),
        }
    }

    /// Handles input.
    fn input(reward_eligible: bool) -> PublishInput {
        PublishInput {
            schema_version: "1.0.0".to_string(),
            issued_at: "2026-01-01T00:00:00Z".to_string(),
            subject_id: "publish-1".to_string(),
            dataset_id: "sst".to_string(),
            dataset_version: "v1".to_string(),
            input_refs: vec!["obj://zarr/1".to_string()],
            output_refs: vec!["obj://release/1".to_string()],
            published_artifacts: vec![CheckpointArtifact {
                artifact_id: "artifact-1".to_string(),
                content_root_hash: "root-1".to_string(),
                content_descriptor_ref: None,
                content_descriptor_hash: None,
                media_type: "application/vnd+zarr".to_string(),
            }],
            primary_artifact_id: "artifact-1".to_string(),
            checkpoint_manifest_ref: "checkpoint://1".to_string(),
            checkpoint_manifest_hash: "checkpoint-hash".to_string(),
            checkpoint_id: "checkpoint-1".to_string(),
            checkpoint_log_root_hash: "checkpoint-log-root".to_string(),
            lineage_refs: vec!["capture://1".to_string(), "transform://1".to_string()],
            verification_policy_id: "verify-default".to_string(),
            key_id: "key-1".to_string(),
            stac_refs: vec![],
            reward_context_ref: None,
            reward_context_hash: None,
            provenance_start_mode: "transport_capture".to_string(),
            bootstrap_origin_label: None,
            reward_eligible,
        }
    }

    /// Tests that build publish envelope without reward proof-log linkage is valid when not reward eligible.
    #[test]
    fn build_publish_envelope_without_reward_proof_log_is_valid_when_not_reward_eligible() {
        let env = build_publish_envelope(&input(false), attestation());
        assert!(env.validate().is_ok());
    }

    /// Tests that build publish envelope with reward proof-log linkage is valid.
    #[test]
    fn build_publish_envelope_with_reward_proof_log_is_valid() {
        let env = build_publish_envelope(&input(true), attestation());
        assert!(env.validate().is_ok());
    }
}