trustchain_ion/
sidetree.rs1use crate::ion::IONTest as ION;
3use did_ion::sidetree::{Delta, Sidetree, SuffixData};
4use serde::{Deserialize, Serialize};
5use trustchain_core::{commitment::CommitmentError, utils::get_did_suffix};
6
7#[derive(Clone, Debug, Serialize, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct CreateSuffixData {
11 pub suffix_data: SuffixData,
13}
14#[derive(Clone, Debug, Serialize, Deserialize)]
16#[serde(rename_all = "camelCase")]
17pub struct OtherOperationSuffixData {
18 pub did_suffix: String,
20 pub reveal_value: String,
22}
23
24#[derive(Clone, Debug, Serialize, Deserialize)]
26#[serde(rename_all = "camelCase")]
27pub struct CoreIndexFileOperations {
28 pub create: Option<Vec<CreateSuffixData>>,
30 pub recover: Option<Vec<OtherOperationSuffixData>>,
32 pub deactivate: Option<Vec<OtherOperationSuffixData>>,
34}
35#[derive(Clone, Debug, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub struct CoreIndexFile {
39 pub core_proof_file_uri: Option<String>,
41 pub provisional_index_file_uri: Option<String>,
43 pub writer_lock_id: Option<String>,
45 pub operations: Option<CoreIndexFileOperations>,
47}
48
49impl CoreIndexFile {
50 pub fn created_did_suffixes(&self) -> Option<Vec<String>> {
52 Some(
53 self.operations
54 .as_ref()?
55 .create
56 .as_ref()?
57 .iter()
58 .filter_map(|create_suffix_data| {
59 Some(
60 ION::serialize_suffix_data(&create_suffix_data.suffix_data)
61 .ok()?
62 .to_string(),
63 )
64 })
65 .collect::<Vec<_>>(),
66 )
67 }
68 pub fn did_create_operation_index(&self, did: &str) -> Result<usize, CommitmentError> {
70 let did_suffix = get_did_suffix(did);
72 self.created_did_suffixes()
73 .ok_or(CommitmentError::FailedContentVerification(
74 did.to_string(),
75 serde_json::to_string(self).unwrap(),
76 ))?
77 .into_iter()
78 .position(|v| v == did_suffix)
79 .ok_or(CommitmentError::FailedContentVerification(
80 did.to_string(),
81 serde_json::to_string(self).unwrap(),
82 ))
83 }
84}
85
86#[derive(Clone, Debug, Serialize, Deserialize)]
88#[serde(rename_all = "camelCase")]
89pub struct ProvisionalIndexFileOperations {
90 pub update: Vec<OtherOperationSuffixData>,
92}
93
94#[derive(Clone, Debug, Serialize, Deserialize)]
96#[serde(rename_all = "camelCase")]
97pub struct ChunkFileUri {
98 pub chunk_file_uri: String,
100}
101
102#[derive(Clone, Debug, Serialize, Deserialize)]
104#[serde(rename_all = "camelCase")]
105pub struct ProvisionalIndexFile {
106 pub provisional_proof_file_uri: Option<String>,
108 pub chunks: Option<Vec<ChunkFileUri>>,
110 pub operations: Option<ProvisionalIndexFileOperations>,
112}
113
114#[derive(Clone, Debug, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub struct ChunkFile {
118 pub deltas: Vec<Delta>,
120}
121
122#[cfg(test)]
123mod tests {
124 use crate::data::{
125 TEST_CHUNK_FILE_CONTENT, TEST_CORE_INDEX_FILE_CONTENT, TEST_PROVISIONAL_INDEX_FILE_CONTENT,
126 };
127
128 use super::*;
129
130 const CORE_INDEX_FILE_STRUCTURE: &str = r#"
132 {
133 "coreProofFileUri": "CAS_URI",
134 "provisionalIndexFileUri": "CAS_URI",
135 "writerLockId": "OPTIONAL_LOCKING_VALUE",
136 "operations": {
137 "create": [
138 {
139 "suffixData": {
140 "type": "TYPE_STRING",
141 "deltaHash": "DELTA_HASH",
142 "recoveryCommitment": "COMMITMENT_HASH"
143 }
144 }
145 ],
146 "recover": [
147 {
148 "didSuffix": "SUFFIX_STRING",
149 "revealValue": "MULTIHASH_OF_JWK"
150 }
151 ],
152 "deactivate": [
153 {
154 "didSuffix": "SUFFIX_STRING",
155 "revealValue": "MULTIHASH_OF_JWK"
156 }
157 ]
158 }
159 }"#;
160
161 const PROVISIONAL_INDEX_FILE_STRUCTURE: &str = r#"
163 {
164 "provisionalProofFileUri": "CAS_URI",
165 "chunks": [
166 { "chunkFileUri": "CAS_URI" }
167 ],
168 "operations": {
169 "update": [
170 {
171 "didSuffix": "SUFFIX_STRING",
172 "revealValue": "MULTIHASH_OF_JWK"
173 }
174 ]
175 }
176 }
177 "#;
178
179 #[test]
180 fn test_parse_core_index_file_from_sidetree() {
181 let core_index_file: CoreIndexFile =
182 serde_json::from_str(CORE_INDEX_FILE_STRUCTURE).unwrap();
183 assert!(serde_json::to_string_pretty(&core_index_file).is_ok());
184 }
185 #[test]
186 fn test_parse_core_index_file_from_data() {
187 let core_index_file: CoreIndexFile =
188 serde_json::from_str(TEST_CORE_INDEX_FILE_CONTENT).unwrap();
189 assert!(serde_json::to_string_pretty(&core_index_file).is_ok());
190 }
191 #[test]
192 fn test_parse_provisional_index_file_from_sidetree() {
193 let provisional_index_file: ProvisionalIndexFile =
194 serde_json::from_str(PROVISIONAL_INDEX_FILE_STRUCTURE).unwrap();
195 assert!(serde_json::to_string_pretty(&provisional_index_file).is_ok());
196 }
197 #[test]
198 fn test_parse_provisional_index_file_from_data() {
199 let provisional_index_file: ProvisionalIndexFile =
200 serde_json::from_str(TEST_PROVISIONAL_INDEX_FILE_CONTENT).unwrap();
201 assert!(serde_json::to_string_pretty(&provisional_index_file).is_ok());
202 }
203 #[test]
204 fn test_parse_chunk_file_from_data() {
205 let chunk_file: ChunkFile = serde_json::from_str(TEST_CHUNK_FILE_CONTENT).unwrap();
206 assert!(serde_json::to_string_pretty(&chunk_file).is_ok());
207 }
208 #[test]
209 fn test_created_did_suffixes() {
210 let core_index_file: CoreIndexFile =
211 serde_json::from_str(TEST_CORE_INDEX_FILE_CONTENT).unwrap();
212 let expected = vec![
213 "EiCClfEdkTv_aM3UnBBhlOV89LlGhpQAbfeZLFdFxVFkEg",
214 "EiBVpjUxXeSRJpvj2TewlX9zNF3GKMCKWwGmKBZqF6pk_A",
215 "EiAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65Q",
216 ];
217 let actual = core_index_file.created_did_suffixes().unwrap();
218 assert_eq!(expected, actual);
219 }
220
221 #[test]
222 fn test_extract_suffix_idx() {
223 let core_index_file: CoreIndexFile =
224 serde_json::from_str(TEST_CORE_INDEX_FILE_CONTENT).unwrap();
225 let expected = 1;
226 let actual = core_index_file
227 .did_create_operation_index("EiBVpjUxXeSRJpvj2TewlX9zNF3GKMCKWwGmKBZqF6pk_A")
228 .unwrap();
229 assert_eq!(expected, actual);
230 }
231}