Skip to main content

exo_consensus/
commitment.rs

1// Copyright 2026 Exochain Foundation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at:
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// SPDX-License-Identifier: Apache-2.0
16
17use exo_core::{hash::hash_structured, types::Hash256};
18use serde::Serialize;
19
20use crate::{
21    error::{ConsensusError, Result},
22    round::ModelDeliberationResponse,
23};
24
25/// Cryptographically commits to a position before revealing it.
26/// Uses BLAKE3 under the hood.
27pub fn commit(position_text: &str) -> Hash256 {
28    Hash256::digest(position_text.as_bytes())
29}
30
31/// Verifies that a revealed position matches its prior commitment.
32pub fn verify_commitment(position_text: &str, commitment: &Hash256) -> bool {
33    commit(position_text) == *commitment
34}
35
36/// Cryptographically commits to the structured response evidence before reveal.
37pub fn commit_response(response: &ModelDeliberationResponse) -> Result<Hash256> {
38    #[derive(Serialize)]
39    struct CommitmentPayload<'a> {
40        domain: &'static str,
41        schema_version: &'static str,
42        position_text: &'a str,
43        key_claims: &'a [String],
44        confidence_bps: u64,
45    }
46
47    let payload = CommitmentPayload {
48        domain: "exo.consensus.model_response.commitment.v1",
49        schema_version: "1",
50        position_text: &response.position_text,
51        key_claims: &response.key_claims,
52        confidence_bps: response.confidence_bps,
53    };
54
55    hash_structured(&payload).map_err(|source| ConsensusError::HashSerialization {
56        context: "structured consensus model response commitment",
57        source,
58    })
59}
60
61/// Verifies that revealed structured response evidence matches its commitment.
62pub fn verify_response_commitment(
63    response: &ModelDeliberationResponse,
64    commitment: &Hash256,
65) -> Result<bool> {
66    Ok(commit_response(response)? == *commitment)
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn raw_position_commitment_is_deterministic_and_bound_to_text() {
75        let first = commit("allow subject-signed AVC receipts");
76        let second = commit("allow subject-signed AVC receipts");
77        let changed = commit("deny unsigned AVC receipts");
78
79        assert_eq!(first, second);
80        assert_ne!(first, changed);
81    }
82
83    #[test]
84    fn raw_position_commitment_verification_rejects_changed_text() {
85        let commitment = commit("canonical position");
86
87        assert!(verify_commitment("canonical position", &commitment));
88        assert!(!verify_commitment("different position", &commitment));
89    }
90}