trustchain_core/
commitment.rs1use crate::utils::{json_contains, HasEndpoints, HasKeys};
3use crate::verifier::Timestamp;
4use serde::Serialize;
5use serde_json::{json, Value};
6use ssi::{
7 did::{Document, ServiceEndpoint},
8 jwk::JWK,
9};
10use std::fmt::Display;
11use thiserror::Error;
12
13pub type CommitmentResult<T> = Result<T, CommitmentError>;
15
16#[derive(Error, Debug)]
18pub enum CommitmentError {
19 #[error("Data decoding failed.")]
21 DataDecodingFailure,
22 #[error("Data decoding error: {0}")]
24 DataDecodingError(String),
25 #[error("Failed to compute hash: {0}")]
27 FailedToComputeHash(String),
28 #[error("Failed hash verification.")]
30 FailedHashVerification(String),
31 #[error("Failed content verification. Expected data {0} not found in candidate: {1}.")]
33 FailedContentVerification(String, String),
34 #[error("Failed verification. Empty iterated commitment.")]
36 EmptyChainedCommitment,
37 #[error("Failed retrieval of expected data. Empty expected data.")]
39 EmptyExpectedData,
40 #[error("Failed to deserialize.")]
42 FailedToDeserialize(serde_json::Error),
43}
44
45impl From<serde_json::Error> for CommitmentError {
46 fn from(err: serde_json::Error) -> Self {
47 CommitmentError::FailedToDeserialize(err)
48 }
49}
50
51pub trait TrivialCommitment<T = Value> {
53 fn hasher(&self) -> fn(&[u8]) -> CommitmentResult<String>;
55 fn candidate_data(&self) -> &[u8];
57 fn decode_candidate_data(&self) -> fn(&[u8]) -> CommitmentResult<Value>;
59 fn filter(&self) -> Option<Box<dyn Fn(&serde_json::Value) -> CommitmentResult<Value>>> {
61 None
62 }
63 fn hash(&self) -> CommitmentResult<String> {
65 self.hasher()(self.candidate_data())
67 }
68 fn commitment_content(&self) -> CommitmentResult<Value> {
70 let unfiltered_candidate_data = self.decode_candidate_data()(self.candidate_data())?;
71 let candidate_data = match self.filter() {
73 Some(filter) => filter(&unfiltered_candidate_data).map_err(|e| {
74 CommitmentError::DataDecodingError(format!(
75 "Error filtering commitment content: {}",
76 e
77 ))
78 }),
79 None => Ok(unfiltered_candidate_data.clone()),
80 }?;
81
82 if self.filter().is_some() && !json_contains(&unfiltered_candidate_data, &candidate_data) {
85 return Err(CommitmentError::DataDecodingError(
86 "Filtering of candidate data injects pollution.".to_string(),
87 ));
88 }
89 Ok(candidate_data)
90 }
91 fn to_commitment(self: Box<Self>, expected_data: T) -> Box<dyn Commitment<T>>;
94}
95
96pub trait Commitment<T = Value>: TrivialCommitment<T>
98where
99 T: Serialize + Display,
100{
101 fn expected_data(&self) -> &T;
103
104 fn verify_content(&self) -> CommitmentResult<()> {
106 let candidate_data = self.commitment_content()?;
108
109 if !json_contains(&candidate_data, &json!(self.expected_data())) {
113 return Err(CommitmentError::FailedContentVerification(
114 self.expected_data().to_string(),
115 candidate_data.to_string(),
116 ));
117 }
118 Ok(())
119 }
120
121 fn verify(&self, target: &str) -> CommitmentResult<()> {
123 self.verify_content()?;
125 if self.hash()?.ne(target) {
127 return Err(CommitmentError::FailedHashVerification(
128 "Computed hash not equal to target.".to_string(),
129 ));
130 }
131 Ok(())
132 }
133}
134
135pub trait CommitmentChain: Commitment {
138 fn commitments(&self) -> &Vec<Box<dyn Commitment>>;
140
141 fn mut_commitments(&mut self) -> &mut Vec<Box<dyn Commitment>>;
143
144 fn append(&mut self, trivial_commitment: Box<dyn TrivialCommitment>) -> CommitmentResult<()>;
150}
151
152pub struct ChainedCommitment {
155 commitments: Vec<Box<dyn Commitment>>,
156}
157
158impl ChainedCommitment {
159 pub fn new(commitment: Box<dyn Commitment>) -> Self {
160 let commitments: Vec<Box<dyn Commitment>> = vec![commitment];
161 Self { commitments }
162 }
163}
164
165impl TrivialCommitment for ChainedCommitment {
166 fn hasher(&self) -> fn(&[u8]) -> CommitmentResult<String> {
167 self.commitments()
169 .last()
170 .expect("Unexpected empty commitment chain.")
171 .hasher()
172 }
173
174 fn candidate_data(&self) -> &[u8] {
175 self.commitments
177 .first()
178 .as_ref()
179 .expect("Unexpected empty commitment chain.")
180 .candidate_data()
181 }
182
183 fn decode_candidate_data(&self) -> fn(&[u8]) -> CommitmentResult<Value> {
184 self.commitments()
185 .first()
186 .expect("Unexpected empty commitment chain.")
187 .decode_candidate_data()
188 }
189
190 fn hash(&self) -> CommitmentResult<String> {
191 self.commitments()
193 .last()
194 .ok_or(CommitmentError::EmptyChainedCommitment)?
195 .hash()
196 }
197
198 fn to_commitment(self: Box<Self>, _expected_data: serde_json::Value) -> Box<dyn Commitment> {
199 self
200 }
201}
202
203impl Commitment for ChainedCommitment {
204 fn expected_data(&self) -> &serde_json::Value {
205 self.commitments().first().unwrap().expected_data()
208 }
209
210 fn verify(&self, target: &str) -> CommitmentResult<()> {
212 self.verify_content()?;
214
215 let commitments = self.commitments();
217 if commitments.is_empty() {
218 return Err(CommitmentError::EmptyChainedCommitment);
219 }
220 let mut it = self.commitments().iter();
221 let mut commitment = it.next().unwrap();
222
223 while let Some(&next) = it.next().as_ref() {
224 let this_target = match next.expected_data() {
226 serde_json::Value::String(x) => x,
227 _ => {
228 return Err(CommitmentError::DataDecodingError(
229 "Unhandled JSON Value variant. Expected String.".to_string(),
230 ));
231 }
232 };
233 commitment.verify(this_target)?;
234 commitment = next;
235 }
236 commitment.verify(target)?;
238 Ok(())
239 }
240}
241
242impl CommitmentChain for ChainedCommitment {
243 fn commitments(&self) -> &Vec<Box<dyn Commitment>> {
244 &self.commitments
245 }
246
247 fn mut_commitments(&mut self) -> &mut Vec<Box<dyn Commitment>> {
248 &mut self.commitments
249 }
250
251 fn append(&mut self, trivial_commitment: Box<dyn TrivialCommitment>) -> CommitmentResult<()> {
252 let expected_data = json!(self.hash()?);
255 let new_commitment = trivial_commitment.to_commitment(expected_data);
256
257 self.mut_commitments().push(new_commitment);
258 Ok(())
259 }
260}
261
262pub trait DIDCommitment: Commitment {
263 fn did(&self) -> &str;
265 fn did_document(&self) -> &Document;
267 fn candidate_keys(&self) -> Option<Vec<JWK>> {
269 self.did_document().get_keys()
270 }
271 fn candidate_endpoints(&self) -> Option<Vec<ServiceEndpoint>> {
273 self.did_document().get_endpoints()
274 }
275 fn as_any(&self) -> &dyn std::any::Any;
276}
277
278pub trait TimestampCommitment: Commitment<Timestamp> {
280 fn timestamp(&self) -> Timestamp {
282 self.expected_data().to_owned()
283 }
284}